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
11 changes: 6 additions & 5 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ new attempt. This boundary does not change release signing or Windows policy.
| Trusted Store and existing development Gateway registration identities | `NativeGatewayPackageIdentity` | authoritative |
| Current-user Gateway package registration, health and package-qualified alias discovery | `NativeGatewayPackageResolver` | authoritative |
| Prospective Companion-to-native-Gateway state/config/workspace path mapping | `NativeGatewayDataPathMapper`; `NativeGatewayPackageResolver` supplies package and known-folder context. Independent of existing-log resolution | authoritative |
| Missing-package acquisition, one installation attempt and bounded registration verification | `NativeGatewayPackageAcquisition` | authoritative |
| Store package install/update, one acquisition attempt and bounded registration verification | `NativeGatewayPackageAcquisition` | authoritative |
| Shared Windows capability and permission selection, with runtime-specific install review | `CapabilitiesPage` | authoritative |
| Native profile draft creation and canonical state/config launch paths | `NativeGatewaySetupService` + `NativeGatewayPaths` | authoritative |
| Native onboarding capability admission and default/remembered gateway choice policy | `NativeGatewaySetupEligibility`, consuming `MxcAvailability` session probe metadata | authoritative |
Expand Down Expand Up @@ -227,6 +227,7 @@ new attempt. This boundary does not change release signing or Windows policy.
| Post-save settings change effect ordering, detached snapshot comparison, and concurrent save serialization | `SettingsChangeCoordinator` | authoritative |
| Exactly-once ordered app shutdown sequencing | `AppShutdownCoordinator` | authoritative |
| App composition-root startup sequencing | `AppBootstrapper` (planned) | planned |
| Packaged uninstall cleanup, preservation recovery and removed-target reconciliation | `GatewayPackageCleanup`; App only invokes its startup gate and presents recovery. Legacy name/path markers never grant unattended deletion authority; malformed receipts/settings retain pending evidence and fail closed | authoritative |
| Inno migration records, preparation, and pre-start completion guard | `MigrationRecordCodec`, `MigrationPreparation`, `MigrationInventory`, `InnoMigrationStartupGuard` | authoritative |
| Cross-session migration exclusion and source activity inspection | `MigrationOperationLock`, `InnoSourceActivityVerifier` (App only retains the runtime handle through process exit) | authoritative |
| Store migration startup admission and finalization | `InnoInstallationDetector`, `InnoSourceRemovalVerifier`, `MigrationStartupRecordReader`, `MigrationInventoryCapture`, `StoreMigrationStartupCoordinator`, `StoreMigrationFinalizationCoordinator`, `StoreMigrationStartupGuard` | authoritative |
Expand Down Expand Up @@ -283,7 +284,7 @@ classifies these unavailable probes as network failures.
`NativeGatewayMsixInstaller.InstallAsync` invokes the signed-in user's App Installer
alias (`%LOCALAPPDATA%\Microsoft\WindowsApps\winget.exe`) through the existing
`CommandRunner`, without a shell or elevation. The fixed command is
`install --id 9NV70LV3D6XC --source msstore --silent --accept-package-agreements --accept-source-agreements --disable-interactivity --no-upgrade`.
`install --id 9NV70LV3D6XC --source msstore --silent --accept-package-agreements --accept-source-agreements --disable-interactivity`.
The native review explains that selecting **Set up gateway** authorizes installation
and accepts the package and Store source agreements. Microsoft Store still owns
architecture/package selection, signature validation and deployment. There is no
Expand Down Expand Up @@ -342,9 +343,9 @@ unset, selection, resolution and aliases are identical to the Store path.

After native capability/permission review, `NativeGatewaySetupPage` starts
automatically. It rechecks device support, then calls
`NativeGatewayPackageAcquisition.EnsureAsync`. Only the typed
`NativeGatewayPackageNotInstalledException` starts WinGet installation, once per attempt.
Healthy registration skips installation; duplicate registration, unhealthy packages
`NativeGatewayPackageAcquisition.EnsureAsync`. Missing packages and existing Store
packages start WinGet install/update once per attempt; development packages skip it.
Duplicate registration, unhealthy packages
and missing aliases fail explicitly instead of triggering reinstall loops.
The cancellable acquisition deadline is five minutes including installation and
registration verification, with one-second polling after WinGet completes.
Expand Down
12 changes: 6 additions & 6 deletions docs/ONBOARDING_WIZARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,15 @@ from `PATH`. Native setup uses the same Windows capabilities and permission
selection as WSL, with native package installation consent on that same page.
Selecting **Set up gateway** starts native progress without WSL, Local AI or
Tailscale provisioning and without an additional review page.
A missing package is installed automatically using
`winget install --id 9NV70LV3D6XC --source msstore --silent --accept-package-agreements --accept-source-agreements --disable-interactivity --no-upgrade`;
A Store package is installed or refreshed automatically using
`winget install --id 9NV70LV3D6XC --source msstore --silent --accept-package-agreements --accept-source-agreements --disable-interactivity`;
unhealthy registration or unavailable aliases show an explicit repair error.
The fixed product is [OpenClaw Gateway in Microsoft Store](https://apps.microsoft.com/detail/9NV70LV3D6XC).
No MSIX version or download URL is pinned; Store selects the current compatible
release. Existing healthy installations are not silently upgraded during setup.
release. Native setup checks for available Store updates before reusing a healthy Store package. Existing development profiles keep their package identity.
To explicitly refresh one, run
`winget install --id 9NV70LV3D6XC --source msstore --accept-package-agreements --accept-source-agreements`
without `--no-upgrade`. WinGet may report that no newer package is available.
WinGet reporting that no newer package is available is a successful no-op.

An unfinished draft saved against a different package family (for example, the
older development Gateway) offers **Discard and set up again**, just like a
Expand Down Expand Up @@ -241,8 +241,8 @@ runs one current-user `Repair-WinGetPackageManager -Force -Latest` bootstrap and
The repair installs Microsoft's WinGet client module; it never bypasses certificate validation.
Other failures show explicit repair guidance and **Retry setup**. The normal
path has no separate install, availability-check or wizard-launch buttons.
An already installed healthy package skips installation. Retry checks registration
again before invoking WinGet. Companion does not download Gateway packages directly,
An already installed healthy development/patch package skips installation. Store packages are checked for updates.
Retry checks registration again before invoking WinGet. Companion does not download Gateway packages directly,
change certificate trust, or uninstall packages on cancellation.

Progress reuses the WSL spinner/checkmark rows. Normal completion uses the same
Expand Down
6 changes: 3 additions & 3 deletions docs/SETUP_ENGINE_REDESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ the owned `NativeGatewaySetupConnection` authorization and upstream
checks Windows package registration and package-qualified aliases.
`NativeGatewayMsixInstaller` installs the fixed Store product using the current-user
App Installer alias and `CommandRunner`:
`winget install --id 9NV70LV3D6XC --source msstore --silent --accept-package-agreements --accept-source-agreements --disable-interactivity --no-upgrade`.
`winget install --id 9NV70LV3D6XC --source msstore --silent --accept-package-agreements --accept-source-agreements --disable-interactivity`.
The shared capabilities page shows the native installation and agreement
disclosure before its **Set up gateway** action; other routes retain **Next**.
This preserves consent without adding a separate native review step.
Expand All @@ -129,8 +129,8 @@ diagnostics and retry guidance instead of opening a manual Store page.
the original development identity for existing installations. Multiple matching
registrations fail explicitly for new setup. Existing runtime profiles resolve only
their saved family, allowing both packages to coexist without an implicit migration.
`NativeGatewayPackageAcquisition` invokes installation once only for missing
registration. Installation and verified package readiness share a five-minute
`NativeGatewayPackageAcquisition` invokes acquisition once for a missing or existing Store
package; an existing development package is reused without Store migration. Installation and verified package readiness share a five-minute
deadline. Cancellation stops the WinGet request, but Windows deployment may
continue. Repair errors and timeouts stay visible, with explicit retry rather
than repeated installer launches; retries recheck registration before installing.
Expand Down
31 changes: 27 additions & 4 deletions docs/uninstall-msix.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,39 @@

---

## MSIX Cannot Auto-Clean WSL State
## Automatic cleanup for managed Gateways

**Feasibility verdict:** `runFullTrust` MSIX packages do **not** support a
`CustomInstall` / `CustomUninstall` extension that runs an arbitrary EXE
at uninstall time. The supported extension points
(`windows.startupTask`, `windows.appExecutionAlias`, `com.extension`,
`windows.protocol`, etc.) do not include an uninstall hook.

Therefore, removing the MSIX package via **Settings → Apps → OpenClaw Tray
→ Uninstall** will silently leave behind:
Companion registers a user-scoped cleanup helper outside its MSIX payload when it
creates a managed WSL Gateway or acquires a missing native Store Gateway. The helper
removes the captured WSL registration and disk directory, or the native package it
acquired, after Companion removal. WSL ownership requires its creation marker and
matching registered disk path and registration identity. Replacements, unknown directories
and unreadable inventories are preserved. Native ownership is recorded only after verified
acquisition and bound to the Gateway's LocalState marker; other installed Companion variants
defer removal of the shared package. Unverified failed or cancelled acquisitions remain unclaimed.
The package-local ownership marker and successful Windows Remove receipt
let first launch settle cleanup after a fast reinstall; normal updates retain the
marker. Legacy WSL installations without a creation-bound cleanup registration are
preserved. A name/path marker cannot distinguish an original distro from a replacement
installed before the first upgraded launch, so startup never adopts it for unattended
removal. Use the explicit in-app removal flow for these installations. A pending conflict offers
preservation and continuation. Startup retires only removed managed connections and their
credentials, preserving unrelated Gateways, settings and local MCP state.

Malformed cleanup receipts stop the worker before removal. Malformed receipts or saved
settings also stop startup with a visible recovery error instead of entering the
preserve/disown path. Reconciliation validates settings before retiring credentials and
keeps a failed receipt for repair. Restore valid `cleanup-result.json` and `settings.json`
from a backup using the paths in the error, then reopen Companion. Invalid receipt recovery
does not discard the ownership record.

Older installations without verified ownership may still leave behind:

- **WSL distro** - `OpenClawGateway` remains in `wsl --list`.
- **Roaming app data** under `%APPDATA%\OpenClawTray\` (device key, settings,
Expand All @@ -30,7 +53,7 @@ Therefore, removing the MSIX package via **Settings → Apps → OpenClaw Tray

---

## Recommended: Run "Remove Local Gateway" Before Uninstalling MSIX
## Remove a Gateway while keeping Companion, or clean up an older installation

1. Open the tray icon.
2. Navigate to **Settings → Local Gateway**.
Expand Down
19 changes: 15 additions & 4 deletions src/OpenClaw.SetupEngine.UI/Pages/NativeGatewaySetupPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,16 @@ private async Task ConfigureAsync(CancellationToken cancellationToken)
throw new InvalidOperationException(NativeGatewayEligibilityText.Get(eligibility));
SetCurrentStep(1);
StatusText.Text = SetupLocalization.GetString("Onboarding_Native_Checking");
var acquiredMissingPackage = false;
await NativeGatewayPackageAcquisition.EnsureAsync(
_resolver, InstallAsync,
_resolver, async ct => { acquiredMissingPackage = await InstallAsync(ct); },
() => StatusText.Text = SetupLocalization.GetString("Onboarding_Native_VerifyingPackage"),
cancellationToken);
using var logger = new SetupLogger(filePath: null);
if (acquiredMissingPackage)
await GatewayPackageCleanup.RegisterAsync(new CommandRunner(logger), ownNativePackage: true, ct: CancellationToken.None);
cancellationToken.ThrowIfCancellationRequested();
SetCurrentStep(2);
using var logger = new SetupLogger(filePath: null);
var appLogger = new SetupOpenClawLogger(logger);
var registry = new GatewayRegistry(window.DataDir, logger: appLogger);
var progressDispatcher = DispatcherQueue;
Expand Down Expand Up @@ -185,11 +188,19 @@ void ReportStage(NativeGatewaySetupStage stage) => DispatchProgress(() =>
window.NavigateToNativeAiSetup(session);
}

private async Task InstallAsync(CancellationToken cancellationToken)
private async Task<bool> InstallAsync(CancellationToken cancellationToken)
{
StatusText.Text = SetupLocalization.GetString("Onboarding_Native_InstallingPackage");
using var logger = new SetupLogger(filePath: null);
await _installer.InstallAsync(new CommandRunner(logger), cancellationToken);
var commands = new CommandRunner(logger);
var missing = false;
try { await _resolver.ResolveAsync(cancellationToken); }
catch (NativeGatewayPackageNotInstalledException)
{
missing = true;
}
await _installer.InstallAsync(commands, cancellationToken);
return missing;
}

private void SetCurrentStep(int index)
Expand Down
2 changes: 2 additions & 0 deletions src/OpenClaw.SetupEngine/CreateWslInstanceStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ or ArgumentException
$"Fresh WSL install failed for '{distro}' from '{baseDistro}' (exit {install.ExitCode}): {FirstNonEmpty(install.Stderr, install.Stdout)}{cleanupError}");
}

await GatewayPackageCleanup.RegisterAsync(ctx.Commands, distro, installPath, ct: ct);

var verify = await VerifyFreshDistro(ctx, distro, installPath, ct);
if (!verify.IsSuccess)
{
Expand Down
Loading
Loading