mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Update codex remote-control to start the daemon (#22218)
## Why Update `codex remote-control` to use the new app server daemon commands instead. - if the updater loop is not running, bootstrap the daemon with remote control enabled (`codex app-server daemon bootstrap --remote-control`) - otherwise, enable the persisted remote-control setting and start the daemon normally
This commit is contained in:
committed by
GitHub
Unverified
parent
9ab7f4e6ac
commit
4859d80ffe
@@ -92,6 +92,10 @@ JSON-RPC initialize handshake on the Unix control socket.
|
||||
for future starts. If a managed app-server is already running, they restart it
|
||||
so the new setting takes effect immediately.
|
||||
|
||||
Top-level `codex remote-control` bootstraps with `--remote-control` when the
|
||||
updater loop is not running. Otherwise it enables remote control and starts the
|
||||
daemon normally.
|
||||
|
||||
`stop` sends a graceful termination request first, then sends a second
|
||||
termination signal after the grace window if the process is still alive.
|
||||
|
||||
|
||||
@@ -89,6 +89,13 @@ pub struct BootstrapOutput {
|
||||
pub app_server_version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RemoteControlStartOutput {
|
||||
Bootstrap(BootstrapOutput),
|
||||
Start(LifecycleOutput),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RemoteControlMode {
|
||||
Enabled,
|
||||
@@ -165,6 +172,13 @@ pub async fn bootstrap(options: BootstrapOptions) -> Result<BootstrapOutput> {
|
||||
Daemon::from_environment()?.bootstrap(options).await
|
||||
}
|
||||
|
||||
pub async fn ensure_remote_control_started() -> Result<RemoteControlStartOutput> {
|
||||
ensure_supported_platform()?;
|
||||
Daemon::from_environment()?
|
||||
.ensure_remote_control_started()
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn set_remote_control(mode: RemoteControlMode) -> Result<RemoteControlOutput> {
|
||||
ensure_supported_platform()?;
|
||||
Daemon::from_environment()?.set_remote_control(mode).await
|
||||
@@ -397,8 +411,34 @@ impl Daemon {
|
||||
self.bootstrap_locked(options).await
|
||||
}
|
||||
|
||||
async fn ensure_remote_control_started(&self) -> Result<RemoteControlStartOutput> {
|
||||
let _operation_lock = self.acquire_operation_lock().await?;
|
||||
let settings = self.load_settings().await?;
|
||||
if self.is_bootstrapped(&settings).await? {
|
||||
let _ = self
|
||||
.set_remote_control_locked(RemoteControlMode::Enabled)
|
||||
.await?;
|
||||
let output = self.start().await?;
|
||||
return Ok(RemoteControlStartOutput::Start(output));
|
||||
}
|
||||
|
||||
let output = self
|
||||
.bootstrap_locked(BootstrapOptions {
|
||||
remote_control_enabled: true,
|
||||
})
|
||||
.await?;
|
||||
Ok(RemoteControlStartOutput::Bootstrap(output))
|
||||
}
|
||||
|
||||
async fn set_remote_control(&self, mode: RemoteControlMode) -> Result<RemoteControlOutput> {
|
||||
let _operation_lock = self.acquire_operation_lock().await?;
|
||||
self.set_remote_control_locked(mode).await
|
||||
}
|
||||
|
||||
async fn set_remote_control_locked(
|
||||
&self,
|
||||
mode: RemoteControlMode,
|
||||
) -> Result<RemoteControlOutput> {
|
||||
let previous_settings = self.load_settings().await?;
|
||||
let mut settings = previous_settings.clone();
|
||||
let remote_control_enabled = mode.is_enabled();
|
||||
@@ -517,6 +557,11 @@ impl Daemon {
|
||||
backend.start().await
|
||||
}
|
||||
|
||||
async fn is_bootstrapped(&self, settings: &DaemonSettings) -> Result<bool> {
|
||||
let updater = backend::pid_update_loop_backend(self.backend_paths(settings));
|
||||
updater.is_starting_or_running().await
|
||||
}
|
||||
|
||||
fn ensure_managed_codex_bin(&self) -> Result<()> {
|
||||
if self.managed_codex_bin.is_file() {
|
||||
return Ok(());
|
||||
@@ -687,8 +732,12 @@ fn try_lock_file(_file: &tokio::fs::File) -> Result<bool> {
|
||||
mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::BackendKind;
|
||||
use super::BootstrapOutput;
|
||||
use super::BootstrapStatus;
|
||||
use super::LifecycleOutput;
|
||||
use super::LifecycleStatus;
|
||||
use super::RemoteControlStartOutput;
|
||||
use super::RemoteControlStatus;
|
||||
use super::RestartDecision;
|
||||
use super::RestartIfRunningOutcome;
|
||||
@@ -776,7 +825,7 @@ mod tests {
|
||||
restart_decision(
|
||||
RestartMode::Always,
|
||||
/*info*/ None,
|
||||
/*managed_version*/ None
|
||||
/*managed_version*/ None,
|
||||
),
|
||||
],
|
||||
[
|
||||
@@ -787,4 +836,39 @@ mod tests {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_control_start_output_serializes_inner_output_without_tag() {
|
||||
let lifecycle_output = LifecycleOutput {
|
||||
status: LifecycleStatus::AlreadyRunning,
|
||||
backend: Some(BackendKind::Pid),
|
||||
pid: None,
|
||||
socket_path: "codex.sock".into(),
|
||||
cli_version: Some("1.2.3".to_string()),
|
||||
app_server_version: Some("1.2.4".to_string()),
|
||||
};
|
||||
let output = RemoteControlStartOutput::Start(lifecycle_output.clone());
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_value(output).expect("serialize"),
|
||||
serde_json::to_value(lifecycle_output).expect("serialize")
|
||||
);
|
||||
|
||||
let bootstrap_output = BootstrapOutput {
|
||||
status: BootstrapStatus::Bootstrapped,
|
||||
backend: BackendKind::Pid,
|
||||
auto_update_enabled: true,
|
||||
remote_control_enabled: true,
|
||||
managed_codex_path: "codex".into(),
|
||||
socket_path: "codex.sock".into(),
|
||||
cli_version: "1.2.3".to_string(),
|
||||
app_server_version: "1.2.4".to_string(),
|
||||
};
|
||||
let output = RemoteControlStartOutput::Bootstrap(bootstrap_output.clone());
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_value(output).expect("serialize"),
|
||||
serde_json::to_value(bootstrap_output).expect("serialize")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ enum Subcommand {
|
||||
/// [experimental] Run the app server or related tooling.
|
||||
AppServer(AppServerCommand),
|
||||
|
||||
/// [experimental] Start a headless app-server with remote control enabled.
|
||||
/// [experimental] Ensure the app-server daemon is running with remote control enabled.
|
||||
RemoteControl,
|
||||
|
||||
/// Launch the Codex desktop app (opens the app installer if missing).
|
||||
@@ -772,14 +772,6 @@ struct FeatureSetArgs {
|
||||
feature: String,
|
||||
}
|
||||
|
||||
const REMOTE_CONTROL_FEATURE_OVERRIDE: &str = "features.remote_control=true";
|
||||
|
||||
fn enable_remote_control_for_invocation(config_overrides: &mut CliConfigOverrides) {
|
||||
config_overrides
|
||||
.raw_overrides
|
||||
.push(REMOTE_CONTROL_FEATURE_OVERRIDE.to_string());
|
||||
}
|
||||
|
||||
fn stage_str(stage: Stage) -> &'static str {
|
||||
match stage {
|
||||
Stage::UnderDevelopment => "under development",
|
||||
@@ -989,16 +981,8 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
root_remote_auth_token_env.as_deref(),
|
||||
"remote-control",
|
||||
)?;
|
||||
enable_remote_control_for_invocation(&mut root_config_overrides);
|
||||
codex_app_server::run_main_with_transport(
|
||||
arg0_paths.clone(),
|
||||
root_config_overrides,
|
||||
codex_config::LoaderOverrides::default(),
|
||||
/*default_analytics_enabled*/ false,
|
||||
codex_app_server::AppServerTransport::Off,
|
||||
codex_protocol::protocol::SessionSource::Cli,
|
||||
)
|
||||
.await?;
|
||||
let output = codex_app_server_daemon::ensure_remote_control_started().await?;
|
||||
println!("{}", serde_json::to_string(&output)?);
|
||||
}
|
||||
#[cfg(any(target_os = "macos", target_os = "windows"))]
|
||||
Some(Subcommand::App(app_cli)) => {
|
||||
@@ -2408,24 +2392,6 @@ mod tests {
|
||||
assert!(app_server.analytics_default_enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_control_override_is_appended_after_root_toggles() {
|
||||
let mut config_overrides = CliConfigOverrides::default();
|
||||
config_overrides
|
||||
.raw_overrides
|
||||
.push("features.remote_control=false".to_string());
|
||||
|
||||
enable_remote_control_for_invocation(&mut config_overrides);
|
||||
|
||||
assert_eq!(
|
||||
config_overrides.raw_overrides,
|
||||
vec![
|
||||
"features.remote_control=false".to_string(),
|
||||
REMOTE_CONTROL_FEATURE_OVERRIDE.to_string(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reject_remote_flag_for_remote_control() {
|
||||
let cli = MultitoolCli::try_parse_from([
|
||||
|
||||
Reference in New Issue
Block a user