Fix Command Palette extension and bundle in installer
Some checks failed
Build and Test / test (push) Has been cancelled
Build and Test / build (win-arm64) (push) Has been cancelled
Build and Test / build (win-x64) (push) Has been cancelled
Build and Test / build-extension (arm64) (push) Has been cancelled
Build and Test / build-extension (x64) (push) Has been cancelled
Build and Test / release (push) Has been cancelled
- Fix exe name mismatch: Moltbot.exe -> Moltbot.CommandPalette.exe in manifest - Add AppListEntry=none to hide from Start Menu (only show in Command Palette) - Use OpenUrlCommand from toolkit instead of custom DeepLinkCommand - Add cmdpal-dev.ps1 script for local iteration (auto-detects arm64/x64) - Update installer to optionally install Command Palette extension - Add molty2.png to README - Update CI to bundle Command Palette in installer
24
.github/workflows/ci.yml
vendored
@ -149,6 +149,18 @@ jobs:
|
||||
name: moltbot-tray-win-arm64
|
||||
path: artifacts/tray-win-arm64
|
||||
|
||||
- name: Download win-x64 cmdpal artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: moltbot-commandpalette-x64
|
||||
path: artifacts/cmdpal-x64
|
||||
|
||||
- name: Download win-arm64 cmdpal artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: moltbot-commandpalette-arm64
|
||||
path: artifacts/cmdpal-arm64
|
||||
|
||||
# Create ZIP files for Updatum auto-update (needs "win-x64" in filename)
|
||||
- name: Create Release ZIPs
|
||||
run: |
|
||||
@ -163,6 +175,18 @@ jobs:
|
||||
run: |
|
||||
mkdir publish
|
||||
copy artifacts/tray-win-x64/Moltbot.Tray.exe publish/
|
||||
# Copy Command Palette extension for installer (use x64 for installer)
|
||||
mkdir publish\cmdpal
|
||||
$cmdpalPath = Get-ChildItem -Path "artifacts/cmdpal-x64" -Recurse -Directory | Where-Object { $_.Name -like "win-x64" } | Select-Object -First 1
|
||||
if ($cmdpalPath) {
|
||||
Copy-Item "$($cmdpalPath.FullName)\*" -Destination publish\cmdpal -Recurse
|
||||
} else {
|
||||
# Fallback: copy from the first subfolder that has AppxManifest.xml
|
||||
$manifestFolder = Get-ChildItem -Path "artifacts/cmdpal-x64" -Recurse -Filter "AppxManifest.xml" | Select-Object -First 1
|
||||
if ($manifestFolder) {
|
||||
Copy-Item "$($manifestFolder.DirectoryName)\*" -Destination publish\cmdpal -Recurse
|
||||
}
|
||||
}
|
||||
|
||||
- name: Build Installer (x64)
|
||||
run: |
|
||||
|
||||
@ -6,6 +6,8 @@ A Windows companion suite for [Moltbot](https://moltbot.com) - the AI-powered pe
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Projects
|
||||
|
||||
This monorepo contains three projects:
|
||||
|
||||
BIN
docs/molty2.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
@ -29,10 +29,14 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
Name: "startupicon"; Description: "Start Moltbot Tray when Windows starts"; GroupDescription: "Startup:"; Flags: unchecked
|
||||
Name: "cmdpalette"; Description: "Install PowerToys Command Palette extension"; GroupDescription: "Integrations:"; Flags: unchecked
|
||||
|
||||
[Files]
|
||||
; Tray app
|
||||
Source: "publish\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "src\Moltbot.Tray\moltbot.ico"; DestDir: "{app}"; Flags: ignoreversion
|
||||
; Command Palette extension (all files from build output)
|
||||
Source: "publish\cmdpal\*"; DestDir: "{app}\CommandPalette"; Flags: ignoreversion recursesubdirs; Tasks: cmdpalette
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\moltbot.ico"
|
||||
@ -42,3 +46,9 @@ Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: st
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||
; Register Command Palette extension (silently, only if task selected)
|
||||
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -Command ""Add-AppxPackage -Register '{app}\CommandPalette\AppxManifest.xml' -ForceApplicationShutdown"""; Flags: runhidden; Tasks: cmdpalette
|
||||
|
||||
[UninstallRun]
|
||||
; Unregister Command Palette extension on uninstall
|
||||
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -Command ""Get-AppxPackage -Name '*Moltbot*' | Remove-AppxPackage"""; Flags: runhidden
|
||||
|
||||
|
Before Width: | Height: | Size: 732 B After Width: | Height: | Size: 454 B |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 898 B |
|
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 291 B |
|
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 474 B |
@ -16,13 +16,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="Assets\StoreLogo.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
DisplayName="Moltbot"
|
||||
Description="Moltbot"
|
||||
BackgroundColor="transparent"
|
||||
AppListEntry="none"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||
Square44x44Logo="Assets\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
|
||||
@ -46,7 +47,7 @@
|
||||
<Extensions>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer Executable="Moltbot.exe" Arguments="-RegisterProcessAsComServer" DisplayName="Moltbot">
|
||||
<com:ExeServer Executable="Moltbot.CommandPalette.exe" Arguments="-RegisterProcessAsComServer" DisplayName="Moltbot">
|
||||
<com:Class Id="b71e1e6d-20f4-4fd8-9d8e-cc5dc94ca8b5" DisplayName="Moltbot" />
|
||||
</com:ExeServer>
|
||||
</com:ComServer>
|
||||
|
||||
@ -2,13 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Moltbot.Shared;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@ -16,498 +9,37 @@ namespace Moltbot;
|
||||
|
||||
internal sealed partial class MoltbotPage : ListPage
|
||||
{
|
||||
private static string _gatewayUrl = "ws://localhost:18789";
|
||||
private static string _token = "";
|
||||
|
||||
public MoltbotPage()
|
||||
{
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
|
||||
Title = "Moltbot";
|
||||
Name = "Open";
|
||||
|
||||
// Try to load settings from tray app
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
private static void LoadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settingsPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"MoltbotTray", "settings.json");
|
||||
|
||||
if (File.Exists(settingsPath))
|
||||
{
|
||||
var json = File.ReadAllText(settingsPath);
|
||||
var settings = System.Text.Json.JsonDocument.Parse(json);
|
||||
|
||||
if (settings.RootElement.TryGetProperty("GatewayUrl", out var url))
|
||||
_gatewayUrl = url.GetString() ?? _gatewayUrl;
|
||||
if (settings.RootElement.TryGetProperty("Token", out var token))
|
||||
_token = token.GetString() ?? "";
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
var items = new List<IListItem>
|
||||
{
|
||||
new ListItem(new OpenDashboardCommand(_gatewayUrl, _token))
|
||||
return [
|
||||
new ListItem(new OpenUrlCommand("http://localhost:18789"))
|
||||
{
|
||||
Title = "🦞 Open Dashboard",
|
||||
Subtitle = "Open Moltbot web dashboard in browser"
|
||||
Subtitle = "Open Moltbot web dashboard"
|
||||
},
|
||||
new ListItem(new QuickSendCommand(_gatewayUrl, _token))
|
||||
new ListItem(new OpenUrlCommand("moltbot://chat"))
|
||||
{
|
||||
Title = "💬 Quick Send",
|
||||
Title = "💬 Web Chat",
|
||||
Subtitle = "Open the Moltbot chat window"
|
||||
},
|
||||
new ListItem(new OpenUrlCommand("moltbot://send"))
|
||||
{
|
||||
Title = "📝 Quick Send",
|
||||
Subtitle = "Send a message to Moltbot"
|
||||
},
|
||||
new ListItem(new StatusPage(_gatewayUrl, _token))
|
||||
new ListItem(new OpenUrlCommand("moltbot://settings"))
|
||||
{
|
||||
Title = "📊 Full Status",
|
||||
Subtitle = "View gateway, sessions, and channels"
|
||||
},
|
||||
new ListItem(new SessionsPage(_gatewayUrl, _token))
|
||||
{
|
||||
Title = "⚡ Sessions",
|
||||
Subtitle = "View active agent sessions"
|
||||
},
|
||||
new ListItem(new ChannelsPage(_gatewayUrl, _token))
|
||||
{
|
||||
Title = "📡 Channels",
|
||||
Subtitle = "View Telegram, WhatsApp status"
|
||||
},
|
||||
new ListItem(new HealthCheckCommand(_gatewayUrl, _token))
|
||||
{
|
||||
Title = "🔄 Health Check",
|
||||
Subtitle = "Run a gateway health check"
|
||||
Title = "⚙️ Settings",
|
||||
Subtitle = "Configure Moltbot Tray"
|
||||
}
|
||||
};
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command to open the Moltbot dashboard in the browser.
|
||||
/// </summary>
|
||||
internal sealed partial class OpenDashboardCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _gatewayUrl;
|
||||
private readonly string _token;
|
||||
|
||||
public OpenDashboardCommand(string gatewayUrl, string token)
|
||||
{
|
||||
_gatewayUrl = gatewayUrl;
|
||||
_token = token;
|
||||
}
|
||||
|
||||
public override ICommandResult Invoke()
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = GetDashboardUrl(_gatewayUrl, _token);
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(url)
|
||||
{
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
|
||||
return CommandResult.Hide();
|
||||
}
|
||||
|
||||
internal static string GetDashboardUrl(string gatewayUrl, string token)
|
||||
{
|
||||
var url = gatewayUrl
|
||||
.Replace("ws://", "http://")
|
||||
.Replace("wss://", "https://");
|
||||
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
var separator = url.Contains('?') ? "&" : "?";
|
||||
url = $"{url}{separator}token={Uri.EscapeDataString(token)}";
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command to send a quick message - prompts for input then sends.
|
||||
/// </summary>
|
||||
internal sealed partial class QuickSendCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _gatewayUrl;
|
||||
private readonly string _token;
|
||||
|
||||
public QuickSendCommand(string gatewayUrl, string token)
|
||||
{
|
||||
_gatewayUrl = gatewayUrl;
|
||||
_token = token;
|
||||
Name = "Send message to Moltbot";
|
||||
}
|
||||
|
||||
public override ICommandResult Invoke()
|
||||
{
|
||||
// Open a simple input dialog using Windows forms
|
||||
try
|
||||
{
|
||||
// Use the dashboard URL with a message prompt
|
||||
var url = OpenDashboardCommand.GetDashboardUrl(_gatewayUrl, _token);
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(url)
|
||||
{
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
|
||||
return CommandResult.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command to run a health check.
|
||||
/// </summary>
|
||||
internal sealed partial class HealthCheckCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _gatewayUrl;
|
||||
private readonly string _token;
|
||||
|
||||
public HealthCheckCommand(string gatewayUrl, string token)
|
||||
{
|
||||
_gatewayUrl = gatewayUrl;
|
||||
_token = token;
|
||||
}
|
||||
|
||||
public override ICommandResult Invoke()
|
||||
{
|
||||
// Just run the health check and show a toast/notification
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using var client = new MoltbotGatewayClient(_gatewayUrl, _token);
|
||||
await client.ConnectAsync();
|
||||
await client.CheckHealthAsync();
|
||||
await Task.Delay(1000);
|
||||
await client.DisconnectAsync();
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
|
||||
// Keep palette open - user can check status page
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Page showing active sessions.
|
||||
/// </summary>
|
||||
internal sealed partial class SessionsPage : ContentPage
|
||||
{
|
||||
private readonly string _gatewayUrl;
|
||||
private readonly string _token;
|
||||
|
||||
public SessionsPage(string gatewayUrl, string token)
|
||||
{
|
||||
_gatewayUrl = gatewayUrl;
|
||||
_token = token;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
|
||||
Title = "Sessions";
|
||||
Name = "View sessions";
|
||||
}
|
||||
|
||||
public override IContent[] GetContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("## ⚡ Active Sessions");
|
||||
sb.AppendLine();
|
||||
|
||||
try
|
||||
{
|
||||
using var client = new MoltbotGatewayClient(_gatewayUrl, _token);
|
||||
|
||||
var task = client.ConnectAsync();
|
||||
task.Wait(TimeSpan.FromSeconds(3));
|
||||
|
||||
if (!task.IsCompletedSuccessfully)
|
||||
{
|
||||
sb.AppendLine("❌ Could not connect to gateway");
|
||||
return [new MarkdownContent { Body = sb.ToString() }];
|
||||
}
|
||||
|
||||
client.RequestSessionsAsync().Wait(TimeSpan.FromSeconds(2));
|
||||
var sessions = client.GetSessionList();
|
||||
|
||||
if (sessions.Length == 0)
|
||||
{
|
||||
sb.AppendLine("_No active sessions_");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Group by main/sub
|
||||
var mainSessions = new List<SessionInfo>();
|
||||
var subSessions = new List<SessionInfo>();
|
||||
|
||||
foreach (var s in sessions)
|
||||
{
|
||||
if (s.IsMain) mainSessions.Add(s);
|
||||
else subSessions.Add(s);
|
||||
}
|
||||
|
||||
if (mainSessions.Count > 0)
|
||||
{
|
||||
sb.AppendLine("### ⚡ Main Sessions");
|
||||
foreach (var s in mainSessions)
|
||||
{
|
||||
sb.AppendLine($"- **{s.ShortKey}**");
|
||||
if (!string.IsNullOrEmpty(s.Model))
|
||||
sb.AppendLine($" - Model: `{s.Model}`");
|
||||
if (!string.IsNullOrEmpty(s.Channel))
|
||||
sb.AppendLine($" - Channel: {s.Channel}");
|
||||
if (s.StartedAt.HasValue)
|
||||
sb.AppendLine($" - Started: {s.StartedAt:g}");
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (subSessions.Count > 0)
|
||||
{
|
||||
sb.AppendLine($"### 🔹 Sub-Sessions ({subSessions.Count})");
|
||||
foreach (var s in subSessions)
|
||||
{
|
||||
var activity = !string.IsNullOrEmpty(s.CurrentActivity) ? $" - {s.CurrentActivity}" : "";
|
||||
sb.AppendLine($"- {s.ShortKey}{activity}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.DisconnectAsync().Wait(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
sb.AppendLine($"❌ Error: {ex.Message}");
|
||||
}
|
||||
|
||||
return [new MarkdownContent { Body = sb.ToString() }];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Page showing channel health.
|
||||
/// </summary>
|
||||
internal sealed partial class ChannelsPage : ContentPage
|
||||
{
|
||||
private readonly string _gatewayUrl;
|
||||
private readonly string _token;
|
||||
private ChannelHealth[]? _channels;
|
||||
|
||||
public ChannelsPage(string gatewayUrl, string token)
|
||||
{
|
||||
_gatewayUrl = gatewayUrl;
|
||||
_token = token;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
|
||||
Title = "Channels";
|
||||
Name = "View channels";
|
||||
}
|
||||
|
||||
public override IContent[] GetContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("## 📡 Channel Status");
|
||||
sb.AppendLine();
|
||||
|
||||
try
|
||||
{
|
||||
using var client = new MoltbotGatewayClient(_gatewayUrl, _token);
|
||||
|
||||
client.ChannelHealthUpdated += (s, channels) => _channels = channels;
|
||||
|
||||
var task = client.ConnectAsync();
|
||||
task.Wait(TimeSpan.FromSeconds(3));
|
||||
|
||||
if (!task.IsCompletedSuccessfully)
|
||||
{
|
||||
sb.AppendLine("❌ Could not connect to gateway");
|
||||
return [new MarkdownContent { Body = sb.ToString() }];
|
||||
}
|
||||
|
||||
// Health check fetches channel status
|
||||
client.CheckHealthAsync().Wait(TimeSpan.FromSeconds(2));
|
||||
|
||||
if (_channels == null || _channels.Length == 0)
|
||||
{
|
||||
sb.AppendLine("_No channels configured_");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var ch in _channels)
|
||||
{
|
||||
var statusIcon = ch.Status.ToLowerInvariant() switch
|
||||
{
|
||||
"running" or "ok" or "connected" => "🟢",
|
||||
"ready" => "🟡",
|
||||
"linked" => "🔵",
|
||||
"stopped" or "configured" => "⚪",
|
||||
"error" => "🔴",
|
||||
_ => "⚫"
|
||||
};
|
||||
|
||||
var name = char.ToUpper(ch.Name[0]) + ch.Name[1..];
|
||||
sb.AppendLine($"### {statusIcon} {name}");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"- **Status:** {ch.Status}");
|
||||
|
||||
if (ch.IsLinked)
|
||||
sb.AppendLine("- **Linked:** ✅ Yes");
|
||||
|
||||
if (!string.IsNullOrEmpty(ch.AuthAge))
|
||||
sb.AppendLine($"- **Auth Age:** {ch.AuthAge}");
|
||||
|
||||
if (!string.IsNullOrEmpty(ch.Error))
|
||||
sb.AppendLine($"- **Error:** ⚠️ {ch.Error}");
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
client.DisconnectAsync().Wait(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
sb.AppendLine($"❌ Error: {ex.Message}");
|
||||
}
|
||||
|
||||
return [new MarkdownContent { Body = sb.ToString() }];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Page showing full Moltbot status information.
|
||||
/// </summary>
|
||||
internal sealed partial class StatusPage : ContentPage
|
||||
{
|
||||
private readonly string _gatewayUrl;
|
||||
private readonly string _token;
|
||||
private ChannelHealth[]? _channels;
|
||||
|
||||
public StatusPage(string gatewayUrl, string token)
|
||||
{
|
||||
_gatewayUrl = gatewayUrl;
|
||||
_token = token;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
|
||||
Title = "Moltbot Status";
|
||||
Name = "View status";
|
||||
}
|
||||
|
||||
public override IContent[] GetContent()
|
||||
{
|
||||
var markdown = new MarkdownContent
|
||||
{
|
||||
Body = GetStatusMarkdown()
|
||||
};
|
||||
return [markdown];
|
||||
}
|
||||
|
||||
private string GetStatusMarkdown()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
try
|
||||
{
|
||||
using var client = new MoltbotGatewayClient(_gatewayUrl, _token);
|
||||
|
||||
client.ChannelHealthUpdated += (s, channels) => _channels = channels;
|
||||
|
||||
var task = client.ConnectAsync();
|
||||
task.Wait(TimeSpan.FromSeconds(3));
|
||||
|
||||
if (!task.IsCompletedSuccessfully)
|
||||
{
|
||||
return "## ❌ Disconnected\n\nCould not connect to gateway.\n\nMake sure Moltbot gateway is running.";
|
||||
}
|
||||
|
||||
sb.AppendLine("## 🦞 Moltbot Status");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("### Connection");
|
||||
sb.AppendLine($"- **Gateway:** `{_gatewayUrl}`");
|
||||
sb.AppendLine("- **Status:** ✅ Connected");
|
||||
sb.AppendLine();
|
||||
|
||||
// Get health and sessions
|
||||
client.CheckHealthAsync().Wait(TimeSpan.FromSeconds(2));
|
||||
client.RequestSessionsAsync().Wait(TimeSpan.FromSeconds(1));
|
||||
|
||||
var sessions = client.GetSessionList();
|
||||
|
||||
// Sessions
|
||||
sb.AppendLine("### ⚡ Sessions");
|
||||
if (sessions.Length == 0)
|
||||
{
|
||||
sb.AppendLine("_No active sessions_");
|
||||
}
|
||||
else
|
||||
{
|
||||
var mainCount = 0;
|
||||
var subCount = 0;
|
||||
foreach (var s in sessions)
|
||||
{
|
||||
if (s.IsMain) mainCount++;
|
||||
else subCount++;
|
||||
}
|
||||
sb.AppendLine($"- Main: **{mainCount}** | Sub: **{subCount}**");
|
||||
sb.AppendLine();
|
||||
|
||||
foreach (var s in sessions.Take(5))
|
||||
{
|
||||
var icon = s.IsMain ? "⚡" : "🔹";
|
||||
sb.AppendLine($"- {icon} {s.DisplayText}");
|
||||
}
|
||||
|
||||
if (sessions.Length > 5)
|
||||
sb.AppendLine($"- _...and {sessions.Length - 5} more_");
|
||||
}
|
||||
sb.AppendLine();
|
||||
|
||||
// Channels
|
||||
sb.AppendLine("### 📡 Channels");
|
||||
if (_channels == null || _channels.Length == 0)
|
||||
{
|
||||
sb.AppendLine("_No channels configured_");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var ch in _channels)
|
||||
{
|
||||
var statusIcon = ch.Status.ToLowerInvariant() switch
|
||||
{
|
||||
"running" or "ok" => "🟢",
|
||||
"ready" => "🟡",
|
||||
"linked" => "🔵",
|
||||
"stopped" => "⚪",
|
||||
"error" => "🔴",
|
||||
_ => "⚫"
|
||||
};
|
||||
var name = char.ToUpper(ch.Name[0]) + ch.Name[1..];
|
||||
sb.AppendLine($"- {statusIcon} **{name}:** {ch.Status}");
|
||||
}
|
||||
}
|
||||
|
||||
client.DisconnectAsync().Wait(TimeSpan.FromSeconds(1));
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("---");
|
||||
sb.AppendLine("_Use 🦞 Open Dashboard for the full web interface_");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"## ❌ Error\n\n{ex.Message}\n\nMake sure the gateway is running and your settings are correct.";
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
81
tools/cmdpal-dev.ps1
Normal file
@ -0,0 +1,81 @@
|
||||
# Command Palette Development Script
|
||||
# Usage: .\tools\cmdpal-dev.ps1 [build|deploy|remove|cycle]
|
||||
|
||||
param(
|
||||
[Parameter(Position=0)]
|
||||
[ValidateSet('build', 'deploy', 'remove', 'cycle', 'status')]
|
||||
[string]$Action = 'cycle'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$RepoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$ProjectPath = "$RepoRoot\src\Moltbot.CommandPalette"
|
||||
|
||||
# Detect architecture
|
||||
$Arch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') { 'arm64' } else { 'x64' }
|
||||
$OutputPath = "$ProjectPath\bin\$Arch\Debug\net10.0-windows10.0.26100.0\win-$Arch"
|
||||
|
||||
function Write-Step($msg) { Write-Host "`n>> $msg" -ForegroundColor Cyan }
|
||||
|
||||
function Get-InstalledPackage {
|
||||
Get-AppxPackage -Name "*Moltbot*" 2>$null
|
||||
}
|
||||
|
||||
function Remove-ExtensionPackage {
|
||||
$pkg = Get-InstalledPackage
|
||||
if ($pkg) {
|
||||
Write-Step "Removing $($pkg.PackageFullName)..."
|
||||
Remove-AppxPackage -Package $pkg.PackageFullName
|
||||
Write-Host "Removed!" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "Not installed" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
function Build-Extension {
|
||||
Write-Step "Building Command Palette ($Arch)..."
|
||||
Push-Location $RepoRoot
|
||||
dotnet build src/Moltbot.CommandPalette -c Debug -p:Platform=$Arch
|
||||
if ($LASTEXITCODE -ne 0) { throw "Build failed" }
|
||||
Pop-Location
|
||||
Write-Host "Build succeeded!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Deploy-Extension {
|
||||
Write-Step "Deploying extension..."
|
||||
|
||||
$manifest = "$OutputPath\AppxManifest.xml"
|
||||
if (-not (Test-Path $manifest)) {
|
||||
throw "AppxManifest.xml not found at $manifest - did you build first?"
|
||||
}
|
||||
|
||||
# Register for development (loose files, no MSIX needed)
|
||||
Add-AppxPackage -Register $manifest -ForceApplicationShutdown
|
||||
Write-Host "Deployed! Open Command Palette, type 'Reload', then 'Moltbot'" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Show-Status {
|
||||
Write-Step "Extension Status (Platform: $Arch)"
|
||||
$pkg = Get-InstalledPackage
|
||||
if ($pkg) {
|
||||
Write-Host "Installed: $($pkg.PackageFullName)" -ForegroundColor Green
|
||||
Write-Host "Version: $($pkg.Version)"
|
||||
Write-Host "Location: $($pkg.InstallLocation)"
|
||||
} else {
|
||||
Write-Host "Not installed" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
# Main
|
||||
switch ($Action) {
|
||||
'build' { Build-Extension }
|
||||
'deploy' { Deploy-Extension }
|
||||
'remove' { Remove-ExtensionPackage }
|
||||
'status' { Show-Status }
|
||||
'cycle' {
|
||||
Remove-ExtensionPackage
|
||||
Build-Extension
|
||||
Deploy-Extension
|
||||
Write-Host "`n=== In Command Palette: type 'Reload' then 'Moltbot' ===" -ForegroundColor Magenta
|
||||
}
|
||||
}
|
||||
47
tools/icongen/Program.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
|
||||
var sizes = new[] { (24, "Square44x44Logo.targetsize-24_altform-unplated.png"),
|
||||
(88, "Square44x44Logo.scale-200.png"),
|
||||
(50, "StoreLogo.png"),
|
||||
(300, "Square150x150Logo.scale-200.png"),
|
||||
(48, "LockScreenLogo.scale-200.png") };
|
||||
|
||||
var assetsPath = args[0];
|
||||
|
||||
foreach (var (size, name) in sizes)
|
||||
{
|
||||
using var bmp = CreateLobster(size);
|
||||
bmp.Save(Path.Combine(assetsPath, name), ImageFormat.Png);
|
||||
Console.WriteLine("Created " + name);
|
||||
}
|
||||
|
||||
static Bitmap CreateLobster(int size)
|
||||
{
|
||||
var bmp = new Bitmap(size, size, PixelFormat.Format32bppArgb);
|
||||
using var g = Graphics.FromImage(bmp);
|
||||
g.Clear(Color.Transparent);
|
||||
float s = size / 16f;
|
||||
|
||||
var body = Color.FromArgb(255, 255, 79, 64);
|
||||
var claw = Color.FromArgb(255, 255, 119, 95);
|
||||
var outline = Color.FromArgb(255, 58, 10, 13);
|
||||
var eyeW = Color.FromArgb(255, 245, 251, 255);
|
||||
var eyeB = Color.FromArgb(255, 8, 16, 22);
|
||||
|
||||
void P(int x, int y, Color c) { using var b = new SolidBrush(c); g.FillRectangle(b, (int)(x*s), (int)(y*s), (int)Math.Ceiling(s), (int)Math.Ceiling(s)); }
|
||||
|
||||
int[][] bd = {new[]{5,3},new[]{6,3},new[]{7,3},new[]{8,3},new[]{9,3},new[]{10,3},new[]{4,4},new[]{5,4},new[]{7,4},new[]{8,4},new[]{10,4},new[]{11,4},new[]{3,5},new[]{4,5},new[]{5,5},new[]{7,5},new[]{8,5},new[]{10,5},new[]{11,5},new[]{12,5},new[]{3,6},new[]{4,6},new[]{5,6},new[]{6,6},new[]{7,6},new[]{8,6},new[]{9,6},new[]{10,6},new[]{11,6},new[]{12,6},new[]{3,7},new[]{4,7},new[]{5,7},new[]{6,7},new[]{7,7},new[]{8,7},new[]{9,7},new[]{10,7},new[]{11,7},new[]{12,7},new[]{4,8},new[]{5,8},new[]{6,8},new[]{7,8},new[]{8,8},new[]{9,8},new[]{10,8},new[]{11,8},new[]{5,9},new[]{6,9},new[]{7,9},new[]{8,9},new[]{9,9},new[]{10,9},new[]{5,12},new[]{6,12},new[]{7,12},new[]{8,12},new[]{9,12},new[]{10,12},new[]{6,13},new[]{7,13},new[]{8,13},new[]{9,13}};
|
||||
foreach (var p in bd) P(p[0], p[1], body);
|
||||
|
||||
int[][] cl = {new[]{1,6},new[]{2,5},new[]{2,6},new[]{2,7},new[]{13,5},new[]{13,6},new[]{13,7},new[]{14,6}};
|
||||
foreach (var p in cl) P(p[0], p[1], claw);
|
||||
|
||||
int[][] ol = {new[]{1,5},new[]{1,7},new[]{2,4},new[]{2,8},new[]{3,3},new[]{3,9},new[]{4,2},new[]{4,10},new[]{5,2},new[]{6,2},new[]{7,2},new[]{8,2},new[]{9,2},new[]{10,2},new[]{11,2},new[]{12,3},new[]{12,9},new[]{13,4},new[]{13,8},new[]{14,5},new[]{14,7},new[]{5,11},new[]{6,11},new[]{7,11},new[]{8,11},new[]{9,11},new[]{10,11},new[]{4,12},new[]{11,12},new[]{3,13},new[]{12,13},new[]{5,14},new[]{6,14},new[]{7,14},new[]{8,14},new[]{9,14},new[]{10,14}};
|
||||
foreach (var p in ol) P(p[0], p[1], outline);
|
||||
|
||||
P(6,4,eyeW); P(9,4,eyeW); P(6,5,eyeB); P(9,5,eyeB);
|
||||
return bmp;
|
||||
}
|
||||
14
tools/icongen/icongen.csproj
Normal file
@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Drawing.Common" Version="10.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||