From c758d8894bd33a051e0165026e5f7789d94a6c7f Mon Sep 17 00:00:00 2001 From: celia-oai Date: Wed, 8 Jul 2026 16:42:26 -0700 Subject: [PATCH] app-server-test-client: add logout command --- codex-rs/app-server-test-client/README.md | 12 +++++++ codex-rs/app-server-test-client/src/lib.rs | 39 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/codex-rs/app-server-test-client/README.md b/codex-rs/app-server-test-client/README.md index 9f73b1db6f..9d559d80b4 100644 --- a/codex-rs/app-server-test-client/README.md +++ b/codex-rs/app-server-test-client/README.md @@ -46,6 +46,18 @@ cargo run -p codex-app-server-test-client -- \ The test client redacts `apiKey` from its outbound request log. After login, start a fresh Codex process with the same `CODEX_HOME` to verify that it uses the persisted managed credential. +## Testing logout + +`test-logout` initializes the app-server, sends an `account/logout` request, and waits for the +resulting `account/updated` notification. It uses the active `CODEX_HOME`, so point it at an +isolated directory when testing credential cleanup. + +```bash +cargo run -p codex-app-server-test-client -- \ + --codex-bin ./target/debug/codex \ + test-logout +``` + ## Testing Plugin Analytics The `plugin-analytics-smoke` command exercises `plugin/installed`, plugin diff --git a/codex-rs/app-server-test-client/src/lib.rs b/codex-rs/app-server-test-client/src/lib.rs index 10d66dbc01..21becb302f 100644 --- a/codex-rs/app-server-test-client/src/lib.rs +++ b/codex-rs/app-server-test-client/src/lib.rs @@ -46,6 +46,7 @@ use codex_app_server_protocol::JSONRPCNotification; use codex_app_server_protocol::JSONRPCRequest; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::LoginAccountResponse; +use codex_app_server_protocol::LogoutAccountResponse; use codex_app_server_protocol::ModelListParams; use codex_app_server_protocol::ModelListResponse; use codex_app_server_protocol::RequestId; @@ -245,6 +246,8 @@ enum CliCommand { #[arg(long, value_name = "REGION")] region: Option, }, + /// Log out of the current account and wait for the account update. + TestLogout, /// Fetch the current account rate limits from the Codex app-server. GetAccountRateLimits, /// List the available models from the Codex app-server. @@ -449,6 +452,11 @@ pub async fn run() -> Result<()> { }; test_login(&endpoint, &config_overrides, mode).await } + CliCommand::TestLogout => { + ensure_dynamic_tools_unused(&dynamic_tools, "test-logout")?; + let endpoint = resolve_endpoint(codex_bin, url)?; + test_logout(&endpoint, &config_overrides).await + } CliCommand::GetAccountRateLimits => { ensure_dynamic_tools_unused(&dynamic_tools, "get-account-rate-limits")?; let endpoint = resolve_endpoint(codex_bin, url)?; @@ -1253,6 +1261,27 @@ async fn get_account_rate_limits(endpoint: &Endpoint, config_overrides: &[String .await } +async fn test_logout(endpoint: &Endpoint, config_overrides: &[String]) -> Result<()> { + with_client("test-logout", endpoint, config_overrides, |client| { + let initialize = client.initialize()?; + println!("< initialize response: {initialize:?}"); + + let response = client.logout_account()?; + println!("< account/logout response: {response:?}"); + + loop { + let notification = client.next_notification()?; + if let Ok(ServerNotification::AccountUpdated(account_updated)) = + ServerNotification::try_from(notification) + { + println!("< account/updated notification: {account_updated:?}"); + return Ok(()); + } + } + }) + .await +} + async fn model_list(endpoint: &Endpoint, config_overrides: &[String]) -> Result<()> { with_client("model-list", endpoint, config_overrides, |client| { let initialize = client.initialize()?; @@ -1809,6 +1838,16 @@ impl CodexClient { self.send_request(request, request_id, "account/rateLimits/read") } + fn logout_account(&mut self) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::LogoutAccount { + request_id: request_id.clone(), + params: None, + }; + + self.send_request(request, request_id, "account/logout") + } + fn model_list(&mut self, params: ModelListParams) -> Result { let request_id = self.request_id(); let request = ClientRequest::ModelList {