mirror of
https://github.com/openai/codex.git
synced 2026-05-15 08:42:34 +00:00
## Why The app-server request path had grown around a large `CodexMessageProcessor` plus separate API wrapper/helper modules. That made the dependency graph hard to see and forced unrelated request families to share broad processor state. This PR makes the split mechanical and command-prefix oriented so request families own only the dependencies they use. ## What changed - Replaced `CodexMessageProcessor` with command-prefix request processors under `app-server/src/request_processors/`. - Removed the old config, device-key, external-agent-config, and fs API wrapper files by moving their API handling into processors. - Split apps, plugins, marketplace, catalog, account, MCP, command exec, fs, git, feedback, thread, turn, thread goals, and Windows sandbox handling into dedicated processors. - Kept shared lifecycle, summary conversion, token usage replay, and shared error mapping only where multiple processors use them; single-use helpers were inlined into their owning processor. - Removed the fallback processor path and moved processor tests to `_tests` files. ## Validation - `cargo test -p codex-app-server` - `cargo check -p codex-app-server` - `just fix -p codex-app-server`
201 lines
6.3 KiB
Rust
201 lines
6.3 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::ExecutorFileSystem;
|
|
use codex_exec_server::RemoveOptions;
|
|
use std::io;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct FsRequestProcessor {
|
|
file_system: Arc<dyn ExecutorFileSystem>,
|
|
fs_watch_manager: FsWatchManager,
|
|
}
|
|
|
|
impl FsRequestProcessor {
|
|
pub(crate) fn new(
|
|
file_system: Arc<dyn ExecutorFileSystem>,
|
|
fs_watch_manager: FsWatchManager,
|
|
) -> Self {
|
|
Self {
|
|
file_system,
|
|
fs_watch_manager,
|
|
}
|
|
}
|
|
|
|
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 bytes = self
|
|
.file_system
|
|
.read_file(¶ms.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}"
|
|
))
|
|
})?;
|
|
self.file_system
|
|
.write_file(¶ms.path, bytes, /*sandbox*/ None)
|
|
.await
|
|
.map_err(map_fs_error)?;
|
|
Ok(FsWriteFileResponse {})
|
|
}
|
|
|
|
pub(crate) async fn create_directory(
|
|
&self,
|
|
params: FsCreateDirectoryParams,
|
|
) -> Result<FsCreateDirectoryResponse, JSONRPCErrorError> {
|
|
self.file_system
|
|
.create_directory(
|
|
¶ms.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 metadata = self
|
|
.file_system
|
|
.get_metadata(¶ms.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 entries = self
|
|
.file_system
|
|
.read_directory(¶ms.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> {
|
|
self.file_system
|
|
.remove(
|
|
¶ms.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> {
|
|
self.file_system
|
|
.copy(
|
|
¶ms.source_path,
|
|
¶ms.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.fs_watch_manager.watch(connection_id, params).await
|
|
}
|
|
|
|
pub(crate) async fn unwatch(
|
|
&self,
|
|
connection_id: ConnectionId,
|
|
params: FsUnwatchParams,
|
|
) -> Result<FsUnwatchResponse, JSONRPCErrorError> {
|
|
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())
|
|
}
|
|
}
|