Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1afd4b7
refactor(setup): remove OPENCLAW_WSL_INSTALL_LOCATION env-var binding
May 7, 2026
2682e78
refactor(scripts): extract shared uninstall helpers into _uninstall-h…
May 7, 2026
02af308
chore(squad): restore session artifacts (MSIX validation script + dec…
May 7, 2026
ac91db2
Scribe Round 18: Inbox merge + orchestration logs
May 7, 2026
9e95b9d
feat(uninstall): add LocalGatewayUninstall core engine + unit tests
May 7, 2026
e989a2f
chore(squad): record Commit 3 completion in agent history and inbox
May 7, 2026
39234a5
feat(uninstall): add validate-wsl-gateway-uninstall.ps1
May 7, 2026
d16b6ad
chore(squad): commit 6 post-work — Aaron history + decision record
May 7, 2026
2dc6dee
chore(squad): add aaron-uninstall-commit-6 decision record
May 7, 2026
260799b
feat: WSL Gateway uninstall — CLI flag, Inno hook, docs, gap closures…
May 7, 2026
144e930
chore(squad): Commit 5 history + inbox entry
May 7, 2026
790f6e7
test(uninstall): MSIX storage-path validation + Inno [UninstallRun] o…
May 7, 2026
9af6789
fix(uninstall): keep Local Gateway section visible during/after removal
May 7, 2026
bd1c35e
fix(uninstall): adapt to master's SettingsManager.LegacyToken API rename
May 11, 2026
98188d3
fix(installer): make [UninstallRun] wait for hook to finish before {a…
May 11, 2026
48f88d9
fix(uninstall): timeout-wrap WSL stop step to avoid 30s hang on wedge…
May 11, 2026
e05712d
fix(installer): unblock cmdpal MSIX bundling during Inno build (P1 BU…
May 11, 2026
4a7d2bd
fix(uninstall): clear GatewayRegistry local records + gate Success on…
May 12, 2026
4eef59b
fix(registry): mark migrated/bridged local records IsLocal=true
May 12, 2026
e98e191
fix(settings): pass live GatewayRegistry to uninstall engine + clarif…
May 12, 2026
786e274
chore(repo): remove squad artifacts from uninstall PR
May 12, 2026
00cff6a
Merge remote-tracking branch 'origin/master' into feat/wsl-gateway-un…
May 12, 2026
b061c38
fix(uninstall): exact-match OpenClawGateway distro guard across Power…
May 12, 2026
8d3511d
fix(installer): add AppMutex to coordinate uninstall with running tra…
May 12, 2026
f7f7892
fix(uninstall): delete Logs dir under both LOCALAPPDATA and APPDATA w…
May 12, 2026
d9b6d0f
fix(security): atomic device-key file writes via temp+File.Move (Bot B1)
May 12, 2026
c84b7e1
fix(uninstall): kill entire WSL keepalive process tree to avoid orpha…
May 12, 2026
54d305a
Merge remote-tracking branch 'origin/master' into review-pr-310
shanselman May 12, 2026
68f30ba
fix(uninstall): clear node token and preserve tunnel records
shanselman May 12, 2026
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,8 @@ visual-test-output/

# Local squad agent state
.squad/

# MSIX and packaging test output
msix-validation-evidence/
packaging-test-output/
uninstall-validation-output/
75 changes: 75 additions & 0 deletions docs/uninstall-msix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Uninstalling OpenClaw Tray — MSIX Package

> **Date:** 2026-05-07
> **Branch:** feat/wsl-gateway-uninstall

---

## MSIX Cannot Auto-Clean WSL State

**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:

- **WSL distro** — `OpenClawGateway` remains in `wsl --list`.
- **Roaming app data** under `%APPDATA%\OpenClawTray\` (device key, settings,
mcp-token).
- **Local app data** under `%LOCALAPPDATA%\OpenClawTray\` (setup state, logs,
VHD parent directory).

> **Note:** If the tray was installed with MSIX and the data landed in the
> package-virtualized path (`%LOCALAPPDATA%\Packages\OpenClaw.Tray_<hash>\...`)
> instead of real `%APPDATA%`, those directories are removed automatically by
> MSIX on uninstall. Bostick's commit 7 validation test (Path A vs Path B)
> confirms which layout applies.

---

## Recommended: Run "Remove Local Gateway" Before Uninstalling MSIX

1. Open the tray icon.
2. Navigate to **Settings → Local Gateway**.
3. Click **"Remove Local Gateway"** (Mattingly's warning banner in commit 4
surfaces this step for MSIX users).
4. Wait for the engine to complete — it stops keepalive processes, unregisters
the WSL distro, nulls the device token, removes autostart, and cleans up app
data.
5. Uninstall the MSIX package via **Settings → Apps**.

---

## Manual Recovery (After MSIX Removed Without In-Tray Cleanup)

If the MSIX was already removed and the WSL distro / app data remains:

```powershell
# 1. Unregister the distro (removes .vhdx from wsl's internal store)
wsl --unregister OpenClawGateway

# 2. Remove VHD parent directory (wsl --unregister may leave the folder)
Remove-Item "$env:LOCALAPPDATA\OpenClawTray\wsl\OpenClawGateway" `
-Recurse -Force -ErrorAction SilentlyContinue

# 3. Remove autostart registry entry
Remove-ItemProperty `
-Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" `
-Name "OpenClawTray" -ErrorAction SilentlyContinue

# 4. Remove local app data (setup state, logs)
Remove-Item "$env:LOCALAPPDATA\OpenClawTray" -Recurse -Force -ErrorAction SilentlyContinue

# 5. Remove roaming app data (settings, device key — only if you want full purge)
# NOTE: mcp-token.txt is intentionally preserved here; delete manually if needed.
Remove-Item "$env:APPDATA\OpenClawTray\setup-state.json" -Force -ErrorAction SilentlyContinue
```

Or use the validation script if it is available separately:

```powershell
.\validate-wsl-gateway-uninstall.ps1 -Mode Full -ConfirmDestructive
```
71 changes: 71 additions & 0 deletions docs/uninstall-portable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Uninstalling OpenClaw Tray — Portable ZIP

> **Date:** 2026-05-07
> **Branch:** feat/wsl-gateway-uninstall

Portable (ZIP) installations have **no automatic uninstall hook**.
Simply deleting the folder leaves the WSL distro, app data, and autostart
entry behind. Follow one of the two paths below for a clean removal.

---

## Recommended: In-Tray Removal (Requires the Tray Running)

1. Open the tray icon.
2. Navigate to **Settings → Local Gateway**.
3. Click **"Remove Local Gateway"**.
4. The engine stops keepalive processes, unregisters the WSL distro, nulls
the device token, removes autostart, and cleans up app data.
5. After the operation completes, delete the portable folder.

---

## CLI: Headless Removal (No Tray UI Required)

Run from the portable folder:

```powershell
# Destructive — removes the local WSL gateway cleanly, then print result to stdout
.\OpenClaw.Tray.WinUI.exe --uninstall --confirm-destructive

# With JSON output for programmatic consumption (tokens redacted in output):
.\OpenClaw.Tray.WinUI.exe --uninstall --confirm-destructive --json-output .\uninstall-result.json

# Dry-run — records what would happen without any destruction:
.\OpenClaw.Tray.WinUI.exe --uninstall --dry-run
```

**Exit codes:**

| Code | Meaning |
|------|---------|
| 0 | Success — all steps completed, postconditions satisfied |
| 1 | Partial failure — one or more steps failed (see JSON output or stderr) |
| 2 | Bad arguments — `--confirm-destructive` or `--dry-run` missing |

After the CLI command exits 0, delete the portable folder.

---

## WARNING: Deleting the Folder Without Running Uninstall

Deleting the portable folder **without** running the uninstall first leaves:

- **WSL distro orphaned** — `OpenClawGateway` remains in `wsl --list`.
Manual cleanup: `wsl --unregister OpenClawGateway`

- **App data** remains under:
- `%APPDATA%\OpenClawTray\` — device key, settings, mcp-token
- `%LOCALAPPDATA%\OpenClawTray\` — setup state, logs, exec policy, VHD parent dir

- **Autostart entry** may remain in
`HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\OpenClawTray`

Manual WSL + registry cleanup:

```powershell
wsl --unregister OpenClawGateway
Remove-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" `
-Name "OpenClawTray" -ErrorAction SilentlyContinue
Remove-Item "$env:LOCALAPPDATA\OpenClawTray\wsl\OpenClawGateway" -Recurse -Force -ErrorAction SilentlyContinue
```
24 changes: 22 additions & 2 deletions installer.iss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ WizardStyle=modern
PrivilegesRequired=lowest
SetupIconFile=src\OpenClaw.Tray.WinUI\Assets\openclaw.ico
UninstallDisplayIcon={app}\{#MyAppExeName}
; Round 2 (Scott #5): block install/uninstall while the tray is running.
; Mutex name matches App.xaml.cs (`new Mutex(true, "OpenClawTray", …)`).
; Tray and Inno run in the same user session, so the bare name resolves
; against Local\OpenClawTray — no Global\ prefix needed.
AppMutex=OpenClawTray
#if MyAppArch == "arm64"
ArchitecturesInstallIn64BitMode=arm64
ArchitecturesAllowed=arm64
Expand All @@ -51,8 +56,12 @@ Name: "cmdpalette"; Description: "Install PowerToys Command Palette extension";
[Files]
; WinUI Tray app - include all files (WinUI needs DLLs, not single-file)
Source: "{#publish}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
; Command Palette extension (all files from build output)
Source: "{#publish}\cmdpal\*"; DestDir: "{app}\CommandPalette"; Flags: ignoreversion recursesubdirs; Tasks: cmdpalette
; Command Palette extension (all files from build output).
; skipifsourcedoesntexist: prevents ISCC compile error when the cmdpal publish
; dir is absent (e.g. developer builds that skip the cmdpalette task).
Source: "{#publish}\cmdpal\*"; DestDir: "{app}\CommandPalette"; Flags: ignoreversion recursesubdirs skipifsourcedoesntexist; Tasks: cmdpalette
; WSL gateway uninstall helper — invoked by [UninstallRun] to drive clean removal
Source: "scripts\Uninstall-LocalGateway.ps1"; DestDir: "{app}"; Flags: ignoreversion

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Expand All @@ -66,5 +75,16 @@ Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChang
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -Command ""Add-AppxPackage -Register '{app}\CommandPalette\AppxManifest.xml' -ForceApplicationShutdown"""; Flags: runhidden; Tasks: cmdpalette

[UninstallRun]
; ORDERING NOTE: Inno Setup runs [UninstallRun] entries BEFORE deleting {app}
; directory contents. This guarantees OpenClawTray.exe is still present when
; the script executes. See Inno docs: "[UninstallRun] section".
; Fallback: if OpenClawTray.exe is missing for any reason, Uninstall-LocalGateway.ps1
; logs the error to {app}\uninstall-gateway-error.log and exits 0 so Inno continues.
; *** DO NOT COMMENT OUT OR REMOVE THE Flags LINE BELOW ***
; waituntilterminated is non-negotiable: without it Inno races ahead and deletes
; {app} while the PowerShell hook (and the CLI engine it invokes) is still running,
; leaving 279+ application files behind after unins000.exe reports exit 0.
; runhidden suppresses the console window that would otherwise flash briefly.
Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\Uninstall-LocalGateway.ps1"""; Flags: shellexec waituntilterminated runhidden; StatusMsg: "Removing local WSL gateway..."
; Unregister Command Palette extension on uninstall
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -Command ""Get-AppxPackage -Name '*OpenClaw*' | Remove-AppxPackage"""; Flags: runhidden
79 changes: 79 additions & 0 deletions scripts/Uninstall-LocalGateway.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<#
.SYNOPSIS
Inno Setup [UninstallRun] helper — removes the local WSL gateway via the
OpenClaw tray CLI flag.

.DESCRIPTION
INNO ORDERING CONTRACT
----------------------
Per Inno Setup documentation, [UninstallRun] entries execute BEFORE the
{app} directory is deleted. OpenClawTray.exe is therefore guaranteed to
be present when this script runs.

WHAT THIS SCRIPT DOES
---------------------
1. Locates OpenClawTray.exe in the same directory as this script ({app}).
2. Invokes: OpenClawTray.exe --uninstall --confirm-destructive --json-output <log>
3. Logs success or failure to {app}\uninstall-gateway-result.json.
4. If the EXE is missing (e.g., partial install), logs the error and exits 0
so the Inno uninstaller continues. The user may need to clean up manually
(see docs\uninstall-portable.md for manual steps).

FALLBACK
--------
Exit 0 in all error cases so Inno does not abort the uninstall if gateway
cleanup fails. The result JSON captures the failure for post-mortem.

.NOTES
Date: 2026-05-07
Author: Aaron (Backend / Infrastructure Engineer)
Branch: feat/wsl-gateway-uninstall
Commit: 5 of 7

Token / key material is NEVER written to the result log; the engine
and CLI layer both redact sensitive fields before serializing.
#>

[CmdletBinding()]
param()

$ErrorActionPreference = 'Stop'

$scriptDir = $PSScriptRoot
$exePath = Join-Path $scriptDir 'OpenClaw.Tray.WinUI.exe'
$resultPath = Join-Path $scriptDir 'uninstall-gateway-result.json'
$errorPath = Join-Path $scriptDir 'uninstall-gateway-error.log'

# ---------------------------------------------------------------------------
# EXE presence check — fallback if somehow missing
# ---------------------------------------------------------------------------
if (-not (Test-Path -LiteralPath $exePath)) {
$msg = "[$(Get-Date -Format 'o')] Uninstall-LocalGateway.ps1: " +
"OpenClawTray.exe not found at '$exePath'. " +
"WSL gateway cleanup skipped. Manual cleanup may be required."
try { $msg | Out-File -LiteralPath $errorPath -Encoding UTF8 -Force } catch {}
Write-Warning $msg
exit 0
}

# ---------------------------------------------------------------------------
# Invoke CLI uninstall
# ---------------------------------------------------------------------------
$exitCode = 0
try {
& $exePath --uninstall --confirm-destructive --json-output $resultPath
$exitCode = if ($null -eq $global:LASTEXITCODE) { 0 } else { $global:LASTEXITCODE }

if ($exitCode -eq 0) {
Write-Host "OpenClaw local WSL gateway removed successfully." -ForegroundColor Green
} else {
Write-Warning "OpenClaw gateway uninstall exited $exitCode; see '$resultPath' for details."
}
} catch {
$msg = "[$(Get-Date -Format 'o')] Uninstall-LocalGateway.ps1 error: $($_.Exception.Message)"
try { $msg | Out-File -LiteralPath $errorPath -Encoding UTF8 -Force } catch {}
Write-Warning $msg
}

# Always exit 0 so Inno does not abort the broader uninstall.
exit 0
Loading
Loading