mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Apply sandbox intent inside remote exec servers (#29113)
## Why PR #29108 lets the orchestrator send sandbox intent with `process/start` without wrapping the command for its own operating system. This PR completes that boundary by making the executor interpret and enforce the intent using its own filesystem paths and sandbox implementation. For example, a macOS TUI targeting a Linux devbox sends `/bin/bash -lc pwd`. The Linux executor turns that into its own `codex-linux-sandbox ... /bin/bash -lc pwd` launch. ## What changes - Keep `process/start` unchanged when no sandbox intent is present. - Convert sandbox `PathUri` values into native paths on the executor. - Bind symbolic `:workspace_roots` permissions to the executor's native sandbox cwd. - Select the sandbox implementation on the executor and wrap the original command immediately before spawning it. - Reject sandbox-required execution before spawning when the executor cannot enforce the intent. - Pass exec-server runtime paths into process creation so Linux can locate `codex-linux-sandbox`. The boundary is therefore: ```text orchestrator executor original argv + sandbox intent -> select and enforce local sandbox ``` This PR intentionally treats a denied remote command as an ordinary command failure. Draft follow-up #29424 carries a semantic `sandboxDenied` result back to unified exec for the existing approval and retry flow. ## Platform scope Linux and macOS use their existing direct-spawn sandbox transforms. Windows sandboxed remote process launch is intentionally unsupported in this PR. The current Windows direct-spawn wrapper does not correctly preserve arbitrary argv, TTY behavior, or pass the full child environment out of band. The executor rejects the request instead of running it incorrectly or unsandboxed. ## Known follow-ups - The transported permission profile can still contain orchestrator-materialized helper or explicit paths. A `TODO(jif)` marks where the executor boundary should receive pre-host-materialization permission intent. - The sandbox wrapper currently replaces a requested custom inner `arg0`. A `TODO(jif)` marks where this must be preserved or rejected explicitly. - Draft PR #29424 contains the deferred sandbox-denial classification and approval/retry behavior. ## Rollout assumption This executor-sandbox stack is unreleased and its client and executor are expected to move together. This PR does not add mixed-version negotiation with older exec servers.
This commit is contained in:
@@ -496,11 +496,7 @@ impl<'a> SandboxAttempt<'a> {
|
||||
exec_request.exec_server_sandbox = Some(FileSystemSandboxContext {
|
||||
permissions: exec_server_permissions.into(),
|
||||
cwd: Some(exec_request.windows_sandbox_policy_cwd.clone()),
|
||||
workspace_roots: self
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(PathUri::from_abs_path)
|
||||
.collect(),
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: self.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
||||
use_legacy_landlock: self.use_legacy_landlock,
|
||||
|
||||
@@ -258,7 +258,7 @@ fn exec_server_env_keeps_command_native_and_carries_sandbox_context() {
|
||||
Some(codex_exec_server::FileSystemSandboxContext {
|
||||
permissions: exec_server_permissions.into(),
|
||||
cwd: Some(cwd_uri),
|
||||
workspace_roots: vec![PathUri::from_abs_path(&cwd)],
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
use_legacy_landlock: false,
|
||||
|
||||
@@ -488,7 +488,9 @@ impl Environment {
|
||||
exec_server_url: None,
|
||||
remote_client: None,
|
||||
startup_task: Arc::new(Mutex::new(None)),
|
||||
exec_backend: Arc::new(LocalProcess::default()),
|
||||
exec_backend: Arc::new(LocalProcess::with_local_runtime_paths(
|
||||
local_runtime_paths.clone(),
|
||||
)),
|
||||
filesystem: Arc::new(LocalFileSystem::with_runtime_paths(
|
||||
local_runtime_paths.clone(),
|
||||
)),
|
||||
@@ -1165,6 +1167,50 @@ mod tests {
|
||||
assert_eq!(response.process.process_id().as_str(), "default-env-proc");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn local_environment_passes_runtime_paths_to_exec_backend() {
|
||||
let environment = Environment::local(test_runtime_paths());
|
||||
#[cfg(unix)]
|
||||
let uri = "file://server/share/checkout";
|
||||
#[cfg(windows)]
|
||||
let uri = "file:///usr/local/checkout";
|
||||
let sandbox_cwd = PathUri::parse(uri).expect("non-native sandbox cwd URI");
|
||||
let source = sandbox_cwd
|
||||
.to_abs_path()
|
||||
.expect_err("sandbox cwd should not be native to this host");
|
||||
let sandbox = crate::FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||
codex_protocol::models::PermissionProfile::workspace_write(),
|
||||
sandbox_cwd.clone(),
|
||||
);
|
||||
|
||||
let result = environment
|
||||
.get_exec_backend()
|
||||
.start(crate::ExecParams {
|
||||
process_id: ProcessId::from("local-sandbox-proc"),
|
||||
argv: vec!["true".to_string()],
|
||||
cwd: PathUri::from_path(std::env::current_dir().expect("read current dir"))
|
||||
.expect("cwd URI"),
|
||||
env_policy: None,
|
||||
env: Default::default(),
|
||||
tty: false,
|
||||
pipe_stdin: false,
|
||||
arg0: None,
|
||||
sandbox: Some(sandbox),
|
||||
enforce_managed_network: false,
|
||||
})
|
||||
.await;
|
||||
let Err(err) = result else {
|
||||
panic!("sandbox cwd should be rejected after resolving runtime paths");
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
format!(
|
||||
"exec-server rejected request (-32602): sandbox cwd URI `{sandbox_cwd}` is not valid on this exec-server host: {source}"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_environment_rejects_sandboxed_filesystem_without_runtime_paths() {
|
||||
let environment = Environment::default_for_tests();
|
||||
|
||||
@@ -16,6 +16,7 @@ mod noise_channel;
|
||||
mod noise_relay;
|
||||
mod process;
|
||||
mod process_id;
|
||||
mod process_sandbox;
|
||||
mod protocol;
|
||||
mod regular_file;
|
||||
mod relay;
|
||||
|
||||
@@ -26,9 +26,11 @@ use crate::ExecProcessEvent;
|
||||
use crate::ExecProcessEventReceiver;
|
||||
use crate::ExecProcessFuture;
|
||||
use crate::ExecServerError;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ProcessId;
|
||||
use crate::StartedExecProcess;
|
||||
use crate::process::ExecProcessEventLog;
|
||||
use crate::process_sandbox::prepare_exec_request;
|
||||
use crate::protocol::EXEC_CLOSED_METHOD;
|
||||
use crate::protocol::ExecClosedNotification;
|
||||
use crate::protocol::ExecEnvPolicy;
|
||||
@@ -133,6 +135,7 @@ struct Inner {
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct LocalProcess {
|
||||
inner: Arc<Inner>,
|
||||
runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
}
|
||||
|
||||
struct LocalExecProcess {
|
||||
@@ -144,20 +147,39 @@ struct LocalExecProcess {
|
||||
|
||||
impl Default for LocalProcess {
|
||||
fn default() -> Self {
|
||||
let (outgoing_tx, mut outgoing_rx) =
|
||||
mpsc::channel::<RpcServerOutboundMessage>(NOTIFICATION_CHANNEL_CAPACITY);
|
||||
tokio::spawn(async move { while outgoing_rx.recv().await.is_some() {} });
|
||||
Self::new(RpcNotificationSender::new(outgoing_tx))
|
||||
Self::with_discarded_notifications(/*runtime_paths*/ None)
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalProcess {
|
||||
pub(crate) fn new(notifications: RpcNotificationSender) -> Self {
|
||||
pub(crate) fn with_local_runtime_paths(runtime_paths: ExecServerRuntimePaths) -> Self {
|
||||
Self::with_discarded_notifications(Some(runtime_paths))
|
||||
}
|
||||
|
||||
fn with_discarded_notifications(runtime_paths: Option<ExecServerRuntimePaths>) -> Self {
|
||||
let (outgoing_tx, mut outgoing_rx) =
|
||||
mpsc::channel::<RpcServerOutboundMessage>(NOTIFICATION_CHANNEL_CAPACITY);
|
||||
tokio::spawn(async move { while outgoing_rx.recv().await.is_some() {} });
|
||||
Self::with_runtime_paths(RpcNotificationSender::new(outgoing_tx), runtime_paths)
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
notifications: RpcNotificationSender,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
) -> Self {
|
||||
Self::with_runtime_paths(notifications, Some(runtime_paths))
|
||||
}
|
||||
|
||||
fn with_runtime_paths(
|
||||
notifications: RpcNotificationSender,
|
||||
runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Inner {
|
||||
notifications: std::sync::RwLock::new(Some(notifications)),
|
||||
processes: Mutex::new(HashMap::new()),
|
||||
}),
|
||||
runtime_paths,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,16 +213,12 @@ impl LocalProcess {
|
||||
params: ExecParams,
|
||||
) -> Result<(ExecResponse, watch::Sender<u64>, ExecProcessEventLog), JSONRPCErrorError> {
|
||||
let process_id = params.process_id.clone();
|
||||
let (program, args) = params
|
||||
.argv
|
||||
let prepared =
|
||||
prepare_exec_request(¶ms, child_env(¶ms), self.runtime_paths.as_ref())?;
|
||||
let (program, args) = prepared
|
||||
.command
|
||||
.split_first()
|
||||
.ok_or_else(|| invalid_params("argv must not be empty".to_string()))?;
|
||||
let native_cwd = params.cwd.to_abs_path().map_err(|err| {
|
||||
invalid_params(format!(
|
||||
"cwd URI `{}` is not valid on this exec-server host: {err}",
|
||||
params.cwd
|
||||
))
|
||||
})?;
|
||||
|
||||
let start = Arc::new(ProcessStart);
|
||||
{
|
||||
@@ -216,14 +234,13 @@ impl LocalProcess {
|
||||
);
|
||||
}
|
||||
|
||||
let env = child_env(¶ms);
|
||||
let spawned_result = if params.tty {
|
||||
codex_utils_pty::spawn_pty_process(
|
||||
program,
|
||||
args,
|
||||
native_cwd.as_path(),
|
||||
&env,
|
||||
¶ms.arg0,
|
||||
prepared.cwd.as_path(),
|
||||
&prepared.env,
|
||||
&prepared.arg0,
|
||||
TerminalSize::default(),
|
||||
)
|
||||
.await
|
||||
@@ -231,18 +248,18 @@ impl LocalProcess {
|
||||
codex_utils_pty::spawn_pipe_process(
|
||||
program,
|
||||
args,
|
||||
native_cwd.as_path(),
|
||||
&env,
|
||||
¶ms.arg0,
|
||||
prepared.cwd.as_path(),
|
||||
&prepared.env,
|
||||
&prepared.arg0,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
codex_utils_pty::spawn_pipe_process_no_stdin(
|
||||
program,
|
||||
args,
|
||||
native_cwd.as_path(),
|
||||
&env,
|
||||
¶ms.arg0,
|
||||
prepared.cwd.as_path(),
|
||||
&prepared.env,
|
||||
&prepared.arg0,
|
||||
)
|
||||
.await
|
||||
};
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_sandboxing::SandboxCommand;
|
||||
use codex_sandboxing::SandboxDirectSpawnTransformRequest;
|
||||
use codex_sandboxing::SandboxManager;
|
||||
use codex_sandboxing::SandboxTransformRequest;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_sandboxing::SandboxablePreference;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::protocol::ExecParams;
|
||||
use crate::rpc::invalid_params;
|
||||
|
||||
pub(crate) struct PreparedExecRequest {
|
||||
pub(crate) command: Vec<String>,
|
||||
pub(crate) cwd: AbsolutePathBuf,
|
||||
pub(crate) env: HashMap<String, String>,
|
||||
pub(crate) arg0: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) fn prepare_exec_request(
|
||||
params: &ExecParams,
|
||||
env: HashMap<String, String>,
|
||||
runtime_paths: Option<&ExecServerRuntimePaths>,
|
||||
) -> Result<PreparedExecRequest, JSONRPCErrorError> {
|
||||
let Some(sandbox_context) = params.sandbox.as_ref() else {
|
||||
return Ok(PreparedExecRequest {
|
||||
command: params.argv.clone(),
|
||||
cwd: native_path(¶ms.cwd, "cwd")?,
|
||||
env,
|
||||
arg0: params.arg0.clone(),
|
||||
});
|
||||
};
|
||||
let runtime_paths = runtime_paths
|
||||
.ok_or_else(|| invalid_params("sandbox runtime paths are not configured".to_string()))?;
|
||||
// TODO(jif): Transport permissions before orchestrator-local paths are materialized,
|
||||
// then resolve executor-local helper and workspace paths here.
|
||||
let permissions: PermissionProfile = sandbox_context
|
||||
.permissions
|
||||
.clone()
|
||||
.try_into()
|
||||
.map_err(|err| invalid_params(format!("invalid sandbox permission path URI: {err}")))?;
|
||||
let sandbox_policy_cwd = sandbox_context.cwd.as_ref().unwrap_or(¶ms.cwd);
|
||||
let native_sandbox_policy_cwd = native_path(sandbox_policy_cwd, "sandbox cwd")?;
|
||||
let native_workspace_roots = sandbox_context
|
||||
.workspace_roots
|
||||
.iter()
|
||||
.map(|root| native_path(root, "sandbox workspace root"))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let workspace_roots = if native_workspace_roots.is_empty() {
|
||||
std::slice::from_ref(&native_sandbox_policy_cwd)
|
||||
} else {
|
||||
native_workspace_roots.as_slice()
|
||||
};
|
||||
let permissions = permissions.materialize_project_roots_with_workspace_roots(workspace_roots);
|
||||
let (file_system_policy, network_policy) = permissions.to_runtime_permissions();
|
||||
let sandbox_manager = SandboxManager::new();
|
||||
let sandbox = sandbox_manager.select_initial(
|
||||
&file_system_policy,
|
||||
network_policy,
|
||||
SandboxablePreference::Require,
|
||||
sandbox_context.windows_sandbox_level,
|
||||
params.enforce_managed_network,
|
||||
);
|
||||
match sandbox {
|
||||
SandboxType::None => {
|
||||
return Err(invalid_params(
|
||||
"sandbox intent cannot be enforced on this executor".to_string(),
|
||||
));
|
||||
}
|
||||
SandboxType::WindowsRestrictedToken => {
|
||||
// TODO(jif): Launch generic remote commands through the Windows sandbox session API
|
||||
// while preserving argv and TTY behavior and passing the child environment out of band.
|
||||
return Err(invalid_params(
|
||||
"sandboxed remote process launch is not supported on Windows".to_string(),
|
||||
));
|
||||
}
|
||||
SandboxType::MacosSeatbelt | SandboxType::LinuxSeccomp => {}
|
||||
}
|
||||
let (program, args) = params
|
||||
.argv
|
||||
.split_first()
|
||||
.ok_or_else(|| invalid_params("argv must not be empty".to_string()))?;
|
||||
let request = sandbox_manager
|
||||
.transform_for_direct_spawn(SandboxDirectSpawnTransformRequest {
|
||||
workspace_roots,
|
||||
transform: SandboxTransformRequest {
|
||||
// TODO(jif): Preserve params.arg0 for the inner command across the sandbox
|
||||
// wrapper, or reject sandboxed requests with a custom arg0.
|
||||
command: SandboxCommand {
|
||||
program: program.into(),
|
||||
args: args.to_vec(),
|
||||
cwd: params.cwd.clone(),
|
||||
env,
|
||||
additional_permissions: None,
|
||||
},
|
||||
permissions: &permissions,
|
||||
sandbox,
|
||||
enforce_managed_network: params.enforce_managed_network,
|
||||
environment_id: None,
|
||||
network: None,
|
||||
sandbox_policy_cwd,
|
||||
codex_linux_sandbox_exe: runtime_paths.codex_linux_sandbox_exe.as_deref(),
|
||||
use_legacy_landlock: sandbox_context.use_legacy_landlock,
|
||||
windows_sandbox_level: sandbox_context.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: sandbox_context.windows_sandbox_private_desktop,
|
||||
},
|
||||
})
|
||||
.map_err(|err| invalid_params(format!("failed to prepare process sandbox: {err}")))?;
|
||||
Ok(PreparedExecRequest {
|
||||
command: request.command,
|
||||
cwd: native_path(&request.cwd, "cwd")?,
|
||||
env: request.env,
|
||||
arg0: request.arg0,
|
||||
})
|
||||
}
|
||||
|
||||
fn native_path(path: &PathUri, label: &str) -> Result<AbsolutePathBuf, JSONRPCErrorError> {
|
||||
path.to_abs_path().map_err(|err| {
|
||||
invalid_params(format!(
|
||||
"{label} URI `{path}` is not valid on this exec-server host: {err}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "process_sandbox_tests.rs"]
|
||||
mod tests;
|
||||
@@ -0,0 +1,109 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(unix)]
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::prepare_exec_request;
|
||||
use crate::ExecParams;
|
||||
#[cfg(unix)]
|
||||
use crate::ExecServerRuntimePaths;
|
||||
#[cfg(unix)]
|
||||
use crate::FileSystemSandboxContext;
|
||||
use crate::ProcessId;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn sandbox_request_wraps_native_argv_on_executor() {
|
||||
let cwd: AbsolutePathBuf = std::env::current_dir()
|
||||
.expect("current directory")
|
||||
.try_into()
|
||||
.expect("absolute cwd");
|
||||
let cwd_uri = PathUri::from_abs_path(&cwd);
|
||||
let self_exe = std::env::current_exe().expect("current executable");
|
||||
let runtime_paths =
|
||||
ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths");
|
||||
let sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||
PermissionProfile::workspace_write(),
|
||||
cwd_uri.clone(),
|
||||
);
|
||||
let params = ExecParams {
|
||||
process_id: ProcessId::from("process-1"),
|
||||
argv: vec![
|
||||
"/bin/bash".to_string(),
|
||||
"-lc".to_string(),
|
||||
"pwd".to_string(),
|
||||
],
|
||||
cwd: cwd_uri,
|
||||
env_policy: None,
|
||||
env: HashMap::new(),
|
||||
tty: false,
|
||||
pipe_stdin: false,
|
||||
arg0: None,
|
||||
sandbox: Some(sandbox),
|
||||
enforce_managed_network: false,
|
||||
};
|
||||
|
||||
let prepared = prepare_exec_request(¶ms, HashMap::new(), Some(&runtime_paths))
|
||||
.expect("prepare sandboxed request");
|
||||
|
||||
assert_ne!(prepared.command, params.argv);
|
||||
assert_eq!(prepared.cwd, cwd);
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
assert_eq!(
|
||||
prepared.command.first(),
|
||||
Some(&runtime_paths.codex_self_exe.to_string_lossy().into_owned())
|
||||
);
|
||||
let permission_profile_json = prepared
|
||||
.command
|
||||
.iter()
|
||||
.position(|arg| arg == "--permission-profile")
|
||||
.and_then(|index| prepared.command.get(index + 1))
|
||||
.expect("sandbox wrapper permission profile");
|
||||
let permission_profile: PermissionProfile =
|
||||
serde_json::from_str(permission_profile_json).expect("permission profile JSON");
|
||||
assert_eq!(
|
||||
permission_profile,
|
||||
PermissionProfile::workspace_write()
|
||||
.materialize_project_roots_with_workspace_roots(std::slice::from_ref(&cwd))
|
||||
);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
assert_eq!(
|
||||
prepared.command.first().map(String::as_str),
|
||||
Some("/usr/bin/sandbox-exec")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_request_preserves_native_launch_fields() {
|
||||
let cwd: AbsolutePathBuf = std::env::current_dir()
|
||||
.expect("current directory")
|
||||
.try_into()
|
||||
.expect("absolute cwd");
|
||||
let cwd_uri = PathUri::from_abs_path(&cwd);
|
||||
let env = HashMap::from([("TEST_ENV".to_string(), "value".to_string())]);
|
||||
let params = ExecParams {
|
||||
process_id: ProcessId::from("process-1"),
|
||||
argv: vec!["echo".to_string(), "hello".to_string()],
|
||||
cwd: cwd_uri,
|
||||
env_policy: None,
|
||||
env: HashMap::new(),
|
||||
tty: false,
|
||||
pipe_stdin: false,
|
||||
arg0: Some("custom-arg0".to_string()),
|
||||
sandbox: None,
|
||||
enforce_managed_network: false,
|
||||
};
|
||||
|
||||
let prepared = prepare_exec_request(¶ms, env.clone(), /*runtime_paths*/ None)
|
||||
.expect("prepare native request");
|
||||
|
||||
assert_eq!(prepared.command, params.argv);
|
||||
assert_eq!(prepared.cwd, cwd);
|
||||
assert_eq!(prepared.env, env);
|
||||
assert_eq!(prepared.arg0, params.arg0);
|
||||
}
|
||||
@@ -66,6 +66,7 @@ pub(crate) struct ExecServerHandler {
|
||||
background_task_shutdown: CancellationToken,
|
||||
background_tasks: TaskTracker,
|
||||
file_system: FileSystemHandler,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
initialize_requested: AtomicBool,
|
||||
initialized: AtomicBool,
|
||||
}
|
||||
@@ -83,7 +84,8 @@ impl ExecServerHandler {
|
||||
active_body_stream_ids: Mutex::new(HashSet::new()),
|
||||
background_task_shutdown: CancellationToken::new(),
|
||||
background_tasks: TaskTracker::new(),
|
||||
file_system: FileSystemHandler::new(runtime_paths),
|
||||
file_system: FileSystemHandler::new(runtime_paths.clone()),
|
||||
runtime_paths,
|
||||
initialize_requested: AtomicBool::new(false),
|
||||
initialized: AtomicBool::new(false),
|
||||
}
|
||||
@@ -116,7 +118,11 @@ impl ExecServerHandler {
|
||||
|
||||
let session = match self
|
||||
.session_registry
|
||||
.attach(params.resume_session_id.clone(), self.notifications.clone())
|
||||
.attach(
|
||||
params.resume_session_id.clone(),
|
||||
self.notifications.clone(),
|
||||
self.runtime_paths.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(session) => session,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::local_process::LocalProcess;
|
||||
use crate::protocol::ExecParams;
|
||||
use crate::protocol::ExecResponse;
|
||||
@@ -19,9 +20,12 @@ pub(crate) struct ProcessHandler {
|
||||
}
|
||||
|
||||
impl ProcessHandler {
|
||||
pub(crate) fn new(notifications: RpcNotificationSender) -> Self {
|
||||
pub(crate) fn new(
|
||||
notifications: RpcNotificationSender,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
) -> Self {
|
||||
Self {
|
||||
process: LocalProcess::new(notifications),
|
||||
process: LocalProcess::new(notifications, runtime_paths),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::rpc::RpcNotificationSender;
|
||||
use crate::rpc::invalid_request;
|
||||
use crate::rpc::session_already_attached;
|
||||
@@ -60,6 +61,7 @@ impl SessionRegistry {
|
||||
self: &Arc<Self>,
|
||||
resume_session_id: Option<String>,
|
||||
notifications: RpcNotificationSender,
|
||||
runtime_paths: ExecServerRuntimePaths,
|
||||
) -> Result<SessionHandle, JSONRPCErrorError> {
|
||||
enum AttachOutcome {
|
||||
Attached(Arc<SessionEntry>),
|
||||
@@ -95,7 +97,7 @@ impl SessionRegistry {
|
||||
let session_id = Uuid::new_v4().to_string();
|
||||
let entry = Arc::new(SessionEntry::new(
|
||||
session_id.clone(),
|
||||
ProcessHandler::new(notifications),
|
||||
ProcessHandler::new(notifications, runtime_paths),
|
||||
connection_id,
|
||||
));
|
||||
sessions.insert(session_id, Arc::clone(&entry));
|
||||
|
||||
Reference in New Issue
Block a user