diff --git a/electron/main.js b/electron/main.js index 4441288f..57d1e090 100644 --- a/electron/main.js +++ b/electron/main.js @@ -25,17 +25,8 @@ process.on('unhandledRejection', (reason) => { app.on('child-process-gone', (_event, details) => { console.error('[diag][main:child-process-gone]', JSON.stringify(details)); }); -// Platform-split auto-updater: electron-updater on Mac (full-featured: allowPrerelease, allowDowngrade, progress), Electron's built-in autoUpdater on Windows (required for Squirrel.Windows target; electron-updater dropped Squirrel support). let autoUpdater; -let isSquirrelUpdater = false; -try { - if (process.platform === 'win32') { - autoUpdater = require('electron').autoUpdater; - isSquirrelUpdater = true; - } else { - autoUpdater = require('electron-updater').autoUpdater; - } -} catch (_) {} +try { autoUpdater = require('electron-updater').autoUpdater; } catch (_) {} const path = require('path'); const { spawn, execFileSync } = require('child_process'); const os = require('os'); @@ -45,8 +36,8 @@ const http = require('http'); const affiliateTracking = require('./affiliateTracking'); const workflowsLifecycle = require('./workflowsLifecycle'); -// Defender warmup helper. Touches bundled exes so Windows scans them now instead of on first launch. -function _squirrelPrewarmTouch() { +// Defender warmup: NSIS runs us with --prewarm right after install so Windows scans the bundled binaries while the user is already watching the installer instead of staring at a slow first launch. +if (process.argv.includes('--prewarm') && process.platform === 'win32') { const touchExe = (rel) => { const full = path.join(process.resourcesPath, rel); try { @@ -58,27 +49,9 @@ function _squirrelPrewarmTouch() { touchExe(path.join('python-env', 'python.exe')); touchExe(path.join('node', 'x64', 'node.exe')); touchExe(path.join('node', 'arm64', 'node.exe')); -} - -// Legacy NSIS prewarm path (kept for any user still on 1.1.40 stable NSIS install). -if (process.argv.includes('--prewarm') && process.platform === 'win32') { - _squirrelPrewarmTouch(); process.exit(0); } -// Squirrel.Windows lifecycle: app is invoked with --squirrel-* args during install/update/uninstall. Handle and exit fast; --squirrel-firstrun is the only one we let fall through to normal boot. -(function handleSquirrelEvents() { - if (process.platform !== 'win32' || process.argv.length < 2) return; - const sq = process.argv[1]; - if (sq === '--squirrel-install' || sq === '--squirrel-updated') { - _squirrelPrewarmTouch(); - process.exit(0); - } - if (sq === '--squirrel-uninstall' || sq === '--squirrel-obsolete') { - process.exit(0); - } -})(); - // Prevent duplicate instances. Without this, double-clicking the app icon // (or macOS auto-launch + manual launch overlapping) spawns two independent // processes — each with its own backend on a different port — resulting in @@ -982,25 +955,15 @@ function sendToRenderer(channel, ...args) { function setupAutoUpdater() { if (!autoUpdater) return; - if (isSquirrelUpdater) { - // Squirrel.Windows: setFeedURL to GH Releases /latest/download/ so Squirrel fetches RELEASES from there. allowPrerelease/allowDowngrade not supported by built-in autoUpdater; experimental users on Windows get only the latest stable until we wire a separate Squirrel feed for prereleases. - try { - autoUpdater.setFeedURL({ url: 'https://github.com/openswarm-ai/openswarm/releases/latest/download/' }); - } catch (err) { - console.warn('[updater] Squirrel setFeedURL failed:', err && err.message); - return; - } - } else { - // Silent background updates: download on detect, install on next quit. - // The OS gates the install on main-process exit (can't replace a - // running .app / locked .exe), so an active session is never disrupted. - autoUpdater.autoDownload = true; - autoUpdater.autoInstallOnAppQuit = true; - // Renderer pushes the user's experimental-updates setting via IPC right after settings load. - autoUpdater.allowPrerelease = false; - // Lets us un-ship a bad release: re-flip GH 'latest' to an older one and users hop back to it. - autoUpdater.allowDowngrade = true; - } + // Silent background updates: download on detect, install on next quit. + // The OS gates the install on main-process exit (can't replace a + // running .app / locked .exe), so an active session is never disrupted. + autoUpdater.autoDownload = true; + autoUpdater.autoInstallOnAppQuit = true; + // Renderer pushes the user's experimental-updates setting via IPC right after settings load. + autoUpdater.allowPrerelease = false; + // Lets us un-ship a bad release: re-flip GH 'latest' to an older one and users hop back to it. + autoUpdater.allowDowngrade = true; autoUpdater.on('update-available', (info) => { console.log(`Update available: ${info.version}`); @@ -1670,8 +1633,6 @@ ipcMain.handle('check-for-updates', async () => { ipcMain.handle('download-update', async () => { if (!autoUpdater) return { success: false, error: 'Updater not available' }; - // Squirrel built-in autoUpdater auto-downloads on detect; no manual trigger needed. - if (isSquirrelUpdater) return { success: true }; try { await autoUpdater.downloadUpdate(); return { success: true }; @@ -1682,8 +1643,6 @@ ipcMain.handle('download-update', async () => { ipcMain.handle('set-allow-prerelease', async (_e, value) => { if (!autoUpdater) return { success: false, error: 'Updater not available' }; - // Squirrel built-in autoUpdater has no allowPrerelease; experimental channel on Windows is a TODO once we wire a separate Squirrel feed URL. - if (isSquirrelUpdater) return { success: false, error: 'Experimental channel not yet supported on Windows Squirrel target' }; const next = Boolean(value); if (autoUpdater.allowPrerelease === next) return { success: true, changed: false }; autoUpdater.allowPrerelease = next; @@ -1703,12 +1662,7 @@ ipcMain.handle('install-update', async () => { const vetoed = await workflowsLifecycle.maybeVetoInstall(); if (vetoed) return { vetoed: true }; } catch (_) {} - // Built-in autoUpdater on Windows takes no args; electron-updater on Mac takes (isSilent, isForceRunAfter). - if (isSquirrelUpdater) { - autoUpdater.quitAndInstall(); - } else { - autoUpdater.quitAndInstall(false, true); - } + autoUpdater.quitAndInstall(false, true); }); ipcMain.handle('capture-page', async (event, rect) => { diff --git a/electron/package.json b/electron/package.json index f035514a..57096c6b 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "openswarm", - "version": "1.1.59", + "version": "1.1.60", "description": "OpenSwarm — AI Agent Orchestrator", "author": "openswarm-ai", "main": "main.js", @@ -68,7 +68,7 @@ "icon": "build/icon.ico", "target": [ { - "target": "squirrel", + "target": "nsis", "arch": ["x64"] } ], @@ -77,8 +77,16 @@ "signingHashAlgorithms": ["sha256"], "signDlls": false }, - "squirrelWindows": { - "iconUrl": "https://raw.githubusercontent.com/openswarm-ai/openswarm/main/electron/build/icon.ico" + "nsis": { + "oneClick": true, + "perMachine": false, + "allowToChangeInstallationDirectory": false, + "createDesktopShortcut": true, + "createStartMenuShortcut": true, + "shortcutName": "OpenSwarm", + "deleteAppDataOnUninstall": false, + "artifactName": "OpenSwarm-Setup-${arch}.${ext}", + "include": "build/installer-recovery.nsh" }, "extraResources": [ {