Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 38 additions & 9 deletions codex-rs/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,51 @@ path = "src/lib.rs"
[lints]
workspace = true

[features]
default = ["full"]

# Full build with all features (original behavior)
full = [
"app-server",
"mcp-server",
"exec-mode",
"cloud-tasks",
"http-providers",
"responses-proxy",
]

# Minimal ACP-only build: CLI + TUI + ACP
minimal = []

# Optional CLI modes
app-server = ["dep:codex-app-server"]
mcp-server = ["dep:codex-mcp-server"]
exec-mode = ["dep:codex-exec"]
cloud-tasks = ["dep:codex-cloud-tasks"]

# Legacy HTTP provider support (OpenAI, ChatGPT login, etc.)
http-providers = ["dep:codex-chatgpt", "dep:codex-login", "dep:codex-rmcp-client"]
responses-proxy = ["dep:codex-responses-api-proxy"]

[dependencies]
# === Always required (core CLI + TUI + ACP) ===
anyhow = { workspace = true }
clap = { workspace = true, features = ["derive"] }
clap_complete = { workspace = true }
codex-acp = { workspace = true }
codex-app-server = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-arg0 = { workspace = true }
codex-chatgpt = { workspace = true }
codex-cloud-tasks = { path = "../cloud-tasks" }
codex-common = { workspace = true, features = ["cli"] }
codex-core = { workspace = true }
codex-exec = { workspace = true }
codex-execpolicy = { workspace = true }
codex-login = { workspace = true }
codex-mcp-server = { workspace = true }
codex-process-hardening = { workspace = true }
codex-protocol = { workspace = true }
codex-responses-api-proxy = { workspace = true }
codex-rmcp-client = { workspace = true }
codex-stdio-to-uds = { workspace = true }
codex-tui = { workspace = true }
ctor = { workspace = true }
libc = { workspace = true }
owo-colors = { workspace = true }
regex-lite = { workspace = true}
regex-lite = { workspace = true }
serde_json = { workspace = true }
supports-color = { workspace = true }
toml = { workspace = true }
Expand All @@ -53,6 +72,16 @@ tokio = { workspace = true, features = [
] }
tracing = { workspace = true }

# === Feature-gated optional dependencies ===
codex-app-server = { workspace = true, optional = true }
codex-chatgpt = { workspace = true, optional = true }
codex-cloud-tasks = { path = "../cloud-tasks", optional = true }
codex-exec = { workspace = true, optional = true }
codex-login = { workspace = true, optional = true }
codex-mcp-server = { workspace = true, optional = true }
codex-responses-api-proxy = { workspace = true, optional = true }
codex-rmcp-client = { workspace = true, optional = true }

[target.'cfg(target_os = "windows")'.dependencies]
codex_windows_sandbox = { package = "codex-windows-sandbox", path = "../windows-sandbox-rs" }

Expand Down
1 change: 1 addition & 0 deletions codex-rs/cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod debug_sandbox;
mod exit_status;
#[cfg(feature = "http-providers")]
pub mod login;

use clap::Parser;
Expand Down
65 changes: 52 additions & 13 deletions codex-rs/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,51 @@
use clap::Args;
use clap::CommandFactory;
use clap::Parser;
use clap_complete::Shell;
use clap_complete::generate;
use codex_acp::init_file_tracing;
use codex_arg0::arg0_dispatch_or_else;
use codex_chatgpt::apply_command::ApplyCommand;
use codex_chatgpt::apply_command::run_apply_command;
use codex_cli::LandlockCommand;
use codex_cli::SeatbeltCommand;
use codex_cli::WindowsCommand;
use codex_common::CliConfigOverrides;
use codex_execpolicy::ExecPolicyCheckCommand;
use codex_tui::AppExitInfo;
use codex_tui::Cli as TuiCli;
use codex_tui::update_action::UpdateAction;
use owo_colors::OwoColorize;
use std::path::PathBuf;
use supports_color::Stream;

// Feature-gated imports
#[cfg(feature = "http-providers")]
use codex_chatgpt::apply_command::ApplyCommand;
#[cfg(feature = "http-providers")]
use codex_chatgpt::apply_command::run_apply_command;
#[cfg(feature = "http-providers")]
use codex_cli::login::read_api_key_from_stdin;
#[cfg(feature = "http-providers")]
use codex_cli::login::run_login_status;
#[cfg(feature = "http-providers")]
use codex_cli::login::run_login_with_api_key;
#[cfg(feature = "http-providers")]
use codex_cli::login::run_login_with_chatgpt;
#[cfg(feature = "http-providers")]
use codex_cli::login::run_login_with_device_code;
#[cfg(feature = "http-providers")]
use codex_cli::login::run_logout;
#[cfg(feature = "cloud-tasks")]
use codex_cloud_tasks::Cli as CloudTasksCli;
use codex_common::CliConfigOverrides;
#[cfg(feature = "exec-mode")]
use codex_exec::Cli as ExecCli;
use codex_execpolicy::ExecPolicyCheckCommand;
#[cfg(feature = "responses-proxy")]
use codex_responses_api_proxy::Args as ResponsesApiProxyArgs;
use codex_tui::AppExitInfo;
use codex_tui::Cli as TuiCli;
use codex_tui::update_action::UpdateAction;
use owo_colors::OwoColorize;
use std::path::PathBuf;
use supports_color::Stream;

#[cfg(feature = "mcp-server")]
mod mcp_cmd;
#[cfg(not(windows))]
mod wsl_paths;

#[cfg(feature = "mcp-server")]
use crate::mcp_cmd::McpCli;

use codex_core::config::Config;
Expand Down Expand Up @@ -70,22 +84,28 @@ struct MultitoolCli {
#[derive(Debug, clap::Subcommand)]
enum Subcommand {
/// Run Codex non-interactively.
#[cfg(feature = "exec-mode")]
#[clap(visible_alias = "e")]
Exec(ExecCli),

/// Manage login.
#[cfg(feature = "http-providers")]
Login(LoginCommand),

/// Remove stored authentication credentials.
#[cfg(feature = "http-providers")]
Logout(LogoutCommand),

/// [experimental] Run Codex as an MCP server and manage MCP servers.
#[cfg(feature = "mcp-server")]
Mcp(McpCli),

/// [experimental] Run the Codex MCP server (stdio transport).
#[cfg(feature = "mcp-server")]
McpServer,

/// [experimental] Run the app server or related tooling.
#[cfg(feature = "app-server")]
AppServer(AppServerCommand),

/// Generate shell completion scripts.
Expand All @@ -100,17 +120,20 @@ enum Subcommand {
Execpolicy(ExecpolicyCommand),

/// Apply the latest diff produced by Codex agent as a `git apply` to your local working tree.
#[cfg(feature = "http-providers")]
#[clap(visible_alias = "a")]
Apply(ApplyCommand),

/// Resume a previous interactive session (picker by default; use --last to continue the most recent).
Resume(ResumeCommand),

/// [EXPERIMENTAL] Browse tasks from Codex Cloud and apply changes locally.
#[cfg(feature = "cloud-tasks")]
#[clap(name = "cloud", alias = "cloud-tasks")]
Cloud(CloudTasksCli),

/// Internal: run the responses API proxy.
#[cfg(feature = "responses-proxy")]
#[clap(hide = true)]
ResponsesApiProxy(ResponsesApiProxyArgs),

Expand Down Expand Up @@ -181,6 +204,7 @@ enum ExecpolicySubcommand {
Check(ExecPolicyCheckCommand),
}

#[cfg(feature = "http-providers")]
#[derive(Debug, Parser)]
struct LoginCommand {
#[clap(skip)]
Expand Down Expand Up @@ -216,25 +240,29 @@ struct LoginCommand {
action: Option<LoginSubcommand>,
}

#[cfg(feature = "http-providers")]
#[derive(Debug, clap::Subcommand)]
enum LoginSubcommand {
/// Show login status.
Status,
}

#[cfg(feature = "http-providers")]
#[derive(Debug, Parser)]
struct LogoutCommand {
#[clap(skip)]
config_overrides: CliConfigOverrides,
}

#[cfg(feature = "app-server")]
#[derive(Debug, Parser)]
struct AppServerCommand {
/// Omit to run the app server; specify a subcommand for tooling.
#[command(subcommand)]
subcommand: Option<AppServerSubcommand>,
}

#[cfg(feature = "app-server")]
#[derive(Debug, clap::Subcommand)]
enum AppServerSubcommand {
/// [experimental] Generate TypeScript bindings for the app server protocol.
Expand All @@ -244,7 +272,8 @@ enum AppServerSubcommand {
GenerateJsonSchema(GenerateJsonSchemaCommand),
}

#[derive(Debug, Args)]
#[cfg(feature = "app-server")]
#[derive(Debug, clap::Args)]
struct GenerateTsCommand {
/// Output directory where .ts files will be written
#[arg(short = 'o', long = "out", value_name = "DIR")]
Expand All @@ -255,7 +284,8 @@ struct GenerateTsCommand {
prettier: Option<PathBuf>,
}

#[derive(Debug, Args)]
#[cfg(feature = "app-server")]
#[derive(Debug, clap::Args)]
struct GenerateJsonSchemaCommand {
/// Output directory where the schema bundle will be written
#[arg(short = 'o', long = "out", value_name = "DIR")]
Expand Down Expand Up @@ -451,21 +481,25 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
let exit_info = codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
handle_app_exit(exit_info)?;
}
#[cfg(feature = "exec-mode")]
Some(Subcommand::Exec(mut exec_cli)) => {
prepend_config_flags(
&mut exec_cli.config_overrides,
root_config_overrides.clone(),
);
codex_exec::run_main(exec_cli, codex_linux_sandbox_exe).await?;
}
#[cfg(feature = "mcp-server")]
Some(Subcommand::McpServer) => {
codex_mcp_server::run_main(codex_linux_sandbox_exe, root_config_overrides).await?;
}
#[cfg(feature = "mcp-server")]
Some(Subcommand::Mcp(mut mcp_cli)) => {
// Propagate any root-level config overrides (e.g. `-c key=value`).
prepend_config_flags(&mut mcp_cli.config_overrides, root_config_overrides.clone());
mcp_cli.run().await?;
}
#[cfg(feature = "app-server")]
Some(Subcommand::AppServer(app_server_cli)) => match app_server_cli.subcommand {
None => {
codex_app_server::run_main(codex_linux_sandbox_exe, root_config_overrides).await?;
Expand Down Expand Up @@ -497,6 +531,7 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
let exit_info = codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
handle_app_exit(exit_info)?;
}
#[cfg(feature = "http-providers")]
Some(Subcommand::Login(mut login_cli)) => {
prepend_config_flags(
&mut login_cli.config_overrides,
Expand Down Expand Up @@ -528,6 +563,7 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
}
}
}
#[cfg(feature = "http-providers")]
Some(Subcommand::Logout(mut logout_cli)) => {
prepend_config_flags(
&mut logout_cli.config_overrides,
Expand All @@ -538,6 +574,7 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
Some(Subcommand::Completion(completion_cli)) => {
print_completion(completion_cli);
}
#[cfg(feature = "cloud-tasks")]
Some(Subcommand::Cloud(mut cloud_cli)) => {
prepend_config_flags(
&mut cloud_cli.config_overrides,
Expand Down Expand Up @@ -583,13 +620,15 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
Some(Subcommand::Execpolicy(ExecpolicyCommand { sub })) => match sub {
ExecpolicySubcommand::Check(cmd) => run_execpolicycheck(cmd)?,
},
#[cfg(feature = "http-providers")]
Some(Subcommand::Apply(mut apply_cli)) => {
prepend_config_flags(
&mut apply_cli.config_overrides,
root_config_overrides.clone(),
);
run_apply_command(apply_cli, None).await?;
}
#[cfg(feature = "responses-proxy")]
Some(Subcommand::ResponsesApiProxy(args)) => {
tokio::task::spawn_blocking(move || codex_responses_api_proxy::run_main(args))
.await??;
Expand Down
14 changes: 11 additions & 3 deletions codex-rs/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@ workspace = true
clap = { workspace = true, features = ["derive", "wrap_help"], optional = true }
codex-app-server-protocol = { workspace = true }
codex-core = { workspace = true }
codex-lmstudio = { workspace = true }
codex-ollama = { workspace = true }
codex-protocol = { workspace = true }
once_cell = { workspace = true }
serde = { workspace = true, optional = true }
toml = { workspace = true, optional = true }

# Feature-gated optional dependencies (OSS providers)
codex-lmstudio = { workspace = true, optional = true }
codex-ollama = { workspace = true, optional = true }

[features]
default = ["oss-providers"]

# Separate feature so that `clap` is not a mandatory dependency.
cli = ["clap", "serde", "toml"]
cli = ["dep:clap", "dep:serde", "dep:toml"]

# Local OSS model providers (Ollama, LM Studio)
oss-providers = ["dep:codex-ollama", "dep:codex-lmstudio"]

elapsed = []
sandbox_summary = []
24 changes: 23 additions & 1 deletion codex-rs/common/src/oss.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//! OSS provider utilities shared between TUI and exec.

#[cfg(feature = "oss-providers")]
use codex_core::LMSTUDIO_OSS_PROVIDER_ID;
#[cfg(feature = "oss-providers")]
use codex_core::OLLAMA_OSS_PROVIDER_ID;
use codex_core::config::Config;

/// Returns the default model for a given OSS provider.
#[cfg(feature = "oss-providers")]
pub fn get_default_model_for_oss_provider(provider_id: &str) -> Option<&'static str> {
match provider_id {
LMSTUDIO_OSS_PROVIDER_ID => Some(codex_lmstudio::DEFAULT_OSS_MODEL),
Expand All @@ -13,7 +16,15 @@ pub fn get_default_model_for_oss_provider(provider_id: &str) -> Option<&'static
}
}

/// Returns the default model for a given OSS provider.
/// Stub version when OSS providers are not compiled in.
#[cfg(not(feature = "oss-providers"))]
pub fn get_default_model_for_oss_provider(_provider_id: &str) -> Option<&'static str> {
None
}

/// Ensures the specified OSS provider is ready (models downloaded, service reachable).
#[cfg(feature = "oss-providers")]
pub async fn ensure_oss_provider_ready(
provider_id: &str,
config: &Config,
Expand All @@ -36,7 +47,18 @@ pub async fn ensure_oss_provider_ready(
Ok(())
}

#[cfg(test)]
/// Ensures the specified OSS provider is ready.
/// Stub version when OSS providers are not compiled in.
#[cfg(not(feature = "oss-providers"))]
pub async fn ensure_oss_provider_ready(
_provider_id: &str,
_config: &Config,
) -> Result<(), std::io::Error> {
// OSS providers not available in this build
Ok(())
}

#[cfg(all(test, feature = "oss-providers"))]
mod tests {
use super::*;

Expand Down
Loading
Loading