A comprehensive guide for building, running, and contributing to the OpenClaw Windows Hub.
- .NET 10 SDK - Download here
- Windows 10/11 - WinUI 3 and Windows App SDK require Windows 10 version 1903 or later
- WebView2 Runtime - Usually pre-installed on Windows 10+ (Manual download)
- Visual Studio 2022 (optional) - For easier development and debugging with WinUI 3 designer support
- A running OpenClaw gateway instance - The gateway provides the backend for chat, sessions, and notifications
- Default gateway URL:
ws://localhost:18789 - You'll need a valid authentication token from your OpenClaw instance
- Default gateway URL:
- PowerToys (latest version) - Required for testing the Command Palette extension
This monorepo contains three main projects:
openclaw-windows-hub/
├── src/
│ ├── OpenClaw.Shared/ # Shared gateway client library
│ │ ├── OpenClawGatewayClient.cs # WebSocket client for gateway protocol
│ │ ├── Models.cs # Data models (SessionInfo, ChannelHealth, etc.)
│ │ └── IOpenClawLogger.cs # Logging interface
│ │
│ ├── OpenClaw.Tray.WinUI/ # WinUI 3 system tray application (primary)
│ │ ├── App.xaml.cs # Main application, tray icon, gateway connection
│ │ ├── Services/ # Settings, logging, hotkeys, deep links
│ │ ├── Windows/ # UI windows (Settings, WebChat, Status, etc.)
│ │ ├── Dialogs/ # Modal dialogs
│ │ └── Helpers/ # Icon generation, utilities
│ │
│ ├── OpenClaw.Tray/ # Legacy WinForms tray app (deprecated)
│ │
│ └── OpenClaw.CommandPalette/ # PowerToys Command Palette extension
│ ├── OpenClaw.cs # Extension entry point
│ ├── OpenClawCommandsProvider.cs # Command provider implementation
│ └── Pages/ # XAML pages for command results
│
├── tests/
│ └── OpenClaw.Shared.Tests/ # Unit tests for shared library
│
├── tools/
│ ├── cmdpal-dev.ps1 # Helper script for Command Palette development
│ └── icongen/ # Icon generation tool
│
├── .github/workflows/
│ └── ci.yml # GitHub Actions CI/CD workflow
│
├── moltbot-windows-hub.slnx # Solution file (historical name)
├── README.md # User-facing documentation
└── DEVELOPMENT.md # This file
Note on Naming: The solution file is named
moltbot-windows-hub.slnxdue to the project's history (formerly known as Moltbot, formerly known as Clawdbot). The repository and current branding use "OpenClaw".
OpenClaw.Tray.WinUI ──depends on──▶ OpenClaw.Shared
OpenClaw.CommandPalette ──depends on──▶ OpenClaw.Shared
OpenClaw.Shared.Tests ──tests──▶ OpenClaw.Shared
| Subsystem | Location | Purpose |
|---|---|---|
| Gateway Communication | OpenClaw.Shared/OpenClawGatewayClient.cs |
WebSocket client with protocol v3, reconnect/backoff logic |
| Notification System | OpenClaw.Tray.WinUI/App.xaml.cs |
Event routing, toast notifications, classification |
| WebView2 Integration | OpenClaw.Tray.WinUI/Windows/WebChatWindow.xaml.cs |
Embedded chat panel with lifecycle management |
| Tray Icon Management | OpenClaw.Tray.WinUI/Helpers/IconHelper.cs |
GDI handle management, dynamic icon generation |
| Session Tracking | OpenClaw.Shared/OpenClawGatewayClient.cs |
Session state, activity tracking, polling |
| Settings & Logging | OpenClaw.Tray.WinUI/Services/ |
JSON settings persistence, file rotation logging |
From the repository root:
dotnet restore
dotnet buildThis builds all projects (Shared library, Tray app, Command Palette extension).
Shared Library:
dotnet build src/OpenClaw.SharedTray App (WinUI):
dotnet build src/OpenClaw.Tray.WinUICommand Palette Extension:
dotnet build src/OpenClaw.CommandPalette -p:Platform=x64Note: Command Palette requires explicit platform (x64 or arm64).
The solution supports both Intel/AMD (x64) and ARM (arm64) architectures:
-
Tray App: Can be built for either architecture
dotnet build src/OpenClaw.Tray.WinUI -r win-x64 dotnet build src/OpenClaw.Tray.WinUI -r win-arm64
-
Command Palette: Must match your system architecture
# On x64 systems: dotnet build src/OpenClaw.CommandPalette -p:Platform=x64 # On ARM64 systems: dotnet build src/OpenClaw.CommandPalette -p:Platform=arm64
⚠️ Important for ARM64 Users: Both the Command Palette extension AND the Tray app must be built for ARM64 architecture for WebView2 and deep links to work correctly. Running an x64 build on ARM64 will cause errors.
The Shared library is cross-platform and can be built on Windows, Linux, or macOS:
cd src/OpenClaw.Shared
dotnet buildThe WinUI Tray app and Command Palette are Windows-only but can be built on Linux using:
dotnet build -p:EnableWindowsTargeting=true- Open
moltbot-windows-hub.slnxin Visual Studio 2022 - Set
OpenClaw.Tray.WinUIas the startup project - Press F5 to run with debugging
dotnet run --project src/OpenClaw.Tray.WinUIFor verbose output:
dotnet run --project src/OpenClaw.Tray.WinUI -c DebugUse the provided helper script for rapid iteration:
.\tools\cmdpal-dev.ps1 cycleThis script:
- Removes the currently installed extension
- Builds the extension for your platform
- Deploys it via
Add-AppxPackage -Register - Reminds you to run "Reload" in Command Palette
Manual steps:
# Build
dotnet build src/OpenClaw.CommandPalette -p:Platform=x64
# Deploy (development mode, no MSIX needed)
$manifest = "src/OpenClaw.CommandPalette/bin/x64/Debug/net10.0-windows10.0.26100.0/win-x64/AppxManifest.xml"
Add-AppxPackage -Register $manifest -ForceApplicationShutdown
# Test: Open PowerToys Command Palette (Win+Alt+Space), type "Reload", then "OpenClaw"For distribution:
dotnet publish src/OpenClaw.Tray.WinUI -c Release -r win-x64 --self-contained -o publishThis creates a standalone executable with all dependencies bundled.
The OpenClawGatewayClient manages the connection to the OpenClaw gateway:
Connection Flow:
- WebSocket connects to gateway URL (default:
ws://localhost:18789) - Client waits for
challengeevent from gateway - Client responds with authentication token
- Gateway sends
connectedevent confirming authentication - Client begins receiving events and can send requests
Reconnect & Backoff Logic:
- Automatic reconnection on disconnect or error
- Exponential backoff: 1s, 2s, 4s, 8s, 15s, 30s, 60s (max)
- Resets backoff counter on successful connection
- Connection state exposed via
StatusChangedevent
Implementation:
// Backoff sequence in milliseconds
private static readonly int[] BackoffMs = { 1000, 2000, 4000, 8000, 15000, 30000, 60000 };The gateway sends structured events over WebSocket. The client parses these into typed notifications:
| Event Type | Handler | Description | UI Result |
|---|---|---|---|
challenge |
Initial handshake | Gateway requests authentication | Client sends token |
connected |
Authentication success | Gateway confirms connection | Status → Connected |
agent (stream=job) |
HandleJobEvent |
Job/task activity | Activity indicator, tray badge |
agent (stream=tool) |
HandleToolEvent |
Tool execution (exec, read, write, etc.) | Activity with tool name + args |
chat |
HandleChatEvent |
Assistant chat messages | Toast notification for short messages |
health |
ParseChannelHealth |
Channel health status | Channel status in tray menu |
session |
HandleSessionEvent |
Session list updates | Session display refresh |
usage |
ParseUsage |
Token usage, cost, requests | Usage info in status window |
Notifications are classified using two strategies:
- Structured (preferred): Events with explicit
type,category, ornotificationTypefields - Text-based (fallback): Keyword matching on notification content
Categories:
health- Blood sugar, glucose, CGM readingsurgent- Critical alerts requiring immediate attentionreminder- Calendar reminders, tasksstock- Stock price alertsemail- Email notificationscalendar- Calendar eventserror- Error messagesbuild- CI/CD build statusinfo- General information (default)
Routing:
- Notifications trigger Windows toast notifications (if enabled in settings)
- Stored in notification history for later review
- Can be filtered by category
The WebChatWindow uses Microsoft Edge WebView2 for embedded web content:
Initialization:
- WebView2 control created in XAML
CoreWebView2environment initialized on window load- User data folder:
%LOCALAPPDATA%\OpenClawTray\WebView2 - Navigation guard prevents external navigation
Lifecycle:
Window Created → WebView2.EnsureCoreWebView2Async() → Navigate to Chat URL → User Interaction → Window Hidden (not disposed)
Key Design Decisions:
- Singleton pattern: Only one WebChat window instance exists
- Hidden instead of disposed: Window is hidden when closed to preserve state
- Separate user data folder: Isolates cookies/storage from browser
- Navigation guard: Prevents accidental navigation away from chat
Implementation:
// Initialize WebView2 environment
await WebView.EnsureCoreWebView2Async();
WebView.CoreWebView2.Navigate(chatUrl);
// Navigation guard
WebView.CoreWebView2.NavigationStarting += (s, e) => {
if (!e.Uri.StartsWith(allowedHost)) {
e.Cancel = true;
}
};The tray icon system uses GDI handles for icon creation. Proper management prevents handle leaks:
Icon Creation Pattern:
// Create bitmap
using var bitmap = new Bitmap(16, 16);
using var graphics = Graphics.FromImage(bitmap);
graphics.DrawSomething(...);
// Convert to icon (creates GDI handle)
var hIcon = bitmap.GetHicon();
var icon = Icon.FromHandle(hIcon);
// Clone to own the data
var result = (Icon)icon.Clone();
// CRITICAL: Destroy the GDI handle
DestroyIcon(hIcon);
return result;Why This Matters:
- GDI handles are a limited system resource (10,000 per process on Windows)
- Not calling
DestroyIcon()causes handle leaks - Each tray icon update could leak a handle without proper cleanup
- The pattern: Create → Clone → Destroy ensures we own the icon data and release the GDI handle
Caching: Icons are cached to avoid repeated GDI operations:
private static Icon? _connectedIcon;
private static Icon? _disconnectedIcon;
// ... etcThe client tracks active agent sessions with intelligent display logic:
Session State:
- Main session: Primary user conversation
- Sub-sessions: Background tasks, tool executions
- Each session has: key, status, model, channel, activity
Polling:
RequestSessionsAsync()called periodically (every 5 seconds when connected)- Gateway responds with session list
- Client updates internal
_sessionsdictionary
Display Selection Algorithm:
- Active main session always takes priority
- Currently displayed session kept if still active (prevents flipping)
- Falls back to most recently active sub-session
- 3-second debounce prevents jitter during rapid changes
Why This Matters: Without stable selection, the activity display would rapidly flip between sessions during concurrent operations, creating a poor user experience.
File-based logging with automatic rotation:
Log File:
- Location:
%LOCALAPPDATA%\OpenClawTray\openclaw-tray.log - Rotation: When log exceeds 5MB, old log →
openclaw-tray.log.old - Thread-safe: Uses lock for concurrent writes
Log Levels:
INFO- Normal operation (connections, events)WARN- Recoverable issues (reconnects, timeouts)ERROR- Failures (connection errors, exceptions)DEBUG- Detailed diagnostics (only in DEBUG builds)
Format:
[2026-02-01 12:34:56.789] [INFO] Gateway connected, waiting for challenge...
[2026-02-01 12:34:57.123] [WARN] Reconnecting with 2000ms backoff...
[2026-02-01 12:34:58.456] [ERROR] Connection failed: Host not found
Debug Output:
In DEBUG builds, logs are also written to Visual Studio Output window via System.Diagnostics.Debug.WriteLine().
Security: Sensitive data (authentication tokens) are never logged.
The OpenClaw.Shared.Tests project contains comprehensive tests for the shared library:
# Run all tests
dotnet test
# Run with detailed output
dotnet test --verbosity detailed
# Run specific test class
dotnet test --filter "FullyQualifiedName~AgentActivityTests"Test Coverage:
- ✅ 68 tests for data models (AgentActivity, ChannelHealth, SessionInfo, GatewayUsageInfo)
- ✅ 20 tests for gateway client utilities (notification classification, tool mapping, path formatting)
- ✅ All tests are pure unit tests (no network, no file system, no external dependencies)
See tests/OpenClaw.Shared.Tests/README.md for detailed test documentation.
You can test the UI and basic functionality without a running gateway:
Tray App:
- Launch the app:
dotnet run --project src/OpenClaw.Tray.WinUI - Right-click tray icon → Settings
- Enter a dummy gateway URL (e.g.,
ws://localhost:18789) - The app will show "Disconnected" status but you can:
- Test the tray menu structure
- Open Settings dialog and configure preferences
- Test auto-start functionality
- View logs
Command Palette:
- Deploy the extension:
.\tools\cmdpal-dev.ps1 deploy - Open PowerToys Command Palette (Win+Alt+Space)
- Type "OpenClaw"
- Commands will show but most require a connected gateway to function
-
Disconnected (Gray):
- Start app without gateway running
- Verify icon is gray
- Verify tooltip shows "Disconnected"
-
Connecting (Amber):
- Configure valid gateway URL but don't start gateway yet
- Restart app
- Briefly observe amber icon during connection attempt
-
Connected (Green):
- Start gateway
- Verify icon turns green
- Verify tooltip shows "Connected"
-
Error (Red):
- Connect to gateway, then stop gateway
- Verify icon turns red after timeout
-
Activity Badge:
- Connect to gateway
- Send a chat message that triggers tool use
- Verify small colored dot appears on tray icon during tool execution
-
Toast Notifications:
- Connect to gateway
- Send a message that triggers a chat response
- Verify Windows toast notification appears (if enabled)
- Click toast → should open relevant UI
-
Notification History:
- Right-click tray → Notification History
- Verify past notifications are listed
- Test filtering by category
-
Notification Settings:
- Settings → Disable notifications
- Send a chat message
- Verify no toast appears (but history still records it)
-
Open WebChat:
- Right-click tray → Open Web Chat
- Verify window opens with WebView2 content
- Test sending a message
-
Window State Persistence:
- Move/resize WebChat window
- Close and reopen
- Verify position/size restored (future feature)
-
WebView2 Fallback:
- Test on system without WebView2 Runtime
- Verify graceful fallback (opens browser instead)
The repository uses GitHub Actions for continuous integration and release automation.
Workflow File: .github/workflows/ci.yml
Trigger Events:
- Push to
mainormasterbranch - Pull requests to
mainormaster - Git tags matching
v*(e.g.,v1.2.3) for releases
The CI builds multiple configurations:
Test Job:
- Runs on
windows-latest - Builds Shared library, Tray app (WinForms), Tray app (WinUI), Tests
- Runs unit tests:
dotnet test tests/OpenClaw.Shared.Tests - Uses GitVersion for semantic versioning
Build Job (Tray):
- Matrix:
win-x64,win-arm64 - Builds WinUI Tray app for both architectures
- Publishes self-contained executables
- Signs with Azure Trusted Signing (on tag releases only)
Build Job (Command Palette):
- Matrix:
x64,arm64 - Builds Command Palette extension for both platforms
- Produces MSIX packages for deployment
On every build, the following artifacts are uploaded:
| Artifact | Contents | Purpose |
|---|---|---|
openclaw-tray-win-x64 |
x64 Tray app binaries | Testing, distribution |
openclaw-tray-win-arm64 |
ARM64 Tray app binaries | Testing, distribution |
openclaw-commandpalette-x64 |
x64 Command Palette MSIX | Testing, distribution |
openclaw-commandpalette-arm64 |
ARM64 Command Palette MSIX | Testing, distribution |
When a tag is pushed (e.g., git tag v1.2.3 && git push origin v1.2.3):
-
Build & Sign:
- All artifacts built for x64 and ARM64
- Executables signed with Azure Trusted Signing certificate
-
Create Installers:
- Inno Setup creates Windows installers
- Includes both Tray app and Command Palette extension
- Separate installers for x64 and ARM64
-
GitHub Release:
- Automatic release created with tag name
- Includes:
- Installers:
OpenClawTray-Setup-x64.exe,OpenClawTray-Setup-arm64.exe - Portable ZIPs:
OpenClawTray-{version}-win-x64.zip,OpenClawTray-{version}-win-arm64.zip
- Installers:
- Release notes auto-generated from commits
Check Latest Build:
gh run list --repo shanselman/openclaw-windows-hub --limit 5View Specific Run:
gh run view <run-id> --repo shanselman/openclaw-windows-hubDownload Artifacts:
gh run download <run-id> --repo shanselman/openclaw-windows-hub✅ Build Success:
- All projects compile without errors
- Both x64 and ARM64 builds succeed
- Dependencies restore correctly
✅ Unit Tests:
- All tests pass
- No test failures or skips
✅ Code Signing:
- Executables signed (on releases)
- Signature verification passes
❌ Not Currently Checked:
- Linting/code style (no linter configured)
- Integration tests (no integration test suite)
- Code coverage metrics (no coverage reporting)
-
Fork and Clone:
git clone https://github.com/YOUR_USERNAME/openclaw-windows-hub.git cd openclaw-windows-hub -
Create Feature Branch:
git checkout -b feature/my-new-feature
-
Make Changes:
- Follow existing code style and patterns
- Add tests for new functionality
- Update documentation as needed
-
Test Locally:
dotnet build dotnet test dotnet run --project src/OpenClaw.Tray.WinUI -
Commit and Push:
git add . git commit -m "Add my new feature" git push origin feature/my-new-feature
-
Open Pull Request:
- Go to GitHub and open a PR from your branch
- Describe your changes
- Wait for CI to pass
- Address review feedback
- C#: Follow standard .NET conventions
- XAML: Consistent indentation, organize resources logically
- Naming: Descriptive names, avoid abbreviations
- Comments: Explain "why", not "what"
- Error Handling: Use try-catch for expected failures, let unexpected exceptions bubble
Example: Adding a New Gateway Event Type
-
Add Model (
OpenClaw.Shared/Models.cs):public class MyNewEventData { public string Property { get; set; } = ""; }
-
Add Event (
OpenClaw.Shared/OpenClawGatewayClient.cs):public event EventHandler<MyNewEventData>? MyNewEvent;
-
Parse Event (
OpenClawGatewayClient.cs, inListenForMessagesAsync):if (eventType == "my_new_event") { var data = JsonSerializer.Deserialize<MyNewEventData>(json); MyNewEvent?.Invoke(this, data); }
-
Handle in Tray App (
OpenClaw.Tray.WinUI/App.xaml.cs):_gatewayClient.MyNewEvent += OnMyNewEvent; private void OnMyNewEvent(object? sender, MyNewEventData e) { _dispatcherQueue?.TryEnqueue(() => { // Update UI }); }
-
Add Tests (
tests/OpenClaw.Shared.Tests/):[Fact] public void MyNewEventData_DisplaysCorrectly() { var data = new MyNewEventData { Property = "test" }; Assert.Equal("test", data.Property); }
Common Issues:
-
Build Error: "Windows SDK not found"
- Install Windows 10 SDK 19041 or later
- Or build Shared library only:
dotnet build src/OpenClaw.Shared
-
Command Palette Extension Not Loading
- Verify correct architecture (x64 on x64, arm64 on ARM64)
- Check PowerToys version (latest recommended)
- View logs:
%LOCALAPPDATA%\Microsoft\PowerToys\CmdPal\Logs - Run "Reload" command in Command Palette after deploying
-
WebView2 Error 0x8007000B on ARM64
- Both Tray app AND Command Palette must be ARM64
- Rebuild:
dotnet build src/OpenClaw.Tray.WinUI -r win-arm64
-
Tray Icon Not Appearing
- Check Windows notification area settings
- Verify app is running (Task Manager)
- Check logs:
%LOCALAPPDATA%\OpenClawTray\openclaw-tray.log
-
Gateway Connection Fails
- Verify gateway is running:
curl http://localhost:18789/health - Check gateway URL in settings
- Verify authentication token is correct
- Check firewall settings
- Verify gateway is running:
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: OpenClaw Docs
Made with 🦞 love by Scott Hanselman and the OpenClaw community