use std::path::PathBuf;
use std::time::Duration;
#[derive(Clone, Debug)]
pub struct CodexAgentConfig {
pub command: PathBuf,
pub args: Vec<String>,
pub cwd: PathBuf,
pub auth_method_id: String,
pub authenticate: bool,
pub client_name: String,
pub client_version: String,
pub rpc_deadline: Duration,
pub max_line_bytes: usize,
pub max_output_queue: usize,
pub auto_allow_permissions: bool,
pub advertise_fs: bool,
pub raw_dump_path: Option<PathBuf>,
}
impl Default for CodexAgentConfig {
fn default() -> Self {
let (command, args) = discover_acp_command();
Self {
command,
args,
cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
auth_method_id: "openai-api-key".into(),
authenticate: false,
client_name: "monoloop-codex".into(),
client_version: env!("CARGO_PKG_VERSION").into(),
rpc_deadline: Duration::from_secs(90),
max_line_bytes: 8 * 1024 * 1024,
max_output_queue: 256,
auto_allow_permissions: false,
advertise_fs: false,
raw_dump_path: None,
}
}
}
impl CodexAgentConfig {
pub fn for_project(cwd: impl Into<PathBuf>) -> Self {
Self {
cwd: cwd.into(),
..Default::default()
}
}
pub fn with_raw_dump(mut self, path: impl Into<PathBuf>) -> Self {
self.raw_dump_path = Some(path.into());
self
}
pub fn with_codex_path_env_hint(self) -> Self {
self
}
pub fn with_global_codex_acp(mut self) -> Self {
self.command = PathBuf::from("codex-acp");
self.args.clear();
self
}
pub fn with_auto_allow_permissions(mut self) -> Self {
self.auto_allow_permissions = true;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_denies_auto_permissions() {
assert!(!CodexAgentConfig::default().auto_allow_permissions);
}
#[test]
fn opt_in_enables_auto_permissions() {
assert!(
CodexAgentConfig::default()
.with_auto_allow_permissions()
.auto_allow_permissions
);
}
}
fn discover_acp_command() -> (PathBuf, Vec<String>) {
if let Some(bin) = std::env::var_os("CODEX_ACP_BIN") {
return (PathBuf::from(bin), Vec::new());
}
if which("codex-acp").is_some() {
return (PathBuf::from("codex-acp"), Vec::new());
}
(
PathBuf::from(std::env::var_os("NPX_BIN").unwrap_or_else(|| "npx".into())),
vec!["--yes".into(), "@agentclientprotocol/codex-acp".into()],
)
}
fn which(name: &str) -> Option<PathBuf> {
let path = std::env::var_os("PATH")?;
for dir in std::env::split_paths(&path) {
let candidate = dir.join(name);
if candidate.is_file() {
return Some(candidate);
}
}
None
}
#[derive(Clone, Debug)]
pub struct CodexSessionConfig {
pub cwd: PathBuf,
pub mcp_servers: serde_json::Value,
pub mode_id: Option<String>,
}
impl CodexSessionConfig {
pub fn new(cwd: impl Into<PathBuf>) -> Self {
Self {
cwd: cwd.into(),
mcp_servers: serde_json::json!([]),
mode_id: None,
}
}
pub fn with_read_only_mode(mut self) -> Self {
self.mode_id = Some("read-only".into());
self
}
pub fn with_agent_mode(mut self) -> Self {
self.mode_id = Some("agent".into());
self
}
pub fn with_agent_full_access_mode(mut self) -> Self {
self.mode_id = Some("agent-full-access".into());
self
}
}