@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
@@ -8,6 +8,7 @@ readonly CATS_PLATFORM_UNIX_NODE_COMMON_SH=1
8
8
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
9
  # shellcheck source=provider-cli-common.sh
10
10
  . "$SCRIPT_DIR/provider-cli-common.sh"
11
+ . "$SCRIPT_DIR/verified-npm-update.sh"
11
12
 
12
13
  normalize_npm_registry() {
13
14
  case "$1" in
@@ -52,25 +53,51 @@ ensure_node_and_npm() {
52
53
  printf 'npm is required but was not found.\n' >&2
53
54
  return 1
54
55
  fi
56
+ # Desktop invokes each helper in a fresh non-login shell. Read the configured
57
+ # prefix, never source arbitrary shell startup files to recover its commands.
58
+ local configured_prefix
59
+ configured_prefix="$(npm config get prefix 2>/dev/null || true)"
60
+ [ -z "$configured_prefix" ] || prepend_path_if_missing "$configured_prefix/bin"
61
+ return 0
62
+ }
63
+
64
+ has_explicit_npm_prefix() {
65
+ [ -z "${NPM_CONFIG_PREFIX:-${npm_config_prefix:-}}" ] || return 0
66
+ local npmrc="${NPM_CONFIG_USERCONFIG:-${npm_config_userconfig:-$HOME/.npmrc}}"
67
+ [ -f "$npmrc" ] && grep -qE '^[[:space:]]*prefix[[:space:]]*=' "$npmrc"
68
+ }
69
+
70
+ preferred_npm_prefix() {
71
+ local existing canonical_home
72
+ existing="$(npm config get prefix 2>/dev/null || true)"
73
+ if [ -n "$existing" ] && has_explicit_npm_prefix; then printf '%s\n' "$existing"; return 0; fi
74
+ if [ -d "$existing" ] && [ -w "$existing" ]; then
75
+ existing="$(cd -P -- "$existing" && pwd -P)"
76
+ canonical_home="$(cd -P -- "$HOME" && pwd -P)"
77
+ case "$existing" in "$canonical_home"/*) printf '%s\n' "$existing"; return 0 ;; esac
78
+ fi
79
+ printf '%s\n' "$HOME/.npm-global"
55
80
  }
56
81
 
57
82
  ensure_npm_global_path_export() {
58
- local shell_rc="$1"
83
+ local shell_rc="$1" prefix="${2:-$HOME/.npm-global}" export_line
84
+ printf -v export_line 'export PATH=%q:$PATH' "$prefix/bin"
59
85
  append_line_if_missing "$shell_rc" '# Added by cats-platform self-hosted npm helpers' '# Added by cats-platform self-hosted npm helpers'
60
- append_line_if_missing "$shell_rc" 'export PATH="$HOME/.npm-global/bin:$PATH"' 'export PATH="$HOME/.npm-global/bin:$PATH"'
61
- prepend_path_if_missing "$HOME/.npm-global/bin"
86
+ append_line_if_missing "$shell_rc" "$export_line" "$export_line"
87
+ prepend_path_if_missing "$prefix/bin"
62
88
  }
63
89
 
64
90
  node_prefix_ready() {
65
- local prefix
91
+ local prefix desired_prefix
66
92
  local registry
67
93
  local shell_rc
68
94
 
69
95
  prefix="$(npm config get prefix 2>/dev/null || printf '')"
70
96
  registry="$(normalize_npm_registry "$(npm config get registry 2>/dev/null || printf '')")"
71
97
  shell_rc="$(detect_shell_rc)"
98
+ desired_prefix="$(preferred_npm_prefix)"
72
99
 
73
- if nvm_is_active; then
100
+ if nvm_is_active && ! has_explicit_npm_prefix; then
74
101
  if npmrc_has_prefix_conflict; then
75
102
  return 1
76
103
  fi
@@ -78,7 +105,7 @@ node_prefix_ready() {
78
105
  return $?
79
106
  fi
80
107
 
81
- if [ "$prefix" != "$HOME/.npm-global" ]; then
108
+ if [ "$prefix" != "$desired_prefix" ]; then
82
109
  return 1
83
110
  fi
84
111
 
@@ -86,11 +113,11 @@ node_prefix_ready() {
86
113
  return 1
87
114
  fi
88
115
 
89
- if [[ ":$PATH:" == *":$HOME/.npm-global/bin:"* ]]; then
116
+ if [[ ":$PATH:" == *":$desired_prefix/bin:"* ]]; then
90
117
  return 0
91
118
  fi
92
119
 
93
- grep -Fq 'export PATH="$HOME/.npm-global/bin:$PATH"' "$shell_rc" 2>/dev/null
120
+ return 1
94
121
  }
95
122
 
96
123
  node_prefix_help() {
@@ -119,7 +146,7 @@ run_node_prefix_setup() {
119
146
  local force='false'
120
147
  local emit_json='false'
121
148
  local shell_rc
122
- local prefix
149
+ local prefix desired_prefix
123
150
  local registry
124
151
  local status='ready'
125
152
  local execution_mode='apply'
@@ -170,6 +197,7 @@ run_node_prefix_setup() {
170
197
  execution_mode='apply'
171
198
  fi
172
199
 
200
+ desired_prefix="$HOME/.npm-global"
173
201
  load_nvm_if_present
174
202
  if ! ensure_node_and_npm; then
175
203
  if [ "$emit_json" = 'true' ]; then
@@ -178,7 +206,7 @@ run_node_prefix_setup() {
178
206
  printf '"mode":"%s",' "$execution_mode"
179
207
  printf '"status":"failed",'
180
208
  printf '"restartRequired":false,'
181
- printf '"desiredPrefix":"%s",' "$(json_escape "$HOME/.npm-global")"
209
+ printf '"desiredPrefix":"%s",' "$(json_escape "$desired_prefix")"
182
210
  printf '"shellRc":"%s",' "$(json_escape "$(detect_shell_rc)")"
183
211
  printf '"plannedActions":[],'
184
212
  printf '"appliedChanges":[],'
@@ -191,10 +219,11 @@ run_node_prefix_setup() {
191
219
  fi
192
220
 
193
221
  shell_rc="$(detect_shell_rc)"
222
+ desired_prefix="$(preferred_npm_prefix)"
194
223
  prefix="$(npm config get prefix 2>/dev/null || printf '')"
195
224
  registry="$(normalize_npm_registry "$(npm config get registry 2>/dev/null || printf '')")"
196
225
 
197
- if nvm_is_active; then
226
+ if nvm_is_active && ! has_explicit_npm_prefix; then
198
227
  if npmrc_has_prefix_conflict; then
199
228
  planned_actions+=('clear_npm_prefix_conflict')
200
229
  fi
@@ -202,13 +231,13 @@ run_node_prefix_setup() {
202
231
  planned_actions+=('set_npm_registry')
203
232
  fi
204
233
  else
205
- if [ "$prefix" != "$HOME/.npm-global" ]; then
234
+ if [ "$prefix" != "$desired_prefix" ]; then
206
235
  planned_actions+=('set_npm_prefix')
207
236
  fi
208
237
  if [ "$registry" != 'https://registry.npmjs.org/' ]; then
209
238
  planned_actions+=('set_npm_registry')
210
239
  fi
211
- if [[ ":$PATH:" != *":$HOME/.npm-global/bin:"* ]]; then
240
+ if [[ ":$PATH:" != *":$desired_prefix/bin:"* ]]; then
212
241
  planned_actions+=('repair_npm_global_path')
213
242
  fi
214
243
  fi
@@ -224,7 +253,7 @@ run_node_prefix_setup() {
224
253
  printf '"mode":"check",'
225
254
  printf '"status":"%s",' "$status"
226
255
  printf '"restartRequired":false,'
227
- printf '"desiredPrefix":"%s",' "$(json_escape "$HOME/.npm-global")"
256
+ printf '"desiredPrefix":"%s",' "$(json_escape "$desired_prefix")"
228
257
  printf '"shellRc":"%s",' "$(json_escape "$shell_rc")"
229
258
  printf '"plannedActions":'
230
259
  json_string_array "${planned_actions[@]}"
@@ -251,7 +280,7 @@ run_node_prefix_setup() {
251
280
  printf '"mode":"%s",' "$execution_mode"
252
281
  printf '"status":"ready",'
253
282
  printf '"restartRequired":false,'
254
- printf '"desiredPrefix":"%s",' "$(json_escape "$HOME/.npm-global")"
283
+ printf '"desiredPrefix":"%s",' "$(json_escape "$desired_prefix")"
255
284
  printf '"shellRc":"%s",' "$(json_escape "$shell_rc")"
256
285
  printf '"plannedActions":[],'
257
286
  printf '"appliedChanges":[],'
@@ -265,7 +294,7 @@ run_node_prefix_setup() {
265
294
  return 0
266
295
  fi
267
296
 
268
- if nvm_is_active; then
297
+ if nvm_is_active && ! has_explicit_npm_prefix; then
269
298
  nvm use --delete-prefix "$(node -v)" --silent >/dev/null 2>&1 || true
270
299
  npm config delete prefix >/dev/null 2>&1 || true
271
300
  npm config delete globalconfig >/dev/null 2>&1 || true
@@ -275,10 +304,10 @@ run_node_prefix_setup() {
275
304
  applied_changes+=('clear_npm_prefix_conflict')
276
305
  fi
277
306
  else
278
- mkdir -p "$HOME/.npm-global"
279
- npm config set prefix "$HOME/.npm-global"
307
+ mkdir -p "$desired_prefix"
308
+ npm config set prefix "$desired_prefix"
280
309
  npm config set registry 'https://registry.npmjs.org/'
281
- ensure_npm_global_path_export "$shell_rc"
310
+ ensure_npm_global_path_export "$shell_rc" "$desired_prefix"
282
311
  applied_changes+=('set_npm_prefix' 'set_npm_registry' 'repair_npm_global_path')
283
312
  fi
284
313
 
@@ -294,7 +323,7 @@ run_node_prefix_setup() {
294
323
  printf '"mode":"%s",' "$execution_mode"
295
324
  printf '"status":"%s",' "$status"
296
325
  printf '"restartRequired":false,'
297
- printf '"desiredPrefix":"%s",' "$(json_escape "$HOME/.npm-global")"
326
+ printf '"desiredPrefix":"%s",' "$(json_escape "$desired_prefix")"
298
327
  printf '"shellRc":"%s",' "$(json_escape "$shell_rc")"
299
328
  printf '"plannedActions":'
300
329
  json_string_array "${planned_actions[@]}"
@@ -340,8 +369,8 @@ remove_superseded_npm_packages() {
340
369
  while IFS= read -r legacy; do
341
370
  [ -n "$legacy" ] || continue
342
371
  npm list -g "$legacy" --depth=0 >/dev/null 2>&1 || continue
343
- printf 'Removing superseded package %s (replaced by %s).\n' "$legacy" "$package_name"
344
- npm uninstall -g "$legacy" >/dev/null 2>&1 || true
372
+ printf 'Removing superseded package %s (replaced by %s).\n' "$legacy" "$package_name" >&2
373
+ npm uninstall -g "$legacy" >/dev/null 2>&1 || return 1
345
374
  done <<EOF
346
375
  $(node_cli_superseded_packages "$package_name")
347
376
  EOF
@@ -1081,9 +1110,6 @@ run_npm_cli_provider() {
1081
1110
  shift
1082
1111
  done
1083
1112
 
1084
- if [ "$check_only" != 'true' ] && [ "$uninstall" != 'true' ]; then
1085
- remove_superseded_npm_packages "$package_name"
1086
- fi
1087
1113
 
1088
1114
  if [ "$force" = 'true' ]; then
1089
1115
  upgrade='false'
@@ -1134,7 +1160,17 @@ run_npm_cli_provider() {
1134
1160
  if command -v "$command_name" >/dev/null 2>&1 || npm list -g "$package_name" --depth=0 >/dev/null 2>&1; then
1135
1161
  installed='true'
1136
1162
  command_path="$(command -v "$command_name" 2>/dev/null || true)"
1137
- detected_version="$(npm list -g --depth=0 --json "$package_name" 2>/dev/null | sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' | head -1)"
1163
+ detected_version="$( (npm list -g --depth=0 --json "$package_name" 2>/dev/null || true) | node -e 'let s="";process.stdin.on("data",d=>s+=d);process.stdin.on("end",()=>{try{console.log(JSON.parse(s).dependencies?.[process.argv[1]]?.version||"")}catch{}})' "$package_name")"
1164
+ fi
1165
+
1166
+ if [ "$installed" = true ] && [ -z "$detected_version" ]; then
1167
+ local legacy
1168
+ while IFS= read -r legacy; do
1169
+ [ -n "$legacy" ] || continue
1170
+ if npm list -g "$legacy" --depth=0 >/dev/null 2>&1; then installed=false; break; fi
1171
+ done <<LEGACY_EOF
1172
+ $(node_cli_superseded_packages "$package_name")
1173
+ LEGACY_EOF
1138
1174
  fi
1139
1175
 
1140
1176
  if [ "$execution_mode" = 'uninstall' ]; then
@@ -1208,13 +1244,15 @@ run_npm_cli_provider() {
1208
1244
  return 0
1209
1245
  fi
1210
1246
 
1211
- local is_outdated='false'
1212
- if [ "$upgrade" = 'true' ] || [ "$force" = 'true' ]; then
1213
- if [ "$installed" = 'true' ]; then
1214
- if npm outdated -g "$package_name" --json 2>/dev/null | grep -q "\"$package_name\""; then
1215
- is_outdated='true'
1216
- fi
1217
- fi
1247
+ local is_outdated=false expected_version=''
1248
+ if [ "$check_only" = false ] && [ "$uninstall" = false ] && [ "$dry_run" = false ] && { [ "$upgrade" = true ] || [ "$force" = true ] || [ "$installed" = false ]; }; then
1249
+ if [ "$upgrade" = true ]; then cats_upgrade_npm || return 1; fi
1250
+ expected_version="$(npm view "${package_name}@latest" version --fetch-retries=0 --fetch-timeout=10000 --loglevel=error)" || { printf 'Provider version query failed.\n' >&2; return 1; }
1251
+ [[ "$expected_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$ ]] || { printf 'Invalid provider version metadata.\n' >&2; return 1; }
1252
+ local comparison
1253
+ comparison="$(cats_version_compare "$detected_version" "$expected_version")"
1254
+ if [ -z "$detected_version" ] || [ "$comparison" = -1 ]; then is_outdated=true
1255
+ elif [ "$comparison" = 0 ] && [[ "$detected_version" == *-* ]] && [[ "$expected_version" != *-* ]]; then is_outdated=true; fi
1218
1256
  fi
1219
1257
 
1220
1258
  local planned_action='skip'
@@ -1293,25 +1331,26 @@ run_npm_cli_provider() {
1293
1331
  fi
1294
1332
 
1295
1333
  local applied=()
1296
- case "$planned_action" in
1297
- install)
1298
- npm install -g --include=optional "$package_name" >/dev/null
1299
- applied+=("${package_name}:install")
1300
- ;;
1301
- upgrade)
1302
- npm install -g --include=optional "${package_name}@latest" >/dev/null
1303
- applied+=("${package_name}:upgrade")
1304
- ;;
1305
- reinstall)
1306
- npm install -g --include=optional "$package_name" --force >/dev/null
1307
- applied+=("${package_name}:reinstall")
1308
- ;;
1309
- esac
1334
+ if [ "$dry_run" = true ]; then
1335
+ printf '{"helper":"%s","mode":"%s","status":"preview","plannedActions":["%s:%s"],"appliedChanges":[],"warnings":[],"manualSteps":[],"interruptions":[]}\n' "$helper_id" "$execution_mode" "$package_name" "$planned_action"
1336
+ return 0
1337
+ fi
1338
+ if [ "$check_only" != 'true' ] && [ "$dry_run" != 'true' ]; then
1339
+ remove_superseded_npm_packages "$package_name"
1340
+ fi
1341
+ npm install -g --include=optional --engine-strict --fetch-retries=0 --fetch-timeout=30000 "${package_name}@${expected_version}" >/dev/null || return 1
1342
+ applied+=("${package_name}:${planned_action}")
1343
+ hash -r
1310
1344
 
1311
1345
  local final_command_path
1312
1346
  final_command_path="$(command -v "$command_name" 2>/dev/null || true)"
1313
1347
  local final_version
1314
- final_version="$(npm list -g --depth=0 --json "$package_name" 2>/dev/null | sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' | head -1)"
1348
+ final_version="$(npm list -g --depth=0 --json "$package_name" 2>/dev/null | node -e 'let s="";process.stdin.on("data",d=>s+=d);process.stdin.on("end",()=>{try{console.log(JSON.parse(s).dependencies?.[process.argv[1]]?.version||"")}catch{}})' "$package_name")"
1349
+
1350
+ if [ -z "$final_command_path" ] || [ "$final_version" != "$expected_version" ]; then
1351
+ printf 'Provider verification failed. Check npm prefix and PATH.\n' >&2
1352
+ return 1
1353
+ fi
1315
1354
 
1316
1355
  if [ "$emit_json" = 'true' ]; then
1317
1356
  printf '{'
@@ -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
  }