Add managed desktop daemon runtime support
This commit is contained in:
Generated
+37
-2
@@ -2632,6 +2632,9 @@ name = "paseo"
|
||||
version = "0.1.18"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs",
|
||||
"futures-util",
|
||||
"http",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2643,6 +2646,8 @@ dependencies = [
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-updater",
|
||||
"tauri-plugin-websocket",
|
||||
"tokio",
|
||||
"tokio-tungstenite 0.24.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4342,7 +4347,7 @@ dependencies = [
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.28.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4604,6 +4609,18 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tungstenite 0.24.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.28.0"
|
||||
@@ -4616,7 +4633,7 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tungstenite",
|
||||
"tungstenite 0.28.0",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
@@ -4833,6 +4850,24 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"thiserror 1.0.69",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.28.0"
|
||||
|
||||
@@ -23,6 +23,9 @@ tauri-build = { version = "2.5.3", features = [] }
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22"
|
||||
dirs = "6"
|
||||
futures-util = "0.3"
|
||||
http = "1"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
log = "0.4"
|
||||
@@ -33,7 +36,8 @@ tauri-plugin-notification = "2"
|
||||
tauri-plugin-opener = "2"
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-websocket = "2"
|
||||
|
||||
tokio = { version = "1", features = ["net", "sync"] }
|
||||
tokio-tungstenite = "0.24"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -33,6 +33,9 @@
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"createUpdaterArtifacts": true,
|
||||
"resources": [
|
||||
"resources/**/*"
|
||||
],
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
|
||||
Reference in New Issue
Block a user