Mountain/ProcessManagement/
CocoonManagement.rs1#![allow(non_snake_case, non_camel_case_types)]
11
12use std::{collections::HashMap, process::Stdio, sync::Arc, time::Duration};
13
14use Common::Error::CommonError::CommonError;
15use log::{info, trace, warn};
16use tauri::{
17 AppHandle,
18 Manager,
19 Wry,
20 path::{BaseDirectory, PathResolver},
21};
22use tokio::{
23 io::{AsyncBufReadExt, BufReader},
24 process::Command,
25 time::sleep,
26};
27
28use super::InitializationData;
29use crate::{Environment::MountainEnvironment::MountainEnvironment, Vine};
30
31pub async fn InitializeCocoon(
34 ApplicationHandle:&AppHandle,
35
36 Environment:&Arc<MountainEnvironment>,
37) -> Result<(), CommonError> {
38 info!("[CocoonManagement] Initializing Cocoon sidecar manager...");
39
40 #[cfg(feature = "ExtensionHostCocoon")]
41 {
42 LaunchAndManageCocoonSideCar(ApplicationHandle.clone(), Environment.clone()).await
44 }
45
46 #[cfg(not(feature = "ExtensionHostCocoon"))]
47 {
48 info!("[CocoonManagement] 'ExtensionHostCocoon' feature is disabled. Cocoon will not be launched.");
49
50 Ok(())
51 }
52}
53
54async fn LaunchAndManageCocoonSideCar(
56 ApplicationHandle:AppHandle,
57
58 Environment:Arc<MountainEnvironment>,
59) -> Result<(), CommonError> {
60 let SideCarIdentifier = "cocoon-main".to_string();
61
62 let path_resolver:PathResolver<Wry> = ApplicationHandle.path().clone();
63
64 let ScriptPath = path_resolver
65 .resolve("scripts/cocoon/bootstrap-fork.js", BaseDirectory::Resource)
66 .map_err(|Error| CommonError::FileSystemNotFound(Error.to_string().into()))?;
67
68 if !ScriptPath.exists() {
69 return Err(CommonError::FileSystemNotFound(
70 "Cocoon bootstrap-fork.js script not found.".into(),
71 ));
72 }
73
74 let mut NodeCommand = Command::new("node");
75
76 let mut EnvironmentVariables = HashMap::new();
77
78 EnvironmentVariables.insert("VSCODE_PIPE_LOGGING".to_string(), "true".to_string());
79
80 EnvironmentVariables.insert("VSCODE_VERBOSE_LOGGING".to_string(), "true".to_string());
81
82 EnvironmentVariables.insert("VSCODE_PARENT_PID".to_string(), std::process::id().to_string());
83
84 EnvironmentVariables.insert("MOUNTAIN_GRPC_PORT".to_string(), "50051".to_string());
85
86 EnvironmentVariables.insert("COCOON_GRPC_PORT".to_string(), "50052".to_string());
87
88 NodeCommand
89 .arg(&ScriptPath)
90 .env_clear()
91 .envs(EnvironmentVariables)
92 .stdin(Stdio::piped())
93 .stdout(Stdio::piped())
94 .stderr(Stdio::piped());
95
96 let mut ChildProcess = NodeCommand
97 .spawn()
98 .map_err(|Error| CommonError::IPCError { Description:format!("Failed to spawn Cocoon: {}", Error) })?;
99
100 info!("[CocoonManagement] Cocoon process spawned [PID: {:?}]", ChildProcess.id());
101
102 if let Some(stdout) = ChildProcess.stdout.take() {
103 tokio::spawn(async move {
104 let Reader = BufReader::new(stdout);
105
106 let mut Lines = Reader.lines();
107
108 while let Ok(Some(Line)) = Lines.next_line().await {
109 trace!("[Cocoon stdout] {}", Line);
110 }
111 });
112 }
113 if let Some(stderr) = ChildProcess.stderr.take() {
114 tokio::spawn(async move {
115 let Reader = BufReader::new(stderr);
116
117 let mut Lines = Reader.lines();
118
119 while let Ok(Some(Line)) = Lines.next_line().await {
120 warn!("[Cocoon stderr] {}", Line);
121 }
122 });
123 }
124
125 info!("[CocoonManagement] Waiting for Cocoon gRPC server to start...");
126
127 sleep(Duration::from_millis(2000)).await;
128
129 Vine::Client::ConnectToSideCar(SideCarIdentifier.clone(), "127.0.0.1:50052".to_string())
130 .await
131 .map_err(|Error| CommonError::IPCError { Description:Error.to_string() })?;
132
133 info!("[CocoonManagement] Cocoon is ready. Sending initialization data...");
134
135 let MainInitializationData = InitializationData::ConstructExtensionHostInitializationData(&Environment).await?;
136
137 let Response = Vine::Client::SendRequest(
138 &SideCarIdentifier,
139 "InitializeExtensionHost".to_string(),
140 MainInitializationData,
141 60000,
142 )
143 .await
144 .map_err(|Error| CommonError::IPCError { Description:Error.to_string() })?;
145
146 if Response.as_str() == Some("initialized") {
147 info!("[CocoonManagement] Cocoon handshake complete.");
148 } else {
149 return Err(CommonError::IPCError {
150 Description:format!("Cocoon initialization failed with response: {}", Response),
151 });
152 }
153
154 Ok(())
155}