Skip to content
Merged
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
6 changes: 6 additions & 0 deletions code-rs/app-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ use code_common::CliConfigOverrides;

#[derive(Debug, Parser)]
struct AppServerArgs {
/// Accepted for Codex Desktop compatibility. Every Code handles analytics
/// policy through its normal config path, so this flag is intentionally a
/// no-op for the app-server process.
#[arg(long = "analytics-default-enabled", default_value_t = false)]
_analytics_default_enabled: bool,

/// Transport endpoint URL. Supported values: `stdio://` (default),
/// `ws://IP:PORT`.
#[arg(
Expand Down
95 changes: 95 additions & 0 deletions code-rs/app-server/src/message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ use code_app_server_protocol::ConfigWriteErrorCode;
use code_app_server_protocol::ConfigWriteResponse;
use code_app_server_protocol::ExternalAgentConfigDetectParams;
use code_app_server_protocol::ExternalAgentConfigImportParams;
use code_app_server_protocol::ExperimentalFeatureListResponse;
use code_app_server_protocol::GetAccountParams;
use code_app_server_protocol::ListMcpServerStatusResponse;
use code_app_server_protocol::LoginAccountParams;
use code_app_server_protocol::MergeStrategy;
use code_app_server_protocol::ModelListResponse;
use code_app_server_protocol::ThreadListResponse;
use code_app_server_protocol::ToolsV2;
use code_app_server_protocol::AskForApproval as V2AskForApproval;
use code_app_server_protocol::WriteStatus;
Expand Down Expand Up @@ -279,6 +283,17 @@ impl MessageProcessor {
| "config/batchWrite"
| "externalAgentConfig/detect"
| "externalAgentConfig/import"
| "thread/list"
| "model/list"
| "skills/list"
| "plugin/list"
| "hooks/list"
| "mcpServerStatus/list"
| "remoteControl/status/read"
| "remoteControl/enable"
| "collaborationMode/list"
| "experimentalFeature/list"
| "experimentalFeature/enablement/set"
| "account/read"
| "account/login/start"
| "account/login/cancel"
Expand Down Expand Up @@ -453,6 +468,86 @@ impl MessageProcessor {
}
true
}
"thread/list" => {
let response = ThreadListResponse {
data: Vec::new(),
next_cursor: None,
};
self.outgoing.send_response(request_id, response).await;
true
}
"model/list" => {
let response = ModelListResponse {
data: Vec::new(),
next_cursor: None,
};
self.outgoing.send_response(request_id, response).await;
true
}
"skills/list" => {
self.outgoing
.send_response(request_id, json!({ "data": [], "nextCursor": null }))
.await;
true
}
"plugin/list" | "hooks/list" => {
self.outgoing
.send_response(request_id, json!({ "data": [], "nextCursor": null }))
.await;
true
}
"mcpServerStatus/list" => {
let response = ListMcpServerStatusResponse {
data: Vec::new(),
next_cursor: None,
};
self.outgoing.send_response(request_id, response).await;
true
}
"remoteControl/status/read" => {
self.outgoing
.send_response(request_id, json!({ "enabled": false }))
.await;
true
}
"remoteControl/enable" => {
self.outgoing
.send_response(
request_id,
json!({
"enabled": false,
"unsupported": true,
}),
)
.await;
true
}
"collaborationMode/list" => {
self.outgoing
.send_response(request_id, json!({ "data": [], "nextCursor": null }))
.await;
true
}
"experimentalFeature/list" => {
let response = ExperimentalFeatureListResponse {
data: Vec::new(),
next_cursor: None,
};
self.outgoing.send_response(request_id, response).await;
true
}
"experimentalFeature/enablement/set" => {
self.outgoing
.send_response(
request_id,
json!({
"enabled": false,
"unsupported": true,
}),
)
.await;
true
}
"account/read" => {
let params_value = request.params.clone().unwrap_or_else(|| json!({}));
let params: GetAccountParams = match serde_json::from_value(params_value) {
Expand Down
77 changes: 76 additions & 1 deletion code-rs/app-server/tests/binary_smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ fn app_server_bin() -> PathBuf {
PathBuf::from(assert_cmd::cargo::cargo_bin!("code-app-server"))
}

fn run_jsonrpc_script(requests: &[Value]) -> BTreeMap<i64, Value> {
fn run_jsonrpc_script_with_args(args: &[&str], requests: &[Value]) -> BTreeMap<i64, Value> {
let mut child = Command::new(app_server_bin())
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
Expand Down Expand Up @@ -51,6 +52,10 @@ fn run_jsonrpc_script(requests: &[Value]) -> BTreeMap<i64, Value> {
.collect()
}

fn run_jsonrpc_script(requests: &[Value]) -> BTreeMap<i64, Value> {
run_jsonrpc_script_with_args(&[], requests)
}

#[test]
fn binary_smoke_requires_init_and_executes_command() {
let marker = "hello-from-app-server-binary-smoke";
Expand Down Expand Up @@ -122,3 +127,73 @@ fn binary_smoke_requires_init_and_executes_command() {
"execOneOffCommand stdout missing marker. stdout was: {stdout}"
);
}

#[test]
fn binary_smoke_accepts_desktop_startup_polling_methods() {
let requests = vec![
json!({
"jsonrpc":"2.0",
"id":1,
"method":"initialize",
"params":{
"clientInfo":{
"name":"codex-desktop-smoke",
"version":"0.1.0"
}
}
}),
json!({"jsonrpc":"2.0","id":2,"method":"thread/list","params":{}}),
json!({"jsonrpc":"2.0","id":3,"method":"model/list","params":{}}),
json!({"jsonrpc":"2.0","id":4,"method":"skills/list","params":{}}),
json!({"jsonrpc":"2.0","id":5,"method":"plugin/list","params":{}}),
json!({"jsonrpc":"2.0","id":6,"method":"hooks/list","params":{}}),
json!({"jsonrpc":"2.0","id":7,"method":"mcpServerStatus/list","params":{}}),
json!({"jsonrpc":"2.0","id":8,"method":"remoteControl/status/read","params":{}}),
json!({"jsonrpc":"2.0","id":9,"method":"remoteControl/enable","params":{"enabled":true}}),
json!({"jsonrpc":"2.0","id":10,"method":"collaborationMode/list","params":{}}),
json!({"jsonrpc":"2.0","id":11,"method":"experimentalFeature/list","params":{}}),
json!({"jsonrpc":"2.0","id":12,"method":"experimentalFeature/enablement/set","params":{"featureId":"desktop-smoke","enabled":true}}),
];

let responses = run_jsonrpc_script_with_args(&["--analytics-default-enabled"], &requests);

for id in 2..=12 {
let response = responses
.get(&id)
.unwrap_or_else(|| panic!("missing response for request id {id}"));
assert!(
response.get("error").is_none(),
"desktop startup method returned error for id {id}: {response}"
);
}

for id in [2, 3, 4, 5, 6, 7, 10, 11] {
let result = responses
.get(&id)
.and_then(|response| response.get("result"))
.expect("expected list result");
assert_eq!(result.get("data"), Some(&json!([])));
assert_eq!(result.get("nextCursor"), Some(&json!(null)));
}
assert_eq!(
responses
.get(&8)
.and_then(|response| response.get("result"))
.and_then(|result| result.get("enabled")),
Some(&json!(false))
);
assert_eq!(
responses
.get(&9)
.and_then(|response| response.get("result"))
.and_then(|result| result.get("unsupported")),
Some(&json!(true))
);
assert_eq!(
responses
.get(&12)
.and_then(|response| response.get("result"))
.and_then(|result| result.get("unsupported")),
Some(&json!(true))
);
}
30 changes: 27 additions & 3 deletions code-rs/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod llm;
mod update;
use llm::{LlmCli, run_llm};
use update::{UpdateCheckCommand, UpdateCommand, run_update, run_update_check};
use code_app_server::AppServerTransport;
use code_common::CliConfigOverrides;
use code_core::{entry_to_rollout_path, SessionCatalog, SessionQuery};
use code_core::spawn::spawn_std_command_with_retry;
Expand Down Expand Up @@ -122,7 +123,7 @@ enum Subcommand {
McpServer,

/// [experimental] Run the app server.
AppServer,
AppServer(AppServerArgs),

/// Generate shell completion scripts.
Completion(CompletionCommand),
Expand Down Expand Up @@ -172,6 +173,24 @@ enum Subcommand {
Bridge(BridgeCommand),
}

#[derive(Debug, Parser)]
struct AppServerArgs {
/// Accepted for Codex Desktop compatibility. Every Code handles analytics
/// policy through its normal config path, so this flag is intentionally a
/// no-op for the app-server process.
#[arg(long = "analytics-default-enabled", default_value_t = false)]
_analytics_default_enabled: bool,

/// Transport endpoint URL. Supported values: `stdio://` (default),
/// `ws://IP:PORT`.
#[arg(
long = "listen",
value_name = "URL",
default_value = AppServerTransport::DEFAULT_LISTEN_URL
)]
listen: AppServerTransport,
}

#[derive(Debug, Parser)]
struct CompletionCommand {
/// Shell to generate completions for
Expand Down Expand Up @@ -499,8 +518,13 @@ async fn cli_main(code_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()>
prepend_config_flags(&mut mcp_cli.config_overrides, root_config_overrides.clone());
mcp_cli.run().await?;
}
Some(Subcommand::AppServer) => {
code_app_server::run_main(code_linux_sandbox_exe, root_config_overrides).await?;
Some(Subcommand::AppServer(args)) => {
code_app_server::run_main_with_transport(
code_linux_sandbox_exe,
root_config_overrides,
args.listen,
)
.await?;
}
Some(Subcommand::Resume(ResumeCommand {
session_id,
Expand Down