1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
const CLI_INSTALL_DIR: &str = ".opencode/bin";
const CLI_BINARY_NAME: &str = "opencode";
fn get_cli_install_path() -> Option<std::path::PathBuf> {
std::env::var("HOME").ok().map(|home| {
std::path::PathBuf::from(home)
.join(CLI_INSTALL_DIR)
.join(CLI_BINARY_NAME)
})
}
pub fn get_sidecar_path() -> std::path::PathBuf {
tauri::utils::platform::current_exe()
.expect("Failed to get current exe")
.parent()
.expect("Failed to get parent dir")
.join("opencode-cli")
}
fn is_cli_installed() -> bool {
get_cli_install_path()
.map(|path| path.exists())
.unwrap_or(false)
}
const INSTALL_SCRIPT: &str = include_str!("../../../../install");
#[tauri::command]
pub fn install_cli() -> Result<String, String> {
if cfg!(not(unix)) {
return Err("CLI installation is only supported on macOS & Linux".to_string());
}
let sidecar = get_sidecar_path();
if !sidecar.exists() {
return Err("Sidecar binary not found".to_string());
}
let temp_script = std::env::temp_dir().join("opencode-install.sh");
std::fs::write(&temp_script, INSTALL_SCRIPT)
.map_err(|e| format!("Failed to write install script: {}", e))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&temp_script, std::fs::Permissions::from_mode(0o755))
.map_err(|e| format!("Failed to set script permissions: {}", e))?;
}
let output = std::process::Command::new(&temp_script)
.arg("--binary")
.arg(&sidecar)
.output()
.map_err(|e| format!("Failed to run install script: {}", e))?;
let _ = std::fs::remove_file(&temp_script);
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("Install script failed: {}", stderr));
}
let install_path =
get_cli_install_path().ok_or_else(|| "Could not determine install path".to_string())?;
Ok(install_path.to_string_lossy().to_string())
}
pub fn sync_cli(app: tauri::AppHandle) -> Result<(), String> {
if cfg!(debug_assertions) {
println!("Skipping CLI sync for debug build");
return Ok(());
}
if !is_cli_installed() {
println!("No CLI installation found, skipping sync");
return Ok(());
}
let cli_path =
get_cli_install_path().ok_or_else(|| "Could not determine CLI install path".to_string())?;
let output = std::process::Command::new(&cli_path)
.arg("--version")
.output()
.map_err(|e| format!("Failed to get CLI version: {}", e))?;
if !output.status.success() {
return Err("Failed to get CLI version".to_string());
}
let cli_version_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
let cli_version = semver::Version::parse(&cli_version_str)
.map_err(|e| format!("Failed to parse CLI version '{}': {}", cli_version_str, e))?;
let app_version = app.package_info().version.clone();
if cli_version >= app_version {
println!(
"CLI version {} is up to date (app version: {}), skipping sync",
cli_version, app_version
);
return Ok(());
}
println!(
"CLI version {} is older than app version {}, syncing",
cli_version, app_version
);
install_cli()?;
println!("Synced installed CLI");
Ok(())
}
|