Files
codex/codex-rs/app-server-test-client

App Server Test Client

Quickstart for running and hitting codex app-server.

Quickstart

Run from <reporoot>/codex-rs.

# 1) Build debug codex binary
cargo build -p codex-cli --bin codex

# 2) Start websocket app-server in background
cargo run -p codex-app-server-test-client -- \
  --codex-bin ./target/debug/codex \
  serve --listen ws://127.0.0.1:4222 --kill

# 3) Call app-server (defaults to ws://127.0.0.1:4222)
cargo run -p codex-app-server-test-client -- model-list

Testing Plugin Analytics

The plugin-analytics-smoke command exercises plugin/installed, plugin enable/disable config writes, and a structured plugin mention through one app-server connection. Analytics are captured to a local JSONL file and are not sent to the analytics backend. The model turn uses a loopback Responses API server.

The selected plugin must already be installed remotely, and the active Codex profile must be authenticated.

# Build a debug Codex binary; analytics capture is unavailable in release builds.
cargo build -p codex-cli --bin codex

cargo run -p codex-app-server-test-client -- \
  --codex-bin ./target/debug/codex \
  plugin-analytics-smoke \
  --plugin-id linear@openai-curated-remote

Use --capture-file /tmp/plugin-analytics.jsonl to select the output path. The command validates one codex_plugin_disabled, codex_plugin_enabled, and codex_plugin_used event, prints them, and leaves the JSONL file in place for inspection. Each event must retain the local ID in both plugin_id and local_plugin_id, and include the backend ID in remote_plugin_id. The command does not install or uninstall plugins and does not modify the profile's persistent config.

Testing remote install and uninstall analytics

plugin-analytics-mutation-smoke is a manually invoked live smoke test. It contacts the configured remote plugin API and temporarily changes the active account's installed-plugin state. It is not run by cargo test, just test, or CI.

Choose a remote plugin that is available to the active account and is not currently installed. The command refuses to run when the plugin is already installed, installs it, validates codex_plugin_installed, uninstalls it, validates codex_plugin_uninstalled, and verifies that the original uninstalled state was restored.

The mutation events retain the backend ID in the legacy plugin_id field, include the local Codex ID in local_plugin_id, and include the backend ID in remote_plugin_id.

--remote-plugin-id takes the backend ID, such as plugins~Plugin_..., not the local <plugin>@<marketplace> ID.

cargo run -p codex-app-server-test-client -- \
  --codex-bin ./target/debug/codex \
  plugin-analytics-mutation-smoke \
  --remote-plugin-id <REMOTE_PLUGIN_ID> \
  --confirm-account-mutation \
  --capture-file /tmp/plugin-mutation-analytics.jsonl

Analytics use the normal queue, reduction, batching, and serialization path, but the debug capture destination suppresses analytics network delivery. The command prints one of these final states:

  • PASS: both events validated and the plugin is uninstalled.
  • FAIL-CLEAN: validation failed, but the original uninstalled state was restored.
  • FAIL-LOCAL-CACHE: the backend is uninstalled, but local cleanup reported an error.
  • FAIL-DIRTY: cleanup failed and the plugin still appears installed.
  • FAIL-UNKNOWN: the command could not verify the final installed state.

For a dirty or uncertain result, retry cleanup with:

cargo run -p codex-app-server-test-client -- \
  --codex-bin ./target/debug/codex \
  plugin-remote-uninstall \
  --remote-plugin-id <REMOTE_PLUGIN_ID> \
  --confirm-account-mutation

Watching Raw Inbound Traffic

Initialize a connection, then print every inbound JSON-RPC message until you stop it with Ctrl+C:

cargo run -p codex-app-server-test-client -- watch

Testing Thread Rejoin Behavior

Build and start an app server using commands above. The app-server log is written to /tmp/codex-app-server-test-client/app-server.log

1) Get a thread id

Create at least one thread, then list threads:

cargo run -p codex-app-server-test-client -- send-message-v2 "seed thread for rejoin test"
cargo run -p codex-app-server-test-client -- thread-list --limit 5

Copy a thread id from the thread-list output.

2) Rejoin while a turn is in progress (two terminals)

Terminal A:

cargo run --bin codex-app-server-test-client -- \
  resume-message-v2 <THREAD_ID> "respond with thorough docs on the rust core"

Terminal B (while Terminal A is still streaming):

cargo run --bin codex-app-server-test-client -- thread-resume <THREAD_ID>