Skip to content

Release: UI improvements, deb packaging, MSIX pipeline fixes#17

Open
sharpninja wants to merge 94 commits intomainfrom
develop
Open

Release: UI improvements, deb packaging, MSIX pipeline fixes#17
sharpninja wants to merge 94 commits intomainfrom
develop

Conversation

@sharpninja
Copy link
Owner

Summary

This PR merges all recent develop work into main.

Changes

UI (RemoteAgent.Desktop)

  • Extracted all 13 navigation panels to dedicated UserControls with IsVisible bindings
  • Added FindControlDeep<T> test helper for cross-NameScope control lookup
  • Added Copy as Markdown (📋) and Open Logs Folder (📁) buttons to status log viewer
  • Added Open Logs Folder button to AppLog panel
  • All status log / AppLog buttons are icon-only using Segoe MDL2 glyphs
  • Windows: use Segoe MDL2 Assets for icons and Segoe UI as base font

Service (RemoteAgent.Service)

  • Fixed Windows port binding: replaced UseUrls() with ConfigureKestrel(ListenAnyIP) so explicit Kestrel config overrides ASPNETCORE_URLS from launchSettings.json
  • Windows service listens on port 5244, Linux on 5243
  • Improved startup logging: logs actual bound URLs on start

CI Pipeline

  • Fixed Build MSIX: upgraded gittools/actions/gitversion/setup and execute from @v1@v3 (supports GitVersion 6.x)
  • Added build-deb job (ubuntu-latest): builds remote-agent-service and remote-agent-desktop .deb packages via scripts/package-deb.sh
  • Added release job: runs when both MSIX and .deb builds succeed on push; creates a GitHub Release with both artifacts attached (pre-release on non-main branches)

CQRS compliance

  • All new UI buttons wired via CQRS handlers with full unit + UI tests
  • 164/164 desktop UI tests pass

Scripts

  • Added scripts/remote-agent-ctl.sh, scripts/monitor-service-windows.ps1, scripts/bump-patch-version.sh
  • package-deb.sh: installs remote-agent-ctl management script into the service .deb

Test Evidence

All tests pass:

  • ./scripts/build-desktop-dotnet9.sh Release — 164/164 tests ✅
  • ./scripts/build-dotnet10.sh Release — 27/27 service tests ✅

sharpninja and others added 30 commits February 18, 2026 17:57
- MobileHandlerTests.cs: tests for all 8 mobile CQRS handlers
  (ConnectMobileSession, DisconnectMobileSession, CreateMobileSession,
  TerminateMobileSession, SendMobileMessage, SendMobileAttachment,
  ArchiveMobileMessage, UsePromptTemplate)
- Fix CS0104 ambiguity in McpRegistryPageViewModelTests: use
  LogicRequests alias to disambiguate DeleteMcpServerRequest vs
  RemoteAgent.Proto.DeleteMcpServerRequest
- Fix CS8601 in ConnectMobileSessionHandler: null-coalesce host
  assignment (workspace.Host = host ?? "")
- Fix ConnectMobileSessionHandler: remove NotifyConnectionStateChanged
  from catch block to prevent status overwrite on connection failure
- Fix xUnit1031: make Disconnect test async, await handler result

App.Tests: 67 -> 89 (+22)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mplete

- requirements-completion-summary.md: FR-12.12 Pending → Done
  (AppLoggerProvider, InMemoryAppLogStore, AppLogViewModel,
  ClearAppLogHandler, SaveAppLogHandler all implemented)
- requirements-completion-summary.md: TR-18.4 test count 218 → 240
  (89 App.Tests + 151 Desktop.UiTests)
- implementation-plan-todo.md: mark all 318 remaining items [x]
  (all phases 0–4 are complete)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Split DocFX metadata into 4 entries (net10.0 src, net9.0 Desktop, net10.0 tests, net9.0 Desktop.UiTests)
- Added docs/api/index.md, docs/api-tests/index.md landing pages
- Added docs/filterConfig.yml (exclude System/Microsoft namespaces)
- Updated docs/toc.yml with API Reference and Testing API Reference sections
- Added /// <summary> + [Trait] attributes to all Desktop.UiTests handler tests (32 files)
- Added /// <summary> + [Trait] to App.Tests test classes (7 files)
- Added /// <summary> to SharedHandlerTestStubs.cs, InMemoryServerRegistrationStore.cs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rewrote README.md: full architecture overview, CQRS pattern, sub-VM
  decomposition, Management App Log, project structure, protocol tables,
  build scripts, test counts (240), CI/CD, F-Droid install, contributing
- Fixed broken /README.md link in implementing-cli-agents.md (use GitHub URL)
- Fixed broken ../SESSION_HANDOFF link in implementation-plan-mvvm-cqrs-refactor.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add namespace stub YAMLs for intermediate namespaces that DocFX does
not auto-generate (no types directly in them). Stubs added to both
api/ and api-tests/ so relative breadcrumb links resolve correctly
in each section.

api/: RemoteAgent, RemoteAgent.App, RemoteAgent.Plugins (3 files)
api-tests/: RemoteAgent, RemoteAgent.App, RemoteAgent.Desktop,
            RemoteAgent.Service (4 files)

Site now has 422 models and 0 broken links (was 505 broken).
DuplicateUids warnings (5) are non-fatal and expected for shared
namespace names across api/ and api-tests/ sections.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Initial plan

* Add requirements traceability matrix generation script

Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com>

* Add FR/TR annotations to test classes and fix script AWK logic

Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com>

* Fix requirements matrix generation script to find all test files

Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com>

* Add requirements traceability documentation to CONTRIBUTING.md

Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com>
…on bump script

- Use SemVer (not MajorMinorPatch) from GitVersion in build-msix workflow
  so pre-release tags (e.g. -develop.1) appear in the MSIX filename
- Set next-version to 0.1.0 in GitVersion.yml
- package-msix.ps1: default SelfContained to true, add PublishSingleFile
  and IncludeNativeLibrariesForSelfExtract flags for single-file builds
- package-msix.ps1: fall back to next-version from GitVersion.yml instead
  of hardcoded 1.0.0 when no -Version param, local tool, or git tag found
- Add scripts/bump-minor-version.sh to increment minor version in GitVersion.yml
- Desktop csproj: add RollForward=LatestPatch so installed 9.0.x patch
  runtime is accepted without requiring exact 9.0.0
- build-msix.ps1: display generated MSIX path on completion

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
git describe was finding v1.0.0-* tags and the regex was stripping
the pre-release suffix, producing 1.0.0. GitVersion.yml is the
authoritative source and should be checked first.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…st content

Adds a review step after AppxManifest.xml is written showing:
- SemVer and MSIX Identity version used
- Full manifest content

Pauses for confirmation unless -Force is passed (CI always uses -Force).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uses Get-AuthenticodeSignature to check whether the package is signed
before calling Add-AppxPackage, so unsigned dev builds install without
needing manual -AllowUnsigned.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…igned

-AllowUnsigned requires Developer Mode on the machine (0x80073D2C).
The install script now:
- Detects unsigned packages via Get-AuthenticodeSignature
- Locates signtool.exe from the Windows SDK
- Creates or reuses a self-signed cert (CN=RemoteAgent Dev)
- Trusts the cert in Cert:\LocalMachine\Root
- Signs the MSIX in-place before calling Add-AppxPackage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Runs dotnet clean on each project before publishing when -Clean is
specified. Cleans service, Ollama plugin, and desktop projects
according to the -ServiceOnly/-DesktopOnly flags.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After StartSessionAsync succeeds, set SelectedManagementSection to
'Sessions' so the chat view is shown instead of leaving the Server
Setup panel visible and overlapping the session view.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…failure

Add-AppxPackage -ForceUpdateFromAnyVersion fails if RemoteAgentService
is running when the update is applied. Stop the service first so the
MSIX engine can replace the service binary.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dotnet clean removes obj/ asset files so the implicit restore inside
dotnet publish cannot resolve packages. Add explicit dotnet restore
with the NuGet config after cleaning each project.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dotnet clean cannot use --configfile so it fails to resolve package
targets when globalPackagesFolder is repo-relative (.nuget/packages).
Directly removing bin/ and obj/ directories is simpler and reliable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Calls install-remote-agent.ps1 after packaging completes.
Passes -MsixPath and -CertPath (if a dev cert was generated).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove-Item -Recurse -Force can fail with 'directory not empty'
on Windows for certain nested paths. System.IO.Directory::Delete
with recursive=true is more reliable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Increments the corresponding semver component in GitVersion.yml
next-version before the build runs, so the new version is picked
up immediately by the version-detection fallback chain.

The three flags are mutually exclusive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- scripts/MsixTools/MsixTools.psm1 — reusable module with four
  exported functions: Read-MsixConfig, New-MsixPackage,
  Install-MsixPackage, Uninstall-MsixPackage
- scripts/MsixTools/MsixTools.psd1 — module manifest (PS 7.0+)
- msix.yml — workspace-level YAML config (package, service, desktop,
  plugins, build, output, icons sections)
- package-msix.ps1 — now a thin wrapper; bump/validate logic stays
  here, build logic delegated to New-MsixPackage
- install-remote-agent.ps1 — now a thin wrapper around
  Install-MsixPackage / Uninstall-MsixPackage

Fixes 0x80073CFB: Install-MsixPackage now removes any existing package
before Add-AppxPackage instead of using -ForceUpdateFromAnyVersion,
which blocks same-version reinstalls with changed contents.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
System.Drawing.Bitmap.Save() is a .NET method that ignores
PowerShell's working directory and fails with relative paths.
Resolving OutDir to an absolute path early ensures all derived
paths (msix-layout/Assets/*) are also absolute.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
[System.IO.Path]::GetFullPath() resolves relative to .NET's CWD
which can differ from PowerShell's $PWD. Using PS's own session-state
path resolver ensures WorkspaceRoot (and all derived paths: OutDir,
project paths, icons) are always absolute, fixing Bitmap.Save failure
when -WorkspaceRoot . is passed.

After pulling, reload the module before calling directly:
  Import-Module .\scripts\MsixTools\MsixTools.psd1 -Force

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sharpninja and others added 3 commits February 19, 2026 19:20
SetPairingUsers RPC now generates a cryptographically random 32-byte
API key, saves it as Agent.ApiKey in appsettings.json alongside the
pairing user, and returns it in SetPairingUsersResponse.GeneratedApiKey.

The desktop handler applies the returned key to Workspace.ApiKey so
subsequent gRPC calls (capacity check, session creation) use it
immediately without requiring a manual reconnect.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…leSessionHandler

Without this, a service configured with Agent:ApiKey returns 401 on
GetServerInfoAsync and GetSessionCapacityAsync, causing the mobile app
to show 'Could not verify server session capacity'.

Added regression test that asserts the key reaches both API calls.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
page.Navigation.PushModalAsync on a ContentPage inside a MAUI Shell can
silently no-op on Android when the page isn't the active navigation host.
Switch to Shell.Current.Navigation which always routes through the Shell's
own navigation controller.  Also wrap in MainThread.InvokeOnMainThreadAsync
because Permissions.RequestAsync may resume on a thread-pool thread.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sharpninja and others added 2 commits February 19, 2026 19:58
… UI; persist ApiKey on connect

- Replace all TextBlock with SelectableTextBlock in all 16 Avalonia .axaml files
  so users can copy text from any label in the desktop app.

- Simplify mobile connection card to two buttons:
  * 'Scan QR to Pair' — disabled when ApiKey is already stored
  * 'Connect' — disabled until ApiKey is stored (requires QR scan first)
  Remove New Session / Terminate Session / extra Connect buttons from pre-connect view.

- Add HasApiKey property to MainPageViewModel; update CanExecute for both commands.

- ConnectMobileSessionHandler now persists ApiKey alongside Host/Port on
  successful connect so server details survive app restart as a complete set.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nia app

Single style rule in App.axaml targeting Button, TextBox, ComboBox, CheckBox,
ListBox, SelectableTextBlock, and TabControl so every control gets consistent
4px spacing without touching individual XAML files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The CDN qrcode.js script was blocked/unavailable so no QR was shown.
Switch to QRCoder (server-side) which generates the QR as a PNG and
embeds it as a data:image/png;base64 <img> tag — no external requests
needed, works in air-gapped / firewalled environments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename 'Scan QR to Pair' button to 'Login'
- Host setter calls ChangeCanExecute on ScanQrCodeCommand
- IQrCodeScanner.ScanAsync() now accepts loginUrl parameter
- ScanQrCodeHandler builds http://{host}:1{port}/pair URL and passes to scanner; validates host not empty
- MauiQrCodeScanner opens PairLoginPage (WebView) instead of camera; no camera permission needed
- PairLoginPage: WebView loads /pair login form; on navigate to /pair/key, JS extracts deep link from <a class='btn'> href; closes modal with result
- Remove QrScannerPage (camera scanner) and ZXing.Net.Maui.Controls dependency
- Add tests: HandleAsync_NoHost_ReturnsFail, HandleAsync_BuildsLoginUrlFromHostPort
- All 138 tests pass (111 App + 27 Service)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t, and desktop UX

- FR-17: Device pairing flow (admin sets credentials, service generates API key, web login, deep link extraction)
- FR-18: Desktop UX refinements (SelectableTextBlock, 4px margin)
- TR-19: Pairing infrastructure (SetPairingUsers RPC, IOptionsMonitor, dual-port, QRCoder, PairLoginPage WebView, deep-link format)
- TR-20: Desktop UX technical (SelectableTextBlock replacement, global style)
- Updated completion summary: 80 FR + 124 TR, all Done

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sharpninja and others added 8 commits February 19, 2026 21:28
Remove IConnectionModeSelector interface, MauiConnectionModeSelector class,
and all direct-mode code paths. Android always connects in server mode.
Remove ConnectionModeLabel from UI and associated ViewModel property.
Update all tests to reflect removal of mode selection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace REST call to /api/sessions/capacity with gRPC CheckSessionCapacity.
Remove unused CreateHttpFailure, TryReadErrorDetailAsync, JsonOptions,
and System.Net.Http.Json / System.Text.Json imports. All ServerApiClient
methods now use gRPC exclusively.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add IsConnected and ConnectionStateChanged to ISessionCommandBus.
AppShellViewModel exposes IsConnected, subscribes to state changes.
Flyout items (sessions, Start Session, Settings, Account) are hidden
until connected. MCP Registry tab hidden until connected.
Settings and Account pages also hidden from tab bar until connected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The host/port/connect/disconnect card in the chat workspace is now
hidden once connected, giving more space to the chat messages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create ServerProfile model + IServerProfileStore in App.Logic (shared)
- LiteDB-backed LocalServerProfileStore for mobile (CRUD by host:port)
- ConnectMobileSessionHandler auto-saves profile on successful connect
- SettingsPage rewritten with server profile list + edit form
- SettingsPageViewModel with save/delete commands
- Desktop ServerRegistration gains PerRequestContext + DefaultSessionContext
- Desktop ServerSetupPanel shows per-request/session context fields
- SaveServerRegistrationRequest/Handler persist new fields
- ServerWorkspaceViewModel loads PerRequestContext from registration
- All 310 tests pass (110 App + 27 Service + 173 Desktop)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Android has no Ctrl+Enter, so intercept the hardware Enter key on the
native AppCompatEditText to dispatch SendMessageCommand immediately.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- FR-19: Server profiles with persistent connection settings per host:port
- FR-20: Mobile chat UX (Enter sends, server-only mode, nav hidden until connected, gRPC-only)
- TR-21: Implementation details for server profile storage (LiteDB, shared model, auto-save)
- TR-22: Implementation details for mobile Enter key, connection mode, visibility, gRPC migration
- Updated FR-2.5/2.6 and TR-5.7 to reflect platform-specific Enter behavior
- Updated requirements-test-coverage.md with FR-17..20 and TR-19..22 entries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use EditorAction + ImeAction.Send instead of KeyPress (soft keyboard
  does not fire KeyPress events on Android)
- Send button now uses FontImageSource icon instead of text
- Reduced global Button padding from 24,12 to 12,6 and min height
  from 48 to 36 for a more compact mobile UI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sharpninja and others added 5 commits February 19, 2026 23:11
- Add SaveServerProfileRequest/Handler for persisting profile edits
- Add DeleteServerProfileRequest/Handler for removing profiles
- Add ClearServerApiKeyRequest/Handler to clear stored API keys
- Refactor SettingsPageViewModel to dispatch all commands via IRequestDispatcher
- Add API key status display and Clear API Key button to SettingsPage
- Register all three handlers in MauiProgram.cs DI
- Add ServerProfileHandlerTests with 7 test cases (save, delete, clear key)
- Compact mobile chat UI: remove icon label, shrink session tabs/buttons
- Reduce toolbar to Connect/Disconnect only (session controls in card)
- Default port changed to 5243 (Linux/Docker)
- Update PortPickerViewModelTests to match new default

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaced the default .NET text logo (splash.svg) with the app's own
icon foreground (appiconfg.svg) so the Android splash screen shows the
Remote Agent logo instead of the generic .NET branding.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SetRawInputType(InputTypes.ClassText) overrides the IME input type
so Android shows a Send button instead of Enter/newline for the
multi-line Editor control. The EditorAction handler fires on tap.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants