Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/OpenClaw.Shared/IOperatorGatewayClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public interface IOperatorGatewayClient
Task RequestNodePairListAsync();
Task<bool> NodePairApproveAsync(string requestId);
Task<bool> NodePairRejectAsync(string requestId);
Task<NodeForgetResult> NodePairRemoveAsync(string nodeId);
Task<NodeRenameResult> NodeRenameAsync(string nodeId, string displayName);
Task RequestDevicePairListAsync();
Task<bool> DevicePairApproveAsync(string requestId);
Task<bool> DevicePairRejectAsync(string requestId);
Expand Down
64 changes: 60 additions & 4 deletions src/OpenClaw.Shared/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,32 @@ public class GatewayNodeInfo
public List<string> DisabledCommands { get; set; } = new();
public Dictionary<string, bool> Permissions { get; set; } = new(StringComparer.OrdinalIgnoreCase);

// Identity / hardware (from gateway NodeListNode schema)
public string? Version { get; set; }
public string? CoreVersion { get; set; }
public string? UiVersion { get; set; }
public string? ClientId { get; set; }
public string? ClientMode { get; set; }
public string? DeviceFamily { get; set; }
public string? ModelIdentifier { get; set; }
public string? RemoteIp { get; set; }
public string? PathEnv { get; set; }

// Timestamps and state
public DateTime? ConnectedAt { get; set; }
public DateTime? ApprovedAt { get; set; }
public string? LastSeenReason { get; set; }

// True when the node is in the gateway's paired set (regardless of current
// connection state). Distinct from IsOnline — a paired node can be offline.
public bool IsPaired { get; set; }

// True when the gateway provided an explicit displayName/name/label.
// False when the parser had to fall back to shortId or nodeId. UI surfaces
// (e.g. the rename dialog) use this to distinguish "the user gave this
// node a name" from "we showed the id because there was nothing better".
public bool HasExplicitDisplayName { get; set; }

public string ShortId => NodeId.Length <= 12 ? NodeId : NodeId[..12] + "…";

public string DisplayText
Expand Down Expand Up @@ -522,6 +548,26 @@ public string DetailText
private static string FormatAge(DateTime timestampUtc) => ModelFormatting.FormatAge(timestampUtc);
}

/// <summary>
/// Result of a <c>node.rename</c> request to the gateway.
/// </summary>
/// <param name="Success">True when the gateway accepted the rename and persisted it.</param>
/// <param name="NodeId">Node id the gateway returned; same as the requested id on success.</param>
/// <param name="DisplayName">Updated display name as persisted by the gateway.</param>
/// <param name="ErrorMessage">Gateway-supplied or transport-derived error description; null on success.</param>
public sealed record NodeRenameResult(
bool Success,
string? NodeId = null,
string? DisplayName = null,
string? ErrorMessage = null);

/// <summary>
/// Result of a <c>node.pair.remove</c> request to the gateway.
/// </summary>
/// <param name="Success">True when the gateway accepted the removal.</param>
/// <param name="ErrorMessage">Gateway-supplied or transport-derived error description; null on success.</param>
public sealed record NodeForgetResult(bool Success, string? ErrorMessage = null);

public enum GatewayDiagnosticSeverity
{
Info,
Expand Down Expand Up @@ -1490,18 +1536,28 @@ private static string BuildLocalTunnelUrl(int localPort) =>
}

/// <summary>Shared display-formatting helpers used by model classes.</summary>
internal static class ModelFormatting
public static class ModelFormatting
{
/// <summary>
/// Formats a UTC timestamp as a human-readable age string (e.g. "just now", "5m ago", "2h ago", "3d ago").
/// Formats a UTC timestamp as a human-readable age string.
/// Examples: "just now", "5m ago", "12h ago", "3d ago", "2026-03-12".
/// Public so all UI surfaces share one canonical formatter — divergent
/// thresholds between callers can otherwise show the same timestamp as
/// "1d ago" in one place and "36h ago" in another for the same node.
/// </summary>
internal static string FormatAge(DateTime timestampUtc)
public static string FormatAge(DateTime timestampUtc)
{
var delta = DateTime.UtcNow - timestampUtc;
// Clock skew between gateway host and local machine can produce
// timestamps slightly in the future. Treat those as "just now".
if (delta < TimeSpan.Zero) return "just now";
if (delta.TotalSeconds < 60) return "just now";
if (delta.TotalMinutes < 60) return $"{(int)delta.TotalMinutes}m ago";
if (delta.TotalHours < 48) return $"{(int)delta.TotalHours}h ago";
return $"{(int)delta.TotalDays}d ago";
if (delta.TotalDays < 30) return $"{(int)delta.TotalDays}d ago";
// For very old timestamps the relative form loses meaning; show
// an absolute local date instead.
return timestampUtc.ToLocalTime().ToString("yyyy-MM-dd");
}

/// <summary>
Expand Down
155 changes: 144 additions & 11 deletions src/OpenClaw.Shared/OpenClawGatewayClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,101 @@ public Task<bool> NodePairRejectAsync(string requestId)
return TrySendTrackedRequestAsync("node.pair.reject", new { requestId });
}

/// <summary>
/// Removes a paired node from the gateway and waits for the gateway's
/// application-level response. Returns Success=true only when the
/// gateway confirms the removal — Success=false on transport failure,
/// missing scope, unknown nodeId, or any server-side rejection. The
/// gateway also broadcasts <c>node.pair.resolved</c> with
/// <c>decision="removed"</c> after success, which the broadcast handler
/// turns into a node.list + node.pair.list refresh.
/// </summary>
public async Task<NodeForgetResult> NodePairRemoveAsync(string nodeId)
{
if (string.IsNullOrWhiteSpace(nodeId))
return new NodeForgetResult(false, "nodeId required");
if (!IsConnected)
return new NodeForgetResult(false, "Gateway connection is not open");

try
{
// SendWizardRequestAsync awaits the matching ack frame and
// throws InvalidOperationException when the gateway responds
// with ok=false, so callers see a real failure result on
// rejection (missing scope, unknown nodeId) rather than a
// false success the moment the WS frame is sent.
await SendWizardRequestAsync("node.pair.remove", new { nodeId });
return new NodeForgetResult(true);
}
catch (InvalidOperationException ex)
{
// Gateway business error (e.g. "missing scope: operator.pairing",
// "unknown nodeId"). Surface this verbatim so the user sees an
// actionable message.
_logger.Warn($"node.pair.remove rejected: {ex.Message}");
return new NodeForgetResult(false, ex.Message);
}
catch (Exception ex)
{
// Transport / timeout / unexpected exception. Don't leak raw
// exception text into the UI — return null so the caller uses
// its localized fallback string.
_logger.Warn($"node.pair.remove failed: {ex.Message}");
return new NodeForgetResult(false, ErrorMessage: null);
}
}

/// <summary>
/// Renames the display name of a paired node. Awaits the gateway's
/// response, so callers can rely on <see cref="NodeRenameResult.Success"/>
/// before refreshing UI state. The gateway does not broadcast a rename,
/// so callers should follow a successful rename with
/// <see cref="RequestNodesAsync"/> to pick up the new value.
/// </summary>
public async Task<NodeRenameResult> NodeRenameAsync(string nodeId, string displayName)
{
if (string.IsNullOrWhiteSpace(nodeId))
return new NodeRenameResult(false, ErrorMessage: "nodeId required");
var trimmed = displayName?.Trim() ?? string.Empty;
if (string.IsNullOrWhiteSpace(trimmed))
return new NodeRenameResult(false, ErrorMessage: "displayName required");
if (!IsConnected)
return new NodeRenameResult(false, ErrorMessage: "Gateway connection is not open");

try
{
var response = await SendWizardRequestAsync(
"node.rename",
new { nodeId, displayName = trimmed });

var returnedNodeId = response.ValueKind == JsonValueKind.Object &&
response.TryGetProperty("nodeId", out var idEl)
? idEl.GetString()
: nodeId;
var returnedDisplayName = response.ValueKind == JsonValueKind.Object &&
response.TryGetProperty("displayName", out var nameEl)
? nameEl.GetString() ?? trimmed
: trimmed;
return new NodeRenameResult(true, returnedNodeId, returnedDisplayName);
}
catch (InvalidOperationException ex)
{
// Gateway business error (e.g. "missing scope: operator.pairing",
// "unknown nodeId"). Surface this verbatim so the user sees an
// actionable message.
_logger.Warn($"node.rename rejected: {ex.Message}");
return new NodeRenameResult(false, ErrorMessage: ex.Message);
}
catch (Exception ex)
{
// Transport / timeout / unexpected exception. Don't leak raw
// exception text into the UI — return null so the caller uses
// its localized fallback string.
_logger.Warn($"node.rename failed: {ex.Message}");
return new NodeRenameResult(false, ErrorMessage: null);
}
}

public async Task RequestDevicePairListAsync()
{
if (_devicePairListUnsupported) return;
Expand Down Expand Up @@ -1815,8 +1910,13 @@ private void HandleEvent(JsonElement root)
break;
case "node.pair.requested":
case "node.pair.resolved":
// Refresh node pair list when pairing state changes
// Refresh node pair list when pairing state changes. Also
// refresh node.list because resolved decisions (in particular
// "removed") drop the node from the gateway's known set, so
// any UI mirroring node.list would otherwise show stale data
// until the next poll.
_ = RequestNodePairListAsync();
_ = RequestNodesAsync();
break;
case "device.pair.requested":
case "device.pair.resolved":
Expand Down Expand Up @@ -2395,40 +2495,73 @@ private void ParseNodeList(JsonElement nodesPayload)
"unknown");
var connected = GetOptionalBool(nodeElement, "connected");
var online = GetOptionalBool(nodeElement, "online");
var paired = GetOptionalBool(nodeElement, "paired");
var capabilities = GetStringArray(nodeElement, "caps");
if (capabilities.Length == 0)
capabilities = GetStringArray(nodeElement, "capabilities");
var commands = GetStringArray(nodeElement, "declaredCommands");
if (commands.Length == 0)
commands = GetStringArray(nodeElement, "commands");
var disabledCommands = GetStringArray(nodeElement, "disabledCommands");
var permissions = GetBoolDictionary(nodeElement, "permissions");

var clientMode = GetString(nodeElement, "clientMode");

// Distinguish "user gave this node a name" from "we fell back
// to the id". The rename dialog uses this so it can prefill
// empty when the node has no explicit name (rather than
// pre-seeding the textbox with the id, which would otherwise
// get persisted as the new display name on Enter).
var explicitName = FirstNonEmpty(
GetString(nodeElement, "displayName"),
GetString(nodeElement, "name"),
GetString(nodeElement, "label"));

buffer[count++] = new GatewayNodeInfo
{
NodeId = nodeId!,
DisplayName = FirstNonEmpty(
GetString(nodeElement, "displayName"),
GetString(nodeElement, "name"),
GetString(nodeElement, "label"),
GetString(nodeElement, "shortId"),
nodeId)!,
DisplayName = !string.IsNullOrWhiteSpace(explicitName)
? explicitName!
: FirstNonEmpty(GetString(nodeElement, "shortId"), nodeId)!,
HasExplicitDisplayName = !string.IsNullOrWhiteSpace(explicitName),
Mode = FirstNonEmpty(
GetString(nodeElement, "mode"),
GetString(nodeElement, "clientMode"),
clientMode,
"node")!,
Status = status!,
Platform = FirstNonEmpty(
GetString(nodeElement, "platform"),
GetString(nodeElement, "os")),
LastSeen = ParseUnixTimestampMs(nodeElement, "lastSeenAt") ??
// Gateway NodeListNode wire schema uses *Ms suffix; older
// fallbacks kept for compatibility with mocks/tests.
// ConnectedAt is parsed independently below — do NOT fall
// back to it here, otherwise the UI shows the same value
// twice as both "Connected Xm ago" and "Seen Xm ago".
LastSeen = ParseUnixTimestampMs(nodeElement, "lastSeenAtMs") ??
ParseUnixTimestampMs(nodeElement, "lastSeenAt") ??
ParseUnixTimestampMs(nodeElement, "lastSeen") ??
ParseUnixTimestampMs(nodeElement, "updatedAt") ??
ParseUnixTimestampMs(nodeElement, "connectedAt"),
ParseUnixTimestampMs(nodeElement, "updatedAt"),
ConnectedAt = ParseUnixTimestampMs(nodeElement, "connectedAtMs") ??
ParseUnixTimestampMs(nodeElement, "connectedAt"),
ApprovedAt = ParseUnixTimestampMs(nodeElement, "approvedAtMs") ??
ParseUnixTimestampMs(nodeElement, "approvedAt"),
LastSeenReason = GetString(nodeElement, "lastSeenReason"),
Capabilities = capabilities.ToList(),
Commands = commands.ToList(),
DisabledCommands = disabledCommands.ToList(),
Permissions = permissions,
CapabilityCount = capabilities.Length,
CommandCount = commands.Length,
Version = GetString(nodeElement, "version"),
CoreVersion = GetString(nodeElement, "coreVersion"),
UiVersion = GetString(nodeElement, "uiVersion"),
ClientId = GetString(nodeElement, "clientId"),
ClientMode = clientMode,
DeviceFamily = GetString(nodeElement, "deviceFamily"),
ModelIdentifier = GetString(nodeElement, "modelIdentifier"),
RemoteIp = GetString(nodeElement, "remoteIp"),
PathEnv = GetString(nodeElement, "pathEnv"),
IsPaired = paired ?? false,
IsOnline = online ?? connected ?? status is "ok" or "online" or "connected" or "ready" or "active"
};
}
Expand Down
13 changes: 8 additions & 5 deletions src/OpenClaw.Tray.WinUI/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
public LocalGatewaySetupEngine CreateLocalGatewaySetupEngine(
bool replaceExistingConfigurationConfirmed = false)
{
var settings = _settings ?? new SettingsManager();

Check warning on line 79 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

Possible null reference argument for parameter 'settings' in 'void SshTunnelService.EnsureStarted(SettingsManager settings)'.

Check warning on line 79 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

Possible null reference argument for parameter 'settings' in 'void SshTunnelService.EnsureStarted(SettingsManager settings)'.

Check warning on line 79 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

Possible null reference argument for parameter 'settings' in 'void SshTunnelService.EnsureStarted(SettingsManager settings)'.

Check warning on line 79 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

Possible null reference argument for parameter 'settings' in 'void SshTunnelService.EnsureStarted(SettingsManager settings)'.

Check warning on line 79 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

Possible null reference argument for parameter 'settings' in 'void SshTunnelService.EnsureStarted(SettingsManager settings)'.

Check warning on line 79 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

Possible null reference argument for parameter 'settings' in 'void SshTunnelService.EnsureStarted(SettingsManager settings)'.

Check warning on line 79 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build-msix (win-arm64)

Possible null reference argument for parameter 'settings' in 'void SshTunnelService.EnsureStarted(SettingsManager settings)'.
var nodeService = EnsureNodeServiceForLocalGatewaySetup(settings);
// Suppress node auto-connect in the connection manager during setup.
// The engine controls node pairing in its own phase (PairWindowsTrayNode).
Expand Down Expand Up @@ -256,7 +256,7 @@
// Hook up crash handlers
this.UnhandledException += OnUnhandledException;
AppDomain.CurrentDomain.UnhandledException += OnDomainUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;

Check warning on line 259 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 259 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 259 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 259 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 259 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 259 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
}

Expand Down Expand Up @@ -293,7 +293,7 @@
{
var dir = Path.GetDirectoryName(CrashLogPath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);

Check warning on line 296 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 296 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 296 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 296 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

var message = $"\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {source}\n{ex}\n";
File.AppendAllText(CrashLogPath, message);
Expand All @@ -315,11 +315,11 @@
}

// -----------------------------------------------------------------------
// CLI uninstall path

Check warning on line 318 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 318 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 318 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.
// Invoked when --uninstall is present in argv. Runs headlessly without
// creating the tray UI. Attaches to the parent console so stdout/stderr
// are visible when invoked from PowerShell or cmd.
// -----------------------------------------------------------------------

Check warning on line 322 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 322 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
Expand Down Expand Up @@ -482,7 +482,7 @@
{
try
{
var dir = Path.GetDirectoryName(RunMarkerPath);

Check warning on line 485 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
File.WriteAllText(RunMarkerPath, DateTime.Now.ToString("O"));
Expand Down Expand Up @@ -1383,18 +1383,21 @@
}

// ── Connected Devices with inline permission toggles ──
if (_lastNodes.Length > 0)
// Only show currently-connected nodes; offline/stale paired nodes
// remain visible on the full Nodes page where they can be renamed
// or forgotten.
var connectedNodes = _lastNodes.Where(n => n.IsOnline).ToArray();
if (connectedNodes.Length > 0)
{
menu.AddSeparator();

var onlineCount = _lastNodes.Count(n => n.IsOnline);
var totalCaps = _lastNodes.Sum(n => n.CapabilityCount);
var deviceSummaryRight = $"{onlineCount} online · {totalCaps} caps";
var totalCaps = connectedNodes.Sum(n => n.CapabilityCount);
var deviceSummaryRight = $"{connectedNodes.Length} online · {totalCaps} caps";
menu.AddCustomElement(BuildSectionHeader("Devices", deviceSummaryRight));

var currentHost = Environment.MachineName;

foreach (var node in _lastNodes.Take(5))
foreach (var node in connectedNodes.Take(5))
{
var card = BuildDeviceCard(node);
var flyoutItems = BuildDeviceFlyoutItems(node);
Expand Down Expand Up @@ -4701,8 +4704,8 @@
internal class AppLogger : IOpenClawLogger
{
public void Info(string message) => Logger.Info(message);
public void Debug(string message) => Logger.Debug(message);

Check warning on line 4707 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4707 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-x64)

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4707 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4707 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4707 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build-msix (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.
public void Warn(string message) => Logger.Warn(message);

Check warning on line 4708 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4708 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4708 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.
public void Error(string message, Exception? ex = null) =>

Check warning on line 4709 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4709 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4709 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.
Logger.Error(ex != null ? $"{message}: {ex.Message}" : message);
}

Check warning on line 4711 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / test

The type 'Logger' in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'D:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.

Check warning on line 4711 in src/OpenClaw.Tray.WinUI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build (win-arm64)

The type 'Logger' in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs' conflicts with the imported type 'Logger' in 'OpenClawTray.FunctionalUI, Version=0.5.1.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in 'C:\a\openclaw-windows-node\openclaw-windows-node\src\OpenClaw.Tray.WinUI\Services\Logger.cs'.
Loading
Loading