Skip to content

Commit e5694c0

Browse files
milispclaude
andcommitted
refactor: modularize codex client and add MCP support
- Split monolithic codex_client.rs into organized modules (client, command_builder, event_handler, process_manager) - Add MCP (Model Context Protocol) integration for enhanced AI capabilities - Improve UI components with better state management and user experience enhancements - Update configuration handling and provider management - Enhance file tree and chat interface functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 5cfdf64 commit e5694c0

27 files changed

+880
-685
lines changed

src-tauri/src/codex_client.rs

Lines changed: 0 additions & 434 deletions
This file was deleted.
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
use anyhow::Result;
2+
use serde_json;
3+
use tauri::AppHandle;
4+
use uuid::Uuid;
5+
6+
use crate::protocol::{CodexConfig, InputItem, Op, Submission};
7+
8+
use super::{CommandBuilder, ProcessManager, EventHandler};
9+
10+
pub struct CodexClient {
11+
#[allow(dead_code)]
12+
app: AppHandle,
13+
session_id: String,
14+
process_manager: ProcessManager,
15+
#[allow(dead_code)]
16+
config: CodexConfig,
17+
}
18+
19+
impl CodexClient {
20+
pub async fn new(app: &AppHandle, session_id: String, config: CodexConfig) -> Result<Self> {
21+
log::debug!(
22+
"Creating CodexClient for session and config: {} {:?}",
23+
session_id,
24+
config
25+
);
26+
27+
// Build the command and environment variables
28+
let (cmd, env_vars) = CommandBuilder::build_command(&config).await?;
29+
30+
// Start the process
31+
let mut process_manager = ProcessManager::start_process(cmd, env_vars, &config).await?;
32+
33+
// Set up event handlers for stdout and stderr
34+
if let Some(process) = &mut process_manager.process {
35+
let stdout = process.stdout.take().expect("Failed to open stdout");
36+
let stderr = process.stderr.take().expect("Failed to open stderr");
37+
38+
EventHandler::start_stdout_handler(app.clone(), stdout, session_id.clone());
39+
EventHandler::start_stderr_handler(stderr, session_id.clone());
40+
}
41+
42+
let client = Self {
43+
app: app.clone(),
44+
session_id,
45+
process_manager,
46+
config: config.clone(),
47+
};
48+
49+
Ok(client)
50+
}
51+
52+
async fn send_submission(&self, submission: Submission) -> Result<()> {
53+
let json = serde_json::to_string(&submission)?;
54+
log::debug!("📤 Sending JSON to codex: {}", json);
55+
self.process_manager.send_to_stdin(json)?;
56+
Ok(())
57+
}
58+
59+
pub async fn send_user_input(&self, message: String) -> Result<()> {
60+
let submission = Submission {
61+
id: Uuid::new_v4().to_string(),
62+
op: Op::UserInput {
63+
items: vec![InputItem::Text { text: message }],
64+
},
65+
};
66+
67+
self.send_submission(submission).await
68+
}
69+
70+
pub async fn send_user_input_with_media(
71+
&self,
72+
message: String,
73+
media_paths: Vec<String>,
74+
) -> Result<()> {
75+
log::debug!("🎯 [CodexClient] send_user_input_with_media called:");
76+
log::debug!(" 💬 message: {}", message);
77+
log::debug!(" 📸 media_paths: {:?}", media_paths);
78+
log::debug!(" 📊 media_paths count: {}", media_paths.len());
79+
80+
let mut items = vec![InputItem::Text { text: message }];
81+
82+
// Add media files as LocalImage items - codex will convert to base64 automatically
83+
for path in media_paths {
84+
let path_buf = std::path::PathBuf::from(path.clone());
85+
log::debug!(" 🔗 Adding local image path: {}", path);
86+
items.push(InputItem::LocalImage { path: path_buf });
87+
}
88+
89+
log::debug!(" 📦 Total items in submission: {}", items.len());
90+
91+
let submission = Submission {
92+
id: Uuid::new_v4().to_string(),
93+
op: Op::UserInput { items },
94+
};
95+
96+
log::debug!(" 🚀 Sending submission to codex");
97+
self.send_submission(submission).await
98+
}
99+
100+
pub async fn send_exec_approval(&self, approval_id: String, approved: bool) -> Result<()> {
101+
let decision = if approved { "approved" } else { "denied" }.to_string();
102+
103+
let submission = Submission {
104+
id: Uuid::new_v4().to_string(),
105+
op: Op::ExecApproval {
106+
id: approval_id,
107+
decision,
108+
},
109+
};
110+
111+
self.send_submission(submission).await
112+
}
113+
114+
#[allow(dead_code)]
115+
pub async fn send_patch_approval(&self, approval_id: String, approved: bool) -> Result<()> {
116+
let decision = if approved { "approved" } else { "denied" }.to_string();
117+
118+
let submission = Submission {
119+
id: Uuid::new_v4().to_string(),
120+
op: Op::PatchApproval {
121+
id: approval_id,
122+
decision,
123+
},
124+
};
125+
126+
self.send_submission(submission).await
127+
}
128+
129+
pub async fn send_apply_patch_approval(&self, approval_id: String, approved: bool) -> Result<()> {
130+
let decision = if approved { "approved" } else { "denied" }.to_string();
131+
132+
let submission = Submission {
133+
id: Uuid::new_v4().to_string(),
134+
op: Op::PatchApproval {
135+
id: approval_id,
136+
decision,
137+
},
138+
};
139+
140+
self.send_submission(submission).await
141+
}
142+
143+
pub async fn interrupt(&self) -> Result<()> {
144+
let submission = Submission {
145+
id: Uuid::new_v4().to_string(),
146+
op: Op::Interrupt,
147+
};
148+
149+
self.send_submission(submission).await
150+
}
151+
152+
pub async fn close_session(&mut self) -> Result<()> {
153+
log::debug!("Closing session: {}", self.session_id);
154+
155+
// Send shutdown command to codex (graceful shutdown)
156+
let submission = Submission {
157+
id: Uuid::new_v4().to_string(),
158+
op: Op::Shutdown,
159+
};
160+
161+
if let Err(e) = self.send_submission(submission).await {
162+
log::error!("Failed to send shutdown command: {}", e);
163+
}
164+
165+
// Terminate the process
166+
self.process_manager.terminate().await?;
167+
168+
log::debug!("Session {} closed", self.session_id);
169+
Ok(())
170+
}
171+
172+
#[allow(dead_code)]
173+
pub async fn shutdown(&mut self) -> Result<()> {
174+
self.close_session().await
175+
}
176+
177+
#[allow(dead_code)]
178+
pub fn is_active(&self) -> bool {
179+
self.process_manager.is_active()
180+
}
181+
}

0 commit comments

Comments
 (0)