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