Honor feature overrides when starting the app-server daemon (#60489)

## Why

`codex app-server daemon start` ignored CLI feature overrides, while `restart` already parsed and applied them.

## What changed

Parse and validate feature overrides for `start` using the same handling as `restart`, including `--enable`, `--disable`, and `-c features.<name>=<bool>`. Pass the overrides through to daemon startup so they are saved when launching a stopped daemon. Starting an already running daemon preserves its process and saved overrides.

## Testing

Add coverage for applying overrides on a fresh start and preserving them on a subsequent `start`. Extend validation coverage to ensure both `start` and `restart` reject non-toggle feature keys without changing settings or the running process.

GitOrigin-RevId: 9edbfdfc723146674051a9f0713e528b552eeb00
This commit is contained in:
iceweasel-oai
2026-11-10 01:31:19 +00:00
committed by copyberry
parent 6aa88f9946
commit 4dcd8df891
3 changed files with 73 additions and 30 deletions
+12 -10
View File
@@ -1329,9 +1329,6 @@ async fn dispatch_cli(
}
}
Some(AppServerSubcommand::Daemon(daemon_cli)) => match daemon_cli.subcommand {
AppServerDaemonSubcommand::Start => {
print_app_server_daemon_output(AppServerLifecycleCommand::Start).await?;
}
AppServerDaemonSubcommand::Bootstrap(bootstrap_cli) => {
let output =
codex_app_server_daemon::bootstrap(AppServerBootstrapOptions {
@@ -1340,7 +1337,10 @@ async fn dispatch_cli(
.await?;
println!("{}", serde_json::to_string(&output)?);
}
AppServerDaemonSubcommand::Restart => {
command @ (AppServerDaemonSubcommand::Start
| AppServerDaemonSubcommand::Restart) => {
let start = matches!(command, AppServerDaemonSubcommand::Start);
let action = if start { "start" } else { "restart" };
let overrides = root_config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
@@ -1369,18 +1369,20 @@ async fn dispatch_cli(
let feature =
codex_features::feature_for_key(name).ok_or_else(|| {
anyhow::anyhow!(
"daemon restart requires a boolean feature toggle: {name}"
"daemon {action} requires a boolean feature toggle: {name}"
)
})?;
let enabled = value.as_bool().ok_or_else(|| {
anyhow::anyhow!("daemon restart requires a boolean enablement for features.{name}")
anyhow::anyhow!("daemon {action} requires a boolean enablement for features.{name}")
})?;
feature_overrides.insert(feature.key().to_string(), enabled);
}
print_app_server_daemon_output(AppServerLifecycleCommand::Restart {
feature_overrides,
})
.await?;
let command = if start {
AppServerLifecycleCommand::Start { feature_overrides }
} else {
AppServerLifecycleCommand::Restart { feature_overrides }
};
print_app_server_daemon_output(command).await?;
}
AppServerDaemonSubcommand::Update {
from_cli: true,