Skip to content

Commit 1d0008b

Browse files
authored
Merge branch 'main' into add-github-action-for-nix
2 parents 0e9ddd7 + 516acc0 commit 1d0008b

File tree

101 files changed

+9266
-896
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+9266
-896
lines changed

codex-rs/Cargo.lock

Lines changed: 560 additions & 394 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codex-rs/Cargo.toml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
[workspace]
22
members = [
3+
"backend-client",
34
"ansi-escape",
5+
"app-server",
46
"apply-patch",
57
"arg0",
8+
"codex-backend-openapi-models",
9+
"cloud-tasks",
10+
"cloud-tasks-client",
611
"cli",
712
"common",
813
"core",
@@ -23,6 +28,8 @@ members = [
2328
"responses-api-proxy",
2429
"otel",
2530
"tui",
31+
"git-apply",
32+
"utils/json-to-toml",
2633
"utils/readiness",
2734
]
2835
resolver = "2"
@@ -37,7 +44,9 @@ edition = "2024"
3744

3845
[workspace.dependencies]
3946
# Internal
47+
app_test_support = { path = "app-server/tests/common" }
4048
codex-ansi-escape = { path = "ansi-escape" }
49+
codex-app-server = { path = "app-server" }
4150
codex-apply-patch = { path = "apply-patch" }
4251
codex-arg0 = { path = "arg0" }
4352
codex-chatgpt = { path = "chatgpt" }
@@ -51,12 +60,14 @@ codex-login = { path = "login" }
5160
codex-mcp-client = { path = "mcp-client" }
5261
codex-mcp-server = { path = "mcp-server" }
5362
codex-ollama = { path = "ollama" }
63+
codex-otel = { path = "otel" }
5464
codex-process-hardening = { path = "process-hardening" }
5565
codex-protocol = { path = "protocol" }
5666
codex-protocol-ts = { path = "protocol-ts" }
67+
codex-responses-api-proxy = { path = "responses-api-proxy" }
5768
codex-rmcp-client = { path = "rmcp-client" }
5869
codex-tui = { path = "tui" }
59-
codex-otel = { path = "otel" }
70+
codex-utils-json-to-toml = { path = "utils/json-to-toml" }
6071
codex-utils-readiness = { path = "utils/readiness" }
6172
core_test_support = { path = "core/tests/common" }
6273
mcp-types = { path = "mcp-types" }
@@ -106,10 +117,10 @@ multimap = "0.10.0"
106117
nucleo-matcher = "0.3.1"
107118
openssl-sys = "*"
108119
opentelemetry = "0.30.0"
109-
opentelemetry_sdk = "0.30.0"
110-
opentelemetry-otlp = "0.30.0"
111120
opentelemetry-appender-tracing = "0.30.0"
121+
opentelemetry-otlp = "0.30.0"
112122
opentelemetry-semantic-conventions = "0.30.0"
123+
opentelemetry_sdk = "0.30.0"
113124
os_info = "3.12.0"
114125
owo-colors = "4.2.0"
115126
path-absolutize = "3.1.1"
@@ -148,14 +159,14 @@ tokio-test = "0.4"
148159
tokio-util = "0.7.16"
149160
toml = "0.9.5"
150161
toml_edit = "0.23.4"
162+
tonic = "0.13.1"
151163
tracing = "0.1.41"
152164
tracing-appender = "0.2.3"
153165
tracing-subscriber = "0.3.20"
154166
tracing-test = "0.2.5"
155167
tree-sitter = "0.25.9"
156168
tree-sitter-bash = "0.25.0"
157169
ts-rs = "11"
158-
tonic = "0.13.1"
159170
unicode-segmentation = "1.12.0"
160171
unicode-width = "0.2"
161172
url = "2"

codex-rs/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ Codex supports a rich set of configuration options. Note that the Rust CLI uses
2525

2626
Codex CLI functions as an MCP client that can connect to MCP servers on startup. See the [`mcp_servers`](../docs/config.md#mcp_servers) section in the configuration documentation for details.
2727

28-
It is still experimental, but you can also launch Codex as an MCP _server_ by running `codex mcp`. Use the [`@modelcontextprotocol/inspector`](https://github.com/modelcontextprotocol/inspector) to try it out:
28+
It is still experimental, but you can also launch Codex as an MCP _server_ by running `codex mcp-server`. Use the [`@modelcontextprotocol/inspector`](https://github.com/modelcontextprotocol/inspector) to try it out:
2929

3030
```shell
31-
npx @modelcontextprotocol/inspector codex mcp
31+
npx @modelcontextprotocol/inspector codex mcp-server
3232
```
3333

34+
Use `codex mcp` to add/list/get/remove MCP server launchers defined in `config.toml`, and `codex mcp-server` to run the MCP server directly.
35+
3436
### Notifications
3537

3638
You can enable notifications by configuring a script that is run whenever the agent finishes a turn. The [notify documentation](../docs/config.md#notify) includes a detailed example that explains how to get desktop notifications via [terminal-notifier](https://github.com/julienXX/terminal-notifier) on macOS.

codex-rs/app-server/Cargo.toml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
[package]
2+
edition = "2024"
3+
name = "codex-app-server"
4+
version = { workspace = true }
5+
6+
[[bin]]
7+
name = "codex-app-server"
8+
path = "src/main.rs"
9+
10+
[lib]
11+
name = "codex_app_server"
12+
path = "src/lib.rs"
13+
14+
[lints]
15+
workspace = true
16+
17+
[dependencies]
18+
anyhow = { workspace = true }
19+
codex-arg0 = { workspace = true }
20+
codex-common = { workspace = true, features = ["cli"] }
21+
codex-core = { workspace = true }
22+
codex-file-search = { workspace = true }
23+
codex-login = { workspace = true }
24+
codex-protocol = { workspace = true }
25+
codex-utils-json-to-toml = { workspace = true }
26+
# We should only be using mcp-types for JSON-RPC types: it would be nice to
27+
# split this out into a separate crate at some point.
28+
mcp-types = { workspace = true }
29+
serde = { workspace = true, features = ["derive"] }
30+
serde_json = { workspace = true }
31+
tokio = { workspace = true, features = [
32+
"io-std",
33+
"macros",
34+
"process",
35+
"rt-multi-thread",
36+
"signal",
37+
] }
38+
tracing = { workspace = true, features = ["log"] }
39+
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
40+
uuid = { workspace = true, features = ["serde", "v7"] }
41+
42+
[dev-dependencies]
43+
app_test_support = { workspace = true }
44+
assert_cmd = { workspace = true }
45+
base64 = { workspace = true }
46+
core_test_support = { workspace = true }
47+
os_info = { workspace = true }
48+
pretty_assertions = { workspace = true }
49+
tempfile = { workspace = true }
50+
toml = { workspace = true }
51+
wiremock = { workspace = true }

codex-rs/mcp-server/src/codex_message_processor.rs renamed to codex-rs/app-server/src/codex_message_processor.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::error_code::INTERNAL_ERROR_CODE;
22
use crate::error_code::INVALID_REQUEST_ERROR_CODE;
33
use crate::fuzzy_file_search::run_fuzzy_file_search;
4-
use crate::json_to_toml::json_to_toml;
54
use crate::outgoing_message::OutgoingMessageSender;
65
use crate::outgoing_message::OutgoingNotification;
76
use codex_core::AuthManager;
@@ -77,6 +76,7 @@ use codex_protocol::mcp_protocol::SendUserMessageResponse;
7776
use codex_protocol::mcp_protocol::SendUserTurnParams;
7877
use codex_protocol::mcp_protocol::SendUserTurnResponse;
7978
use codex_protocol::mcp_protocol::ServerNotification;
79+
use codex_protocol::mcp_protocol::SessionConfiguredNotification;
8080
use codex_protocol::mcp_protocol::SetDefaultModelParams;
8181
use codex_protocol::mcp_protocol::SetDefaultModelResponse;
8282
use codex_protocol::mcp_protocol::UserInfoResponse;
@@ -85,6 +85,7 @@ use codex_protocol::models::ContentItem;
8585
use codex_protocol::models::ResponseItem;
8686
use codex_protocol::protocol::InputMessageKind;
8787
use codex_protocol::protocol::USER_MESSAGE_BEGIN;
88+
use codex_utils_json_to_toml::json_to_toml;
8889
use mcp_types::JSONRPCErrorError;
8990
use mcp_types::RequestId;
9091
use std::collections::HashMap;
@@ -153,6 +154,9 @@ impl CodexMessageProcessor {
153154

154155
pub async fn process_request(&mut self, request: ClientRequest) {
155156
match request {
157+
ClientRequest::Initialize { .. } => {
158+
panic!("Initialize should be handled in MessageProcessor");
159+
}
156160
ClientRequest::NewConversation { request_id, params } => {
157161
// Do not tokio::spawn() to process new_conversation()
158162
// asynchronously because we need to ensure the conversation is
@@ -762,11 +766,19 @@ impl CodexMessageProcessor {
762766
session_configured,
763767
..
764768
}) => {
765-
let event = Event {
766-
id: "".to_string(),
767-
msg: EventMsg::SessionConfigured(session_configured.clone()),
768-
};
769-
self.outgoing.send_event_as_notification(&event, None).await;
769+
self.outgoing
770+
.send_server_notification(ServerNotification::SessionConfigured(
771+
SessionConfiguredNotification {
772+
session_id: session_configured.session_id,
773+
model: session_configured.model.clone(),
774+
reasoning_effort: session_configured.reasoning_effort,
775+
history_log_id: session_configured.history_log_id,
776+
history_entry_count: session_configured.history_entry_count,
777+
initial_messages: session_configured.initial_messages.clone(),
778+
rollout_path: session_configured.rollout_path.clone(),
779+
},
780+
))
781+
.await;
770782
let initial_messages = session_configured.initial_messages.map(|msgs| {
771783
msgs.into_iter()
772784
.filter(|event| {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub(crate) const INVALID_REQUEST_ERROR_CODE: i64 = -32600;
2+
pub(crate) const INTERNAL_ERROR_CODE: i64 = -32603;

codex-rs/app-server/src/lib.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#![deny(clippy::print_stdout, clippy::print_stderr)]
2+
3+
use std::io::ErrorKind;
4+
use std::io::Result as IoResult;
5+
use std::path::PathBuf;
6+
7+
use codex_common::CliConfigOverrides;
8+
use codex_core::config::Config;
9+
use codex_core::config::ConfigOverrides;
10+
11+
use mcp_types::JSONRPCMessage;
12+
use tokio::io::AsyncBufReadExt;
13+
use tokio::io::AsyncWriteExt;
14+
use tokio::io::BufReader;
15+
use tokio::io::{self};
16+
use tokio::sync::mpsc;
17+
use tracing::debug;
18+
use tracing::error;
19+
use tracing::info;
20+
use tracing_subscriber::EnvFilter;
21+
22+
use crate::message_processor::MessageProcessor;
23+
use crate::outgoing_message::OutgoingMessage;
24+
use crate::outgoing_message::OutgoingMessageSender;
25+
26+
mod codex_message_processor;
27+
mod error_code;
28+
mod fuzzy_file_search;
29+
mod message_processor;
30+
mod outgoing_message;
31+
32+
/// Size of the bounded channels used to communicate between tasks. The value
33+
/// is a balance between throughput and memory usage – 128 messages should be
34+
/// plenty for an interactive CLI.
35+
const CHANNEL_CAPACITY: usize = 128;
36+
37+
pub async fn run_main(
38+
codex_linux_sandbox_exe: Option<PathBuf>,
39+
cli_config_overrides: CliConfigOverrides,
40+
) -> IoResult<()> {
41+
// Install a simple subscriber so `tracing` output is visible. Users can
42+
// control the log level with `RUST_LOG`.
43+
tracing_subscriber::fmt()
44+
.with_writer(std::io::stderr)
45+
.with_env_filter(EnvFilter::from_default_env())
46+
.init();
47+
48+
// Set up channels.
49+
let (incoming_tx, mut incoming_rx) = mpsc::channel::<JSONRPCMessage>(CHANNEL_CAPACITY);
50+
let (outgoing_tx, mut outgoing_rx) = mpsc::unbounded_channel::<OutgoingMessage>();
51+
52+
// Task: read from stdin, push to `incoming_tx`.
53+
let stdin_reader_handle = tokio::spawn({
54+
async move {
55+
let stdin = io::stdin();
56+
let reader = BufReader::new(stdin);
57+
let mut lines = reader.lines();
58+
59+
while let Some(line) = lines.next_line().await.unwrap_or_default() {
60+
match serde_json::from_str::<JSONRPCMessage>(&line) {
61+
Ok(msg) => {
62+
if incoming_tx.send(msg).await.is_err() {
63+
// Receiver gone – nothing left to do.
64+
break;
65+
}
66+
}
67+
Err(e) => error!("Failed to deserialize JSONRPCMessage: {e}"),
68+
}
69+
}
70+
71+
debug!("stdin reader finished (EOF)");
72+
}
73+
});
74+
75+
// Parse CLI overrides once and derive the base Config eagerly so later
76+
// components do not need to work with raw TOML values.
77+
let cli_kv_overrides = cli_config_overrides.parse_overrides().map_err(|e| {
78+
std::io::Error::new(
79+
ErrorKind::InvalidInput,
80+
format!("error parsing -c overrides: {e}"),
81+
)
82+
})?;
83+
let config = Config::load_with_cli_overrides(cli_kv_overrides, ConfigOverrides::default())
84+
.map_err(|e| {
85+
std::io::Error::new(ErrorKind::InvalidData, format!("error loading config: {e}"))
86+
})?;
87+
88+
// Task: process incoming messages.
89+
let processor_handle = tokio::spawn({
90+
let outgoing_message_sender = OutgoingMessageSender::new(outgoing_tx);
91+
let mut processor = MessageProcessor::new(
92+
outgoing_message_sender,
93+
codex_linux_sandbox_exe,
94+
std::sync::Arc::new(config),
95+
);
96+
async move {
97+
while let Some(msg) = incoming_rx.recv().await {
98+
match msg {
99+
JSONRPCMessage::Request(r) => processor.process_request(r).await,
100+
JSONRPCMessage::Response(r) => processor.process_response(r).await,
101+
JSONRPCMessage::Notification(n) => processor.process_notification(n).await,
102+
JSONRPCMessage::Error(e) => processor.process_error(e),
103+
}
104+
}
105+
106+
info!("processor task exited (channel closed)");
107+
}
108+
});
109+
110+
// Task: write outgoing messages to stdout.
111+
let stdout_writer_handle = tokio::spawn(async move {
112+
let mut stdout = io::stdout();
113+
while let Some(outgoing_message) = outgoing_rx.recv().await {
114+
let msg: JSONRPCMessage = outgoing_message.into();
115+
match serde_json::to_string(&msg) {
116+
Ok(json) => {
117+
if let Err(e) = stdout.write_all(json.as_bytes()).await {
118+
error!("Failed to write to stdout: {e}");
119+
break;
120+
}
121+
if let Err(e) = stdout.write_all(b"\n").await {
122+
error!("Failed to write newline to stdout: {e}");
123+
break;
124+
}
125+
}
126+
Err(e) => error!("Failed to serialize JSONRPCMessage: {e}"),
127+
}
128+
}
129+
130+
info!("stdout writer exited (channel closed)");
131+
});
132+
133+
// Wait for all tasks to finish. The typical exit path is the stdin reader
134+
// hitting EOF which, once it drops `incoming_tx`, propagates shutdown to
135+
// the processor and then to the stdout task.
136+
let _ = tokio::join!(stdin_reader_handle, processor_handle, stdout_writer_handle);
137+
138+
Ok(())
139+
}

codex-rs/app-server/src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use codex_app_server::run_main;
2+
use codex_arg0::arg0_dispatch_or_else;
3+
use codex_common::CliConfigOverrides;
4+
5+
fn main() -> anyhow::Result<()> {
6+
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
7+
run_main(codex_linux_sandbox_exe, CliConfigOverrides::default()).await?;
8+
Ok(())
9+
})
10+
}

0 commit comments

Comments
 (0)