@cats-inc/cats-platform 0.2.8 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/README.md +17 -0
  2. package/build/desktop/bootstrapPage.d.ts.map +1 -1
  3. package/build/desktop/bootstrapPage.js +26 -129
  4. package/build/desktop/bootstrapPage.js.map +1 -1
  5. package/build/desktop/cliInventoryProbe.d.ts +6 -0
  6. package/build/desktop/cliInventoryProbe.d.ts.map +1 -1
  7. package/build/desktop/cliInventoryProbe.js +6 -3
  8. package/build/desktop/cliInventoryProbe.js.map +1 -1
  9. package/build/desktop/contracts.d.ts +2 -0
  10. package/build/desktop/contracts.d.ts.map +1 -1
  11. package/build/desktop/contracts.js.map +1 -1
  12. package/build/desktop/main.js +95 -41
  13. package/build/desktop/main.js.map +1 -1
  14. package/build/desktop/preload.cjs +8 -6
  15. package/build/desktop/preload.cjs.map +1 -1
  16. package/build/desktop/providerManager.d.ts +46 -0
  17. package/build/desktop/providerManager.d.ts.map +1 -0
  18. package/build/desktop/providerManager.js +220 -0
  19. package/build/desktop/providerManager.js.map +1 -0
  20. package/build/desktop/providerSelection.d.ts +1 -0
  21. package/build/desktop/providerSelection.d.ts.map +1 -1
  22. package/build/desktop/providerSelection.js +7 -1
  23. package/build/desktop/providerSelection.js.map +1 -1
  24. package/build/desktop/setupAssets.d.ts.map +1 -1
  25. package/build/desktop/setupAssets.js +12 -0
  26. package/build/desktop/setupAssets.js.map +1 -1
  27. package/build/desktop/setupAudit.d.ts +3 -1
  28. package/build/desktop/setupAudit.d.ts.map +1 -1
  29. package/build/desktop/setupAudit.js +12 -8
  30. package/build/desktop/setupAudit.js.map +1 -1
  31. package/build/desktop/setupBridge.js +1 -1
  32. package/build/desktop/setupBridge.js.map +1 -1
  33. package/build/desktop/setupPrerequisites.d.ts +11 -0
  34. package/build/desktop/setupPrerequisites.d.ts.map +1 -0
  35. package/build/desktop/setupPrerequisites.js +47 -0
  36. package/build/desktop/setupPrerequisites.js.map +1 -0
  37. package/build/renderer/assets/ActiveSessionPermissionChip-B49WcZBJ.js +1 -0
  38. package/build/renderer/assets/{App-BZA23O2J.js → App-BpxZB5ym.js} +1 -1
  39. package/build/renderer/assets/{App-rGw9J8PZ.js → App-DYTUfAl6.js} +1 -1
  40. package/build/renderer/assets/{App-YqprEL2n.js → App-boB4WrmU.js} +1 -1
  41. package/build/renderer/assets/{WorkspaceAppRoutes-BCHCikmJ.js → WorkspaceAppRoutes-CPqz85iw.js} +1 -1
  42. package/build/renderer/assets/app-C52wwCGz.js +85 -0
  43. package/build/renderer/assets/app-CqnsY5ed.css +1 -0
  44. package/build/renderer/assets/{client-BdGHAUoK.js → client-DREGSZqQ.js} +1 -1
  45. package/build/renderer/assets/conversationSidebarRecentEntries-DK0iWS73.js +6 -0
  46. package/build/renderer/assets/{screenshotOverlay-DfBcolX-.js → screenshotOverlay-EF07cXvw.js} +1 -1
  47. package/build/renderer/assets/{useDraftSessionChips-D9BX6X92.js → useDraftSessionChips-BdZ_B1gy.js} +1 -1
  48. package/build/renderer/desktop/overlay/index.html +2 -2
  49. package/build/renderer/index.html +3 -3
  50. package/build/server/app/renderer/auth/api.d.ts.map +1 -1
  51. package/build/server/app/renderer/auth/api.js +4 -1
  52. package/build/server/app/renderer/auth/api.js.map +1 -1
  53. package/build/server/app/renderer/providerCatalogClient.d.ts.map +1 -1
  54. package/build/server/app/renderer/providerCatalogClient.js +19 -6
  55. package/build/server/app/renderer/providerCatalogClient.js.map +1 -1
  56. package/build/server/app/renderer/providerClientInvalidation.d.ts +6 -0
  57. package/build/server/app/renderer/providerClientInvalidation.d.ts.map +1 -0
  58. package/build/server/app/renderer/providerClientInvalidation.js +12 -0
  59. package/build/server/app/renderer/providerClientInvalidation.js.map +1 -0
  60. package/build/server/app/renderer/providerReadLoop.d.ts +5 -0
  61. package/build/server/app/renderer/providerReadLoop.d.ts.map +1 -0
  62. package/build/server/app/renderer/providerReadLoop.js +52 -0
  63. package/build/server/app/renderer/providerReadLoop.js.map +1 -0
  64. package/build/server/app/renderer/providerRegistryClient.d.ts +2 -1
  65. package/build/server/app/renderer/providerRegistryClient.d.ts.map +1 -1
  66. package/build/server/app/renderer/providerRegistryClient.js +33 -17
  67. package/build/server/app/renderer/providerRegistryClient.js.map +1 -1
  68. package/build/server/app/server/authGatePolicy.js +7 -0
  69. package/build/server/app/server/authGatePolicy.js.map +1 -1
  70. package/build/server/app/server/subscribeRoutes.d.ts.map +1 -1
  71. package/build/server/app/server/subscribeRoutes.js +5 -3
  72. package/build/server/app/server/subscribeRoutes.js.map +1 -1
  73. package/build/server/design/components/providerModelFieldsSupport.d.ts +2 -21
  74. package/build/server/design/components/providerModelFieldsSupport.d.ts.map +1 -1
  75. package/build/server/design/components/providerModelFieldsSupport.js +32 -36
  76. package/build/server/design/components/providerModelFieldsSupport.js.map +1 -1
  77. package/build/server/design/components/useProviderCatalogState.d.ts +1 -2
  78. package/build/server/design/components/useProviderCatalogState.d.ts.map +1 -1
  79. package/build/server/design/components/useProviderCatalogState.js +71 -152
  80. package/build/server/design/components/useProviderCatalogState.js.map +1 -1
  81. package/build/server/design/components/useProviderModelFieldActions.js +2 -2
  82. package/build/server/design/components/useProviderModelFieldActions.js.map +1 -1
  83. package/build/server/design/components/useProviderRegistryState.d.ts +1 -7
  84. package/build/server/design/components/useProviderRegistryState.d.ts.map +1 -1
  85. package/build/server/design/components/useProviderRegistryState.js +41 -91
  86. package/build/server/design/components/useProviderRegistryState.js.map +1 -1
  87. package/build/server/design/components/useProviderTargetReconciliation.d.ts +1 -0
  88. package/build/server/design/components/useProviderTargetReconciliation.d.ts.map +1 -1
  89. package/build/server/design/components/useProviderTargetReconciliation.js +1 -1
  90. package/build/server/design/components/useProviderTargetReconciliation.js.map +1 -1
  91. package/build/server/platform/orchestration/entitySubscriptions/channel.d.ts +2 -1
  92. package/build/server/platform/orchestration/entitySubscriptions/channel.d.ts.map +1 -1
  93. package/build/server/platform/orchestration/entitySubscriptions/channel.js +3 -18
  94. package/build/server/platform/orchestration/entitySubscriptions/channel.js.map +1 -1
  95. package/build/server/platform/orchestration/entitySubscriptions/index.d.ts +1 -0
  96. package/build/server/platform/orchestration/entitySubscriptions/index.d.ts.map +1 -1
  97. package/build/server/platform/orchestration/entitySubscriptions/index.js.map +1 -1
  98. package/build/server/products/chat/api/routeSupport.d.ts +9 -1
  99. package/build/server/products/chat/api/routeSupport.d.ts.map +1 -1
  100. package/build/server/products/chat/api/routeSupport.js +22 -0
  101. package/build/server/products/chat/api/routeSupport.js.map +1 -1
  102. package/build/server/products/chat/state/model/readModels.d.ts +1 -0
  103. package/build/server/products/chat/state/model/readModels.d.ts.map +1 -1
  104. package/build/server/products/chat/state/model/readModels.js +2 -1
  105. package/build/server/products/chat/state/model/readModels.js.map +1 -1
  106. package/build/server/products/shared/renderer/api/appShell.d.ts +1 -0
  107. package/build/server/products/shared/renderer/api/appShell.d.ts.map +1 -1
  108. package/build/server/products/shared/renderer/api/appShell.js +9 -0
  109. package/build/server/products/shared/renderer/api/appShell.js.map +1 -1
  110. package/build/server/runtime/client.d.ts.map +1 -1
  111. package/build/server/runtime/client.js +5 -1
  112. package/build/server/runtime/client.js.map +1 -1
  113. package/build/server/runtime/setup.d.ts +4 -0
  114. package/build/server/runtime/setup.d.ts.map +1 -1
  115. package/build/server/runtime/setup.js +3 -1
  116. package/build/server/runtime/setup.js.map +1 -1
  117. package/build/server/server/routes/providers.d.ts.map +1 -1
  118. package/build/server/server/routes/providers.js +21 -50
  119. package/build/server/server/routes/providers.js.map +1 -1
  120. package/build/server/shared/desktopRecoveryBridge.d.ts +2 -1
  121. package/build/server/shared/desktopRecoveryBridge.d.ts.map +1 -1
  122. package/build/server/shared/desktopRecoveryBridge.js.map +1 -1
  123. package/build/server/shared/providerCatalogData.d.ts +128 -57
  124. package/build/server/shared/providerCatalogData.d.ts.map +1 -1
  125. package/build/server/shared/providerCatalogData.js +51 -26
  126. package/build/server/shared/providerCatalogData.js.map +1 -1
  127. package/build/server/shared/providerRegistryWarnings.d.ts +4 -0
  128. package/build/server/shared/providerRegistryWarnings.d.ts.map +1 -1
  129. package/build/server/shared/providerRegistryWarnings.js +7 -0
  130. package/build/server/shared/providerRegistryWarnings.js.map +1 -1
  131. package/package.json +12 -5
  132. package/packages/provider-setup/manager.d.ts +79 -0
  133. package/packages/provider-setup/manager.js +441 -0
  134. package/scripts/README.md +23 -0
  135. package/scripts/build-desktop-installer.mjs +124 -16
  136. package/scripts/linux/install-github-cli.sh +5 -0
  137. package/scripts/linux/install-node.sh +9 -3
  138. package/scripts/linux/node-cli-common.sh +87 -48
  139. package/scripts/linux/provider-cli-common.sh +80 -105
  140. package/scripts/linux/provider-version-common.sh +247 -0
  141. package/scripts/linux/test-linux-package-smoke.sh +2 -0
  142. package/scripts/linux/verified-npm-update.sh +20 -0
  143. package/scripts/macos/install-github-cli.sh +5 -0
  144. package/scripts/macos/install-node.sh +9 -3
  145. package/scripts/macos/node-cli-common.sh +87 -48
  146. package/scripts/macos/provider-cli-common.sh +80 -105
  147. package/scripts/macos/provider-version-common.sh +247 -0
  148. package/scripts/macos/test-macos-package-smoke.sh +2 -0
  149. package/scripts/macos/verified-npm-update.sh +20 -0
  150. package/scripts/testing/conversation-navigation-smoke-electron.cjs +134 -0
  151. package/scripts/testing/conversation-navigation-smoke-renderer.tsx +102 -0
  152. package/scripts/testing/conversation-navigation-smoke.mjs +67 -0
  153. package/scripts/validate-release-assets.mjs +4 -1
  154. package/scripts/windows/Install-Antigravity.ps1 +26 -3
  155. package/scripts/windows/Install-ClaudeCode.ps1 +25 -7
  156. package/scripts/windows/Install-CursorAgent.ps1 +45 -30
  157. package/scripts/windows/Install-Devin.ps1 +16 -47
  158. package/scripts/windows/Install-GitHubCli.ps1 +4 -0
  159. package/scripts/windows/Install-Goose.ps1 +18 -1
  160. package/scripts/windows/Install-Grok.ps1 +14 -1
  161. package/scripts/windows/Install-Junie.ps1 +32 -11
  162. package/scripts/windows/Install-KiroCli.ps1 +37 -13
  163. package/scripts/windows/Install-MetaCli.ps1 +8 -1
  164. package/scripts/windows/Install-Node.ps1 +6 -1
  165. package/scripts/windows/Setup-NodeGlobalPrefix.ps1 +10 -0
  166. package/scripts/windows/Test-WindowsInstallerSmoke.ps1 +2 -0
  167. package/scripts/windows/_HiddenProcess.ps1 +14 -3
  168. package/scripts/windows/_NativeInstallerSupport.ps1 +308 -0
  169. package/scripts/windows/_NpmCliInstaller.ps1 +35 -14
  170. package/scripts/windows/_PackagedUninstall.ps1 +9 -1
  171. package/scripts/windows/_VerifiedNpmUpdate.ps1 +25 -0
  172. package/build/renderer/assets/ActiveSessionPermissionChip-CAgN-tXv.js +0 -1
  173. package/build/renderer/assets/app-BV3UAQkR.css +0 -1
  174. package/build/renderer/assets/app-CVPqqfZo.js +0 -45
  175. package/build/renderer/assets/conversationSidebarRecentEntries-iqaLqA1n.js +0 -6
  176. package/build/server/app/renderer/settings/runtimeLifecycleHelpers.d.ts +0 -48
  177. package/build/server/app/renderer/settings/runtimeLifecycleHelpers.d.ts.map +0 -1
  178. package/build/server/app/renderer/settings/runtimeLifecycleHelpers.js +0 -535
  179. package/build/server/app/renderer/settings/runtimeLifecycleHelpers.js.map +0 -1
  180. package/build/server/design/components/useProviderRegistryAutoRecheck.d.ts +0 -13
  181. package/build/server/design/components/useProviderRegistryAutoRecheck.d.ts.map +0 -1
  182. package/build/server/design/components/useProviderRegistryAutoRecheck.js +0 -70
  183. package/build/server/design/components/useProviderRegistryAutoRecheck.js.map +0 -1
  184. package/build/server/products/shared/renderer/api/providerCatalogRefreshStore.d.ts +0 -19
  185. package/build/server/products/shared/renderer/api/providerCatalogRefreshStore.d.ts.map +0 -1
  186. package/build/server/products/shared/renderer/api/providerCatalogRefreshStore.js +0 -53
  187. package/build/server/products/shared/renderer/api/providerCatalogRefreshStore.js.map +0 -1
  188. package/build/server/products/shared/renderer/api/providers.d.ts +0 -17
  189. package/build/server/products/shared/renderer/api/providers.d.ts.map +0 -1
  190. package/build/server/products/shared/renderer/api/providers.js +0 -50
  191. package/build/server/products/shared/renderer/api/providers.js.map +0 -1
@@ -28,6 +28,7 @@ Set-StrictMode -Version Latest
28
28
  $ErrorActionPreference = 'Stop'
29
29
 
30
30
  . (Join-Path $PSScriptRoot '_HiddenProcess.ps1')
31
+ . (Join-Path $PSScriptRoot '_NativeInstallerSupport.ps1')
31
32
  . (Join-Path $PSScriptRoot '_PackagedUninstall.ps1')
32
33
 
33
34
  function Write-StructuredResult {
@@ -36,6 +37,10 @@ function Write-StructuredResult {
36
37
  [int]$ExitCode
37
38
  )
38
39
 
40
+ if (Get-Variable -Name catsInitialObservation -Scope Script -ErrorAction SilentlyContinue) {
41
+ Add-CatsNativeObservation -Result $Result -Before $script:catsInitialObservation -Attempted ([bool]$script:shouldInstall) -Skipped ([bool]$SkipInstaller)
42
+ }
43
+
39
44
  if ($Json) {
40
45
  $Result | ConvertTo-Json -Depth 10
41
46
  } else {
@@ -274,7 +279,15 @@ if ($CheckOnly) {
274
279
  Write-StructuredResult -Result $result -ExitCode 0
275
280
  }
276
281
 
282
+ $installFailed = $false
283
+ $catsInitialObservation = $detected.PSObject.Copy()
277
284
  $shouldInstall = $Force -or $Upgrade -or -not $detected.installed
285
+ if ($Upgrade -and -not $Force -and $detected.installed -and -not $SkipInstaller -and -not $DryRun) {
286
+ $versionGate = Test-CatsNativeUpgrade -Provider 'antigravity' -InstalledVersion $detected.detectedVersion
287
+ $shouldInstall = $versionGate.shouldInstall
288
+ if (-not $versionGate.known) { $warnings.Add('Could not compare the published version; the official installer will verify the update.') }
289
+ if (-not $shouldInstall) { $plannedActions.Clear() }
290
+ }
278
291
  $installFailed = $false
279
292
  $installSkipped = $false
280
293
  if ($shouldInstall) {
@@ -282,8 +295,11 @@ if ($shouldInstall) {
282
295
  $warnings.Add('Dry-run requested; Antigravity installer invocation was skipped.')
283
296
  $installSkipped = $true
284
297
  } else {
285
- if (($Force -or $Upgrade) -and (Test-Path -LiteralPath (Resolve-AntigravityExecutablePath) -PathType Leaf)) {
298
+ $previousBinary = $null
299
+ if (($Force -or $Upgrade) -and -not $SkipInstaller -and (Test-Path -LiteralPath (Resolve-AntigravityExecutablePath) -PathType Leaf)) {
286
300
  try {
301
+ $previousBinary = Join-Path ([IO.Path]::GetTempPath()) ('cats-agy-' + [guid]::NewGuid().ToString('N') + '.exe')
302
+ Copy-Item -LiteralPath (Resolve-AntigravityExecutablePath) -Destination $previousBinary -ErrorAction Stop
287
303
  Remove-Item -LiteralPath (Resolve-AntigravityExecutablePath) -Force -ErrorAction Stop
288
304
  } catch {
289
305
  $warnings.Add("Unable to remove existing agy.exe before refresh: $($_.Exception.Message)")
@@ -308,12 +324,19 @@ if ($shouldInstall) {
308
324
  }
309
325
 
310
326
  Start-Sleep -Seconds 2
311
- Add-AntigravityToUserPath
327
+ if (-not $SkipInstaller) { Add-AntigravityToUserPath }
312
328
  $detected = Detect-AntigravityInstall
313
329
  if (-not $detected.installed -and -not $installSkipped -and -not $installFailed) {
314
330
  $warnings.Add('Antigravity installation completed but agy.exe was not detected at the expected path.')
315
331
  $installFailed = $true
316
332
  }
333
+ if ($previousBinary) {
334
+ if ($installFailed) {
335
+ Copy-Item -LiteralPath $previousBinary -Destination (Resolve-AntigravityExecutablePath) -Force
336
+ $detected = Detect-AntigravityInstall
337
+ }
338
+ Remove-Item -LiteralPath $previousBinary -Force -ErrorAction SilentlyContinue
339
+ }
317
340
  }
318
341
 
319
342
  if ($installFailed -or ($DryRun -and $shouldInstall)) {
@@ -332,7 +355,7 @@ $interruptions = [System.Collections.Generic.List[object]]::new()
332
355
  if ($shouldInstall -and -not $installFailed -and -not $DryRun) {
333
356
  $interruptions.Add([pscustomobject]@{
334
357
  kind = 'relaunch_required'
335
- summary = 'Relaunch Cats Desktop Host after the Antigravity install step, then rerun the packaged setup check.'
358
+ summary = 'Run Detect Again after the Antigravity install step if the command is not visible yet.'
336
359
  resumable = $true
337
360
  requiresRestart = $false
338
361
  requiresElevation = $false
@@ -74,6 +74,7 @@ Set-StrictMode -Version Latest
74
74
  $ErrorActionPreference = 'Stop'
75
75
 
76
76
  . (Join-Path $PSScriptRoot '_HiddenProcess.ps1')
77
+ . (Join-Path $PSScriptRoot '_NativeInstallerSupport.ps1')
77
78
 
78
79
  function Write-StructuredResult {
79
80
  param(
@@ -81,6 +82,10 @@ function Write-StructuredResult {
81
82
  [int]$ExitCode
82
83
  )
83
84
 
85
+ if (Get-Variable -Name catsInitialObservation -Scope Script -ErrorAction SilentlyContinue) {
86
+ Add-CatsNativeObservation -Result $Result -Before $script:catsInitialObservation -Attempted ([bool]$script:shouldInstall) -Skipped ([bool]$SkipInstaller)
87
+ }
88
+
84
89
  if ($Json) {
85
90
  $Result | ConvertTo-Json -Depth 10
86
91
  } else {
@@ -250,7 +255,8 @@ function Invoke-ClaudeInstaller {
250
255
 
251
256
  try {
252
257
  $installScript = Invoke-RestMethod 'https://claude.ai/install.ps1'
253
- Invoke-Expression $installScript
258
+ $child = Invoke-CatsRemoteInstaller -ScriptText $installScript
259
+ if (-not $child.success) { throw $child.stderr }
254
260
  return [pscustomobject]@{
255
261
  usedWingetFallback = $false
256
262
  skipped = $false
@@ -493,6 +499,10 @@ if ($CheckOnly) {
493
499
  Write-StructuredResult -Result $result -ExitCode 0
494
500
  }
495
501
 
502
+ if ($DryRun) {
503
+ Write-CatsNativePreview -Helper 'windows-claude-native-installer' -Mode $executionMode -Detected $detected -Actions $plannedActions.ToArray() -EmitJson ([bool]$Json)
504
+ }
505
+
496
506
  $cleanedNpmShim = $false
497
507
  if ($npmShimPresent) {
498
508
  $cleanedNpmShim = Remove-ClaudeNpmShim
@@ -503,9 +513,16 @@ if ($npmShimPresent) {
503
513
  }
504
514
  }
505
515
 
516
+ $installFailed = $false
517
+ $catsInitialObservation = $detected.PSObject.Copy()
506
518
  $shouldInstall = $Force -or $Upgrade -or -not $detected.installed
519
+ $installFailed = $false
507
520
  if ($shouldInstall) {
508
- $installResult = Invoke-ClaudeInstaller
521
+ $installResult = try { Invoke-ClaudeInstaller } catch { [pscustomobject]@{ skipped = $false; success = $false; stderr = [string]$_.Exception.Message; usedPowerShell51Fallback = $false; usedWingetFallback = $false } }
522
+ if ($installResult.PSObject.Properties['success'] -and -not $installResult.success) {
523
+ $installFailed = $true
524
+ $warnings.Add($installResult.stderr)
525
+ }
509
526
  $usedWingetFallback = [bool]$installResult.usedWingetFallback
510
527
  if ($usedWingetFallback) {
511
528
  $warnings.Add('Claude Code installer required the winget fallback path.')
@@ -516,8 +533,9 @@ if ($shouldInstall) {
516
533
 
517
534
  Start-Sleep -Seconds 2
518
535
  $detected = Detect-ClaudeInstall
519
- if (-not $detected.installed -and -not $SkipInstaller) {
520
- throw 'Claude Code installation completed but claude was still not detected.'
536
+ if (-not $detected.installed -and -not $SkipInstaller -and -not $installFailed) {
537
+ $warnings.Add('Claude Code installation completed but claude was still not detected.')
538
+ $installFailed = $true
521
539
  }
522
540
 
523
541
  if ($Force) {
@@ -535,7 +553,7 @@ $interruptions = [System.Collections.Generic.List[object]]::new()
535
553
  if ($shouldInstall -or $cleanedNpmShim) {
536
554
  $interruptions.Add([pscustomobject]@{
537
555
  kind = 'relaunch_required'
538
- summary = 'Relaunch Cats Desktop Host after the Claude Code install step, then rerun the packaged setup check.'
556
+ summary = 'Run Detect Again after the Claude Code install step if the command is not visible yet.'
539
557
  resumable = $true
540
558
  requiresRestart = $false
541
559
  requiresElevation = $false
@@ -555,7 +573,7 @@ $restartRequired = $false
555
573
  $result = [pscustomobject]@{
556
574
  helper = 'windows-claude-native-installer'
557
575
  mode = $executionMode
558
- status = if ($interruptions.Count -gt 0) { [string]$interruptions[0].kind } else { 'ready' }
576
+ status = if ($installFailed) { 'failed' } elseif ($interruptions.Count -gt 0) { [string]$interruptions[0].kind } else { 'ready' }
559
577
  installed = [bool]$detected.installed
560
578
  detectedVersion = if ($detected.detectedVersion) { $detected.detectedVersion } else { $null }
561
579
  commandPath = $detected.commandPath
@@ -568,4 +586,4 @@ $result = [pscustomobject]@{
568
586
  cleanedNpmShim = $cleanedNpmShim
569
587
  usedWingetFallback = $usedWingetFallback
570
588
  }
571
- Write-StructuredResult -Result $result -ExitCode 0
589
+ Write-StructuredResult -Result $result -ExitCode $(if ($installFailed) { 1 } else { 0 })
@@ -59,6 +59,7 @@ Set-StrictMode -Version Latest
59
59
  $ErrorActionPreference = 'Stop'
60
60
 
61
61
  . (Join-Path $PSScriptRoot '_HiddenProcess.ps1')
62
+ . (Join-Path $PSScriptRoot '_NativeInstallerSupport.ps1')
62
63
  . (Join-Path $PSScriptRoot '_PackagedUninstall.ps1')
63
64
 
64
65
  function Write-StructuredResult {
@@ -67,6 +68,10 @@ function Write-StructuredResult {
67
68
  [int]$ExitCode
68
69
  )
69
70
 
71
+ if (Get-Variable -Name catsInitialObservation -Scope Script -ErrorAction SilentlyContinue) {
72
+ Add-CatsNativeObservation -Result $Result -Before $script:catsInitialObservation -Attempted ([bool]$script:shouldInstall) -Skipped ([bool]$SkipInstaller)
73
+ }
74
+
70
75
  if ($Json) {
71
76
  $Result | ConvertTo-Json -Depth 10
72
77
  } else {
@@ -88,7 +93,13 @@ function Write-StructuredResult {
88
93
  }
89
94
 
90
95
  function Resolve-CursorExecutablePath {
91
- return Join-Path $env:USERPROFILE '.local\bin\cursor-agent.exe'
96
+ foreach ($name in @('cursor-agent.exe', 'cursor-agent.cmd', 'agent.exe', 'agent.cmd')) {
97
+ $candidate = Join-Path $env:LOCALAPPDATA "cursor-agent\$name"
98
+ if (Test-Path -LiteralPath $candidate -PathType Leaf) { return $candidate }
99
+ }
100
+ $legacy = Join-Path $env:USERPROFILE '.local\bin\cursor-agent.exe'
101
+ if (Test-Path -LiteralPath $legacy -PathType Leaf) { return $legacy }
102
+ return Join-Path $env:LOCALAPPDATA 'cursor-agent\cursor-agent.cmd'
92
103
  }
93
104
 
94
105
  function Refresh-UserPath {
@@ -133,6 +144,10 @@ function Detect-CursorInstall {
133
144
  }
134
145
  }
135
146
 
147
+ if ($installed -and -not $version -and $commandPath -match '\.(cmd|bat)$') {
148
+ $version = Get-CatsShimVersionText -Path $commandPath
149
+ }
150
+
136
151
  return [pscustomobject]@{
137
152
  installed = $installed
138
153
  commandPath = $commandPath
@@ -161,30 +176,11 @@ function Invoke-CursorInstaller {
161
176
  }
162
177
  }
163
178
 
164
- try {
165
- $installScript = Invoke-RestMethod 'https://cursor.com/install?win32=true'
166
- Invoke-Expression $installScript
167
- return [pscustomobject]@{
168
- usedPowerShell51Fallback = $false
169
- skipped = $false
170
- }
171
- } catch {
172
- $isPowerShell7 = $PSVersionTable.PSVersion.Major -ge 7
173
- $ps51Path = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
174
- if (-not $isPowerShell7 -or -not (Test-Path -LiteralPath $ps51Path -PathType Leaf)) {
175
- throw
176
- }
177
-
178
- & $ps51Path -NoProfile -ExecutionPolicy Bypass -Command "irm 'https://cursor.com/install?win32=true' | iex"
179
- if ($LASTEXITCODE -ne 0) {
180
- throw 'Cursor Agent installer fallback via Windows PowerShell 5.1 failed.'
181
- }
179
+ $installScript = Invoke-RestMethod 'https://cursor.com/install?win32=true' -TimeoutSec 30 -UseBasicParsing
180
+ $result = Invoke-CatsRemoteInstaller -ScriptText $installScript
181
+ $result | Add-Member -NotePropertyName usedPowerShell51Fallback -NotePropertyValue $true
182
+ return $result
182
183
 
183
- return [pscustomobject]@{
184
- usedPowerShell51Fallback = $true
185
- skipped = $false
186
- }
187
- }
188
184
  }
189
185
 
190
186
  if (-not $CheckOnly -and -not $Apply -and -not $Upgrade -and -not $Force -and -not $Uninstall) {
@@ -219,6 +215,7 @@ if ($Uninstall) {
219
215
  Invoke-PackagedProviderUninstall `
220
216
  -HelperId 'windows-cursor-native-installer' `
221
217
  -UserBinaryPath (Resolve-CursorExecutablePath) `
218
+ -ExtraUserOwnedPaths @((Join-Path $env:LOCALAPPDATA 'cursor-agent')) `
222
219
  -RedetectCommand { Detect-CursorInstall } `
223
220
  -EmitJson:$Json `
224
221
  -DryRun:$DryRun
@@ -289,9 +286,26 @@ if ($CheckOnly) {
289
286
  Write-StructuredResult -Result $result -ExitCode 0
290
287
  }
291
288
 
289
+ if ($DryRun) {
290
+ Write-CatsNativePreview -Helper 'windows-cursor-native-installer' -Mode $executionMode -Detected $detected -Actions $plannedActions.ToArray() -EmitJson ([bool]$Json)
291
+ }
292
+
293
+ $installFailed = $false
294
+ $catsInitialObservation = $detected.PSObject.Copy()
292
295
  $shouldInstall = $Force -or $Upgrade -or -not $detected.installed
296
+ if ($Upgrade -and -not $Force -and $detected.installed -and -not $SkipInstaller -and -not $DryRun) {
297
+ $versionGate = Test-CatsNativeUpgrade -Provider 'cursor' -InstalledVersion $detected.detectedVersion
298
+ $shouldInstall = $versionGate.shouldInstall
299
+ if (-not $versionGate.known) { $warnings.Add('Could not compare the published version; the official installer will verify the update.') }
300
+ if (-not $shouldInstall) { $plannedActions.Clear() }
301
+ }
302
+ $installFailed = $false
293
303
  if ($shouldInstall) {
294
- $installResult = Invoke-CursorInstaller
304
+ $installResult = try { Invoke-CursorInstaller } catch { [pscustomobject]@{ skipped = $false; success = $false; stderr = [string]$_.Exception.Message; usedPowerShell51Fallback = $false; usedWingetFallback = $false } }
305
+ if ($installResult.PSObject.Properties['success'] -and -not $installResult.success) {
306
+ $installFailed = $true
307
+ $warnings.Add($installResult.stderr)
308
+ }
295
309
  $usedPowerShell51Fallback = [bool]$installResult.usedPowerShell51Fallback
296
310
  if ($usedPowerShell51Fallback) {
297
311
  $warnings.Add('Cursor installer required the Windows PowerShell 5.1 fallback.')
@@ -302,8 +316,9 @@ if ($shouldInstall) {
302
316
 
303
317
  Start-Sleep -Seconds 2
304
318
  $detected = Detect-CursorInstall
305
- if (-not $detected.installed -and -not $SkipInstaller) {
306
- throw 'Cursor Agent installation completed but cursor-agent was still not detected.'
319
+ if (-not $detected.installed -and -not $SkipInstaller -and -not $installFailed) {
320
+ $warnings.Add('Cursor Agent installation completed but cursor-agent was still not detected.')
321
+ $installFailed = $true
307
322
  }
308
323
 
309
324
  if ($Force) {
@@ -320,7 +335,7 @@ $interruptions = [System.Collections.Generic.List[object]]::new()
320
335
  if ($shouldInstall) {
321
336
  $interruptions.Add([pscustomobject]@{
322
337
  kind = 'relaunch_required'
323
- summary = 'Relaunch Cats Desktop Host after the Cursor Agent install step, then rerun the packaged setup check.'
338
+ summary = 'Run Detect Again after the Cursor Agent install step if the command is not visible yet.'
324
339
  resumable = $true
325
340
  requiresRestart = $false
326
341
  requiresElevation = $false
@@ -339,7 +354,7 @@ if (-not $authSatisfied) {
339
354
  $result = [pscustomobject]@{
340
355
  helper = 'windows-cursor-native-installer'
341
356
  mode = $executionMode
342
- status = if ($interruptions.Count -gt 0) { [string]$interruptions[0].kind } else { 'ready' }
357
+ status = if ($installFailed) { 'failed' } elseif ($interruptions.Count -gt 0) { [string]$interruptions[0].kind } else { 'ready' }
343
358
  installed = [bool]$detected.installed
344
359
  detectedVersion = if ($detected.detectedVersion) { $detected.detectedVersion } else { $null }
345
360
  commandPath = $detected.commandPath
@@ -350,4 +365,4 @@ $result = [pscustomobject]@{
350
365
  interruptions = $interruptions.ToArray()
351
366
  usedPowerShell51Fallback = $usedPowerShell51Fallback
352
367
  }
353
- Write-StructuredResult -Result $result -ExitCode 0
368
+ Write-StructuredResult -Result $result -ExitCode $(if ($installFailed) { 1 } else { 0 })
@@ -31,6 +31,7 @@ Set-StrictMode -Version Latest
31
31
  $ErrorActionPreference = 'Stop'
32
32
 
33
33
  . (Join-Path $PSScriptRoot '_HiddenProcess.ps1')
34
+ . (Join-Path $PSScriptRoot '_NativeInstallerSupport.ps1')
34
35
  . (Join-Path $PSScriptRoot '_PackagedUninstall.ps1')
35
36
 
36
37
  function Write-StructuredResult {
@@ -39,6 +40,10 @@ function Write-StructuredResult {
39
40
  [int]$ExitCode
40
41
  )
41
42
 
43
+ if (Get-Variable -Name catsInitialObservation -Scope Script -ErrorAction SilentlyContinue) {
44
+ Add-CatsNativeObservation -Result $Result -Before $script:catsInitialObservation -Attempted ([bool]$script:shouldInstall) -Skipped ([bool]$SkipInstaller)
45
+ }
46
+
42
47
  if ($Json) {
43
48
  $Result | ConvertTo-Json -Depth 10
44
49
  } else {
@@ -126,12 +131,6 @@ function Invoke-DevinInstaller {
126
131
  return [pscustomobject]@{ skipped = $true; success = $true; exitCode = 0; stderr = '' }
127
132
  }
128
133
 
129
- $tempScript = [System.IO.Path]::Combine(
130
- [System.IO.Path]::GetTempPath(),
131
- "cats-devin-install-$([System.Guid]::NewGuid().ToString('N')).ps1"
132
- )
133
- $stdoutPath = "$tempScript.stdout"
134
- $stderrPath = "$tempScript.stderr"
135
134
  # Devin's setup.ps1 ends by launching the interactive `devin setup` wizard.
136
135
  # Packaged setup runs with no console a prompt can reach, so an unstripped
137
136
  # installer would stall the step rather than fail.
@@ -174,46 +173,8 @@ function Invoke-DevinInstaller {
174
173
  }
175
174
  $bootstrap = ($patchedLines -join [Environment]::NewLine)
176
175
 
177
- try {
178
- [System.IO.File]::WriteAllText(
179
- $tempScript,
180
- $bootstrap,
181
- [System.Text.UTF8Encoding]::new($false)
182
- )
183
- $powerShellExe = if (Get-Command pwsh.exe -ErrorAction SilentlyContinue) {
184
- 'pwsh.exe'
185
- } else {
186
- 'powershell.exe'
187
- }
188
- $process = Start-Process -FilePath $powerShellExe `
189
- -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $tempScript) `
190
- -Wait -PassThru -NoNewWindow `
191
- -RedirectStandardOutput $stdoutPath `
192
- -RedirectStandardError $stderrPath
193
- $stderrRaw = if (Test-Path -LiteralPath $stderrPath -PathType Leaf) {
194
- Get-Content -LiteralPath $stderrPath -Raw -ErrorAction SilentlyContinue
195
- } else { $null }
176
+ return Invoke-CatsRemoteInstaller -ScriptText $bootstrap -RetrySharingViolation
196
177
 
197
- return [pscustomobject]@{
198
- skipped = $false
199
- success = ($process.ExitCode -eq 0)
200
- exitCode = $process.ExitCode
201
- stderr = if ($null -eq $stderrRaw) { '' } else { [string]$stderrRaw }
202
- }
203
- } catch {
204
- return [pscustomobject]@{
205
- skipped = $false
206
- success = $false
207
- exitCode = -1
208
- stderr = "Failed to invoke Devin installer: $($_.Exception.Message)"
209
- }
210
- } finally {
211
- foreach ($path in @($tempScript, $stdoutPath, $stderrPath)) {
212
- if (Test-Path -LiteralPath $path -PathType Leaf) {
213
- Remove-Item -LiteralPath $path -Force -ErrorAction SilentlyContinue
214
- }
215
- }
216
- }
217
178
  }
218
179
 
219
180
  if (-not $CheckOnly -and -not $Apply -and -not $Upgrade -and -not $Force -and -not $Uninstall) {
@@ -303,7 +264,15 @@ if ($CheckOnly) {
303
264
  }) -ExitCode 0
304
265
  }
305
266
 
267
+ $installFailed = $false
268
+ $catsInitialObservation = $detected.PSObject.Copy()
306
269
  $shouldInstall = $Force -or $Upgrade -or -not $detected.installed
270
+ if ($Upgrade -and -not $Force -and $detected.installed -and -not $SkipInstaller -and -not $DryRun) {
271
+ $versionGate = Test-CatsNativeUpgrade -Provider 'devin' -InstalledVersion $detected.detectedVersion
272
+ $shouldInstall = $versionGate.shouldInstall
273
+ if (-not $versionGate.known) { $warnings.Add('Could not compare the published version; the official installer will verify the update.') }
274
+ if (-not $shouldInstall) { $plannedActions.Clear() }
275
+ }
307
276
  $installFailed = $false
308
277
  $installSkipped = $false
309
278
  if ($shouldInstall) {
@@ -347,12 +316,12 @@ if ($shouldInstall) {
347
316
  }
348
317
  }
349
318
 
350
- $manualSteps.Add('Run `devin login` or set XAI_API_KEY before first use.')
319
+ $manualSteps.Add('Run `devin auth login` to authenticate; check with `devin auth status`. The interactive setup wizard is skipped.')
351
320
  $interruptions = [System.Collections.Generic.List[object]]::new()
352
321
  if ($shouldInstall -and -not $installFailed -and -not $DryRun) {
353
322
  $interruptions.Add([pscustomobject]@{
354
323
  kind = 'relaunch_required'
355
- summary = 'Relaunch Cats Desktop Host after the Devin install step, then rerun the packaged setup check.'
324
+ summary = 'Run Detect Again after the Devin install step if the command is not visible yet.'
356
325
  resumable = $true
357
326
  requiresRestart = $false
358
327
  requiresElevation = $false
@@ -179,6 +179,10 @@ $mode = if ($CheckOnly) {
179
179
  'apply'
180
180
  }
181
181
 
182
+ # Desktop can outlive an installation. A fresh check must include the registry
183
+ # PATH, while retaining caller-provided command locations in this process.
184
+ $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' +
185
+ [System.Environment]::GetEnvironmentVariable('Path', 'User') + ';' + $env:Path
182
186
  $installed = Test-GhInstalled
183
187
  $detected = Get-GhDetectedVersion -Override $DetectedVersion
184
188
  $commandPath = if ($SkipGhProbe) { '' } else { Resolve-GhCommandPath }
@@ -59,6 +59,7 @@ Set-StrictMode -Version Latest
59
59
  $ErrorActionPreference = 'Stop'
60
60
 
61
61
  . (Join-Path $PSScriptRoot '_HiddenProcess.ps1')
62
+ . (Join-Path $PSScriptRoot '_NativeInstallerSupport.ps1')
62
63
  . (Join-Path $PSScriptRoot '_PackagedUninstall.ps1')
63
64
 
64
65
  function Write-StructuredResult {
@@ -67,6 +68,10 @@ function Write-StructuredResult {
67
68
  [int]$ExitCode
68
69
  )
69
70
 
71
+ if (Get-Variable -Name catsInitialObservation -Scope Script -ErrorAction SilentlyContinue) {
72
+ Add-CatsNativeObservation -Result $Result -Before $script:catsInitialObservation -Attempted ([bool]$script:shouldInstall) -Skipped ([bool]$SkipInstaller)
73
+ }
74
+
70
75
  if ($Json) {
71
76
  $Result | ConvertTo-Json -Depth 10
72
77
  } else {
@@ -364,7 +369,19 @@ if ($CheckOnly) {
364
369
  Write-StructuredResult -Result $result -ExitCode 0
365
370
  }
366
371
 
372
+ if ($DryRun) {
373
+ Write-CatsNativePreview -Helper 'windows-goose-native-installer' -Mode $executionMode -Detected $detected -Actions $plannedActions.ToArray() -EmitJson ([bool]$Json)
374
+ }
375
+
376
+ $installFailed = $false
377
+ $catsInitialObservation = $detected.PSObject.Copy()
367
378
  $shouldInstall = $Force -or $Upgrade -or -not $detected.installed
379
+ if ($Upgrade -and -not $Force -and $detected.installed -and -not $SkipInstaller -and -not $DryRun) {
380
+ $versionGate = Test-CatsNativeUpgrade -Provider 'goose' -InstalledVersion $detected.detectedVersion
381
+ $shouldInstall = $versionGate.shouldInstall
382
+ if (-not $versionGate.known) { $warnings.Add('Could not compare the published version; the official installer will verify the update.') }
383
+ if (-not $shouldInstall) { $plannedActions.Clear() }
384
+ }
368
385
  $installFailed = $false
369
386
  $installSkipped = $false
370
387
  if ($shouldInstall) {
@@ -410,7 +427,7 @@ $interruptions = [System.Collections.Generic.List[object]]::new()
410
427
  if ($shouldInstall -and -not $installFailed) {
411
428
  $interruptions.Add([pscustomobject]@{
412
429
  kind = 'relaunch_required'
413
- summary = 'Relaunch Cats Desktop Host after the Goose install step, then rerun the packaged setup check.'
430
+ summary = 'Run Detect Again after the Goose install step if the command is not visible yet.'
414
431
  resumable = $true
415
432
  requiresRestart = $false
416
433
  requiresElevation = $false
@@ -28,6 +28,7 @@ Set-StrictMode -Version Latest
28
28
  $ErrorActionPreference = 'Stop'
29
29
 
30
30
  . (Join-Path $PSScriptRoot '_HiddenProcess.ps1')
31
+ . (Join-Path $PSScriptRoot '_NativeInstallerSupport.ps1')
31
32
  . (Join-Path $PSScriptRoot '_PackagedUninstall.ps1')
32
33
 
33
34
  function Write-StructuredResult {
@@ -36,6 +37,10 @@ function Write-StructuredResult {
36
37
  [int]$ExitCode
37
38
  )
38
39
 
40
+ if (Get-Variable -Name catsInitialObservation -Scope Script -ErrorAction SilentlyContinue) {
41
+ Add-CatsNativeObservation -Result $Result -Before $script:catsInitialObservation -Attempted ([bool]$script:shouldInstall) -Skipped ([bool]$SkipInstaller)
42
+ }
43
+
39
44
  if ($Json) {
40
45
  $Result | ConvertTo-Json -Depth 10
41
46
  } else {
@@ -273,7 +278,15 @@ if ($CheckOnly) {
273
278
  }) -ExitCode 0
274
279
  }
275
280
 
281
+ $installFailed = $false
282
+ $catsInitialObservation = $detected.PSObject.Copy()
276
283
  $shouldInstall = $Force -or $Upgrade -or -not $detected.installed
284
+ if ($Upgrade -and -not $Force -and $detected.installed -and -not $SkipInstaller -and -not $DryRun) {
285
+ $versionGate = Test-CatsNativeUpgrade -Provider 'grok' -InstalledVersion $detected.detectedVersion
286
+ $shouldInstall = $versionGate.shouldInstall
287
+ if (-not $versionGate.known) { $warnings.Add('Could not compare the published version; the official installer will verify the update.') }
288
+ if (-not $shouldInstall) { $plannedActions.Clear() }
289
+ }
277
290
  $installFailed = $false
278
291
  $installSkipped = $false
279
292
  if ($shouldInstall) {
@@ -322,7 +335,7 @@ $interruptions = [System.Collections.Generic.List[object]]::new()
322
335
  if ($shouldInstall -and -not $installFailed -and -not $DryRun) {
323
336
  $interruptions.Add([pscustomobject]@{
324
337
  kind = 'relaunch_required'
325
- summary = 'Relaunch Cats Desktop Host after the Grok install step, then rerun the packaged setup check.'
338
+ summary = 'Run Detect Again after the Grok install step if the command is not visible yet.'
326
339
  resumable = $true
327
340
  requiresRestart = $false
328
341
  requiresElevation = $false
@@ -59,6 +59,7 @@ Set-StrictMode -Version Latest
59
59
  $ErrorActionPreference = 'Stop'
60
60
 
61
61
  . (Join-Path $PSScriptRoot '_HiddenProcess.ps1')
62
+ . (Join-Path $PSScriptRoot '_NativeInstallerSupport.ps1')
62
63
  . (Join-Path $PSScriptRoot '_PackagedUninstall.ps1')
63
64
 
64
65
  function Write-StructuredResult {
@@ -67,6 +68,10 @@ function Write-StructuredResult {
67
68
  [int]$ExitCode
68
69
  )
69
70
 
71
+ if (Get-Variable -Name catsInitialObservation -Scope Script -ErrorAction SilentlyContinue) {
72
+ Add-CatsNativeObservation -Result $Result -Before $script:catsInitialObservation -Attempted ([bool]$script:shouldInstall) -Skipped ([bool]$SkipInstaller)
73
+ }
74
+
70
75
  if ($Json) {
71
76
  $Result | ConvertTo-Json -Depth 10
72
77
  } else {
@@ -136,6 +141,7 @@ function Detect-JunieInstall {
136
141
  }
137
142
  }
138
143
 
144
+ if ($installed -and -not $version -and $commandPath -match '\.(cmd|bat)$') { $version = Get-CatsShimVersionText -Path $commandPath }
139
145
  return [pscustomobject]@{
140
146
  installed = $installed
141
147
  commandPath = $commandPath
@@ -165,11 +171,8 @@ function Invoke-JunieInstaller {
165
171
  }
166
172
  }
167
173
 
168
- $installScript = Invoke-RestMethod 'https://junie.jetbrains.com/install.ps1'
169
- Invoke-Expression $installScript
170
- return [pscustomobject]@{
171
- skipped = $false
172
- }
174
+ $installScript = Invoke-RestMethod -TimeoutSec 30 -UseBasicParsing 'https://junie.jetbrains.com/install.ps1'
175
+ return Invoke-CatsRemoteInstaller -ScriptText $installScript
173
176
  }
174
177
 
175
178
  if (-not $CheckOnly -and -not $Apply -and -not $Upgrade -and -not $Force -and -not $Uninstall) {
@@ -278,17 +281,35 @@ if ($CheckOnly) {
278
281
  Write-StructuredResult -Result $result -ExitCode 0
279
282
  }
280
283
 
284
+ if ($DryRun) {
285
+ Write-CatsNativePreview -Helper 'windows-junie-native-installer' -Mode $executionMode -Detected $detected -Actions $plannedActions.ToArray() -EmitJson ([bool]$Json)
286
+ }
287
+
288
+ $installFailed = $false
289
+ $catsInitialObservation = $detected.PSObject.Copy()
281
290
  $shouldInstall = $Force -or $Upgrade -or -not $detected.installed
291
+ if ($Upgrade -and -not $Force -and $detected.installed -and -not $SkipInstaller -and -not $DryRun) {
292
+ $versionGate = Test-CatsNativeUpgrade -Provider 'junie' -InstalledVersion $detected.detectedVersion
293
+ $shouldInstall = $versionGate.shouldInstall
294
+ if (-not $versionGate.known) { $warnings.Add('Could not compare the published version; the official installer will verify the update.') }
295
+ if (-not $shouldInstall) { $plannedActions.Clear() }
296
+ }
297
+ $installFailed = $false
282
298
  if ($shouldInstall) {
283
- $installResult = Invoke-JunieInstaller
299
+ $installResult = try { Invoke-JunieInstaller } catch { [pscustomobject]@{ skipped = $false; success = $false; stderr = [string]$_.Exception.Message; usedPowerShell51Fallback = $false; usedWingetFallback = $false } }
300
+ if ($installResult.PSObject.Properties['success'] -and -not $installResult.success) {
301
+ $installFailed = $true
302
+ $warnings.Add($installResult.stderr)
303
+ }
284
304
  if ($installResult.skipped) {
285
305
  $warnings.Add('Installer invocation was skipped by request.')
286
306
  }
287
307
 
288
308
  Start-Sleep -Seconds 2
289
309
  $detected = Detect-JunieInstall
290
- if (-not $detected.installed -and -not $SkipInstaller) {
291
- throw 'Junie installation completed but junie was still not detected.'
310
+ if (-not $detected.installed -and -not $SkipInstaller -and -not $installFailed) {
311
+ $warnings.Add('Junie installation completed but junie was still not detected.')
312
+ $installFailed = $true
292
313
  }
293
314
 
294
315
  if ($Force) {
@@ -306,7 +327,7 @@ $interruptions = [System.Collections.Generic.List[object]]::new()
306
327
  if ($shouldInstall) {
307
328
  $interruptions.Add([pscustomobject]@{
308
329
  kind = 'relaunch_required'
309
- summary = 'Relaunch Cats Desktop Host after the Junie install step, then rerun the packaged setup check.'
330
+ summary = 'Run Detect Again after the Junie install step if the command is not visible yet.'
310
331
  resumable = $true
311
332
  requiresRestart = $false
312
333
  requiresElevation = $false
@@ -325,7 +346,7 @@ if (-not $authSatisfied) {
325
346
  $result = [pscustomobject]@{
326
347
  helper = 'windows-junie-native-installer'
327
348
  mode = $executionMode
328
- status = if ($interruptions.Count -gt 0) { [string]$interruptions[0].kind } else { 'ready' }
349
+ status = if ($installFailed) { 'failed' } elseif ($interruptions.Count -gt 0) { [string]$interruptions[0].kind } else { 'ready' }
329
350
  installed = [bool]$detected.installed
330
351
  detectedVersion = if ($detected.detectedVersion) { $detected.detectedVersion } else { $null }
331
352
  commandPath = $detected.commandPath
@@ -336,4 +357,4 @@ $result = [pscustomobject]@{
336
357
  manualSteps = $manualSteps.ToArray()
337
358
  interruptions = $interruptions.ToArray()
338
359
  }
339
- Write-StructuredResult -Result $result -ExitCode 0
360
+ Write-StructuredResult -Result $result -ExitCode $(if ($installFailed) { 1 } else { 0 })