Skip to content

Commit bde734f

Browse files
authored
feat(app-server): add an --analytics-default-enabled flag (#9118)
Add a new `codex app-server --analytics-default-enabled` CLI flag that controls whether analytics are enabled by default. Analytics are disabled by default for app-server. Users have to explicitly opt in via the `analytics` section in the config.toml file. However, for first-party use cases like the VSCode IDE extension, we default analytics to be enabled by default by setting this flag. Users can still opt out by setting this in their config.toml: ```toml [analytics] enabled = false ``` See https://developers.openai.com/codex/config-advanced/#metrics for more details.
1 parent 58e8f75 commit bde734f

File tree

5 files changed

+110
-1
lines changed

5 files changed

+110
-1
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub async fn run_main(
4444
codex_linux_sandbox_exe: Option<PathBuf>,
4545
cli_config_overrides: CliConfigOverrides,
4646
loader_overrides: LoaderOverrides,
47+
default_analytics_enabled: bool,
4748
) -> IoResult<()> {
4849
// Set up channels.
4950
let (incoming_tx, mut incoming_rx) = mpsc::channel::<JSONRPCMessage>(CHANNEL_CAPACITY);
@@ -96,7 +97,7 @@ pub async fn run_main(
9697
&config,
9798
env!("CARGO_PKG_VERSION"),
9899
Some("codex_app_server"),
99-
false,
100+
default_analytics_enabled,
100101
)
101102
.map_err(|e| {
102103
std::io::Error::new(

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ fn main() -> anyhow::Result<()> {
2020
codex_linux_sandbox_exe,
2121
CliConfigOverrides::default(),
2222
loader_overrides,
23+
false,
2324
)
2425
.await?;
2526
Ok(())
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use anyhow::Result;
2+
use codex_core::config::ConfigBuilder;
3+
use codex_core::config::types::OtelExporterKind;
4+
use codex_core::config::types::OtelHttpProtocol;
5+
use pretty_assertions::assert_eq;
6+
use std::collections::HashMap;
7+
use tempfile::TempDir;
8+
9+
const SERVICE_VERSION: &str = "0.0.0-test";
10+
11+
fn set_metrics_exporter(config: &mut codex_core::config::Config) {
12+
config.otel.metrics_exporter = OtelExporterKind::OtlpHttp {
13+
endpoint: "http://localhost:4318".to_string(),
14+
headers: HashMap::new(),
15+
protocol: OtelHttpProtocol::Json,
16+
tls: None,
17+
};
18+
}
19+
20+
#[tokio::test]
21+
async fn app_server_default_analytics_disabled_without_flag() -> Result<()> {
22+
let codex_home = TempDir::new()?;
23+
let mut config = ConfigBuilder::default()
24+
.codex_home(codex_home.path().to_path_buf())
25+
.build()
26+
.await?;
27+
set_metrics_exporter(&mut config);
28+
config.analytics_enabled = None;
29+
30+
let provider = codex_core::otel_init::build_provider(
31+
&config,
32+
SERVICE_VERSION,
33+
Some("codex_app_server"),
34+
false,
35+
)
36+
.map_err(|err| anyhow::anyhow!(err.to_string()))?;
37+
38+
// With analytics unset in the config and the default flag is false, metrics are disabled.
39+
// No provider is built.
40+
assert_eq!(provider.is_none(), true);
41+
Ok(())
42+
}
43+
44+
#[tokio::test]
45+
async fn app_server_default_analytics_enabled_with_flag() -> Result<()> {
46+
let codex_home = TempDir::new()?;
47+
let mut config = ConfigBuilder::default()
48+
.codex_home(codex_home.path().to_path_buf())
49+
.build()
50+
.await?;
51+
set_metrics_exporter(&mut config);
52+
config.analytics_enabled = None;
53+
54+
let provider = codex_core::otel_init::build_provider(
55+
&config,
56+
SERVICE_VERSION,
57+
Some("codex_app_server"),
58+
true,
59+
)
60+
.map_err(|err| anyhow::anyhow!(err.to_string()))?;
61+
62+
// With analytics unset in the config and the default flag is true, metrics are enabled.
63+
let has_metrics = provider.as_ref().and_then(|otel| otel.metrics()).is_some();
64+
assert_eq!(has_metrics, true);
65+
Ok(())
66+
}

codex-rs/app-server/tests/suite/v2/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod account;
2+
mod analytics;
23
mod config_rpc;
34
mod initialize;
45
mod model_list;

codex-rs/cli/src/main.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,24 @@ struct AppServerCommand {
268268
/// Omit to run the app server; specify a subcommand for tooling.
269269
#[command(subcommand)]
270270
subcommand: Option<AppServerSubcommand>,
271+
272+
/// Controls whether analytics are enabled by default.
273+
///
274+
/// Analytics are disabled by default for app-server. Users have to explicitly opt in
275+
/// via the `analytics` section in the config.toml file.
276+
///
277+
/// However, for first-party use cases like the VSCode IDE extension, we default analytics
278+
/// to be enabled by default by setting this flag. Users can still opt out by setting this
279+
/// in their config.toml:
280+
///
281+
/// ```toml
282+
/// [analytics]
283+
/// enabled = false
284+
/// ```
285+
///
286+
/// See https://developers.openai.com/codex/config-advanced/#metrics for more details.
287+
#[arg(long = "analytics-default-enabled")]
288+
analytics_default_enabled: bool,
271289
}
272290

273291
#[derive(Debug, clap::Subcommand)]
@@ -500,6 +518,7 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
500518
codex_linux_sandbox_exe,
501519
root_config_overrides,
502520
codex_core::config_loader::LoaderOverrides::default(),
521+
app_server_cli.analytics_default_enabled,
503522
)
504523
.await?;
505524
}
@@ -910,6 +929,14 @@ mod tests {
910929
finalize_fork_interactive(interactive, root_overrides, session_id, last, all, fork_cli)
911930
}
912931

932+
fn app_server_from_args(args: &[&str]) -> AppServerCommand {
933+
let cli = MultitoolCli::try_parse_from(args).expect("parse");
934+
let Subcommand::AppServer(app_server) = cli.subcommand.expect("app-server present") else {
935+
unreachable!()
936+
};
937+
app_server
938+
}
939+
913940
fn sample_exit_info(conversation: Option<&str>) -> AppExitInfo {
914941
let token_usage = TokenUsage {
915942
output_tokens: 2,
@@ -1108,6 +1135,19 @@ mod tests {
11081135
assert!(interactive.fork_show_all);
11091136
}
11101137

1138+
#[test]
1139+
fn app_server_analytics_default_disabled_without_flag() {
1140+
let app_server = app_server_from_args(["codex", "app-server"].as_ref());
1141+
assert!(!app_server.analytics_default_enabled);
1142+
}
1143+
1144+
#[test]
1145+
fn app_server_analytics_default_enabled_with_flag() {
1146+
let app_server =
1147+
app_server_from_args(["codex", "app-server", "--analytics-default-enabled"].as_ref());
1148+
assert!(app_server.analytics_default_enabled);
1149+
}
1150+
11111151
#[test]
11121152
fn feature_toggles_known_features_generate_overrides() {
11131153
let toggles = FeatureToggles {

0 commit comments

Comments
 (0)