use anyhow::{Context, Result};
pub fn configure(enable: bool, port: u16) -> Result<()> {
#[cfg(windows)]
{
configure_windows(enable, port)
}
#[cfg(not(windows))]
{
configure_auto_launch(enable, port)
}
}
pub fn hide_console_window() {
#[cfg(windows)]
{
const PS: &str = "\
$sig = '[DllImport(\"kernel32.dll\")] public static extern IntPtr GetConsoleWindow();\
[DllImport(\"user32.dll\")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);';\
$t = Add-Type -MemberDefinition $sig -Name NativeMethods -Namespace Win32 -PassThru;\
$h = $t::GetConsoleWindow();\
if ($h -ne [IntPtr]::Zero) { [void]$t::ShowWindow($h, 0) }";
let _ = std::process::Command::new("powershell")
.args(["-NoProfile", "-NonInteractive", "-Command", PS])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status();
}
}
pub fn is_enabled() -> bool {
#[cfg(windows)]
{
use winreg::RegKey;
use winreg::enums::{HKEY_CURRENT_USER, KEY_READ};
RegKey::predef(HKEY_CURRENT_USER)
.open_subkey_with_flags(RUN_KEY, KEY_READ)
.and_then(|key| key.get_value::<String, _>(VALUE_NAME))
.is_ok()
}
#[cfg(target_os = "linux")]
{
std::env::var("HOME")
.map(|home| is_enabled_in(std::path::Path::new(&home)))
.unwrap_or(false)
}
#[cfg(all(not(windows), not(target_os = "linux")))]
{
let Ok(exe) = std::env::current_exe() else {
return false;
};
let path = exe.to_string_lossy();
auto_launch::AutoLaunchBuilder::new()
.set_app_name("llmtrim")
.set_app_path(path.as_ref())
.build()
.and_then(|auto| auto.is_enabled())
.unwrap_or(false)
}
}
#[cfg(target_os = "linux")]
fn is_enabled_in(base: &std::path::Path) -> bool {
xdg_autostart_dir(base).join("llmtrim.desktop").exists()
}
#[cfg(windows)]
const RUN_KEY: &str = r"Software\Microsoft\Windows\CurrentVersion\Run";
#[cfg(windows)]
const STARTUP_APPROVED_KEY: &str =
r"Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run";
#[cfg(windows)]
const VALUE_NAME: &str = "llmtrim";
#[cfg(windows)]
fn configure_windows(enable: bool, port: u16) -> Result<()> {
use winreg::RegKey;
use winreg::enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
if enable {
let exe = std::env::current_exe().context("could not find the llmtrim executable")?;
let (key, _) = RegKey::predef(HKEY_CURRENT_USER)
.create_subkey_with_flags(RUN_KEY, KEY_READ | KEY_WRITE)
.with_context(|| format!("failed to open HKCU\\{RUN_KEY}"))?;
let cmd = format!(
"\"{}\" serve --port {} --supervised --hide-console",
exe.display(),
port
);
key.set_value(VALUE_NAME, &cmd)
.context("failed to set llmtrim autostart in the registry Run key")?;
purge_legacy_autostart();
} else {
remove_autostart_everywhere();
}
Ok(())
}
#[cfg(windows)]
fn best_effort_delete(hive: winreg::HKEY, subkey: &str) {
use winreg::RegKey;
use winreg::enums::KEY_WRITE;
if let Ok(key) = RegKey::predef(hive).open_subkey_with_flags(subkey, KEY_WRITE) {
let _ = key.delete_value(VALUE_NAME);
}
}
#[cfg(windows)]
fn purge_legacy_autostart() {
use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
best_effort_delete(HKEY_LOCAL_MACHINE, RUN_KEY);
best_effort_delete(HKEY_CURRENT_USER, STARTUP_APPROVED_KEY);
best_effort_delete(HKEY_LOCAL_MACHINE, STARTUP_APPROVED_KEY);
}
#[cfg(windows)]
fn remove_autostart_everywhere() {
use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
best_effort_delete(HKEY_CURRENT_USER, RUN_KEY);
best_effort_delete(HKEY_LOCAL_MACHINE, RUN_KEY);
best_effort_delete(HKEY_CURRENT_USER, STARTUP_APPROVED_KEY);
best_effort_delete(HKEY_LOCAL_MACHINE, STARTUP_APPROVED_KEY);
}
#[cfg(target_os = "linux")]
fn xdg_autostart_dir(base: &std::path::Path) -> std::path::PathBuf {
base.join(".config").join("autostart")
}
#[cfg(target_os = "linux")]
fn desktop_entry(exe: &std::path::Path, port: u16) -> String {
format!(
"[Desktop Entry]\n\
Type=Application\n\
Version=1.0\n\
Name=llmtrim\n\
Comment=llmtrim startup script\n\
Exec={} serve --port {} --supervised\n\
StartupNotify=false\n\
Terminal=false",
exe.display(),
port
)
}
#[cfg(target_os = "linux")]
fn configure_in(enable: bool, port: u16, base: &std::path::Path) -> Result<()> {
let dir = xdg_autostart_dir(base);
let file = dir.join("llmtrim.desktop");
if enable {
let exe = std::env::current_exe().context("could not find the llmtrim executable")?;
std::fs::create_dir_all(&dir)
.with_context(|| format!("failed to create {}", dir.display()))?;
std::fs::write(&file, desktop_entry(&exe, port))
.with_context(|| format!("failed to write {}", file.display()))?;
} else if file.exists() {
std::fs::remove_file(&file)
.with_context(|| format!("failed to remove {}", file.display()))?;
}
Ok(())
}
#[cfg(target_os = "linux")]
fn configure_auto_launch(enable: bool, port: u16) -> Result<()> {
let home = std::env::var("HOME")
.context("HOME is not set — cannot determine XDG autostart directory")?;
configure_in(enable, port, std::path::Path::new(&home))
}
#[cfg(all(not(windows), not(target_os = "linux")))]
fn configure_auto_launch(enable: bool, port: u16) -> Result<()> {
use auto_launch::AutoLaunchBuilder;
let exe = std::env::current_exe().context("could not find the llmtrim executable")?;
let path = exe.to_string_lossy();
let port_arg = port.to_string();
let auto = AutoLaunchBuilder::new()
.set_app_name("llmtrim")
.set_app_path(path.as_ref())
.set_args(&["serve", "--port", port_arg.as_str(), "--supervised"])
.build()
.map_err(|e| anyhow::anyhow!("failed to configure autostart: {e}"))?;
if enable {
auto.enable()
.map_err(|e| anyhow::anyhow!("failed to enable autostart: {e}"))?;
} else {
auto.disable()
.map_err(|e| anyhow::anyhow!("failed to disable autostart: {e}"))?;
}
Ok(())
}
pub fn configure_tray(enable: bool) -> Result<()> {
let exe = crate::tray::tray_binary();
if enable && exe.is_none() {
anyhow::bail!(
"the llmtrim tray app isn't installed — install the desktop bundle before \
enabling tray autostart"
);
}
#[cfg(windows)]
{
configure_tray_windows(enable, exe.as_deref())
}
#[cfg(target_os = "linux")]
{
let home = std::env::var("HOME")
.context("HOME is not set — cannot determine XDG autostart directory")?;
configure_tray_in(enable, exe.as_deref(), std::path::Path::new(&home))
}
#[cfg(all(not(windows), not(target_os = "linux")))]
{
configure_tray_auto_launch(enable, exe.as_deref())
}
}
pub fn is_tray_enabled() -> bool {
#[cfg(windows)]
{
use winreg::RegKey;
use winreg::enums::{HKEY_CURRENT_USER, KEY_READ};
RegKey::predef(HKEY_CURRENT_USER)
.open_subkey_with_flags(RUN_KEY, KEY_READ)
.and_then(|key| key.get_value::<String, _>(VALUE_NAME_TRAY))
.is_ok()
}
#[cfg(target_os = "linux")]
{
std::env::var("HOME")
.map(|home| is_tray_enabled_in(std::path::Path::new(&home)))
.unwrap_or(false)
}
#[cfg(all(not(windows), not(target_os = "linux")))]
{
let Some(exe) = crate::tray::tray_binary() else {
return false;
};
let path = exe.to_string_lossy();
auto_launch::AutoLaunchBuilder::new()
.set_app_name("llmtrim-tray")
.set_app_path(path.as_ref())
.build()
.and_then(|auto| auto.is_enabled())
.unwrap_or(false)
}
}
#[cfg(windows)]
const VALUE_NAME_TRAY: &str = "llmtrim-tray";
#[cfg(windows)]
fn configure_tray_windows(enable: bool, exe: Option<&std::path::Path>) -> Result<()> {
use winreg::RegKey;
use winreg::enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
let (key, _) = RegKey::predef(HKEY_CURRENT_USER)
.create_subkey_with_flags(RUN_KEY, KEY_READ | KEY_WRITE)
.with_context(|| format!("failed to open HKCU\\{RUN_KEY}"))?;
if enable {
let exe = exe.context("the llmtrim tray app isn't installed")?;
let cmd = format!("\"{}\"", exe.display());
key.set_value(VALUE_NAME_TRAY, &cmd)
.context("failed to set llmtrim-tray autostart in the registry Run key")?;
} else {
let _ = key.delete_value(VALUE_NAME_TRAY);
}
Ok(())
}
#[cfg(target_os = "linux")]
fn tray_desktop_entry(exe: &std::path::Path) -> String {
let path = exe.to_string_lossy().replace('%', "%%");
format!(
"[Desktop Entry]\n\
Type=Application\n\
Version=1.0\n\
Name=llmtrim tray\n\
Comment=llmtrim compression savings tray\n\
Exec=\"{path}\"\n\
StartupNotify=false\n\
Terminal=false"
)
}
#[cfg(target_os = "linux")]
fn configure_tray_in(
enable: bool,
exe: Option<&std::path::Path>,
base: &std::path::Path,
) -> Result<()> {
let dir = xdg_autostart_dir(base);
let file = dir.join("llmtrim-tray.desktop");
if enable {
let exe = exe.context("the llmtrim tray app isn't installed")?;
std::fs::create_dir_all(&dir)
.with_context(|| format!("failed to create {}", dir.display()))?;
std::fs::write(&file, tray_desktop_entry(exe))
.with_context(|| format!("failed to write {}", file.display()))?;
} else if file.exists() {
std::fs::remove_file(&file)
.with_context(|| format!("failed to remove {}", file.display()))?;
}
Ok(())
}
#[cfg(target_os = "linux")]
fn is_tray_enabled_in(base: &std::path::Path) -> bool {
xdg_autostart_dir(base)
.join("llmtrim-tray.desktop")
.exists()
}
#[cfg(all(not(windows), not(target_os = "linux")))]
fn configure_tray_auto_launch(enable: bool, exe: Option<&std::path::Path>) -> Result<()> {
use auto_launch::AutoLaunchBuilder;
let path = exe
.map(|p| p.to_string_lossy().into_owned())
.unwrap_or_default();
let auto = AutoLaunchBuilder::new()
.set_app_name("llmtrim-tray")
.set_app_path(&path)
.build()
.map_err(|e| anyhow::anyhow!("failed to configure tray autostart: {e}"))?;
if enable {
auto.enable()
.map_err(|e| anyhow::anyhow!("failed to enable tray autostart: {e}"))?;
} else if auto.is_enabled().unwrap_or(false) {
auto.disable()
.map_err(|e| anyhow::anyhow!("failed to disable tray autostart: {e}"))?;
}
Ok(())
}
#[cfg(all(test, target_os = "linux"))]
mod tests {
use super::*;
use std::path::PathBuf;
struct TempDir(PathBuf);
impl TempDir {
fn new(suffix: &str) -> Self {
let dir = std::env::temp_dir().join(format!(
"llmtrim-autostart-test-{}-{}",
std::process::id(),
suffix
));
std::fs::create_dir_all(&dir).expect("create temp dir");
Self(dir)
}
fn path(&self) -> &std::path::Path {
&self.0
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
#[cfg(target_os = "linux")]
#[test]
fn configure_in_install_creates_desktop_file_with_expected_content() {
let dir = TempDir::new("install");
let base = dir.path();
configure_in(true, 8787, base).expect("configure_in enable");
let file = base
.join(".config")
.join("autostart")
.join("llmtrim.desktop");
assert!(file.exists(), ".desktop file not created");
let content = std::fs::read_to_string(&file).expect("read .desktop");
assert!(
content.contains("[Desktop Entry]"),
"missing [Desktop Entry]"
);
assert!(content.contains("Name=llmtrim"), "missing Name=");
assert!(
content.contains("serve --port 8787 --supervised"),
"missing serve invocation with port"
);
assert!(content.contains("Terminal=false"), "missing Terminal=false");
}
#[cfg(target_os = "linux")]
#[test]
fn configure_in_install_twice_is_idempotent() {
let dir = TempDir::new("idempotent");
let base = dir.path();
configure_in(true, 8787, base).expect("first enable");
configure_in(true, 8787, base).expect("second enable");
let autostart_dir = base.join(".config").join("autostart");
let count = std::fs::read_dir(&autostart_dir)
.expect("read autostart dir")
.filter_map(|e| e.ok())
.count();
assert_eq!(count, 1, "expected exactly 1 .desktop file, found {count}");
}
#[cfg(target_os = "linux")]
#[test]
fn configure_in_remove_deletes_desktop_file() {
let dir = TempDir::new("remove");
let base = dir.path();
configure_in(true, 8787, base).expect("enable");
let file = base
.join(".config")
.join("autostart")
.join("llmtrim.desktop");
assert!(file.exists(), "file should exist before disable");
configure_in(false, 8787, base).expect("disable");
assert!(!file.exists(), ".desktop file still present after disable");
}
#[cfg(target_os = "linux")]
#[test]
fn is_enabled_in_tracks_configure_in() {
let dir = TempDir::new("probe");
let base = dir.path();
assert!(!is_enabled_in(base), "fresh home → not enabled");
configure_in(true, 8787, base).expect("enable");
assert!(is_enabled_in(base), "enabled after configure");
configure_in(false, 8787, base).expect("disable");
assert!(!is_enabled_in(base), "disabled after remove");
}
#[cfg(target_os = "linux")]
#[test]
fn configure_tray_in_writes_and_removes_distinct_desktop_file() {
let dir = TempDir::new("tray");
let base = dir.path();
let exe = PathBuf::from("/usr/local/bin/llmtrim-tray");
configure_tray_in(true, Some(&exe), base).expect("enable tray");
let file = base
.join(".config")
.join("autostart")
.join("llmtrim-tray.desktop");
assert!(file.exists(), "tray .desktop not created");
let content = std::fs::read_to_string(&file).expect("read tray .desktop");
assert!(content.contains("Name=llmtrim tray"), "missing tray Name=");
assert!(
content.contains("Exec=\"/usr/local/bin/llmtrim-tray\""),
"missing tray Exec="
);
assert!(
!base
.join(".config")
.join("autostart")
.join("llmtrim.desktop")
.exists(),
"tray enable should not create the daemon entry"
);
configure_tray_in(false, None, base).expect("disable tray");
assert!(!file.exists(), "tray .desktop still present after disable");
}
#[cfg(target_os = "linux")]
#[test]
fn is_tray_enabled_in_tracks_configure_tray_in() {
let dir = TempDir::new("tray-probe");
let base = dir.path();
let exe = PathBuf::from("/usr/local/bin/llmtrim-tray");
assert!(!is_tray_enabled_in(base), "fresh home → tray not enabled");
configure_tray_in(true, Some(&exe), base).expect("enable tray");
assert!(is_tray_enabled_in(base), "tray enabled after configure");
configure_tray_in(false, None, base).expect("disable tray");
assert!(!is_tray_enabled_in(base), "tray disabled after remove");
}
#[cfg(target_os = "linux")]
#[test]
fn configure_in_remove_when_absent_is_ok_noop() {
let dir = TempDir::new("remove-absent");
let base = dir.path();
configure_in(false, 8787, base).expect("disable when absent must be Ok");
let file = base
.join(".config")
.join("autostart")
.join("llmtrim.desktop");
assert!(!file.exists(), ".desktop file should not exist");
}
#[cfg(target_os = "linux")]
#[test]
fn configure_tray_in_remove_when_absent_is_ok() {
let dir = TempDir::new("tray-remove-absent");
let base = dir.path();
configure_tray_in(false, None, base).expect("disable tray when absent must be Ok");
assert!(
!is_tray_enabled_in(base),
"no tray .desktop after no-op disable"
);
}
#[cfg(target_os = "linux")]
#[test]
fn tray_desktop_entry_quotes_and_escapes_exec_path() {
let entry = tray_desktop_entry(std::path::Path::new("/opt/my apps/llmtrim-tray %v"));
assert!(
entry.contains("Exec=\"/opt/my apps/llmtrim-tray %%v\""),
"Exec line not quoted/escaped: {entry}"
);
}
}