agent_code_lib/services/
bridge.rs1use std::path::PathBuf;
19
20use serde::{Deserialize, Serialize};
21use tracing::info;
22
23pub struct BridgeState {
25 pub port: u16,
27 pub lock_file: PathBuf,
29}
30
31#[derive(Debug, Serialize)]
33pub struct BridgeStatus {
34 pub session_id: String,
35 pub model: String,
36 pub cwd: String,
37 pub is_active: bool,
38 pub turn_count: usize,
39 pub tool_count: usize,
40}
41
42#[derive(Debug, Deserialize)]
44pub struct BridgeMessageRequest {
45 pub content: String,
46}
47
48pub fn write_lock_file(port: u16, cwd: &str) -> Result<PathBuf, String> {
50 let lock_dir = dirs::cache_dir()
51 .unwrap_or_else(|| PathBuf::from("/tmp"))
52 .join("agent-code")
53 .join("bridge");
54
55 std::fs::create_dir_all(&lock_dir)
56 .map_err(|e| format!("Failed to create bridge lock dir: {e}"))?;
57
58 let lock_file = lock_dir.join(format!("{}.lock", std::process::id()));
59
60 let content = serde_json::json!({
61 "port": port,
62 "pid": std::process::id(),
63 "cwd": cwd,
64 "started_at": chrono::Utc::now().to_rfc3339(),
65 });
66
67 std::fs::write(&lock_file, serde_json::to_string_pretty(&content).unwrap())
68 .map_err(|e| format!("Failed to write lock file: {e}"))?;
69
70 info!("Bridge lock file: {}", lock_file.display());
71 Ok(lock_file)
72}
73
74pub fn remove_lock_file(lock_file: &PathBuf) {
76 if lock_file.exists() {
77 let _ = std::fs::remove_file(lock_file);
78 }
79}
80
81pub fn discover_bridges() -> Vec<BridgeInstance> {
83 let lock_dir = match dirs::cache_dir() {
84 Some(d) => d.join("agent-code").join("bridge"),
85 None => return Vec::new(),
86 };
87
88 if !lock_dir.is_dir() {
89 return Vec::new();
90 }
91
92 std::fs::read_dir(&lock_dir)
93 .ok()
94 .into_iter()
95 .flatten()
96 .flatten()
97 .filter_map(|entry| {
98 let content = std::fs::read_to_string(entry.path()).ok()?;
99 let data: serde_json::Value = serde_json::from_str(&content).ok()?;
100
101 let pid = data.get("pid")?.as_u64()? as u32;
102 let port = data.get("port")?.as_u64()? as u16;
103 let cwd = data.get("cwd")?.as_str()?.to_string();
104
105 let alive = std::process::Command::new("kill")
107 .args(["-0", &pid.to_string()])
108 .output()
109 .map(|o| o.status.success())
110 .unwrap_or(false);
111
112 if !alive {
113 let _ = std::fs::remove_file(entry.path());
115 return None;
116 }
117
118 Some(BridgeInstance { pid, port, cwd })
119 })
120 .collect()
121}
122
123#[derive(Debug)]
125pub struct BridgeInstance {
126 pub pid: u32,
127 pub port: u16,
128 pub cwd: String,
129}