codex_mobile_bridge/app_server/
mod.rs1mod handshake;
2mod manager;
3mod running;
4mod streams;
5
6#[cfg(test)]
7mod tests;
8
9use std::collections::HashMap;
10use std::path::PathBuf;
11use std::sync::{
12 Arc,
13 atomic::{AtomicBool, AtomicU64},
14};
15
16use anyhow::Result;
17use serde::{Deserialize, Serialize};
18use serde_json::Value;
19use tokio::io::BufWriter;
20use tokio::process::{Child, ChildStdin};
21use tokio::sync::{Mutex, mpsc, oneshot};
22
23const APP_SERVER_EXPERIMENTAL_API_ENABLED: bool = true;
24const APP_SERVER_OPT_OUT_NOTIFICATION_METHODS: &[&str] = &[
25 "account/login/completed",
26 "account/rateLimits/updated",
27 "account/updated",
28 "app/list/updated",
29 "fs/changed",
30 "fuzzyFileSearch/sessionCompleted",
31 "fuzzyFileSearch/sessionUpdated",
32 "hook/completed",
33 "hook/started",
34 "item/autoApprovalReview/completed",
35 "item/autoApprovalReview/started",
36 "item/commandExecution/terminalInteraction",
37 "mcpServer/oauthLogin/completed",
38 "mcpServer/startupStatus/updated",
39 "skills/changed",
40 "thread/compacted",
41 "thread/realtime/closed",
42 "thread/realtime/error",
43 "thread/realtime/itemAdded",
44 "thread/realtime/outputAudio/delta",
45 "thread/realtime/sdp",
46 "thread/realtime/started",
47 "thread/realtime/transcriptUpdated",
48 "windows/worldWritableWarning",
49 "windowsSandbox/setupCompleted",
50];
51
52#[derive(Debug, Clone)]
53pub struct AppServerLaunchConfig {
54 pub runtime_id: String,
55 pub codex_binary: String,
56 pub codex_home: Option<PathBuf>,
57}
58
59#[derive(Debug, Clone)]
60pub struct InitializeInfo {
61 pub user_agent: String,
62 pub codex_home: String,
63 pub platform_family: String,
64 pub platform_os: String,
65}
66
67#[derive(Debug, Clone)]
68pub enum AppServerInbound {
69 Starting {
70 runtime_id: String,
71 },
72 ProcessChanged {
73 runtime_id: String,
74 pid: Option<u32>,
75 running: bool,
76 },
77 Initializing {
78 runtime_id: String,
79 experimental_api_enabled: bool,
80 opt_out_notification_methods: Vec<String>,
81 },
82 Initialized {
83 runtime_id: String,
84 info: InitializeInfo,
85 experimental_api_enabled: bool,
86 opt_out_notification_methods: Vec<String>,
87 },
88 HandshakeFailed {
89 runtime_id: String,
90 message: String,
91 experimental_api_enabled: bool,
92 opt_out_notification_methods: Vec<String>,
93 },
94 Notification {
95 runtime_id: String,
96 method: String,
97 params: Value,
98 },
99 ServerRequest {
100 runtime_id: String,
101 id: Value,
102 method: String,
103 params: Value,
104 },
105 Exited {
106 runtime_id: String,
107 message: String,
108 expected: bool,
109 },
110 LogChunk {
111 runtime_id: String,
112 stream: String,
113 level: String,
114 source: String,
115 message: String,
116 detail: Option<Value>,
117 occurred_at_ms: i64,
118 },
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct RpcErrorPayload {
123 pub code: i64,
124 pub message: String,
125 #[serde(default)]
126 pub data: Option<Value>,
127}
128
129impl std::fmt::Display for RpcErrorPayload {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 write!(f, "[{}] {}", self.code, self.message)
132 }
133}
134
135pub struct AppServerManager {
136 launch_config: AppServerLaunchConfig,
137 inbound_tx: mpsc::UnboundedSender<AppServerInbound>,
138 inner: Mutex<Option<Arc<RunningAppServer>>>,
139}
140
141struct RunningAppServer {
142 runtime_id: String,
143 stdin: Arc<Mutex<BufWriter<ChildStdin>>>,
144 child: Arc<Mutex<Option<Child>>>,
145 pending: Arc<Mutex<HashMap<String, oneshot::Sender<Result<Value, RpcErrorPayload>>>>>,
146 next_id: AtomicU64,
147 alive: Arc<AtomicBool>,
148 stopping: Arc<AtomicBool>,
149}