diff --git a/installer.iss b/installer.iss
index d4dbd9aaf..91722f0e0 100644
--- a/installer.iss
+++ b/installer.iss
@@ -1,5 +1,22 @@
; OpenClaw Companion Inno Setup Script (WinUI version)
-#define MyAppName "OpenClaw Companion"
+; Pass /DDevBuild=1 to produce a side-by-side dev installer.
+#ifdef DevBuild
+ #define MyAppName "OpenClaw Companion (Dev)"
+ #define MyAppId "{{M0LTB0T-TRAY-4PP1-DEV}"
+ #define MyInstallDir "OpenClawTray-Dev"
+ #define MyMutex "OpenClawTray-Dev"
+ #define MyProtocol "openclaw-dev"
+ #define MyOutputSuffix "-Dev"
+ #define MyAutoStartName "OpenClawTray-Dev"
+#else
+ #define MyAppName "OpenClaw Companion"
+ #define MyAppId "{{M0LTB0T-TRAY-4PP1-D3N7}"
+ #define MyInstallDir "OpenClawTray"
+ #define MyMutex "OpenClawTray"
+ #define MyProtocol "openclaw"
+ #define MyOutputSuffix ""
+ #define MyAutoStartName "OpenClawTray"
+#endif
#define MyAppPublisher "Scott Hanselman"
#define MyAppURL "https://github.com/openclaw/openclaw-windows-node"
#define MyAppExeName "OpenClaw.Tray.WinUI.exe"
@@ -20,17 +37,17 @@
[Setup]
; Inno requires "{{" to emit a literal opening brace in AppId.
; Do not add a second closing brace here; that creates a malformed uninstall registry key.
-AppId={{M0LTB0T-TRAY-4PP1-D3N7}
+AppId={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL=https://github.com/openclaw/openclaw-windows-node/issues
AppUpdatesURL=https://github.com/openclaw/openclaw-windows-node/releases
-DefaultDirName={localappdata}\OpenClawTray
+DefaultDirName={localappdata}\{#MyInstallDir}
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
-OutputBaseFilename=OpenClawCompanion-Setup-{#MyAppArch}
+OutputBaseFilename=OpenClawCompanion{#MyOutputSuffix}-Setup-{#MyAppArch}
Compression={#MyCompression}
SolidCompression={#MySolidCompression}
WizardStyle=modern
@@ -41,7 +58,7 @@ UninstallDisplayIcon={app}\{#MyAppExeName}
; Mutex name matches App.xaml.cs (`new Mutex(true, "OpenClawTray", …)`).
; Tray and Inno run in the same user session, so the bare name resolves
; against Local\OpenClawTray — no Global\ prefix needed.
-AppMutex=OpenClawTray
+AppMutex={#MyMutex}
#if MyAppArch == "arm64"
ArchitecturesInstallIn64BitMode=arm64
ArchitecturesAllowed=arm64
@@ -86,17 +103,17 @@ Source: "{#vcRedist}"; DestDir: "{tmp}"; DestName: "vc_redist.exe"; Flags: delet
#endif
[Registry]
-Root: HKCU; Subkey: "Software\Classes\openclaw"; ValueType: string; ValueName: ""; ValueData: "URL:OpenClaw Protocol"; Flags: uninsdeletekey
-Root: HKCU; Subkey: "Software\Classes\openclaw"; ValueType: string; ValueName: "URL Protocol"; ValueData: ""
-Root: HKCU; Subkey: "Software\Classes\openclaw\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"",0"
-Root: HKCU; Subkey: "Software\Classes\openclaw\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
+Root: HKCU; Subkey: "Software\Classes\{#MyProtocol}"; ValueType: string; ValueName: ""; ValueData: "URL:OpenClaw Protocol"; Flags: uninsdeletekey
+Root: HKCU; Subkey: "Software\Classes\{#MyProtocol}"; ValueType: string; ValueName: "URL Protocol"; ValueData: ""
+Root: HKCU; Subkey: "Software\Classes\{#MyProtocol}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"",0"
+Root: HKCU; Subkey: "Software\Classes\{#MyProtocol}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
-Name: "{group}\OpenClaw Gateway Setup"; Filename: "{app}\{#MyAppExeName}"; Parameters: "openclaw://setup"; IconFilename: "{app}\{#MyAppExeName}"
-Name: "{group}\OpenClaw Companion Settings"; Filename: "{app}\{#MyAppExeName}"; Parameters: "openclaw://commandcenter"; IconFilename: "{app}\{#MyAppExeName}"
-Name: "{group}\OpenClaw Chat"; Filename: "{app}\{#MyAppExeName}"; Parameters: "openclaw://chat"; IconFilename: "{app}\{#MyAppExeName}"
-Name: "{group}\Check for Updates"; Filename: "{app}\{#MyAppExeName}"; Parameters: "openclaw://check-updates"; IconFilename: "{app}\{#MyAppExeName}"
+Name: "{group}\OpenClaw Gateway Setup"; Filename: "{app}\{#MyAppExeName}"; Parameters: "{#MyProtocol}://setup"; IconFilename: "{app}\{#MyAppExeName}"
+Name: "{group}\OpenClaw Companion Settings"; Filename: "{app}\{#MyAppExeName}"; Parameters: "{#MyProtocol}://commandcenter"; IconFilename: "{app}\{#MyAppExeName}"
+Name: "{group}\OpenClaw Chat"; Filename: "{app}\{#MyAppExeName}"; Parameters: "{#MyProtocol}://chat"; IconFilename: "{app}\{#MyAppExeName}"
+Name: "{group}\Check for Updates"; Filename: "{app}\{#MyAppExeName}"; Parameters: "{#MyProtocol}://check-updates"; IconFilename: "{app}\{#MyAppExeName}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: startupicon
diff --git a/src/OpenClaw.Tray.WinUI/App.xaml.cs b/src/OpenClaw.Tray.WinUI/App.xaml.cs
index af833fb82..d3c3d7db9 100644
--- a/src/OpenClaw.Tray.WinUI/App.xaml.cs
+++ b/src/OpenClaw.Tray.WinUI/App.xaml.cs
@@ -235,7 +235,7 @@ public IntPtr GetHubWindowHandle()
private static readonly string DataPath = DataDirOverride
?? Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
- "OpenClawTray");
+ AppIdentity.IsDev ? "OpenClawTray-Dev" : "OpenClawTray");
private static readonly string DeepLinkPipeName =
DeepLinkSecurityPolicy.BuildCurrentUserScopedPipeName(DataPath);
// Operator/node identity store. In normal installs this is %APPDATA%\OpenClawTray.
@@ -245,7 +245,7 @@ public IntPtr GetHubWindowHandle()
?? Path.Combine(
Environment.GetEnvironmentVariable("OPENCLAW_TRAY_APPDATA_DIR")
?? Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
- "OpenClawTray");
+ AppIdentity.IsDev ? "OpenClawTray-Dev" : "OpenClawTray");
private readonly AppCrashLogger _crashLogger = new(Path.Combine(DataPath, "crash.log"));
private static readonly AppRunMarker s_runMarker = new(Path.Combine(DataPath, "run.marker"));
@@ -332,6 +332,14 @@ private void OnUnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExce
e.Handled = true; // Try to prevent crash
}
+ ///
+ /// Returns true if looks like a deep link URI for this build variant.
+ /// Dev builds accept both openclaw-dev:// and openclaw:// for compatibility.
+ ///
+ private static bool IsDeepLinkArg(string arg) =>
+ arg.StartsWith($"{AppIdentity.ProtocolScheme}://", StringComparison.OrdinalIgnoreCase)
+ || (AppIdentity.IsDev && arg.StartsWith("openclaw://", StringComparison.OrdinalIgnoreCase));
+
private void OnDomainUnhandledException(object sender, System.UnhandledExceptionEventArgs e)
{
_crashLogger.Log("DomainUnhandledException", e.ExceptionObject as Exception);
@@ -429,7 +437,7 @@ private async Task OnLaunchedAsync(LaunchActivatedEventArgs args)
// (round 2, Scott #5). The suffixed test-isolation variant is
// intentionally not covered by AppMutex — production installs only
// ever use the unsuffixed name.
- var mutexName = "OpenClawTray";
+ var mutexName = AppIdentity.MutexBaseName;
if (DataDirOverride is not null)
{
var hash = System.Security.Cryptography.SHA256.HashData(
@@ -456,10 +464,10 @@ private async Task OnLaunchedAsync(LaunchActivatedEventArgs args)
{
// Forward deep link args to running instance (command-line or protocol activation)
var deepLink = protocolUri
- ?? (_startupArgs.Length > 1 && _startupArgs[1].StartsWith("openclaw://", StringComparison.OrdinalIgnoreCase)
+ ?? (_startupArgs.Length > 1 && IsDeepLinkArg(_startupArgs[1])
? _startupArgs[1] : null)
?? (string.Equals(_postSetupLaunch, "chat", StringComparison.OrdinalIgnoreCase)
- ? "openclaw://chat" : null);
+ ? $"{AppIdentity.ProtocolScheme}://chat" : null);
if (deepLink != null)
{
SendDeepLinkToRunningInstance(deepLink);
@@ -720,7 +728,7 @@ _dispatcherQueue is null
// Process startup deep link (command-line or MSIX protocol activation)
var startupDeepLink = _pendingProtocolUri
- ?? (_startupArgs.Length > 1 && _startupArgs[1].StartsWith("openclaw://", StringComparison.OrdinalIgnoreCase)
+ ?? (_startupArgs.Length > 1 && IsDeepLinkArg(_startupArgs[1])
? _startupArgs[1] : null);
if (!setupShownDuringStartup && startupDeepLink != null)
{
@@ -728,7 +736,7 @@ _dispatcherQueue is null
}
else if (!setupShownDuringStartup && string.Equals(_postSetupLaunch, "chat", StringComparison.OrdinalIgnoreCase))
{
- await HandleDeepLinkAsync("openclaw://chat");
+ await HandleDeepLinkAsync($"{AppIdentity.ProtocolScheme}://chat");
}
Logger.Info("Application started (WinUI 3)");
diff --git a/src/OpenClaw.Tray.WinUI/AppIdentity.cs b/src/OpenClaw.Tray.WinUI/AppIdentity.cs
new file mode 100644
index 000000000..b7a300191
--- /dev/null
+++ b/src/OpenClaw.Tray.WinUI/AppIdentity.cs
@@ -0,0 +1,52 @@
+namespace OpenClawTray;
+
+///
+/// Compile-time app identity constants that vary between Dev and Release builds,
+/// enabling side-by-side installation of both variants (similar to WinUI Gallery).
+///
+internal static class AppIdentity
+{
+#if DEV_BUILD
+ /// Human-visible app name shown in tray tooltips, window titles, and notifications.
+ public const string DisplayName = "OpenClaw Companion (Dev)";
+
+ /// Short name used in tray tooltip prefix.
+ public const string TrayName = "OpenClaw Tray (Dev)";
+
+ /// MSIX package identity name (must differ from release for side-by-side).
+ public const string PackageIdentityName = "OpenClaw.Companion.Dev";
+
+ /// Windows Registry auto-start value name (must differ so both can auto-start).
+ public const string AutoStartRegistryName = "OpenClawTray-Dev";
+
+ /// Single-instance mutex base name.
+ public const string MutexBaseName = "OpenClawTray-Dev";
+
+ /// Protocol scheme for deep links.
+ public const string ProtocolScheme = "openclaw-dev";
+
+ /// Whether this is a development build.
+ public const bool IsDev = true;
+#else
+ /// Human-visible app name shown in tray tooltips, window titles, and notifications.
+ public const string DisplayName = "OpenClaw Companion";
+
+ /// Short name used in tray tooltip prefix.
+ public const string TrayName = "OpenClaw Tray";
+
+ /// MSIX package identity name.
+ public const string PackageIdentityName = "OpenClaw.Companion";
+
+ /// Windows Registry auto-start value name.
+ public const string AutoStartRegistryName = "OpenClawTray";
+
+ /// Single-instance mutex base name.
+ public const string MutexBaseName = "OpenClawTray";
+
+ /// Protocol scheme for deep links.
+ public const string ProtocolScheme = "openclaw";
+
+ /// Whether this is a development build.
+ public const bool IsDev = false;
+#endif
+}
diff --git a/src/OpenClaw.Tray.WinUI/OpenClaw.Tray.WinUI.csproj b/src/OpenClaw.Tray.WinUI/OpenClaw.Tray.WinUI.csproj
index c8f29ad05..fe84563ab 100644
--- a/src/OpenClaw.Tray.WinUI/OpenClaw.Tray.WinUI.csproj
+++ b/src/OpenClaw.Tray.WinUI/OpenClaw.Tray.WinUI.csproj
@@ -14,6 +14,15 @@
win-x64;win-arm64
+
+
+ true
+
+
+ $(DefineConstants);DEV_BUILD
+
+
win-x64
@@ -192,6 +201,64 @@
+
+
+
+
+
+
+
+
+
+
+ ]*?\\bName\\s*=\\s*[\"'])[^\"']+([\"'])",
+ System.Text.RegularExpressions.RegexOptions.IgnoreCase);
+ text = nameRegex.Replace(text, "${1}" + IdentityName + "$2", 1);
+
+ // Patch Properties/DisplayName element
+ var propsDisplayRegex = new System.Text.RegularExpressions.Regex(
+ "()[^<]+()");
+ text = propsDisplayRegex.Replace(text, "${1}" + DisplayName + "$2", 2);
+
+ // Patch VisualElements DisplayName attribute
+ var visualRegex = new System.Text.RegularExpressions.Regex(
+ "(DisplayName\\s*=\\s*\")[^\"]+(\")");
+ text = visualRegex.Replace(text, "${1}" + DisplayName + "$2");
+
+ // Patch Protocol Name attribute (openclaw -> openclaw-dev)
+ var protocolRegex = new System.Text.RegularExpressions.Regex(
+ "(
+
+
+
diff --git a/src/OpenClaw.Tray.WinUI/Services/AutoStartManager.cs b/src/OpenClaw.Tray.WinUI/Services/AutoStartManager.cs
index 3cdae99a6..77397bdd7 100644
--- a/src/OpenClaw.Tray.WinUI/Services/AutoStartManager.cs
+++ b/src/OpenClaw.Tray.WinUI/Services/AutoStartManager.cs
@@ -11,7 +11,7 @@ namespace OpenClawTray.Services;
public static class AutoStartManager
{
private const string RegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
- private const string AppName = "OpenClawTray";
+ private static readonly string AppName = AppIdentity.AutoStartRegistryName;
public static bool IsAutoStartEnabled()
{
diff --git a/src/OpenClaw.Tray.WinUI/Services/TrayTooltipBuilder.cs b/src/OpenClaw.Tray.WinUI/Services/TrayTooltipBuilder.cs
index 6b82c73e0..a44a65613 100644
--- a/src/OpenClaw.Tray.WinUI/Services/TrayTooltipBuilder.cs
+++ b/src/OpenClaw.Tray.WinUI/Services/TrayTooltipBuilder.cs
@@ -40,7 +40,7 @@ internal string Build()
if (HasRelevantMcpStartupError()) warningCount++;
if (_snapshot.Channels.Length == 0 && isHealthy) warningCount++;
- var tooltip = $"OpenClaw Tray - {statusText}; " +
+ var tooltip = $"{AppIdentity.TrayName} - {statusText}; " +
$"{topology.DisplayName}; " +
$"Channels {channelReady}/{_snapshot.Channels.Length}; " +
$"Nodes {nodeOnline}/{nodeTotal}; " +
@@ -49,7 +49,7 @@ internal string Build()
if (_snapshot.CurrentActivity != null && !string.IsNullOrEmpty(_snapshot.CurrentActivity.DisplayText))
{
- tooltip = $"OpenClaw Tray - {_snapshot.CurrentActivity.DisplayText}; {statusText}";
+ tooltip = $"{AppIdentity.TrayName} - {_snapshot.CurrentActivity.DisplayText}; {statusText}";
}
return TrayTooltipFormatter.FitShellTooltip(tooltip);
diff --git a/tests/OpenClaw.Tray.Tests/AppRefactorContractTests.cs b/tests/OpenClaw.Tray.Tests/AppRefactorContractTests.cs
index d06cb2196..0b3e45eaa 100644
--- a/tests/OpenClaw.Tray.Tests/AppRefactorContractTests.cs
+++ b/tests/OpenClaw.Tray.Tests/AppRefactorContractTests.cs
@@ -350,7 +350,7 @@ public void Setup_IsHostedInTrayAndUsesSelfRestartAfterCompletion()
Assert.Contains("\"--post-setup-restart\"", source);
Assert.Contains("\"--wait-for-pid\"", source);
Assert.Contains("\"--post-setup-launch\"", source);
- Assert.Contains("? \"openclaw://chat\" : null", source);
+ Assert.Contains("$\"{AppIdentity.ProtocolScheme}://chat\"", source);
Assert.Contains("WaitForRestartSourceIfRequested(Environment.GetCommandLineArgs())", source);
AssertInOrder(source, "WaitForRestartSourceIfRequested(Environment.GetCommandLineArgs())", "_mutex = new Mutex");
Assert.DoesNotContain("ResolveSetupEngineUiPath", source);
diff --git a/tests/OpenClaw.Tray.Tests/InstallerIssAssertionTests.cs b/tests/OpenClaw.Tray.Tests/InstallerIssAssertionTests.cs
index a777cb4b2..a97e88eba 100644
--- a/tests/OpenClaw.Tray.Tests/InstallerIssAssertionTests.cs
+++ b/tests/OpenClaw.Tray.Tests/InstallerIssAssertionTests.cs
@@ -16,15 +16,17 @@ public sealed class InstallerIssAssertionTests
public void Installer_HasAppMutexMatchingTraySingleInstance()
{
var iss = File.ReadAllText(Path.Combine(TestRepositoryPaths.GetRepositoryRoot(), "installer.iss"));
- Assert.Contains("AppMutex=OpenClawTray", iss);
+ // Release build uses "OpenClawTray" mutex; dev build uses "OpenClawTray-Dev".
+ // The installer default (non-DevBuild) must match the release mutex.
+ Assert.Contains("AppMutex={#MyMutex}", iss);
+ Assert.Contains(@"#define MyMutex ""OpenClawTray""", iss);
Assert.Contains("Inno requires \"{{\" to emit a literal opening brace in AppId.", iss);
- Assert.Contains("AppId={{M0LTB0T-TRAY-4PP1-D3N7}", iss);
- Assert.DoesNotContain("AppId={{M0LTB0T-TRAY-4PP1-D3N7}}", iss);
+ Assert.Contains(@"#define MyAppId ""{{M0LTB0T-TRAY-4PP1-D3N7}""", iss);
- // The matching tray-side mutex name must be present in App.xaml.cs.
+ // The matching tray-side mutex name must be present in App.xaml.cs via AppIdentity.
var appXamlCs = File.ReadAllText(Path.Combine(
TestRepositoryPaths.GetRepositoryRoot(), "src", "OpenClaw.Tray.WinUI", "App.xaml.cs"));
- Assert.Contains("var mutexName = \"OpenClawTray\";", appXamlCs);
+ Assert.Contains("var mutexName = AppIdentity.MutexBaseName;", appXamlCs);
}
[Fact]
@@ -46,12 +48,12 @@ public void Installer_CreatesStartMenuEntrypointsForTraySetupAndSupport()
Assert.Contains(@"#define MyAppName ""OpenClaw Companion""", iss);
Assert.Contains(@"#define MyCompression ""lzma""", iss);
Assert.Contains(@"#define MySolidCompression ""yes""", iss);
- Assert.Contains("OutputBaseFilename=OpenClawCompanion-Setup-{#MyAppArch}", iss);
+ Assert.Contains("OutputBaseFilename=OpenClawCompanion{#MyOutputSuffix}-Setup-{#MyAppArch}", iss);
Assert.Contains(@"Name: ""{group}\{#MyAppName}""; Filename: ""{app}\{#MyAppExeName}""", iss);
- Assert.Contains(@"Name: ""{group}\OpenClaw Gateway Setup""; Filename: ""{app}\{#MyAppExeName}""; Parameters: ""openclaw://setup""", iss);
- Assert.Contains(@"Name: ""{group}\OpenClaw Companion Settings""; Filename: ""{app}\{#MyAppExeName}""; Parameters: ""openclaw://commandcenter""", iss);
- Assert.Contains(@"Name: ""{group}\OpenClaw Chat""; Filename: ""{app}\{#MyAppExeName}""; Parameters: ""openclaw://chat""", iss);
- Assert.Contains(@"Name: ""{group}\Check for Updates""; Filename: ""{app}\{#MyAppExeName}""; Parameters: ""openclaw://check-updates""", iss);
+ Assert.Contains(@"Name: ""{group}\OpenClaw Gateway Setup""; Filename: ""{app}\{#MyAppExeName}""; Parameters: ""{#MyProtocol}://setup""", iss);
+ Assert.Contains(@"Name: ""{group}\OpenClaw Companion Settings""; Filename: ""{app}\{#MyAppExeName}""; Parameters: ""{#MyProtocol}://commandcenter""", iss);
+ Assert.Contains(@"Name: ""{group}\OpenClaw Chat""; Filename: ""{app}\{#MyAppExeName}""; Parameters: ""{#MyProtocol}://chat""", iss);
+ Assert.Contains(@"Name: ""{group}\Check for Updates""; Filename: ""{app}\{#MyAppExeName}""; Parameters: ""{#MyProtocol}://check-updates""", iss);
}
[Fact]
@@ -112,11 +114,14 @@ public void Installer_RegistersOpenClawProtocol()
{
var iss = File.ReadAllText(Path.Combine(TestRepositoryPaths.GetRepositoryRoot(), "installer.iss"));
- Assert.Contains(@"Subkey: ""Software\Classes\openclaw""", iss);
+ // Protocol registration uses preprocessor variable {#MyProtocol}
+ Assert.Contains(@"Subkey: ""Software\Classes\{#MyProtocol}""", iss);
Assert.Contains(@"ValueName: ""URL Protocol""", iss);
- Assert.Contains(@"Subkey: ""Software\Classes\openclaw\shell\open\command""", iss);
+ Assert.Contains(@"Subkey: ""Software\Classes\{#MyProtocol}\shell\open\command""", iss);
Assert.Contains(@"{app}\{#MyAppExeName}", iss);
Assert.Contains(@"""%1""", iss);
+ // Ensure release default protocol is "openclaw"
+ Assert.Contains(@"#define MyProtocol ""openclaw""", iss);
}
[Fact]
diff --git a/tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj b/tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj
index 11e2caba0..d1fd00c17 100644
--- a/tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj
+++ b/tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj
@@ -82,6 +82,7 @@
+
diff --git a/tests/OpenClaw.Tray.Tests/Services/TrayTooltipBuilderTests.cs b/tests/OpenClaw.Tray.Tests/Services/TrayTooltipBuilderTests.cs
index 897a29ab9..a1932fc7f 100644
--- a/tests/OpenClaw.Tray.Tests/Services/TrayTooltipBuilderTests.cs
+++ b/tests/OpenClaw.Tray.Tests/Services/TrayTooltipBuilderTests.cs
@@ -1,5 +1,6 @@
using OpenClaw.Shared;
using OpenClaw.Connection;
+using OpenClawTray;
using OpenClawTray.Helpers;
using OpenClawTray.Services;
using System;
@@ -49,7 +50,7 @@ public void Build_ConnectedWithChannelsAndNodes_ContainsExpectedSegments()
var result = new TrayTooltipBuilder(snapshot).Build();
- Assert.Contains("OpenClaw Tray - Connected", result);
+ Assert.Contains($"{AppIdentity.TrayName} - Connected", result);
Assert.Contains("Channels 1/2", result);
Assert.Contains("Nodes 1/1", result);
Assert.Contains("Warnings 0", result);
@@ -108,7 +109,7 @@ public void Build_DegradedOverall_DoesNotReadConnected()
var result = new TrayTooltipBuilder(snapshot).Build();
- Assert.Contains("OpenClaw Tray - Degraded", result);
+ Assert.Contains($"{AppIdentity.TrayName} - Degraded", result);
Assert.Contains("Warnings 1", result);
}
@@ -130,7 +131,7 @@ public void Build_LocalMcpOnly_IsExplicit()
var result = new TrayTooltipBuilder(snapshot).Build();
- Assert.Contains("OpenClaw Tray - Local MCP only", result);
+ Assert.Contains($"{AppIdentity.TrayName} - Local MCP only", result);
Assert.Contains("Warnings 1", result);
}
@@ -153,7 +154,7 @@ public void Build_LocalMcpOnly_DoesNotMaskDegradedGatewayLifecycle()
var result = new TrayTooltipBuilder(snapshot).Build();
- Assert.Contains("OpenClaw Tray - Degraded", result);
+ Assert.Contains($"{AppIdentity.TrayName} - Degraded", result);
Assert.DoesNotContain("Local MCP only", result);
}
@@ -174,7 +175,7 @@ public void Build_McpStartupError_IsExplicit()
var result = new TrayTooltipBuilder(snapshot).Build();
- Assert.Contains("OpenClaw Tray - Local MCP failed", result);
+ Assert.Contains($"{AppIdentity.TrayName} - Local MCP failed", result);
Assert.Contains("Warnings 2", result);
}
@@ -194,7 +195,7 @@ public void Build_StaleMcpStartupError_IsIgnoredWhenMcpDisabled()
var result = new TrayTooltipBuilder(snapshot).Build();
- Assert.Contains("OpenClaw Tray - Connected", result);
+ Assert.Contains($"{AppIdentity.TrayName} - Connected", result);
Assert.Contains("Warnings 1", result);
Assert.DoesNotContain("Local MCP failed", result);
}