1#![allow(unused_imports)]
2
3use std::sync::{
62 Arc,
63 atomic::{AtomicBool, Ordering},
64};
65
66use tauri::{App, Manager, RunEvent, Wry};
67use Echo::Scheduler::{Scheduler::Scheduler, SchedulerBuilder::SchedulerBuilder};
68
69use crate::dev_log;
70use crate::{
71 ApplicationState::State::ApplicationState::ApplicationState,
73 Binary::Build::DnsCommands::{
74 StartupTime::init_dns_startup_time,
75 dns_get_forward_allowlist::dns_get_forward_allowlist,
76 dns_get_health_status::dns_get_health_status,
77 dns_get_server_info::dns_get_server_info,
78 dns_get_zone_info::dns_get_zone_info,
79 dns_health_check::dns_health_check,
80 dns_resolve::dns_resolve,
81 dns_test_resolution::dns_test_resolution,
82 },
83 Binary::Build::LocalhostPlugin::LocalhostPlugin as LocalhostPluginFn,
85 Binary::Build::LoggingPlugin::LoggingPlugin as LoggingPluginFn,
86 Binary::Build::Scheme::{self, DnsPort, init_service_registry, land_scheme_handler, register_land_service},
87 Binary::Build::ServiceRegistry::ServiceRegistry as ServiceRegistryFn,
88 Binary::Build::TauriBuild::TauriBuild as TauriBuildFn,
89 Binary::Build::WindowBuild::WindowBuild as WindowBuildFn,
90 Binary::Extension::ExtensionPopulate::ExtensionPopulate as ExtensionPopulateFn,
91 Binary::Extension::ScanPathConfigure::ScanPathConfigure as ScanPathConfigureFn,
92 Binary::Initialize::CliParse::Parse as CliParseFn,
93 Binary::Initialize::LogLevel::Resolve as ResolveLogLevel,
94 Binary::Initialize::PortSelector::BuildUrl as BuildPortUrl,
95 Binary::Initialize::PortSelector::Select as SelectPort,
96 Binary::Initialize::StateBuild::Build as BuildStateFn,
97 Binary::Register::AdvancedFeaturesRegister::AdvancedFeaturesRegister as AdvancedFeaturesRegisterFn,
98 Binary::Register::CommandRegister::CommandRegister as CommandRegisterFn,
99 Binary::Register::IPCServerRegister::IPCServerRegister as IPCServerRegisterFn,
100 Binary::Register::StatusReporterRegister::StatusReporterRegister as StatusReporterRegisterFn,
101 Binary::Register::WindSyncRegister::WindSyncRegister as WindSyncRegisterFn,
102 Binary::Service::CocoonStart::CocoonStart as CocoonStartFn,
103 Binary::Service::ConfigurationInitialize::ConfigurationInitialize as ConfigurationInitializeFn,
104 Binary::Service::VineStart::VineStart as VineStartFn,
105 Binary::Shutdown::RuntimeShutdown::RuntimeShutdown as RuntimeShutdownFn,
106 Binary::Shutdown::SchedulerShutdown::SchedulerShutdown as SchedulerShutdownFn,
107 Command,
108 Environment::MountainEnvironment::MountainEnvironment,
109 ProcessManagement::InitializationData,
110 RunTime::ApplicationRunTime::ApplicationRunTime,
111 Track,
112};
113use super::AppLifecycle::AppLifecycleSetup;
114
115macro_rules! TraceStep {
120
121 ($($arg:tt)*) => {{
122
123 dev_log!("lifecycle", $($arg)*);
124 }};
125}
126
127pub fn Fn() {
141 match keyring::use_native_store(false) {
152 Ok(()) => dev_log!("lifecycle", "[Boot] [Keyring] Native store initialized for secret management"),
153 Err(E) => {
154 dev_log!(
155 "lifecycle",
156 "warn: [Boot] [Keyring] Failed to initialize native store ({}); secret operations will fall back to \
157 no-op",
158 E
159 )
160 },
161 }
162
163 crate::IPC::DevLog::InitEager::Fn();
172
173 let IsTtyLaunch =
190 std::env::var("TERM_PROGRAM").is_ok() || std::env::var("TERM").map_or(false, |V| V != "dumb" && V != "unknown");
191
192 if !IsTtyLaunch {
193 crate::Environment::Utility::EnhanceShellEnvironment::Fn();
194 }
195
196 if !IsTtyLaunch {
203 {
204 fn LoadEnvFile(Path:&std::path::Path) -> bool {
205 let Ok(Content) = std::fs::read_to_string(Path) else {
206 return false;
207 };
208
209 for Line in Content.lines() {
210 let Trimmed = Line.trim();
211
212 if Trimmed.is_empty() || Trimmed.starts_with('#') {
213 continue;
214 }
215
216 if let Some((Key, Value)) = Trimmed.split_once('=') {
217 let CleanKey = Key.trim();
218
219 let CleanValue = Value.trim().trim_matches('"').trim_matches('\'');
220
221 if std::env::var_os(CleanKey).is_none() {
222 unsafe { std::env::set_var(CleanKey, CleanValue) };
226 }
227 }
228 }
229
230 true
231 }
232
233 let mut Candidates:Vec<std::path::PathBuf> = Vec::new();
234
235 if let Ok(Cwd) = std::env::current_dir() {
236 Candidates.push(Cwd.join(".env.Land"));
237
238 if let Some(Parent) = Cwd.parent() {
239 Candidates.push(Parent.join(".env.Land"));
240 }
241
242 Candidates.push(Cwd.join(".env.Land.Sample"));
243
244 if let Some(Parent) = Cwd.parent() {
245 Candidates.push(Parent.join(".env.Land.Sample"));
246 }
247 }
248
249 if let Ok(Exe) = std::env::current_exe() {
251 let Ancestors:Vec<&std::path::Path> = Exe.ancestors().collect();
252
253 for Candidate in Ancestors.iter().take(6) {
254 Candidates.push(Candidate.join(".env.Land"));
255
256 Candidates.push(Candidate.join(".env.Land.Sample"));
257 }
258 }
259
260 let mut Loaded = false;
261
262 for Candidate in Candidates {
263 if Candidate.exists() && LoadEnvFile(&Candidate) {
264 crate::dev_log!("lifecycle", "[Boot] [Env] Loaded env from {}", Candidate.display());
265
266 Loaded = true;
267
268 break;
269 }
270 }
271
272 if !Loaded {
273 crate::dev_log!(
274 "lifecycle",
275 "[Boot] [Env] No .env.Land / .env.Land.Sample found - using defaults"
276 );
277 }
278 }
279 }
280
281 crate::LandFixTier::LogResolvedTiers();
285
286 {
297 let NamedProfile = option_env!("Profile").unwrap_or("unknown");
298
299 let Workbench = option_env!("Pack").unwrap_or("Unknown");
300
301 let Bundle = option_env!("Bundle").unwrap_or("");
302
303 let Compiler = option_env!("Compiler").unwrap_or("default");
304
305 dev_log!(
306 "lifecycle",
307 "[LandFix:Profile] Active profile={} workbench={} bundle={} compiler={}",
308 NamedProfile,
309 Workbench,
310 Bundle,
311 Compiler
312 );
313 }
314
315 TraceStep!("[Boot] [Runtime] Building Tokio runtime...");
319
320 let Runtime = tokio::runtime::Builder::new_multi_thread()
321 .enable_all()
322 .build()
323 .expect("FATAL: Cannot build Tokio runtime.");
324
325 TraceStep!("[Boot] [Runtime] Tokio runtime built.");
326
327 Runtime.block_on(async {
328 crate::Binary::Build::PostHogPlugin::HydrateRuntimeEnvironment::Fn();
338
339 crate::Binary::Build::PostHogPlugin::Initialize::Fn().await;
345
346 CommonLibrary::Telemetry::Initialize::Fn(CommonLibrary::Telemetry::Tier::Tier::Mountain).await;
355
356 let _WorkspaceConfigurationPath = CliParseFn();
360 let _InitialFolders:Vec<String> = vec![];
361
362 dev_log!("lifecycle", "[Boot] [State] Building ApplicationState...");
366
367 let AppState = ApplicationState::default();
369
370 {
378 let InitialFolderPaths = crate::Binary::Initialize::CliParse::ParseWorkspaceFolders();
379 if InitialFolderPaths.is_empty() {
380 dev_log!(
381 "lifecycle",
382 "[Boot] [Workspace] No initial folders resolved - editor will open in \"no folder\" mode."
383 );
384 } else {
385 use crate::ApplicationState::DTO::WorkspaceFolderStateDTO::WorkspaceFolderStateDTO;
386 let mut Folders:Vec<WorkspaceFolderStateDTO> = Vec::new();
387 for (Index, Path) in InitialFolderPaths.iter().enumerate() {
388 let Uri = match url::Url::from_directory_path(Path) {
389 Ok(U) => U,
390 Err(()) => {
391 dev_log!(
392 "lifecycle",
393 "warn: [Boot] [Workspace] Failed to build URL for {}; skipping",
394 Path.display()
395 );
396 continue;
397 },
398 };
399 let Name = Path
400 .file_name()
401 .and_then(|N| N.to_str())
402 .map(str::to_string)
403 .unwrap_or_else(|| Path.display().to_string());
404 match WorkspaceFolderStateDTO::New(Uri, Name, Index) {
405 Ok(Dto) => Folders.push(Dto),
406 Err(Error) => {
407 dev_log!(
408 "lifecycle",
409 "warn: [Boot] [Workspace] Failed to build folder DTO for {}: {}",
410 Path.display(),
411 Error
412 );
413 },
414 }
415 }
416 if !Folders.is_empty() {
417 AppState.Workspace.SetWorkspaceFolders(Folders);
422 dev_log!(
423 "lifecycle",
424 "[Boot] [Workspace] Seeded {} workspace folder(s).",
425 InitialFolderPaths.len()
426 );
427 }
428 }
429 }
430
431 dev_log!(
432 "lifecycle",
433 "[Boot] [State] ApplicationState created with {} workspace folders.",
434 AppState.Workspace.WorkspaceFolders.lock().map(|f| f.len()).unwrap_or(0)
435 );
436
437 let AppStateArcForClosure = Arc::new(AppState.clone());
439
440 let Scheduler = Arc::new(SchedulerBuilder::Create().Build());
444 let SchedulerForClosure = Scheduler.clone();
445 TraceStep!("[Boot] [Echo] Scheduler handles prepared.");
446
447 let ServerPort = SelectPort();
451 let LocalhostUrl = BuildPortUrl(ServerPort);
452
453 let log_level = ResolveLogLevel();
457
458 let Builder = TauriBuildFn();
462
463 Builder
464 .plugin(LoggingPluginFn(log_level))
465 .plugin(LocalhostPluginFn(ServerPort))
466 .manage(AppStateArcForClosure.clone())
467 .setup({
468 let LocalhostUrl = LocalhostUrl.clone();
469 let ServerPortForClosure = ServerPort;
470 move |app:&mut App| {
471 dev_log!("lifecycle", "[Lifecycle] [Setup] Setup hook started.");
472 dev_log!("lifecycle", "[Lifecycle] [Setup] LocalhostUrl={}", LocalhostUrl);
473
474 dev_log!(
478 "lifecycle",
479 "[Lifecycle] [Setup] Initializing ServiceRegistry for land:// scheme..."
480 );
481 let service_registry = ServiceRegistryFn::new();
482 init_service_registry(service_registry.clone());
483
484 dev_log!(
489 "lifecycle",
490 "[Lifecycle] [Setup] Registering code.editor.land service on port {}",
491 ServerPortForClosure
492 );
493 register_land_service("code.editor.land", ServerPortForClosure);
494
495 register_land_service("api.editor.land", ServerPortForClosure);
497
498 register_land_service("assets.editor.land", ServerPortForClosure);
500
501 app.manage(service_registry);
503 dev_log!(
504 "lifecycle",
505 "[Lifecycle] [Setup] ServiceRegistry initialized and services registered."
506 );
507
508 dev_log!("lifecycle", "[Lifecycle] [Setup] Starting DNS server on preferred port 5380...");
514 let dns_port = Mist::start(5380).unwrap_or_else(|e| {
515 dev_log!(
516 "lifecycle",
517 "warn: [Lifecycle] [Setup] Failed to start DNS server on port 5380: {}",
518 e
519 );
520 Mist::start(0).unwrap_or_else(|e| {
522 dev_log!(
523 "lifecycle",
524 "error: [Lifecycle] [Setup] Completely failed to start DNS server: {}",
525 e
526 );
527 0 })
529 });
530
531 if dns_port == 0 {
532 dev_log!(
533 "lifecycle",
534 "warn: [Lifecycle] [Setup] DNS server failed to start, land:// protocol will not be \
535 available"
536 );
537 } else {
538 dev_log!(
539 "lifecycle",
540 "[Lifecycle] [Setup] DNS server started successfully on port {}",
541 dns_port
542 );
543 crate::Binary::Build::DnsCommands::StartupTime::init_dns_startup_time();
545 }
546
547 app.manage(DnsPort(dns_port));
549
550 let AppHandle = app.handle().clone();
551 TraceStep!("[Lifecycle] [Setup] AppHandle acquired.");
552
553 let AppStateArcFromClosure = AppStateArcForClosure.clone();
557
558 if let Err(e) = AppLifecycleSetup(
559 app,
560 AppHandle.clone(),
561 LocalhostUrl.clone(),
562 SchedulerForClosure.clone(),
563 AppStateArcFromClosure,
564 ) {
565 dev_log!("lifecycle", "error: [Lifecycle] [Setup] Failed to setup lifecycle: {}", e);
566 }
567
568 Ok(())
569 }
570 })
571 .register_asynchronous_uri_scheme_protocol("land", |_ctx, request, responder| {
572 let response = crate::Binary::Build::Scheme::land_scheme_handler(&request);
574 responder.respond(response);
575 })
576 .register_asynchronous_uri_scheme_protocol("vscode-file", |ctx, request, responder| {
577 let AppHandle = ctx.app_handle().clone();
580 std::thread::spawn(move || {
581 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &request);
582 responder.respond(response);
583 });
584 })
585 .register_asynchronous_uri_scheme_protocol("vscode-webview", |ctx, request, responder| {
586 let AppHandle = ctx.app_handle().clone();
596 std::thread::spawn(move || {
597 let response = crate::Binary::Build::Scheme::VscodeWebviewSchemeHandler(&AppHandle, &request);
598 responder.respond(response);
599 });
600 })
601 .register_asynchronous_uri_scheme_protocol("vscode-webview-resource", |ctx, request, responder| {
602 let AppHandle = ctx.app_handle().clone();
615 std::thread::spawn(move || {
616 let Original = request.uri().to_string();
617 let RewrittenUri = match Original.strip_prefix("vscode-webview-resource://") {
618 Some(After) => {
619 let Rest = After.find('/').map(|I| &After[I..]).unwrap_or("/");
620 format!("vscode-file://vscode-app{}", Rest)
621 },
622 None => "vscode-file://vscode-app/".to_string(),
623 };
624 crate::dev_log!(
625 "scheme-assets",
626 "[LandFix:VscodeWebviewResource] {} -> {}",
627 Original,
628 RewrittenUri
629 );
630 let mut Builder = tauri::http::request::Request::builder().uri(&RewrittenUri);
631 for (Name, Value) in request.headers().iter() {
632 Builder = Builder.header(Name, Value);
633 }
634 let Forwarded = Builder
635 .method(request.method().clone())
636 .body(request.body().clone())
637 .unwrap_or_else(|_| request.clone());
638 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &Forwarded);
639 responder.respond(response);
640 });
641 })
642 .register_asynchronous_uri_scheme_protocol("vscode-resource", |ctx, request, responder| {
643 let AppHandle = ctx.app_handle().clone();
647 std::thread::spawn(move || {
648 let Original = request.uri().to_string();
649 let RewrittenUri = match Original.strip_prefix("vscode-resource://") {
650 Some(After) => {
651 let Rest = After.find('/').map(|I| &After[I..]).unwrap_or("/");
652 format!("vscode-file://vscode-app{}", Rest)
653 },
654 None => "vscode-file://vscode-app/".to_string(),
655 };
656 crate::dev_log!("scheme-assets", "[LandFix:VscodeResource] {} -> {}", Original, RewrittenUri);
657 let mut Builder = tauri::http::request::Request::builder().uri(&RewrittenUri);
658 for (Name, Value) in request.headers().iter() {
659 Builder = Builder.header(Name, Value);
660 }
661 let Forwarded = Builder
662 .method(request.method().clone())
663 .body(request.body().clone())
664 .unwrap_or_else(|_| request.clone());
665 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &Forwarded);
666 responder.respond(response);
667 });
668 })
669 .plugin(tauri_plugin_dialog::init())
670 .plugin(tauri_plugin_fs::init())
671 .invoke_handler(tauri::generate_handler![
672 crate::Binary::Tray::SwitchTrayIcon::SwitchTrayIcon,
673
674 crate::Binary::IPC::WorkbenchConfigurationCommand::MountainGetWorkbenchConfiguration,
675
676 Command::TreeView::GetTreeViewChildren::GetTreeViewChildren,
677
678 Command::LanguageFeature::MountainProvideHover::MountainProvideHover,
679
680 Command::LanguageFeature::MountainProvideCompletions::MountainProvideCompletions,
681
682 Command::LanguageFeature::MountainProvideDefinition::MountainProvideDefinition,
683
684 Command::LanguageFeature::MountainProvideReferences::MountainProvideReferences,
685
686 Command::SourceControlManagement::GetAllSourceControlManagementState::GetAllSourceControlManagementState,
687
688 Command::Keybinding::GetResolvedKeybinding::GetResolvedKeybinding,
689
690 Track::FrontendCommand::DispatchFrontendCommand::DispatchFrontendCommand,
691
692 Track::UIRequest::ResolveUIRequest::ResolveUIRequest,
693
694 Track::Webview::MountainWebviewPostMessageFromGuest::MountainWebviewPostMessageFromGuest,
695
696 crate::Binary::IPC::MessageReceiveCommand::MountainIPCReceiveMessage,
697
698 crate::Binary::IPC::StatusGetCommand::MountainIPCGetStatus,
699
700 crate::Binary::IPC::InvokeCommand::MountainIPCInvoke,
701
702 crate::Binary::IPC::WindConfigurationCommand::MountainGetWindDesktopConfiguration,
703
704 crate::Binary::IPC::ConfigurationUpdateCommand::MountainUpdateConfigurationFromWind,
705
706 crate::Binary::IPC::ConfigurationSyncCommand::MountainSynchronizeConfiguration,
707
708 crate::Binary::IPC::ConfigurationStatusCommand::MountainGetConfigurationStatus,
709
710 crate::Binary::IPC::IPCStatusCommand::MountainGetIPCStatus,
711
712 crate::Binary::IPC::IPCStatusHistoryCommand::MountainGetIPCStatusHistory,
713
714 crate::Binary::IPC::IPCStatusReportingStartCommand::MountainStartIPCStatusReporting,
715
716 crate::Binary::IPC::PerformanceStatsCommand::MountainGetPerformanceStats,
717
718 crate::Binary::IPC::CacheStatsCommand::MountainGetCacheStats,
719
720 crate::Binary::IPC::CollaborationSessionCommand::MountainCreateCollaborationSession,
721
722 crate::Binary::IPC::CollaborationSessionCommand::MountainGetCollaborationSessions,
723
724 crate::Binary::IPC::DocumentSyncCommand::MountainAddDocumentForSync,
725
726 crate::Binary::IPC::DocumentSyncCommand::MountainGetSyncStatus,
727
728 crate::Binary::IPC::UpdateSubscriptionCommand::MountainSubscribeToUpdates,
729
730 crate::Binary::IPC::ConfigurationDataCommand::GetConfigurationData,
731
732 crate::Binary::IPC::ConfigurationDataCommand::SaveConfigurationData,
733
734 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceOpenFolder,
735
736 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceListFolders,
737
738 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceCloseAllFolders,
739
740 crate::Binary::Build::DnsCommands::dns_get_server_info::dns_get_server_info,
741
742 crate::Binary::Build::DnsCommands::dns_get_zone_info::dns_get_zone_info,
743
744 crate::Binary::Build::DnsCommands::dns_get_forward_allowlist::dns_get_forward_allowlist,
745
746 crate::Binary::Build::DnsCommands::dns_get_health_status::dns_get_health_status,
747
748 crate::Binary::Build::DnsCommands::dns_resolve::dns_resolve,
749
750 crate::Binary::Build::DnsCommands::dns_test_resolution::dns_test_resolution,
751
752 crate::Binary::Build::DnsCommands::dns_health_check::dns_health_check,
753
754 crate::Binary::IPC::ProcessCommand::process_get_exec_path::process_get_exec_path,
756
757 crate::Binary::IPC::ProcessCommand::process_get_platform::process_get_platform,
758
759 crate::Binary::IPC::ProcessCommand::process_get_arch::process_get_arch,
760
761 crate::Binary::IPC::ProcessCommand::process_get_pid::process_get_pid,
762
763 crate::Binary::IPC::ProcessCommand::process_get_shell_env::process_get_shell_env,
764
765 crate::Binary::IPC::ProcessCommand::process_get_memory_info::process_get_memory_info,
766
767 crate::Binary::IPC::HealthCommand::cocoon_extension_host_health::cocoon_extension_host_health,
769
770 crate::Binary::IPC::HealthCommand::cocoon_search_service_health::cocoon_search_service_health,
771
772 crate::Binary::IPC::HealthCommand::cocoon_debug_service_health::cocoon_debug_service_health,
773
774 crate::Binary::IPC::HealthCommand::shared_process_service_health::shared_process_service_health,
775
776 crate::Binary::IPC::RenderDevLogCommand::RenderDevLog,
777
778 crate::Binary::IPC::VineSubscribeCommand::vine_subscribe_notifications,
787
788 crate::Binary::IPC::VineSubscribeCommand::vine_subscriber_count,
789 ])
790 .build(tauri::generate_context!())
791 .expect("FATAL: Error while building Mountain Tauri application")
792 .run(move |app_handle:&tauri::AppHandle, event:tauri::RunEvent| {
793 if cfg!(debug_assertions) {
795 match &event {
796 RunEvent::MainEventsCleared => {},
797 RunEvent::WindowEvent { .. } => {},
798 _ => dev_log!("lifecycle", "[Lifecycle] [RunEvent] {:?}", event),
799 }
800 }
801
802 if let RunEvent::ExitRequested { api, .. } = event {
803 static SHUTTING_DOWN:AtomicBool = AtomicBool::new(false);
811 if SHUTTING_DOWN.swap(true, Ordering::SeqCst) {
812 return;
813 }
814
815 dev_log!(
816 "lifecycle",
817 "warn: [Lifecycle] [Shutdown] Exit requested. Starting graceful shutdown..."
818 );
819 api.prevent_exit();
820
821 let SchedulerHandle = Scheduler.clone();
822 let app_handle_clone = app_handle.clone();
823
824 tokio::spawn(async move {
825 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Shutting down ApplicationRunTime...");
826 let _ = RuntimeShutdownFn(&app_handle_clone).await;
827
828 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Stopping Echo scheduler...");
829 let _ = SchedulerShutdownFn(SchedulerHandle).await;
830
831 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Done. Exiting process.");
832 app_handle_clone.exit(0);
833 });
834 }
835 });
836
837 dev_log!("lifecycle", "[Lifecycle] [Exit] Mountain application has shut down.");
838 });
839}