Add managed desktop daemon runtime support

This commit is contained in:
Mohamed Boudra
2026-03-08 15:40:02 +07:00
parent faa5aaab6f
commit 2d5d0dcacd
35 changed files with 5377 additions and 421 deletions
+163
View File
@@ -1,5 +1,6 @@
use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine as _};
use serde::Serialize;
use serde_json::json;
use std::collections::HashSet;
use std::fs;
use std::path::{Path, PathBuf};
@@ -11,6 +12,17 @@ use tauri::menu::AboutMetadata;
use tauri::{AppHandle, Manager, WebviewWindow};
use tauri_plugin_updater::UpdaterExt;
mod runtime_manager;
pub use runtime_manager::try_run_cli_shim_from_args;
use runtime_manager::{
close_local_daemon_transport, ensure_managed_runtime, install_cli_shim,
managed_daemon_logs, managed_daemon_pairing, managed_daemon_status, managed_runtime_status,
open_local_daemon_transport, restart_managed_daemon,
send_local_daemon_transport_message, start_managed_daemon, stop_managed_daemon,
uninstall_cli_shim, update_managed_daemon_tcp_settings, ManagedTcpSettings,
LocalTransportState,
};
// Store zoom as u64 bits (f64 * 100 as integer for atomic ops)
static ZOOM_LEVEL: AtomicU64 = AtomicU64::new(100);
@@ -64,6 +76,138 @@ struct AttachmentFileResult {
byte_size: u64,
}
#[derive(Debug, Clone)]
enum ManagedHeadlessCommand {
RuntimeStatus,
Bootstrap,
DaemonStatus,
StopDaemon,
InstallCliShim,
UninstallCliShim,
UpdateTcp(ManagedTcpSettings),
}
fn parse_managed_headless_command() -> Result<Option<ManagedHeadlessCommand>, String> {
let args = std::env::args().collect::<Vec<_>>();
let Some(flag_index) = args.iter().position(|value| value == "--managed-headless") else {
return Ok(None);
};
let command = args
.get(flag_index + 1)
.ok_or_else(|| "Missing command after --managed-headless".to_string())?;
let tail = args.iter().skip(flag_index + 2).cloned().collect::<Vec<_>>();
match command.as_str() {
"runtime-status" => Ok(Some(ManagedHeadlessCommand::RuntimeStatus)),
"bootstrap" => Ok(Some(ManagedHeadlessCommand::Bootstrap)),
"daemon-status" => Ok(Some(ManagedHeadlessCommand::DaemonStatus)),
"stop-daemon" => Ok(Some(ManagedHeadlessCommand::StopDaemon)),
"install-cli-shim" => Ok(Some(ManagedHeadlessCommand::InstallCliShim)),
"uninstall-cli-shim" => Ok(Some(ManagedHeadlessCommand::UninstallCliShim)),
"update-tcp" => {
let mut enabled = false;
let mut host = "127.0.0.1".to_string();
let mut port = 7771_u16;
let mut index = 0_usize;
while index < tail.len() {
match tail[index].as_str() {
"--enabled" => {
enabled = tail
.get(index + 1)
.ok_or_else(|| "Missing value after --enabled".to_string())?
.parse::<bool>()
.map_err(|error| format!("Invalid --enabled value: {error}"))?;
index += 2;
}
"--host" => {
host = tail
.get(index + 1)
.ok_or_else(|| "Missing value after --host".to_string())?
.to_string();
index += 2;
}
"--port" => {
port = tail
.get(index + 1)
.ok_or_else(|| "Missing value after --port".to_string())?
.parse::<u16>()
.map_err(|error| format!("Invalid --port value: {error}"))?;
index += 2;
}
other => {
return Err(format!("Unknown --managed-headless update-tcp option: {other}"));
}
}
}
Ok(Some(ManagedHeadlessCommand::UpdateTcp(ManagedTcpSettings {
enabled,
host,
port,
})))
}
other => Err(format!("Unknown --managed-headless command: {other}")),
}
}
fn maybe_run_managed_headless_command(app: &AppHandle) -> Result<bool, String> {
let Some(command) = parse_managed_headless_command()? else {
return Ok(false);
};
if let Some(window) = app.get_webview_window("main") {
let _ = window.hide();
}
let app_handle = app.clone();
tauri::async_runtime::spawn(async move {
let result = async {
let payload = match command {
ManagedHeadlessCommand::RuntimeStatus => {
json!(managed_runtime_status(app_handle.clone()).await?)
}
ManagedHeadlessCommand::Bootstrap => {
ensure_managed_runtime(app_handle.clone()).await?;
json!(start_managed_daemon(app_handle.clone()).await?)
}
ManagedHeadlessCommand::DaemonStatus => {
json!(managed_daemon_status(app_handle.clone()).await?)
}
ManagedHeadlessCommand::StopDaemon => {
json!(stop_managed_daemon(app_handle.clone()).await?)
}
ManagedHeadlessCommand::InstallCliShim => {
json!(install_cli_shim(app_handle.clone()).await?)
}
ManagedHeadlessCommand::UninstallCliShim => {
json!(uninstall_cli_shim(app_handle.clone()).await?)
}
ManagedHeadlessCommand::UpdateTcp(settings) => {
json!(update_managed_daemon_tcp_settings(app_handle.clone(), settings).await?)
}
};
Ok::<serde_json::Value, String>(payload)
}
.await;
match result {
Ok(payload) => {
println!(
"{}",
serde_json::to_string_pretty(&payload)
.unwrap_or_else(|error| format!(r#"{{"error":"{error}"}}"#))
);
app_handle.exit(0);
}
Err(error) => {
eprintln!(
"{}",
serde_json::to_string_pretty(&json!({ "error": error }))
.unwrap_or_else(|_| r#"{"error":"managed headless command failed"}"#.to_string())
);
app_handle.exit(1);
}
}
});
Ok(true)
}
fn resolve_login_shell() -> String {
std::env::var("SHELL")
.ok()
@@ -440,12 +584,27 @@ async fn garbage_collect_attachment_files(
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(LocalTransportState::default())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_websocket::init())
.invoke_handler(tauri::generate_handler![
managed_runtime_status,
ensure_managed_runtime,
managed_daemon_status,
start_managed_daemon,
stop_managed_daemon,
restart_managed_daemon,
managed_daemon_logs,
managed_daemon_pairing,
install_cli_shim,
uninstall_cli_shim,
update_managed_daemon_tcp_settings,
open_local_daemon_transport,
send_local_daemon_transport_message,
close_local_daemon_transport,
get_local_daemon_version,
run_local_daemon_update,
check_app_update,
@@ -457,6 +616,10 @@ pub fn run() {
garbage_collect_attachment_files
])
.setup(|app| {
if maybe_run_managed_headless_command(app.handle())? {
return Ok(());
}
if cfg!(debug_assertions) {
app.handle().plugin(
tauri_plugin_log::Builder::default()
+4
View File
@@ -8,5 +8,9 @@
tauri::embed_plist::embed_info_plist!("../Info.plist");
fn main() {
if let Err(error) = paseo_lib::try_run_cli_shim_from_args() {
eprintln!("{error}");
std::process::exit(1);
}
paseo_lib::run();
}
File diff suppressed because it is too large Load Diff