2025-09-29 12:19:09 -07:00
|
|
|
use std::num::NonZero;
|
|
|
|
|
use std::num::NonZeroUsize;
|
2025-10-02 18:19:13 -07:00
|
|
|
use std::path::Path;
|
2025-09-29 12:19:09 -07:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use std::sync::atomic::AtomicBool;
|
|
|
|
|
|
fix: remove mcp-types from app server protocol (#4537)
We continue the separation between `codex app-server` and `codex
mcp-server`.
In particular, we introduce a new crate, `codex-app-server-protocol`,
and migrate `codex-rs/protocol/src/mcp_protocol.rs` into it, renaming it
`codex-rs/app-server-protocol/src/protocol.rs`.
Because `ConversationId` was defined in `mcp_protocol.rs`, we move it
into its own file, `codex-rs/protocol/src/conversation_id.rs`, and
because it is referenced in a ton of places, we have to touch a lot of
files as part of this PR.
We also decide to get away from proper JSON-RPC 2.0 semantics, so we
also introduce `codex-rs/app-server-protocol/src/jsonrpc_lite.rs`, which
is basically the same `JSONRPCMessage` type defined in `mcp-types`
except with all of the `"jsonrpc": "2.0"` removed.
Getting rid of `"jsonrpc": "2.0"` makes our serialization logic
considerably simpler, as we can lean heavier on serde to serialize
directly into the wire format that we use now.
2025-09-30 19:16:26 -07:00
|
|
|
use codex_app_server_protocol::FuzzyFileSearchResult;
|
2025-09-29 12:19:09 -07:00
|
|
|
use codex_file_search as file_search;
|
|
|
|
|
use tokio::task::JoinSet;
|
|
|
|
|
use tracing::warn;
|
|
|
|
|
|
|
|
|
|
const LIMIT_PER_ROOT: usize = 50;
|
|
|
|
|
const MAX_THREADS: usize = 12;
|
|
|
|
|
const COMPUTE_INDICES: bool = true;
|
|
|
|
|
|
|
|
|
|
pub(crate) async fn run_fuzzy_file_search(
|
|
|
|
|
query: String,
|
|
|
|
|
roots: Vec<String>,
|
|
|
|
|
cancellation_flag: Arc<AtomicBool>,
|
|
|
|
|
) -> Vec<FuzzyFileSearchResult> {
|
|
|
|
|
#[expect(clippy::expect_used)]
|
|
|
|
|
let limit_per_root =
|
|
|
|
|
NonZero::new(LIMIT_PER_ROOT).expect("LIMIT_PER_ROOT should be a valid non-zero usize");
|
|
|
|
|
|
|
|
|
|
let cores = std::thread::available_parallelism()
|
|
|
|
|
.map(std::num::NonZero::get)
|
|
|
|
|
.unwrap_or(1);
|
|
|
|
|
let threads = cores.min(MAX_THREADS);
|
|
|
|
|
let threads_per_root = (threads / roots.len()).max(1);
|
|
|
|
|
let threads = NonZero::new(threads_per_root).unwrap_or(NonZeroUsize::MIN);
|
|
|
|
|
|
|
|
|
|
let mut files: Vec<FuzzyFileSearchResult> = Vec::new();
|
|
|
|
|
let mut join_set = JoinSet::new();
|
|
|
|
|
|
|
|
|
|
for root in roots {
|
|
|
|
|
let search_dir = PathBuf::from(&root);
|
|
|
|
|
let query = query.clone();
|
|
|
|
|
let cancel_flag = cancellation_flag.clone();
|
|
|
|
|
join_set.spawn_blocking(move || {
|
|
|
|
|
match file_search::run(
|
|
|
|
|
query.as_str(),
|
|
|
|
|
limit_per_root,
|
|
|
|
|
&search_dir,
|
|
|
|
|
Vec::new(),
|
|
|
|
|
threads,
|
|
|
|
|
cancel_flag,
|
|
|
|
|
COMPUTE_INDICES,
|
2025-10-23 10:04:40 -07:00
|
|
|
true,
|
2025-09-29 12:19:09 -07:00
|
|
|
) {
|
|
|
|
|
Ok(res) => Ok((root, res)),
|
|
|
|
|
Err(err) => Err((root, err)),
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while let Some(res) = join_set.join_next().await {
|
|
|
|
|
match res {
|
|
|
|
|
Ok(Ok((root, res))) => {
|
|
|
|
|
for m in res.matches {
|
2025-10-02 18:19:13 -07:00
|
|
|
let path = m.path;
|
|
|
|
|
//TODO(shijie): Move file name generation to file_search lib.
|
|
|
|
|
let file_name = Path::new(&path)
|
|
|
|
|
.file_name()
|
|
|
|
|
.map(|name| name.to_string_lossy().into_owned())
|
|
|
|
|
.unwrap_or_else(|| path.clone());
|
2025-09-29 12:19:09 -07:00
|
|
|
let result = FuzzyFileSearchResult {
|
|
|
|
|
root: root.clone(),
|
2025-10-02 18:19:13 -07:00
|
|
|
path,
|
|
|
|
|
file_name,
|
2025-09-29 12:19:09 -07:00
|
|
|
score: m.score,
|
|
|
|
|
indices: m.indices,
|
|
|
|
|
};
|
|
|
|
|
files.push(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(Err((root, err))) => {
|
|
|
|
|
warn!("fuzzy-file-search in dir '{root}' failed: {err}");
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
warn!("fuzzy-file-search join_next failed: {err}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
files.sort_by(file_search::cmp_by_score_desc_then_path_asc::<
|
|
|
|
|
FuzzyFileSearchResult,
|
|
|
|
|
_,
|
|
|
|
|
_,
|
|
|
|
|
>(|f| f.score, |f| f.path.as_str()));
|
|
|
|
|
|
|
|
|
|
files
|
|
|
|
|
}
|