use codex_codes::{
AsyncClient, CommandExecutionApprovalDecision, CommandExecutionRequestApprovalResponse,
FileChangeApprovalDecision, FileChangeRequestApprovalResponse, Notification, ServerMessage,
ServerRequest, ThreadItem, ThreadStartParams, TurnStartParams, UserInput,
};
use std::io::{self, Write};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
println!("\nCodex REPL (app-server)");
println!("======================");
println!("Type your queries and press Enter. Type 'exit' to quit.\n");
let mut client = AsyncClient::start().await?;
let thread = client
.thread_start(&serde_json::from_value::<ThreadStartParams>(serde_json::json!({})).unwrap())
.await?;
let thread_id = thread.thread.id.clone();
println!("Thread: {}\n", thread_id);
loop {
print!("> ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input.eq_ignore_ascii_case("exit") || input.eq_ignore_ascii_case("quit") {
break;
}
if input.is_empty() {
continue;
}
client
.turn_start(&TurnStartParams {
thread_id: thread_id.clone(),
input: vec![UserInput::Text {
text: input.to_string(),
text_elements: None,
}],
approval_policy: None,
approvals_reviewer: None,
cwd: None,
effort: None,
model: None,
output_schema: None,
personality: None,
sandbox_policy: None,
service_tier: None,
summary: None,
})
.await?;
println!("\n--- Response ---");
loop {
let msg = match client.next_message().await? {
Some(m) => m,
None => {
eprintln!("[connection closed]");
return Ok(());
}
};
match msg {
ServerMessage::Notification(n) => match n {
Notification::AgentMessageDelta(d) => {
print!("{}", d.delta);
io::stdout().flush()?;
}
Notification::CmdOutputDelta(d) => {
print!("{}", d.delta);
io::stdout().flush()?;
}
Notification::ReasoningDelta(d) => {
print!("[thinking] {}", d.delta);
}
Notification::ItemStarted(item_event) => match item_event.item {
ThreadItem::CommandExecution { command, .. } => {
println!("\n[Command: {}]", command);
}
ThreadItem::FileChange { .. } => {
println!("\n[File change]");
}
_ => {}
},
Notification::TurnCompleted(_) => {
println!();
break;
}
Notification::Error(e) => {
eprintln!("\n[Error: {}]", e.error.message);
}
other => {
log::debug!("Notification: {}", other.method());
}
},
ServerMessage::Request { id, request } => match request {
ServerRequest::CmdExecApproval(p) => {
println!(
"\n[Approving command: {}]",
p.command.as_deref().unwrap_or("<no command>")
);
client
.respond(
id,
&CommandExecutionRequestApprovalResponse {
decision: CommandExecutionApprovalDecision::Accept,
},
)
.await?;
}
ServerRequest::FileChangeApproval(_) => {
println!("\n[Approving file change]");
client
.respond(
id,
&FileChangeRequestApprovalResponse {
decision: FileChangeApprovalDecision::Accept,
},
)
.await?;
}
ServerRequest::Unknown { method, .. } => {
eprintln!("[unhandled server request: {}]", method);
}
other => {
eprintln!("[unhandled server request: {}]", other.method());
}
},
}
}
println!("--- End ---\n");
}
client.shutdown().await?;
Ok(())
}