Compare commits

...

5 Commits

Author SHA1 Message Date
8a333d6770 potentially fixed mac support 2026-11-04 21:19:30 -08:00
9c4d250640 updated 2026-11-01 14:16:41 -08:00
03ed2f0e53 fixed lock file 2026-11-01 14:16:04 -08:00
47c97fdf21 refactor: move component archives to GitLab Package Registry and keep release assets minimal 2026-10-30 23:00:25 -07:00
e7204589a4 v1.1.54 2026-10-30 22:58:34 -07:00
12 changed files with 113 additions and 31 deletions

View File

@@ -17,13 +17,14 @@ class CryptoUtils {
let key; let key;
try { try {
const isMac = os.platform() === 'darwin';
const machineId = [ const machineId = [
os.hostname(), isMac ? '' : os.hostname(),
os.platform(), os.platform(),
os.arch(), os.arch(),
os.cpus()[0] ? os.cpus()[0].model : '', os.cpus()[0] ? os.cpus()[0].model : '',
process.env.COMPUTERNAME || '', process.env.COMPUTERNAME || '',
process.env.USERNAME || '' process.env.USERNAME || (isMac ? os.userInfo().username : '')
].join('|'); ].join('|');
const hash = crypto.createHash('sha256'); const hash = crypto.createHash('sha256');

View File

@@ -459,5 +459,8 @@ process.on('SIGINT', () => {
process.on('SIGTERM', () => { process.on('SIGTERM', () => {
process.exit(0); process.exit(0);
}); });
process.on('SIGHUP', () => {
process.exit(0);
});
start(); start();

7
package-lock.json generated
View File

@@ -1,19 +1,18 @@
{ {
"name": "vyla-home", "name": "vyla-home",
"version": "1.1.49", "version": "1.1.56",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "vyla-home", "name": "vyla-home",
"version": "1.1.49", "version": "1.1.56",
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.12.1", "@tauri-apps/api": "2.12.1",
"@tauri-apps/plugin-opener": "2.7.0", "@tauri-apps/plugin-opener": "2.7.0",
"@tauri-apps/plugin-process": "2.4.0", "@tauri-apps/plugin-process": "2.4.0",
"@tauri-apps/plugin-updater": "^2",
"@tauri-apps/plugin-shell": "2.5.0", "@tauri-apps/plugin-shell": "2.5.0",
"@vyla-entertainment/sdk": "^1.20.0", "@vyla-entertainment/sdk": "latest",
"adm-zip": "^0.6.1", "adm-zip": "^0.6.1",
"express": "^4.22.3" "express": "^4.22.3"
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "vyla-home", "name": "vyla-home",
"version": "1.1.55", "version": "1.1.56",
"description": "Vyla Home Desktop App Launcher", "description": "Vyla Home Desktop App Launcher",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@@ -243,6 +243,20 @@ async function main() {
console.log('Uploading update-payload.zip...'); console.log('Uploading update-payload.zip...');
const payloadUrl = await uploadFileToGitLabPackage(payloadPath, 'vyla-home', version, `update-payload-${timestamp}.zip`); const payloadUrl = await uploadFileToGitLabPackage(payloadPath, 'vyla-home', version, `update-payload-${timestamp}.zip`);
const manifestData = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
for (const [name, c] of Object.entries(manifestData.components || {})) {
console.log(`Uploading ${c.file}...`);
c.url = await uploadFileToGitLabPackage(path.join(__dirname, 'release-assets', c.file), 'vyla-home', version, `${name}-${timestamp}.zip`);
}
const signingKeyPem = process.env.VYLA_UPDATE_SIGNING_KEY;
if (!signingKeyPem) {
throw new Error('VYLA_UPDATE_SIGNING_KEY is required to sign the update manifest.');
}
delete manifestData.signature;
manifestData.signature = crypto.sign(null, Buffer.from(UpdateClient.canonicalManifest(manifestData)), signingKeyPem.replace(/\\n/g, '\n')).toString('base64');
fs.writeFileSync(manifestPath, JSON.stringify(manifestData, null, 2), 'utf8');
console.log('Uploading manifest.json...'); console.log('Uploading manifest.json...');
const manifestUrl = await uploadFileToGitLabPackage(manifestPath, 'vyla-home', version, `manifest-${timestamp}.json`); const manifestUrl = await uploadFileToGitLabPackage(manifestPath, 'vyla-home', version, `manifest-${timestamp}.json`);
@@ -251,13 +265,6 @@ async function main() {
{ name: 'manifest.json', url: manifestUrl, link_type: 'other' } { name: 'manifest.json', url: manifestUrl, link_type: 'other' }
]; ];
const manifestData = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
for (const [name, c] of Object.entries(manifestData.components || {})) {
console.log(`Uploading ${c.file}...`);
const componentUrl = await uploadFileToGitLabPackage(path.join(__dirname, 'release-assets', c.file), 'vyla-home', version, `${name}-${timestamp}.zip`);
releaseLinks.push({ name: c.file, url: componentUrl, link_type: 'other' });
}
if (installerPath && installerUploadName) { if (installerPath && installerUploadName) {
console.log(`Uploading installer ${installerUploadName}...`); console.log(`Uploading installer ${installerUploadName}...`);
const ext = path.extname(installerUploadName); const ext = path.extname(installerUploadName);

View File

@@ -54,6 +54,9 @@ class ServiceManager {
const env = { const env = {
...process.env, ...process.env,
...(process.platform === 'darwin'
? { PATH: [process.env.PATH, '/opt/homebrew/bin', '/usr/local/bin', '/usr/bin', '/bin'].filter(Boolean).join(path.delimiter) }
: {}),
PORT: (this.envConfig[name] && this.envConfig[name].PORT) || defaultPorts[name] || process.env.PORT || 3000, PORT: (this.envConfig[name] && this.envConfig[name].PORT) || defaultPorts[name] || process.env.PORT || 3000,
TOKEN_SECRET: tokenSecret, TOKEN_SECRET: tokenSecret,
...(this.envConfig[name] || {}), ...(this.envConfig[name] || {}),

3
src-tauri/Cargo.lock generated
View File

@@ -4073,8 +4073,9 @@ dependencies = [
[[package]] [[package]]
name = "vyla-home" name = "vyla-home"
version = "1.1.55" version = "1.1.56"
dependencies = [ dependencies = [
"libc",
"objc2 0.5.2", "objc2 0.5.2",
"serde", "serde",
"serde_json", "serde_json",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "vyla-home" name = "vyla-home"
version = "1.1.55" version = "1.1.56"
description = "Vyla Home Desktop App Launcher" description = "Vyla Home Desktop App Launcher"
edition = "2021" edition = "2021"
@@ -17,6 +17,9 @@ window-vibrancy = "0.8"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.5" objc2 = "0.5"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[profile.release] [profile.release]
opt-level = 3 opt-level = 3
lto = true lto = true

View File

@@ -8,6 +8,8 @@
<true/> <true/>
<key>NSAllowsArbitraryLoads</key> <key>NSAllowsArbitraryLoads</key>
<true/> <true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict> </dict>
</dict> </dict>
</plist> </plist>

View File

@@ -44,6 +44,8 @@ static WINDOW_COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::Atomi
static READY_URL: Mutex<Option<String>> = Mutex::new(None); static READY_URL: Mutex<Option<String>> = Mutex::new(None);
static APP_LOADED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
fn js_log(app: &AppHandle, msg: &str) { fn js_log(app: &AppHandle, msg: &str) {
debug_log(msg); debug_log(msg);
if let Some(w) = app.get_webview_window("main") { if let Some(w) = app.get_webview_window("main") {
@@ -94,6 +96,17 @@ fn kill_child_process(child: &mut Child) {
.creation_flags(CREATE_NO_WINDOW) .creation_flags(CREATE_NO_WINDOW)
.output(); .output();
} }
#[cfg(unix)]
{
let pgid = child.id() as i32;
unsafe {
libc::killpg(pgid, libc::SIGTERM);
}
std::thread::sleep(std::time::Duration::from_millis(400));
unsafe {
libc::killpg(pgid, libc::SIGKILL);
}
}
let _ = child.kill(); let _ = child.kill();
let _ = child.wait(); let _ = child.wait();
} }
@@ -580,7 +593,7 @@ fn open_new_window(app: AppHandle, url: String) -> Result<(), String> {
fn handle_ready(w: &tauri::WebviewWindow, msg: &BackendMessage) { fn handle_ready(w: &tauri::WebviewWindow, msg: &BackendMessage) {
let url_str = match msg.data.get("url").and_then(|u| u.as_str()) { let url_str = match msg.data.get("url").and_then(|u| u.as_str()) {
Some(u) => u, Some(u) => u.to_string(),
None => return, None => return,
}; };
@@ -592,19 +605,36 @@ fn handle_ready(w: &tauri::WebviewWindow, msg: &BackendMessage) {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
use objc2::msg_send; use objc2::msg_send;
use objc2::runtime::AnyObject; use objc2::runtime::{AnyObject, Sel};
debug_log("Attempting with_webview on ready event"); use objc2::sel;
let result = w.with_webview(|webview| unsafe { let _ = w.with_webview(|webview| unsafe {
debug_log("Inside with_webview closure");
let wk_webview = webview.inner() as *mut AnyObject; let wk_webview = webview.inner() as *mut AnyObject;
let config: *mut AnyObject = msg_send![wk_webview, configuration]; let config: *mut AnyObject = msg_send![wk_webview, configuration];
let _: () = msg_send![config, setUpgradeKnownHostsToHTTPS: false]; let selector: Sel = sel!(setUpgradeKnownHostsToHTTPS:);
debug_log("setUpgradeKnownHostsToHTTPS set to false"); let supported: bool = msg_send![config, respondsToSelector: selector];
if supported {
let _: () = msg_send![config, setUpgradeKnownHostsToHTTPS: false];
}
}); });
debug_log(&format!("with_webview result: {:?}", result));
} }
let _ = w.navigate(url_parsed); APP_LOADED.store(false, std::sync::atomic::Ordering::SeqCst);
let _ = w.navigate(url_parsed.clone());
let watcher = w.clone();
std::thread::spawn(move || {
for attempt in 1..=8 {
std::thread::sleep(std::time::Duration::from_secs(4));
if APP_LOADED.load(std::sync::atomic::Ordering::SeqCst) {
return;
}
debug_log(&format!("App page not loaded yet, retry {}", attempt));
let target = serde_json::to_string(&url_str).unwrap_or_default();
let _ = watcher.eval(&format!("window.location.replace({});", target));
let _ = watcher.navigate(url_parsed.clone());
}
debug_log("App page never loaded after retries");
});
} }
fn main() { fn main() {
@@ -663,6 +693,9 @@ fn main() {
.initialization_script("window.__VYLA_WINDOW_LABEL = 'main';") .initialization_script("window.__VYLA_WINDOW_LABEL = 'main';")
.on_page_load(move |wv, payload| { .on_page_load(move |wv, payload| {
if matches!(payload.event(), tauri::webview::PageLoadEvent::Finished) { if matches!(payload.event(), tauri::webview::PageLoadEvent::Finished) {
if payload.url().host_str() == Some("127.0.0.1") {
APP_LOADED.store(true, std::sync::atomic::Ordering::SeqCst);
}
if payload.url().host_str() != Some("127.0.0.1") { if payload.url().host_str() != Some("127.0.0.1") {
let known = READY_URL.lock().ok().and_then(|g| g.clone()); let known = READY_URL.lock().ok().and_then(|g| g.clone());
if let Some(u) = known { if let Some(u) = known {
@@ -726,6 +759,16 @@ fn main() {
} }
}; };
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if let Ok(meta) = std::fs::metadata(&node_bin) {
let mut perms = meta.permissions();
perms.set_mode(0o755);
let _ = std::fs::set_permissions(&node_bin, perms);
}
}
let vyla_files_str = clean_path(&vyla_files); let vyla_files_str = clean_path(&vyla_files);
let mut cmd = Command::new(&node_bin); let mut cmd = Command::new(&node_bin);
@@ -746,6 +789,12 @@ fn main() {
cmd.creation_flags(CREATE_NO_WINDOW); cmd.creation_flags(CREATE_NO_WINDOW);
} }
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
cmd.process_group(0);
}
match cmd.spawn() { match cmd.spawn() {
Ok(mut child) => { Ok(mut child) => {
debug_log(&format!( debug_log(&format!(
@@ -878,6 +927,16 @@ fn main() {
} }
} }
}) })
.run(tauri::generate_context!()) .build(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while building tauri application")
.run(|app, event| {
if let tauri::RunEvent::Exit = event {
let state: State<NodeServices> = app.state();
if let Ok(mut lock) = state.0.lock() {
if let Some(mut child) = lock.take() {
kill_child_process(&mut child);
}
};
}
});
} }

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Vyla Home", "productName": "Vyla Home",
"version": "1.1.55", "version": "1.1.56",
"identifier": "com.vyla.home", "identifier": "com.vyla.home",
"build": { "build": {
"frontendDist": "../ui" "frontendDist": "../ui"

View File

@@ -380,10 +380,14 @@ class UpdateClient {
const components = {}; const components = {};
for (const [name, c] of Object.entries(manifest.components)) { for (const [name, c] of Object.entries(manifest.components)) {
const link = links.find((a) => a.name === c.file); const link = links.find((a) => a.name === c.file);
if (!link) { const direct = typeof c.url === 'string' &&
c.url.startsWith('https://gitlab.com/api/v4/projects/') &&
c.url.includes('/packages/generic/vyla-home/');
const url = direct ? c.url : (link && link.url);
if (!url) {
throw new Error(`Release is missing asset ${c.file}.`); throw new Error(`Release is missing asset ${c.file}.`);
} }
components[name] = { url: link.url, sha256: c.sha256, id: c.id, size: c.size }; components[name] = { url, sha256: c.sha256, id: c.id, size: c.size };
} }
let installer = null; let installer = null;