Add Windows sandbox provisioning setup command (#24831)

## Why

Some Windows users do not have local admin access, so they cannot
complete the elevated portion of the Windows sandbox setup when Codex
first needs it. This adds an alpha provisioning path that an admin or IT
deployment script can run ahead of time for the Codex user.

The intended managed-deployment shape is:

```powershell
codex sandbox setup --elevated --user "$env:COMPUTERNAME\Alice" --codex-home "C:\Users\Alice\.codex"
```

`--elevated` is treated as the requested sandbox setup level, not as
proof that the process is elevated. The Windows sandbox setup
orchestration still checks that the caller is actually elevated before
launching the helper without a UAC prompt.

## What changed

- Added `codex sandbox setup --elevated` with explicit user selection
via either `--current-user` or `--user ... --codex-home ...`.
- Moved the CLI implementation into `cli/src/sandbox_setup.rs` instead
of growing `cli/src/main.rs`.
- Added a Windows sandbox `ProvisionOnly` helper mode that runs the
elevation-required provisioning work without requiring a workspace cwd
or runtime sandbox policy.
- Reused the existing elevated helper path for creating/updating sandbox
users, configuring firewall/WFP rules, and applying sandbox directory
ACLs.
- Persisted `windows.sandbox = "elevated"` into the target `CODEX_HOME`
so the desktop app does not show the initial sandbox setup banner after
pre-provisioning succeeds.

## Validation

- `cargo fmt -p codex-windows-sandbox -p codex-core -p codex-cli`
- `cargo test -p codex-cli sandbox_setup --target-dir
target\sandbox-setup-check`
- `cargo test -p codex-windows-sandbox
payload_accepts_provision_only_mode --target-dir
target\sandbox-setup-check`
- `git diff --check`
- Manual Windows alpha flow with a standard local user (`Mandi Lavida`):
ran the new setup command from an admin shell, verified the target
`.codex` contents, sandbox marker/secrets, ACLs, firewall rules, and
desktop startup without the sandbox setup banner once experimental
network proxy requirements were disabled.

## Notes

This intentionally does not solve later elevated update coordination for
IT-managed deployments. The setup command can still apply provisioning
updates when run again, but a broader coordination/process story is out
of scope for this alpha.
This commit is contained in:
iceweasel-oai
2026-05-29 11:01:44 -07:00
committed by GitHub
parent 10b0399034
commit cb9178e8b3
7 changed files with 469 additions and 113 deletions

View File

@@ -50,6 +50,8 @@ mod marketplace_cmd;
mod mcp_cmd;
mod plugin_cmd;
mod remote_control_cmd;
#[cfg(target_os = "windows")]
mod sandbox_setup;
mod state_db_recovery;
#[cfg(not(windows))]
mod wsl_paths;
@@ -1250,6 +1252,16 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
.await?;
}
Some(Subcommand::Sandbox(mut sandbox_cli)) => {
#[cfg(target_os = "windows")]
if let Some(setup_cli) = sandbox_setup::parse_setup_command(&sandbox_cli.command)? {
reject_remote_mode_for_subcommand(
root_remote.as_deref(),
root_remote_auth_token_env.as_deref(),
"sandbox setup",
)?;
sandbox_setup::run(setup_cli).await?;
return Ok(());
}
reject_remote_mode_for_subcommand(
root_remote.as_deref(),
root_remote_auth_token_env.as_deref(),

View File

@@ -0,0 +1,206 @@
use std::path::PathBuf;
use clap::ArgAction;
use clap::ArgGroup;
use clap::Parser;
use codex_core::config::edit::ConfigEditsBuilder;
use codex_core::config::find_codex_home;
#[derive(Debug, Parser)]
#[command(group(
ArgGroup::new("sandbox_user")
.required(true)
.args(["user", "current_user"])
))]
pub(crate) struct SandboxSetupCommand {
/// Set up the elevated Windows sandbox.
#[arg(long = "elevated", action = ArgAction::SetTrue)]
elevated_sandbox_level: bool,
/// Windows user that will run Codex after managed deployment.
#[arg(
long = "user",
value_name = "USER",
conflicts_with = "current_user",
requires = "codex_home"
)]
user: Option<String>,
/// Use the current Windows user as the Codex user.
#[arg(
long = "current-user",
default_value_t = false,
conflicts_with = "user"
)]
current_user: bool,
/// CODEX_HOME for the Codex user. Required with --user.
#[arg(long = "codex-home", value_name = "DIR")]
codex_home: Option<PathBuf>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SandboxSetupLevel {
Elevated,
}
impl SandboxSetupCommand {
fn setup_level(&self) -> anyhow::Result<SandboxSetupLevel> {
if self.elevated_sandbox_level {
Ok(SandboxSetupLevel::Elevated)
} else {
anyhow::bail!("`codex sandbox setup` currently requires --elevated");
}
}
}
pub(crate) async fn run(cmd: SandboxSetupCommand) -> anyhow::Result<()> {
match cmd.setup_level()? {
SandboxSetupLevel::Elevated => run_elevated(cmd).await,
}
}
pub(crate) fn parse_setup_command(
sandbox_command: &[String],
) -> anyhow::Result<Option<SandboxSetupCommand>> {
if sandbox_command
.first()
.is_none_or(|command| command != "setup")
{
return Ok(None);
}
SandboxSetupCommand::try_parse_from(sandbox_command.iter().map(String::as_str))
.map(Some)
.map_err(anyhow::Error::from)
}
async fn run_elevated(cmd: SandboxSetupCommand) -> anyhow::Result<()> {
let identity = resolve_sandbox_setup_identity(&cmd)?;
codex_core::windows_sandbox::run_elevated_provisioning_setup(
identity.codex_home.as_path(),
identity.real_user.as_str(),
)?;
ConfigEditsBuilder::new(identity.codex_home.as_path())
.set_windows_sandbox_mode("elevated")
.apply()
.await
.map_err(|err| {
anyhow::anyhow!(
"sandbox provisioning succeeded, but failed to persist elevated sandbox config: {err}"
)
})?;
println!(
"Windows elevated sandbox setup completed for {} at {}.",
identity.real_user,
identity.codex_home.display()
);
Ok(())
}
struct SandboxSetupIdentity {
real_user: String,
codex_home: PathBuf,
}
fn resolve_sandbox_setup_identity(
cmd: &SandboxSetupCommand,
) -> anyhow::Result<SandboxSetupIdentity> {
if cmd.current_user {
let real_user = std::env::var("USERNAME")
.or_else(|_| std::env::var("USER"))
.map_err(|err| {
anyhow::anyhow!("failed to determine current user from environment: {err}")
})?;
let codex_home = match cmd.codex_home.clone() {
Some(codex_home) => codex_home,
None => find_codex_home()?.to_path_buf(),
};
return Ok(SandboxSetupIdentity {
real_user,
codex_home,
});
}
let real_user = cmd
.user
.clone()
.ok_or_else(|| anyhow::anyhow!("--user or --current-user is required"))?;
let codex_home = cmd
.codex_home
.clone()
.ok_or_else(|| anyhow::anyhow!("--codex-home is required with --user"))?;
Ok(SandboxSetupIdentity {
real_user,
codex_home,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_managed_user_identity() {
let command = SandboxSetupCommand::try_parse_from([
"setup",
"--elevated",
"--user",
"DOMAIN\\alice",
"--codex-home",
r"C:\Users\alice\.codex",
])
.expect("parse");
assert!(command.elevated_sandbox_level);
assert_eq!(command.user.as_deref(), Some(r"DOMAIN\alice"));
assert!(!command.current_user);
assert_eq!(
command.codex_home.as_deref(),
Some(std::path::Path::new(r"C:\Users\alice\.codex"))
);
}
#[test]
fn requires_explicit_user_identity() {
let err = SandboxSetupCommand::try_parse_from(["setup", "--elevated"])
.expect_err("parse should fail");
assert_eq!(err.kind(), clap::error::ErrorKind::MissingRequiredArgument);
}
#[test]
fn requires_codex_home_for_managed_user() {
let err =
SandboxSetupCommand::try_parse_from(["setup", "--elevated", "--user", "DOMAIN\\alice"])
.expect_err("parse should fail");
assert_eq!(err.kind(), clap::error::ErrorKind::MissingRequiredArgument);
}
#[test]
fn parses_setup_from_sandbox_command_args() {
let command = parse_setup_command(&[
"setup".to_string(),
"--elevated".to_string(),
"--user".to_string(),
r"DOMAIN\alice".to_string(),
"--codex-home".to_string(),
r"C:\Users\alice\.codex".to_string(),
])
.expect("parse")
.expect("setup command");
assert_eq!(command.user.as_deref(), Some(r"DOMAIN\alice"));
}
#[test]
fn ignores_non_setup_sandbox_command_args() {
let command =
parse_setup_command(&["echo".to_string(), "hello".to_string()]).expect("parse");
assert!(command.is_none());
}
}

View File

@@ -169,6 +169,11 @@ pub fn run_elevated_setup(
)
}
#[cfg(target_os = "windows")]
pub fn run_elevated_provisioning_setup(codex_home: &Path, real_user: &str) -> anyhow::Result<()> {
codex_windows_sandbox::run_elevated_provisioning_setup(codex_home, real_user)
}
#[cfg(not(target_os = "windows"))]
pub fn run_elevated_setup(
_permission_profile: &PermissionProfile,
@@ -180,6 +185,11 @@ pub fn run_elevated_setup(
anyhow::bail!("elevated Windows sandbox setup is only supported on Windows")
}
#[cfg(not(target_os = "windows"))]
pub fn run_elevated_provisioning_setup(_codex_home: &Path, _real_user: &str) -> anyhow::Result<()> {
anyhow::bail!("elevated Windows sandbox setup is only supported on Windows")
}
#[cfg(target_os = "windows")]
pub fn run_legacy_setup_preflight(
permission_profile: &PermissionProfile,

View File

@@ -105,6 +105,7 @@ struct Payload {
enum SetupMode {
#[default]
Full,
ProvisionOnly,
ReadAclsOnly,
}
@@ -476,6 +477,7 @@ fn real_main() -> Result<()> {
fn run_setup(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Result<()> {
match payload.mode {
SetupMode::ReadAclsOnly => run_read_acl_only(payload, log),
SetupMode::ProvisionOnly => run_provision_only(payload, log, sbx_dir),
SetupMode::Full => run_setup_full(payload, log, sbx_dir),
}
}
@@ -543,31 +545,182 @@ fn run_read_acl_only(payload: &Payload, log: &mut dyn Write) -> Result<()> {
Ok(())
}
fn provision_and_hide_sandbox_users(
payload: &Payload,
log: &mut dyn Write,
sbx_dir: &Path,
) -> Result<()> {
let provision_result = provision_sandbox_users(
&payload.codex_home,
&payload.offline_username,
&payload.online_username,
&payload.proxy_ports,
payload.allow_local_binding,
log,
);
if let Err(err) = provision_result {
if extract_setup_failure(&err).is_some() {
return Err(err);
}
return Err(anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperUserProvisionFailed,
format!("provision sandbox users failed: {err}"),
)));
}
let users = vec![
payload.offline_username.clone(),
payload.online_username.clone(),
];
hide_newly_created_users(&users, sbx_dir);
Ok(())
}
fn configure_offline_sandbox_network(
payload: &Payload,
offline_sid_str: &str,
log: &mut dyn Write,
) -> Result<()> {
let proxy_allowlist_result = firewall::ensure_offline_proxy_allowlist(
offline_sid_str,
&payload.proxy_ports,
payload.allow_local_binding,
log,
);
if let Err(err) = proxy_allowlist_result {
if extract_setup_failure(&err).is_some() {
return Err(err);
}
return Err(anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperFirewallRuleCreateOrAddFailed,
format!("ensure offline proxy allowlist failed: {err}"),
)));
}
let firewall_result = firewall::ensure_offline_outbound_block(offline_sid_str, log);
if let Err(err) = firewall_result {
if extract_setup_failure(&err).is_some() {
return Err(err);
}
return Err(anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperFirewallRuleCreateOrAddFailed,
format!("ensure offline outbound block failed: {err}"),
)));
}
install_wfp_filters(
&payload.codex_home,
&payload.offline_username,
payload.otel.as_ref(),
|message| {
let _ = log_line(log, message);
},
);
Ok(())
}
fn lock_persistent_sandbox_dirs(
payload: &Payload,
sandbox_group_sid: &[u8],
log: &mut dyn Write,
) -> Result<()> {
lock_sandbox_dir(
&sandbox_dir(&payload.codex_home),
&payload.real_user,
sandbox_group_sid,
GRANT_ACCESS,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE,
log,
)
.map_err(|err| {
anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperSandboxLockFailed,
format!(
"lock sandbox dir {} failed: {err}",
sandbox_dir(&payload.codex_home).display()
),
))
})?;
lock_sandbox_dir(
&sandbox_secrets_dir(&payload.codex_home),
&payload.real_user,
sandbox_group_sid,
DENY_ACCESS,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE,
log,
)
.map_err(|err| {
anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperSandboxLockFailed,
format!(
"lock sandbox secrets dir {} failed: {err}",
sandbox_secrets_dir(&payload.codex_home).display()
),
))
})?;
let legacy_users = sandbox_dir(&payload.codex_home).join("sandbox_users.json");
if legacy_users.exists() {
let _ = std::fs::remove_file(&legacy_users);
}
Ok(())
}
fn lock_sandbox_bin_dir(
payload: &Payload,
sandbox_group_sid: &[u8],
log: &mut dyn Write,
) -> Result<()> {
lock_sandbox_dir(
&sandbox_bin_dir(&payload.codex_home),
&payload.real_user,
sandbox_group_sid,
GRANT_ACCESS,
FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
log,
)
.map_err(|err| {
anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperSandboxLockFailed,
format!(
"lock sandbox bin dir {} failed: {err}",
sandbox_bin_dir(&payload.codex_home).display()
),
))
})
}
fn run_provision_only(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Result<()> {
provision_and_hide_sandbox_users(payload, log, sbx_dir)?;
let offline_sid = resolve_sid(&payload.offline_username).map_err(|err| {
anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperSidResolveFailed,
format!(
"resolve SID for offline user {} failed: {err}",
payload.offline_username
),
))
})?;
let offline_sid_str = string_from_sid_bytes(&offline_sid).map_err(anyhow::Error::msg)?;
let sandbox_group_sid = resolve_sandbox_users_group_sid().map_err(|err| {
anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperSidResolveFailed,
format!("resolve sandbox users group SID failed: {err}"),
))
})?;
configure_offline_sandbox_network(payload, &offline_sid_str, log)?;
lock_sandbox_bin_dir(payload, &sandbox_group_sid, log)?;
lock_persistent_sandbox_dirs(payload, &sandbox_group_sid, log)?;
log_note("setup provisioning binary completed", Some(sbx_dir));
Ok(())
}
fn run_setup_full(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Result<()> {
let refresh_only = payload.refresh_only;
if !refresh_only {
let provision_result = provision_sandbox_users(
&payload.codex_home,
&payload.offline_username,
&payload.online_username,
&payload.proxy_ports,
payload.allow_local_binding,
log,
);
if let Err(err) = provision_result {
if extract_setup_failure(&err).is_some() {
return Err(err);
}
return Err(anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperUserProvisionFailed,
format!("provision sandbox users failed: {err}"),
)));
}
let users = vec![
payload.offline_username.clone(),
payload.online_username.clone(),
];
hide_newly_created_users(&users, sbx_dir);
provision_and_hide_sandbox_users(payload, log, sbx_dir)?;
}
let offline_sid = resolve_sid(&payload.offline_username).map_err(|err| {
anyhow::Error::new(SetupFailure::new(
@@ -597,39 +750,7 @@ fn run_setup_full(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Res
let mut refresh_errors: Vec<String> = Vec::new();
if !refresh_only {
let proxy_allowlist_result = firewall::ensure_offline_proxy_allowlist(
&offline_sid_str,
&payload.proxy_ports,
payload.allow_local_binding,
log,
);
if let Err(err) = proxy_allowlist_result {
if extract_setup_failure(&err).is_some() {
return Err(err);
}
return Err(anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperFirewallRuleCreateOrAddFailed,
format!("ensure offline proxy allowlist failed: {err}"),
)));
}
let firewall_result = firewall::ensure_offline_outbound_block(&offline_sid_str, log);
if let Err(err) = firewall_result {
if extract_setup_failure(&err).is_some() {
return Err(err);
}
return Err(anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperFirewallRuleCreateOrAddFailed,
format!("ensure offline outbound block failed: {err}"),
)));
}
install_wfp_filters(
&payload.codex_home,
&payload.offline_username,
payload.otel.as_ref(),
|message| {
let _ = log_line(log, message);
},
);
configure_offline_sandbox_network(payload, &offline_sid_str, log)?;
}
// Deny-read ACEs must be present before the sandboxed command starts. Apply
@@ -865,24 +986,7 @@ fn run_setup_full(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Res
}
}
lock_sandbox_dir(
&sandbox_bin_dir(&payload.codex_home),
&payload.real_user,
&sandbox_group_sid,
GRANT_ACCESS,
FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
log,
)
.map_err(|err| {
anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperSandboxLockFailed,
format!(
"lock sandbox bin dir {} failed: {err}",
sandbox_bin_dir(&payload.codex_home).display()
),
))
})?;
lock_sandbox_bin_dir(payload, &sandbox_group_sid, log)?;
if refresh_only {
log_line(
@@ -895,46 +999,7 @@ fn run_setup_full(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Res
)?;
}
if !refresh_only {
lock_sandbox_dir(
&sandbox_dir(&payload.codex_home),
&payload.real_user,
&sandbox_group_sid,
GRANT_ACCESS,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE,
log,
)
.map_err(|err| {
anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperSandboxLockFailed,
format!(
"lock sandbox dir {} failed: {err}",
sandbox_dir(&payload.codex_home).display()
),
))
})?;
lock_sandbox_dir(
&sandbox_secrets_dir(&payload.codex_home),
&payload.real_user,
&sandbox_group_sid,
DENY_ACCESS,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE,
log,
)
.map_err(|err| {
anyhow::Error::new(SetupFailure::new(
SetupErrorCode::HelperSandboxLockFailed,
format!(
"lock sandbox secrets dir {} failed: {err}",
sandbox_secrets_dir(&payload.codex_home).display()
),
))
})?;
let legacy_users = sandbox_dir(&payload.codex_home).join("sandbox_users.json");
if legacy_users.exists() {
let _ = std::fs::remove_file(&legacy_users);
}
lock_persistent_sandbox_dirs(payload, &sandbox_group_sid, log)?;
}
unsafe {
@@ -986,6 +1051,15 @@ mod tests {
assert_eq!(payload.otel, None);
}
#[test]
fn payload_accepts_provision_only_mode() {
let mut payload = payload_json();
payload["mode"] = json!("provision-only");
let payload: Payload = serde_json::from_value(payload).expect("payload");
assert_eq!(payload.mode, super::SetupMode::ProvisionOnly);
}
#[test]
fn payload_accepts_otel_settings() {
let mut payload = payload_json();

View File

@@ -241,6 +241,8 @@ pub use setup::SandboxSetupRequest;
#[cfg(target_os = "windows")]
pub use setup::SetupRootOverrides;
#[cfg(target_os = "windows")]
pub use setup::run_elevated_provisioning_setup;
#[cfg(target_os = "windows")]
pub use setup::run_elevated_setup;
#[cfg(target_os = "windows")]
pub use setup::run_setup_refresh;

View File

@@ -203,6 +203,7 @@ fn run_setup_refresh_inner(
allow_local_binding: offline_proxy_settings.allow_local_binding,
otel: None,
real_user: std::env::var("USERNAME").unwrap_or_else(|_| "Administrators".to_string()),
mode: SetupMode::Full,
refresh_only: true,
};
let json = serde_json::to_vec(&payload)?;
@@ -496,10 +497,18 @@ struct ElevationPayload {
allow_local_binding: bool,
otel: Option<codex_otel::StatsigMetricsSettings>,
real_user: String,
mode: SetupMode,
#[serde(default)]
refresh_only: bool,
}
#[derive(Clone, Copy, Serialize)]
#[serde(rename_all = "kebab-case")]
enum SetupMode {
Full,
ProvisionOnly,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct OfflineProxySettings {
pub proxy_ports: Vec<u16>,
@@ -808,6 +817,7 @@ pub fn run_elevated_setup(
allow_local_binding: offline_proxy_settings.allow_local_binding,
real_user: std::env::var("USERNAME").unwrap_or_else(|_| "Administrators".to_string()),
otel: codex_otel::global_statsig_metrics_settings(),
mode: SetupMode::Full,
refresh_only: false,
};
let needs_elevation = !is_elevated().map_err(|err| {
@@ -819,6 +829,45 @@ pub fn run_elevated_setup(
run_setup_exe(&payload, needs_elevation, request.codex_home)
}
pub fn run_elevated_provisioning_setup(codex_home: &Path, real_user: &str) -> Result<()> {
let sbx_dir = sandbox_dir(codex_home);
std::fs::create_dir_all(&sbx_dir).map_err(|err| {
failure(
SetupErrorCode::OrchestratorSandboxDirCreateFailed,
format!("failed to create sandbox dir {}: {err}", sbx_dir.display()),
)
})?;
if !is_elevated().map_err(|err| {
failure(
SetupErrorCode::OrchestratorElevationCheckFailed,
format!("failed to determine elevation state: {err}"),
)
})? {
return Err(failure(
SetupErrorCode::OrchestratorElevationRequired,
"sandbox provisioning setup must be run from an elevated process",
));
}
let payload = ElevationPayload {
version: SETUP_VERSION,
offline_username: OFFLINE_USERNAME.to_string(),
online_username: ONLINE_USERNAME.to_string(),
codex_home: codex_home.to_path_buf(),
command_cwd: codex_home.to_path_buf(),
read_roots: Vec::new(),
write_roots: Vec::new(),
deny_read_paths: Vec::new(),
deny_write_paths: Vec::new(),
proxy_ports: Vec::new(),
allow_local_binding: false,
otel: codex_otel::global_statsig_metrics_settings(),
real_user: real_user.to_string(),
mode: SetupMode::ProvisionOnly,
refresh_only: false,
};
run_setup_exe(&payload, /*needs_elevation*/ false, codex_home)
}
fn build_payload_roots(
request: &SandboxSetupRequest<'_>,
overrides: &SetupRootOverrides,

View File

@@ -19,6 +19,8 @@ pub enum SetupErrorCode {
OrchestratorSandboxDirCreateFailed,
/// Failed to determine whether the current process is elevated.
OrchestratorElevationCheckFailed,
/// The setup command requires an already elevated process.
OrchestratorElevationRequired,
/// Failed to serialize the elevation payload before launching the helper.
OrchestratorPayloadSerializeFailed,
/// Failed to launch the setup helper process (spawn or ShellExecuteExW).
@@ -75,6 +77,7 @@ impl SetupErrorCode {
match self {
Self::OrchestratorSandboxDirCreateFailed => "orchestrator_sandbox_dir_create_failed",
Self::OrchestratorElevationCheckFailed => "orchestrator_elevation_check_failed",
Self::OrchestratorElevationRequired => "orchestrator_elevation_required",
Self::OrchestratorPayloadSerializeFailed => "orchestrator_payload_serialize_failed",
Self::OrchestratorHelperLaunchFailed => "orchestrator_helper_launch_failed",
Self::OrchestratorHelperLaunchCanceled => "orchestrator_helper_launch_canceled",