mirror of
https://github.com/openai/codex.git
synced 2026-09-10 20:26:47 +00:00
## Why We're moving exec-server to use PathUri for its internal path representations. ## What Move `ExecutorFileSystem` APIs to use `PathUri` instead of `AbsolutePathBuf`. Future changes will convert higher-level parts of exec-server.
221 lines
7.4 KiB
Rust
221 lines
7.4 KiB
Rust
use crate::error_code::internal_error;
|
|
use crate::error_code::invalid_request;
|
|
use crate::fs_watch::FsWatchManager;
|
|
use crate::outgoing_message::ConnectionId;
|
|
use base64::Engine;
|
|
use base64::engine::general_purpose::STANDARD;
|
|
use codex_app_server_protocol::FsCopyParams;
|
|
use codex_app_server_protocol::FsCopyResponse;
|
|
use codex_app_server_protocol::FsCreateDirectoryParams;
|
|
use codex_app_server_protocol::FsCreateDirectoryResponse;
|
|
use codex_app_server_protocol::FsGetMetadataParams;
|
|
use codex_app_server_protocol::FsGetMetadataResponse;
|
|
use codex_app_server_protocol::FsReadDirectoryEntry;
|
|
use codex_app_server_protocol::FsReadDirectoryParams;
|
|
use codex_app_server_protocol::FsReadDirectoryResponse;
|
|
use codex_app_server_protocol::FsReadFileParams;
|
|
use codex_app_server_protocol::FsReadFileResponse;
|
|
use codex_app_server_protocol::FsRemoveParams;
|
|
use codex_app_server_protocol::FsRemoveResponse;
|
|
use codex_app_server_protocol::FsUnwatchParams;
|
|
use codex_app_server_protocol::FsUnwatchResponse;
|
|
use codex_app_server_protocol::FsWatchParams;
|
|
use codex_app_server_protocol::FsWatchResponse;
|
|
use codex_app_server_protocol::FsWriteFileParams;
|
|
use codex_app_server_protocol::FsWriteFileResponse;
|
|
use codex_app_server_protocol::JSONRPCErrorError;
|
|
use codex_exec_server::CopyOptions;
|
|
use codex_exec_server::CreateDirectoryOptions;
|
|
use codex_exec_server::EnvironmentManager;
|
|
use codex_exec_server::ExecutorFileSystem;
|
|
use codex_exec_server::RemoveOptions;
|
|
use codex_utils_path_uri::PathUri;
|
|
use std::io;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct FsRequestProcessor {
|
|
environment_manager: Arc<EnvironmentManager>,
|
|
fs_watch_manager: FsWatchManager,
|
|
}
|
|
|
|
impl FsRequestProcessor {
|
|
pub(crate) fn new(
|
|
environment_manager: Arc<EnvironmentManager>,
|
|
fs_watch_manager: FsWatchManager,
|
|
) -> Self {
|
|
Self {
|
|
environment_manager,
|
|
fs_watch_manager,
|
|
}
|
|
}
|
|
|
|
fn file_system(&self) -> Result<Arc<dyn ExecutorFileSystem>, JSONRPCErrorError> {
|
|
self.environment_manager
|
|
.try_local_environment()
|
|
.map(|environment| environment.get_filesystem())
|
|
.ok_or_else(|| internal_error("local filesystem is not configured"))
|
|
}
|
|
|
|
pub(crate) async fn connection_closed(&self, connection_id: ConnectionId) {
|
|
self.fs_watch_manager.connection_closed(connection_id).await;
|
|
}
|
|
|
|
pub(crate) async fn read_file(
|
|
&self,
|
|
params: FsReadFileParams,
|
|
) -> Result<FsReadFileResponse, JSONRPCErrorError> {
|
|
let path = PathUri::from_abs_path(¶ms.path).map_err(map_fs_error)?;
|
|
let bytes = self
|
|
.file_system()?
|
|
.read_file(&path, /*sandbox*/ None)
|
|
.await
|
|
.map_err(map_fs_error)?;
|
|
Ok(FsReadFileResponse {
|
|
data_base64: STANDARD.encode(bytes),
|
|
})
|
|
}
|
|
|
|
pub(crate) async fn write_file(
|
|
&self,
|
|
params: FsWriteFileParams,
|
|
) -> Result<FsWriteFileResponse, JSONRPCErrorError> {
|
|
let bytes = STANDARD.decode(params.data_base64).map_err(|err| {
|
|
invalid_request(format!(
|
|
"fs/writeFile requires valid base64 dataBase64: {err}"
|
|
))
|
|
})?;
|
|
let path = PathUri::from_abs_path(¶ms.path).map_err(map_fs_error)?;
|
|
self.file_system()?
|
|
.write_file(&path, bytes, /*sandbox*/ None)
|
|
.await
|
|
.map_err(map_fs_error)?;
|
|
Ok(FsWriteFileResponse {})
|
|
}
|
|
|
|
pub(crate) async fn create_directory(
|
|
&self,
|
|
params: FsCreateDirectoryParams,
|
|
) -> Result<FsCreateDirectoryResponse, JSONRPCErrorError> {
|
|
let path = PathUri::from_abs_path(¶ms.path).map_err(map_fs_error)?;
|
|
self.file_system()?
|
|
.create_directory(
|
|
&path,
|
|
CreateDirectoryOptions {
|
|
recursive: params.recursive.unwrap_or(true),
|
|
},
|
|
/*sandbox*/ None,
|
|
)
|
|
.await
|
|
.map_err(map_fs_error)?;
|
|
Ok(FsCreateDirectoryResponse {})
|
|
}
|
|
|
|
pub(crate) async fn get_metadata(
|
|
&self,
|
|
params: FsGetMetadataParams,
|
|
) -> Result<FsGetMetadataResponse, JSONRPCErrorError> {
|
|
let path = PathUri::from_abs_path(¶ms.path).map_err(map_fs_error)?;
|
|
let metadata = self
|
|
.file_system()?
|
|
.get_metadata(&path, /*sandbox*/ None)
|
|
.await
|
|
.map_err(map_fs_error)?;
|
|
Ok(FsGetMetadataResponse {
|
|
is_directory: metadata.is_directory,
|
|
is_file: metadata.is_file,
|
|
is_symlink: metadata.is_symlink,
|
|
created_at_ms: metadata.created_at_ms,
|
|
modified_at_ms: metadata.modified_at_ms,
|
|
})
|
|
}
|
|
|
|
pub(crate) async fn read_directory(
|
|
&self,
|
|
params: FsReadDirectoryParams,
|
|
) -> Result<FsReadDirectoryResponse, JSONRPCErrorError> {
|
|
let path = PathUri::from_abs_path(¶ms.path).map_err(map_fs_error)?;
|
|
let entries = self
|
|
.file_system()?
|
|
.read_directory(&path, /*sandbox*/ None)
|
|
.await
|
|
.map_err(map_fs_error)?;
|
|
Ok(FsReadDirectoryResponse {
|
|
entries: entries
|
|
.into_iter()
|
|
.map(|entry| FsReadDirectoryEntry {
|
|
file_name: entry.file_name,
|
|
is_directory: entry.is_directory,
|
|
is_file: entry.is_file,
|
|
})
|
|
.collect(),
|
|
})
|
|
}
|
|
|
|
pub(crate) async fn remove(
|
|
&self,
|
|
params: FsRemoveParams,
|
|
) -> Result<FsRemoveResponse, JSONRPCErrorError> {
|
|
let path = PathUri::from_abs_path(¶ms.path).map_err(map_fs_error)?;
|
|
self.file_system()?
|
|
.remove(
|
|
&path,
|
|
RemoveOptions {
|
|
recursive: params.recursive.unwrap_or(true),
|
|
force: params.force.unwrap_or(true),
|
|
},
|
|
/*sandbox*/ None,
|
|
)
|
|
.await
|
|
.map_err(map_fs_error)?;
|
|
Ok(FsRemoveResponse {})
|
|
}
|
|
|
|
pub(crate) async fn copy(
|
|
&self,
|
|
params: FsCopyParams,
|
|
) -> Result<FsCopyResponse, JSONRPCErrorError> {
|
|
let source_path = PathUri::from_abs_path(¶ms.source_path).map_err(map_fs_error)?;
|
|
let destination_path =
|
|
PathUri::from_abs_path(¶ms.destination_path).map_err(map_fs_error)?;
|
|
self.file_system()?
|
|
.copy(
|
|
&source_path,
|
|
&destination_path,
|
|
CopyOptions {
|
|
recursive: params.recursive,
|
|
},
|
|
/*sandbox*/ None,
|
|
)
|
|
.await
|
|
.map_err(map_fs_error)?;
|
|
Ok(FsCopyResponse {})
|
|
}
|
|
|
|
pub(crate) async fn watch(
|
|
&self,
|
|
connection_id: ConnectionId,
|
|
params: FsWatchParams,
|
|
) -> Result<FsWatchResponse, JSONRPCErrorError> {
|
|
self.file_system()?;
|
|
self.fs_watch_manager.watch(connection_id, params).await
|
|
}
|
|
|
|
pub(crate) async fn unwatch(
|
|
&self,
|
|
connection_id: ConnectionId,
|
|
params: FsUnwatchParams,
|
|
) -> Result<FsUnwatchResponse, JSONRPCErrorError> {
|
|
self.file_system()?;
|
|
self.fs_watch_manager.unwatch(connection_id, params).await
|
|
}
|
|
}
|
|
|
|
fn map_fs_error(err: io::Error) -> JSONRPCErrorError {
|
|
if err.kind() == io::ErrorKind::InvalidInput {
|
|
invalid_request(err.to_string())
|
|
} else {
|
|
internal_error(err.to_string())
|
|
}
|
|
}
|