# Office Copilot Install / Update Script (PowerShell) # Usage: irm https://ai.li-rui.top/addin/install.ps1 | iex $ProgressPreference = "SilentlyContinue" $ErrorActionPreference = "Stop" $InstallDir = Join-Path $env:APPDATA "OfficeCopilot" $ManifestPath = Join-Path $InstallDir "manifest.xml" $ManifestUrl = "https://ai.li-rui.top/addin/manifest.xml" $DefaultAddinId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890" function Get-OfficeVersions { $versions = New-Object "System.Collections.Generic.HashSet[string]" try { $curVerKey = Get-Item -Path "Registry::HKEY_CLASSES_ROOT\Word.Application\CurVer" $curVer = $curVerKey.GetValue("") if ($curVer -match "Word\.Application\.(\d+)") { [void]$versions.Add("$($Matches[1]).0") } } catch {} try { $officeRoot = "HKCU:\Software\Microsoft\Office" if (Test-Path $officeRoot) { Get-ChildItem -Path $officeRoot -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -match "^\d+\.\d+$" } | ForEach-Object { [void]$versions.Add($_.PSChildName) } } } catch {} if ($versions.Count -eq 0) { [void]$versions.Add("16.0") } return @($versions) | Sort-Object { [version]$_ } -Descending } function Stop-OfficeHostProcesses { $hostProcesses = @("WINWORD", "EXCEL", "POWERPNT", "OUTLOOK", "ONENOTE") $backgroundProcesses = @("WefHost", "WefHelper", "OfficeC2RClient") $running = Get-Process -Name $hostProcesses -ErrorAction SilentlyContinue if ($running) { Write-Host "[0/4] Closing running Office host processes..." -ForegroundColor Cyan $running | ForEach-Object { $_.CloseMainWindow() | Out-Null } Start-Sleep -Seconds 3 $remaining = Get-Process -Name $hostProcesses -ErrorAction SilentlyContinue if ($remaining) { $remaining | Stop-Process -Force -ErrorAction SilentlyContinue } } try { $wvProcs = Get-CimInstance Win32_Process -Filter "Name='msedgewebview2.exe'" -ErrorAction SilentlyContinue | Where-Object { ($_.CommandLine -like "*Microsoft\\Office\\WebView2*") -or ($_.CommandLine -like "*ai.li-rui.top*") -or ($_.CommandLine -like "*_host_Info=Word*") } foreach ($proc in $wvProcs) { Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue } } catch {} $bgRunning = Get-Process -Name $backgroundProcesses -ErrorAction SilentlyContinue if ($bgRunning) { $bgRunning | Stop-Process -Force -ErrorAction SilentlyContinue } Start-Sleep -Seconds 1 } function Download-Manifest { param ( [Parameter(Mandatory = $true)][string]$Url, [Parameter(Mandatory = $true)][string]$OutFile ) try { if ($PSVersionTable.PSVersion.Major -le 5) { Invoke-WebRequest -Uri $Url -OutFile $OutFile -UseBasicParsing } else { Invoke-WebRequest -Uri $Url -OutFile $OutFile } return $true } catch { return $false } } function Set-RegistryValueCompat { param ( [Parameter(Mandatory = $true)][string]$Path, [Parameter(Mandatory = $true)][string]$Name, [Parameter(Mandatory = $true)][object]$Value, [Parameter(Mandatory = $true)][ValidateSet("String", "ExpandString", "DWord", "QWord", "Binary", "MultiString")][string]$PropertyType ) if (-not (Test-Path $Path)) { New-Item -Path $Path -Force | Out-Null } # Use New-ItemProperty for cross-version compatibility (Windows PowerShell + PowerShell 7). New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $PropertyType -Force | Out-Null } function Get-ManifestAddinId { param ( [Parameter(Mandatory = $true)][string]$Path, [Parameter(Mandatory = $true)][string]$Fallback ) try { [xml]$manifest = Get-Content -Path $Path -Raw $manifestId = [string]$manifest.OfficeApp.Id if (-not [string]::IsNullOrWhiteSpace($manifestId)) { return $manifestId.Trim() } } catch {} return $Fallback } function Test-RegistryInstallState { param ( [Parameter(Mandatory = $true)][string]$OfficeVersion, [Parameter(Mandatory = $true)][string]$AddinId, [Parameter(Mandatory = $true)][string]$ManifestPath ) $devPath = "HKCU:\Software\Microsoft\Office\$OfficeVersion\WEF\Developer" $devWordPath = "HKCU:\Software\Microsoft\Office\$OfficeVersion\WEF\Developer\Word" $catalogPath = "HKCU:\Software\Microsoft\Office\$OfficeVersion\WEF\TrustedCatalogs\OfficeCopilot" $devOk = $false $devWordOk = $false $catalogOk = $false try { $devValue = (Get-ItemProperty -Path $devPath -Name $AddinId -ErrorAction Stop).$AddinId $devOk = ($devValue -eq $ManifestPath) } catch {} try { $devWordValue = (Get-ItemProperty -Path $devWordPath -Name $AddinId -ErrorAction Stop).$AddinId $devWordOk = ($devWordValue -eq $ManifestPath) } catch {} try { $catalog = Get-ItemProperty -Path $catalogPath -ErrorAction Stop $catalogOk = (($catalog.Url -eq "$InstallDir\") -and ([int]$catalog.Flags -eq 1)) } catch {} return (($devOk -or $devWordOk) -and $catalogOk) } function Write-ManualEnableHints { Write-Host "" Write-Host "If Word does not show the ribbon button immediately:" -ForegroundColor Yellow Write-Host " 1. Open Word -> Insert -> My Add-ins -> Shared Folder" Write-Host " 2. Select 'Office Copilot' and click Add" Write-Host " 3. Restart Word once" } $OfficeVersions = Get-OfficeVersions Write-Host "========================================" -ForegroundColor Cyan Write-Host " Office Copilot - Install / Update" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" Write-Host "Detected Office version(s): $($OfficeVersions -join ', ')" Write-Host "PowerShell: $($PSVersionTable.PSEdition) $($PSVersionTable.PSVersion)" Write-Host "" # 0. Ensure Office host processes are closed. Stop-OfficeHostProcesses # 1. Prepare install directory if (-not (Test-Path $InstallDir)) { New-Item -Path $InstallDir -ItemType Directory -Force | Out-Null Write-Host "[1/4] Created install directory: $InstallDir" } else { Write-Host "[1/4] Install directory already exists." } # 2. Download / update manifest Write-Host "[2/4] Downloading latest manifest from server..." $downloaded = Download-Manifest -Url $ManifestUrl -OutFile $ManifestPath if ($downloaded) { Write-Host " Download successful." -ForegroundColor Green } elseif (Test-Path $ManifestPath) { Write-Host " [WARN] Failed to download latest manifest. Using existing local manifest." -ForegroundColor Yellow } else { Write-Host " [ERROR] Failed to download from $ManifestUrl" -ForegroundColor Red Write-Host " Please check your network connection and try again." -ForegroundColor Red return } $AddinId = Get-ManifestAddinId -Path $ManifestPath -Fallback $DefaultAddinId # 3. Register trusted catalog and sideload Write-Host "[3/4] Configuring Office trust settings..." try { foreach ($OfficeVersion in $OfficeVersions) { $RegPath = "HKCU:\Software\Microsoft\Office\$OfficeVersion\WEF\TrustedCatalogs\OfficeCopilot" $DevPath = "HKCU:\Software\Microsoft\Office\$OfficeVersion\WEF\Developer" $DevWordPath = "HKCU:\Software\Microsoft\Office\$OfficeVersion\WEF\Developer\Word" Set-RegistryValueCompat -Path $RegPath -Name "Url" -Value "$InstallDir\" -PropertyType "String" Set-RegistryValueCompat -Path $RegPath -Name "Flags" -Value 1 -PropertyType "DWord" if (-not (Test-Path $DevWordPath)) { New-Item -Path $DevWordPath -Force | Out-Null } # Remove stale global developer entries to avoid non-Word host side effects. if (Test-Path $DevPath) { Remove-ItemProperty -Path $DevPath -Name $AddinId -ErrorAction SilentlyContinue Remove-ItemProperty -Path $DevPath -Name $ManifestPath -ErrorAction SilentlyContinue } Remove-ItemProperty -Path $DevWordPath -Name $ManifestPath -ErrorAction SilentlyContinue # Register on Developer root (official tooling path) and keep Word-specific entry as fallback. Set-RegistryValueCompat -Path $DevPath -Name $AddinId -Value $ManifestPath -PropertyType "String" Set-RegistryValueCompat -Path $DevPath -Name "RefreshAddins" -Value 1 -PropertyType "DWord" Set-RegistryValueCompat -Path $DevWordPath -Name $AddinId -Value $ManifestPath -PropertyType "String" Set-RegistryValueCompat -Path $DevWordPath -Name "RefreshAddins" -Value 1 -PropertyType "DWord" } } catch { Write-Host " [ERROR] Failed to write Office registry settings." -ForegroundColor Red Write-Host " $($_.Exception.Message)" -ForegroundColor Red return } # 4. Clear Office add-in cache Write-Host "[4/4] Clearing add-in cache..." foreach ($OfficeVersion in $OfficeVersions) { $cacheDir = Join-Path $env:LOCALAPPDATA "Microsoft\Office\$OfficeVersion\Wef" $addinCacheDir = Join-Path $env:LOCALAPPDATA "Microsoft\Office\$OfficeVersion\AddinCache" if (Test-Path $cacheDir) { Remove-Item -Path $cacheDir -Recurse -Force -ErrorAction SilentlyContinue } if (Test-Path $addinCacheDir) { Remove-Item -Path $addinCacheDir -Recurse -Force -ErrorAction SilentlyContinue } } $officeWv2 = Join-Path $env:LOCALAPPDATA "Microsoft\Office\WebView2" if (Test-Path $officeWv2) { Remove-Item -Path $officeWv2 -Recurse -Force -ErrorAction SilentlyContinue } $allOk = $true foreach ($OfficeVersion in $OfficeVersions) { if (-not (Test-RegistryInstallState -OfficeVersion $OfficeVersion -AddinId $AddinId -ManifestPath $ManifestPath)) { $allOk = $false Write-Host " [WARN] Registry verification failed for Office $OfficeVersion." -ForegroundColor Yellow } } Write-Host "" if ($allOk) { Write-Host "========================================" -ForegroundColor Green Write-Host " Install / Update complete!" -ForegroundColor Green Write-Host "" Write-Host " Please restart Word." Write-Host "========================================" -ForegroundColor Green Write-ManualEnableHints } else { Write-Host "========================================" -ForegroundColor Yellow Write-Host " Install finished with warnings." -ForegroundColor Yellow Write-Host " Please run PowerShell as current Windows user and retry." -ForegroundColor Yellow Write-Host "========================================" -ForegroundColor Yellow Write-ManualEnableHints }