codex_app_server_client/
compat.rs1use std::process::Command;
2
3use serde::{Deserialize, Serialize};
4
5include!(concat!(env!("OUT_DIR"), "/compat_generated.rs"));
6
7#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
8pub struct SurfaceSummary {
9 pub client_requests: usize,
10 pub server_requests: usize,
11 pub server_notifications: usize,
12 pub client_notifications: usize,
13}
14
15impl SurfaceSummary {
16 pub const fn current() -> Self {
17 Self {
18 client_requests: CLIENT_REQUEST_METHOD_COUNT,
19 server_requests: SERVER_REQUEST_METHOD_COUNT,
20 server_notifications: SERVER_NOTIFICATION_METHOD_COUNT,
21 client_notifications: CLIENT_NOTIFICATION_METHOD_COUNT,
22 }
23 }
24}
25
26#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
27pub struct CompatibilityReport {
28 pub schema_codex_version: String,
29 pub installed_codex_version: Option<String>,
30 pub surface: SurfaceSummary,
31}
32
33impl CompatibilityReport {
34 pub fn current() -> Self {
35 Self::from_installed_version(installed_codex_version())
36 }
37
38 pub fn from_installed_version(installed_codex_version: Option<String>) -> Self {
39 Self {
40 schema_codex_version: CODEX_SCHEMA_VERSION.trim().to_owned(),
41 installed_codex_version,
42 surface: SurfaceSummary::current(),
43 }
44 }
45
46 pub fn schema_matches_installed(&self) -> bool {
47 self.installed_codex_version
48 .as_deref()
49 .map(str::trim)
50 .is_some_and(|installed| installed == self.schema_codex_version)
51 }
52}
53
54fn installed_codex_version() -> Option<String> {
55 let output = Command::new("codex").arg("--version").output().ok()?;
56 if !output.status.success() {
57 return None;
58 }
59 let version = String::from_utf8(output.stdout).ok()?;
60 let version = version.trim();
61 (!version.is_empty()).then(|| version.to_owned())
62}