Mountain/ProcessManagement/
InitializationData.rs1use std::{
136 collections::HashMap,
137 env,
138 path::PathBuf,
139 sync::{Arc, OnceLock},
140};
141
142static SESSION_ID:OnceLock<String> = OnceLock::new();
147
148fn SessionId() -> &'static str { SESSION_ID.get_or_init(|| Uuid::new_v4().to_string()) }
149
150use CommonLibrary::{
151 Environment::Requires::Requires,
152 Error::CommonError::CommonError,
153 ExtensionManagement::ExtensionManagementService::ExtensionManagementService,
154 Workspace::WorkspaceProvider::WorkspaceProvider,
155};
156use serde_json::{Value, json};
157use tauri::{AppHandle, Manager, Wry};
158use uuid::Uuid;
159
160use crate::{
161 ApplicationState::State::ApplicationState::ApplicationState,
162 Environment::MountainEnvironment::MountainEnvironment,
163 dev_log,
164};
165
166async fn get_or_generate_machine_id(app_data_dir:&PathBuf) -> String {
177 let machine_id_path = app_data_dir.join("machine-id.txt");
178
179 if let Ok(content) = tokio::fs::read_to_string(&machine_id_path).await {
182 let trimmed = content.trim();
183
184 if !trimmed.is_empty() {
185 dev_log!("cocoon", "[InitializationData] Loaded existing machine ID from disk");
186
187 return trimmed.to_string();
188 }
189 }
190
191 let new_machine_id = Uuid::new_v4().to_string();
193
194 if let Some(parent) = machine_id_path.parent() {
196 if let Err(e) = tokio::fs::create_dir_all(parent).await {
197 dev_log!(
198 "cocoon",
199 "warn: [InitializationData] Failed to create machine ID directory: {}",
200 e
201 );
202 }
203 }
204
205 if let Err(e) = tokio::fs::write(&machine_id_path, &new_machine_id).await {
207 dev_log!(
208 "cocoon",
209 "warn: [InitializationData] Failed to persist machine ID to disk: {}",
210 e
211 );
212 } else {
213 dev_log!("cocoon", "[InitializationData] Generated and persisted new machine ID");
214 }
215
216 new_machine_id
217}
218
219pub async fn ConstructSandboxConfiguration(
221 ApplicationHandle:&AppHandle<Wry>,
222
223 ApplicationState:&Arc<ApplicationState>,
224) -> Result<Value, CommonError> {
225 dev_log!("cocoon", "[InitializationData] Constructing ISandboxConfiguration for Sky.");
226
227 let PathResolver = ApplicationHandle.path();
228
229 let AppRootUri = PathResolver.resource_dir().map_err(|Error| {
230 CommonError::ConfigurationLoad {
231 Description:format!("Failed to resolve resource directory (app root): {}", Error),
232 }
233 })?;
234
235 let AppDataDir = PathResolver.app_data_dir().map_err(|Error| {
236 CommonError::ConfigurationLoad { Description:format!("Failed to resolve app data directory: {}", Error) }
237 })?;
238
239 let HomeDir = PathResolver.home_dir().map_err(|Error| {
240 CommonError::ConfigurationLoad { Description:format!("Failed to resolve home directory: {}", Error) }
241 })?;
242
243 let TmpDir = env::temp_dir();
244
245 let BackupPath = AppDataDir.join("Backups").join(ApplicationState.GetWorkspaceIdentifier()?);
246
247 let LogsPath = AppDataDir.join("logs").join(crate::IPC::DevLog::SessionTimestamp::Fn());
252
253 let _ = std::fs::create_dir_all(&LogsPath);
254
255 let Platform = match env::consts::OS {
256 "windows" => "win32",
257
258 "macos" => "darwin",
259
260 "linux" => "linux",
261
262 _ => "unknown",
263 };
264
265 let Arch = match env::consts::ARCH {
266 "x86_64" => "x64",
267
268 "aarch64" => "arm64",
269
270 "x86" => "ia32",
271
272 _ => "unknown",
273 };
274
275 let Versions = json!({
276 "mountain": ApplicationHandle.package_info().version.to_string(),
277
278 "electron": "0.0.0-tauri",
280
281 "chrome": "120.0.0.0",
283
284 "node": "18.18.2"
286 });
287
288 let machine_id = get_or_generate_machine_id(&AppDataDir).await;
290
291 let UserProfile = AppDataDir.join("User");
295
296 let FileUri = |P:std::path::PathBuf| -> serde_json::Value {
298 json!({
299 "scheme": "file",
300 "authority": "",
301 "path": P.to_string_lossy(),
302 "query": "",
303 "fragment": ""
304 })
305 };
306
307 let DefaultProfile = json!({
308 "id": "__default__profile__",
309 "name": "Default",
310 "location": FileUri(UserProfile.clone()),
311 "isDefault": true,
312 "globalStorageHome": FileUri(UserProfile.join("globalStorage")),
313 "settingsResource": FileUri(UserProfile.join("settings.json")),
314 "keybindingsResource": FileUri(UserProfile.join("keybindings.json")),
315 "tasksResource": FileUri(UserProfile.join("tasks.json")),
316 "snippetsHome": FileUri(UserProfile.join("snippets")),
317 "promptsHome": FileUri(UserProfile.join("prompts")),
318 "extensionsResource": FileUri(UserProfile.join("extensions.json")),
319 "mcpResource": FileUri(UserProfile.join("mcp.json")),
320 "languageModelsResource": FileUri(UserProfile.join("chatLanguageModels.json")),
321 "agentPluginsHome": FileUri(UserProfile.join("agent-plugins")),
322 "cacheHome": FileUri(UserProfile.join("profiles/.cache/__default__profile__"))
323 });
324
325 let ProfilesSection = json!({
326 "home": FileUri(UserProfile.join("profiles")),
327 "all": [DefaultProfile.clone()],
328 "profile": DefaultProfile
329 });
330
331 let NlsSection = json!({
334 "messages": {},
335 "language": "en",
336 "availableLanguages": { "en": "English" }
337 });
338
339 let ProductConfig = json!({
340 "nameShort": std::env::var("ProductNameShort").unwrap_or_else(|_| "FIDDEE".into()),
341 "nameLong": std::env::var("ProductNameLong").unwrap_or_else(|_| "FIDDEE".into()),
342 "applicationName": std::env::var("ProductApplicationName").unwrap_or_else(|_| "fiddee".into()),
343 "embedderIdentifier": std::env::var("ProductEmbedderIdentifier").unwrap_or_else(|_| "fiddee-desktop".into()),
344 "dataFolderName": std::env::var("ProductDataFolderName").unwrap_or_else(|_| ".fiddee".into()),
345 "sharedDataFolderName": std::env::var("ProductDataFolderName").unwrap_or_else(|_| ".fiddee".into()),
346 "version": std::env::var("ProductVersion").unwrap_or_else(|_| "1.0.0".into()),
347 });
348
349 let OsSection = json!({
350 "release": "22.0.0",
351 "hostname": "land",
352 "arch": env::consts::ARCH,
353 });
354
355 Ok(json!({
356 "windowId": ApplicationHandle.get_webview_window("main").unwrap().label(),
357
358 "machineId": machine_id,
363
364 "sessionId": SessionId(),
365
366 "logLevel": log::max_level() as i32,
367
368 "userEnv": env::vars().collect::<HashMap<_,_>>(),
369
370 "appRoot": AppRootUri.to_string_lossy(),
377
378 "appName": ApplicationHandle.package_info().name.clone(),
379
380 "appUriScheme": "mountain",
381
382 "appLanguage": "en",
383
384 "appHost": "desktop",
385
386 "platform": Platform,
387
388 "arch": Arch,
389
390 "versions": Versions,
391
392 "execPath": env::current_exe().unwrap_or_default().to_string_lossy(),
393
394 "homeDir": HomeDir.to_string_lossy(),
398
399 "tmpDir": TmpDir.to_string_lossy(),
400
401 "userDataDir": AppDataDir.to_string_lossy(),
402
403 "backupPath": BackupPath.to_string_lossy(),
404
405 "logsPath": LogsPath.to_string_lossy(),
406
407 "perfMarks": [],
411
412 "colorScheme": { "dark": false, "highContrast": false },
413
414 "loggers": [],
415
416 "mainPid": std::process::id(),
417
418 "os": OsSection,
419
420 "nls": NlsSection,
421
422 "productConfiguration": ProductConfig,
425
426 "resourcesPath": PathResolver.resource_dir().unwrap_or_default().to_string_lossy(),
427
428 "VSCODE_CWD": env::current_dir().unwrap_or_default().to_string_lossy(),
429
430 "profiles": ProfilesSection,
432
433 "sqmId": "",
436 "devDeviceId": "",
437 "isPortable": false,
438 }))
439}
440
441pub async fn ConstructExtensionHostInitializationData(Environment:&MountainEnvironment) -> Result<Value, CommonError> {
443 dev_log!("cocoon", "[InitializationData] Constructing IExtensionHostInitData for Cocoon.");
444
445 let ApplicationState = &Environment.ApplicationState;
446
447 let ApplicationHandle = &Environment.ApplicationHandle;
448
449 let ExtensionManagementProvider:Arc<dyn ExtensionManagementService> = Environment.Require();
450
451 let ExtensionsDTO = ExtensionManagementProvider.GetExtensions().await?;
452
453 let WorkspaceProvider:Arc<dyn WorkspaceProvider> = Environment.Require();
454
455 let WorkspaceName = WorkspaceProvider
456 .GetWorkspaceName()
457 .await?
458 .unwrap_or_else(|| "Mountain Workspace".to_string());
459
460 let WorkspaceDTO = {
466 let Guard = ApplicationState.Workspace.WorkspaceFolders.lock().unwrap();
467
468 let FoldersWire:Vec<Value> = Guard
479 .iter()
480 .map(|Folder| {
481 json!({
482 "uri": Folder.URI.to_string(),
483 "name": Folder.GetDisplayName(),
484 "index": Folder.Index,
485 })
486 })
487 .collect();
488
489 let FolderCount = FoldersWire.len();
491
492 let FolderSample = FoldersWire.first().map(|F| F.to_string()).unwrap_or_else(|| "<none>".into());
493
494 let IsEmpty = Guard.is_empty();
495
496 drop(Guard); dev_log!(
499 "cocoon",
500 "[InitializationData] FoldersWire count={} sample0={}",
501 FolderCount,
502 FolderSample
503 );
504
505 if IsEmpty {
506 Value::Null
507 } else {
508 json!({
509 "id": ApplicationState.GetWorkspaceIdentifier()?,
510 "name": WorkspaceName,
511 "folders": FoldersWire, "configuration": ApplicationState.Workspace.WorkspaceConfigurationPath.lock().unwrap().as_ref().map(|p| p.to_string_lossy()),
513 "isUntitled": ApplicationState.Workspace.WorkspaceConfigurationPath.lock().unwrap().is_none(),
514 "transient": false
515 })
516 }
517 };
518
519 let PathResolver = ApplicationHandle.path();
520
521 let AppRoot = PathResolver
522 .resource_dir()
523 .ok()
524 .filter(|P| !P.as_os_str().is_empty() && P.exists())
525 .or_else(|| {
526 let ExeDir = std::env::current_exe()
535 .ok()
536 .and_then(|P| P.parent().map(|D| D.to_path_buf()))
537 .unwrap_or_default();
538 let BundleResources = ExeDir.join("../Resources");
539 if BundleResources.exists() {
540 return Some(BundleResources.canonicalize().unwrap_or(BundleResources));
541 }
542 let SkyTarget = ExeDir.join("../../../Sky/Target");
543 if SkyTarget.exists() {
544 return Some(SkyTarget.canonicalize().unwrap_or(SkyTarget));
545 }
546 None
547 })
548 .ok_or_else(|| {
549 CommonError::ConfigurationLoad {
550 Description:"Could not resolve AppRoot from resource_dir, ../Resources, or ../../../Sky/Target"
551 .to_string(),
552 }
553 })?;
554
555 let AppData = PathResolver
556 .app_data_dir()
557 .map_err(|Error| CommonError::ConfigurationLoad { Description:Error.to_string() })?;
558
559 let LogsLocation = PathResolver
560 .app_log_dir()
561 .map_err(|Error| CommonError::ConfigurationLoad { Description:Error.to_string() })?;
562
563 let GlobalStorage = AppData.join("User/globalStorage");
564
565 let WorkspaceStorage = AppData.join("User/workspaceStorage");
566
567 Ok(json!({
568
569 "commit": std::env::var("ProductCommit").unwrap_or_else(|_| "dev".into()),
575
576 "version": std::env::var("ProductVersion").unwrap_or_else(|_| {
577 ApplicationHandle.package_info().version.to_string()
578 }),
579
580 "quality": std::env::var("ProductQuality").unwrap_or_else(|_| "development".into()),
581
582 "parentPid": std::process::id(),
583
584 "environment": {
585
586 "isExtensionDevelopmentDebug": false,
587
588 "appName": "Mountain",
589
590 "appHost": "desktop",
591
592 "appUriScheme": "mountain",
593
594 "appLanguage": "en",
595
596 "isExtensionTelemetryLoggingOnly": true,
597
598 "appRoot": url::Url::from_directory_path(AppRoot.clone()).unwrap(),
599
600 "globalStorageHome": url::Url::from_directory_path(GlobalStorage).unwrap(),
601
602 "workspaceStorageHome": url::Url::from_directory_path(WorkspaceStorage).unwrap(),
603
604 "extensionDevelopmentLocationURI": [],
605
606 "extensionTestsLocationURI": Value::Null,
607
608 "extensionLogLevel": [["info", "Default"]],
609
610 },
611
612 "workspace": WorkspaceDTO,
613
614 "remote": {
615
616 "isRemote": false,
617
618 "authority": Value::Null,
619
620 "connectionData": Value::Null,
621
622 },
623
624 "consoleForward": { "includeStack": true, "logNative": true },
625
626 "logLevel": log::max_level() as i32,
627
628 "logsLocation": url::Url::from_directory_path(LogsLocation).unwrap(),
629
630 "telemetryInfo": {
631
632 "sessionId": SessionId(),
633
634 "machineId": get_or_generate_machine_id(&AppData).await,
635
636 "firstSessionDate": "2024-01-01T00:00:00.000Z",
637
638 "msftInternal": false
639 },
640
641 "extensions": ExtensionsDTO,
642
643 "autoStart": true,
644
645 "uiKind": 1,
647 }))
648}