Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/IPC/WindServiceHandlers/NativeHost/
InstallShellCommand.rs

1//! `nativeHost:installShellCommand` - create a `land` (or `code`) symlink in
2//! `/usr/local/bin` pointing at the running executable so the user can launch
3//! the editor from a terminal. Mirrors VS Code's "Install 'code' command in
4//! PATH" command. Uses `pkexec`/`osascript` to acquire elevated privileges
5//! when `/usr/local/bin` is not writable by the current user.
6
7use std::path::PathBuf;
8
9use serde_json::Value;
10
11use crate::dev_log;
12
13const CLI_NAME:&str = "land";
14
15const SYMLINK_DIR:&str = "/usr/local/bin";
16
17pub async fn Fn(_Arguments:Vec<Value>) -> Result<Value, String> {
18	let ExePath = std::env::current_exe().map_err(|E| format!("installShellCommand: cannot get exe path: {E}"))?;
19
20	let Target = PathBuf::from(SYMLINK_DIR).join(CLI_NAME);
21
22	dev_log!("shell-cmd", "installShellCommand: {} → {}", Target.display(), ExePath.display());
23
24	// Remove stale link first (ignore errors - may not exist yet).
25	let _ = std::fs::remove_file(&Target);
26
27	match std::os::unix::fs::symlink(&ExePath, &Target) {
28		Ok(()) => {
29			dev_log!("shell-cmd", "installShellCommand: symlink created");
30
31			Ok(Value::Bool(true))
32		},
33
34		Err(E) if E.kind() == std::io::ErrorKind::PermissionDenied => {
35			// Retry with osascript-elevated write on macOS.
36			#[cfg(target_os = "macos")]
37			{
38				// Pass paths via env vars; use AppleScript's `quoted form of` for
39				// safe shell quoting - no interpolation into script source.
40				let Status = tokio::process::Command::new("osascript")
41					.env("SH_SRC", ExePath.as_os_str())
42					.env("SH_DST", Target.as_os_str())
43					.args([
44						"-e",
45						"do shell script (\"ln -sf \" & quoted form of (system attribute \"SH_SRC\") & \" \" & quoted \
46						 form of (system attribute \"SH_DST\")) with administrator privileges",
47					])
48					.status()
49					.await
50					.map_err(|E| format!("installShellCommand: osascript failed: {E}"))?;
51
52				if Status.success() {
53					dev_log!("shell-cmd", "installShellCommand: symlink created (elevated)");
54
55					return Ok(Value::Bool(true));
56				}
57			}
58
59			Err(format!("installShellCommand: permission denied and elevation failed"))
60		},
61
62		Err(E) => Err(format!("installShellCommand: {E}")),
63	}
64}