@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
@@ -4,6 +4,7 @@ if [ -n "${CATS_PLATFORM_UNIX_PROVIDER_COMMON_SH:-}" ]; then
4
4
  return 0 2>/dev/null || exit 0
5
5
  fi
6
6
  readonly CATS_PLATFORM_UNIX_PROVIDER_COMMON_SH=1
7
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/provider-version-common.sh"
7
8
 
8
9
  json_escape() {
9
10
  local value="$1"
@@ -343,7 +344,7 @@ run_downloaded_shell_installer() {
343
344
  local installer_path status=0
344
345
 
345
346
  installer_path="$(mktemp)"
346
- if ! curl -fsSL "$url" -o "$installer_path"; then
347
+ if ! curl -fsSL --connect-timeout 10 --max-time 60 "$url" -o "$installer_path"; then
347
348
  rm -f "$installer_path"
348
349
  printf 'Failed to download the installer from %s\n' "$url" >&2
349
350
  return 1
@@ -411,26 +412,8 @@ run_remote_pipe_installer() {
411
412
  url="$(provider_install_url "$provider")"
412
413
 
413
414
  case "$provider" in
414
- antigravity|grok)
415
- curl -fsSL "$url" | bash
416
- ;;
417
- goose)
418
- curl -fsSL "$url" | env CONFIGURE=false bash
419
- ;;
420
- muse)
421
- # Download first, then run: with `curl ... | bash` a failed download still
422
- # exits 0, because bash reads empty input and succeeds.
423
- run_downloaded_shell_installer "$url"
424
- ;;
425
- cursor)
426
- curl "$url" -fsSL | bash
427
- ;;
428
- claude|junie)
429
- curl -fsSL "$url" | bash
430
- ;;
431
- *)
432
- return 1
433
- ;;
415
+ goose) CONFIGURE=false run_downloaded_shell_installer "$url" ;;
416
+ *) run_downloaded_shell_installer "$url" ;;
434
417
  esac
435
418
  }
436
419
 
@@ -457,20 +440,14 @@ run_kiro_installer() {
457
440
  local installer_path
458
441
 
459
442
  installer_path="$(mktemp)"
460
- curl -fsSL "$(provider_install_url kiro)" -o "$installer_path"
443
+ curl -fsSL --connect-timeout 10 --max-time 60 "$(provider_install_url kiro)" -o "$installer_path" || { rm -f "$installer_path"; return 1; }
461
444
  chmod +x "$installer_path"
462
445
 
463
- if [ "$mode" = 'reinstall' ]; then
464
- rm -f "$HOME/.local/bin/kiro-cli" "$HOME/.local/bin/kiro-cli-chat" "$HOME/.local/bin/kiro-cli-term"
465
- if [ "$platform" = 'macos' ]; then
466
- rm -rf '/Applications/Kiro CLI.app'
467
- rm -f "$HOME/.local/bin/bash (kiro-cli-term)" "$HOME/.local/bin/fish (kiro-cli-term)" \
468
- "$HOME/.local/bin/nu (kiro-cli-term)" "$HOME/.local/bin/zsh (kiro-cli-term)"
469
- fi
470
- fi
471
446
 
472
- bash "$installer_path" --force < /dev/null
447
+ local install_status=0
448
+ bash "$installer_path" --force < /dev/null || install_status=$?
473
449
  rm -f "$installer_path"
450
+ [ "$install_status" -eq 0 ] || return "$install_status"
474
451
 
475
452
  if [ "$platform" = 'macos' ]; then
476
453
  ensure_kiro_cli_symlink_macos
@@ -485,15 +462,17 @@ run_provider_install_action() {
485
462
 
486
463
  case "$provider" in
487
464
  antigravity)
488
- if [ "$action" = 'upgrade' ]; then
489
- rm -f "$HOME/.local/bin/agy" || true
490
- run_remote_pipe_installer "$provider"
491
- elif [ "$action" = 'force' ]; then
492
- rm -f "$HOME/.local/bin/agy" || true
493
- run_remote_pipe_installer "$provider"
494
- else
495
- run_remote_pipe_installer "$provider"
465
+ local backup='' result=0 destination="$HOME/.local/bin/agy"
466
+ if { [ "$action" = upgrade ] || [ "$action" = force ]; } && [ -f "$destination" ] && [ ! -L "$destination" ]; then
467
+ backup="$(mktemp)" || return 1
468
+ cp -p "$destination" "$backup" || { rm -f "$backup"; return 1; }
469
+ rm -f "$destination" || { rm -f "$backup"; return 1; }
496
470
  fi
471
+ run_remote_pipe_installer "$provider" || result=$?
472
+ [ -x "$destination" ] || result=1
473
+ if [ "$result" -ne 0 ] && [ -n "$backup" ]; then cp -p "$backup" "$destination" || true; fi
474
+ [ -z "$backup" ] || rm -f "$backup"
475
+ return "$result"
497
476
  ;;
498
477
  grok)
499
478
  run_remote_pipe_installer "$provider"
@@ -508,7 +487,7 @@ run_provider_install_action() {
508
487
  ;;
509
488
  claude)
510
489
  if [ "$action" = 'upgrade' ] && current_command="$(detect_provider_command "$platform" "$provider")"; then
511
- "$current_command" update || true
490
+ "$current_command" update
512
491
  else
513
492
  run_remote_pipe_installer "$provider"
514
493
  fi
@@ -986,76 +965,72 @@ run_native_provider_installer() {
986
965
  return 0
987
966
  fi
988
967
 
989
- printf 'Installing %s...\n' "$display_name"
990
- if [ "$force" = 'true' ]; then
991
- run_provider_install_action "$platform" "$provider" 'force'
992
- elif [ "$upgrade" = 'true' ]; then
993
- run_provider_install_action "$platform" "$provider" 'upgrade'
994
- else
995
- run_provider_install_action "$platform" "$provider" 'install'
968
+ local before_version="$detected_version" latest='' version_state=unknown
969
+ local install_status=0 attempted=true final_installed=false observed_change=unchanged
970
+ if [ "$upgrade" = true ] && [ "$force" = false ] && [ "$initial_installed" = true ]; then
971
+ latest="$(cats_native_latest_version "$provider")"
972
+ version_state="$(cats_native_version_state "$provider" "$detected_version" "$latest")"
973
+ if [ "$version_state" = current ]; then
974
+ attempted=false
975
+ planned_actions=()
976
+ elif [ -z "$latest" ] && [ "$provider" != claude ] && [ "$provider" != muse ]; then
977
+ warnings+=('Could not compare the published version; the official installer will verify the update.')
978
+ fi
996
979
  fi
997
- applied_changes=("${planned_actions[@]}")
998
-
999
- ensure_provider_bin_path_export "$shell_rc" "$provider"
1000
- ensure_provider_alias "$shell_rc" "$provider"
1001
-
1002
- while [ $attempt -le 3 ]; do
980
+ if [ "$attempted" = true ]; then
981
+ printf 'Installing %s...\n' "$display_name" >&2
982
+ if [ "$force" = true ]; then
983
+ run_provider_install_action "$platform" "$provider" force || install_status=$?
984
+ elif [ "$upgrade" = true ]; then
985
+ run_provider_install_action "$platform" "$provider" upgrade || install_status=$?
986
+ else
987
+ run_provider_install_action "$platform" "$provider" install || install_status=$?
988
+ fi
989
+ fi
990
+ hash -r
991
+ if [ "$install_status" -eq 0 ]; then
992
+ ensure_provider_bin_path_export "$shell_rc" "$provider"
993
+ ensure_provider_alias "$shell_rc" "$provider"
994
+ fi
995
+ for attempt in 1 2 3; do
1003
996
  if command_path="$(detect_provider_command "$platform" "$provider")"; then
997
+ final_installed=true
1004
998
  detected_version="$(provider_version_line "$command_path" "$provider")"
1005
- if [ "$emit_json" = 'true' ]; then
1006
- warnings=("Reload your shell if ${display_name} is not visible yet: source ${shell_rc}")
1007
- printf '{'
1008
- printf '"helper":"%s-%s-native-installer",' "$platform" "$provider"
1009
- printf '"mode":"%s",' "$execution_mode"
1010
- printf '"status":"ready",'
1011
- printf '"installed":true,'
1012
- printf '"commandPath":"%s",' "$(json_escape "$command_path")"
1013
- printf '"detectedVersion":"%s",' "$(json_escape "$detected_version")"
1014
- printf '"plannedActions":'
1015
- json_string_array "${planned_actions[@]}"
1016
- printf ','
1017
- printf '"appliedChanges":'
1018
- json_string_array "${applied_changes[@]}"
1019
- printf ','
1020
- printf '"warnings":'
1021
- json_string_array "${warnings[@]}"
1022
- printf ','
1023
- printf '"manualSteps":'
1024
- json_string_array "$(provider_manual_steps "$provider")"
1025
- printf ','
1026
- printf '"interruptions":[]'
1027
- printf '}\n'
1028
- else
1029
- printf '%s ready: %s\n' "$display_name" "$detected_version"
1030
- printf 'Reload your shell if %s is not visible yet: source %s\n' "$display_name" "$shell_rc"
1031
- fi
1032
- return 0
999
+ break
1033
1000
  fi
1034
-
1001
+ [ "$install_status" -eq 0 ] && [ "$attempted" = true ] || break
1035
1002
  sleep 2
1036
- attempt=$((attempt + 1))
1037
1003
  done
1038
-
1039
- if [ "$emit_json" = 'true' ]; then
1040
- printf '{'
1041
- printf '"helper":"%s-%s-native-installer",' "$platform" "$provider"
1042
- printf '"mode":"%s",' "$execution_mode"
1043
- printf '"status":"failed",'
1044
- printf '"installed":false,'
1045
- printf '"commandPath":null,'
1046
- printf '"detectedVersion":null,'
1047
- printf '"plannedActions":'
1048
- json_string_array "${planned_actions[@]}"
1049
- printf ','
1050
- printf '"appliedChanges":'
1051
- json_string_array "${applied_changes[@]}"
1052
- printf ','
1053
- printf '"warnings":[],'
1054
- printf '"manualSteps":[],'
1055
- printf '"interruptions":[]'
1056
- printf '}\n'
1057
- return 1
1004
+ local final_status=ready
1005
+ if [ "$install_status" -ne 0 ] || [ "$final_installed" = false ]; then
1006
+ final_status=failed
1007
+ observed_change=failed
1008
+ warnings+=("Installer or verification failed (exit $install_status); any previous installation is shown separately.")
1009
+ elif [ "$attempted" = false ]; then observed_change=unchanged
1010
+ elif [ "$initial_installed" = false ]; then observed_change=installed
1011
+ elif [ -n "$before_version" ] && [ -n "$detected_version" ] && [ "$before_version" != "$detected_version" ]; then
1012
+ case "$(cats_version_compare "$(cats_native_version_token "$provider" "$detected_version")" "$(cats_native_version_token "$provider" "$before_version")")" in
1013
+ 1) observed_change=upgraded ;; -1) observed_change=downgraded ;; *) observed_change=version_changed ;;
1014
+ esac
1015
+ else observed_change=unchanged; fi
1016
+ if [ "$final_status" = ready ] && { [ "$upgrade" = true ] || [ "$force" = true ]; }; then
1017
+ cats_cleanup_native_versions "$provider" "$detected_version" || true
1058
1018
  fi
1059
- printf 'Failed to verify %s after install.\n' "$display_name" >&2
1060
- return 1
1019
+ case "$observed_change" in
1020
+ installed|upgraded|downgraded|version_changed) applied_changes=("${observed_change}_${provider}_installation") ;;
1021
+ esac
1022
+ if [ "$emit_json" = true ]; then
1023
+ printf '{"helper":"%s-%s-native-installer","mode":"%s","status":"%s",' "$platform" "$provider" "$execution_mode" "$final_status"
1024
+ printf '"installed":%s,"commandPath":"%s","detectedVersion":"%s",' "$final_installed" "$(json_escape "$command_path")" "$(json_escape "$detected_version")"
1025
+ printf '"previousVersion":"%s","observedChange":"%s",' "$(json_escape "$before_version")" "$observed_change"
1026
+ printf '"summary":"%s: %s -> %s",' "$observed_change" "$(json_escape "$before_version")" "$(json_escape "$detected_version")"
1027
+ printf '"plannedActions":'; json_string_array "${planned_actions[@]}"
1028
+ printf ',"appliedChanges":'; json_string_array "${applied_changes[@]}"
1029
+ printf ',"warnings":'; json_string_array "${warnings[@]}"
1030
+ printf ',"manualSteps":'; json_string_array "$(provider_manual_steps "$provider")"
1031
+ printf ',"interruptions":[]}\n'
1032
+ else
1033
+ printf '%s %s: %s -> %s\n' "$display_name" "$observed_change" "$before_version" "$detected_version"
1034
+ fi
1035
+ [ "$final_status" = ready ]
1061
1036
  }
@@ -0,0 +1,247 @@
1
+ #!/usr/bin/env bash
2
+ # Source this file for passive native version comparison and bounded vendor queries.
3
+ # Usage: source provider-version-common.sh; cats_native_latest_version <provider>
4
+ # Adapted from environment-bootstrap shared/posix/upstream-version.sh at 752dc13.
5
+ # Queries return an empty value on failure; check-only mode never invokes them.
6
+ cats_version_token() {
7
+ printf '%s' "$1" | tr -d '\r' | grep -oE '[0-9]+(\.[0-9]+)+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*|\.[A-Za-z][0-9A-Za-z]*(\.[0-9]+)*)?' | head -n 1 || true
8
+ }
9
+
10
+ # cats_version_compare <a> <b>
11
+ # 只比數字段、補滿四段:印 -1(a 較舊)、0、1(a 較新);任一邊解析不出版本印空字串。
12
+ # 空字串要當成「無法比較、交回原本流程」,不能當成落後,否則查不到版本時每次都會重裝。
13
+ cats_version_compare() {
14
+ local a b i x y
15
+ a="$(printf '%s' "$1" | tr -d '\r' | grep -oE '[0-9]+(\.[0-9]+)*' | head -n 1 || true)"
16
+ b="$(printf '%s' "$2" | tr -d '\r' | grep -oE '[0-9]+(\.[0-9]+)*' | head -n 1 || true)"
17
+ if [ -z "$a" ] || [ -z "$b" ]; then
18
+ printf ''
19
+ return 0
20
+ fi
21
+
22
+ i=1
23
+ while [ "$i" -le 4 ]; do
24
+ x="$(printf '%s' "$a" | awk -F. -v n="$i" '{print $n}')"
25
+ y="$(printf '%s' "$b" | awk -F. -v n="$i" '{print $n}')"
26
+ [ -n "$x" ] || x=0
27
+ [ -n "$y" ] || y=0
28
+ if [ "$((10#$x))" -lt "$((10#$y))" ]; then printf -- '-1'; return 0; fi
29
+ if [ "$((10#$x))" -gt "$((10#$y))" ]; then printf '1'; return 0; fi
30
+ i=$((i + 1))
31
+ done
32
+ printf '0'
33
+ }
34
+
35
+ # _cats_json_string_field <field> :從 stdin 的 JSON 文字撈第一個 "field":"value" 的 value
36
+ _cats_json_string_field() {
37
+ grep -oE "\"$1\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | head -n 1 | sed -E 's/.*"([^"]*)"$/\1/'
38
+ }
39
+
40
+ # cats_github_latest_release <owner/repo>
41
+ # GitHub Releases 的最新穩定版 tag(去掉開頭的 v);失敗印空。
42
+ cats_github_latest_release() {
43
+ local json
44
+ json="$(curl -fsSL --connect-timeout 5 --max-time 20 -H 'Accept: application/vnd.github+json' "https://api.github.com/repos/$1/releases/latest" 2>/dev/null)" || return 0
45
+ printf '%s' "$json" | _cats_json_string_field tag_name | sed 's/^v//' || true
46
+ }
47
+
48
+ # cats_antigravity_latest_version [已下載的官方 install.sh]
49
+ # 官方腳本讀的是 $DOWNLOAD_BASE_URL/manifests/<platform>.json 的 version。
50
+ # 傳入抓下來的腳本可以順便從裡面撈 base URL,官方搬家時就不必改這裡;撈不到用寫死的。
51
+ # platform 的算法照抄官方(darwin_arm64 / linux_amd64 / linux_amd64_musl …)。
52
+ cats_antigravity_latest_version() {
53
+ local base='https://antigravity-cli-auto-updater-974169037036.us-central1.run.app'
54
+ local found os arch platform
55
+
56
+ if [ -n "${1:-}" ] && [ -r "$1" ]; then
57
+ found="$(grep -oE 'DOWNLOAD_BASE_URL="https://[^"]+"' "$1" | head -n 1 | sed -E 's/^DOWNLOAD_BASE_URL="(.*)"$/\1/')"
58
+ [ -n "$found" ] && base="$found"
59
+ fi
60
+
61
+ case "$(uname -s)" in
62
+ Darwin) os=darwin ;;
63
+ Linux) os=linux ;;
64
+ *) return 0 ;;
65
+ esac
66
+ case "$(uname -m)" in
67
+ x86_64|amd64) arch=amd64 ;;
68
+ arm64|aarch64) arch=arm64 ;;
69
+ *) return 0 ;;
70
+ esac
71
+ if [ "$os" = linux ] && { [ -f /lib/libc.musl-x86_64.so.1 ] || [ -f /lib/libc.musl-aarch64.so.1 ] || ldd /bin/ls 2>&1 | grep -q musl; }; then
72
+ platform="linux_${arch}_musl"
73
+ else
74
+ platform="${os}_${arch}"
75
+ fi
76
+
77
+ curl -fsSL --connect-timeout 5 --max-time 20 "$base/manifests/$platform.json" 2>/dev/null | _cats_json_string_field version || true
78
+ }
79
+
80
+ # cats_cursor_agent_latest_version <已下載的官方 install 腳本>
81
+ # 官方 https://cursor.com/install 是支寫死版本的腳本,目錄名就是版本
82
+ # (…/versions/2026.09.10-fd3934a)。版本是「日期-commit」,cats_version_compare 只比得到
83
+ # 日期,同日不同 commit 由呼叫端用字串相等另判。
84
+ cats_cursor_agent_latest_version() {
85
+ [ -n "${1:-}" ] && [ -r "$1" ] || return 0
86
+ grep -oE 'versions/[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[0-9A-Za-z]+' "$1" | head -n 1 | sed 's#^versions/##' || true
87
+ }
88
+
89
+ # cats_junie_latest_build
90
+ # 官方 install.sh 讀 GitHub raw 的 update-info.jsonl:一行一個 build,version 是 build 號
91
+ # (3110.7,也是 versions/ 底下的目錄名)、marketing 才是 26.9.7、platform 是
92
+ # linux-amd64 / macos-aarch64 這種。印本平台最大的 build 號;失敗印空。
93
+ cats_junie_latest_build() {
94
+ local os arch platform jsonl line v best=''
95
+
96
+ case "$(uname -s)" in
97
+ Darwin) os=macos ;;
98
+ Linux) os=linux ;;
99
+ *) return 0 ;;
100
+ esac
101
+ case "$(uname -m)" in
102
+ x86_64|amd64) arch=amd64 ;;
103
+ arm64|aarch64) arch=aarch64 ;;
104
+ *) return 0 ;;
105
+ esac
106
+ platform="${os}-${arch}"
107
+
108
+ jsonl="$(curl -fsSL --connect-timeout 5 --max-time 20 'https://raw.githubusercontent.com/jetbrains-junie/junie/main/update-info.jsonl' 2>/dev/null)" || return 0
109
+
110
+ while IFS= read -r line; do
111
+ case "$line" in
112
+ *"\"platform\":\"$platform\""*) ;;
113
+ *) continue ;;
114
+ esac
115
+ v="$(printf '%s' "$line" | _cats_json_string_field version || true)"
116
+ [ -n "$v" ] || continue
117
+ if [ -z "$best" ] || [ "$(cats_version_compare "$v" "$best")" = "1" ]; then
118
+ best="$v"
119
+ fi
120
+ done <<< "$jsonl"
121
+
122
+ printf '%s' "$best"
123
+ }
124
+
125
+ # cats_kiro_latest_version
126
+ # Kiro 的官方 installer 讀 stable/latest/manifest.json(Windows 版 Install-KiroCLI.ps1
127
+ # 用的是同一份),欄位 version。
128
+ cats_kiro_latest_version() {
129
+ curl -fsSL --connect-timeout 5 --max-time 20 'https://prod.download.cli.kiro.dev/stable/latest/manifest.json' 2>/dev/null | _cats_json_string_field version || true
130
+ }
131
+
132
+ # cats_devin_latest_version
133
+ # Devin 的官方 install.sh 與 Windows 版 setup.ps1 讀的都是 cli/current/manifest.json,欄位
134
+ # version(3000.10.27)。官方腳本同版雖不重抓,POSIX 版仍會重建兩個符號連結、Windows 版更會
135
+ # 無條件重寫 180 MB 的 devin.exe;先問這裡,同版就完全不動。
136
+ cats_devin_latest_version() {
137
+ curl -fsSL --connect-timeout 5 --max-time 20 'https://static.devin.ai/cli/current/manifest.json' 2>/dev/null | _cats_json_string_field version || true
138
+ }
139
+
140
+ # cats_grok_latest_version [channel]
141
+ # Grok 的官方 install.sh(與 Windows 的 install.ps1)決定要下載哪一版,靠的只是一個純文字端點:
142
+ # https://x.ai/cli/<channel>(Cloudflare 前端)不通就退到 GCS 上的同名檔案,內容就是版本號
143
+ # (1.0.30)。這裡照抄同一套順序;channel 預設跟官方一樣吃 GROK_CHANNEL(stable)。
144
+ # 三平台都問這裡,刻意不用 `grok update --check --json`:那要執行 grok 本體去連 x.ai 做 TLS
145
+ # 交握,而 grok 的 TLS 實作用到 SHA-512 硬體指令(sha512su0),沒有這個擴充的 aarch64
146
+ # (Raspberry Pi 4/5 的 Cortex-A72/A76)會當場 SIGILL、輸出全空 —— 見 CLAUDE.md Known Gotchas。
147
+ # 這個端點不必執行任何工具,問到的也正是安裝器接下來會抓的版本。失敗印空。
148
+ cats_grok_latest_version() {
149
+ local channel="${1:-${GROK_CHANNEL:-stable}}" url v
150
+ for url in "https://x.ai/cli/${channel}" "https://storage.googleapis.com/grok-build-public-artifacts/cli/${channel}"; do
151
+ v="$(curl -fsSL --connect-timeout 5 --max-time 20 "$url" 2>/dev/null | tr -d '\r' | head -n 1 | tr -d '[:space:]' || true)"
152
+ if printf '%s' "$v" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._]+)?$'; then
153
+ printf '%s' "$v"
154
+ return 0
155
+ fi
156
+ done
157
+ printf ''
158
+ }
159
+
160
+
161
+ cats_native_latest_version() {
162
+ local script
163
+ case "$1" in
164
+ antigravity) cats_antigravity_latest_version ;;
165
+ cursor)
166
+ script="$(curl -fsSL --connect-timeout 5 --max-time 20 https://cursor.com/install 2>/dev/null)" || return 0
167
+ printf '%s' "$script" | grep -oE 'versions/[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[0-9A-Za-z]+' | head -n 1 | sed 's#^versions/##' || true
168
+ ;;
169
+ goose) cats_github_latest_release aaif-goose/goose ;;
170
+ junie) cats_junie_latest_build ;;
171
+ kiro) cats_kiro_latest_version ;;
172
+ devin) cats_devin_latest_version ;;
173
+ grok) cats_grok_latest_version ;;
174
+ *) return 0 ;;
175
+ esac
176
+ }
177
+
178
+ cats_native_version_token() {
179
+ if [ "$1" = junie ]; then
180
+ printf '%s' "$2" | sed -nE 's/.*\(([0-9]+(\.[0-9]+)+)\).*/\1/p' | head -n 1 || true
181
+ else
182
+ cats_version_token "$2"
183
+ fi
184
+ }
185
+
186
+ # Print current/older/unknown; Cursor same-day hashes are distinct releases.
187
+ cats_native_version_state() {
188
+ local current latest comparison
189
+ current="$(cats_native_version_token "$1" "$2")"
190
+ latest="$3"
191
+ [ -n "$current" ] && [ -n "$latest" ] || { printf unknown; return 0; }
192
+ comparison="$(cats_version_compare "$current" "$latest")"
193
+ if [ "$comparison" = 1 ] || { [ "$comparison" = 0 ] && { [ "$1" != cursor ] || [ "$current" = "$latest" ]; }; }; then
194
+ printf current
195
+ elif [ -n "$comparison" ]; then printf older
196
+ else printf unknown; fi
197
+ }
198
+
199
+ # Delete only older numeric version directories in a known installer-owned tree.
200
+ # Current/pending/newer builds, symlinks and custom locations outside HOME remain.
201
+ cats_cleanup_native_versions() {
202
+ local provider="$1" version="$2" root='' data='' keep='' actual='' directory='' name='' ancestor=''
203
+ keep="$(cats_native_version_token "$provider" "$version")"
204
+ [ -n "$keep" ] || return 0
205
+ case "$provider" in
206
+ cursor)
207
+ root="$HOME/.local/share/cursor-agent/versions"
208
+ [ -L "$HOME/.local/bin/cursor-agent" ] || return 0
209
+ actual="$(readlink "$HOME/.local/bin/cursor-agent")" || return 0
210
+ [ "$(basename "$(dirname "$actual")")" = "$keep" ] || return 0
211
+ ;;
212
+ devin)
213
+ root="${XDG_DATA_HOME:-$HOME/.local/share}/devin/cli/_versions"
214
+ [ -L "$root/current" ] || return 0
215
+ actual="$(readlink "$root/current")" || return 0
216
+ [ "$(basename "$actual")" = "$keep" ] || return 0
217
+ ;;
218
+ junie)
219
+ data="${JUNIE_DATA_DIR:-$HOME/.local/share/junie}"
220
+ root="$data/versions"
221
+ [ ! -e "$data/updates/pending-update.json" ] && [ -L "$data/current" ] || return 0
222
+ actual="$(readlink "$data/current")" || return 0
223
+ [ "$(basename "$actual")" = "$keep" ] || return 0
224
+ ;;
225
+ *) return 0 ;;
226
+ esac
227
+ case "$root" in "$HOME"/*) ;; *) return 0 ;; esac
228
+ [ -d "$root" ] || return 0
229
+ ancestor="$root"
230
+ while [ "$ancestor" != / ] && [ "$ancestor" != . ]; do
231
+ [ ! -L "$ancestor" ] || return 0
232
+ ancestor="$(dirname "$ancestor")"
233
+ done
234
+ local canonical_home
235
+ canonical_home="$(cd -P -- "$HOME" && pwd -P)" || return 0
236
+ root="$(cd -P -- "$root" && pwd -P)" || return 0
237
+ case "$root" in "$canonical_home"/*) ;; *) return 0 ;; esac
238
+ for directory in "$root"/*; do
239
+ [ -d "$directory" ] && [ ! -L "$directory" ] || continue
240
+ name="$(basename "$directory")"
241
+ printf '%s' "$name" | grep -qE '^[0-9]+(\.[0-9]+)+(-[0-9A-Za-z]+)?$' || continue
242
+ [ "$(cats_version_compare "$name" "$keep")" = -1 ] || continue
243
+ # Skip directory trees with links rather than recurse through an unknown layout.
244
+ [ -z "$(find "$directory" -type l -print 2>/dev/null)" ] || continue
245
+ rm -rf -- "$directory" || printf 'Could not remove old %s build %s.\n' "$provider" "$name" >&2
246
+ done
247
+ }
@@ -66,6 +66,8 @@ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-goose.sh" 'bun
66
66
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-junie.sh" 'bundled Linux Junie installer helper'
67
67
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-kiro-cli.sh" 'bundled Linux Kiro installer helper'
68
68
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/check-installation.sh" 'bundled Linux readiness audit helper'
69
+ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/provider-version-common.sh" 'bundled native version library'
70
+ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/verified-npm-update.sh" 'bundled npm verification library'
69
71
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/provider-cli-common.sh" 'bundled Linux provider helper library'
70
72
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/node-cli-common.sh" 'bundled Linux npm helper library'
71
73
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/manifest.json" 'bundled setup-assets manifest'
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # Usage: source verified-npm-update.sh; cats_upgrade_npm
3
+ # Explicit npm upgrade only. Adapted from environment-bootstrap 4690075.
4
+ # Preserve the current prefix (including nvm), enforce engines, verify command and package.
5
+ cats_upgrade_npm() {
6
+ local current latest installed root
7
+ current="$(npm --version)" || { printf 'Could not read npm version.\n' >&2; return 1; }
8
+ latest="$(npm view npm@latest version --fetch-retries=0 --fetch-timeout=10000 --loglevel=error)" || { printf 'npm version query failed.\n' >&2; return 1; }
9
+ [[ "$latest" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { printf 'Invalid npm version metadata.\n' >&2; return 1; }
10
+ [ "$current" != "$latest" ] || return 0
11
+ [ "$(cats_version_compare "$current" "$latest")" != 1 ] || return 0
12
+ npm install -g "npm@$latest" --engine-strict --fetch-retries=0 --fetch-timeout=30000 || return 1
13
+ hash -r
14
+ installed="$(npm --version)" && root="$(npm root -g)" || return 1
15
+ [ "$installed" = "$latest" ] && node -e '
16
+ const path = require("path");
17
+ const pkg = require(path.join(process.argv[1], "npm", "package.json"));
18
+ process.exit(pkg.version === process.argv[2] ? 0 : 1);
19
+ ' "$root" "$latest" || { printf 'npm verification failed. Check PATH and prefix.\n' >&2; return 1; }
20
+ }
@@ -60,6 +60,11 @@ declare -a applied_changes=()
60
60
  declare -a warnings=()
61
61
  declare -a manual_steps=()
62
62
 
63
+ # A Desktop-launched helper may inherit PATH from before gh was installed.
64
+ # Include our fallback destination and the supported Homebrew locations in this
65
+ # process only; check mode must not edit the user's shell profile.
66
+ export PATH="$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
67
+
63
68
  resolve_gh_path() {
64
69
  command -v gh 2>/dev/null || true
65
70
  }
@@ -90,7 +90,7 @@ is_node_installed() {
90
90
  if [ "$skip_node_probe" = 'true' ]; then
91
91
  return 1
92
92
  fi
93
- command -v node >/dev/null 2>&1
93
+ command -v node >/dev/null 2>&1 && command -v npm >/dev/null 2>&1
94
94
  }
95
95
 
96
96
  json_array_from_lines() {
@@ -149,6 +149,12 @@ JSON
149
149
  exit "$exit_code"
150
150
  }
151
151
 
152
+ # Desktop's environment may predate nvm installation. Load it before read-only
153
+ # detection too; a bundled or standalone node without npm is not this substrate.
154
+ if [ "$install_state" = 'auto' ] && [ "$skip_node_probe" = 'false' ]; then
155
+ load_nvm
156
+ fi
157
+
152
158
  # Mode: check
153
159
  if [ "$mode" = 'check' ]; then
154
160
  if is_node_installed; then
@@ -214,10 +220,10 @@ case "$mode" in
214
220
  ;;
215
221
  esac
216
222
 
217
- if command -v node >/dev/null 2>&1; then
223
+ if command -v node >/dev/null 2>&1 && command -v npm >/dev/null 2>&1; then
218
224
  emit_result 'ready' 'true' 0
219
225
  else
220
- warnings+=('Node binary still missing after install.')
226
+ warnings+=('Node.js or npm is still missing after install.')
221
227
  manual_steps+=('Open a new terminal so PATH picks up the nvm-managed node binary.')
222
228
  emit_result 'failed' 'false' 1
223
229
  fi