Skip to content

Commit 8f62fdb

Browse files
Peter vogelPeter vogel
authored andcommitted
Fix codex app-server spawn environment and timeout
1 parent 13cea5b commit 8f62fdb

2 files changed

Lines changed: 74 additions & 3 deletions

File tree

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ tauri = { version = "2", features = ["macos-private-api"] }
2222
tauri-plugin-opener = "2"
2323
serde = { version = "1", features = ["derive"] }
2424
serde_json = "1"
25-
tokio = { version = "1", features = ["io-util", "process", "rt", "sync"] }
25+
tokio = { version = "1", features = ["io-util", "process", "rt", "sync", "time"] }
2626
uuid = { version = "1", features = ["v4"] }
2727
tauri-plugin-dialog = "2"
2828
git2 = "0.20.3"

src-tauri/src/lib.rs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use tauri::{WebviewUrl, WebviewWindowBuilder};
1212
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
1313
use tokio::process::{Child, ChildStdin, Command};
1414
use tokio::sync::{Mutex, oneshot};
15+
use tokio::time::{timeout, Duration};
1516
use uuid::Uuid;
1617

1718
#[derive(Debug, Serialize, Deserialize, Clone)]
@@ -216,11 +217,24 @@ fn build_codex_command(entry: &WorkspaceEntry) -> Command {
216217
.filter(|value| !value.trim().is_empty())
217218
.unwrap_or_else(|| "codex".into());
218219
let mut command = Command::new(bin);
220+
// Cargo sets DYLD_* vars in dev runs on macOS so that the current binary can
221+
// locate its dependent dylibs. Those variables leak into child processes and
222+
// can break unrelated binaries (like `codex`) by forcing them to load the
223+
// wrong libraries. Strip them for the app-server child process.
224+
command.env_remove("DYLD_LIBRARY_PATH");
225+
command.env_remove("DYLD_FALLBACK_LIBRARY_PATH");
226+
command.env_remove("DYLD_FRAMEWORK_PATH");
227+
command.env_remove("DYLD_FALLBACK_FRAMEWORK_PATH");
228+
command.env_remove("DYLD_INSERT_LIBRARIES");
219229
if default_bin {
220230
let mut paths: Vec<String> = env::var("PATH")
221231
.unwrap_or_default()
222232
.split(':')
223233
.filter(|value| !value.is_empty())
234+
// `npm run` prepends a bunch of `node_modules/.bin` folders, which can
235+
// accidentally shadow the real `codex` install with an unrelated
236+
// script. We want the system `codex` binary here.
237+
.filter(|value| !value.contains("node_modules/.bin"))
224238
.map(|value| value.to_string())
225239
.collect();
226240
let mut extras = vec![
@@ -254,6 +268,12 @@ async fn spawn_workspace_session(
254268
entry: WorkspaceEntry,
255269
app_handle: AppHandle,
256270
) -> Result<Arc<WorkspaceSession>, String> {
271+
if cfg!(debug_assertions) {
272+
eprintln!(
273+
"[codex-monitor] spawning codex app-server workspace_id={} path={}",
274+
entry.id, entry.path
275+
);
276+
}
257277
let mut command = build_codex_command(&entry);
258278
command.arg("app-server");
259279
command.stdin(std::process::Stdio::piped());
@@ -323,6 +343,26 @@ async fn spawn_workspace_session(
323343
let _ = app_handle_clone.emit("app-server-event", payload);
324344
}
325345
}
346+
347+
if cfg!(debug_assertions) {
348+
eprintln!("[codex-monitor] app-server stdout closed workspace_id={workspace_id}");
349+
}
350+
if cfg!(debug_assertions) {
351+
let status = {
352+
let mut child = session_clone.child.lock().await;
353+
child
354+
.try_wait()
355+
.ok()
356+
.flatten()
357+
.map(|value| value.to_string())
358+
};
359+
if let Some(status) = status {
360+
eprintln!(
361+
"[codex-monitor] app-server exit status workspace_id={workspace_id} {status}"
362+
);
363+
}
364+
}
365+
session_clone.pending.lock().await.clear();
326366
});
327367

328368
let workspace_id = entry.id.clone();
@@ -333,6 +373,9 @@ async fn spawn_workspace_session(
333373
if line.trim().is_empty() {
334374
continue;
335375
}
376+
if cfg!(debug_assertions) {
377+
eprintln!("[codex-monitor] app-server stderr workspace_id={workspace_id} {line}");
378+
}
336379
let payload = AppServerEvent {
337380
workspace_id: workspace_id.clone(),
338381
message: json!({
@@ -351,7 +394,22 @@ async fn spawn_workspace_session(
351394
"version": "0.1.0"
352395
}
353396
});
354-
session.send_request("initialize", init_params).await?;
397+
match timeout(Duration::from_secs(5), session.send_request("initialize", init_params)).await {
398+
Ok(result) => {
399+
result?;
400+
}
401+
Err(_) => {
402+
if cfg!(debug_assertions) {
403+
eprintln!(
404+
"[codex-monitor] app-server initialize timed out workspace_id={}",
405+
entry.id
406+
);
407+
}
408+
let mut child = session.child.lock().await;
409+
let _ = child.kill().await;
410+
return Err("codex app-server initialize timed out".to_string());
411+
}
412+
}
355413
session.send_notification("initialized", None).await?;
356414

357415
let payload = AppServerEvent {
@@ -368,6 +426,9 @@ async fn spawn_workspace_session(
368426

369427
#[tauri::command]
370428
async fn list_workspaces(state: State<'_, AppState>) -> Result<Vec<WorkspaceInfo>, String> {
429+
if cfg!(debug_assertions) {
430+
eprintln!("[codex-monitor] list_workspaces");
431+
}
371432
let workspaces = state.workspaces.lock().await;
372433
let sessions = state.sessions.lock().await;
373434
let mut result = Vec::new();
@@ -392,6 +453,9 @@ async fn add_workspace(
392453
state: State<'_, AppState>,
393454
app: AppHandle,
394455
) -> Result<WorkspaceInfo, String> {
456+
if cfg!(debug_assertions) {
457+
eprintln!("[codex-monitor] add_workspace path={path}");
458+
}
395459
let name = PathBuf::from(&path)
396460
.file_name()
397461
.and_then(|s| s.to_str())
@@ -405,7 +469,14 @@ async fn add_workspace(
405469
settings: WorkspaceSettings::default(),
406470
};
407471

408-
let session = spawn_workspace_session(entry.clone(), app).await?;
472+
let session = spawn_workspace_session(entry.clone(), app.clone())
473+
.await
474+
.map_err(|error| {
475+
if cfg!(debug_assertions) {
476+
eprintln!("[codex-monitor] add_workspace spawn failed: {error}");
477+
}
478+
error
479+
})?;
409480
{
410481
let mut workspaces = state.workspaces.lock().await;
411482
workspaces.insert(entry.id.clone(), entry.clone());

0 commit comments

Comments
 (0)