Skip to main content

codex_mobile_bridge/bridge_protocol/
runtime.rs

1use serde::{Deserialize, Serialize};
2
3use super::helpers::now_millis;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct ApiError {
7    pub code: String,
8    pub message: String,
9}
10
11impl ApiError {
12    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
13        Self {
14            code: code.into(),
15            message: message.into(),
16        }
17    }
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21#[serde(rename_all = "camelCase")]
22pub struct RuntimeRecord {
23    pub runtime_id: String,
24    pub display_name: String,
25    pub codex_home: Option<String>,
26    pub codex_binary: String,
27    pub is_primary: bool,
28    pub auto_start: bool,
29    pub created_at_ms: i64,
30    pub updated_at_ms: i64,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub struct AppServerHandshakeSummary {
36    pub state: String,
37    pub protocol: String,
38    pub transport: String,
39    pub experimental_api_enabled: bool,
40    pub notification_mode: String,
41    #[serde(default)]
42    pub opt_out_notification_methods: Vec<String>,
43    pub detail: Option<String>,
44    pub updated_at_ms: i64,
45}
46
47impl Default for AppServerHandshakeSummary {
48    fn default() -> Self {
49        Self::inactive()
50    }
51}
52
53impl AppServerHandshakeSummary {
54    pub fn new(
55        state: impl Into<String>,
56        experimental_api_enabled: bool,
57        opt_out_notification_methods: Vec<String>,
58        detail: Option<String>,
59    ) -> Self {
60        let notification_mode = if opt_out_notification_methods.is_empty() {
61            "full".to_string()
62        } else {
63            "optimized".to_string()
64        };
65        Self {
66            state: state.into(),
67            protocol: "jsonrpc2.0".to_string(),
68            transport: "stdio".to_string(),
69            experimental_api_enabled,
70            notification_mode,
71            opt_out_notification_methods,
72            detail,
73            updated_at_ms: now_millis(),
74        }
75    }
76
77    pub fn inactive() -> Self {
78        Self::new("inactive", false, Vec::new(), None)
79    }
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(rename_all = "camelCase")]
84pub struct RuntimeStatusSnapshot {
85    pub runtime_id: String,
86    pub status: String,
87    pub codex_home: Option<String>,
88    pub user_agent: Option<String>,
89    pub platform_family: Option<String>,
90    pub platform_os: Option<String>,
91    pub last_error: Option<String>,
92    pub pid: Option<u32>,
93    #[serde(default)]
94    pub app_server_handshake: AppServerHandshakeSummary,
95    pub updated_at_ms: i64,
96}
97
98impl RuntimeStatusSnapshot {
99    pub fn stopped(runtime_id: impl Into<String>) -> Self {
100        Self {
101            runtime_id: runtime_id.into(),
102            status: "stopped".to_string(),
103            codex_home: None,
104            user_agent: None,
105            platform_family: None,
106            platform_os: None,
107            last_error: None,
108            pid: None,
109            app_server_handshake: AppServerHandshakeSummary::inactive(),
110            updated_at_ms: now_millis(),
111        }
112    }
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct RuntimeSummary {
118    pub runtime_id: String,
119    pub display_name: String,
120    pub codex_home: Option<String>,
121    pub codex_binary: String,
122    pub is_primary: bool,
123    pub auto_start: bool,
124    pub created_at_ms: i64,
125    pub updated_at_ms: i64,
126    pub status: RuntimeStatusSnapshot,
127}
128
129impl RuntimeSummary {
130    pub fn from_parts(record: &RuntimeRecord, status: RuntimeStatusSnapshot) -> Self {
131        Self {
132            runtime_id: record.runtime_id.clone(),
133            display_name: record.display_name.clone(),
134            codex_home: record.codex_home.clone(),
135            codex_binary: record.codex_binary.clone(),
136            is_primary: record.is_primary,
137            auto_start: record.auto_start,
138            created_at_ms: record.created_at_ms,
139            updated_at_ms: record.updated_at_ms,
140            status,
141        }
142    }
143}