1use project::Project;
2use settings::SettingsStore;
3use std::path::Path;
4use std::rc::Rc;
5
6use anyhow::Result;
7use gpui::{App, Entity, Task};
8
9use crate::acp_connection::AcpConnection;
10use crate::{AgentServer, AgentServerCommand, AllAgentServersSettings};
11use acp_thread::AgentConnection;
12
13#[derive(Clone)]
14pub struct Codex;
15
16impl AgentServer for Codex {
17 fn name(&self) -> &'static str {
18 "Codex"
19 }
20
21 fn empty_state_headline(&self) -> &'static str {
22 "Welcome to Codex"
23 }
24
25 fn empty_state_message(&self) -> &'static str {
26 "What can I help with?"
27 }
28
29 fn logo(&self) -> ui::IconName {
30 ui::IconName::AiOpenAi
31 }
32
33 fn connect(
34 &self,
35 _root_dir: &Path,
36 project: &Entity<Project>,
37 cx: &mut App,
38 ) -> Task<Result<Rc<dyn AgentConnection>>> {
39 let project = project.clone();
40 let server_name = self.name();
41 cx.spawn(async move |cx| {
42 let settings = cx.read_global(|settings: &SettingsStore, _| {
43 settings.get::<AllAgentServersSettings>(None).codex.clone()
44 })?;
45
46 let Some(command) =
47 AgentServerCommand::resolve("codex", &["mcp"], settings, &project, cx).await
48 else {
49 anyhow::bail!("Failed to find codex binary");
50 };
51
52 let conn = AcpConnection::stdio(server_name, command, cx).await?;
53 Ok(Rc::new(conn) as _)
54 })
55 }
56}
57
58#[cfg(test)]
59pub(crate) mod tests {
60 use super::*;
61 use crate::AgentServerCommand;
62 use std::path::Path;
63
64 crate::common_e2e_tests!(Codex, allow_option_id = "approve");
65
66 pub fn local_command() -> AgentServerCommand {
67 let cli_path = Path::new(env!("CARGO_MANIFEST_DIR"))
68 .join("../../../codex/codex-rs/target/debug/codex");
69
70 AgentServerCommand {
71 path: cli_path,
72 args: vec!["mcp".into()],
73 env: None,
74 }
75 }
76}