openclaw-windows-node/src/OpenClaw.Tray/Program.cs
Scott Hanselman 2954a45ed2 Rename repo references to openclaw-windows-hub
- Update Updatum manager in both WinForms and WinUI trays
- Update GitHub link in settings dialog
2026-01-31 22:07:17 -08:00

206 lines
7.0 KiB
C#

using OpenClawTray;
using System;
using System.Diagnostics;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Updatum;
namespace OpenClawTray;
internal static class Program
{
private const string PipeName = "OpenClawTray-DeepLink";
internal static readonly UpdatumManager AppUpdater = new("shanselman", "openclaw-windows-hub")
{
FetchOnlyLatestRelease = true,
InstallUpdateSingleFileExecutableName = "OpenClaw.Tray",
};
[STAThread]
static void Main(string[] args)
{
// Single instance check
using var mutex = new Mutex(true, "OpenClawTray", out bool createdNew);
if (!createdNew)
{
// Forward deep link args to running instance via named pipe
if (args.Length > 0 && args[0].StartsWith("openclaw://", StringComparison.OrdinalIgnoreCase))
{
SendDeepLinkToRunningInstance(args[0]);
}
else
{
MessageBox.Show("OpenClaw Tray is already running.", "OpenClaw Tray",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
return;
}
// Register URI scheme on first run
DeepLinkHandler.RegisterUriScheme();
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Check for updates before launching
var shouldLaunch = CheckForUpdatesAsync().GetAwaiter().GetResult();
if (shouldLaunch)
{
var trayApp = new TrayApplication(args);
Application.Run(trayApp);
}
}
private static async Task<bool> CheckForUpdatesAsync()
{
try
{
Logger.Info("Checking for updates...");
var updateFound = await AppUpdater.CheckForUpdatesAsync();
if (!updateFound)
{
Logger.Info("No updates available");
return true;
}
var release = AppUpdater.LatestRelease!;
var changelog = AppUpdater.GetChangelog(true) ?? "No release notes available.";
Logger.Info($"Update available: {release.TagName}");
using var dialog = new UpdateDialog(release.TagName, changelog);
dialog.ShowDialog();
if (dialog.Result == UpdateDialogResult.Download)
{
var installed = await DownloadAndInstallUpdateAsync();
// If install succeeded, app will restart - don't launch
// If install failed, let user continue to the app
return !installed;
}
// RemindLater or Skip - continue to launch
return true;
}
catch (Exception ex)
{
// Update check failed - don't block the app, just launch
Logger.Warn($"Update check failed: {ex.Message}");
return true;
}
}
private static async Task<bool> DownloadAndInstallUpdateAsync()
{
DownloadProgressDialog? progressDialog = null;
try
{
progressDialog = new DownloadProgressDialog(AppUpdater);
progressDialog.Show();
var downloadedAsset = await AppUpdater.DownloadUpdateAsync();
progressDialog?.Close();
progressDialog = null;
if (downloadedAsset == null)
{
MessageBox.Show("Failed to download the update. Please try again later.",
"Download Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
if (!System.IO.File.Exists(downloadedAsset.FilePath))
{
MessageBox.Show($"Update file was deleted or is inaccessible:\n{downloadedAsset.FilePath}\n\nThis may be caused by antivirus software.",
"Update File Missing", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
var confirmResult = MessageBox.Show(
"The update has been downloaded. OpenClaw Tray will now restart to install the update.\n\nContinue?",
"Install Update",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (confirmResult == DialogResult.Yes)
{
Logger.Info("Installing update and restarting...");
await AppUpdater.InstallUpdateAsync(downloadedAsset);
return true; // App will restart
}
return false; // User cancelled, continue to app
}
catch (UnauthorizedAccessException ex)
{
MessageBox.Show($"Access denied when accessing update file.\n\n1. Antivirus may be blocking the update\n2. Windows SmartScreen may need approval\n\nError: {ex.Message}",
"Access Denied", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
catch (Exception ex)
{
Logger.Error($"Update download/install failed: {ex.Message}");
MessageBox.Show($"Failed to download or install update: {ex.Message}",
"Update Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
finally
{
progressDialog?.Close();
}
}
private static void SendDeepLinkToRunningInstance(string uri)
{
try
{
using var pipe = new NamedPipeClientStream(".", PipeName, PipeDirection.Out);
pipe.Connect(1000); // 1 second timeout
using var writer = new System.IO.StreamWriter(pipe);
writer.WriteLine(uri);
writer.Flush();
Logger.Info($"Forwarded deep link to running instance: {uri}");
}
catch (Exception ex)
{
Logger.Warn($"Failed to forward deep link: {ex.Message}");
MessageBox.Show($"OpenClaw Tray is running but couldn't process the deep link.\n\nPlease try again.",
"Deep Link Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
internal static void StartDeepLinkServer(Action<string> onDeepLinkReceived)
{
Task.Run(async () =>
{
while (true)
{
try
{
using var pipe = new NamedPipeServerStream(PipeName, PipeDirection.In);
await pipe.WaitForConnectionAsync();
using var reader = new System.IO.StreamReader(pipe);
var uri = await reader.ReadLineAsync();
if (!string.IsNullOrEmpty(uri))
{
Logger.Info($"Received deep link via IPC: {uri}");
onDeepLinkReceived(uri);
}
}
catch (Exception ex)
{
Logger.Warn($"Deep link server error: {ex.Message}");
await Task.Delay(1000);
}
}
});
}
}