mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
enable/disable remote control at runtime, not via features (#22578)
## Why reapplies https://github.com/openai/codex/pull/22386 which was previously reverted Also, introduce `remoteControl/enable` and `remoteControl/disable` app-server APIs to toggle on/off remote control at runtime for a given running app-server instance. ## What Changed - Adds experimental v2 RPCs: - `remoteControl/enable` - `remoteControl/disable` - Adds `RemoteControlRequestProcessor` and routes the new RPCs through it instead of `ConfigRequestProcessor`. - Adds named `RemoteControlHandle::enable`, `disable`, and `status` methods. - Makes `remoteControl/enable` return an error when sqlite state DB is unavailable, while keeping enrollment/websocket failures as async status updates. - Adds `AppServerRuntimeOptions.remote_control_enabled` and hidden `--remote-control` flags for `codex app-server` and `codex-app-server`. - Updates managed daemon startup to use `codex app-server --remote-control --listen unix://`. - Marks `Feature::RemoteControl` as removed and ignores `[features].remote_control`. - Updates app-server README entries for the new remote-control methods.
This commit is contained in:
committed by
GitHub
Unverified
parent
512f8f8012
commit
4e368aa2e9
@@ -8,7 +8,6 @@ use codex_config::RemoteThreadConfigLoader;
|
||||
use codex_config::ThreadConfigLoader;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::resolve_installation_id;
|
||||
use codex_features::Feature;
|
||||
use codex_login::AuthManager;
|
||||
use codex_utils_cli::CliConfigOverrides;
|
||||
use std::collections::HashMap;
|
||||
@@ -401,12 +400,14 @@ pub enum PluginStartupTasks {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct AppServerRuntimeOptions {
|
||||
pub plugin_startup_tasks: PluginStartupTasks,
|
||||
pub remote_control_enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for AppServerRuntimeOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
plugin_startup_tasks: PluginStartupTasks::Start,
|
||||
remote_control_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -684,15 +685,15 @@ pub async fn run_main_with_transport_options(
|
||||
let auth_manager =
|
||||
AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await;
|
||||
|
||||
let remote_control_config_enabled = config.features.enabled(Feature::RemoteControl);
|
||||
let remote_control_enabled = remote_control_config_enabled && state_db.is_some();
|
||||
if remote_control_config_enabled && state_db.is_none() {
|
||||
let remote_control_requested = runtime_options.remote_control_enabled;
|
||||
let remote_control_enabled = remote_control_requested && state_db.is_some();
|
||||
if remote_control_requested && state_db.is_none() {
|
||||
error!("remote control disabled because sqlite state db is unavailable");
|
||||
}
|
||||
if transport_accept_handles.is_empty() && !remote_control_enabled {
|
||||
return Err(std::io::Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
if remote_control_config_enabled && state_db.is_none() {
|
||||
if remote_control_requested && state_db.is_none() {
|
||||
"no transport configured; remote control disabled because sqlite state db is unavailable"
|
||||
} else {
|
||||
"no transport configured; use --listen or enable remote control"
|
||||
|
||||
@@ -48,6 +48,10 @@ struct AppServerArgs {
|
||||
#[cfg(debug_assertions)]
|
||||
#[arg(long = "disable-plugin-startup-tasks-for-tests", hide = true)]
|
||||
disable_plugin_startup_tasks_for_tests: bool,
|
||||
|
||||
/// Enable remote control for this app-server process.
|
||||
#[arg(long = "remote-control", hide = true)]
|
||||
remote_control: bool,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
@@ -59,6 +63,7 @@ fn main() -> anyhow::Result<()> {
|
||||
strict_config,
|
||||
#[cfg(debug_assertions)]
|
||||
disable_plugin_startup_tasks_for_tests,
|
||||
remote_control,
|
||||
} = AppServerArgs::parse();
|
||||
let loader_overrides = if disable_managed_config_from_debug_env() {
|
||||
LoaderOverrides::without_managed_config_for_tests()
|
||||
@@ -74,6 +79,7 @@ fn main() -> anyhow::Result<()> {
|
||||
if disable_plugin_startup_tasks_for_tests {
|
||||
runtime_options.plugin_startup_tasks = PluginStartupTasks::Skip;
|
||||
}
|
||||
runtime_options.remote_control_enabled = remote_control;
|
||||
|
||||
run_main_with_transport_options(
|
||||
arg0_paths,
|
||||
|
||||
@@ -30,6 +30,7 @@ use crate::request_processors::MarketplaceRequestProcessor;
|
||||
use crate::request_processors::McpRequestProcessor;
|
||||
use crate::request_processors::PluginRequestProcessor;
|
||||
use crate::request_processors::ProcessExecRequestProcessor;
|
||||
use crate::request_processors::RemoteControlRequestProcessor;
|
||||
use crate::request_processors::SearchRequestProcessor;
|
||||
use crate::request_processors::ThreadGoalRequestProcessor;
|
||||
use crate::request_processors::ThreadRequestProcessor;
|
||||
@@ -173,6 +174,7 @@ pub(crate) struct MessageProcessor {
|
||||
marketplace_processor: MarketplaceRequestProcessor,
|
||||
mcp_processor: McpRequestProcessor,
|
||||
plugin_processor: PluginRequestProcessor,
|
||||
remote_control_processor: RemoteControlRequestProcessor,
|
||||
search_processor: SearchRequestProcessor,
|
||||
thread_goal_processor: ThreadGoalRequestProcessor,
|
||||
thread_processor: ThreadRequestProcessor,
|
||||
@@ -389,6 +391,7 @@ impl MessageProcessor {
|
||||
config_manager.clone(),
|
||||
workspace_settings_cache,
|
||||
);
|
||||
let remote_control_processor = RemoteControlRequestProcessor::new(remote_control_handle);
|
||||
let search_processor = SearchRequestProcessor::new(outgoing.clone());
|
||||
let thread_goal_processor = ThreadGoalRequestProcessor::new(
|
||||
Arc::clone(&thread_manager),
|
||||
@@ -446,7 +449,6 @@ impl MessageProcessor {
|
||||
auth_manager,
|
||||
thread_manager.clone(),
|
||||
analytics_events_client,
|
||||
remote_control_handle,
|
||||
);
|
||||
let external_agent_config_processor = ExternalAgentConfigRequestProcessor::new(
|
||||
outgoing.clone(),
|
||||
@@ -488,6 +490,7 @@ impl MessageProcessor {
|
||||
marketplace_processor,
|
||||
mcp_processor,
|
||||
plugin_processor,
|
||||
remote_control_processor,
|
||||
search_processor,
|
||||
thread_goal_processor,
|
||||
thread_processor,
|
||||
@@ -886,6 +889,14 @@ impl MessageProcessor {
|
||||
.experimental_feature_enablement_set(request_id.clone(), params)
|
||||
.await
|
||||
}
|
||||
ClientRequest::RemoteControlEnable { .. } => self
|
||||
.remote_control_processor
|
||||
.enable()
|
||||
.map(|response| Some(response.into())),
|
||||
ClientRequest::RemoteControlDisable { .. } => self
|
||||
.remote_control_processor
|
||||
.disable()
|
||||
.map(|response| Some(response.into())),
|
||||
ClientRequest::ConfigRequirementsRead { params: _, .. } => self
|
||||
.config_processor
|
||||
.config_requirements_read()
|
||||
|
||||
@@ -448,6 +448,7 @@ mod marketplace_processor;
|
||||
mod mcp_processor;
|
||||
mod plugins;
|
||||
mod process_exec_processor;
|
||||
mod remote_control_processor;
|
||||
mod search;
|
||||
mod thread_processor;
|
||||
mod token_usage_replay;
|
||||
@@ -469,6 +470,7 @@ pub(crate) use marketplace_processor::MarketplaceRequestProcessor;
|
||||
pub(crate) use mcp_processor::McpRequestProcessor;
|
||||
pub(crate) use plugins::PluginRequestProcessor;
|
||||
pub(crate) use process_exec_processor::ProcessExecRequestProcessor;
|
||||
pub(crate) use remote_control_processor::RemoteControlRequestProcessor;
|
||||
pub(crate) use search::SearchRequestProcessor;
|
||||
pub(crate) use thread_goal_processor::ThreadGoalRequestProcessor;
|
||||
pub(crate) use thread_processor::ThreadRequestProcessor;
|
||||
|
||||
@@ -6,7 +6,6 @@ use crate::error_code::internal_error;
|
||||
use crate::error_code::invalid_request;
|
||||
use crate::outgoing_message::ConnectionRequestId;
|
||||
use crate::outgoing_message::OutgoingMessageSender;
|
||||
use crate::transport::RemoteControlHandle;
|
||||
use codex_analytics::AnalyticsEventsClient;
|
||||
use codex_app_server_protocol::AppListUpdatedNotification;
|
||||
use codex_app_server_protocol::ClientResponsePayload;
|
||||
@@ -39,7 +38,6 @@ use codex_config::MatcherGroup as CoreMatcherGroup;
|
||||
use codex_config::ResidencyRequirement as CoreResidencyRequirement;
|
||||
use codex_config::SandboxModeRequirement as CoreSandboxModeRequirement;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_features::Feature;
|
||||
use codex_features::canonical_feature_for_key;
|
||||
use codex_features::feature_for_key;
|
||||
use codex_login::AuthManager;
|
||||
@@ -67,7 +65,6 @@ pub(crate) struct ConfigRequestProcessor {
|
||||
auth_manager: Arc<AuthManager>,
|
||||
thread_manager: Arc<ThreadManager>,
|
||||
analytics_events_client: AnalyticsEventsClient,
|
||||
remote_control_handle: Option<RemoteControlHandle>,
|
||||
}
|
||||
|
||||
impl ConfigRequestProcessor {
|
||||
@@ -77,7 +74,6 @@ impl ConfigRequestProcessor {
|
||||
auth_manager: Arc<AuthManager>,
|
||||
thread_manager: Arc<ThreadManager>,
|
||||
analytics_events_client: AnalyticsEventsClient,
|
||||
remote_control_handle: Option<RemoteControlHandle>,
|
||||
) -> Self {
|
||||
Self {
|
||||
outgoing,
|
||||
@@ -85,7 +81,6 @@ impl ConfigRequestProcessor {
|
||||
auth_manager,
|
||||
thread_manager,
|
||||
analytics_events_client,
|
||||
remote_control_handle,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,21 +182,6 @@ impl ConfigRequestProcessor {
|
||||
pub(crate) async fn handle_config_mutation(&self) {
|
||||
self.thread_manager.plugins_manager().clear_cache();
|
||||
self.thread_manager.skills_manager().clear_cache();
|
||||
let Some(remote_control_handle) = &self.remote_control_handle else {
|
||||
return;
|
||||
};
|
||||
|
||||
match self.load_latest_config(/*fallback_cwd*/ None).await {
|
||||
Ok(config) => {
|
||||
remote_control_handle.set_enabled(config.features.enabled(Feature::RemoteControl));
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
"failed to load config for remote control enablement refresh after config mutation: {}",
|
||||
error.message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_config_mutation_result<T>(
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
use crate::error_code::internal_error;
|
||||
use crate::error_code::invalid_request;
|
||||
use crate::transport::RemoteControlHandle;
|
||||
use crate::transport::RemoteControlUnavailable;
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::RemoteControlDisableResponse;
|
||||
use codex_app_server_protocol::RemoteControlEnableResponse;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RemoteControlRequestProcessor {
|
||||
remote_control_handle: Option<RemoteControlHandle>,
|
||||
}
|
||||
|
||||
impl RemoteControlRequestProcessor {
|
||||
pub(crate) fn new(remote_control_handle: Option<RemoteControlHandle>) -> Self {
|
||||
Self {
|
||||
remote_control_handle,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enable(&self) -> Result<RemoteControlEnableResponse, JSONRPCErrorError> {
|
||||
let handle = self.handle()?;
|
||||
handle
|
||||
.enable()
|
||||
.map(RemoteControlEnableResponse::from)
|
||||
.map_err(map_unavailable)
|
||||
}
|
||||
|
||||
pub(crate) fn disable(&self) -> Result<RemoteControlDisableResponse, JSONRPCErrorError> {
|
||||
let handle = self.handle()?;
|
||||
Ok(RemoteControlDisableResponse::from(handle.disable()))
|
||||
}
|
||||
|
||||
fn handle(&self) -> Result<&RemoteControlHandle, JSONRPCErrorError> {
|
||||
self.remote_control_handle
|
||||
.as_ref()
|
||||
.ok_or_else(|| internal_error("remote control is unavailable for this app-server"))
|
||||
}
|
||||
}
|
||||
|
||||
fn map_unavailable(err: RemoteControlUnavailable) -> JSONRPCErrorError {
|
||||
invalid_request(err.to_string())
|
||||
}
|
||||
@@ -20,6 +20,7 @@ pub(crate) use codex_app_server_transport::OutgoingMessage;
|
||||
pub(crate) use codex_app_server_transport::QueuedOutgoingMessage;
|
||||
pub(crate) use codex_app_server_transport::RemoteControlHandle;
|
||||
pub(crate) use codex_app_server_transport::RemoteControlStartConfig;
|
||||
pub(crate) use codex_app_server_transport::RemoteControlUnavailable;
|
||||
pub(crate) use codex_app_server_transport::TransportEvent;
|
||||
pub use codex_app_server_transport::app_server_control_socket_path;
|
||||
pub use codex_app_server_transport::auth;
|
||||
|
||||
Reference in New Issue
Block a user