1use std::sync::{
60 Arc,
61 atomic::{AtomicBool, Ordering},
62};
63
64use tauri::{App, Manager, RunEvent, Wry};
65use Echo::Scheduler::{Scheduler::Scheduler, SchedulerBuilder::SchedulerBuilder};
66
67use crate::dev_log;
68use crate::{
69 ApplicationState::State::ApplicationState::ApplicationState,
71 Binary::Build::DnsCommands::{
72 StartupTime::init_dns_startup_time,
73 dns_get_forward_allowlist::dns_get_forward_allowlist,
74 dns_get_health_status::dns_get_health_status,
75 dns_get_server_info::dns_get_server_info,
76 dns_get_zone_info::dns_get_zone_info,
77 dns_health_check::dns_health_check,
78 dns_resolve::dns_resolve,
79 dns_test_resolution::dns_test_resolution,
80 },
81 Binary::Build::LocalhostPlugin::LocalhostPlugin as LocalhostPluginFn,
83 Binary::Build::LoggingPlugin::LoggingPlugin as LoggingPluginFn,
84 Binary::Build::Scheme::{self, DnsPort, init_service_registry, land_scheme_handler, register_land_service},
85 Binary::Build::ServiceRegistry::ServiceRegistry as ServiceRegistryFn,
86 Binary::Build::TauriBuild::TauriBuild as TauriBuildFn,
87 Binary::Build::WindowBuild::WindowBuild as WindowBuildFn,
88 Binary::Extension::ExtensionPopulate::Fn as ExtensionPopulateFn,
89 Binary::Extension::ScanPathConfigure::ScanPathConfigure as ScanPathConfigureFn,
90 Binary::Initialize::CliParse::Parse as CliParseFn,
91 Binary::Initialize::LogLevel::Resolve as ResolveLogLevel,
92 Binary::Initialize::PortSelector::BuildUrl as BuildPortUrl,
93 Binary::Initialize::PortSelector::Select as SelectPort,
94 Binary::Initialize::StateBuild::Build as BuildStateFn,
95 Binary::Register::AdvancedFeaturesRegister::AdvancedFeaturesRegister as AdvancedFeaturesRegisterFn,
96 Binary::Register::CommandRegister::CommandRegister as CommandRegisterFn,
97 Binary::Register::IPCServerRegister::IPCServerRegister as IPCServerRegisterFn,
98 Binary::Register::StatusReporterRegister::StatusReporterRegister as StatusReporterRegisterFn,
99 Binary::Register::WindSyncRegister::WindSyncRegister as WindSyncRegisterFn,
100 Binary::Service::CocoonStart::Fn as CocoonStartFn,
101 Binary::Service::ConfigurationInitialize::Fn as ConfigurationInitializeFn,
102 Binary::Service::VineStart::Fn as VineStartFn,
103 Binary::Shutdown::RuntimeShutdown::RuntimeShutdown as RuntimeShutdownFn,
104 Binary::Shutdown::SchedulerShutdown::SchedulerShutdown as SchedulerShutdownFn,
105 Command,
106 Environment::MountainEnvironment::MountainEnvironment,
107 ProcessManagement::InitializationData,
108 RunTime::ApplicationRunTime::ApplicationRunTime,
109 Track,
110};
111use super::AppLifecycle::AppLifecycleSetup;
112
113macro_rules! TraceStep {
118
119 ($($arg:tt)*) => {{
120
121 dev_log!("lifecycle", $($arg)*);
122 }};
123}
124
125pub fn Fn() {
139 match keyring::use_native_store(false) {
150 Ok(()) => dev_log!("lifecycle", "[Boot] [Keyring] Native store initialized for secret management"),
151
152 Err(E) => {
153 dev_log!(
154 "lifecycle",
155 "warn: [Boot] [Keyring] Failed to initialize native store ({}); secret operations will fall back to \
156 no-op",
157 E
158 )
159 },
160 }
161
162 crate::IPC::DevLog::InitEager::Fn();
171
172 let IsTtyLaunch =
189 std::env::var("TERM_PROGRAM").is_ok() || std::env::var("TERM").map_or(false, |V| V != "dumb" && V != "unknown");
190
191 if !IsTtyLaunch {
192 crate::Environment::Utility::EnhanceShellEnvironment::Fn();
193 }
194
195 if !IsTtyLaunch {
202 {
203 fn LoadEnvFile(Path:&std::path::Path) -> bool {
204 let Ok(Content) = std::fs::read_to_string(Path) else {
205 return false;
206 };
207
208 for Line in Content.lines() {
209 let Trimmed = Line.trim();
210
211 if Trimmed.is_empty() || Trimmed.starts_with('#') {
212 continue;
213 }
214
215 if let Some((Key, Value)) = Trimmed.split_once('=') {
216 let CleanKey = Key.trim();
217
218 let CleanValue = Value.trim().trim_matches('"').trim_matches('\'');
219
220 if std::env::var_os(CleanKey).is_none() {
221 unsafe { std::env::set_var(CleanKey, CleanValue) };
225 }
226 }
227 }
228
229 true
230 }
231
232 let mut Candidates:Vec<std::path::PathBuf> = Vec::new();
233
234 if let Ok(Cwd) = std::env::current_dir() {
235 Candidates.push(Cwd.join(".env.Land"));
236
237 if let Some(Parent) = Cwd.parent() {
238 Candidates.push(Parent.join(".env.Land"));
239 }
240
241 Candidates.push(Cwd.join(".env.Land.Sample"));
242
243 if let Some(Parent) = Cwd.parent() {
244 Candidates.push(Parent.join(".env.Land.Sample"));
245 }
246 }
247
248 if let Ok(Exe) = std::env::current_exe() {
250 let Ancestors:Vec<&std::path::Path> = Exe.ancestors().collect();
251
252 for Candidate in Ancestors.iter().take(6) {
253 Candidates.push(Candidate.join(".env.Land"));
254
255 Candidates.push(Candidate.join(".env.Land.Sample"));
256 }
257 }
258
259 let mut Loaded = false;
260
261 for Candidate in Candidates {
262 if Candidate.exists() && LoadEnvFile(&Candidate) {
263 crate::dev_log!("lifecycle", "[Boot] [Env] Loaded env from {}", Candidate.display());
264
265 Loaded = true;
266
267 break;
268 }
269 }
270
271 if !Loaded {
272 crate::dev_log!(
273 "lifecycle",
274 "[Boot] [Env] No .env.Land / .env.Land.Sample found - using defaults"
275 );
276 }
277 }
278 }
279
280 crate::LandFixTier::LogResolvedTiers();
284
285 {
296 let NamedProfile = option_env!("Profile").unwrap_or("unknown");
297
298 let Workbench = option_env!("Pack").unwrap_or("Unknown");
299
300 let Bundle = option_env!("Bundle").unwrap_or("");
301
302 let Compiler = option_env!("Compiler").unwrap_or("default");
303
304 dev_log!(
305 "lifecycle",
306 "[LandFix:Profile] Active profile={} workbench={} bundle={} compiler={}",
307 NamedProfile,
308 Workbench,
309 Bundle,
310 Compiler
311 );
312 }
313
314 TraceStep!("[Boot] [Runtime] Building Tokio runtime...");
318
319 let Runtime = tokio::runtime::Builder::new_multi_thread()
320 .enable_all()
321 .build()
322 .expect("FATAL: Cannot build Tokio runtime.");
323
324 TraceStep!("[Boot] [Runtime] Tokio runtime built.");
325
326 Runtime.block_on(async {
327 crate::Binary::Build::PostHogPlugin::HydrateRuntimeEnvironment::Fn();
337
338 crate::Binary::Build::PostHogPlugin::Initialize::Fn().await;
344
345 CommonLibrary::Telemetry::Initialize::Fn(CommonLibrary::Telemetry::Tier::Tier::Mountain).await;
354
355 let _WorkspaceConfigurationPath = CliParseFn();
359
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
380 if InitialFolderPaths.is_empty() {
381 dev_log!(
382 "lifecycle",
383 "[Boot] [Workspace] No initial folders resolved - editor will open in \"no folder\" mode."
384 );
385 } else {
386 use crate::ApplicationState::DTO::WorkspaceFolderStateDTO::WorkspaceFolderStateDTO;
387
388 let mut Folders:Vec<WorkspaceFolderStateDTO> = Vec::new();
389
390 for (Index, Path) in InitialFolderPaths.iter().enumerate() {
391 let Uri = match url::Url::from_directory_path(Path) {
392 Ok(U) => U,
393 Err(()) => {
394 dev_log!(
395 "lifecycle",
396 "warn: [Boot] [Workspace] Failed to build URL for {}; skipping",
397 Path.display()
398 );
399
400 continue;
401 },
402 };
403
404 let Name = Path
405 .file_name()
406 .and_then(|N| N.to_str())
407 .map(str::to_string)
408 .unwrap_or_else(|| Path.display().to_string());
409
410 match WorkspaceFolderStateDTO::New(Uri, Name, Index) {
411 Ok(Dto) => Folders.push(Dto),
412 Err(Error) => {
413 dev_log!(
414 "lifecycle",
415 "warn: [Boot] [Workspace] Failed to build folder DTO for {}: {}",
416 Path.display(),
417 Error
418 );
419 },
420 }
421 }
422
423 if !Folders.is_empty() {
424 AppState.Workspace.SetWorkspaceFolders(Folders);
429
430 dev_log!(
431 "lifecycle",
432 "[Boot] [Workspace] Seeded {} workspace folder(s).",
433 InitialFolderPaths.len()
434 );
435 }
436 }
437 }
438
439 dev_log!(
440 "lifecycle",
441 "[Boot] [State] ApplicationState created with {} workspace folders.",
442 AppState.Workspace.WorkspaceFolders.lock().map(|f| f.len()).unwrap_or(0)
443 );
444
445 let AppStateArcForClosure = Arc::new(AppState.clone());
447
448 let Scheduler = Arc::new(SchedulerBuilder::Create().Build());
452
453 let SchedulerForClosure = Scheduler.clone();
454
455 TraceStep!("[Boot] [Echo] Scheduler handles prepared.");
456
457 let ServerPort = SelectPort();
461
462 let LocalhostUrl = BuildPortUrl(ServerPort);
463
464 let log_level = ResolveLogLevel();
468
469 let Builder = TauriBuildFn();
473
474 Builder
475 .plugin(LoggingPluginFn(log_level))
476 .plugin(LocalhostPluginFn(ServerPort))
477 .manage(AppStateArcForClosure.clone())
478 .setup({
479 let LocalhostUrl = LocalhostUrl.clone();
480
481 let ServerPortForClosure = ServerPort;
482
483 move |app:&mut App| {
484 dev_log!("lifecycle", "[Lifecycle] [Setup] Setup hook started.");
485
486 dev_log!("lifecycle", "[Lifecycle] [Setup] LocalhostUrl={}", LocalhostUrl);
487
488 dev_log!(
492 "lifecycle",
493 "[Lifecycle] [Setup] Initializing ServiceRegistry for land:// scheme..."
494 );
495
496 let service_registry = ServiceRegistryFn::new();
497
498 init_service_registry(service_registry.clone());
499
500 dev_log!(
505 "lifecycle",
506 "[Lifecycle] [Setup] Registering code.land.playform.cloud service on port {}",
507 ServerPortForClosure
508 );
509
510 register_land_service("code.land.playform.cloud", ServerPortForClosure);
511
512 register_land_service("api.land.playform.cloud", ServerPortForClosure);
514
515 register_land_service("assets.editor.land", ServerPortForClosure);
517
518 app.manage(service_registry);
520
521 dev_log!(
522 "lifecycle",
523 "[Lifecycle] [Setup] ServiceRegistry initialized and services registered."
524 );
525
526 dev_log!("lifecycle", "[Lifecycle] [Setup] Starting DNS server on preferred port 5380...");
532
533 let dns_port = Mist::start(5380).unwrap_or_else(|e| {
534 dev_log!(
535 "lifecycle",
536 "warn: [Lifecycle] [Setup] Failed to start DNS server on port 5380: {}",
537 e
538 );
539
540 Mist::start(0).unwrap_or_else(|e| {
542 dev_log!(
543 "lifecycle",
544 "error: [Lifecycle] [Setup] Completely failed to start DNS server: {}",
545 e
546 );
547
548 0 })
550 });
551
552 if dns_port == 0 {
553 dev_log!(
554 "lifecycle",
555 "warn: [Lifecycle] [Setup] DNS server failed to start, land:// protocol will not be \
556 available"
557 );
558 } else {
559 dev_log!(
560 "lifecycle",
561 "[Lifecycle] [Setup] DNS server started successfully on port {}",
562 dns_port
563 );
564
565 crate::Binary::Build::DnsCommands::StartupTime::init_dns_startup_time();
567 }
568
569 let TierWebSocketSetting = std::env::var("TierWebSocket")
584 .unwrap_or_else(|_| env!("TierWebSocket", "Disabled").to_string());
585
586 if TierWebSocketSetting == "Mist" {
587 dev_log!(
588 "lifecycle",
589 "[Lifecycle] [Setup] TierWebSocket=Mist - starting WebSocket transport on 127.0.0.1:5051"
590 );
591
592 let MistRegistry = Mist::WebSocket::HandlerRegistry::new();
593
594 let MistSecret = Mist::WebSocket::SharedSecret::random();
595
596 unsafe {
601 std::env::set_var("MountainWebSocketSecret", MistSecret.as_hex());
602
603 std::env::set_var("MountainWebSocketPort", "5051");
604 }
605
606 tokio::spawn(async move {
607 if let Err(Error) = Mist::WebSocket::ServeLocal(5051, MistSecret, MistRegistry).await {
608 dev_log!("lifecycle", "warn: [Lifecycle] [Mist] WebSocket server exited: {:?}", Error);
609 }
610 });
611 } else {
612 dev_log!(
613 "lifecycle",
614 "[Lifecycle] [Setup] TierWebSocket={} - WebSocket transport disabled",
615 TierWebSocketSetting
616 );
617 }
618
619 app.manage(DnsPort(dns_port));
621
622 let AppHandle = app.handle().clone();
623
624 TraceStep!("[Lifecycle] [Setup] AppHandle acquired.");
625
626 let AppStateArcFromClosure = AppStateArcForClosure.clone();
630
631 if let Err(e) = AppLifecycleSetup(
632 app,
633 AppHandle.clone(),
634 LocalhostUrl.clone(),
635 SchedulerForClosure.clone(),
636 AppStateArcFromClosure,
637 ) {
638 dev_log!("lifecycle", "error: [Lifecycle] [Setup] Failed to setup lifecycle: {}", e);
639 }
640
641 Ok(())
642 }
643 })
644 .register_asynchronous_uri_scheme_protocol("fiddee", |_ctx, request, responder| {
645 let response = crate::Binary::Build::Scheme::land_scheme_handler(&request);
647
648 responder.respond(response);
649 })
650 .register_asynchronous_uri_scheme_protocol("vscode-file", |ctx, request, responder| {
651 let AppHandle = ctx.app_handle().clone();
654
655 std::thread::spawn(move || {
656 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &request);
657
658 responder.respond(response);
659 });
660 })
661 .register_asynchronous_uri_scheme_protocol("vscode-webview", |ctx, request, responder| {
662 let AppHandle = ctx.app_handle().clone();
672
673 std::thread::spawn(move || {
674 let response = crate::Binary::Build::Scheme::VscodeWebviewSchemeHandler(&AppHandle, &request);
675
676 responder.respond(response);
677 });
678 })
679 .register_asynchronous_uri_scheme_protocol("vscode-webview-resource", |ctx, request, responder| {
680 let AppHandle = ctx.app_handle().clone();
693
694 std::thread::spawn(move || {
695 let Original = request.uri().to_string();
696
697 let RewrittenUri = match Original.strip_prefix("vscode-webview-resource://") {
698 Some(After) => {
699 let Rest = After.find('/').map(|I| &After[I..]).unwrap_or("/");
700
701 format!("vscode-file://vscode-app{}", Rest)
702 },
703 None => "vscode-file://vscode-app/".to_string(),
704 };
705
706 crate::dev_log!(
707 "scheme-assets",
708 "[LandFix:VscodeWebviewResource] {} -> {}",
709 Original,
710 RewrittenUri
711 );
712
713 let mut Builder = tauri::http::request::Request::builder().uri(&RewrittenUri);
714
715 for (Name, Value) in request.headers().iter() {
716 Builder = Builder.header(Name, Value);
717 }
718
719 let Forwarded = Builder
720 .method(request.method().clone())
721 .body(request.body().clone())
722 .unwrap_or_else(|_| request.clone());
723
724 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &Forwarded);
725
726 responder.respond(response);
727 });
728 })
729 .register_asynchronous_uri_scheme_protocol("vscode-resource", |ctx, request, responder| {
730 let AppHandle = ctx.app_handle().clone();
734
735 std::thread::spawn(move || {
736 let Original = request.uri().to_string();
737
738 let RewrittenUri = match Original.strip_prefix("vscode-resource://") {
739 Some(After) => {
740 let Rest = After.find('/').map(|I| &After[I..]).unwrap_or("/");
741
742 format!("vscode-file://vscode-app{}", Rest)
743 },
744 None => "vscode-file://vscode-app/".to_string(),
745 };
746
747 crate::dev_log!("scheme-assets", "[LandFix:VscodeResource] {} -> {}", Original, RewrittenUri);
748
749 let mut Builder = tauri::http::request::Request::builder().uri(&RewrittenUri);
750
751 for (Name, Value) in request.headers().iter() {
752 Builder = Builder.header(Name, Value);
753 }
754
755 let Forwarded = Builder
756 .method(request.method().clone())
757 .body(request.body().clone())
758 .unwrap_or_else(|_| request.clone());
759
760 let response = crate::Binary::Build::Scheme::VscodeFileSchemeHandler(&AppHandle, &Forwarded);
761
762 responder.respond(response);
763 });
764 })
765 .plugin(tauri_plugin_dialog::init())
766 .plugin(tauri_plugin_fs::init())
767 .invoke_handler(tauri::generate_handler![
768 crate::Binary::Tray::SwitchTrayIcon::SwitchTrayIcon,
769
770 crate::Binary::IPC::WorkbenchConfigurationCommand::MountainGetWorkbenchConfiguration,
771
772 Command::TreeView::GetTreeViewChildren::GetTreeViewChildren,
773
774 Command::LanguageFeature::MountainProvideHover::MountainProvideHover,
775
776 Command::LanguageFeature::MountainProvideCompletions::MountainProvideCompletions,
777
778 Command::LanguageFeature::MountainProvideDefinition::MountainProvideDefinition,
779
780 Command::LanguageFeature::MountainProvideReferences::MountainProvideReferences,
781
782 Command::SourceControlManagement::GetAllSourceControlManagementState::GetAllSourceControlManagementState,
783
784 Command::Keybinding::GetResolvedKeybinding::GetResolvedKeybinding,
785
786 Track::FrontendCommand::DispatchFrontendCommand::DispatchFrontendCommand,
787
788 Track::UIRequest::ResolveUIRequest::ResolveUIRequest,
789
790 Track::Webview::MountainWebviewPostMessageFromGuest::MountainWebviewPostMessageFromGuest,
791
792 crate::Binary::IPC::MessageReceiveCommand::MountainIPCReceiveMessage,
793
794 crate::Binary::IPC::StatusGetCommand::MountainIPCGetStatus,
795
796 crate::Binary::IPC::InvokeCommand::MountainIPCInvoke,
797
798 crate::Binary::IPC::WindConfigurationCommand::MountainGetWindDesktopConfiguration,
799
800 crate::Binary::IPC::ConfigurationUpdateCommand::MountainUpdateConfigurationFromWind,
801
802 crate::Binary::IPC::ConfigurationSyncCommand::MountainSynchronizeConfiguration,
803
804 crate::Binary::IPC::ConfigurationStatusCommand::MountainGetConfigurationStatus,
805
806 crate::Binary::IPC::IPCStatusCommand::MountainGetIPCStatus,
807
808 crate::Binary::IPC::IPCStatusHistoryCommand::MountainGetIPCStatusHistory,
809
810 crate::Binary::IPC::IPCStatusReportingStartCommand::MountainStartIPCStatusReporting,
811
812 crate::Binary::IPC::PerformanceStatsCommand::MountainGetPerformanceStats,
813
814 crate::Binary::IPC::CacheStatsCommand::MountainGetCacheStats,
815
816 crate::Binary::IPC::CollaborationSessionCommand::MountainCreateCollaborationSession,
817
818 crate::Binary::IPC::CollaborationSessionCommand::MountainGetCollaborationSessions,
819
820 crate::Binary::IPC::DocumentSyncCommand::MountainAddDocumentForSync,
821
822 crate::Binary::IPC::DocumentSyncCommand::MountainGetSyncStatus,
823
824 crate::Binary::IPC::UpdateSubscriptionCommand::MountainSubscribeToUpdates,
825
826 crate::Binary::IPC::ConfigurationDataCommand::GetConfigurationData,
827
828 crate::Binary::IPC::ConfigurationDataCommand::SaveConfigurationData,
829
830 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceOpenFolder,
831
832 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceListFolders,
833
834 crate::Binary::IPC::WorkspaceFolderCommand::MountainWorkspaceCloseAllFolders,
835
836 crate::Binary::Build::DnsCommands::dns_get_server_info::dns_get_server_info,
837
838 crate::Binary::Build::DnsCommands::dns_get_zone_info::dns_get_zone_info,
839
840 crate::Binary::Build::DnsCommands::dns_get_forward_allowlist::dns_get_forward_allowlist,
841
842 crate::Binary::Build::DnsCommands::dns_get_health_status::dns_get_health_status,
843
844 crate::Binary::Build::DnsCommands::dns_resolve::dns_resolve,
845
846 crate::Binary::Build::DnsCommands::dns_test_resolution::dns_test_resolution,
847
848 crate::Binary::Build::DnsCommands::dns_health_check::dns_health_check,
849
850 crate::Binary::IPC::ProcessCommand::process_get_exec_path::process_get_exec_path,
852
853 crate::Binary::IPC::ProcessCommand::process_get_platform::process_get_platform,
854
855 crate::Binary::IPC::ProcessCommand::process_get_arch::process_get_arch,
856
857 crate::Binary::IPC::ProcessCommand::process_get_pid::process_get_pid,
858
859 crate::Binary::IPC::ProcessCommand::process_get_shell_env::process_get_shell_env,
860
861 crate::Binary::IPC::ProcessCommand::process_get_memory_info::process_get_memory_info,
862
863 crate::Binary::IPC::HealthCommand::cocoon_extension_host_health::cocoon_extension_host_health,
865
866 crate::Binary::IPC::HealthCommand::cocoon_search_service_health::cocoon_search_service_health,
867
868 crate::Binary::IPC::HealthCommand::cocoon_debug_service_health::cocoon_debug_service_health,
869
870 crate::Binary::IPC::HealthCommand::shared_process_service_health::shared_process_service_health,
871
872 crate::Binary::IPC::RenderDevLogCommand::RenderDevLog,
873
874 crate::Binary::IPC::VineSubscribeCommand::vine_subscribe_notifications,
883
884 crate::Binary::IPC::VineSubscribeCommand::vine_subscriber_count,
885 ])
886 .build(tauri::generate_context!())
887 .expect("FATAL: Error while building Mountain Tauri application")
888 .run(move |app_handle:&tauri::AppHandle, event:tauri::RunEvent| {
889 if cfg!(debug_assertions) {
891 match &event {
892 RunEvent::MainEventsCleared => {},
893 RunEvent::WindowEvent { .. } => {},
894 _ => dev_log!("lifecycle", "[Lifecycle] [RunEvent] {:?}", event),
895 }
896 }
897
898 if let RunEvent::ExitRequested { api, .. } = event {
899 static SHUTTING_DOWN:AtomicBool = AtomicBool::new(false);
907
908 if SHUTTING_DOWN.swap(true, Ordering::SeqCst) {
909 return;
910 }
911
912 dev_log!(
913 "lifecycle",
914 "warn: [Lifecycle] [Shutdown] Exit requested. Starting graceful shutdown..."
915 );
916
917 api.prevent_exit();
918
919 let SchedulerHandle = Scheduler.clone();
920
921 let app_handle_clone = app_handle.clone();
922
923 tokio::spawn(async move {
924 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Shutting down ApplicationRunTime...");
925
926 let _ = RuntimeShutdownFn(&app_handle_clone).await;
927
928 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Stopping Echo scheduler...");
929
930 let _ = SchedulerShutdownFn(SchedulerHandle).await;
931
932 dev_log!("lifecycle", "[Lifecycle] [Shutdown] Done. Exiting process.");
933
934 app_handle_clone.exit(0);
935 });
936 }
937 });
938
939 dev_log!("lifecycle", "[Lifecycle] [Exit] Mountain application has shut down.");
940 });
941}