Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T05:38:35.246Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

main.rs

2024 lines · 75.2 KB · rust
1// Disable command line from opening on release mode
2#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3
4mod reliability;
5mod zed;
6
7// Ensure the binary name stays in sync with the application identity.
8const _: () = assert!(
9    app_identity::BINARY_NAME
10        .as_bytes()
11        .eq_ignore_ascii_case(env!("CARGO_BIN_NAME").as_bytes()),
12    "app_identity::BINARY_NAME must match the binary name.",
13);
14
15use agent_ui::AgentPanel;
16use anyhow::{Context as _, Result};
17use clap::Parser;
18use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
19use client::{Client, ProxySettings, RefreshLlmTokenListener, UserStore, parse_zed_link};
20use collections::HashMap;
21use crashes::InitCrashHandler;
22use db::kvp::{GlobalKeyValueStore, KeyValueStore};
23use extension::ExtensionHostProxy;
24use fs::{Fs, RealFs};
25use futures::{StreamExt, channel::oneshot};
26use git::GitHostingProviderRegistry;
27use git_ui::clone::clone_and_open;
28use gpui::{
29    App, AppContext, Application, AsyncApp, QuitMode, Task, TaskExt, UpdateGlobal as _, block_on,
30};
31use gpui_platform;
32
33use gpui_tokio::Tokio;
34use language::LanguageRegistry;
35use onboarding::await_identity_ready;
36use project_panel::ProjectPanel;
37use prompt_store::PromptBuilder;
38use remote::RemoteConnectionOptions;
39use reqwest_client::ReqwestClient;
40
41use assets::Assets;
42use node_runtime::{NodeBinaryOptions, NodeRuntime};
43use parking_lot::Mutex;
44use project::{project_settings::ProjectSettings, trusted_worktrees};
45use recent_projects::{RemoteSettings, open_remote_project};
46use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
47use session::{AppSession, Session};
48use settings::{BaseKeymap, Settings, SettingsStore, watch_config_file};
49use smol::future::poll_once;
50use std::{
51    cell::RefCell,
52    env,
53    io::{self, IsTerminal},
54    path::{Path, PathBuf},
55    process,
56    rc::Rc,
57    sync::{Arc, LazyLock, OnceLock},
58    time::Instant,
59};
60use theme::{ActiveTheme, GlobalTheme, ThemeRegistry};
61use theme_settings::load_user_theme;
62use util::{ResultExt, maybe};
63use uuid::Uuid;
64use workspace::{
65    AppState, MultiWorkspace, SerializedWorkspaceLocation, SessionWorkspace, Toast,
66    WorkspaceSettings, WorkspaceStore, notifications::NotificationId, restore_multiworkspace,
67};
68use zed::{
69    OpenListener, OpenRequest, RawOpenRequest, app_menus, build_window_options,
70    derive_paths_with_position, edit_prediction_registry, handle_cli_connection,
71    handle_keymap_file_changes, initialize_workspace, open_paths_with_positions,
72};
73
74use crate::zed::{CrashHandler, OpenRequestKind, eager_load_active_theme_and_icon_theme};
75
76#[cfg(feature = "mimalloc")]
77#[global_allocator]
78static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
79
80fn build_application() -> Application {
81    let platform = gpui_platform::current_platform(false);
82    if std::env::var("ZED_EXPERIMENTAL_A11Y").as_deref() == Ok("1") {
83        Application::with_platform(platform)
84    } else {
85        Application::new_inaccessible(platform)
86    }
87}
88
89fn files_not_created_on_launch(errors: HashMap<io::ErrorKind, Vec<&Path>>) {
90    let message = "Omega failed to launch";
91    let error_details = errors
92        .into_iter()
93        .flat_map(|(kind, paths)| {
94            #[allow(unused_mut)] // for non-unix platforms
95            let mut error_kind_details = match paths.len() {
96                0 => return None,
97                1 => format!(
98                    "{kind} when creating directory {:?}",
99                    paths.first().expect("match arm checks for a single entry")
100                ),
101                _many => format!("{kind} when creating directories {paths:?}"),
102            };
103
104            #[cfg(unix)]
105            {
106                if kind == io::ErrorKind::PermissionDenied {
107                    error_kind_details.push_str("\n\nConsider using chown and chmod tools for altering the directories permissions if your user has corresponding rights.\
108                        \nFor example, `sudo chown $(whoami):staff ~/.config` and `chmod +uwrx ~/.config`");
109                }
110            }
111
112            Some(error_kind_details)
113        })
114        .collect::<Vec<_>>().join("\n\n");
115
116    eprintln!("{message}: {error_details}");
117    build_application()
118        .with_quit_mode(QuitMode::Explicit)
119        .run(move |cx| {
120            if let Ok(window) = cx.open_window(gpui::WindowOptions::default(), |_, cx| {
121                cx.new(|_| gpui::Empty)
122            }) {
123                window
124                    .update(cx, |_, window, cx| {
125                        let response = window.prompt(
126                            gpui::PromptLevel::Critical,
127                            message,
128                            Some(&error_details),
129                            &["Exit"],
130                            cx,
131                        );
132
133                        cx.spawn_in(window, async move |_, cx| {
134                            response.await?;
135                            cx.update(|_, cx| cx.quit())
136                        })
137                        .detach_and_log_err(cx);
138                    })
139                    .log_err();
140            } else {
141                fail_to_open_window(anyhow::anyhow!("{message}: {error_details}"), cx)
142            }
143        })
144}
145
146fn fail_to_open_window_async(e: anyhow::Error, cx: &mut AsyncApp) {
147    cx.update(|cx| fail_to_open_window(e, cx));
148}
149
150fn fail_to_open_window(e: anyhow::Error, _cx: &mut App) {
151    eprintln!(
152        "Omega failed to open a window: {e:?}. See {} for troubleshooting steps.",
153        app_identity::PRODUCT_DOCS_URL
154    );
155    #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
156    {
157        process::exit(1);
158    }
159
160    // Maybe unify this with gpui::platform::linux::platform::ResultExt::notify_err(..)?
161    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
162    {
163        use ashpd::desktop::notification::{Notification, NotificationProxy, Priority};
164        _cx.spawn(async move |_cx| {
165            let Ok(proxy) = NotificationProxy::new().await else {
166                process::exit(1);
167            };
168
169            let notification_id = "com.openagents.omega.Oops";
170            proxy
171                .add_notification(
172                    notification_id,
173                    Notification::new("Omega failed to launch")
174                        .body(Some(
175                            format!(
176                                "{e:?}. See {} for troubleshooting steps.",
177                                app_identity::PRODUCT_DOCS_URL
178                            )
179                            .as_str(),
180                        ))
181                        .priority(Priority::High)
182                        .icon(ashpd::desktop::Icon::with_names(&[
183                            "dialog-question-symbolic",
184                        ])),
185                )
186                .await
187                .ok();
188
189            process::exit(1);
190        })
191        .detach();
192    }
193}
194static STARTUP_TIME: OnceLock<Instant> = OnceLock::new();
195
196fn main() {
197    STARTUP_TIME.get_or_init(|| Instant::now());
198
199    // If this process was re-executed as a Linux sandbox helper, run that mode
200    // without returning. Must run before argument parsing: the wrapped command's
201    // args are appended verbatim and would otherwise be misinterpreted as Zed's
202    // own arguments.
203    sandbox::run_sandbox_launcher_if_invoked();
204
205    #[cfg(unix)]
206    util::prevent_root_execution();
207
208    let args = Args::parse();
209
210    // `zed --askpass` Makes zed operate in nc/netcat mode for use with askpass
211    #[cfg(not(target_os = "windows"))]
212    if let Some(socket) = &args.askpass {
213        askpass::main(socket);
214        return;
215    }
216
217    // `zed --crash-handler` Makes zed operate in minidump crash handler mode
218    if let Some(socket) = &args.crash_handler {
219        crashes::crash_server(socket.as_path(), paths::logs_dir().clone());
220        return;
221    }
222
223    #[cfg(target_os = "windows")]
224    if args.record_etw_trace {
225        let zed_pid = args
226            .etw_zed_pid
227            .and_then(|pid| if pid >= 0 { Some(pid as u32) } else { None });
228        let Some(output_path) = args.etw_output else {
229            eprintln!("--etw-output is required for --record-etw-trace");
230            process::exit(1);
231        };
232
233        let Some(etw_socket) = args.etw_socket else {
234            eprintln!("--etw-socket is required for --record-etw-trace");
235            process::exit(1);
236        };
237
238        if let Err(error) =
239            etw_tracing::record_etw_trace(zed_pid, &output_path, etw_socket.as_str())
240        {
241            eprintln!("ETW trace recording failed: {error:#}");
242            process::exit(1);
243        }
244        return;
245    }
246
247    #[cfg(all(not(debug_assertions), target_os = "windows"))]
248    unsafe {
249        use windows::Win32::System::Console::{ATTACH_PARENT_PROCESS, AttachConsole};
250
251        if args.foreground {
252            let _ = AttachConsole(ATTACH_PARENT_PROCESS);
253        }
254    }
255
256    // `zed --printenv` Outputs environment variables as JSON to stdout
257    if args.printenv {
258        util::shell_env::print_env();
259        return;
260    }
261
262    if args.dump_all_actions {
263        dump_all_gpui_actions();
264        return;
265    }
266
267    if args.demo_workroom {
268        unsafe {
269            std::env::set_var(workroom_ui::PUBLIC_DEMO_ENV, "1");
270        }
271    }
272
273    // Set custom data directory.
274    if let Some(dir) = &args.user_data_dir {
275        paths::set_custom_data_dir(dir);
276    }
277
278    #[cfg(target_os = "windows")]
279    match util::get_zed_cli_path() {
280        Ok(path) => askpass::set_askpass_program(path),
281        Err(err) => {
282            eprintln!("Error: {}", err);
283            if std::option_env!("ZED_BUNDLE").is_some() {
284                process::exit(1);
285            }
286        }
287    }
288
289    let file_errors = init_paths();
290    if !file_errors.is_empty() {
291        files_not_created_on_launch(file_errors);
292        return;
293    }
294
295    zlog::init();
296
297    if stdout_is_a_pty() {
298        zlog::init_output_stdout();
299    } else {
300        let result = zlog::init_output_file(paths::log_file(), Some(paths::old_log_file()));
301        if let Err(err) = result {
302            eprintln!("Could not open log file: {}... Defaulting to stdout", err);
303            zlog::init_output_stdout();
304        };
305    }
306    ztracing::init();
307
308    let version = option_env!("ZED_BUILD_ID");
309    let app_commit_sha =
310        option_env!("ZED_COMMIT_SHA").map(|commit_sha| AppCommitSha::new(commit_sha.to_string()));
311    let app_version = AppVersion::load(env!("CARGO_PKG_VERSION"), version, app_commit_sha.clone());
312
313    if args.system_specs {
314        let system_specs = system_specs::SystemSpecs::new_stateless(
315            app_version,
316            app_commit_sha,
317            *release_channel::RELEASE_CHANNEL,
318            client::telemetry::os_name(),
319            client::telemetry::os_version(),
320        );
321        println!("Omega System Specs (from CLI):\n{}", system_specs);
322        return;
323    }
324
325    rayon::ThreadPoolBuilder::new()
326        .num_threads(std::thread::available_parallelism().map_or(1, |n| n.get().div_ceil(2)))
327        .stack_size(10 * 1024 * 1024)
328        .thread_name(|ix| format!("RayonWorker{}", ix))
329        .build_global()
330        .unwrap();
331
332    log::info!(
333        "========== starting omega version {}, sha {} ==========",
334        app_version,
335        app_commit_sha
336            .as_ref()
337            .map(|sha| sha.short())
338            .as_deref()
339            .unwrap_or("unknown"),
340    );
341
342    #[cfg(windows)]
343    check_for_conpty_dll();
344
345    let app = build_application().with_assets(Assets);
346
347    let app_db = db::AppDatabase::new();
348    let system_id = app.background_executor().spawn(system_id());
349    let installation_id = app
350        .background_executor()
351        .spawn(installation_id(KeyValueStore::from_app_db(&app_db)));
352    let session_id = Uuid::new_v4().to_string();
353    let session = app.background_executor().spawn(Session::new(
354        session_id.clone(),
355        KeyValueStore::from_app_db(&app_db),
356    ));
357    let background_executor = app.background_executor();
358
359    let (open_listener, mut open_rx) = OpenListener::new();
360
361    let failed_single_instance_check = if *zed_env_vars::ZED_STATELESS
362        || *release_channel::RELEASE_CHANNEL == ReleaseChannel::Dev
363    {
364        false
365    } else {
366        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
367        {
368            crate::zed::listen_for_cli_connections(open_listener.clone()).is_err()
369        }
370
371        #[cfg(target_os = "windows")]
372        {
373            !crate::zed::windows_only_instance::handle_single_instance(open_listener.clone(), &args)
374        }
375
376        #[cfg(target_os = "macos")]
377        {
378            use zed::mac_only_instance::*;
379            ensure_only_instance() != IsOnlyInstance::Yes
380        }
381    };
382    if failed_single_instance_check {
383        println!("Omega is already running");
384        return;
385    }
386
387    let should_install_crash_handler =
388        client::telemetry::should_install_crash_handler(*release_channel::RELEASE_CHANNEL);
389
390    let crash_handler = if should_install_crash_handler {
391        Some(
392            app.background_executor().spawn(crashes::init(
393                InitCrashHandler {
394                    session_id,
395                    // strip the build and channel information from the version string, we send them separately
396                    zed_version: semver::Version::new(
397                        app_version.major,
398                        app_version.minor,
399                        app_version.patch,
400                    )
401                    .to_string(),
402                    binary: "zed".to_string(),
403                    release_channel: release_channel::RELEASE_CHANNEL_NAME.clone(),
404                    commit_sha: app_commit_sha
405                        .as_ref()
406                        .map(|sha| sha.full())
407                        .unwrap_or_else(|| "no sha".to_owned()),
408                },
409                {
410                    let background_executor1 = app.background_executor();
411                    move |task| {
412                        background_executor1.spawn(task).detach();
413                    }
414                },
415                |pid| paths::temp_dir().join(format!("zed-crash-handler-{pid}")),
416                move |duration| background_executor.timer(duration),
417            )),
418        )
419    } else {
420        crashes::force_backtrace();
421        None
422    };
423
424    let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new());
425    let git_binary_path =
426        if cfg!(target_os = "macos") && option_env!("ZED_BUNDLE").as_deref() == Some("true") {
427            app.path_for_auxiliary_executable("git")
428                .context("could not find git binary path")
429                .log_err()
430        } else {
431            None
432        };
433    if let Some(git_binary_path) = &git_binary_path {
434        log::info!("Using git binary path: {:?}", git_binary_path);
435    }
436
437    let fs = Arc::new(RealFs::new(git_binary_path, app.background_executor()));
438    let (user_keymap_file_rx, user_keymap_watcher) = watch_config_file(
439        &app.background_executor(),
440        fs.clone(),
441        paths::keymap_file().clone(),
442    );
443
444    let (shell_env_loaded_tx, shell_env_loaded_rx) = oneshot::channel();
445    if !stdout_is_a_pty() {
446        app.background_executor()
447            .spawn(async {
448                #[cfg(unix)]
449                util::load_login_shell_environment().await.log_err();
450                shell_env_loaded_tx.send(()).ok();
451            })
452            .detach();
453    } else {
454        drop(shell_env_loaded_tx)
455    }
456
457    app.on_open_urls({
458        let open_listener = open_listener.clone();
459        move |urls| {
460            open_listener.open(RawOpenRequest {
461                urls,
462                diff_paths: Vec::new(),
463                ..Default::default()
464            })
465        }
466    });
467    app.on_reopen(move |cx| {
468        if let Some(app_state) = AppState::try_global(cx) {
469            cx.spawn({
470                async move |cx| {
471                    if let Err(e) = restore_or_create_workspace(app_state, cx).await {
472                        fail_to_open_window_async(e, cx)
473                    }
474                }
475            })
476            .detach();
477        }
478    });
479
480    app.run(move |cx| {
481        cx.set_global(app_db);
482        let db_trusted_paths = match workspace::WorkspaceDb::global(cx).fetch_trusted_worktrees() {
483            Ok(trusted_paths) => trusted_paths,
484            Err(e) => {
485                log::error!("Failed to do initial trusted worktrees fetch: {e:#}");
486                HashMap::default()
487            }
488        };
489        trusted_worktrees::init(db_trusted_paths, cx);
490        menu::init();
491        zed_actions::init();
492
493        release_channel::init(app_version, cx);
494        gpui_tokio::init(cx);
495        if let Some(app_commit_sha) = app_commit_sha {
496            AppCommitSha::set_global(app_commit_sha, cx);
497        }
498        settings::init(cx);
499        zlog_settings::init(cx);
500        zed::watch_settings_files(fs.clone(), cx);
501        handle_keymap_file_changes(user_keymap_file_rx, user_keymap_watcher, cx);
502
503        let user_agent = format!(
504            "Omega/{} ({}; {})",
505            AppVersion::global(cx),
506            std::env::consts::OS,
507            std::env::consts::ARCH
508        );
509        let proxy_url = ProxySettings::get_global(cx).proxy_url();
510        let http = {
511            let _guard = Tokio::handle(cx).enter();
512
513            ReqwestClient::proxy_and_user_agent(proxy_url, &user_agent)
514                .expect("could not start HTTP client")
515        };
516        cx.set_http_client(Arc::new(http));
517
518        <dyn Fs>::set_global(fs.clone(), cx);
519
520        GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx);
521        git_hosting_providers::init(cx);
522
523        OpenListener::set_global(cx, open_listener.clone());
524
525        extension::init(cx);
526        let extension_host_proxy = ExtensionHostProxy::global(cx);
527
528        let client = Client::production(cx);
529        cx.set_http_client(client.http_client());
530        let mut languages = LanguageRegistry::new(cx.background_executor().clone());
531        languages.set_language_server_download_dir(paths::languages_dir().clone());
532        let languages = Arc::new(languages);
533        let (mut tx, rx) = watch::channel(None);
534        cx.observe_global::<SettingsStore>(move |cx| {
535            let settings = &ProjectSettings::get_global(cx).node;
536            let options = NodeBinaryOptions {
537                allow_path_lookup: !settings.ignore_system_version,
538                // TODO: Expose this setting
539                allow_binary_download: true,
540                use_paths: settings.path.as_ref().map(|node_path| {
541                    let node_path = PathBuf::from(shellexpand::tilde(node_path).as_ref());
542                    let npm_path = settings
543                        .npm_path
544                        .as_ref()
545                        .map(|path| PathBuf::from(shellexpand::tilde(&path).as_ref()));
546                    (
547                        node_path.clone(),
548                        npm_path.unwrap_or_else(|| {
549                            let base_path = PathBuf::new();
550                            node_path.parent().unwrap_or(&base_path).join("npm")
551                        }),
552                    )
553                }),
554            };
555            tx.send(Some(options)).log_err();
556        })
557        .detach();
558        ui::on_new_scrollbars::<SettingsStore>(cx);
559
560        let node_runtime = NodeRuntime::new(client.http_client(), Some(shell_env_loaded_rx), rx);
561
562        debug_adapter_extension::init(extension_host_proxy.clone(), cx);
563        languages::init(languages.clone(), fs.clone(), node_runtime.clone(), cx);
564        let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
565        let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
566
567        language_extension::init(
568            language_extension::LspAccess::ViaWorkspaces({
569                let workspace_store = workspace_store.clone();
570                Arc::new(move |cx: &mut App| {
571                    workspace_store.update(cx, |workspace_store, cx| {
572                        Ok(workspace_store
573                            .workspaces()
574                            .filter_map(|weak| weak.upgrade())
575                            .map(|workspace: gpui::Entity<workspace::Workspace>| {
576                                workspace.read(cx).project().read(cx).lsp_store()
577                            })
578                            .collect())
579                    })
580                })
581            }),
582            extension_host_proxy.clone(),
583            languages.clone(),
584        );
585
586        Client::set_global(client.clone(), cx);
587
588        zed::init(cx);
589        #[cfg(target_os = "macos")]
590        project::Project::init(&client, cx);
591        debugger_ui::init(cx);
592        omega_effectd::init_openagents_session(cx);
593        omega_effectd::init_openagents_binding(cx);
594        omega_effectd::init_with_host_handler(Some(agent_ui::omega_effectd_host_handler(cx)), cx);
595        agent_computer_ui::init(cx);
596        workroom_ui::init(cx);
597        debugger_tools::init(cx);
598        client::init(&client, cx);
599        feature_flags::FeatureFlagStore::init(cx);
600
601        let system_id = cx.foreground_executor().block_on(system_id).ok();
602        let installation_id = cx.foreground_executor().block_on(installation_id).ok();
603        let session = cx.foreground_executor().block_on(session);
604
605        let telemetry = client.telemetry();
606        telemetry.start(
607            system_id.as_ref().map(|id| id.to_string()),
608            installation_id.as_ref().map(|id| id.to_string()),
609            session.id().to_owned(),
610            cx,
611        );
612        cx.subscribe(&user_store, {
613            let telemetry = telemetry.clone();
614            move |_, evt: &client::user::Event, cx| match evt {
615                client::user::Event::PrivateUserInfoUpdated => {
616                    if let Some(crash_client) = cx.try_global::<CrashHandler>() {
617                        crashes::set_user_info(
618                            &crash_client.0,
619                            crashes::UserInfo {
620                                metrics_id: telemetry.metrics_id().map(|s| s.to_string()),
621                                is_staff: telemetry.is_staff(),
622                            },
623                        );
624                    }
625                }
626                _ => {}
627            }
628        })
629        .detach();
630
631        let is_new_install = matches!(&installation_id, Some(IdType::New(_)));
632
633        // We should rename these in the future to `first app open`, `first app open for release channel`, and `app open`
634        if let (Some(system_id), Some(installation_id)) = (&system_id, &installation_id) {
635            match (&system_id, &installation_id) {
636                (IdType::New(_), IdType::New(_)) => {
637                    telemetry::event!("App First Opened");
638                    telemetry::event!("App First Opened For Release Channel");
639                }
640                (IdType::Existing(_), IdType::New(_)) => {
641                    telemetry::event!("App First Opened For Release Channel");
642                }
643                (_, IdType::Existing(_)) => {
644                    telemetry::event!("App Opened");
645                }
646            }
647        }
648        let app_session = cx.new(|cx| AppSession::new(session, cx));
649
650        let app_state = Arc::new(AppState {
651            languages,
652            client: client.clone(),
653            user_store,
654            fs: fs.clone(),
655            build_window_options,
656            workspace_store,
657            node_runtime,
658            session: app_session,
659        });
660        AppState::set_global(app_state.clone(), cx);
661
662        auto_update::init(client.clone(), cx);
663        dap_adapters::init(cx);
664        auto_update_ui::init(cx);
665        reliability::init(client.clone(), app_state.workspace_store.clone(), cx);
666        extension_host::init(
667            extension_host_proxy.clone(),
668            app_state.fs.clone(),
669            app_state.client.clone(),
670            app_state.node_runtime.clone(),
671            cx,
672        );
673
674        theme_settings::init(theme::LoadThemes::All(Box::new(Assets)), cx);
675        eager_load_active_theme_and_icon_theme(fs.clone(), cx);
676        theme_extension::init(
677            extension_host_proxy,
678            ThemeRegistry::global(cx),
679            cx.background_executor().clone(),
680        );
681        command_palette::init(cx);
682        let copilot_chat_configuration = copilot_chat::CopilotChatConfiguration {
683            enterprise_uri: language::language_settings::all_language_settings(None, cx)
684                .edit_predictions
685                .copilot
686                .enterprise_uri
687                .clone(),
688        };
689        copilot_chat::init(
690            app_state.fs.clone(),
691            app_state.client.http_client(),
692            copilot_chat_configuration,
693            cx,
694        );
695
696        copilot_ui::init(&app_state, cx);
697        language_model::init(cx);
698        RefreshLlmTokenListener::register(
699            app_state.client.clone(),
700            app_state.user_store.clone(),
701            cx,
702        );
703        language_models::init(app_state.user_store.clone(), app_state.client.clone(), cx);
704        acp_tools::init(cx);
705        zed::telemetry_log::init(cx);
706        zed::remote_debug::init(cx);
707        edit_prediction_ui::init(cx);
708        web_search::init(cx);
709        web_search_providers::init(app_state.client.clone(), app_state.user_store.clone(), cx);
710        snippet_provider::init(cx);
711        edit_prediction_registry::init(app_state.client.clone(), app_state.user_store.clone(), cx);
712        let prompt_builder = PromptBuilder::load(app_state.fs.clone(), stdout_is_a_pty(), cx);
713        project::AgentRegistryStore::init_global(
714            cx,
715            app_state.fs.clone(),
716            app_state.client.http_client(),
717        );
718        agent_ui::init(
719            app_state.fs.clone(),
720            prompt_builder,
721            app_state.languages.clone(),
722            is_new_install,
723            false,
724            cx,
725        );
726        zed::watch_user_agents_md(app_state.fs.clone(), cx);
727
728        repl::init(app_state.fs.clone(), cx);
729        recent_projects::init(cx);
730        dev_container::init(cx);
731
732        load_embedded_fonts(cx);
733
734        editor::init(cx);
735        image_viewer::init(cx);
736        repl::notebook::init(cx);
737        diagnostics::init(cx);
738
739        audio::init(cx);
740        workspace::init(app_state.clone(), cx);
741        ui_prompt::init(cx);
742
743        go_to_line::init(cx);
744        file_finder::init(cx);
745        tab_switcher::init(cx);
746        outline::init(cx);
747        project_symbols::init(cx);
748        project_panel::init(cx);
749        outline_panel::init(cx);
750        tasks_ui::init(cx);
751        snippets_ui::init(cx);
752        channel::init(&app_state.client.clone(), app_state.user_store.clone(), cx);
753        search::init(cx);
754        lsp_locations::init(cx);
755        cx.set_global(workspace::PaneSearchBarCallbacks {
756            setup_search_bar: |languages, toolbar, window, cx| {
757                let search_bar = cx.new(|cx| search::BufferSearchBar::new(languages, window, cx));
758                toolbar.update(cx, |toolbar, cx| {
759                    toolbar.add_item(search_bar, window, cx);
760                });
761            },
762            wrap_div_with_search_actions: search::buffer_search::register_pane_search_actions,
763        });
764        vim::init(cx);
765        terminal_view::init(cx);
766        journal::init(app_state.clone(), cx);
767        encoding_selector::init(cx);
768        language_selector::init(cx);
769        line_ending_selector::init(cx);
770        toolchain_selector::init(cx);
771        theme_selector::init(cx);
772        settings_profile_selector::init(cx);
773        language_tools::init(cx);
774        call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
775        notifications::init(app_state.client.clone(), app_state.user_store.clone(), cx);
776        git_ui::init(cx);
777        feedback::init(cx);
778        markdown_preview::init(cx);
779        csv_preview::init(cx);
780        svg_preview::init(cx);
781        onboarding::init(cx);
782        settings_ui::init(cx);
783        keymap_editor::init(cx);
784        extensions_ui::init(cx);
785        edit_prediction::init(cx);
786        inspector_ui::init(app_state.clone(), cx);
787        json_schema_store::init(cx);
788        miniprofiler_ui::init(*STARTUP_TIME.get().unwrap(), cx);
789        which_key::init(cx);
790        #[cfg(target_os = "windows")]
791        etw_tracing::init(cx);
792
793        cx.observe_global::<SettingsStore>({
794            let http = app_state.client.http_client();
795            let client = app_state.client.clone();
796            move |cx| {
797                for &mut window in cx.windows().iter_mut() {
798                    let background_appearance = cx.theme().window_background_appearance();
799                    window
800                        .update(cx, |_, window, _| {
801                            window.set_background_appearance(background_appearance)
802                        })
803                        .ok();
804                }
805
806                cx.set_text_rendering_mode(
807                    match WorkspaceSettings::get_global(cx).text_rendering_mode {
808                        settings::TextRenderingMode::PlatformDefault => {
809                            gpui::TextRenderingMode::PlatformDefault
810                        }
811                        settings::TextRenderingMode::Subpixel => gpui::TextRenderingMode::Subpixel,
812                        settings::TextRenderingMode::Grayscale => {
813                            gpui::TextRenderingMode::Grayscale
814                        }
815                    },
816                );
817
818                let new_host = &client::ClientSettings::get_global(cx).server_url;
819                if &http.base_url() != new_host {
820                    http.set_base_url(new_host);
821                    if client.status().borrow().is_connected() {
822                        client.reconnect(&cx.to_async());
823                    }
824                }
825            }
826        })
827        .detach();
828        app_state.languages.set_theme(cx.theme().clone());
829        cx.observe_global::<GlobalTheme>({
830            let languages = app_state.languages.clone();
831            move |cx| {
832                languages.set_theme(cx.theme().clone());
833            }
834        })
835        .detach();
836        telemetry::event!(
837            "Settings Changed",
838            setting = "theme",
839            value = cx.theme().name.to_string()
840        );
841        telemetry::event!(
842            "Settings Changed",
843            setting = "keymap",
844            value = BaseKeymap::get_global(cx).to_string()
845        );
846        telemetry.flush_events().detach();
847
848        let fs = app_state.fs.clone();
849        load_user_themes_in_background(fs.clone(), cx);
850        watch_themes(fs.clone(), cx);
851        #[cfg(debug_assertions)]
852        watch_languages(fs.clone(), app_state.languages.clone(), cx);
853
854        let menus = app_menus(cx);
855        cx.set_menus(menus);
856
857        if let Some(mut crash_handler) = crash_handler {
858            let crash_handler2 = block_on(poll_once(&mut crash_handler));
859            match crash_handler2 {
860                Some(crash_handler) => {
861                    cx.set_global(CrashHandler(crash_handler));
862                }
863                None => {
864                    cx.spawn(async move |cx| {
865                        let client1 = crash_handler.await;
866                        cx.update(|cx| {
867                            cx.set_global(CrashHandler(client1));
868                        });
869                    })
870                    .detach();
871                }
872            }
873        }
874
875        initialize_workspace(app_state.clone(), cx);
876
877        cx.activate(true);
878
879        cx.spawn({
880            let client = app_state.client.clone();
881            async move |cx| authenticate(client, cx).await
882        })
883        .detach_and_log_err(cx);
884
885        let urls: Vec<_> = args
886            .paths_or_urls
887            .iter()
888            .map(|arg| parse_url_arg(arg, cx))
889            .collect();
890
891        // Check if any diff paths are directories to determine diff_all mode
892        let diff_all_mode = args
893            .diff
894            .chunks(2)
895            .any(|pair| Path::new(&pair[0]).is_dir() || Path::new(&pair[1]).is_dir());
896
897        let diff_paths: Vec<[String; 2]> = args
898            .diff
899            .chunks(2)
900            .map(|chunk| [chunk[0].clone(), chunk[1].clone()])
901            .collect();
902
903        #[cfg(target_os = "windows")]
904        let wsl = args.wsl;
905        #[cfg(not(target_os = "windows"))]
906        let wsl = None;
907
908        if !urls.is_empty() || !diff_paths.is_empty() {
909            open_listener.open(RawOpenRequest {
910                urls,
911                diff_paths,
912                wsl,
913                diff_all: diff_all_mode,
914                dev_container: args.dev_container,
915                ..Default::default()
916            })
917        }
918
919        let (current_session_id, last_session_id) = {
920            let session = app_state.session.read(cx);
921            (
922                session.id().to_owned(),
923                session.last_session_id().map(|id| id.to_owned()),
924            )
925        };
926
927        let restore_task = match open_rx
928            .try_recv()
929            .ok()
930            .and_then(|request| OpenRequest::parse(request, cx).log_err())
931        {
932            Some(request) if request.is_focus_app_only() => cx.spawn({
933                let app_state = app_state.clone();
934                async move |cx| {
935                    if let Err(e) = restore_or_create_workspace(app_state, cx).await {
936                        fail_to_open_window_async(e, cx)
937                    }
938                }
939            }),
940            Some(request) => {
941                handle_open_request(request, app_state.clone(), cx);
942                Task::ready(())
943            }
944            None => cx.spawn({
945                let app_state = app_state.clone();
946                async move |cx| {
947                    if let Err(e) = restore_or_create_workspace(app_state, cx).await {
948                        fail_to_open_window_async(e, cx)
949                    }
950                }
951            }),
952        };
953
954        cx.spawn({
955            let db = workspace::WorkspaceDb::global(cx);
956            let fs = app_state.fs.clone();
957            async move |_cx| {
958                restore_task.await;
959                db.garbage_collect_workspaces(
960                    fs.as_ref(),
961                    &current_session_id,
962                    last_session_id.as_deref(),
963                )
964                .await
965            }
966        })
967        .detach_and_log_err(cx);
968
969        let app_state = app_state.clone();
970
971        component_preview::init(app_state.clone(), cx);
972
973        cx.spawn(async move |cx| {
974            while let Some(urls) = open_rx.next().await {
975                cx.update(|cx| {
976                    if let Some(request) = OpenRequest::parse(urls, cx).log_err() {
977                        handle_open_request(request, app_state.clone(), cx);
978                    }
979                });
980            }
981        })
982        .detach();
983    });
984}
985
986fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut App) {
987    cx.spawn(async move |cx| {
988        if let Err(error) = await_identity_ready(app_state.clone(), cx).await {
989            fail_to_open_window_async(error, cx);
990            return;
991        }
992        cx.update(|cx| dispatch_open_request(request, app_state, cx));
993    })
994    .detach();
995}
996
997fn dispatch_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut App) {
998    if let Some(kind) = request.kind {
999        match kind {
1000            OpenRequestKind::CliConnection(connection) => {
1001                cx.spawn(async move |cx| handle_cli_connection(connection, app_state, cx).await)
1002                    .detach();
1003            }
1004            OpenRequestKind::FocusApp => {
1005                cx.spawn(async move |cx| {
1006                    if workspace::activate_any_workspace_window(cx).is_some() {
1007                        return anyhow::Ok(());
1008                    }
1009                    restore_or_create_workspace(app_state, cx).await
1010                })
1011                .detach_and_log_err(cx);
1012            }
1013            OpenRequestKind::Extension { extension_id } => {
1014                cx.spawn(async move |cx| {
1015                    let workspace =
1016                        workspace::get_any_active_multi_workspace(app_state, cx.clone()).await?;
1017                    workspace.update(cx, |_, window, cx| {
1018                        window.dispatch_action(
1019                            Box::new(zed_actions::Extensions {
1020                                category_filter: None,
1021                                id: Some(extension_id),
1022                            }),
1023                            cx,
1024                        );
1025                    })
1026                })
1027                .detach_and_log_err(cx);
1028            }
1029            OpenRequestKind::AgentPanel {
1030                external_source_prompt,
1031            } => {
1032                cx.spawn(async move |cx| {
1033                    let multi_workspace =
1034                        workspace::get_any_active_multi_workspace(app_state, cx.clone()).await?;
1035
1036                    let panels_task = multi_workspace.update(cx, |multi_workspace, _, cx| {
1037                        multi_workspace
1038                            .workspace()
1039                            .update(cx, |workspace, _| workspace.take_panels_task())
1040                    })?;
1041                    if let Some(task) = panels_task {
1042                        task.await.log_err();
1043                    }
1044
1045                    multi_workspace.update(cx, |multi_workspace, window, cx| {
1046                        multi_workspace.workspace().update(cx, |workspace, cx| {
1047                            if let Some(panel) = workspace.focus_panel::<AgentPanel>(window, cx) {
1048                                panel.update(cx, |panel, cx| {
1049                                    panel.new_agent_thread_with_external_source_prompt(
1050                                        external_source_prompt,
1051                                        window,
1052                                        cx,
1053                                    );
1054                                });
1055                            } else {
1056                                log::warn!(
1057                                    "zed://agent received but the AgentPanel is not registered \
1058                                     (is `disable_ai` enabled?)"
1059                                );
1060                            }
1061                        });
1062                    })
1063                })
1064                .detach_and_log_err(cx);
1065            }
1066            OpenRequestKind::InstallSkill { content } => {
1067                cx.spawn(async move |cx| {
1068                    let multi_workspace =
1069                        workspace::get_any_active_multi_workspace(app_state, cx.clone()).await?;
1070
1071                    multi_workspace.update(cx, |_multi_workspace, _window, cx| {
1072                        settings_ui::open_skill_creator(
1073                            settings_ui::pages::SkillCreatorOpenMode::Install { content },
1074                            Some(multi_workspace),
1075                            cx,
1076                        );
1077                    })
1078                })
1079                .detach_and_log_err(cx);
1080            }
1081            OpenRequestKind::DockMenuAction { index } => {
1082                cx.perform_dock_menu_action(index);
1083            }
1084            OpenRequestKind::BuiltinJsonSchema { schema_path } => {
1085                workspace::with_active_or_new_workspace(cx, |_workspace, window, cx| {
1086                    cx.spawn_in(window, async move |workspace, cx| {
1087                        let res = async move {
1088                            let json = app_state.languages.language_for_name("JSONC").await.ok();
1089                            let lsp_store = workspace.update(cx, |workspace, cx| {
1090                                workspace
1091                                    .project()
1092                                    .update(cx, |project, _| project.lsp_store())
1093                            })?;
1094                            let uri = format!("zed://schemas/{}", schema_path);
1095                            let json_schema_content =
1096                                json_schema_store::handle_schema_request(lsp_store, uri, cx)
1097                                    .await?;
1098                            let json_schema_value: serde_json::Value =
1099                                serde_json::from_str(&json_schema_content)
1100                                    .context("Failed to parse JSON Schema")?;
1101                            let json_schema_content =
1102                                serde_json::to_string_pretty(&json_schema_value)
1103                                    .context("Failed to serialize JSON Schema as JSON")?;
1104                            let buffer_task = workspace.update(cx, |workspace, cx| {
1105                                workspace.project().update(cx, |project, cx| {
1106                                    project.create_buffer(json, false, cx)
1107                                })
1108                            })?;
1109
1110                            let buffer = buffer_task.await?;
1111
1112                            workspace.update_in(cx, |workspace, window, cx| {
1113                                buffer.update(cx, |buffer, cx| {
1114                                    buffer.edit([(0..0, json_schema_content)], None, cx);
1115                                    buffer.edit(
1116                                        [(0..0, format!("// {} JSON Schema\n", schema_path))],
1117                                        None,
1118                                        cx,
1119                                    );
1120                                });
1121
1122                                workspace.add_item_to_active_pane(
1123                                    Box::new(cx.new(|cx| {
1124                                        let mut editor =
1125                                            editor::Editor::for_buffer(buffer, None, window, cx);
1126                                        editor.set_read_only(true);
1127                                        editor
1128                                    })),
1129                                    None,
1130                                    true,
1131                                    window,
1132                                    cx,
1133                                );
1134                            })
1135                        }
1136                        .await;
1137                        res.context("Failed to open builtin JSON Schema").log_err();
1138                    })
1139                    .detach();
1140                });
1141            }
1142            OpenRequestKind::Setting { setting_path } => {
1143                // zed://settings/languages/$(language)/tab_size  - DONT SUPPORT
1144                // zed://settings/languages/Rust/tab_size  - SUPPORT
1145                // languages.$(language).tab_size
1146                // [ languages $(language) tab_size]
1147                cx.spawn(async move |cx| {
1148                    let workspace =
1149                        workspace::get_any_active_multi_workspace(app_state, cx.clone()).await?;
1150
1151                    workspace.update(cx, |_, window, cx| match setting_path {
1152                        None => window.dispatch_action(Box::new(zed_actions::OpenSettings), cx),
1153                        Some(setting_path) => window.dispatch_action(
1154                            Box::new(zed_actions::OpenSettingsAt {
1155                                path: setting_path,
1156                                target: None,
1157                            }),
1158                            cx,
1159                        ),
1160                    })
1161                })
1162                .detach_and_log_err(cx);
1163            }
1164            OpenRequestKind::GitClone { repo_url } => {
1165                workspace::with_active_or_new_workspace(cx, |_workspace, window, cx| {
1166                    if window.is_window_active() {
1167                        clone_and_open(
1168                            repo_url,
1169                            cx.weak_entity(),
1170                            window,
1171                            cx,
1172                            Arc::new(|workspace: &mut workspace::Workspace, window, cx| {
1173                                workspace.focus_panel::<ProjectPanel>(window, cx);
1174                            }),
1175                        );
1176                        return;
1177                    }
1178
1179                    let subscription = Rc::new(RefCell::new(None));
1180                    subscription.replace(Some(cx.observe_in(&cx.entity(), window, {
1181                        let subscription = subscription.clone();
1182                        let repo_url = repo_url;
1183                        move |_, workspace_entity, window, cx| {
1184                            if window.is_window_active() && subscription.take().is_some() {
1185                                clone_and_open(
1186                                    repo_url.clone(),
1187                                    workspace_entity.downgrade(),
1188                                    window,
1189                                    cx,
1190                                    Arc::new(|workspace: &mut workspace::Workspace, window, cx| {
1191                                        workspace.focus_panel::<ProjectPanel>(window, cx);
1192                                    }),
1193                                );
1194                            }
1195                        }
1196                    })));
1197                });
1198            }
1199            OpenRequestKind::GitCommit { sha } => {
1200                let base_open_options = zed::open_options_for_request(
1201                    request.open_behavior,
1202                    &workspace::SerializedWorkspaceLocation::Local,
1203                    cx,
1204                );
1205                cx.spawn(async move |cx| {
1206                    let paths_with_position =
1207                        derive_paths_with_position(app_state.fs.as_ref(), request.open_paths).await;
1208                    let (workspace, _results) = open_paths_with_positions(
1209                        &paths_with_position,
1210                        &[],
1211                        false,
1212                        app_state,
1213                        base_open_options,
1214                        cx,
1215                    )
1216                    .await?;
1217
1218                    workspace
1219                        .update(cx, |multi_workspace, window, cx| {
1220                            multi_workspace
1221                                .workspace()
1222                                .clone()
1223                                .update(cx, |workspace, cx| {
1224                                    let Some(repo) =
1225                                        workspace.project().read(cx).active_repository(cx)
1226                                    else {
1227                                        log::error!("no active repository found for commit view");
1228                                        return Err(anyhow::anyhow!("no active repository found"));
1229                                    };
1230
1231                                    git_ui::commit_view::CommitView::open(
1232                                        sha,
1233                                        repo.downgrade(),
1234                                        workspace.weak_handle(),
1235                                        None,
1236                                        None,
1237                                        window,
1238                                        cx,
1239                                    );
1240                                    Ok(())
1241                                })
1242                        })
1243                        .log_err();
1244
1245                    anyhow::Ok(())
1246                })
1247                .detach_and_log_err(cx);
1248            }
1249        }
1250
1251        return;
1252    }
1253
1254    if let Some(connection_options) = request.remote_connection {
1255        let open_behavior = request.open_behavior;
1256        let location = workspace::SerializedWorkspaceLocation::Remote(connection_options.clone());
1257        let base_open_options = zed::open_options_for_request(open_behavior, &location, cx);
1258        cx.spawn(async move |cx| {
1259            let paths: Vec<PathBuf> = request.open_paths.into_iter().map(PathBuf::from).collect();
1260            open_remote_project(connection_options, paths, app_state, base_open_options, cx).await
1261        })
1262        .detach_and_log_err(cx);
1263        return;
1264    }
1265
1266    let mut task = None;
1267    let dev_container = request.dev_container;
1268    if !request.open_paths.is_empty() || !request.diff_paths.is_empty() {
1269        let app_state = app_state.clone();
1270        let base_open_options = zed::open_options_for_request(
1271            request.open_behavior,
1272            &workspace::SerializedWorkspaceLocation::Local,
1273            cx,
1274        );
1275        task = Some(cx.spawn(async move |cx| {
1276            let paths_with_position =
1277                derive_paths_with_position(app_state.fs.as_ref(), request.open_paths).await;
1278            let (_window, results) = open_paths_with_positions(
1279                &paths_with_position,
1280                &request.diff_paths,
1281                request.diff_all,
1282                app_state,
1283                workspace::OpenOptions {
1284                    open_in_dev_container: dev_container,
1285                    ..base_open_options
1286                },
1287                cx,
1288            )
1289            .await?;
1290            for result in results.into_iter().flatten() {
1291                if let Err(err) = result {
1292                    log::error!("Error opening path: {err:#}");
1293                }
1294            }
1295            anyhow::Ok(())
1296        }));
1297    }
1298
1299    if !request.open_channel_notes.is_empty() || request.join_channel.is_some() {
1300        cx.spawn(async move |cx| {
1301            let result = maybe!(async {
1302                if let Some(task) = task {
1303                    task.await?;
1304                }
1305                let client = app_state.client.clone();
1306                // we continue even if connection fails as join_channel/ open channel notes will
1307                // show a visible error message.
1308                client.connect(true, cx).await.into_response().log_err();
1309
1310                if let Some(channel_id) = request.join_channel {
1311                    cx.update(|cx| {
1312                        workspace::join_channel(
1313                            client::ChannelId(channel_id),
1314                            app_state.clone(),
1315                            None,
1316                            None,
1317                            cx,
1318                        )
1319                    })
1320                    .await?;
1321                }
1322
1323                // Zed channel notes are retired in Omega. See omega#59.
1324                anyhow::Ok(())
1325            })
1326            .await;
1327            if let Err(err) = result {
1328                fail_to_open_window_async(err, cx);
1329            }
1330        })
1331        .detach()
1332    } else if let Some(task) = task {
1333        cx.spawn(async move |cx| {
1334            if let Err(err) = task.await {
1335                fail_to_open_window_async(err, cx);
1336            }
1337        })
1338        .detach();
1339    }
1340}
1341
1342async fn authenticate(client: Arc<Client>, cx: &AsyncApp) -> Result<()> {
1343    if !app_identity::zed_production_services_enabled() {
1344        log::info!(
1345            "Skipping Zed account authentication; Omega production services isolation is enabled"
1346        );
1347        return Ok(());
1348    }
1349
1350    if stdout_is_a_pty() {
1351        if client::IMPERSONATE_LOGIN.is_some() {
1352            client.sign_in_with_optional_connect(false, cx).await?;
1353        } else if client.has_credentials(cx).await {
1354            client.sign_in_with_optional_connect(true, cx).await?;
1355        }
1356    } else if client.has_credentials(cx).await {
1357        client.sign_in_with_optional_connect(true, cx).await?;
1358    }
1359
1360    Ok(())
1361}
1362
1363async fn system_id() -> Result<IdType> {
1364    let key_name = "system_id".to_string();
1365    let db = GlobalKeyValueStore::global();
1366
1367    if let Ok(Some(system_id)) = db.read_kvp(&key_name) {
1368        return Ok(IdType::Existing(system_id));
1369    }
1370
1371    let system_id = Uuid::new_v4().to_string();
1372
1373    db.write_kvp(key_name, system_id.clone()).await?;
1374
1375    Ok(IdType::New(system_id))
1376}
1377
1378async fn installation_id(db: KeyValueStore) -> Result<IdType> {
1379    let legacy_key_name = "device_id".to_string();
1380    let key_name = "installation_id".to_string();
1381
1382    // Migrate legacy key to new key
1383    if let Ok(Some(installation_id)) = db.read_kvp(&legacy_key_name) {
1384        db.write_kvp(key_name, installation_id.clone()).await?;
1385        db.delete_kvp(legacy_key_name).await?;
1386        return Ok(IdType::Existing(installation_id));
1387    }
1388
1389    if let Ok(Some(installation_id)) = db.read_kvp(&key_name) {
1390        return Ok(IdType::Existing(installation_id));
1391    }
1392
1393    let installation_id = Uuid::new_v4().to_string();
1394
1395    db.write_kvp(key_name, installation_id.clone()).await?;
1396
1397    Ok(IdType::New(installation_id))
1398}
1399
1400pub(crate) async fn restore_or_create_workspace(
1401    app_state: Arc<AppState>,
1402    cx: &mut AsyncApp,
1403) -> Result<()> {
1404    await_identity_ready(app_state.clone(), cx).await?;
1405    if let Some(multi_workspaces) = restorable_workspaces(cx, &app_state).await {
1406        let mut error_count = 0;
1407        for multi_workspace in multi_workspaces {
1408            let result = match &multi_workspace.active_workspace.location {
1409                SerializedWorkspaceLocation::Local => {
1410                    restore_multiworkspace(multi_workspace, app_state.clone(), cx)
1411                        .await
1412                        .map(|_| ())
1413                }
1414                SerializedWorkspaceLocation::Remote(connection_options) => {
1415                    let mut connection_options = connection_options.clone();
1416                    if let RemoteConnectionOptions::Ssh(options) = &mut connection_options {
1417                        cx.update(|cx| {
1418                            RemoteSettings::get_global(cx)
1419                                .fill_connection_options_from_settings(options)
1420                        });
1421                    }
1422
1423                    let paths = multi_workspace
1424                        .active_workspace
1425                        .paths
1426                        .paths()
1427                        .iter()
1428                        .map(PathBuf::from)
1429                        .collect::<Vec<_>>();
1430                    let state = multi_workspace.state.clone();
1431                    async {
1432                        let window = open_remote_project(
1433                            connection_options,
1434                            paths,
1435                            app_state.clone(),
1436                            workspace::OpenOptions::default(),
1437                            cx,
1438                        )
1439                        .await?;
1440                        workspace::apply_restored_multiworkspace_state(
1441                            window,
1442                            &state,
1443                            app_state.fs.clone(),
1444                            cx,
1445                        )
1446                        .await;
1447                        Ok::<(), anyhow::Error>(())
1448                    }
1449                    .await
1450                }
1451            };
1452
1453            if let Err(error) = result {
1454                log::error!("Failed to restore workspace: {error:#}");
1455                error_count += 1;
1456            }
1457        }
1458
1459        if error_count > 0 {
1460            let message = if error_count == 1 {
1461                "Failed to restore 1 workspace. Check logs for details.".to_string()
1462            } else {
1463                format!(
1464                    "Failed to restore {} workspaces. Check logs for details.",
1465                    error_count
1466                )
1467            };
1468
1469            // Try to find an active workspace to show the toast
1470            let toast_shown = cx.update(|cx| {
1471                if let Some(window) = cx.active_window()
1472                    && let Some(multi_workspace) = window.downcast::<MultiWorkspace>()
1473                {
1474                    multi_workspace
1475                        .update(cx, |multi_workspace, _, cx| {
1476                            multi_workspace.workspace().update(cx, |workspace, cx| {
1477                                workspace.show_toast(
1478                                    Toast::new(NotificationId::unique::<()>(), message.clone()),
1479                                    cx,
1480                                )
1481                            });
1482                        })
1483                        .ok();
1484                    return true;
1485                }
1486                false
1487            });
1488
1489            // If we couldn't show a toast (no windows opened successfully),
1490            // open a fallback empty workspace and show the error there
1491            if !toast_shown {
1492                log::error!("All workspace restorations failed. Opening fallback empty workspace.");
1493                cx.update(|cx| {
1494                    workspace::open_new(
1495                        Default::default(),
1496                        app_state.clone(),
1497                        cx,
1498                        |workspace, _window, cx| {
1499                            workspace.show_toast(
1500                                Toast::new(NotificationId::unique::<()>(), message),
1501                                cx,
1502                            );
1503                        },
1504                    )
1505                })
1506                .await?;
1507            }
1508        }
1509
1510        // If the user cancelled a failed remote connection at startup,
1511        // open_remote_project returns Ok but removes the window, so error_count
1512        // stays 0 and the toast fallback above does not trigger. Without this
1513        // check, Zed would exit silently.
1514        if cx.update(|cx| cx.windows().is_empty()) {
1515            cx.update(|cx| {
1516                workspace::open_new(
1517                    Default::default(),
1518                    app_state.clone(),
1519                    cx,
1520                    |_workspace, window, cx| {
1521                        let restore_on_startup =
1522                            WorkspaceSettings::get_global(cx).restore_on_startup;
1523                        match restore_on_startup {
1524                            // OMEGA-DELTA-0019. The launchpad behaviour opens
1525                            // no content, and overriding it would be Omega
1526                            // ignoring a setting the user set.
1527                            workspace::RestoreOnStartupBehavior::Launchpad => {}
1528                            // OMEGA-DELTA-0019. Upstream Zed opens an empty
1529                            // untitled buffer here. Omega opens its front
1530                            // door: the New Agent Thread surface, focused.
1531                            _ => {
1532                                agent_ui::AgentPanel::open_front_door(window, cx);
1533                            }
1534                        }
1535                    },
1536                )
1537            })
1538            .await?;
1539        }
1540    } else {
1541        cx.update(|cx| {
1542            workspace::open_new(
1543                Default::default(),
1544                app_state,
1545                cx,
1546                |_workspace, window, cx| {
1547                    let restore_on_startup = WorkspaceSettings::get_global(cx).restore_on_startup;
1548                    match restore_on_startup {
1549                        // OMEGA-DELTA-0019. See above: the launchpad choice
1550                        // stands, and the empty buffer becomes the front door.
1551                        workspace::RestoreOnStartupBehavior::Launchpad => {}
1552                        _ => {
1553                            agent_ui::AgentPanel::open_front_door(window, cx);
1554                        }
1555                    }
1556                },
1557            )
1558        })
1559        .await?;
1560    }
1561
1562    Ok(())
1563}
1564
1565async fn restorable_workspaces(
1566    cx: &mut AsyncApp,
1567    app_state: &Arc<AppState>,
1568) -> Option<Vec<workspace::SerializedMultiWorkspace>> {
1569    let locations = restorable_workspace_locations(cx, app_state).await?;
1570    Some(cx.update(|cx| workspace::read_serialized_multi_workspaces(locations, cx)))
1571}
1572
1573pub(crate) async fn restorable_workspace_locations(
1574    cx: &mut AsyncApp,
1575    app_state: &Arc<AppState>,
1576) -> Option<Vec<SessionWorkspace>> {
1577    let (mut restore_behavior, db) = cx.update(|cx| {
1578        (
1579            WorkspaceSettings::get(None, cx).restore_on_startup,
1580            workspace::WorkspaceDb::global(cx),
1581        )
1582    });
1583
1584    let session_handle = app_state.session.clone();
1585    let (last_session_id, last_session_window_stack) = cx.update(|cx| {
1586        let session = session_handle.read(cx);
1587
1588        (
1589            session.last_session_id().map(|id| id.to_string()),
1590            session.last_session_window_stack(),
1591        )
1592    });
1593
1594    if last_session_id.is_none()
1595        && matches!(
1596            restore_behavior,
1597            workspace::RestoreOnStartupBehavior::LastSession
1598        )
1599    {
1600        restore_behavior = workspace::RestoreOnStartupBehavior::LastWorkspace;
1601    }
1602
1603    match restore_behavior {
1604        workspace::RestoreOnStartupBehavior::LastWorkspace => {
1605            workspace::last_opened_workspace_location(&db, app_state.fs.as_ref())
1606                .await
1607                .map(|(workspace_id, location, paths)| {
1608                    vec![SessionWorkspace {
1609                        workspace_id,
1610                        location,
1611                        paths,
1612                        window_id: None,
1613                    }]
1614                })
1615        }
1616        workspace::RestoreOnStartupBehavior::LastSession => {
1617            if let Some(last_session_id) = last_session_id {
1618                let ordered = last_session_window_stack.is_some();
1619
1620                let mut locations = workspace::last_session_workspace_locations(
1621                    &db,
1622                    &last_session_id,
1623                    last_session_window_stack,
1624                    app_state.fs.as_ref(),
1625                )
1626                .await
1627                .filter(|locations| !locations.is_empty());
1628
1629                // Since last_session_window_order returns the windows ordered front-to-back
1630                // we need to open the window that was frontmost last.
1631                if ordered && let Some(locations) = locations.as_mut() {
1632                    locations.reverse();
1633                }
1634
1635                locations
1636            } else {
1637                None
1638            }
1639        }
1640        _ => None,
1641    }
1642}
1643
1644fn init_paths() -> HashMap<io::ErrorKind, Vec<&'static Path>> {
1645    [
1646        paths::config_dir(),
1647        paths::extensions_dir(),
1648        paths::languages_dir(),
1649        paths::debug_adapters_dir(),
1650        paths::database_dir(),
1651        paths::logs_dir(),
1652        paths::temp_dir(),
1653        paths::hang_traces_dir(),
1654    ]
1655    .into_iter()
1656    .fold(HashMap::default(), |mut errors, path| {
1657        if let Err(e) = std::fs::create_dir_all(path) {
1658            errors.entry(e.kind()).or_insert_with(Vec::new).push(path);
1659        }
1660        errors
1661    })
1662}
1663
1664pub(crate) static FORCE_CLI_MODE: LazyLock<bool> = LazyLock::new(|| {
1665    let env_var = std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_some();
1666    unsafe { std::env::remove_var(FORCE_CLI_MODE_ENV_VAR_NAME) };
1667    env_var
1668});
1669
1670fn stdout_is_a_pty() -> bool {
1671    !*FORCE_CLI_MODE && io::stdout().is_terminal()
1672}
1673
1674#[derive(Parser, Debug)]
1675#[command(name = "omega", disable_version_flag = true, max_term_width = 100)]
1676struct Args {
1677    /// A sequence of space-separated paths or urls that you want to open.
1678    ///
1679    /// Use `path:line:row` syntax to open a file at a specific location.
1680    /// Non-existing paths and directories will ignore `:line:row` suffix.
1681    ///
1682    /// URLs can use `file://`, the current Omega channel scheme, or the legacy `zed://` scheme.
1683    paths_or_urls: Vec<String>,
1684
1685    /// Opens the explicitly fictional, offline-safe public demo workroom.
1686    ///
1687    /// A custom user data directory is required so the demo cannot read or
1688    /// modify the normal Omega profile.
1689    #[arg(long, requires = "user_data_dir")]
1690    demo_workroom: bool,
1691
1692    /// Pairs of file paths to diff. Can be specified multiple times.
1693    /// When directories are provided, recurses into them and shows all changed files in a single multi-diff view.
1694    #[arg(long, action = clap::ArgAction::Append, num_args = 2, value_names = ["OLD_PATH", "NEW_PATH"])]
1695    diff: Vec<String>,
1696
1697    /// Sets a custom directory for all user data (e.g., database, extensions, logs).
1698    ///
1699    /// This overrides the default platform-specific data directory location.
1700    /// On macOS, the default is `~/Library/Application Support/<Omega channel>`.
1701    /// On Linux/FreeBSD, the default is `$XDG_DATA_HOME/<omega-channel>`.
1702    /// On Windows, the default is `%LOCALAPPDATA%\<Omega channel>`.
1703    #[arg(long, value_name = "DIR", verbatim_doc_comment)]
1704    user_data_dir: Option<String>,
1705
1706    /// The username and WSL distribution to use when opening paths. If not specified,
1707    /// Omega will attempt to open the paths directly.
1708    ///
1709    /// The username is optional, and if not specified, the default user for the distribution
1710    /// will be used.
1711    ///
1712    /// Example: `me@Ubuntu` or `Ubuntu`.
1713    ///
1714    /// WARN: You should not fill in this field by hand.
1715    #[cfg(target_os = "windows")]
1716    #[arg(long, value_name = "USER@DISTRO")]
1717    wsl: Option<String>,
1718
1719    /// Open the project in a dev container.
1720    ///
1721    /// Automatically triggers "Reopen in Dev Container" if a `.devcontainer/`
1722    /// configuration is found in the project directory.
1723    #[arg(long)]
1724    dev_container: bool,
1725
1726    /// Instructs Omega to run as a dev server on this machine. (not implemented)
1727    #[arg(long)]
1728    dev_server_token: Option<String>,
1729
1730    /// Prints system specs.
1731    ///
1732    /// Useful for submitting issues on GitHub when encountering a bug that
1733    /// prevents Omega from starting, so you can't run `omega: copy system specs to
1734    /// clipboard`
1735    #[arg(long)]
1736    system_specs: bool,
1737
1738    /// Used for recording minidumps on crashes by having Omega run a separate
1739    /// process communicating over a socket.
1740    #[arg(long, hide = true)]
1741    crash_handler: Option<PathBuf>,
1742
1743    /// Run Omega in the foreground, only used on Windows, to match the behavior on macOS.
1744    #[arg(long)]
1745    #[cfg(target_os = "windows")]
1746    #[arg(hide = true)]
1747    foreground: bool,
1748
1749    /// The dock action to perform. This is used on Windows only.
1750    #[arg(long)]
1751    #[cfg(target_os = "windows")]
1752    #[arg(hide = true)]
1753    dock_action: Option<usize>,
1754
1755    /// Used for SSH/Git password authentication, to remove the need for netcat as a dependency,
1756    /// by having Omega act like netcat communicating over a Unix socket.
1757    #[arg(long)]
1758    #[cfg(not(target_os = "windows"))]
1759    #[arg(hide = true)]
1760    askpass: Option<String>,
1761
1762    #[arg(long, hide = true)]
1763    dump_all_actions: bool,
1764
1765    /// Output current environment variables as JSON to stdout
1766    #[arg(long, hide = true)]
1767    printenv: bool,
1768
1769    /// Record an ETW trace. Must be run as administrator.
1770    #[cfg(target_os = "windows")]
1771    #[arg(long, hide = true)]
1772    record_etw_trace: bool,
1773
1774    /// The PID of the Omega process to trace for heap analysis.
1775    #[cfg(target_os = "windows")]
1776    #[arg(long, hide = true, allow_hyphen_values = true)]
1777    etw_zed_pid: Option<i64>,
1778
1779    /// Output path for the ETW trace file.
1780    #[cfg(target_os = "windows")]
1781    #[arg(long, hide = true)]
1782    etw_output: Option<PathBuf>,
1783
1784    /// Unix socket path for IPC with the parent Omega process.
1785    #[cfg(target_os = "windows")]
1786    #[arg(long, hide = true)]
1787    etw_socket: Option<String>,
1788}
1789
1790#[derive(Clone, Debug)]
1791enum IdType {
1792    New(String),
1793    Existing(String),
1794}
1795
1796impl ToString for IdType {
1797    fn to_string(&self) -> String {
1798        match self {
1799            IdType::New(id) | IdType::Existing(id) => id.clone(),
1800        }
1801    }
1802}
1803
1804fn parse_url_arg(arg: &str, cx: &App) -> String {
1805    match std::fs::canonicalize(Path::new(&arg)) {
1806        Ok(path) => format!("file://{}", path.display()),
1807        Err(_) => {
1808            if arg.starts_with("file://")
1809                || arg.starts_with("zed://")
1810                || arg.starts_with("zed-cli://")
1811                || arg.starts_with("ssh://")
1812                || arg.starts_with(&format!(
1813                    "{}://",
1814                    ReleaseChannel::try_global(cx)
1815                        .unwrap_or(*release_channel::RELEASE_CHANNEL)
1816                        .protocol_scheme()
1817                ))
1818                || parse_zed_link(arg, cx).is_some()
1819            {
1820                arg.into()
1821            } else {
1822                format!("file://{arg}")
1823            }
1824        }
1825    }
1826}
1827
1828fn load_embedded_fonts(cx: &App) {
1829    let asset_source = cx.asset_source();
1830    let font_paths = asset_source.list("fonts").unwrap();
1831    let embedded_fonts = Mutex::new(Vec::new());
1832    let executor = cx.background_executor();
1833
1834    cx.foreground_executor().block_on(executor.scoped(|scope| {
1835        for font_path in &font_paths {
1836            if !font_path.ends_with(".ttf") {
1837                continue;
1838            }
1839
1840            scope.spawn(async {
1841                let font_bytes = asset_source.load(font_path).unwrap().unwrap();
1842                embedded_fonts.lock().push(font_bytes);
1843            });
1844        }
1845    }));
1846
1847    cx.text_system()
1848        .add_fonts(embedded_fonts.into_inner())
1849        .unwrap();
1850}
1851
1852/// Spawns a background task to load the user themes from the themes directory.
1853fn load_user_themes_in_background(fs: Arc<dyn fs::Fs>, cx: &mut App) {
1854    cx.spawn({
1855        let fs = fs.clone();
1856        async move |cx| {
1857            let theme_registry = cx.update(|cx| ThemeRegistry::global(cx));
1858            let themes_dir = paths::themes_dir().as_ref();
1859            match fs
1860                .metadata(themes_dir)
1861                .await
1862                .ok()
1863                .flatten()
1864                .map(|m| m.is_dir)
1865            {
1866                Some(is_dir) => {
1867                    anyhow::ensure!(is_dir, "Themes dir path {themes_dir:?} is not a directory")
1868                }
1869                None => {
1870                    fs.create_dir(themes_dir).await.with_context(|| {
1871                        format!("Failed to create themes dir at path {themes_dir:?}")
1872                    })?;
1873                }
1874            }
1875
1876            let mut theme_paths = fs
1877                .read_dir(themes_dir)
1878                .await
1879                .with_context(|| format!("reading themes from {themes_dir:?}"))?;
1880
1881            while let Some(theme_path) = theme_paths.next().await {
1882                let Some(theme_path) = theme_path.log_err() else {
1883                    continue;
1884                };
1885                let Some(bytes) = fs.load_bytes(&theme_path).await.log_err() else {
1886                    continue;
1887                };
1888
1889                load_user_theme(&theme_registry, &bytes).log_err();
1890            }
1891
1892            cx.update(theme_settings::reload_theme);
1893            anyhow::Ok(())
1894        }
1895    })
1896    .detach_and_log_err(cx);
1897}
1898
1899/// Spawns a background task to watch the themes directory for changes.
1900fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut App) {
1901    use std::time::Duration;
1902    cx.spawn(async move |cx| {
1903        let (mut events, _) = fs
1904            .watch(paths::themes_dir(), Duration::from_millis(100))
1905            .await;
1906
1907        while let Some(paths) = events.next().await {
1908            for event in paths {
1909                if fs
1910                    .metadata(&event.path)
1911                    .await
1912                    .ok()
1913                    .flatten()
1914                    .is_some_and(|m| !m.is_dir)
1915                {
1916                    let theme_registry = cx.update(|cx| ThemeRegistry::global(cx));
1917                    if let Some(bytes) = fs.load_bytes(&event.path).await.log_err()
1918                        && load_user_theme(&theme_registry, &bytes).log_err().is_some()
1919                    {
1920                        cx.update(theme_settings::reload_theme);
1921                    }
1922                }
1923            }
1924        }
1925    })
1926    .detach()
1927}
1928
1929#[cfg(debug_assertions)]
1930fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>, cx: &mut App) {
1931    use std::time::Duration;
1932
1933    cx.background_spawn(async move {
1934        let languages_src = Path::new("crates/grammars/src");
1935        let Some(languages_src) = fs.canonicalize(languages_src).await.log_err() else {
1936            return;
1937        };
1938
1939        let (mut events, watcher) = fs.watch(&languages_src, Duration::from_millis(100)).await;
1940
1941        // add subdirectories since fs.watch is not recursive on Linux
1942        if let Some(mut paths) = fs.read_dir(&languages_src).await.log_err() {
1943            while let Some(path) = paths.next().await {
1944                if let Some(path) = path.log_err()
1945                    && fs.is_dir(&path).await
1946                {
1947                    watcher.add(&path).log_err();
1948                }
1949            }
1950        }
1951
1952        while let Some(event) = events.next().await {
1953            let has_language_file = event
1954                .iter()
1955                .any(|event| event.path.extension().is_some_and(|ext| ext == "scm"));
1956            if has_language_file {
1957                languages.reload();
1958            }
1959        }
1960    })
1961    .detach();
1962}
1963
1964fn dump_all_gpui_actions() {
1965    #[derive(Debug, serde::Serialize)]
1966    struct ActionDef {
1967        name: &'static str,
1968        human_name: String,
1969        schema: Option<serde_json::Value>,
1970        deprecated_aliases: &'static [&'static str],
1971        deprecation_message: Option<&'static str>,
1972        documentation: Option<&'static str>,
1973    }
1974    let mut generator = settings::KeymapFile::action_schema_generator();
1975    let mut actions = gpui::generate_list_of_all_registered_actions()
1976        .map(|action| {
1977            let schema = (action.json_schema)(&mut generator)
1978                .map(|s| serde_json::to_value(s).expect("Failed to serialize action schema"));
1979            ActionDef {
1980                name: action.name,
1981                human_name: command_palette::humanize_action_name(action.name),
1982                schema,
1983                deprecated_aliases: action.deprecated_aliases,
1984                deprecation_message: action.deprecation_message,
1985                documentation: action.documentation,
1986            }
1987        })
1988        .collect::<Vec<ActionDef>>();
1989
1990    actions.sort_by_key(|a| a.name);
1991
1992    let schema_definitions = serde_json::to_value(generator.definitions())
1993        .expect("Failed to serialize schema definitions");
1994
1995    let output = serde_json::json!({
1996        "actions": actions,
1997        "schema_definitions": schema_definitions,
1998    });
1999
2000    io::Write::write(
2001        &mut std::io::stdout(),
2002        serde_json::to_string_pretty(&output).unwrap().as_bytes(),
2003    )
2004    .unwrap();
2005}
2006
2007#[cfg(target_os = "windows")]
2008fn check_for_conpty_dll() {
2009    use windows::{
2010        Win32::{Foundation::FreeLibrary, System::LibraryLoader::LoadLibraryW},
2011        core::w,
2012    };
2013
2014    if let Ok(hmodule) = unsafe { LoadLibraryW(w!("conpty.dll")) } {
2015        unsafe {
2016            FreeLibrary(hmodule)
2017                .context("Failed to free conpty.dll")
2018                .log_err();
2019        }
2020    } else {
2021        log::warn!("Failed to load conpty.dll. Terminal will work with reduced functionality.");
2022    }
2023}
2024
Served at tenant.openagents/omega Member data and write actions are omitted.