mirror of
https://github.com/openai/codex.git
synced 2026-11-03 19:53:25 +00:00
Merge 7f84dcbfb7 into sapling-pr-archive-bolinfest
This commit is contained in:
Generated
+3
@@ -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",
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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<PathBuf>,
|
||||
) -> std::io::Result<Config> {
|
||||
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<PathBuf> {
|
||||
InstallContext::current()
|
||||
.bundled_zsh_path()
|
||||
.map(AbsolutePathBuf::into_path_buf)
|
||||
}
|
||||
|
||||
pub(crate) fn protected_feature_keys(config_layer_stack: &ConfigLayerStack) -> BTreeSet<String> {
|
||||
let mut protected_features = config_layer_stack
|
||||
.effective_config()
|
||||
|
||||
@@ -173,6 +173,20 @@ impl McpProcess {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn new_with_program_and_env(
|
||||
codex_home: &Path,
|
||||
program: &Path,
|
||||
env_overrides: &[(&str, Option<&str>)],
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::new_with_program_env_and_args(
|
||||
codex_home,
|
||||
program,
|
||||
env_overrides,
|
||||
&[DISABLE_PLUGIN_STARTUP_TASKS_ARG],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn new_with_env_and_args(
|
||||
codex_home: &Path,
|
||||
env_overrides: &[(&str, Option<&str>)],
|
||||
@@ -180,6 +194,15 @@ impl McpProcess {
|
||||
) -> anyhow::Result<Self> {
|
||||
let program = codex_utils_cargo_bin::cargo_bin("codex-app-server")
|
||||
.context("should find binary for codex-app-server")?;
|
||||
Self::new_with_program_env_and_args(codex_home, &program, env_overrides, args).await
|
||||
}
|
||||
|
||||
async fn new_with_program_env_and_args(
|
||||
codex_home: &Path,
|
||||
program: &Path,
|
||||
env_overrides: &[(&str, Option<&str>)],
|
||||
args: &[&str],
|
||||
) -> anyhow::Result<Self> {
|
||||
let mut cmd = Command::new(program);
|
||||
|
||||
cmd.stdin(Stdio::piped());
|
||||
|
||||
@@ -37,6 +37,7 @@ use core_test_support::skip_if_no_network;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
use tokio::time::timeout;
|
||||
|
||||
@@ -94,10 +95,9 @@ async fn turn_start_shell_zsh_fork_executes_command_v2() -> Result<()> {
|
||||
(Feature::UnifiedExec, false),
|
||||
(Feature::ShellSnapshot, false),
|
||||
]),
|
||||
&zsh_path,
|
||||
)?;
|
||||
|
||||
let mut mcp = create_zsh_test_mcp_process(&codex_home, &workspace).await?;
|
||||
let mut mcp = create_zsh_test_mcp_process(&codex_home, &workspace, &zsh_path).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let start_id = mcp
|
||||
@@ -162,7 +162,7 @@ async fn turn_start_shell_zsh_fork_executes_command_v2() -> Result<()> {
|
||||
};
|
||||
assert_eq!(id, "call-zsh-fork");
|
||||
assert_eq!(status, CommandExecutionStatus::InProgress);
|
||||
assert!(command.starts_with(&zsh_path.display().to_string()));
|
||||
assert!(command.starts_with(&packaged_zsh_path(&codex_home).display().to_string()));
|
||||
assert!(command.contains("/bin/sh -c"));
|
||||
assert!(command.contains("sleep 0.01"));
|
||||
assert!(command.contains(&release_marker.display().to_string()));
|
||||
@@ -213,10 +213,9 @@ async fn turn_start_shell_zsh_fork_exec_approval_decline_v2() -> Result<()> {
|
||||
(Feature::UnifiedExec, false),
|
||||
(Feature::ShellSnapshot, false),
|
||||
]),
|
||||
&zsh_path,
|
||||
)?;
|
||||
|
||||
let mut mcp = create_zsh_test_mcp_process(&codex_home, &workspace).await?;
|
||||
let mut mcp = create_zsh_test_mcp_process(&codex_home, &workspace, &zsh_path).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let start_id = mcp
|
||||
@@ -346,10 +345,9 @@ async fn turn_start_shell_zsh_fork_exec_approval_cancel_v2() -> Result<()> {
|
||||
(Feature::UnifiedExec, false),
|
||||
(Feature::ShellSnapshot, false),
|
||||
]),
|
||||
&zsh_path,
|
||||
)?;
|
||||
|
||||
let mut mcp = create_zsh_test_mcp_process(&codex_home, &workspace).await?;
|
||||
let mut mcp = create_zsh_test_mcp_process(&codex_home, &workspace, &zsh_path).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let start_id = mcp
|
||||
@@ -505,10 +503,9 @@ async fn turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2()
|
||||
(Feature::UnifiedExec, false),
|
||||
(Feature::ShellSnapshot, false),
|
||||
]),
|
||||
&zsh_path,
|
||||
)?;
|
||||
|
||||
let mut mcp = create_zsh_test_mcp_process(&codex_home, &workspace).await?;
|
||||
let mut mcp = create_zsh_test_mcp_process(&codex_home, &workspace, &zsh_path).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let start_id = mcp
|
||||
@@ -603,7 +600,8 @@ async fn turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2()
|
||||
);
|
||||
approved_subcommand_strings.push(approval_command.to_string());
|
||||
}
|
||||
let is_parent_approval = approval_command.contains(&zsh_path.display().to_string())
|
||||
let is_parent_approval = approval_command
|
||||
.contains(&packaged_zsh_path(&codex_home).display().to_string())
|
||||
&& (approval_command.contains(&shell_command)
|
||||
|| (has_first_file && has_second_file)
|
||||
|| approval_command.contains(&parent_shell_hint));
|
||||
@@ -738,9 +736,53 @@ async fn turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_zsh_test_mcp_process(codex_home: &Path, zdotdir: &Path) -> Result<McpProcess> {
|
||||
async fn create_zsh_test_mcp_process(
|
||||
codex_home: &Path,
|
||||
zdotdir: &Path,
|
||||
zsh_path: &Path,
|
||||
) -> Result<McpProcess> {
|
||||
let app_server = create_test_package_app_server(codex_home, zsh_path)?;
|
||||
let zdotdir = zdotdir.to_string_lossy().into_owned();
|
||||
McpProcess::new_with_env(codex_home, &[("ZDOTDIR", Some(zdotdir.as_str()))]).await
|
||||
McpProcess::new_with_program_and_env(
|
||||
codex_home,
|
||||
&app_server,
|
||||
&[("ZDOTDIR", Some(zdotdir.as_str()))],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn create_test_package_app_server(codex_home: &Path, zsh_path: &Path) -> Result<PathBuf> {
|
||||
let package_dir = codex_home.join("test-package");
|
||||
let bin_dir = package_dir.join("bin");
|
||||
let package_zsh_path = packaged_zsh_path(codex_home);
|
||||
let Some(zsh_bin_dir) = package_zsh_path.parent() else {
|
||||
anyhow::bail!("packaged zsh path should have parent");
|
||||
};
|
||||
std::fs::create_dir_all(&bin_dir)?;
|
||||
std::fs::create_dir_all(zsh_bin_dir)?;
|
||||
std::fs::write(package_dir.join("codex-package.json"), "{}")?;
|
||||
|
||||
let app_server = bin_dir.join("codex-app-server");
|
||||
copy_with_permissions(
|
||||
&codex_utils_cargo_bin::cargo_bin("codex-app-server")?,
|
||||
&app_server,
|
||||
)?;
|
||||
copy_with_permissions(zsh_path, &package_zsh_path)?;
|
||||
Ok(app_server)
|
||||
}
|
||||
|
||||
fn packaged_zsh_path(codex_home: &Path) -> PathBuf {
|
||||
codex_home
|
||||
.join("test-package")
|
||||
.join("codex-resources")
|
||||
.join("zsh")
|
||||
.join("bin")
|
||||
.join("zsh")
|
||||
}
|
||||
|
||||
fn copy_with_permissions(source: &Path, destination: &Path) -> std::io::Result<()> {
|
||||
std::fs::copy(source, destination)?;
|
||||
std::fs::set_permissions(destination, std::fs::metadata(source)?.permissions())
|
||||
}
|
||||
|
||||
fn create_config_toml(
|
||||
@@ -748,7 +790,6 @@ fn create_config_toml(
|
||||
server_uri: &str,
|
||||
approval_policy: &str,
|
||||
feature_flags: &BTreeMap<Feature, bool>,
|
||||
zsh_path: &Path,
|
||||
) -> std::io::Result<()> {
|
||||
let mut features = BTreeMap::from([(Feature::RemoteModels, false)]);
|
||||
for (feature, enabled) in feature_flags {
|
||||
@@ -774,7 +815,6 @@ fn create_config_toml(
|
||||
model = "mock-model"
|
||||
approval_policy = "{approval_policy}"
|
||||
sandbox_mode = "read-only"
|
||||
zsh_path = "{zsh_path}"
|
||||
|
||||
model_provider = "mock_provider"
|
||||
|
||||
@@ -787,9 +827,7 @@ base_url = "{server_uri}/v1"
|
||||
wire_api = "responses"
|
||||
request_max_retries = 0
|
||||
stream_max_retries = 0
|
||||
"#,
|
||||
approval_policy = approval_policy,
|
||||
zsh_path = zsh_path.display()
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -74,6 +74,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;
|
||||
@@ -1562,6 +1563,12 @@ async fn load_exec_server_config(
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub(crate) fn default_zsh_path() -> Option<PathBuf> {
|
||||
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,
|
||||
@@ -1686,6 +1693,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),
|
||||
|
||||
@@ -301,9 +301,6 @@ pub struct ConfigToml {
|
||||
#[schemars(skip)]
|
||||
pub js_repl_node_module_dirs: Option<Vec<AbsolutePathBuf>>,
|
||||
|
||||
/// Optional absolute path to patched zsh used by zsh-exec-bridge-backed shell execution.
|
||||
pub zsh_path: Option<AbsolutePathBuf>,
|
||||
|
||||
/// Profile to use from the `profiles` map.
|
||||
pub profile: Option<String>,
|
||||
|
||||
|
||||
@@ -48,8 +48,6 @@ pub struct ConfigProfile {
|
||||
/// Deprecated: ignored.
|
||||
#[schemars(skip)]
|
||||
pub js_repl_node_module_dirs: Option<Vec<AbsolutePathBuf>>,
|
||||
/// Optional absolute path to patched zsh used by zsh-exec-bridge-backed shell execution.
|
||||
pub zsh_path: Option<AbsolutePathBuf>,
|
||||
pub experimental_compact_prompt_file: Option<AbsolutePathBuf>,
|
||||
pub include_permissions_instructions: Option<bool>,
|
||||
pub include_apps_instructions: Option<bool>,
|
||||
|
||||
@@ -711,14 +711,6 @@
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"zsh_path": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "Optional absolute path to patched zsh used by zsh-exec-bridge-backed shell execution."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -4986,14 +4978,6 @@
|
||||
],
|
||||
"default": null,
|
||||
"description": "Windows-specific configuration."
|
||||
},
|
||||
"zsh_path": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "Optional absolute path to patched zsh used by zsh-exec-bridge-backed shell execution."
|
||||
}
|
||||
},
|
||||
"title": "ConfigToml",
|
||||
|
||||
@@ -4470,6 +4470,25 @@ async fn add_dir_override_extends_workspace_writable_roots() -> std::io::Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn default_zsh_path_sets_runtime_zsh_path() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let default_zsh_path = codex_home.path().join("packaged-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));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sqlite_home_defaults_to_codex_home_for_workspace_write() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -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(),
|
||||
@@ -2128,7 +2129,7 @@ pub struct ConfigOverrides {
|
||||
pub codex_self_exe: Option<PathBuf>,
|
||||
pub codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
pub main_execve_wrapper_exe: Option<PathBuf>,
|
||||
pub zsh_path: Option<PathBuf>,
|
||||
pub default_zsh_path: Option<PathBuf>,
|
||||
pub base_instructions: Option<String>,
|
||||
pub developer_instructions: Option<String>,
|
||||
pub personality: Option<Personality>,
|
||||
@@ -2479,7 +2480,7 @@ impl Config {
|
||||
codex_self_exe,
|
||||
codex_linux_sandbox_exe,
|
||||
main_execve_wrapper_exe,
|
||||
zsh_path: zsh_path_override,
|
||||
default_zsh_path,
|
||||
base_instructions,
|
||||
developer_instructions,
|
||||
personality,
|
||||
@@ -3198,7 +3199,7 @@ 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 = default_zsh_path;
|
||||
|
||||
let review_model = override_review_model.or(cfg.review_model);
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ async fn restricted_read_implicitly_allows_helper_executables() -> std::io::Resu
|
||||
},
|
||||
ConfigOverrides {
|
||||
cwd: Some(cwd.clone()),
|
||||
zsh_path: Some(zsh_path.clone()),
|
||||
default_zsh_path: Some(zsh_path.clone()),
|
||||
main_execve_wrapper_exe: Some(execve_wrapper),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
@@ -826,13 +826,13 @@ impl Session {
|
||||
} else if use_zsh_fork_shell {
|
||||
let zsh_path = config.zsh_path.as_ref().ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"zsh fork feature enabled, but `zsh_path` is not configured; set `zsh_path` in config.toml"
|
||||
"zsh fork feature enabled, but no packaged zsh fork is available for this install"
|
||||
)
|
||||
})?;
|
||||
let zsh_path = zsh_path.to_path_buf();
|
||||
shell::get_shell(shell::ShellType::Zsh, Some(&zsh_path)).ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"zsh fork feature enabled, but zsh_path `{}` is not usable; set `zsh_path` to a valid zsh executable",
|
||||
"zsh fork feature enabled, but packaged zsh fork `{}` is not usable",
|
||||
zsh_path.display()
|
||||
)
|
||||
})?
|
||||
|
||||
@@ -4293,7 +4293,7 @@ async fn absolute_cwd_update_with_turn_environment_is_allowed() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
||||
async fn session_new_fails_when_zsh_fork_enabled_without_packaged_zsh() {
|
||||
let codex_home = tempfile::tempdir().expect("create temp dir");
|
||||
let mut config = build_test_config(codex_home.path()).await;
|
||||
config
|
||||
@@ -4394,7 +4394,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
||||
Err(err) => err,
|
||||
};
|
||||
let msg = format!("{err:#}");
|
||||
assert!(msg.contains("zsh fork feature enabled, but `zsh_path` is not configured"));
|
||||
assert!(msg.contains("zsh fork feature enabled, but no packaged zsh fork is available"));
|
||||
}
|
||||
|
||||
// todo: use online model info
|
||||
|
||||
@@ -22,6 +22,8 @@ use codex_sandboxing::SandboxCommand;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(unix)]
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) mod apply_patch;
|
||||
pub(crate) mod shell;
|
||||
@@ -70,6 +72,29 @@ pub(crate) fn exec_env_for_sandbox_permissions(
|
||||
env
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub(crate) fn apply_zsh_fork_path_prepend(
|
||||
env: &mut HashMap<String, String>,
|
||||
explicit_env_overrides: &mut HashMap<String, String>,
|
||||
shell_zsh_path: &Path,
|
||||
) {
|
||||
let Some(zsh_bin_dir) = shell_zsh_path.parent() else {
|
||||
return;
|
||||
};
|
||||
let zsh_bin_dir = zsh_bin_dir.to_string_lossy().to_string();
|
||||
let updated_path = match env.get("PATH") {
|
||||
Some(path) if !path.is_empty() => std::iter::once(zsh_bin_dir.as_str())
|
||||
.chain(path.split(':').filter(|entry| *entry != zsh_bin_dir))
|
||||
.collect::<Vec<_>>()
|
||||
.join(":"),
|
||||
_ => zsh_bin_dir,
|
||||
};
|
||||
env.insert("PATH".to_string(), updated_path.clone());
|
||||
// Snapshot wrapping restores explicit overrides after sourcing the shell
|
||||
// snapshot, so capture this PATH override there as well.
|
||||
explicit_env_overrides.insert("PATH".to_string(), updated_path);
|
||||
}
|
||||
|
||||
pub(crate) fn disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
command: &[String],
|
||||
shell_type: Option<&ShellType>,
|
||||
|
||||
@@ -144,6 +144,48 @@ async fn explicit_escalation_prepares_exec_without_managed_network() -> anyhow::
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn apply_zsh_fork_path_prepend_uses_shell_parent() {
|
||||
let mut env = HashMap::from([("PATH".to_string(), "/usr/bin:/bin".to_string())]);
|
||||
let mut explicit_env_overrides = HashMap::new();
|
||||
|
||||
apply_zsh_fork_path_prepend(
|
||||
&mut env,
|
||||
&mut explicit_env_overrides,
|
||||
PathBuf::from("/package/codex-resources/zsh/bin/zsh").as_path(),
|
||||
);
|
||||
|
||||
let expected = "/package/codex-resources/zsh/bin:/usr/bin:/bin";
|
||||
assert_eq!(env.get("PATH").map(String::as_str), Some(expected));
|
||||
assert_eq!(
|
||||
explicit_env_overrides.get("PATH").map(String::as_str),
|
||||
Some(expected)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn apply_zsh_fork_path_prepend_moves_existing_shell_parent_to_front() {
|
||||
let mut env = HashMap::from([(
|
||||
"PATH".to_string(),
|
||||
"/usr/bin:/package/codex-resources/zsh/bin:/bin:/package/codex-resources/zsh/bin"
|
||||
.to_string(),
|
||||
)]);
|
||||
let mut explicit_env_overrides = HashMap::new();
|
||||
|
||||
apply_zsh_fork_path_prepend(
|
||||
&mut env,
|
||||
&mut explicit_env_overrides,
|
||||
PathBuf::from("/package/codex-resources/zsh/bin/zsh").as_path(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
env.get("PATH").map(String::as_str),
|
||||
Some("/package/codex-resources/zsh/bin:/usr/bin:/bin")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_escalation_keeps_user_proxy_env_without_codex_marker() {
|
||||
let env = HashMap::from([
|
||||
@@ -819,6 +861,57 @@ fn maybe_wrap_shell_lc_with_snapshot_applies_explicit_path_override() {
|
||||
assert_eq!(String::from_utf8_lossy(&output.stdout), "/worktree/bin");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn maybe_wrap_shell_lc_with_snapshot_preserves_zsh_fork_path_prepend() {
|
||||
let dir = tempdir().expect("create temp dir");
|
||||
let snapshot_path = dir.path().join("snapshot.sh");
|
||||
std::fs::write(
|
||||
&snapshot_path,
|
||||
"# Snapshot file\nexport PATH='/snapshot/bin'\n",
|
||||
)
|
||||
.expect("write snapshot");
|
||||
let session_shell = shell_with_snapshot(
|
||||
ShellType::Bash,
|
||||
"/bin/bash",
|
||||
snapshot_path.abs(),
|
||||
dir.path().abs(),
|
||||
);
|
||||
let command = vec![
|
||||
"/bin/bash".to_string(),
|
||||
"-lc".to_string(),
|
||||
"printf '%s' \"$PATH\"".to_string(),
|
||||
];
|
||||
let zsh_path = dir
|
||||
.path()
|
||||
.join("codex-resources")
|
||||
.join("zsh")
|
||||
.join("bin")
|
||||
.join("zsh");
|
||||
let zsh_bin_dir = zsh_path.parent().expect("zsh path should have parent");
|
||||
let mut env = HashMap::from([("PATH".to_string(), "/worktree/bin".to_string())]);
|
||||
let mut explicit_env_overrides = HashMap::new();
|
||||
apply_zsh_fork_path_prepend(&mut env, &mut explicit_env_overrides, zsh_path.as_path());
|
||||
let rewritten = maybe_wrap_shell_lc_with_snapshot(
|
||||
&command,
|
||||
&session_shell,
|
||||
&dir.path().abs(),
|
||||
&explicit_env_overrides,
|
||||
&env,
|
||||
);
|
||||
let output = Command::new(&rewritten[0])
|
||||
.args(&rewritten[1..])
|
||||
.env("PATH", env.get("PATH").expect("PATH should be set"))
|
||||
.output()
|
||||
.expect("run rewritten command");
|
||||
|
||||
assert!(output.status.success(), "command failed: {output:?}");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
format!("{}:/worktree/bin", zsh_bin_dir.display())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maybe_wrap_shell_lc_with_snapshot_does_not_embed_override_values_in_argv() {
|
||||
let dir = tempdir().expect("create temp dir");
|
||||
|
||||
@@ -20,6 +20,8 @@ use crate::shell::ShellType;
|
||||
use crate::tools::flat_tool_name;
|
||||
use crate::tools::network_approval::NetworkApprovalMode;
|
||||
use crate::tools::network_approval::NetworkApprovalSpec;
|
||||
#[cfg(unix)]
|
||||
use crate::tools::runtimes::apply_zsh_fork_path_prepend;
|
||||
use crate::tools::runtimes::build_sandbox_command;
|
||||
use crate::tools::runtimes::disable_powershell_profile_for_elevated_windows_sandbox;
|
||||
use crate::tools::runtimes::exec_env_for_sandbox_permissions;
|
||||
@@ -231,12 +233,19 @@ impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
|
||||
let session_shell = ctx.session.user_shell();
|
||||
let managed_network =
|
||||
managed_network_for_sandbox_permissions(req.network.as_ref(), req.sandbox_permissions);
|
||||
let env = exec_env_for_sandbox_permissions(&req.env, req.sandbox_permissions);
|
||||
let mut env = exec_env_for_sandbox_permissions(&req.env, req.sandbox_permissions);
|
||||
let mut explicit_env_overrides = req.explicit_env_overrides.clone();
|
||||
#[cfg(unix)]
|
||||
if self.backend == ShellRuntimeBackend::ShellCommandZshFork
|
||||
&& let Some(shell_zsh_path) = ctx.session.services.shell_zsh_path.as_deref()
|
||||
{
|
||||
apply_zsh_fork_path_prepend(&mut env, &mut explicit_env_overrides, shell_zsh_path);
|
||||
}
|
||||
let command = maybe_wrap_shell_lc_with_snapshot(
|
||||
&req.command,
|
||||
session_shell.as_ref(),
|
||||
&req.cwd,
|
||||
&req.explicit_env_overrides,
|
||||
&explicit_env_overrides,
|
||||
&env,
|
||||
);
|
||||
let command = disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
@@ -252,7 +261,9 @@ impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
|
||||
};
|
||||
|
||||
if self.backend == ShellRuntimeBackend::ShellCommandZshFork {
|
||||
match zsh_fork_backend::maybe_run_shell_command(req, attempt, ctx, &command).await? {
|
||||
match zsh_fork_backend::maybe_run_shell_command(req, attempt, ctx, &command, &env)
|
||||
.await?
|
||||
{
|
||||
Some(out) => return Ok(out),
|
||||
None => {
|
||||
tracing::warn!(
|
||||
|
||||
@@ -15,7 +15,6 @@ use crate::sandboxing::ExecRequest;
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
use crate::shell::ShellType;
|
||||
use crate::tools::runtimes::build_sandbox_command;
|
||||
use crate::tools::runtimes::exec_env_for_sandbox_permissions;
|
||||
use crate::tools::sandboxing::PermissionRequestPayload;
|
||||
use crate::tools::sandboxing::SandboxAttempt;
|
||||
use crate::tools::sandboxing::ToolCtx;
|
||||
@@ -102,6 +101,7 @@ pub(super) async fn try_run_zsh_fork(
|
||||
attempt: &SandboxAttempt<'_>,
|
||||
ctx: &ToolCtx,
|
||||
command: &[String],
|
||||
env: &HashMap<String, String>,
|
||||
) -> Result<Option<ExecToolCallOutput>, ToolError> {
|
||||
let Some(shell_zsh_path) = ctx.session.services.shell_zsh_path.as_ref() else {
|
||||
tracing::warn!("ZshFork backend specified, but shell_zsh_path is not configured.");
|
||||
@@ -116,9 +116,8 @@ pub(super) async fn try_run_zsh_fork(
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let env = exec_env_for_sandbox_permissions(&req.env, req.sandbox_permissions);
|
||||
let command =
|
||||
build_sandbox_command(command, &req.cwd, &env, req.additional_permissions.clone())?;
|
||||
build_sandbox_command(command, &req.cwd, env, req.additional_permissions.clone())?;
|
||||
let options = ExecOptions {
|
||||
expiration: req.timeout_ms.into(),
|
||||
capture_policy: ExecCapturePolicy::ShellTool,
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::tools::sandboxing::ToolError;
|
||||
use crate::unified_exec::SpawnLifecycleHandle;
|
||||
use codex_protocol::exec_output::ExecToolCallOutput;
|
||||
use codex_tools::ZshForkConfig;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub(crate) struct PreparedUnifiedExecSpawn {
|
||||
pub(crate) exec_request: ExecRequest,
|
||||
@@ -23,8 +24,9 @@ pub(crate) async fn maybe_run_shell_command(
|
||||
attempt: &SandboxAttempt<'_>,
|
||||
ctx: &ToolCtx,
|
||||
command: &[String],
|
||||
env: &HashMap<String, String>,
|
||||
) -> Result<Option<ExecToolCallOutput>, ToolError> {
|
||||
imp::maybe_run_shell_command(req, attempt, ctx, command).await
|
||||
imp::maybe_run_shell_command(req, attempt, ctx, command, env).await
|
||||
}
|
||||
|
||||
/// Prepares unified exec to launch through the zsh-fork backend when the
|
||||
@@ -76,8 +78,9 @@ mod imp {
|
||||
attempt: &SandboxAttempt<'_>,
|
||||
ctx: &ToolCtx,
|
||||
command: &[String],
|
||||
env: &HashMap<String, String>,
|
||||
) -> Result<Option<ExecToolCallOutput>, ToolError> {
|
||||
unix_escalation::try_run_zsh_fork(req, attempt, ctx, command).await
|
||||
unix_escalation::try_run_zsh_fork(req, attempt, ctx, command, env).await
|
||||
}
|
||||
|
||||
pub(super) async fn maybe_prepare_unified_exec(
|
||||
@@ -118,8 +121,9 @@ mod imp {
|
||||
attempt: &SandboxAttempt<'_>,
|
||||
ctx: &ToolCtx,
|
||||
command: &[String],
|
||||
env: &HashMap<String, String>,
|
||||
) -> Result<Option<ExecToolCallOutput>, ToolError> {
|
||||
let _ = (req, attempt, ctx, command);
|
||||
let _ = (req, attempt, ctx, command, env);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ use crate::shell::ShellType;
|
||||
use crate::tools::flat_tool_name;
|
||||
use crate::tools::network_approval::NetworkApprovalMode;
|
||||
use crate::tools::network_approval::NetworkApprovalSpec;
|
||||
#[cfg(unix)]
|
||||
use crate::tools::runtimes::apply_zsh_fork_path_prepend;
|
||||
use crate::tools::runtimes::build_sandbox_command;
|
||||
use crate::tools::runtimes::disable_powershell_profile_for_elevated_windows_sandbox;
|
||||
use crate::tools::runtimes::exec_env_for_sandbox_permissions;
|
||||
@@ -261,6 +263,15 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
|
||||
if let Some(network) = managed_network {
|
||||
network.apply_to_env(&mut env);
|
||||
}
|
||||
let mut explicit_env_overrides = req.explicit_env_overrides.clone();
|
||||
#[cfg(unix)]
|
||||
if let UnifiedExecShellMode::ZshFork(zsh_fork_config) = &self.shell_mode {
|
||||
apply_zsh_fork_path_prepend(
|
||||
&mut env,
|
||||
&mut explicit_env_overrides,
|
||||
zsh_fork_config.shell_zsh_path.as_path(),
|
||||
);
|
||||
}
|
||||
let environment_is_remote = req.environment.is_remote();
|
||||
let command = if environment_is_remote {
|
||||
base_command.to_vec()
|
||||
@@ -269,7 +280,7 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
|
||||
base_command,
|
||||
session_shell.as_ref(),
|
||||
&req.cwd,
|
||||
&req.explicit_env_overrides,
|
||||
&explicit_env_overrides,
|
||||
&env,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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;
|
||||
@@ -416,7 +417,9 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
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(),
|
||||
zsh_path: None,
|
||||
default_zsh_path: InstallContext::current()
|
||||
.bundled_zsh_path()
|
||||
.map(AbsolutePathBuf::into_path_buf),
|
||||
base_instructions: None,
|
||||
developer_instructions: None,
|
||||
personality: None,
|
||||
|
||||
@@ -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<InstallContext> = OnceLock::new();
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -166,6 +167,18 @@ impl InstallContext {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn bundled_zsh_path(&self) -> Option<AbsolutePathBuf> {
|
||||
if cfg!(windows) {
|
||||
None
|
||||
} else {
|
||||
self.bundled_resource(zsh_resource_path())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bundled_zsh_bin_dir(&self) -> Option<AbsolutePathBuf> {
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1847,6 +1847,13 @@ async fn load_config_or_exit_with_fallback_cwd(
|
||||
strict_config: bool,
|
||||
fallback_cwd: Option<PathBuf>,
|
||||
) -> 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)
|
||||
|
||||
@@ -13,6 +13,7 @@ The builder creates a canonical Codex package directory:
|
||||
│ └── <entrypoint>[.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/<target>-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/<target>-zsh` and installed at
|
||||
`codex-resources/zsh/bin/zsh`.
|
||||
|
||||
@@ -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()
|
||||
|
||||
Executable
+43
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
Reference in New Issue
Block a user