@duckmind/dm-linux-x64 0.65.8 → 0.66.2

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 (549) hide show
  1. package/dm +2 -2
  2. package/extensions/.dm-extensions.json +14 -548
  3. package/extensions/dm-lens/clients/dispatch/runners/utils/runner-helpers.js +1 -1
  4. package/extensions/dm-mcp-adapter/mcp-keyring-helper.cjs +1 -1
  5. package/extensions/dm-ponytail/hooks/ponytail-activate.js +1 -1
  6. package/extensions/dm-ponytail/hooks/ponytail-instructions.js +1 -1
  7. package/extensions/dm-subagents/src/extension/tool-activation.js +7 -2
  8. package/extensions/dm-subagents/src/runs/background/runner-aliases.js +2 -2
  9. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +4 -1
  10. package/extensions/dm-subagents/src/runs/shared/child-session.js +8 -6
  11. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +5 -1
  12. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +9 -1
  13. package/extensions/dm-subagents/src/watchdog/review.js +4 -1
  14. package/extensions/dm-web-access/tool-activation.js +0 -1
  15. package/package.json +1 -1
  16. package/extensions/dm-lens/dist/clients/actionable-warnings-logger.js +0 -24
  17. package/extensions/dm-lens/dist/clients/actionable-warnings.js +0 -1014
  18. package/extensions/dm-lens/dist/clients/advisory-provenance.js +0 -385
  19. package/extensions/dm-lens/dist/clients/agent-behavior-client.js +0 -94
  20. package/extensions/dm-lens/dist/clients/agent-nudge.js +0 -151
  21. package/extensions/dm-lens/dist/clients/analysed-root.js +0 -1
  22. package/extensions/dm-lens/dist/clients/ast-grep-client.js +0 -576
  23. package/extensions/dm-lens/dist/clients/ast-grep-rule-manager.js +0 -114
  24. package/extensions/dm-lens/dist/clients/ast-grep-tool-logger.js +0 -85
  25. package/extensions/dm-lens/dist/clients/ast-grep-types.js +0 -1
  26. package/extensions/dm-lens/dist/clients/ast-grep-yaml-synth.js +0 -109
  27. package/extensions/dm-lens/dist/clients/atomic-write-staging.js +0 -66
  28. package/extensions/dm-lens/dist/clients/atomic-write.js +0 -37
  29. package/extensions/dm-lens/dist/clients/availability-probe-flight.js +0 -11
  30. package/extensions/dm-lens/dist/clients/bash-file-access.js +0 -830
  31. package/extensions/dm-lens/dist/clients/biome-client.js +0 -221
  32. package/extensions/dm-lens/dist/clients/blocker-freshness.js +0 -385
  33. package/extensions/dm-lens/dist/clients/blocker-past-eof.js +0 -49
  34. package/extensions/dm-lens/dist/clients/bootstrap.js +0 -246
  35. package/extensions/dm-lens/dist/clients/bounded-cache.js +0 -105
  36. package/extensions/dm-lens/dist/clients/bounded-pid-file-lock.js +0 -173
  37. package/extensions/dm-lens/dist/clients/bounded-telemetry.js +0 -104
  38. package/extensions/dm-lens/dist/clients/build-identity.js +0 -45
  39. package/extensions/dm-lens/dist/clients/bundled-resource-health.js +0 -41
  40. package/extensions/dm-lens/dist/clients/bus-events-logger.js +0 -48
  41. package/extensions/dm-lens/dist/clients/bus-publish.js +0 -117
  42. package/extensions/dm-lens/dist/clients/cache/rule-cache.js +0 -103
  43. package/extensions/dm-lens/dist/clients/cache-manager.js +0 -340
  44. package/extensions/dm-lens/dist/clients/cache-observability.js +0 -847
  45. package/extensions/dm-lens/dist/clients/call-graph.js +0 -628
  46. package/extensions/dm-lens/dist/clients/cargo-manifest.js +0 -151
  47. package/extensions/dm-lens/dist/clients/cascade-budget.js +0 -52
  48. package/extensions/dm-lens/dist/clients/cascade-format.js +0 -160
  49. package/extensions/dm-lens/dist/clients/cascade-logger.js +0 -22
  50. package/extensions/dm-lens/dist/clients/cascade-types.js +0 -1
  51. package/extensions/dm-lens/dist/clients/child-unref.js +0 -59
  52. package/extensions/dm-lens/dist/clients/code-quality-warnings.js +0 -192
  53. package/extensions/dm-lens/dist/clients/codebase-model.js +0 -110
  54. package/extensions/dm-lens/dist/clients/collateral-test-role.js +0 -8
  55. package/extensions/dm-lens/dist/clients/complexity-client.js +0 -398
  56. package/extensions/dm-lens/dist/clients/config-core/deny.js +0 -117
  57. package/extensions/dm-lens/dist/clients/config-core/index.js +0 -2
  58. package/extensions/dm-lens/dist/clients/config-core/merge.js +0 -228
  59. package/extensions/dm-lens/dist/clients/config-core/normalize.js +0 -217
  60. package/extensions/dm-lens/dist/clients/config-core/process-spec.js +0 -147
  61. package/extensions/dm-lens/dist/clients/config-core/provenance.js +0 -54
  62. package/extensions/dm-lens/dist/clients/config-core/records.js +0 -79
  63. package/extensions/dm-lens/dist/clients/config-core/resolve.js +0 -48
  64. package/extensions/dm-lens/dist/clients/config-core/safe-object.js +0 -22
  65. package/extensions/dm-lens/dist/clients/config-core/schema.js +0 -90
  66. package/extensions/dm-lens/dist/clients/config-diagnostic-codes.js +0 -90
  67. package/extensions/dm-lens/dist/clients/config-locations.js +0 -175
  68. package/extensions/dm-lens/dist/clients/config-resolve.js +0 -359
  69. package/extensions/dm-lens/dist/clients/config-schema.js +0 -128
  70. package/extensions/dm-lens/dist/clients/config-warn.js +0 -146
  71. package/extensions/dm-lens/dist/clients/console-guard-install.js +0 -4
  72. package/extensions/dm-lens/dist/clients/cooperative-budget.js +0 -36
  73. package/extensions/dm-lens/dist/clients/dead-code-client.js +0 -342
  74. package/extensions/dm-lens/dist/clients/dead-code-logger.js +0 -18
  75. package/extensions/dm-lens/dist/clients/deadline-utils.js +0 -117
  76. package/extensions/dm-lens/dist/clients/debug-handles.js +0 -121
  77. package/extensions/dm-lens/dist/clients/debug-heap.js +0 -80
  78. package/extensions/dm-lens/dist/clients/deferred-lsp-work.js +0 -33
  79. package/extensions/dm-lens/dist/clients/degradation-ledger.js +0 -272
  80. package/extensions/dm-lens/dist/clients/demoted-finding-render.js +0 -61
  81. package/extensions/dm-lens/dist/clients/dependency-checker.js +0 -525
  82. package/extensions/dm-lens/dist/clients/deps/ast-grep-napi.js +0 -17
  83. package/extensions/dm-lens/dist/clients/deps/dm-host-deps.js +0 -10
  84. package/extensions/dm-lens/dist/clients/deps/dm-tui-compat.js +0 -58
  85. package/extensions/dm-lens/dist/clients/deps/js-yaml.js +0 -7
  86. package/extensions/dm-lens/dist/clients/deps/minimatch.js +0 -6
  87. package/extensions/dm-lens/dist/clients/deps/pidusage.js +0 -5
  88. package/extensions/dm-lens/dist/clients/deps/typebox.js +0 -5
  89. package/extensions/dm-lens/dist/clients/deps/vscode-jsonrpc.js +0 -8
  90. package/extensions/dm-lens/dist/clients/deps/web-tree-sitter.js +0 -11
  91. package/extensions/dm-lens/dist/clients/diagnostic-dispositions.js +0 -318
  92. package/extensions/dm-lens/dist/clients/diagnostic-line-freshness.js +0 -144
  93. package/extensions/dm-lens/dist/clients/diagnostic-logger.js +0 -60
  94. package/extensions/dm-lens/dist/clients/diagnostic-tracker.js +0 -89
  95. package/extensions/dm-lens/dist/clients/diagnostics-publish.js +0 -134
  96. package/extensions/dm-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -16
  97. package/extensions/dm-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -158
  98. package/extensions/dm-lens/dist/clients/dispatch/collect-later-tier.js +0 -25
  99. package/extensions/dm-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -58
  100. package/extensions/dm-lens/dist/clients/dispatch/dispatcher.js +0 -953
  101. package/extensions/dm-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
  102. package/extensions/dm-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
  103. package/extensions/dm-lens/dist/clients/dispatch/fact-runner.js +0 -18
  104. package/extensions/dm-lens/dist/clients/dispatch/fact-scheduler.js +0 -52
  105. package/extensions/dm-lens/dist/clients/dispatch/fact-store.js +0 -199
  106. package/extensions/dm-lens/dist/clients/dispatch/facts/comment-facts.js +0 -20
  107. package/extensions/dm-lens/dist/clients/dispatch/facts/file-content.js +0 -18
  108. package/extensions/dm-lens/dist/clients/dispatch/facts/function-facts.js +0 -281
  109. package/extensions/dm-lens/dist/clients/dispatch/facts/import-facts.js +0 -169
  110. package/extensions/dm-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -19
  111. package/extensions/dm-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -95
  112. package/extensions/dm-lens/dist/clients/dispatch/finding-policy.js +0 -159
  113. package/extensions/dm-lens/dist/clients/dispatch/indent-detect.js +0 -227
  114. package/extensions/dm-lens/dist/clients/dispatch/inline-suppressions.js +0 -52
  115. package/extensions/dm-lens/dist/clients/dispatch/integration.js +0 -1463
  116. package/extensions/dm-lens/dist/clients/dispatch/lazy.js +0 -8
  117. package/extensions/dm-lens/dist/clients/dispatch/pending-runner-findings.js +0 -75
  118. package/extensions/dm-lens/dist/clients/dispatch/plan.js +0 -278
  119. package/extensions/dm-lens/dist/clients/dispatch/priorities.js +0 -14
  120. package/extensions/dm-lens/dist/clients/dispatch/rule-id-normalize.js +0 -42
  121. package/extensions/dm-lens/dist/clients/dispatch/rule-ignores.js +0 -21
  122. package/extensions/dm-lens/dist/clients/dispatch/rule-policy.js +0 -73
  123. package/extensions/dm-lens/dist/clients/dispatch/rules/async-noise.js +0 -31
  124. package/extensions/dm-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  125. package/extensions/dm-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -35
  126. package/extensions/dm-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -29
  127. package/extensions/dm-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -29
  128. package/extensions/dm-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -31
  129. package/extensions/dm-lens/dist/clients/dispatch/rules/high-complexity.js +0 -56
  130. package/extensions/dm-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -45
  131. package/extensions/dm-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -31
  132. package/extensions/dm-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -45
  133. package/extensions/dm-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -46
  134. package/extensions/dm-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -38
  135. package/extensions/dm-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  136. package/extensions/dm-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -101
  137. package/extensions/dm-lens/dist/clients/dispatch/runner-context.js +0 -46
  138. package/extensions/dm-lens/dist/clients/dispatch/runners/actionlint.js +0 -98
  139. package/extensions/dm-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -690
  140. package/extensions/dm-lens/dist/clients/dispatch/runners/biome-check.js +0 -202
  141. package/extensions/dm-lens/dist/clients/dispatch/runners/cpp-check.js +0 -190
  142. package/extensions/dm-lens/dist/clients/dispatch/runners/credo.js +0 -66
  143. package/extensions/dm-lens/dist/clients/dispatch/runners/cue-vet.js +0 -167
  144. package/extensions/dm-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -151
  145. package/extensions/dm-lens/dist/clients/dispatch/runners/detekt.js +0 -157
  146. package/extensions/dm-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -157
  147. package/extensions/dm-lens/dist/clients/dispatch/runners/elixir-check.js +0 -153
  148. package/extensions/dm-lens/dist/clients/dispatch/runners/eslint.js +0 -99
  149. package/extensions/dm-lens/dist/clients/dispatch/runners/fact-rules.js +0 -20
  150. package/extensions/dm-lens/dist/clients/dispatch/runners/fish-indent.js +0 -65
  151. package/extensions/dm-lens/dist/clients/dispatch/runners/gleam-check.js +0 -107
  152. package/extensions/dm-lens/dist/clients/dispatch/runners/go-vet.js +0 -51
  153. package/extensions/dm-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -89
  154. package/extensions/dm-lens/dist/clients/dispatch/runners/hadolint.js +0 -83
  155. package/extensions/dm-lens/dist/clients/dispatch/runners/helm-lint.js +0 -144
  156. package/extensions/dm-lens/dist/clients/dispatch/runners/helm-render.js +0 -593
  157. package/extensions/dm-lens/dist/clients/dispatch/runners/htmlhint.js +0 -103
  158. package/extensions/dm-lens/dist/clients/dispatch/runners/index.js +0 -100
  159. package/extensions/dm-lens/dist/clients/dispatch/runners/javac.js +0 -82
  160. package/extensions/dm-lens/dist/clients/dispatch/runners/ktlint.js +0 -93
  161. package/extensions/dm-lens/dist/clients/dispatch/runners/lsp.js +0 -206
  162. package/extensions/dm-lens/dist/clients/dispatch/runners/markdownlint.js +0 -129
  163. package/extensions/dm-lens/dist/clients/dispatch/runners/mypy.js +0 -70
  164. package/extensions/dm-lens/dist/clients/dispatch/runners/oxlint.js +0 -305
  165. package/extensions/dm-lens/dist/clients/dispatch/runners/php-lint.js +0 -70
  166. package/extensions/dm-lens/dist/clients/dispatch/runners/phpstan.js +0 -90
  167. package/extensions/dm-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -56
  168. package/extensions/dm-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -307
  169. package/extensions/dm-lens/dist/clients/dispatch/runners/pyright.js +0 -118
  170. package/extensions/dm-lens/dist/clients/dispatch/runners/rubocop.js +0 -79
  171. package/extensions/dm-lens/dist/clients/dispatch/runners/ruff.js +0 -69
  172. package/extensions/dm-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -147
  173. package/extensions/dm-lens/dist/clients/dispatch/runners/shellcheck.js +0 -111
  174. package/extensions/dm-lens/dist/clients/dispatch/runners/shfmt.js +0 -81
  175. package/extensions/dm-lens/dist/clients/dispatch/runners/spellcheck.js +0 -74
  176. package/extensions/dm-lens/dist/clients/dispatch/runners/spotbugs.js +0 -208
  177. package/extensions/dm-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -136
  178. package/extensions/dm-lens/dist/clients/dispatch/runners/stylelint.js +0 -118
  179. package/extensions/dm-lens/dist/clients/dispatch/runners/swiftlint.js +0 -143
  180. package/extensions/dm-lens/dist/clients/dispatch/runners/taplo.js +0 -97
  181. package/extensions/dm-lens/dist/clients/dispatch/runners/terragrunt.js +0 -110
  182. package/extensions/dm-lens/dist/clients/dispatch/runners/tflint.js +0 -104
  183. package/extensions/dm-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -516
  184. package/extensions/dm-lens/dist/clients/dispatch/runners/trivy-config.js +0 -155
  185. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -285
  186. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -61
  187. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -61
  188. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -130
  189. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1067
  190. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -40
  191. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -117
  192. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -88
  193. package/extensions/dm-lens/dist/clients/dispatch/runners/utils.js +0 -8
  194. package/extensions/dm-lens/dist/clients/dispatch/runners/vale.js +0 -106
  195. package/extensions/dm-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -163
  196. package/extensions/dm-lens/dist/clients/dispatch/runners/yamllint.js +0 -83
  197. package/extensions/dm-lens/dist/clients/dispatch/runners/zig-check.js +0 -72
  198. package/extensions/dm-lens/dist/clients/dispatch/suppress-writer.js +0 -57
  199. package/extensions/dm-lens/dist/clients/dispatch/tool-profile.js +0 -30
  200. package/extensions/dm-lens/dist/clients/dispatch/types.js +0 -7
  201. package/extensions/dm-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
  202. package/extensions/dm-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -31
  203. package/extensions/dm-lens/dist/clients/disposition-logger.js +0 -19
  204. package/extensions/dm-lens/dist/clients/disposition-publish.js +0 -88
  205. package/extensions/dm-lens/dist/clients/durable-store.js +0 -77
  206. package/extensions/dm-lens/dist/clients/effective-config.js +0 -160
  207. package/extensions/dm-lens/dist/clients/env-utils.js +0 -26
  208. package/extensions/dm-lens/dist/clients/error-class.js +0 -3
  209. package/extensions/dm-lens/dist/clients/eval-timestamp.js +0 -2
  210. package/extensions/dm-lens/dist/clients/event-loop-hold.js +0 -119
  211. package/extensions/dm-lens/dist/clients/event-loop-monitor.js +0 -84
  212. package/extensions/dm-lens/dist/clients/extension-log.js +0 -230
  213. package/extensions/dm-lens/dist/clients/extension-mode.js +0 -20
  214. package/extensions/dm-lens/dist/clients/feature-hints.js +0 -88
  215. package/extensions/dm-lens/dist/clients/file-kinds.js +0 -304
  216. package/extensions/dm-lens/dist/clients/file-role.js +0 -50
  217. package/extensions/dm-lens/dist/clients/file-time.js +0 -112
  218. package/extensions/dm-lens/dist/clients/file-utils.js +0 -630
  219. package/extensions/dm-lens/dist/clients/finding-delivery-gate.js +0 -129
  220. package/extensions/dm-lens/dist/clients/finding-identity.js +0 -23
  221. package/extensions/dm-lens/dist/clients/fix-worklog.js +0 -75
  222. package/extensions/dm-lens/dist/clients/format-events-publish.js +0 -230
  223. package/extensions/dm-lens/dist/clients/format-service.js +0 -154
  224. package/extensions/dm-lens/dist/clients/formatters-lazy.js +0 -8
  225. package/extensions/dm-lens/dist/clients/formatters.js +0 -1351
  226. package/extensions/dm-lens/dist/clients/freshness-cadence.js +0 -1
  227. package/extensions/dm-lens/dist/clients/freshness.js +0 -8
  228. package/extensions/dm-lens/dist/clients/generated-artifacts.js +0 -209
  229. package/extensions/dm-lens/dist/clients/generation-guard.js +0 -105
  230. package/extensions/dm-lens/dist/clients/git-guard.js +0 -770
  231. package/extensions/dm-lens/dist/clients/git-tracked-ignore.js +0 -120
  232. package/extensions/dm-lens/dist/clients/gitleaks-client.js +0 -282
  233. package/extensions/dm-lens/dist/clients/go-client.js +0 -47
  234. package/extensions/dm-lens/dist/clients/govulncheck-client.js +0 -411
  235. package/extensions/dm-lens/dist/clients/gradle-ktfmt-style.js +0 -177
  236. package/extensions/dm-lens/dist/clients/grammar-source.js +0 -224
  237. package/extensions/dm-lens/dist/clients/gzip-stage-write.js +0 -85
  238. package/extensions/dm-lens/dist/clients/hashline-anchor.js +0 -222
  239. package/extensions/dm-lens/dist/clients/hook-budgets.js +0 -16
  240. package/extensions/dm-lens/dist/clients/host-edit-normalize.js +0 -87
  241. package/extensions/dm-lens/dist/clients/host-ports.js +0 -35
  242. package/extensions/dm-lens/dist/clients/indent-retarget.js +0 -102
  243. package/extensions/dm-lens/dist/clients/inline-blocker-dispositions.js +0 -103
  244. package/extensions/dm-lens/dist/clients/install-diagnostics.js +0 -123
  245. package/extensions/dm-lens/dist/clients/installer/index.js +0 -3770
  246. package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -17
  247. package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh.js +0 -522
  248. package/extensions/dm-lens/dist/clients/instance-reaper-state.js +0 -3
  249. package/extensions/dm-lens/dist/clients/instance-reaper.js +0 -577
  250. package/extensions/dm-lens/dist/clients/instance-registry-lock.js +0 -131
  251. package/extensions/dm-lens/dist/clients/instance-registry.js +0 -452
  252. package/extensions/dm-lens/dist/clients/jscpd-client.js +0 -252
  253. package/extensions/dm-lens/dist/clients/json-cache-read.js +0 -25
  254. package/extensions/dm-lens/dist/clients/knip-client.js +0 -472
  255. package/extensions/dm-lens/dist/clients/language-policy.js +0 -242
  256. package/extensions/dm-lens/dist/clients/language-profile.js +0 -261
  257. package/extensions/dm-lens/dist/clients/language-registry.js +0 -424
  258. package/extensions/dm-lens/dist/clients/latency-logger.js +0 -192
  259. package/extensions/dm-lens/dist/clients/lazy-import.js +0 -14
  260. package/extensions/dm-lens/dist/clients/ledger-bounds.js +0 -21
  261. package/extensions/dm-lens/dist/clients/lens-config.js +0 -237
  262. package/extensions/dm-lens/dist/clients/lens-engine.js +0 -181
  263. package/extensions/dm-lens/dist/clients/lens-events.js +0 -105
  264. package/extensions/dm-lens/dist/clients/lens-flag-registry.js +0 -302
  265. package/extensions/dm-lens/dist/clients/lens-map.js +0 -980
  266. package/extensions/dm-lens/dist/clients/live-bus-emitter.js +0 -53
  267. package/extensions/dm-lens/dist/clients/log-cleanup.js +0 -138
  268. package/extensions/dm-lens/dist/clients/lsp/aggregation.js +0 -88
  269. package/extensions/dm-lens/dist/clients/lsp/cascade-tier.js +0 -203
  270. package/extensions/dm-lens/dist/clients/lsp/client.js +0 -2709
  271. package/extensions/dm-lens/dist/clients/lsp/config.js +0 -252
  272. package/extensions/dm-lens/dist/clients/lsp/content-limits.js +0 -21
  273. package/extensions/dm-lens/dist/clients/lsp/diagnostic-binding.js +0 -79
  274. package/extensions/dm-lens/dist/clients/lsp/document-drift.js +0 -312
  275. package/extensions/dm-lens/dist/clients/lsp/edits.js +0 -930
  276. package/extensions/dm-lens/dist/clients/lsp/index.js +0 -4448
  277. package/extensions/dm-lens/dist/clients/lsp/inferred-project.js +0 -121
  278. package/extensions/dm-lens/dist/clients/lsp/jvm-runtime.js +0 -194
  279. package/extensions/dm-lens/dist/clients/lsp/language.js +0 -23
  280. package/extensions/dm-lens/dist/clients/lsp/launch.js +0 -468
  281. package/extensions/dm-lens/dist/clients/lsp/lombok.js +0 -146
  282. package/extensions/dm-lens/dist/clients/lsp/path-utils.js +0 -1
  283. package/extensions/dm-lens/dist/clients/lsp/pending-aux-coverage.js +0 -90
  284. package/extensions/dm-lens/dist/clients/lsp/position-encoding.js +0 -28
  285. package/extensions/dm-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -42
  286. package/extensions/dm-lens/dist/clients/lsp/server.js +0 -2422
  287. package/extensions/dm-lens/dist/clients/lsp/session-roots.js +0 -52
  288. package/extensions/dm-lens/dist/clients/lsp/spawn-history.js +0 -15
  289. package/extensions/dm-lens/dist/clients/lsp/sync-kind.js +0 -30
  290. package/extensions/dm-lens/dist/clients/lsp/tsserver-sync.js +0 -156
  291. package/extensions/dm-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  292. package/extensions/dm-lens/dist/clients/lsp/wait-policy/classification.js +0 -36
  293. package/extensions/dm-lens/dist/clients/lsp/wait-policy/index.js +0 -3
  294. package/extensions/dm-lens/dist/clients/lsp/wait-policy/strategies.js +0 -175
  295. package/extensions/dm-lens/dist/clients/lsp/watch-queue.js +0 -40
  296. package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -267
  297. package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -7
  298. package/extensions/dm-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -93
  299. package/extensions/dm-lens/dist/clients/lsp-budget.js +0 -83
  300. package/extensions/dm-lens/dist/clients/lsp-document-symbols.js +0 -93
  301. package/extensions/dm-lens/dist/clients/lsp-lazy.js +0 -8
  302. package/extensions/dm-lens/dist/clients/lsp-mutation.js +0 -273
  303. package/extensions/dm-lens/dist/clients/lsp-status.js +0 -37
  304. package/extensions/dm-lens/dist/clients/map-with-concurrency.js +0 -20
  305. package/extensions/dm-lens/dist/clients/mcp/analyze.js +0 -251
  306. package/extensions/dm-lens/dist/clients/mcp/host-shim.js +0 -14
  307. package/extensions/dm-lens/dist/clients/mcp/ipc.js +0 -302
  308. package/extensions/dm-lens/dist/clients/mcp/review.js +0 -137
  309. package/extensions/dm-lens/dist/clients/mcp/session.js +0 -305
  310. package/extensions/dm-lens/dist/clients/memory-sampler.js +0 -107
  311. package/extensions/dm-lens/dist/clients/message-end-attribution.js +0 -18
  312. package/extensions/dm-lens/dist/clients/metrics-client.js +0 -80
  313. package/extensions/dm-lens/dist/clients/metrics-history.js +0 -255
  314. package/extensions/dm-lens/dist/clients/middle-man-analysis.js +0 -161
  315. package/extensions/dm-lens/dist/clients/model-provider.js +0 -31
  316. package/extensions/dm-lens/dist/clients/module-report-lsp.js +0 -140
  317. package/extensions/dm-lens/dist/clients/module-report.js +0 -1642
  318. package/extensions/dm-lens/dist/clients/mutating-tool.js +0 -377
  319. package/extensions/dm-lens/dist/clients/mutation-attribution.js +0 -168
  320. package/extensions/dm-lens/dist/clients/mutation-bridge.js +0 -102
  321. package/extensions/dm-lens/dist/clients/ndjson-logger.js +0 -471
  322. package/extensions/dm-lens/dist/clients/observed-mutation-sources.js +0 -43
  323. package/extensions/dm-lens/dist/clients/observed-mutation.js +0 -709
  324. package/extensions/dm-lens/dist/clients/oldtext-autopatch.js +0 -100
  325. package/extensions/dm-lens/dist/clients/opaque-mutation-scan.js +0 -313
  326. package/extensions/dm-lens/dist/clients/opengrep-client.js +0 -216
  327. package/extensions/dm-lens/dist/clients/opengrep-config.js +0 -52
  328. package/extensions/dm-lens/dist/clients/package-manager.js +0 -303
  329. package/extensions/dm-lens/dist/clients/package-root.js +0 -25
  330. package/extensions/dm-lens/dist/clients/partial-edit-apply.js +0 -337
  331. package/extensions/dm-lens/dist/clients/path-attribution-telemetry.js +0 -31
  332. package/extensions/dm-lens/dist/clients/path-keyed-map.js +0 -57
  333. package/extensions/dm-lens/dist/clients/path-utils.js +0 -476
  334. package/extensions/dm-lens/dist/clients/performance-report.js +0 -291
  335. package/extensions/dm-lens/dist/clients/persist-debounce.js +0 -37
  336. package/extensions/dm-lens/dist/clients/persistent-reverify.js +0 -165
  337. package/extensions/dm-lens/dist/clients/php-cs-fixer-config.js +0 -15
  338. package/extensions/dm-lens/dist/clients/pipeline.js +0 -1091
  339. package/extensions/dm-lens/dist/clients/post-autofix-notice.js +0 -19
  340. package/extensions/dm-lens/dist/clients/print-mode.js +0 -3
  341. package/extensions/dm-lens/dist/clients/probe-home-state.js +0 -62
  342. package/extensions/dm-lens/dist/clients/process-bridge.js +0 -20
  343. package/extensions/dm-lens/dist/clients/process-singletons.js +0 -69
  344. package/extensions/dm-lens/dist/clients/process-snapshot.js +0 -22
  345. package/extensions/dm-lens/dist/clients/project-changes.js +0 -112
  346. package/extensions/dm-lens/dist/clients/project-conventions.js +0 -152
  347. package/extensions/dm-lens/dist/clients/project-diagnostics/cache.js +0 -91
  348. package/extensions/dm-lens/dist/clients/project-diagnostics/extractors.js +0 -53
  349. package/extensions/dm-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -408
  350. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -64
  351. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -38
  352. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -21
  353. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -23
  354. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -28
  355. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -45
  356. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -34
  357. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -32
  358. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -79
  359. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -41
  360. package/extensions/dm-lens/dist/clients/project-diagnostics/scanner.js +0 -349
  361. package/extensions/dm-lens/dist/clients/project-diagnostics/types.js +0 -1
  362. package/extensions/dm-lens/dist/clients/project-lens-config.js +0 -437
  363. package/extensions/dm-lens/dist/clients/project-report.js +0 -527
  364. package/extensions/dm-lens/dist/clients/project-scale.js +0 -60
  365. package/extensions/dm-lens/dist/clients/project-scan-policy.js +0 -4
  366. package/extensions/dm-lens/dist/clients/project-snapshot-fingerprint.js +0 -39
  367. package/extensions/dm-lens/dist/clients/project-snapshot-persist-worker.js +0 -14
  368. package/extensions/dm-lens/dist/clients/project-snapshot.js +0 -1178
  369. package/extensions/dm-lens/dist/clients/project-trust.js +0 -104
  370. package/extensions/dm-lens/dist/clients/python-environment.js +0 -120
  371. package/extensions/dm-lens/dist/clients/python-provenance.js +0 -551
  372. package/extensions/dm-lens/dist/clients/quiet-window-config.js +0 -16
  373. package/extensions/dm-lens/dist/clients/quiet-window.js +0 -126
  374. package/extensions/dm-lens/dist/clients/read-bridge.js +0 -46
  375. package/extensions/dm-lens/dist/clients/read-expansion.js +0 -218
  376. package/extensions/dm-lens/dist/clients/read-guard-logger.js +0 -178
  377. package/extensions/dm-lens/dist/clients/read-guard-tool-lines.js +0 -1042
  378. package/extensions/dm-lens/dist/clients/read-guard.js +0 -1096
  379. package/extensions/dm-lens/dist/clients/recent-touches.js +0 -126
  380. package/extensions/dm-lens/dist/clients/redact/secrets.js +0 -386
  381. package/extensions/dm-lens/dist/clients/resource-sampler.js +0 -344
  382. package/extensions/dm-lens/dist/clients/reverse-deps.js +0 -227
  383. package/extensions/dm-lens/dist/clients/review-graph/builder.js +0 -3635
  384. package/extensions/dm-lens/dist/clients/review-graph/format.js +0 -30
  385. package/extensions/dm-lens/dist/clients/review-graph/git-identity.js +0 -115
  386. package/extensions/dm-lens/dist/clients/review-graph/import-resolvers.js +0 -325
  387. package/extensions/dm-lens/dist/clients/review-graph/persist-worker.js +0 -8
  388. package/extensions/dm-lens/dist/clients/review-graph/query.js +0 -195
  389. package/extensions/dm-lens/dist/clients/review-graph/revision-drift.js +0 -3
  390. package/extensions/dm-lens/dist/clients/review-graph/service.js +0 -60
  391. package/extensions/dm-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -54
  392. package/extensions/dm-lens/dist/clients/review-graph/symbol-id.js +0 -29
  393. package/extensions/dm-lens/dist/clients/review-graph/tsconfig-paths.js +0 -253
  394. package/extensions/dm-lens/dist/clients/review-graph/types.js +0 -1
  395. package/extensions/dm-lens/dist/clients/review-graph/workspace-modules.js +0 -374
  396. package/extensions/dm-lens/dist/clients/review-graph-logger.js +0 -51
  397. package/extensions/dm-lens/dist/clients/ruff-client.js +0 -109
  398. package/extensions/dm-lens/dist/clients/rules-scanner.js +0 -113
  399. package/extensions/dm-lens/dist/clients/run-duration.js +0 -9
  400. package/extensions/dm-lens/dist/clients/runtime-agent-end.js +0 -608
  401. package/extensions/dm-lens/dist/clients/runtime-config.js +0 -39
  402. package/extensions/dm-lens/dist/clients/runtime-context.js +0 -155
  403. package/extensions/dm-lens/dist/clients/runtime-coordinator.js +0 -817
  404. package/extensions/dm-lens/dist/clients/runtime-session.js +0 -1625
  405. package/extensions/dm-lens/dist/clients/runtime-tool-call.js +0 -868
  406. package/extensions/dm-lens/dist/clients/runtime-tool-result.js +0 -1317
  407. package/extensions/dm-lens/dist/clients/runtime-turn.js +0 -2333
  408. package/extensions/dm-lens/dist/clients/rust-client.js +0 -48
  409. package/extensions/dm-lens/dist/clients/safe-spawn.js +0 -1267
  410. package/extensions/dm-lens/dist/clients/sanitize.js +0 -174
  411. package/extensions/dm-lens/dist/clients/scan-utils.js +0 -7
  412. package/extensions/dm-lens/dist/clients/scratch-tree-policy.js +0 -57
  413. package/extensions/dm-lens/dist/clients/search-read-registration.js +0 -59
  414. package/extensions/dm-lens/dist/clients/secret-findings.js +0 -69
  415. package/extensions/dm-lens/dist/clients/security-scan-client.js +0 -208
  416. package/extensions/dm-lens/dist/clients/session-event-guard.js +0 -111
  417. package/extensions/dm-lens/dist/clients/session-lifecycle.js +0 -173
  418. package/extensions/dm-lens/dist/clients/session-start-observability.js +0 -47
  419. package/extensions/dm-lens/dist/clients/session-state-store.js +0 -63
  420. package/extensions/dm-lens/dist/clients/session-summary.js +0 -8
  421. package/extensions/dm-lens/dist/clients/sessionstart-logger.js +0 -22
  422. package/extensions/dm-lens/dist/clients/sg-runner.js +0 -627
  423. package/extensions/dm-lens/dist/clients/sgconfig.js +0 -280
  424. package/extensions/dm-lens/dist/clients/shared-checkout-guard.js +0 -177
  425. package/extensions/dm-lens/dist/clients/single-flight.js +0 -52
  426. package/extensions/dm-lens/dist/clients/situational-tool-telemetry.js +0 -85
  427. package/extensions/dm-lens/dist/clients/skills-resolver.js +0 -38
  428. package/extensions/dm-lens/dist/clients/slow-fs.js +0 -77
  429. package/extensions/dm-lens/dist/clients/smells-rollup.js +0 -118
  430. package/extensions/dm-lens/dist/clients/source-filter.js +0 -320
  431. package/extensions/dm-lens/dist/clients/source-walker.js +0 -96
  432. package/extensions/dm-lens/dist/clients/spawn-output-cap.js +0 -35
  433. package/extensions/dm-lens/dist/clients/spawn-timeout-cooldown.js +0 -51
  434. package/extensions/dm-lens/dist/clients/stale-marker.js +0 -1
  435. package/extensions/dm-lens/dist/clients/startup-scan.js +0 -222
  436. package/extensions/dm-lens/dist/clients/startup-timing.js +0 -67
  437. package/extensions/dm-lens/dist/clients/string-utils.js +0 -6
  438. package/extensions/dm-lens/dist/clients/subagent-mode.js +0 -40
  439. package/extensions/dm-lens/dist/clients/symbol-containment.js +0 -16
  440. package/extensions/dm-lens/dist/clients/symbol-types.js +0 -1
  441. package/extensions/dm-lens/dist/clients/test-runner-client.js +0 -1504
  442. package/extensions/dm-lens/dist/clients/test-runner-delivery.js +0 -260
  443. package/extensions/dm-lens/dist/clients/todo-scanner.js +0 -164
  444. package/extensions/dm-lens/dist/clients/tool-agreement.js +0 -377
  445. package/extensions/dm-lens/dist/clients/tool-config.js +0 -218
  446. package/extensions/dm-lens/dist/clients/tool-cwd.js +0 -235
  447. package/extensions/dm-lens/dist/clients/tool-definition.js +0 -22
  448. package/extensions/dm-lens/dist/clients/tool-event.js +0 -18
  449. package/extensions/dm-lens/dist/clients/tool-policy.js +0 -2393
  450. package/extensions/dm-lens/dist/clients/tool-probe.js +0 -5
  451. package/extensions/dm-lens/dist/clients/tool-render.js +0 -79
  452. package/extensions/dm-lens/dist/clients/tool-set-policy.js +0 -70
  453. package/extensions/dm-lens/dist/clients/tree-sitter-cache.js +0 -206
  454. package/extensions/dm-lens/dist/clients/tree-sitter-client.js +0 -2787
  455. package/extensions/dm-lens/dist/clients/tree-sitter-logger.js +0 -77
  456. package/extensions/dm-lens/dist/clients/tree-sitter-navigator.js +0 -198
  457. package/extensions/dm-lens/dist/clients/tree-sitter-query-loader.js +0 -318
  458. package/extensions/dm-lens/dist/clients/tree-sitter-shared.js +0 -87
  459. package/extensions/dm-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -957
  460. package/extensions/dm-lens/dist/clients/trivy-client.js +0 -305
  461. package/extensions/dm-lens/dist/clients/tui-fit.js +0 -26
  462. package/extensions/dm-lens/dist/clients/turn-context.js +0 -48
  463. package/extensions/dm-lens/dist/clients/turn-end/lane.js +0 -1
  464. package/extensions/dm-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -54
  465. package/extensions/dm-lens/dist/clients/turn-end/lanes/secrets.js +0 -131
  466. package/extensions/dm-lens/dist/clients/turn-summary-render.js +0 -56
  467. package/extensions/dm-lens/dist/clients/turn-summary.js +0 -84
  468. package/extensions/dm-lens/dist/clients/typos-config.js +0 -9
  469. package/extensions/dm-lens/dist/clients/user-notify.js +0 -15
  470. package/extensions/dm-lens/dist/clients/vanished-instance-marker.js +0 -18
  471. package/extensions/dm-lens/dist/clients/warm-attach.js +0 -210
  472. package/extensions/dm-lens/dist/clients/widget-state.js +0 -963
  473. package/extensions/dm-lens/dist/clients/word-index-logger.js +0 -21
  474. package/extensions/dm-lens/dist/clients/word-index-store.js +0 -222
  475. package/extensions/dm-lens/dist/clients/word-index.js +0 -1426
  476. package/extensions/dm-lens/dist/clients/workspace-topology.js +0 -261
  477. package/extensions/dm-lens/dist/clients/write-ordering-guard.js +0 -24
  478. package/extensions/dm-lens/dist/clients/zizmor-config.js +0 -160
  479. package/extensions/dm-lens/dist/i18n.js +0 -67
  480. package/extensions/dm-lens/dist/index.js +0 -1782
  481. package/extensions/dm-lens/dist/mcp/analyze-cli.js +0 -178
  482. package/extensions/dm-lens/dist/mcp/build-staleness.js +0 -55
  483. package/extensions/dm-lens/dist/mcp/cli.js +0 -78
  484. package/extensions/dm-lens/dist/mcp/server.js +0 -1208
  485. package/extensions/dm-lens/dist/mcp/worker.js +0 -33
  486. package/extensions/dm-lens/dist/scripts/lib/process-scan.mjs +0 -256
  487. package/extensions/dm-lens/dist/scripts/lib/skills-predicate.mjs +0 -53
  488. package/extensions/dm-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -45
  489. package/extensions/dm-lens/dist/tools/activate-tools.js +0 -61
  490. package/extensions/dm-lens/dist/tools/ast-grep-outline.js +0 -127
  491. package/extensions/dm-lens/dist/tools/ast-grep-replace.js +0 -226
  492. package/extensions/dm-lens/dist/tools/ast-grep-search.js +0 -687
  493. package/extensions/dm-lens/dist/tools/effective-config.js +0 -59
  494. package/extensions/dm-lens/dist/tools/lens-diagnostic-mark.js +0 -240
  495. package/extensions/dm-lens/dist/tools/lens-diagnostics.js +0 -1484
  496. package/extensions/dm-lens/dist/tools/lsp-diagnostics.js +0 -1088
  497. package/extensions/dm-lens/dist/tools/lsp-navigation.js +0 -1194
  498. package/extensions/dm-lens/dist/tools/lsp-structured-output.js +0 -277
  499. package/extensions/dm-lens/dist/tools/module-report.js +0 -332
  500. package/extensions/dm-lens/dist/tools/project-report.js +0 -72
  501. package/extensions/dm-lens/dist/tools/render-compact.js +0 -272
  502. package/extensions/dm-lens/dist/tools/scan-progress.js +0 -31
  503. package/extensions/dm-lens/dist/tools/shared.js +0 -26
  504. package/extensions/dm-lens/dist/tools/symbol-search.js +0 -133
  505. package/extensions/dm-mcp-adapter/dist/abort.js +0 -35
  506. package/extensions/dm-mcp-adapter/dist/agent-dir.js +0 -45
  507. package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +0 -392
  508. package/extensions/dm-mcp-adapter/dist/agent-plugin-provenance.js +0 -26
  509. package/extensions/dm-mcp-adapter/dist/bearer-command-resolver.js +0 -182
  510. package/extensions/dm-mcp-adapter/dist/claude-plugin-loader.js +0 -350
  511. package/extensions/dm-mcp-adapter/dist/config.js +0 -1310
  512. package/extensions/dm-mcp-adapter/dist/consent-manager.js +0 -84
  513. package/extensions/dm-mcp-adapter/dist/elicitation-handler.js +0 -308
  514. package/extensions/dm-mcp-adapter/dist/errors.js +0 -225
  515. package/extensions/dm-mcp-adapter/dist/http-ca.js +0 -71
  516. package/extensions/dm-mcp-adapter/dist/jev-client.js +0 -359
  517. package/extensions/dm-mcp-adapter/dist/jev-contracts.js +0 -1
  518. package/extensions/dm-mcp-adapter/dist/jev-key-store.js +0 -176
  519. package/extensions/dm-mcp-adapter/dist/json-schema-validator.js +0 -70
  520. package/extensions/dm-mcp-adapter/dist/lifecycle.js +0 -387
  521. package/extensions/dm-mcp-adapter/dist/logger.js +0 -115
  522. package/extensions/dm-mcp-adapter/dist/mcp-auth-fetch.js +0 -86
  523. package/extensions/dm-mcp-adapter/dist/mcp-auth-flow.js +0 -949
  524. package/extensions/dm-mcp-adapter/dist/mcp-auth.js +0 -997
  525. package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +0 -182
  526. package/extensions/dm-mcp-adapter/dist/mcp-callback-server.js +0 -381
  527. package/extensions/dm-mcp-adapter/dist/mcp-oauth-provider.js +0 -524
  528. package/extensions/dm-mcp-adapter/dist/mcp-probe.js +0 -151
  529. package/extensions/dm-mcp-adapter/dist/mcp-tasks.js +0 -300
  530. package/extensions/dm-mcp-adapter/dist/mcp-trace.js +0 -215
  531. package/extensions/dm-mcp-adapter/dist/metadata-cache.js +0 -267
  532. package/extensions/dm-mcp-adapter/dist/npx-resolver.js +0 -465
  533. package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +0 -135
  534. package/extensions/dm-mcp-adapter/dist/request-headers-command.js +0 -294
  535. package/extensions/dm-mcp-adapter/dist/resource-tools.js +0 -7
  536. package/extensions/dm-mcp-adapter/dist/runtime-owner.js +0 -94
  537. package/extensions/dm-mcp-adapter/dist/sampling-handler.js +0 -213
  538. package/extensions/dm-mcp-adapter/dist/secure-keyring.js +0 -191
  539. package/extensions/dm-mcp-adapter/dist/server-manager.js +0 -1514
  540. package/extensions/dm-mcp-adapter/dist/session-approvals.js +0 -127
  541. package/extensions/dm-mcp-adapter/dist/session-recovery.js +0 -82
  542. package/extensions/dm-mcp-adapter/dist/state.js +0 -1
  543. package/extensions/dm-mcp-adapter/dist/types.js +0 -286
  544. package/extensions/dm-mcp-adapter/dist/ui-app-bridge-helpers.js +0 -36
  545. package/extensions/dm-mcp-adapter/dist/ui-resource-handler.js +0 -204
  546. package/extensions/dm-mcp-adapter/dist/ui-stream-types.js +0 -58
  547. package/extensions/dm-mcp-adapter/dist/ui-tool-visibility.js +0 -26
  548. package/extensions/dm-mcp-adapter/dist/unix-socket-transport.js +0 -83
  549. package/extensions/dm-mcp-adapter/dist/utils.js +0 -382
@@ -1,3770 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { existsSync, statSync, unlinkSync } from "node:fs";
3
- import fs from "node:fs/promises";
4
- import https from "node:https";
5
- import { createRequire } from "node:module";
6
- import os from "node:os";
7
- import path from "node:path";
8
- import { writeFileAtomicAsync } from "../atomic-write.js";
9
- import { BoundedLruCache } from "../bounded-cache.js";
10
- import { logExtension } from "../extension-log.js";
11
- import { isFullyQualified } from "../path-utils.js";
12
- import { assertInstallAllowed, projectTrustDenialReason } from "../project-trust.js";
13
- const _installerRequire = createRequire(import.meta.url);
14
- import { createGunzip } from "node:zlib";
15
- import { TRANSIENT_MAX_COOLDOWN_MS } from "../dispatch/runners/utils/availability-policy.js";
16
- import { getDegradationLedgerGeneration, recordDegradationOnce } from "../degradation-ledger.js";
17
- import { commitDurableStoreAsync } from "../durable-store.js";
18
- import { getGlobalDmLensDir } from "../file-utils.js";
19
- import { createGenerationMap } from "../generation-guard.js";
20
- import { resolveToolCwd } from "../tool-cwd.js";
21
- import { allAvailableGlobalBinDirs, installArgs, pmBinary, resolveNodePackageManager } from "../package-manager.js";
22
- import { resetSafeSpawnWindowsCommandCache, safeSpawnAsync } from "../safe-spawn.js";
23
- import { createBoundedOutputSink, DEFAULT_MAX_OUTPUT_BYTES } from "../spawn-output-cap.js";
24
- import { probeToolAsync } from "../tool-probe.js";
25
- import { logSessionStart } from "../sessionstart-logger.js";
26
- import { resolveGitHubToken } from "../zizmor-config.js";
27
- const TOOLS_DIR = path.join(getGlobalDmLensDir(), "tools");
28
- const INSTALL_LOCK_PATH = path.join(TOOLS_DIR, ".install.lock");
29
- const activeInstallLocks = new Set;
30
- let installLockExitCleanupRegistered = false;
31
- export function getManagedToolsDir() {
32
- return TOOLS_DIR;
33
- }
34
- function isProcessAlive(pid) {
35
- try {
36
- process.kill(pid, 0);
37
- return true;
38
- } catch (error) {
39
- return error.code === "EPERM";
40
- }
41
- }
42
- async function acquireInstallLock() {
43
- await fs.mkdir(TOOLS_DIR, { recursive: true });
44
- const timeoutMs = Number(process.env.DM_LENS_INSTALL_LOCK_TIMEOUT_MS) || 150000;
45
- const deadline = Date.now() + timeoutMs;
46
- let lastOwner = "unknown owner";
47
- while (Date.now() < deadline) {
48
- try {
49
- const handle = await fs.open(INSTALL_LOCK_PATH, "wx");
50
- await handle.writeFile(JSON.stringify({ pid: process.pid, createdAt: Date.now() }));
51
- await handle.close();
52
- activeInstallLocks.add(INSTALL_LOCK_PATH);
53
- if (!installLockExitCleanupRegistered) {
54
- installLockExitCleanupRegistered = true;
55
- process.once("exit", () => {
56
- for (const lockPath of activeInstallLocks) {
57
- try {
58
- unlinkSync(lockPath);
59
- } catch {}
60
- }
61
- });
62
- }
63
- let released = false;
64
- return {
65
- release: async () => {
66
- if (released)
67
- return;
68
- released = true;
69
- activeInstallLocks.delete(INSTALL_LOCK_PATH);
70
- await fs.rm(INSTALL_LOCK_PATH, { force: true });
71
- }
72
- };
73
- } catch (error) {
74
- if (error.code !== "EEXIST")
75
- throw error;
76
- try {
77
- const owner = JSON.parse(await fs.readFile(INSTALL_LOCK_PATH, "utf8"));
78
- lastOwner = `pid=${owner.pid} createdAt=${owner.createdAt}`;
79
- const maxAgeMs = (Number(process.env.DM_LENS_INSTALL_TIMEOUT_MS) || 120000) + 60000;
80
- const expired = Number.isFinite(owner.createdAt) && Date.now() - owner.createdAt > maxAgeMs;
81
- if (expired || Number.isInteger(owner.pid) && owner.pid > 0 && !isProcessAlive(owner.pid)) {
82
- await fs.rm(INSTALL_LOCK_PATH, { force: true });
83
- continue;
84
- }
85
- } catch (readError) {
86
- lastOwner = "unreadable owner";
87
- try {
88
- const stat = await fs.stat(INSTALL_LOCK_PATH);
89
- const maxAgeMs = (Number(process.env.DM_LENS_INSTALL_TIMEOUT_MS) || 120000) + 60000;
90
- if (Date.now() - stat.mtimeMs > maxAgeMs) {
91
- await fs.rm(INSTALL_LOCK_PATH, { force: true });
92
- continue;
93
- }
94
- } catch {}
95
- }
96
- await new Promise((resolve) => setTimeout(resolve, 100));
97
- }
98
- }
99
- return {
100
- reason: `timed out after ${timeoutMs}ms waiting for shared tools install lock (${lastOwner})`
101
- };
102
- }
103
- const GITHUB_BIN_DIR = path.join(getGlobalDmLensDir(), "bin");
104
- const DEBUG = process.env.DM_LENS_DEBUG === "1" || process.argv.includes("--debug");
105
- function installerPlatform() {
106
- const override = process.env.DM_LENS_TEST_PLATFORM;
107
- if (override === "win32" || override === "linux") {
108
- return override;
109
- }
110
- return process.platform;
111
- }
112
- function debugLog(...args) {
113
- if (DEBUG) {
114
- logExtension({
115
- subsystem: "auto-install",
116
- level: "debug",
117
- message: args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg)).join(" ")
118
- });
119
- }
120
- }
121
- function archAssetMatch(table) {
122
- return (platform, arch) => {
123
- if (arch !== "x64" && arch !== "arm64")
124
- return;
125
- return table[platform]?.[arch];
126
- };
127
- }
128
- const OS_ARCH_ZIP_ASSETS = {
129
- linux: { x64: "linux_amd64.zip", arm64: "linux_arm64.zip" },
130
- darwin: { x64: "darwin_amd64.zip", arm64: "darwin_arm64.zip" },
131
- win32: { x64: "windows_amd64.zip", arm64: "windows_arm64.zip" }
132
- };
133
- function managedPackageFormatterTool(spec) {
134
- return {
135
- id: spec.id,
136
- name: spec.name,
137
- checkCommand: spec.id,
138
- checkArgs: ["--version"],
139
- installStrategy: spec.installStrategy,
140
- packageName: spec.packageName,
141
- binaryName: spec.id
142
- };
143
- }
144
- const MANAGED_PACKAGE_FORMATTERS = [
145
- { id: "black", name: "Black", installStrategy: "pip", packageName: "black" },
146
- {
147
- id: "cmake-format",
148
- name: "cmake-format",
149
- installStrategy: "pip",
150
- packageName: "cmakelang[yaml]"
151
- },
152
- { id: "oxfmt", name: "oxfmt", installStrategy: "npm", packageName: "oxfmt" }
153
- ];
154
- function managedGitHubFormatterTool(spec) {
155
- return {
156
- id: spec.id,
157
- name: spec.name,
158
- checkCommand: spec.id,
159
- checkArgs: ["--version"],
160
- installStrategy: "github",
161
- binaryName: spec.id,
162
- github: {
163
- repo: `${spec.owner}/${spec.repo}`,
164
- assetMatch: spec.assetPattern,
165
- ...spec.binaryInArchive && { binaryInArchive: spec.binaryInArchive },
166
- ...spec.kind === "phar" && { launcher: "php" },
167
- ...spec.kind === "jar" && { launcher: "java" }
168
- }
169
- };
170
- }
171
- const MANAGED_GITHUB_FORMATTERS = [
172
- {
173
- id: "typstyle",
174
- name: "typstyle",
175
- owner: "typstyle-rs",
176
- repo: "typstyle",
177
- assetPattern: archAssetMatch({
178
- linux: {
179
- x64: "typstyle-x86_64-unknown-linux-gnu",
180
- arm64: "typstyle-aarch64-unknown-linux-gnu"
181
- },
182
- darwin: {
183
- x64: "typstyle-x86_64-apple-darwin",
184
- arm64: "typstyle-aarch64-apple-darwin"
185
- },
186
- win32: {
187
- x64: "typstyle-x86_64-pc-windows-msvc.exe",
188
- arm64: "typstyle-aarch64-pc-windows-msvc.exe"
189
- }
190
- }),
191
- kind: "binary"
192
- },
193
- {
194
- id: "stylua",
195
- name: "StyLua",
196
- owner: "JohnnyMorganz",
197
- repo: "StyLua",
198
- assetPattern: archAssetMatch({
199
- linux: { x64: "linux-x86_64.zip", arm64: "linux-aarch64.zip" },
200
- darwin: { x64: "macos-x86_64.zip", arm64: "macos-aarch64.zip" },
201
- win32: { x64: "windows-x86_64.zip" }
202
- }),
203
- kind: "binary",
204
- binaryInArchive: "stylua"
205
- },
206
- {
207
- id: "php-cs-fixer",
208
- name: "PHP CS Fixer",
209
- owner: "PHP-CS-Fixer",
210
- repo: "PHP-CS-Fixer",
211
- assetPattern: (platform) => platform === "linux" || platform === "darwin" || platform === "win32" ? "php-cs-fixer.phar" : undefined,
212
- kind: "phar"
213
- },
214
- {
215
- id: "cljfmt",
216
- name: "cljfmt",
217
- owner: "weavejester",
218
- repo: "cljfmt",
219
- assetPattern: (platform, arch) => {
220
- if (platform === "linux")
221
- return arch === "arm64" ? "standalone.jar" : "linux-amd64-static.tar.gz";
222
- if (platform === "darwin")
223
- return "standalone.jar";
224
- if (platform === "win32")
225
- return "win-amd64.zip";
226
- return;
227
- },
228
- kind: "jar",
229
- binaryInArchive: "cljfmt"
230
- }
231
- ];
232
- const MANAGED_MAVEN_FORMATTERS = [
233
- {
234
- id: "google-java-format",
235
- name: "google-java-format",
236
- groupId: "com.google.googlejavaformat",
237
- artifactId: "google-java-format",
238
- version: "1.27.0",
239
- classifier: "all-deps"
240
- }
241
- ];
242
- function managedMavenFormatterTool(spec) {
243
- return {
244
- id: spec.id,
245
- name: spec.name,
246
- checkCommand: spec.id,
247
- checkArgs: ["--version"],
248
- installStrategy: "maven",
249
- binaryName: spec.id,
250
- maven: {
251
- groupId: spec.groupId,
252
- artifactId: spec.artifactId,
253
- version: spec.version,
254
- classifier: spec.classifier
255
- }
256
- };
257
- }
258
- export const TOOLS = [
259
- {
260
- id: "typescript-language-server",
261
- name: "TypeScript Language Server",
262
- checkCommand: "typescript-language-server",
263
- checkArgs: ["--version"],
264
- installStrategy: "npm",
265
- packageName: "typescript-language-server@5.3.0",
266
- binaryName: "typescript-language-server"
267
- },
268
- {
269
- id: "typescript",
270
- name: "TypeScript",
271
- checkCommand: "tsc",
272
- checkArgs: ["--version"],
273
- installStrategy: "npm",
274
- packageName: "typescript@5.9.3",
275
- binaryName: "tsc"
276
- },
277
- {
278
- id: "pyright",
279
- name: "Pyright",
280
- checkCommand: "pyright",
281
- checkArgs: ["--version"],
282
- installStrategy: "npm",
283
- packageName: "pyright",
284
- binaryName: "pyright"
285
- },
286
- {
287
- id: "prettier",
288
- name: "Prettier",
289
- checkCommand: "prettier",
290
- checkArgs: ["--version"],
291
- installStrategy: "npm",
292
- packageName: "prettier",
293
- binaryName: "prettier"
294
- },
295
- ...MANAGED_PACKAGE_FORMATTERS.map(managedPackageFormatterTool),
296
- {
297
- id: "ruff",
298
- name: "Ruff",
299
- checkCommand: "ruff",
300
- checkArgs: ["--version"],
301
- installStrategy: "pip",
302
- packageName: "ruff",
303
- binaryName: "ruff"
304
- },
305
- {
306
- id: "jedi-language-server",
307
- name: "Jedi Language Server",
308
- checkCommand: "jedi-language-server",
309
- checkArgs: ["--version"],
310
- installStrategy: "pip",
311
- packageName: "jedi-language-server",
312
- binaryName: "jedi-language-server"
313
- },
314
- {
315
- id: "biome",
316
- name: "Biome",
317
- checkCommand: "biome",
318
- checkArgs: ["--version"],
319
- installStrategy: "npm",
320
- packageName: "@biomejs/biome",
321
- binaryName: "biome",
322
- platformPackage: {
323
- base: "@biomejs/cli",
324
- suffixes: {
325
- "linux-x64": "linux-x64",
326
- "linux-arm64": "linux-arm64",
327
- "darwin-x64": "darwin-x64",
328
- "darwin-arm64": "darwin-arm64",
329
- "win32-x64": "win32-x64",
330
- "win32-arm64": "win32-arm64"
331
- },
332
- binaries: ["biome"]
333
- }
334
- },
335
- {
336
- id: "madge",
337
- name: "Madge",
338
- checkCommand: "madge",
339
- checkArgs: ["--version"],
340
- installStrategy: "npm",
341
- packageName: "madge",
342
- binaryName: "madge"
343
- },
344
- {
345
- id: "jscpd",
346
- name: "jscpd",
347
- checkCommand: "jscpd",
348
- checkArgs: ["--version"],
349
- installStrategy: "npm",
350
- packageName: "jscpd@5.3.0",
351
- binaryName: "jscpd"
352
- },
353
- {
354
- id: "ast-grep",
355
- name: "ast-grep CLI",
356
- checkCommand: "ast-grep",
357
- checkArgs: ["--version"],
358
- installStrategy: "npm",
359
- packageName: "@ast-grep/cli",
360
- binaryName: "ast-grep",
361
- platformPackage: {
362
- suffixes: {
363
- "linux-x64": "linux-x64-gnu",
364
- "linux-arm64": "linux-arm64-gnu",
365
- "darwin-x64": "darwin-x64",
366
- "darwin-arm64": "darwin-arm64",
367
- "win32-x64": "win32-x64-msvc",
368
- "win32-arm64": "win32-arm64-msvc",
369
- "win32-ia32": "win32-ia32-msvc"
370
- },
371
- binaries: ["ast-grep", "sg"]
372
- }
373
- },
374
- {
375
- id: "knip",
376
- name: "Knip",
377
- checkCommand: "knip",
378
- checkArgs: ["--version"],
379
- installStrategy: "npm",
380
- packageName: "knip",
381
- binaryName: "knip"
382
- },
383
- {
384
- id: "yamllint",
385
- name: "yamllint",
386
- checkCommand: "yamllint",
387
- checkArgs: ["--version"],
388
- installStrategy: "pip",
389
- packageName: "yamllint",
390
- binaryName: "yamllint"
391
- },
392
- {
393
- id: "sqlfluff",
394
- name: "sqlfluff",
395
- checkCommand: "sqlfluff",
396
- checkArgs: ["--version"],
397
- installStrategy: "pip",
398
- packageName: "sqlfluff",
399
- binaryName: "sqlfluff"
400
- },
401
- {
402
- id: "bash-language-server",
403
- name: "Bash Language Server",
404
- checkCommand: "bash-language-server",
405
- checkArgs: ["--version"],
406
- verificationTimeoutMs: 20000,
407
- installStrategy: "npm",
408
- packageName: "bash-language-server",
409
- binaryName: "bash-language-server"
410
- },
411
- {
412
- id: "fish-lsp",
413
- name: "Fish Language Server",
414
- checkCommand: "fish-lsp",
415
- checkArgs: ["--version"],
416
- installStrategy: "npm",
417
- packageName: "fish-lsp",
418
- binaryName: "fish-lsp"
419
- },
420
- {
421
- id: "cmake-language-server",
422
- name: "CMake Language Server",
423
- checkCommand: "cmake-language-server",
424
- checkArgs: ["--version"],
425
- installStrategy: "pip",
426
- packageName: "cmake-language-server",
427
- binaryName: "cmake-language-server",
428
- pipConstraints: ["pygls<2"]
429
- },
430
- {
431
- id: "yaml-language-server",
432
- name: "YAML Language Server",
433
- checkCommand: "yaml-language-server",
434
- checkArgs: ["--version"],
435
- installStrategy: "npm",
436
- packageName: "yaml-language-server",
437
- binaryName: "yaml-language-server"
438
- },
439
- {
440
- id: "vscode-json-language-server",
441
- name: "VSCode JSON Language Server",
442
- checkCommand: "vscode-json-language-server",
443
- checkArgs: ["--version"],
444
- verificationTimeoutMs: 20000,
445
- installStrategy: "npm",
446
- packageName: "vscode-langservers-extracted",
447
- binaryName: "vscode-json-language-server"
448
- },
449
- {
450
- id: "vscode-html-languageserver-bin",
451
- name: "VSCode HTML Language Server",
452
- checkCommand: "vscode-html-language-server",
453
- checkArgs: ["--version"],
454
- installStrategy: "npm",
455
- packageName: "vscode-langservers-extracted",
456
- binaryName: "vscode-html-language-server"
457
- },
458
- {
459
- id: "htmlhint",
460
- name: "HTMLHint",
461
- checkCommand: "htmlhint",
462
- checkArgs: ["--version"],
463
- installStrategy: "npm",
464
- packageName: "htmlhint",
465
- binaryName: "htmlhint"
466
- },
467
- {
468
- id: "hadolint",
469
- name: "Hadolint",
470
- checkCommand: "hadolint",
471
- checkArgs: ["--version"],
472
- installStrategy: "github",
473
- binaryName: "hadolint",
474
- github: {
475
- repo: "hadolint/hadolint",
476
- assetMatch: (platform, arch) => {
477
- if (platform === "linux")
478
- return arch === "arm64" ? "linux.aarch64" : "linux.x86_64";
479
- if (platform === "darwin")
480
- return arch === "arm64" ? "macos-arm64" : "macos-x86_64";
481
- if (platform === "win32")
482
- return "windows-x86_64.exe";
483
- return;
484
- }
485
- }
486
- },
487
- {
488
- id: "helm",
489
- name: "Helm",
490
- checkCommand: "helm",
491
- checkArgs: ["--version"],
492
- installStrategy: "github",
493
- binaryName: "helm",
494
- github: {
495
- repo: "helm/helm",
496
- assetMatch: (platform, arch) => {
497
- const cpu = arch === "arm64" ? "arm64" : "amd64";
498
- if (platform === "linux")
499
- return `linux-${cpu}.tar.gz`;
500
- if (platform === "darwin")
501
- return `darwin-${cpu}.tar.gz`;
502
- if (platform === "win32")
503
- return `windows-${cpu}.zip`;
504
- return;
505
- },
506
- binaryInArchive: "helm"
507
- }
508
- },
509
- {
510
- id: "opengrep",
511
- name: "Opengrep",
512
- checkCommand: "opengrep",
513
- checkArgs: ["--version"],
514
- installStrategy: "github",
515
- binaryName: "opengrep",
516
- github: {
517
- repo: "opengrep/opengrep",
518
- assetMatch: (platform, arch) => {
519
- if (platform === "linux")
520
- return arch === "arm64" ? "opengrep_manylinux_aarch64" : "opengrep_manylinux_x86";
521
- if (platform === "darwin")
522
- return arch === "arm64" ? "opengrep_osx_arm64" : "opengrep_osx_x86";
523
- if (platform === "win32")
524
- return "opengrep_windows_x86.exe";
525
- return;
526
- }
527
- }
528
- },
529
- {
530
- id: "vscode-css-languageserver",
531
- name: "VSCode CSS Language Server",
532
- checkCommand: "vscode-css-language-server",
533
- checkArgs: ["--version"],
534
- installStrategy: "npm",
535
- packageName: "vscode-langservers-extracted",
536
- binaryName: "vscode-css-language-server"
537
- },
538
- {
539
- id: "dockerfile-language-server-nodejs",
540
- name: "Dockerfile Language Server",
541
- checkCommand: "docker-langserver",
542
- checkArgs: ["--version"],
543
- installStrategy: "npm",
544
- packageName: "dockerfile-language-server-nodejs",
545
- binaryName: "docker-langserver"
546
- },
547
- {
548
- id: "intelephense",
549
- name: "Intelephense",
550
- checkCommand: "intelephense",
551
- checkArgs: ["--version"],
552
- verification: "package-entry",
553
- installStrategy: "npm",
554
- packageName: "intelephense",
555
- binaryName: "intelephense"
556
- },
557
- {
558
- id: "@prisma/language-server",
559
- name: "Prisma Language Server",
560
- checkCommand: "prisma-language-server",
561
- checkArgs: ["--version"],
562
- verificationTimeoutMs: 40000,
563
- installStrategy: "npm",
564
- packageName: "@prisma/language-server",
565
- binaryName: "prisma-language-server"
566
- },
567
- {
568
- id: "@vue/language-server",
569
- name: "Vue Language Server",
570
- checkCommand: "vue-language-server",
571
- checkArgs: ["--version"],
572
- verificationTimeoutMs: 30000,
573
- installStrategy: "npm",
574
- packageName: "@vue/language-server",
575
- binaryName: "vue-language-server"
576
- },
577
- {
578
- id: "svelte-language-server",
579
- name: "Svelte Language Server",
580
- checkCommand: "svelteserver",
581
- checkArgs: ["--version"],
582
- verificationTimeoutMs: 20000,
583
- installStrategy: "npm",
584
- packageName: "svelte-language-server",
585
- binaryName: "svelteserver"
586
- },
587
- {
588
- id: "markdownlint",
589
- name: "markdownlint-cli2",
590
- checkCommand: "markdownlint-cli2",
591
- checkArgs: ["--no-globs", "-"],
592
- installStrategy: "npm",
593
- packageName: "markdownlint-cli2",
594
- binaryName: "markdownlint-cli2"
595
- },
596
- {
597
- id: "mypy",
598
- name: "mypy",
599
- checkCommand: "mypy",
600
- checkArgs: ["--version"],
601
- installStrategy: "pip",
602
- packageName: "mypy",
603
- binaryName: "mypy"
604
- },
605
- {
606
- id: "rubocop",
607
- name: "RuboCop",
608
- checkCommand: "rubocop",
609
- checkArgs: ["--version"],
610
- installStrategy: "gem",
611
- packageName: "rubocop",
612
- binaryName: "rubocop"
613
- },
614
- {
615
- id: "stylelint",
616
- name: "Stylelint",
617
- checkCommand: "stylelint",
618
- checkArgs: ["--version"],
619
- installStrategy: "npm",
620
- packageName: "stylelint",
621
- binaryName: "stylelint"
622
- },
623
- {
624
- id: "oxlint",
625
- name: "Oxlint",
626
- checkCommand: "oxlint",
627
- checkArgs: ["--version"],
628
- installStrategy: "npm",
629
- packageName: "oxlint",
630
- binaryName: "oxlint"
631
- },
632
- {
633
- id: "shellcheck",
634
- name: "ShellCheck",
635
- checkCommand: "shellcheck",
636
- checkArgs: ["--version"],
637
- installStrategy: "github",
638
- binaryName: "shellcheck",
639
- github: {
640
- repo: "koalaman/shellcheck",
641
- assetMatch: (platform, arch) => {
642
- if (platform === "linux")
643
- return arch === "arm64" ? "linux.aarch64.tar.xz" : "linux.x86_64.tar.xz";
644
- if (platform === "darwin")
645
- return arch === "arm64" ? "darwin.aarch64.tar.xz" : "darwin.x86_64.tar.xz";
646
- if (platform === "win32")
647
- return "zip";
648
- return;
649
- },
650
- binaryInArchive: "shellcheck"
651
- }
652
- },
653
- {
654
- id: "shfmt",
655
- name: "shfmt",
656
- checkCommand: "shfmt",
657
- checkArgs: ["--version"],
658
- installStrategy: "github",
659
- binaryName: "shfmt",
660
- github: {
661
- repo: "mvdan/sh",
662
- assetMatch: (platform, arch) => {
663
- if (platform === "linux")
664
- return arch === "arm64" ? "linux_arm64" : "linux_amd64";
665
- if (platform === "darwin")
666
- return arch === "arm64" ? "darwin_arm64" : "darwin_amd64";
667
- if (platform === "win32")
668
- return arch === "arm64" ? "windows_arm64.exe" : "windows_amd64.exe";
669
- return;
670
- }
671
- }
672
- },
673
- ...MANAGED_GITHUB_FORMATTERS.map(managedGitHubFormatterTool),
674
- ...MANAGED_MAVEN_FORMATTERS.map(managedMavenFormatterTool),
675
- {
676
- id: "rust-analyzer",
677
- name: "rust-analyzer",
678
- checkCommand: "rust-analyzer",
679
- checkArgs: ["--version"],
680
- installStrategy: "github",
681
- binaryName: "rust-analyzer",
682
- github: {
683
- repo: "rust-lang/rust-analyzer",
684
- assetMatch: (platform, arch) => {
685
- if (platform === "linux")
686
- return arch === "arm64" ? "aarch64-unknown-linux-gnu.gz" : "x86_64-unknown-linux-gnu.gz";
687
- if (platform === "darwin")
688
- return arch === "arm64" ? "aarch64-apple-darwin.gz" : "x86_64-apple-darwin.gz";
689
- if (platform === "win32")
690
- return "x86_64-pc-windows-msvc.zip";
691
- return;
692
- }
693
- }
694
- },
695
- {
696
- id: "deno",
697
- name: "Deno",
698
- checkCommand: "deno",
699
- checkArgs: ["--version"],
700
- installStrategy: "github",
701
- binaryName: "deno",
702
- github: {
703
- repo: "denoland/deno",
704
- assetMatch: (platform, arch) => {
705
- if (platform === "linux")
706
- return arch === "arm64" ? "deno-aarch64-unknown-linux-gnu.zip" : "deno-x86_64-unknown-linux-gnu.zip";
707
- if (platform === "darwin")
708
- return arch === "arm64" ? "deno-aarch64-apple-darwin.zip" : "deno-x86_64-apple-darwin.zip";
709
- if (platform === "win32")
710
- return "deno-x86_64-pc-windows-msvc.zip";
711
- return;
712
- }
713
- }
714
- },
715
- {
716
- id: "golangci-lint",
717
- name: "golangci-lint",
718
- checkCommand: "golangci-lint",
719
- checkArgs: ["--version"],
720
- installStrategy: "github",
721
- binaryName: "golangci-lint",
722
- github: {
723
- repo: "golangci/golangci-lint",
724
- assetMatch: (platform, arch) => {
725
- if (platform === "linux")
726
- return arch === "arm64" ? "linux-arm64.tar.gz" : "linux-amd64.tar.gz";
727
- if (platform === "darwin")
728
- return arch === "arm64" ? "darwin-arm64.tar.gz" : "darwin-amd64.tar.gz";
729
- if (platform === "win32")
730
- return arch === "arm64" ? "windows-arm64.zip" : "windows-amd64.zip";
731
- return;
732
- },
733
- binaryInArchive: "golangci-lint"
734
- }
735
- },
736
- {
737
- id: "ktlint",
738
- name: "ktlint",
739
- checkCommand: "ktlint",
740
- checkArgs: ["--version"],
741
- installStrategy: "github",
742
- binaryName: "ktlint",
743
- github: {
744
- repo: "pinterest/ktlint",
745
- assetMatch: (platform, _arch) => {
746
- if (platform === "linux")
747
- return "ktlint";
748
- if (platform === "darwin")
749
- return "ktlint";
750
- if (platform === "win32")
751
- return "ktlint.bat";
752
- return;
753
- },
754
- extraAssets: (platform) => platform === "win32" ? ["ktlint"] : []
755
- }
756
- },
757
- {
758
- id: "ktfmt",
759
- name: "ktfmt",
760
- checkCommand: "ktfmt",
761
- checkArgs: ["--version"],
762
- installStrategy: "maven",
763
- binaryName: "ktfmt",
764
- maven: {
765
- groupId: "com.facebook",
766
- artifactId: "ktfmt",
767
- version: "0.63",
768
- classifier: "with-dependencies"
769
- }
770
- },
771
- {
772
- id: "spotbugs",
773
- name: "SpotBugs",
774
- checkCommand: "spotbugs",
775
- checkArgs: ["--version"],
776
- installStrategy: "archive",
777
- binaryName: "spotbugs",
778
- archive: {
779
- url: "https://github.com/spotbugs/spotbugs/releases/download/4.10.2/spotbugs-4.10.2.tgz",
780
- kind: "tgz",
781
- launcher: "bin/spotbugs"
782
- }
783
- },
784
- {
785
- id: "powershell-editor-services",
786
- name: "PowerShell Editor Services",
787
- checkCommand: "pwsh",
788
- checkArgs: [
789
- "-NoProfile",
790
- "-Command",
791
- "$PSVersionTable.PSVersion.ToString()"
792
- ],
793
- installStrategy: "archive",
794
- binaryName: "powershell-editor-services",
795
- archive: {
796
- url: "https://github.com/PowerShell/PowerShellEditorServices/releases/download/v4.6.0/PowerShellEditorServices.zip",
797
- kind: "zip",
798
- stripComponents: 0,
799
- treeMarker: "PowerShellEditorServices/Start-EditorServices.ps1"
800
- }
801
- },
802
- {
803
- id: "clangd",
804
- name: "clangd",
805
- checkCommand: "clangd",
806
- checkArgs: ["--version"],
807
- installStrategy: "archive",
808
- binaryName: "clangd",
809
- archive: {
810
- url: (platform, arch) => {
811
- const version = "22.1.0";
812
- const base = `https://github.com/clangd/clangd/releases/download/${version}`;
813
- if (platform === "linux")
814
- return arch === "x64" ? `${base}/clangd-linux-${version}.zip` : undefined;
815
- if (platform === "darwin")
816
- return `${base}/clangd-mac-${version}.zip`;
817
- if (platform === "win32")
818
- return `${base}/clangd-windows-${version}.zip`;
819
- return;
820
- },
821
- kind: "zip",
822
- stripComponents: 1,
823
- treeMarker: "bin"
824
- }
825
- },
826
- {
827
- id: "lua-language-server",
828
- name: "lua-language-server",
829
- checkCommand: "lua-language-server",
830
- checkArgs: ["--version"],
831
- installStrategy: "archive",
832
- binaryName: "lua-language-server",
833
- archive: {
834
- url: (platform, arch) => {
835
- const version = "3.18.2";
836
- const base = `https://github.com/LuaLS/lua-language-server/releases/download/${version}`;
837
- if (platform === "linux")
838
- return arch === "arm64" ? `${base}/lua-language-server-${version}-linux-arm64.tar.gz` : `${base}/lua-language-server-${version}-linux-x64.tar.gz`;
839
- if (platform === "darwin")
840
- return arch === "arm64" ? `${base}/lua-language-server-${version}-darwin-arm64.tar.gz` : `${base}/lua-language-server-${version}-darwin-x64.tar.gz`;
841
- if (platform === "win32")
842
- return arch === "arm64" ? undefined : `${base}/lua-language-server-${version}-win32-x64.zip`;
843
- return;
844
- },
845
- kind: (platform) => platform === "win32" ? "zip" : "tgz",
846
- stripComponents: 0,
847
- treeMarker: "bin"
848
- }
849
- },
850
- {
851
- id: "actionlint",
852
- name: "actionlint",
853
- checkCommand: "actionlint",
854
- checkArgs: ["--version"],
855
- installStrategy: "github",
856
- binaryName: "actionlint",
857
- github: {
858
- repo: "rhysd/actionlint",
859
- assetMatch: (platform, arch) => {
860
- if (platform === "linux")
861
- return arch === "arm64" ? "linux_arm64.tar.gz" : "linux_amd64.tar.gz";
862
- if (platform === "darwin")
863
- return arch === "arm64" ? "darwin_arm64.tar.gz" : "darwin_amd64.tar.gz";
864
- if (platform === "win32")
865
- return arch === "arm64" ? "windows_arm64.zip" : "windows_amd64.zip";
866
- return;
867
- },
868
- binaryInArchive: "actionlint"
869
- }
870
- },
871
- {
872
- id: "zizmor",
873
- name: "zizmor",
874
- checkCommand: "zizmor",
875
- checkArgs: ["--version"],
876
- installStrategy: "github",
877
- binaryName: "zizmor",
878
- github: {
879
- repo: "zizmorcore/zizmor",
880
- assetMatch: (platform, arch) => {
881
- if (platform === "linux")
882
- return arch === "arm64" ? "aarch64-unknown-linux-gnu.tar.gz" : "x86_64-unknown-linux-gnu.tar.gz";
883
- if (platform === "darwin")
884
- return arch === "arm64" ? "aarch64-apple-darwin.tar.gz" : "x86_64-apple-darwin.tar.gz";
885
- if (platform === "win32")
886
- return "x86_64-pc-windows-msvc.zip";
887
- return;
888
- },
889
- binaryInArchive: "zizmor"
890
- }
891
- },
892
- {
893
- id: "typos-lsp",
894
- name: "typos-lsp",
895
- checkCommand: "typos-lsp",
896
- checkArgs: ["--version"],
897
- installStrategy: "github",
898
- binaryName: "typos-lsp",
899
- github: {
900
- repo: "tekumara/typos-lsp",
901
- assetMatch: (platform, arch) => {
902
- if (platform === "linux")
903
- return arch === "arm64" ? "aarch64-unknown-linux-gnu.tar.gz" : "x86_64-unknown-linux-gnu.tar.gz";
904
- if (platform === "darwin")
905
- return arch === "arm64" ? "aarch64-apple-darwin.tar.gz" : "x86_64-apple-darwin.tar.gz";
906
- if (platform === "win32")
907
- return arch === "arm64" ? "aarch64-pc-windows-msvc.zip" : "x86_64-pc-windows-msvc.zip";
908
- return;
909
- },
910
- binaryInArchive: "typos-lsp"
911
- }
912
- },
913
- {
914
- id: "tflint",
915
- name: "tflint",
916
- checkCommand: "tflint",
917
- checkArgs: ["--version"],
918
- installStrategy: "github",
919
- binaryName: "tflint",
920
- github: {
921
- repo: "terraform-linters/tflint",
922
- assetMatch: archAssetMatch(OS_ARCH_ZIP_ASSETS),
923
- binaryInArchive: "tflint"
924
- }
925
- },
926
- {
927
- id: "terragrunt",
928
- name: "terragrunt",
929
- checkCommand: "terragrunt",
930
- checkArgs: ["--version"],
931
- installStrategy: "github",
932
- binaryName: "terragrunt",
933
- github: {
934
- repo: "gruntwork-io/terragrunt",
935
- assetMatch: archAssetMatch({
936
- linux: {
937
- x64: "terragrunt_linux_amd64",
938
- arm64: "terragrunt_linux_arm64"
939
- },
940
- darwin: {
941
- x64: "terragrunt_darwin_amd64",
942
- arm64: "terragrunt_darwin_arm64"
943
- },
944
- win32: {
945
- x64: "terragrunt_windows_amd64.exe",
946
- arm64: "terragrunt_windows_amd64.exe"
947
- }
948
- })
949
- }
950
- },
951
- {
952
- id: "gitleaks",
953
- name: "gitleaks",
954
- checkCommand: "gitleaks",
955
- checkArgs: ["--version"],
956
- installStrategy: "github",
957
- binaryName: "gitleaks",
958
- github: {
959
- repo: "gitleaks/gitleaks",
960
- assetMatch: archAssetMatch({
961
- linux: { x64: "linux_x64.tar.gz", arm64: "linux_arm64.tar.gz" },
962
- darwin: { x64: "darwin_x64.tar.gz", arm64: "darwin_arm64.tar.gz" },
963
- win32: { x64: "windows_x64.zip", arm64: "windows_arm64.zip" }
964
- }),
965
- binaryInArchive: "gitleaks"
966
- }
967
- },
968
- {
969
- id: "trivy",
970
- name: "Trivy",
971
- checkCommand: "trivy",
972
- checkArgs: ["--version"],
973
- installStrategy: "github",
974
- binaryName: "trivy",
975
- github: {
976
- repo: "aquasecurity/trivy",
977
- assetMatch: archAssetMatch({
978
- linux: { x64: "Linux-64bit.tar.gz", arm64: "Linux-ARM64.tar.gz" },
979
- darwin: { x64: "macOS-64bit.tar.gz", arm64: "macOS-ARM64.tar.gz" },
980
- win32: { x64: "windows-64bit.zip" }
981
- }),
982
- binaryInArchive: "trivy"
983
- }
984
- },
985
- {
986
- id: "swiftlint",
987
- name: "SwiftLint",
988
- checkCommand: "swiftlint",
989
- checkArgs: ["--version"],
990
- installStrategy: "github",
991
- binaryName: "swiftlint",
992
- github: {
993
- repo: "realm/SwiftLint",
994
- assetMatch: (platform, arch) => {
995
- if (platform === "darwin")
996
- return "portable_swiftlint.zip";
997
- if (platform === "linux")
998
- return arch === "arm64" ? "swiftlint_linux_arm64.zip" : "swiftlint_linux_amd64.zip";
999
- return;
1000
- },
1001
- binaryInArchive: "swiftlint"
1002
- }
1003
- },
1004
- {
1005
- id: "taplo",
1006
- name: "taplo",
1007
- checkCommand: "taplo",
1008
- checkArgs: ["--version"],
1009
- installStrategy: "github",
1010
- binaryName: "taplo",
1011
- github: {
1012
- repo: "tamasfe/taplo",
1013
- assetMatch: (platform, arch) => {
1014
- if (platform === "linux")
1015
- return arch === "arm64" ? "taplo-linux-aarch64.gz" : "taplo-linux-x86_64.gz";
1016
- if (platform === "darwin")
1017
- return arch === "arm64" ? "taplo-darwin-aarch64.gz" : "taplo-darwin-x86_64.gz";
1018
- if (platform === "win32")
1019
- return "taplo-windows-x86_64.gz";
1020
- return;
1021
- }
1022
- }
1023
- },
1024
- {
1025
- id: "vale",
1026
- name: "Vale",
1027
- checkCommand: "vale",
1028
- checkArgs: ["--version"],
1029
- installStrategy: "github",
1030
- binaryName: "vale",
1031
- github: {
1032
- repo: "vale-cli/vale",
1033
- assetMatch: (platform, arch) => {
1034
- const version = "3.14.2";
1035
- if (platform === "linux")
1036
- return arch === "arm64" ? `vale_${version}_Linux_arm64.tar.gz` : `vale_${version}_Linux_64-bit.tar.gz`;
1037
- if (platform === "darwin")
1038
- return arch === "arm64" ? `vale_${version}_macOS_arm64.tar.gz` : `vale_${version}_macOS_64-bit.tar.gz`;
1039
- if (platform === "win32")
1040
- return `vale_${version}_Windows_64-bit.zip`;
1041
- return;
1042
- },
1043
- binaryInArchive: "vale"
1044
- }
1045
- },
1046
- {
1047
- id: "terraform-ls",
1048
- name: "terraform-ls",
1049
- checkCommand: "terraform-ls",
1050
- checkArgs: ["--version"],
1051
- installStrategy: "github",
1052
- binaryName: "terraform-ls",
1053
- github: {
1054
- repo: "hashicorp/terraform-ls",
1055
- hashiCorpReleaseProduct: "terraform-ls",
1056
- assetMatch: archAssetMatch(OS_ARCH_ZIP_ASSETS),
1057
- binaryInArchive: "terraform-ls"
1058
- }
1059
- },
1060
- {
1061
- id: "zls",
1062
- name: "zls",
1063
- checkCommand: "zls",
1064
- checkArgs: ["--version"],
1065
- installStrategy: "github",
1066
- binaryName: "zls",
1067
- github: {
1068
- repo: "zigtools/zls",
1069
- assetMatch: (platform, arch) => {
1070
- if (platform === "linux")
1071
- return arch === "arm64" ? "aarch64-linux.tar.xz" : "x86_64-linux.tar.xz";
1072
- if (platform === "darwin")
1073
- return arch === "arm64" ? "aarch64-macos.tar.xz" : "x86_64-macos.tar.xz";
1074
- if (platform === "win32")
1075
- return arch === "arm64" ? "aarch64-windows.zip" : "x86_64-windows.zip";
1076
- return;
1077
- },
1078
- binaryInArchive: "zls"
1079
- }
1080
- },
1081
- {
1082
- id: "clojure-lsp",
1083
- name: "clojure-lsp",
1084
- checkCommand: "clojure-lsp",
1085
- checkArgs: ["--version"],
1086
- installStrategy: "github",
1087
- binaryName: "clojure-lsp",
1088
- github: {
1089
- repo: "clojure-lsp/clojure-lsp",
1090
- assetMatch: (platform, arch) => {
1091
- if (platform === "linux")
1092
- return arch === "arm64" ? "native-linux-aarch64.zip" : "native-linux-amd64.zip";
1093
- if (platform === "darwin")
1094
- return arch === "arm64" ? "native-macos-aarch64.zip" : "native-macos-amd64.zip";
1095
- if (platform === "win32")
1096
- return "native-windows-amd64.zip";
1097
- return;
1098
- },
1099
- binaryInArchive: "clojure-lsp"
1100
- }
1101
- },
1102
- {
1103
- id: "cue",
1104
- name: "CUE",
1105
- checkCommand: "cue",
1106
- checkArgs: ["version"],
1107
- installStrategy: "github",
1108
- binaryName: "cue",
1109
- github: {
1110
- repo: "cue-lang/cue",
1111
- assetMatch: (platform, arch) => {
1112
- if (platform === "linux")
1113
- return arch === "arm64" ? "linux_arm64.tar.gz" : "linux_amd64.tar.gz";
1114
- if (platform === "darwin")
1115
- return arch === "arm64" ? "darwin_arm64.tar.gz" : "darwin_amd64.tar.gz";
1116
- if (platform === "win32")
1117
- return arch === "arm64" ? "windows_arm64.zip" : "windows_amd64.zip";
1118
- return;
1119
- },
1120
- binaryInArchive: "cue"
1121
- }
1122
- },
1123
- {
1124
- id: "gleam",
1125
- name: "Gleam",
1126
- checkCommand: "gleam",
1127
- checkArgs: ["--version"],
1128
- installStrategy: "github",
1129
- binaryName: "gleam",
1130
- github: {
1131
- repo: "gleam-lang/gleam",
1132
- assetMatch: (platform, arch) => {
1133
- if (platform === "linux")
1134
- return arch === "arm64" ? "aarch64-unknown-linux-musl.tar.gz" : "x86_64-unknown-linux-musl.tar.gz";
1135
- if (platform === "darwin")
1136
- return arch === "arm64" ? "aarch64-apple-darwin.tar.gz" : "x86_64-apple-darwin.tar.gz";
1137
- if (platform === "win32")
1138
- return arch === "arm64" ? "aarch64-pc-windows-msvc.zip" : "x86_64-pc-windows-msvc.zip";
1139
- return;
1140
- },
1141
- binaryInArchive: "gleam"
1142
- }
1143
- },
1144
- {
1145
- id: "tinymist",
1146
- name: "Tinymist",
1147
- checkCommand: "tinymist",
1148
- checkArgs: ["--version"],
1149
- installStrategy: "github",
1150
- binaryName: "tinymist",
1151
- github: {
1152
- repo: "Myriad-Dreamin/tinymist",
1153
- assetMatch: archAssetMatch({
1154
- linux: {
1155
- x64: "tinymist-x86_64-unknown-linux-gnu.tar.gz",
1156
- arm64: "tinymist-aarch64-unknown-linux-gnu.tar.gz"
1157
- },
1158
- darwin: {
1159
- x64: "tinymist-x86_64-apple-darwin.tar.gz",
1160
- arm64: "tinymist-aarch64-apple-darwin.tar.gz"
1161
- },
1162
- win32: {
1163
- x64: "tinymist-x86_64-pc-windows-msvc.zip",
1164
- arm64: "tinymist-aarch64-pc-windows-msvc.zip"
1165
- }
1166
- }),
1167
- binaryInArchive: "tinymist"
1168
- }
1169
- },
1170
- {
1171
- id: "marksman",
1172
- name: "Marksman",
1173
- checkCommand: "marksman",
1174
- checkArgs: ["--version"],
1175
- installStrategy: "github",
1176
- binaryName: "marksman",
1177
- github: {
1178
- repo: "artempyanykh/marksman",
1179
- assetMatch: (platform, arch) => {
1180
- if (platform === "linux")
1181
- return arch === "arm64" ? "marksman-linux-arm64" : "marksman-linux-x64";
1182
- if (platform === "darwin")
1183
- return "marksman-macos";
1184
- if (platform === "win32")
1185
- return "marksman.exe";
1186
- return;
1187
- }
1188
- }
1189
- },
1190
- {
1191
- id: "expert",
1192
- name: "Expert",
1193
- checkCommand: "expert",
1194
- checkArgs: ["--version"],
1195
- installStrategy: "github",
1196
- binaryName: "expert",
1197
- github: {
1198
- repo: "expert-lsp/expert",
1199
- assetMatch: (platform, arch) => {
1200
- if (arch !== "x64" && arch !== "arm64")
1201
- return;
1202
- if (platform === "linux")
1203
- return arch === "arm64" ? "expert_linux_arm64" : "expert_linux_amd64";
1204
- if (platform === "darwin")
1205
- return arch === "arm64" ? "expert_darwin_arm64" : "expert_darwin_amd64";
1206
- if (platform === "win32")
1207
- return "expert_windows_amd64.exe";
1208
- return;
1209
- }
1210
- }
1211
- }
1212
- ];
1213
- export function getToolVerificationTimeout(tool) {
1214
- return tool.verificationTimeoutMs ?? 1e4;
1215
- }
1216
- const ensureInFlight = new Map;
1217
- const installFailureReasons = new Map;
1218
- export function getInstallFailureReason(toolId) {
1219
- return installFailureReasons.get(toolId);
1220
- }
1221
- const installAttempts = new Map;
1222
- function noteInstallAttempt(toolId, outcome, reason) {
1223
- installAttempts.set(toolId, { outcome, reason, at: Date.now() });
1224
- }
1225
- function noteInstallAttemptIfUnrecorded(toolId, outcome, reason) {
1226
- if (installAttempts.has(toolId))
1227
- return;
1228
- noteInstallAttempt(toolId, outcome, reason);
1229
- }
1230
- export function getInstallAttempt(toolId) {
1231
- return installAttempts.get(toolId);
1232
- }
1233
- const lastEnsureResolutionSource = new Map;
1234
- export function getLastEnsureResolutionSource(toolId) {
1235
- return lastEnsureResolutionSource.get(toolId);
1236
- }
1237
- const resolvedPathCache = new BoundedLruCache(256);
1238
- export function resetResolvedPathCache() {
1239
- resolvedPathCache.clear();
1240
- }
1241
- const PROBE_CACHE_PATH = path.join(getGlobalDmLensDir(), "probe-cache.json");
1242
- const PROBE_CACHE_LOCK_STALE_MS = 180000;
1243
- const PROBE_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
1244
- const PROBE_CACHE_TRANSIENT_COOLDOWN_MS = TRANSIENT_MAX_COOLDOWN_MS;
1245
- const PROBE_CACHE_FLUSH_LOCK_WAIT_MS = 250;
1246
- const PROBE_CACHE_FLUSH_RETRY_DELAY_MS = 300;
1247
- const PROBE_CACHE_FLUSH_RETRY_MAX_DELAY_MS = 30000;
1248
- let _probeCache = null;
1249
- let _probeCacheDirty = false;
1250
- let _probeCacheFlushTimer = null;
1251
- let _probeCacheWriteInFlight = null;
1252
- let _probeCacheRetryAttempt = 0;
1253
- let _probeCacheChangeGeneration = 0;
1254
- const _probeCacheChanges = new Map;
1255
- const _probeCacheChangeVersions = new Map;
1256
- async function readProbeCache() {
1257
- if (_probeCache !== null)
1258
- return _probeCache;
1259
- try {
1260
- const raw = await fs.readFile(PROBE_CACHE_PATH, "utf-8");
1261
- const parsed = JSON.parse(raw);
1262
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1263
- throw new Error("probe-cache root is not an object");
1264
- }
1265
- _probeCache = parsed;
1266
- } catch (err) {
1267
- const code = err?.code;
1268
- if (code !== "ENOENT") {
1269
- logSessionStart(`auto-install probe-cache: read failed (${code ?? "invalid"}); treating cache as unavailable`);
1270
- }
1271
- _probeCache = {};
1272
- }
1273
- return _probeCache;
1274
- }
1275
- function markProbeCacheChange(toolId, entry) {
1276
- _probeCacheChanges.set(toolId, entry);
1277
- _probeCacheChangeVersions.set(toolId, ++_probeCacheChangeGeneration);
1278
- _probeCacheDirty = true;
1279
- scheduleProbeFlush();
1280
- }
1281
- function scheduleProbeFlush(delayMs = PROBE_CACHE_FLUSH_RETRY_DELAY_MS) {
1282
- if (_probeCacheFlushTimer !== null)
1283
- return;
1284
- _probeCacheFlushTimer = setTimeout(() => {
1285
- flushProbeCache();
1286
- }, delayMs);
1287
- _probeCacheFlushTimer.unref?.();
1288
- }
1289
- function scheduleProbeFlushRetry() {
1290
- const delay = Math.min(PROBE_CACHE_FLUSH_RETRY_MAX_DELAY_MS, PROBE_CACHE_FLUSH_RETRY_DELAY_MS * 2 ** Math.min(_probeCacheRetryAttempt, 6));
1291
- _probeCacheRetryAttempt += 1;
1292
- scheduleProbeFlush(delay);
1293
- }
1294
- function snapshotProbeCacheChanges() {
1295
- return {
1296
- changes: new Map(_probeCacheChanges),
1297
- versions: new Map(_probeCacheChangeVersions)
1298
- };
1299
- }
1300
- function deserializeProbeCache(contents) {
1301
- if (contents === undefined)
1302
- return {};
1303
- try {
1304
- const parsed = JSON.parse(contents);
1305
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1306
- throw new Error("probe-cache root is not an object");
1307
- }
1308
- return parsed;
1309
- } catch (err) {
1310
- logSessionStart(`auto-install probe-cache: write-side read was corrupt (${err.message}); recovering as empty`);
1311
- return {};
1312
- }
1313
- }
1314
- function applyProbeCacheChanges(disk, changes) {
1315
- for (const [toolId, entry] of changes) {
1316
- if (entry === null)
1317
- delete disk[toolId];
1318
- else
1319
- disk[toolId] = entry;
1320
- }
1321
- }
1322
- function ageProbeCache(disk) {
1323
- const now = Date.now();
1324
- for (const [toolId, entry] of Object.entries(disk)) {
1325
- const ttl = entry.transient ? PROBE_CACHE_TRANSIENT_COOLDOWN_MS : PROBE_CACHE_TTL_MS;
1326
- if (!Number.isFinite(entry.cachedAt) || entry.cachedAt < now - ttl) {
1327
- delete disk[toolId];
1328
- }
1329
- }
1330
- }
1331
- function publishProbeCacheWrite(disk, snapshotVersions) {
1332
- const pendingAfterWrite = new Map(_probeCacheChanges);
1333
- _probeCache = disk;
1334
- for (const [toolId, entry] of pendingAfterWrite) {
1335
- if (entry === null)
1336
- delete _probeCache[toolId];
1337
- else
1338
- _probeCache[toolId] = entry;
1339
- }
1340
- for (const [toolId, version] of snapshotVersions) {
1341
- if (_probeCacheChangeVersions.get(toolId) === version) {
1342
- _probeCacheChanges.delete(toolId);
1343
- _probeCacheChangeVersions.delete(toolId);
1344
- }
1345
- }
1346
- _probeCacheDirty = _probeCacheChanges.size > 0;
1347
- _probeCacheRetryAttempt = 0;
1348
- return _probeCacheDirty ? "written-with-pending" : "written";
1349
- }
1350
- async function writeProbeCache() {
1351
- try {
1352
- const { changes, versions } = snapshotProbeCacheChanges();
1353
- let result = "written";
1354
- const committed = await commitDurableStoreAsync({
1355
- path: PROBE_CACHE_PATH,
1356
- deserialize: deserializeProbeCache,
1357
- merge: (disk) => {
1358
- ageProbeCache(disk);
1359
- applyProbeCacheChanges(disk, changes);
1360
- return disk;
1361
- },
1362
- serialize: (disk) => JSON.stringify(disk, null, 2),
1363
- waitMs: PROBE_CACHE_FLUSH_LOCK_WAIT_MS,
1364
- retryMs: 25,
1365
- staleMs: PROBE_CACHE_LOCK_STALE_MS,
1366
- timeoutMessage: "Timed out waiting for probe-cache lock",
1367
- onContention: "skip-log",
1368
- logContention: () => {
1369
- logSessionStart("auto-install probe-cache: flush deferred because another process owns the lock");
1370
- },
1371
- afterWriteLocked: (disk) => {
1372
- result = publishProbeCacheWrite(disk, versions);
1373
- }
1374
- });
1375
- if (committed === undefined) {
1376
- scheduleProbeFlushRetry();
1377
- return "deferred";
1378
- }
1379
- return result;
1380
- } catch (err) {
1381
- logSessionStart(`auto-install probe-cache: flush failed (${err?.code ?? "write error"}); pending update retained`);
1382
- scheduleProbeFlushRetry();
1383
- return "failed";
1384
- }
1385
- }
1386
- export async function flushProbeCache() {
1387
- if (_probeCacheFlushTimer !== null) {
1388
- clearTimeout(_probeCacheFlushTimer);
1389
- _probeCacheFlushTimer = null;
1390
- }
1391
- if (_probeCacheWriteInFlight) {
1392
- await _probeCacheWriteInFlight;
1393
- if (!_probeCacheDirty)
1394
- return "written";
1395
- }
1396
- if (!_probeCacheDirty || _probeCache === null)
1397
- return "idle";
1398
- const write = writeProbeCache();
1399
- _probeCacheWriteInFlight = write;
1400
- try {
1401
- return await write;
1402
- } finally {
1403
- if (_probeCacheWriteInFlight === write)
1404
- _probeCacheWriteInFlight = null;
1405
- }
1406
- }
1407
- function isAstGrepVersionOutput(output) {
1408
- return /\bast[- ]grep\b/i.test(output);
1409
- }
1410
- async function verifyAstGrepProbePath(binPath) {
1411
- const result = await probeToolAsync(binPath, ["--version"], {
1412
- timeout: 5000
1413
- });
1414
- return !result.error && result.status === 0 && isAstGrepVersionOutput(`${result.stdout}${result.stderr}`);
1415
- }
1416
- export async function checkProbeCache(toolId) {
1417
- const cache = await readProbeCache();
1418
- const entry = cache[toolId];
1419
- if (!entry)
1420
- return;
1421
- const ttl = entry.transient ? PROBE_CACHE_TRANSIENT_COOLDOWN_MS : PROBE_CACHE_TTL_MS;
1422
- if (Date.now() - entry.cachedAt > ttl) {
1423
- logSessionStart(`auto-install probe-cache ${toolId}: miss (${entry.transient ? "transient cooldown" : "ttl"} expired)`);
1424
- delete cache[toolId];
1425
- markProbeCacheChange(toolId, null);
1426
- return;
1427
- }
1428
- try {
1429
- await fs.access(entry.path);
1430
- const stat = await fs.stat(entry.path);
1431
- if (stat.mtimeMs !== entry.mtimeMs || entry.sizeBytes !== undefined && stat.size !== entry.sizeBytes) {
1432
- logSessionStart(`auto-install probe-cache ${toolId}: miss (mtime/size changed)`);
1433
- delete cache[toolId];
1434
- markProbeCacheChange(toolId, null);
1435
- return;
1436
- }
1437
- if (toolId === "ast-grep" && !await verifyAstGrepProbePath(entry.path)) {
1438
- logSessionStart(`auto-install probe-cache ${toolId}: miss (not ast-grep: ${entry.path})`);
1439
- delete cache[toolId];
1440
- markProbeCacheChange(toolId, null);
1441
- return;
1442
- }
1443
- return entry.path;
1444
- } catch {
1445
- logSessionStart(`auto-install probe-cache ${toolId}: miss (gone: ${entry.path})`);
1446
- delete cache[toolId];
1447
- markProbeCacheChange(toolId, null);
1448
- return;
1449
- }
1450
- }
1451
- export async function updateProbeCache(toolId, resolvedPath, transient = false) {
1452
- try {
1453
- const stat = await fs.stat(resolvedPath);
1454
- const cache = await readProbeCache();
1455
- const entry = {
1456
- path: resolvedPath,
1457
- mtimeMs: stat.mtimeMs,
1458
- sizeBytes: stat.size,
1459
- cachedAt: Date.now(),
1460
- ...transient && { transient: true }
1461
- };
1462
- cache[toolId] = entry;
1463
- markProbeCacheChange(toolId, entry);
1464
- } catch {}
1465
- }
1466
- export function resetProbeCacheStateForTesting() {
1467
- _probeCache = null;
1468
- _probeCacheDirty = false;
1469
- _probeCacheChanges.clear();
1470
- _probeCacheChangeVersions.clear();
1471
- _probeCacheChangeGeneration = 0;
1472
- _probeCacheRetryAttempt = 0;
1473
- resetResolvedPathCache();
1474
- ensureInFlight.clear();
1475
- installFailureReasons.clear();
1476
- installAttempts.clear();
1477
- lastEnsureResolutionSource.clear();
1478
- lastManagedInstallVersion.clear();
1479
- lastResolveTransient.clear();
1480
- resetPathWalkMemo();
1481
- if (_probeCacheFlushTimer !== null) {
1482
- clearTimeout(_probeCacheFlushTimer);
1483
- _probeCacheFlushTimer = null;
1484
- }
1485
- }
1486
- export function _peekEnsureInFlightForTesting() {
1487
- return ensureInFlight;
1488
- }
1489
- const pathWalkMemo = new Map;
1490
- function hashSync(value) {
1491
- let hash = 2166136261;
1492
- for (let i = 0;i < value.length; i += 1) {
1493
- hash ^= value.charCodeAt(i);
1494
- hash = Math.imul(hash, 16777619);
1495
- }
1496
- return (hash >>> 0).toString(16);
1497
- }
1498
- export function resetPathWalkMemo() {
1499
- pathWalkMemo.clear();
1500
- }
1501
- export async function isCommandAvailable(command, _args) {
1502
- const isWindows = installerPlatform() === "win32";
1503
- const pathEnv = process.env.PATH || process.env.Path || process.env.path || "";
1504
- const dirs = pathEnv.split(path.delimiter);
1505
- const names = isWindows ? [command, `${command}.exe`, `${command}.cmd`, `${command}.bat`] : [command];
1506
- for (const dir of dirs) {
1507
- if (!dir)
1508
- continue;
1509
- for (const name of names) {
1510
- const candidate = path.join(dir, name);
1511
- try {
1512
- const stat = statSync(candidate);
1513
- if (stat.isFile() && stat.size > 0) {
1514
- return true;
1515
- }
1516
- } catch {}
1517
- }
1518
- }
1519
- return false;
1520
- }
1521
- export async function isSpawnableCommand(command) {
1522
- if (/[\\/]/.test(command)) {
1523
- try {
1524
- const stat = statSync(command);
1525
- return stat.isFile() && stat.size > 0;
1526
- } catch {
1527
- return false;
1528
- }
1529
- }
1530
- const memoKey = `${command}:${hashSync(process.env.PATH || "")}`;
1531
- if (pathWalkMemo.has(memoKey))
1532
- return pathWalkMemo.get(memoKey) ?? false;
1533
- const isSpawnable = await isCommandAvailable(command);
1534
- pathWalkMemo.set(memoKey, isSpawnable);
1535
- return isSpawnable;
1536
- }
1537
- export function isLspTransportRequiredError(output) {
1538
- return /Connection (?:input|output) stream is not set|Use arguments of createConnection/i.test(output);
1539
- }
1540
- const lspTransportRequiredMatcher = /Connection (?:input|output) stream is not set|Use arguments of createConnection/i;
1541
- export function parsePinnedVersion(packageName) {
1542
- const at = packageName.lastIndexOf("@");
1543
- if (at <= 0)
1544
- return;
1545
- return packageName.slice(at + 1) || undefined;
1546
- }
1547
- function extractVersionToken(output) {
1548
- return output.match(/\d+\.\d+\.\d+(?:[-+][\w.]+)?/)?.[0];
1549
- }
1550
- const lastManagedInstallVersion = new Map;
1551
- export function packageEntryVerification(tool) {
1552
- return tool.verification === "package-entry" ? tool.packageName : undefined;
1553
- }
1554
- export async function verifyNpmPackageEntry(binPath, packageName) {
1555
- const nodeModulesDir = path.dirname(path.dirname(binPath));
1556
- const pinned = parsePinnedVersion(packageName);
1557
- const bareName = pinned ? packageName.slice(0, -(pinned.length + 1)) : packageName;
1558
- const packageDir = path.join(nodeModulesDir, ...bareName.split("/"));
1559
- const shimName = path.basename(binPath).replace(/\.(cmd|exe|ps1|bat)$/i, "").toLowerCase();
1560
- const fail = (reason) => {
1561
- logSessionStart(`auto-install verify: failed for ${binPath} (check=package-entry, kind=${reason})`);
1562
- return false;
1563
- };
1564
- try {
1565
- const shim = statSync(binPath);
1566
- if (!shim.isFile() || shim.size === 0)
1567
- return fail("shim-not-a-file");
1568
- } catch {
1569
- return fail("shim-missing");
1570
- }
1571
- let manifest;
1572
- try {
1573
- manifest = JSON.parse(await fs.readFile(path.join(packageDir, "package.json"), "utf8"));
1574
- } catch {
1575
- return fail("package-json-unreadable");
1576
- }
1577
- if (typeof manifest.version !== "string" || !manifest.version) {
1578
- return fail("package-json-no-version");
1579
- }
1580
- const bin = manifest.bin;
1581
- const entry = typeof bin === "string" ? bin : bin && typeof bin === "object" ? Object.entries(bin).find(([name]) => name.toLowerCase() === shimName)?.[1] : undefined;
1582
- if (typeof entry !== "string" || !entry) {
1583
- return fail("package-json-no-entry");
1584
- }
1585
- try {
1586
- const stat = statSync(path.join(packageDir, entry));
1587
- if (!stat.isFile() || stat.size === 0)
1588
- return fail("entry-not-a-file");
1589
- } catch {
1590
- return fail("entry-missing");
1591
- }
1592
- logSessionStart(`auto-install verify: succeeded for ${binPath} (check=package-entry, version=${manifest.version}, entry=${entry})`);
1593
- return true;
1594
- }
1595
- export async function verifyToolBinary(binPath, onVersionOutput, onTransient, timeoutMs = 1e4, verificationArgs = ["--version"], packageEntryOf, onInconclusive) {
1596
- if (packageEntryOf !== undefined) {
1597
- return verifyNpmPackageEntry(binPath, packageEntryOf);
1598
- }
1599
- const isWindows = installerPlatform() === "win32";
1600
- const hasKnownWindowsExt = /\.(cmd|exe|ps1)$/i.test(binPath);
1601
- let execPath = isWindows && !hasKnownWindowsExt ? `${binPath}.cmd` : binPath;
1602
- if (isWindows && /\.ps1$/i.test(execPath)) {
1603
- const cmdSibling = `${execPath.slice(0, -4)}.cmd`;
1604
- if (require("node:fs").existsSync(cmdSibling)) {
1605
- execPath = cmdSibling;
1606
- }
1607
- }
1608
- const useShell = isWindows && /\.(cmd|bat)$/i.test(execPath);
1609
- try {
1610
- const result = await safeSpawnAsync(execPath, verificationArgs, {
1611
- timeout: timeoutMs,
1612
- input: "",
1613
- maxOutputBytes: 64 * 1024,
1614
- matchWhileStreaming: lspTransportRequiredMatcher
1615
- });
1616
- const output = `${result.stdout}
1617
- ${result.stderr}`;
1618
- if (result.outputTruncated) {
1619
- recordDegradationOnce({
1620
- kind: "installer-verification-output-truncated",
1621
- subject: binPath,
1622
- reason: `verification output exceeded 65536 bytes (${verificationArgs.join(" ")})`
1623
- });
1624
- }
1625
- if (result.status === 0 && !result.error) {
1626
- debugLog(`Verified: ${binPath} (${verificationArgs.join(" ")}: ${result.stdout.trim()})`);
1627
- onVersionOutput?.(result.stdout);
1628
- return true;
1629
- }
1630
- if (result.streamingMatch || isLspTransportRequiredError(output)) {
1631
- debugLog(`Verified (stdio LSP, transport-required): ${binPath}`);
1632
- return true;
1633
- }
1634
- if (result.signal !== undefined || result.spawnFailure)
1635
- onTransient?.();
1636
- if (result.outputTruncated && result.signal === undefined && !result.spawnFailure) {
1637
- onInconclusive?.();
1638
- recordDegradationOnce({
1639
- kind: "installer-verification-inconclusive",
1640
- subject: binPath,
1641
- reason: `transport-required marker unresolved in truncated output (${verificationArgs.join(" ")})`
1642
- });
1643
- }
1644
- const kind = result.spawnFailure?.kind ?? (result.signal ? `killed-signal=${result.signal}` : result.error ? "spawn-error" : "exit-nonzero");
1645
- logSessionStart(`auto-install verify: failed for ${binPath} (check=${verificationArgs.join(" ")}, kind=${kind}${result.status !== null ? `, exit=${result.status}` : ""})`);
1646
- return false;
1647
- } catch (err) {
1648
- logSessionStart(`auto-install verify: spawn threw for ${binPath} (${err instanceof Error ? err.message : String(err)})`);
1649
- onTransient?.();
1650
- return false;
1651
- }
1652
- }
1653
- export async function getAllToolStatuses() {
1654
- const statuses = [];
1655
- for (const tool of TOOLS) {
1656
- const status = {
1657
- id: tool.id,
1658
- name: tool.name,
1659
- installed: false,
1660
- source: "not-installed",
1661
- strategy: tool.installStrategy
1662
- };
1663
- if (tool.installStrategy === "archive" && !tool.archive?.launcher) {
1664
- const bundleDir = await getArchiveTreeBundlePath(tool);
1665
- if (bundleDir) {
1666
- status.installed = true;
1667
- status.source = "archive-dist";
1668
- status.path = bundleDir;
1669
- }
1670
- statuses.push(status);
1671
- continue;
1672
- }
1673
- if (await isCommandAvailable(tool.checkCommand, tool.checkArgs)) {
1674
- status.installed = true;
1675
- status.source = "global-path";
1676
- status.path = tool.checkCommand;
1677
- const probe = await probeToolAsync(tool.checkCommand, ["--version"], {
1678
- timeout: 5000
1679
- });
1680
- status.version = `${probe.stdout}${probe.stderr}`.trim().split(`
1681
- `)[0]?.slice(0, 30) || undefined;
1682
- statuses.push(status);
1683
- continue;
1684
- }
1685
- if (tool.installStrategy === "npm") {
1686
- const npmPath = await findNpmGlobalToolPath(tool.binaryName || tool.id, undefined, tool.checkArgs, getToolVerificationTimeout(tool));
1687
- if (npmPath) {
1688
- status.installed = true;
1689
- status.source = "npm-global";
1690
- status.path = npmPath;
1691
- statuses.push(status);
1692
- continue;
1693
- }
1694
- }
1695
- if (tool.installStrategy === "pip") {
1696
- const pipPath = await findPipUserToolPath(tool.binaryName || tool.id, undefined, tool.checkArgs, getToolVerificationTimeout(tool));
1697
- if (pipPath) {
1698
- status.installed = true;
1699
- status.source = "pip-user";
1700
- status.path = pipPath;
1701
- statuses.push(status);
1702
- continue;
1703
- }
1704
- }
1705
- if (tool.installStrategy === "github" || tool.installStrategy === "maven" || tool.installStrategy === "archive") {
1706
- const githubPath = await findGitHubToolPath(tool.binaryName || tool.id);
1707
- if (githubPath) {
1708
- status.installed = true;
1709
- status.source = tool.installStrategy === "maven" ? "maven-jar" : tool.installStrategy === "archive" ? "archive-dist" : "github-release";
1710
- status.path = githubPath;
1711
- statuses.push(status);
1712
- continue;
1713
- }
1714
- }
1715
- const localBase = path.join(TOOLS_DIR, "node_modules", ".bin", tool.binaryName || tool.id);
1716
- const localPath = installerPlatform() === "win32" ? `${localBase}.cmd` : localBase;
1717
- try {
1718
- await fs.access(localPath);
1719
- if (await verifyToolBinary(localPath, undefined, undefined, getToolVerificationTimeout(tool), tool.checkArgs, packageEntryVerification(tool))) {
1720
- status.installed = true;
1721
- status.source = "dm-lens-auto";
1722
- status.path = localPath;
1723
- statuses.push(status);
1724
- continue;
1725
- }
1726
- } catch {}
1727
- if (tool.installStrategy === "npm") {
1728
- status.source = "npx-fallback";
1729
- }
1730
- statuses.push(status);
1731
- }
1732
- return statuses;
1733
- }
1734
- export async function isToolInstalled(toolId) {
1735
- return await getToolPath(toolId) !== undefined;
1736
- }
1737
- async function getArchiveTreeBundlePath(tool) {
1738
- if (tool.installStrategy !== "archive" || tool.archive?.launcher) {
1739
- return;
1740
- }
1741
- const extractDir = path.join(TOOLS_DIR, tool.id);
1742
- const marker = tool.archive?.treeMarker ? path.join(extractDir, ...tool.archive.treeMarker.split("/")) : extractDir;
1743
- try {
1744
- await fs.access(marker);
1745
- return extractDir;
1746
- } catch {
1747
- return;
1748
- }
1749
- }
1750
- export function resolvePlatformPackageBinary(tool) {
1751
- const spec = tool.platformPackage;
1752
- if (!spec || !tool.packageName)
1753
- return;
1754
- const suffix = spec.suffixes[`${installerPlatform()}-${process.arch}`];
1755
- if (!suffix)
1756
- return;
1757
- const platformPkg = `${spec.base ?? tool.packageName}-${suffix}`;
1758
- try {
1759
- const mainPkgJson = _installerRequire.resolve(`${tool.packageName}/package.json`);
1760
- const fromMain = createRequire(mainPkgJson);
1761
- let pkgDir;
1762
- try {
1763
- pkgDir = path.dirname(fromMain.resolve(`${platformPkg}/package.json`));
1764
- } catch {
1765
- pkgDir = path.dirname(_installerRequire.resolve(`${platformPkg}/package.json`));
1766
- }
1767
- const isWin = installerPlatform() === "win32";
1768
- for (const bin of spec.binaries) {
1769
- for (const name of isWin ? [`${bin}.exe`, bin] : [bin]) {
1770
- const candidate = path.join(pkgDir, name);
1771
- if (existsSync(candidate))
1772
- return candidate;
1773
- }
1774
- }
1775
- } catch {}
1776
- return;
1777
- }
1778
- const lastResolveTransient = new Map;
1779
- const lastInstallResolutionId = new Map;
1780
- export function wasLastResolveTransient(toolId) {
1781
- return lastResolveTransient.get(toolId) ?? false;
1782
- }
1783
- export async function getToolPath(toolId) {
1784
- let sawTransient = false;
1785
- const markTransient = () => {
1786
- sawTransient = true;
1787
- };
1788
- const result = await getToolPathResolved(toolId, markTransient);
1789
- lastResolveTransient.set(toolId, sawTransient);
1790
- return result;
1791
- }
1792
- async function getToolPathResolved(toolId, onTransient) {
1793
- const tool = TOOLS.find((t) => t.id === toolId);
1794
- if (!tool)
1795
- return;
1796
- if (tool.installStrategy === "archive" && !tool.archive?.launcher) {
1797
- return getArchiveTreeBundlePath(tool);
1798
- }
1799
- const pinnedVersion = tool.installStrategy === "npm" && tool.packageName ? parsePinnedVersion(tool.packageName) : undefined;
1800
- if (pinnedVersion)
1801
- lastManagedInstallVersion.delete(toolId);
1802
- const recordVersion = pinnedVersion ? (output) => {
1803
- const seen = extractVersionToken(output);
1804
- if (seen)
1805
- lastManagedInstallVersion.set(toolId, seen);
1806
- } : undefined;
1807
- const localBase = path.join(TOOLS_DIR, "node_modules", ".bin", tool.binaryName || tool.id);
1808
- if (installerPlatform() === "win32") {
1809
- const cmdPath = `${localBase}.cmd`;
1810
- try {
1811
- await fs.access(cmdPath);
1812
- if (await verifyToolBinary(cmdPath, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs, packageEntryVerification(tool))) {
1813
- return cmdPath;
1814
- }
1815
- logSessionStart(`auto-install verify: ${cmdPath} exists but is broken, will reinstall`);
1816
- } catch {}
1817
- const exePath = `${localBase}.exe`;
1818
- try {
1819
- await fs.access(exePath);
1820
- if (await verifyToolBinary(exePath, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs, packageEntryVerification(tool))) {
1821
- return exePath;
1822
- }
1823
- logSessionStart(`auto-install verify: ${exePath} exists but is broken, will reinstall`);
1824
- } catch {}
1825
- }
1826
- if (installerPlatform() !== "win32") {
1827
- try {
1828
- await fs.access(localBase);
1829
- if (await verifyToolBinary(localBase, recordVersion, onTransient, getToolVerificationTimeout(tool), tool.checkArgs, packageEntryVerification(tool))) {
1830
- return localBase;
1831
- }
1832
- logSessionStart(`auto-install verify: ${localBase} exists but is broken, will reinstall`);
1833
- } catch {}
1834
- }
1835
- if (tool.platformPackage) {
1836
- const platformBin = resolvePlatformPackageBinary(tool);
1837
- if (platformBin && await verifyToolBinary(platformBin, undefined, onTransient, getToolVerificationTimeout(tool), tool.checkArgs)) {
1838
- logSessionStart(`auto-install ${toolId}: resolved platform-package binary at ${platformBin}`);
1839
- return platformBin;
1840
- }
1841
- logSessionStart(`auto-install ${toolId}: platform-package binary not resolved (${process.platform}-${process.arch}, base=${tool.platformPackage.base ?? tool.packageName}) — falling back to PATH/managed install`);
1842
- }
1843
- if (tool.installStrategy === "github" || tool.installStrategy === "maven" || tool.installStrategy === "archive") {
1844
- const githubPath = await findGitHubToolPath(tool.binaryName || tool.id);
1845
- if (githubPath)
1846
- return githubPath;
1847
- }
1848
- if (await isCommandAvailable(tool.checkCommand)) {
1849
- if (packageEntryVerification(tool) !== undefined)
1850
- return tool.checkCommand;
1851
- let probeStalled = false;
1852
- const verified = await verifyToolBinary(tool.checkCommand, undefined, () => {
1853
- probeStalled = true;
1854
- onTransient();
1855
- }, getToolVerificationTimeout(tool), tool.checkArgs, undefined, () => {
1856
- probeStalled = true;
1857
- });
1858
- if (verified || probeStalled)
1859
- return tool.checkCommand;
1860
- recordDegradationOnce({
1861
- kind: "installer-path-candidate-unrunnable",
1862
- subject: toolId,
1863
- reason: `${tool.checkCommand} on PATH failed its own check (${tool.checkArgs.join(" ")}); ignoring PATH for this tool`
1864
- });
1865
- logSessionStart(`auto-install ${toolId}: PATH candidate ${tool.checkCommand} failed ${tool.checkArgs.join(" ")} — ignoring PATH, trying managed install`);
1866
- }
1867
- if (tool.installStrategy === "npm") {
1868
- const npmPath = await findNpmGlobalToolPath(tool.binaryName || tool.id, onTransient, tool.checkArgs, getToolVerificationTimeout(tool));
1869
- if (npmPath) {
1870
- return npmPath;
1871
- }
1872
- }
1873
- if (tool.installStrategy === "pip") {
1874
- const pipPath = await findPipUserToolPath(tool.binaryName || tool.id, onTransient, tool.checkArgs, getToolVerificationTimeout(tool));
1875
- if (pipPath) {
1876
- return pipPath;
1877
- }
1878
- }
1879
- return;
1880
- }
1881
- async function findGitHubToolPath(binaryName) {
1882
- const isWindows = process.platform === "win32";
1883
- const candidates = isWindows ? [
1884
- path.join(GITHUB_BIN_DIR, `${binaryName}.exe`),
1885
- path.join(GITHUB_BIN_DIR, `${binaryName}.bat`),
1886
- path.join(GITHUB_BIN_DIR, `${binaryName}.cmd`),
1887
- path.join(GITHUB_BIN_DIR, binaryName)
1888
- ] : [path.join(GITHUB_BIN_DIR, binaryName)];
1889
- for (const candidate of candidates) {
1890
- try {
1891
- await fs.access(candidate);
1892
- return candidate;
1893
- } catch {}
1894
- }
1895
- return;
1896
- }
1897
- export async function findManagedToolBinary(toolId) {
1898
- const tool = TOOLS.find((candidate) => candidate.id === toolId);
1899
- if (!tool)
1900
- return;
1901
- if (tool.installStrategy !== "github" && tool.installStrategy !== "maven" && tool.installStrategy !== "archive") {
1902
- return;
1903
- }
1904
- return findGitHubToolPath(tool.binaryName || tool.id);
1905
- }
1906
- function hasExecutableExtension(name) {
1907
- return /\.(exe|bat|cmd|ps1)$/i.test(name);
1908
- }
1909
- function getGitHubInstalledBinaryName(binaryName, platform, assetName) {
1910
- if (platform !== "win32")
1911
- return binaryName;
1912
- if (hasExecutableExtension(binaryName))
1913
- return binaryName;
1914
- if (assetName.endsWith(".bat"))
1915
- return `${binaryName}.bat`;
1916
- if (assetName.endsWith(".cmd"))
1917
- return `${binaryName}.cmd`;
1918
- return `${binaryName}.exe`;
1919
- }
1920
- function launcherForGitHubAsset(spec, assetName) {
1921
- if (spec.launcher === "php" && assetName.endsWith(".phar"))
1922
- return "php";
1923
- if (spec.launcher === "java" && assetName.endsWith(".jar"))
1924
- return "java";
1925
- return;
1926
- }
1927
- function getArchiveBinaryCandidates(binaryName, platform, assetName) {
1928
- if (platform !== "win32")
1929
- return [binaryName];
1930
- if (hasExecutableExtension(binaryName))
1931
- return [binaryName];
1932
- const candidates = new Set;
1933
- if (assetName.endsWith(".bat"))
1934
- candidates.add(`${binaryName}.bat`);
1935
- if (assetName.endsWith(".cmd"))
1936
- candidates.add(`${binaryName}.cmd`);
1937
- candidates.add(`${binaryName}.exe`);
1938
- candidates.add(binaryName);
1939
- candidates.add(`${binaryName}.bat`);
1940
- candidates.add(`${binaryName}.cmd`);
1941
- return [...candidates];
1942
- }
1943
- async function findNpmGlobalToolPath(binaryName, onTransient, verificationArgs = ["--version"], verificationTimeoutMs = 1e4) {
1944
- const isWindows = process.platform === "win32";
1945
- const binDirs = await getNpmGlobalBinCandidates(onTransient);
1946
- for (const dir of binDirs) {
1947
- const candidates = isWindows ? [
1948
- path.join(dir, `${binaryName}.cmd`),
1949
- path.join(dir, `${binaryName}.exe`)
1950
- ] : [path.join(dir, binaryName)];
1951
- for (const candidate of candidates) {
1952
- try {
1953
- await fs.access(candidate);
1954
- if (await verifyToolBinary(candidate, undefined, onTransient, verificationTimeoutMs, verificationArgs)) {
1955
- return candidate;
1956
- }
1957
- } catch {}
1958
- }
1959
- }
1960
- return;
1961
- }
1962
- async function getNpmGlobalBinCandidates(onTransient) {
1963
- const dirs = [];
1964
- const seen = new Set;
1965
- const add = (value) => {
1966
- if (!value)
1967
- return;
1968
- const normalized = path.resolve(value.trim());
1969
- if (!normalized)
1970
- return;
1971
- if (seen.has(normalized))
1972
- return;
1973
- seen.add(normalized);
1974
- dirs.push(normalized);
1975
- };
1976
- if (process.platform === "win32") {
1977
- add(path.join(process.env.APPDATA || "", "npm"));
1978
- } else {
1979
- add(path.join(os.homedir(), ".npm-global", "bin"));
1980
- }
1981
- for (const dir of await allAvailableGlobalBinDirs(onTransient)) {
1982
- add(dir);
1983
- }
1984
- return dirs;
1985
- }
1986
- async function findPipUserToolPath(binaryName, onTransient, verificationArgs = ["--version"], verificationTimeoutMs = 1e4) {
1987
- const isWindows = installerPlatform() === "win32";
1988
- const userBaseCandidates = [
1989
- path.join(getGlobalDmLensDir(), "pip-tools"),
1990
- path.join(getGlobalDmLensDir(), "pip-user"),
1991
- ...await getPythonUserBaseCandidates()
1992
- ];
1993
- for (const userBase of userBaseCandidates) {
1994
- const scriptDirs = [pipScriptsDir(userBase, installerPlatform())];
1995
- if (isWindows) {
1996
- try {
1997
- const children = await fs.readdir(userBase, { withFileTypes: true });
1998
- for (const entry of children) {
1999
- if (!entry.isDirectory())
2000
- continue;
2001
- if (!/^python\d+$/i.test(entry.name))
2002
- continue;
2003
- scriptDirs.push(path.join(userBase, entry.name, "Scripts"));
2004
- }
2005
- } catch {}
2006
- }
2007
- for (const dir of scriptDirs) {
2008
- const candidates = isWindows ? [
2009
- path.join(dir, `${binaryName}.exe`),
2010
- path.join(dir, `${binaryName}.cmd`),
2011
- path.join(dir, binaryName)
2012
- ] : [path.join(dir, binaryName)];
2013
- for (const candidate of candidates) {
2014
- try {
2015
- await fs.access(candidate);
2016
- if (await verifyToolBinary(candidate, undefined, onTransient, verificationTimeoutMs, verificationArgs)) {
2017
- return candidate;
2018
- }
2019
- } catch {}
2020
- }
2021
- }
2022
- }
2023
- return;
2024
- }
2025
- export function userBaseProbeResult(command, code, stdout) {
2026
- if (stdout.truncated) {
2027
- recordDegradationOnce({
2028
- kind: "spawn-output-cap-truncated",
2029
- subject: `user-base-probe:${command}`,
2030
- reason: `\`${command} -m site --user-base\` reached the ${DEFAULT_MAX_OUTPUT_BYTES}-byte default cap after ${stdout.observedBytes} bytes; the probed user base is unusable`,
2031
- metadata: {
2032
- capBytes: DEFAULT_MAX_OUTPUT_BYTES,
2033
- capSource: "default",
2034
- observedBytes: stdout.observedBytes,
2035
- killed: false
2036
- }
2037
- });
2038
- return "";
2039
- }
2040
- return code === 0 ? stdout.text.trim() : "";
2041
- }
2042
- async function getPythonUserBaseCandidates() {
2043
- const candidates = [];
2044
- const seen = new Set;
2045
- const add = (value) => {
2046
- if (!value)
2047
- return;
2048
- const normalized = value.trim();
2049
- if (!normalized)
2050
- return;
2051
- if (seen.has(normalized))
2052
- return;
2053
- seen.add(normalized);
2054
- candidates.push(normalized);
2055
- };
2056
- if (process.platform === "win32") {
2057
- add(path.join(process.env.APPDATA || "", "Python"));
2058
- }
2059
- const probes = process.platform === "win32" ? [
2060
- { command: "py", args: ["-m", "site", "--user-base"] },
2061
- { command: "python", args: ["-m", "site", "--user-base"] }
2062
- ] : [
2063
- { command: "python3", args: ["-m", "site", "--user-base"] },
2064
- { command: "python", args: ["-m", "site", "--user-base"] }
2065
- ];
2066
- for (const probe of probes) {
2067
- const userBase = await new Promise((resolve) => {
2068
- const isWin = process.platform === "win32";
2069
- const spawnCmd = isWin ? [probe.command, ...probe.args].join(" ") : probe.command;
2070
- let proc;
2071
- try {
2072
- proc = spawn(spawnCmd, isWin ? [] : probe.args, {
2073
- stdio: ["ignore", "pipe", "pipe"],
2074
- shell: isWin
2075
- });
2076
- } catch {
2077
- resolve("");
2078
- return;
2079
- }
2080
- const stdout = createBoundedOutputSink();
2081
- proc.stdout?.on("data", (data) => stdout.append(data));
2082
- proc.on("exit", (code) => resolve(userBaseProbeResult(probe.command, code, stdout)));
2083
- proc.on("error", () => resolve(""));
2084
- });
2085
- add(userBase);
2086
- }
2087
- return candidates;
2088
- }
2089
- async function githubApiAuthHeaders() {
2090
- const token = await resolveGitHubToken();
2091
- return token ? { Authorization: `Bearer ${token}` } : {};
2092
- }
2093
-
2094
- class HttpStatusError extends Error {
2095
- statusCode;
2096
- constructor(statusCode, url) {
2097
- super(url ? `HTTP ${statusCode} for ${url}` : `HTTP ${statusCode}`);
2098
- this.name = "HttpStatusError";
2099
- this.statusCode = statusCode;
2100
- }
2101
- }
2102
-
2103
- class GitHubHttpError extends HttpStatusError {
2104
- anonymousRateLimitExhausted;
2105
- constructor(statusCode, requestHeaders, responseHeaders) {
2106
- super(statusCode);
2107
- this.name = "GitHubHttpError";
2108
- this.message = `GitHub API HTTP ${statusCode}`;
2109
- this.anonymousRateLimitExhausted = statusCode === 403 && !Object.keys(requestHeaders).some((key) => key.toLowerCase() === "authorization") && readHeader(responseHeaders, "x-ratelimit-remaining") === "0";
2110
- }
2111
- }
2112
- function readHeader(headers, name) {
2113
- const entry = Object.entries(headers).find(([key]) => key.toLowerCase() === name.toLowerCase())?.[1];
2114
- return Array.isArray(entry) ? entry[0]?.trim() : entry?.trim();
2115
- }
2116
- function isGitHubApiUrl(url) {
2117
- try {
2118
- return new URL(url).host.toLowerCase() === "api.github.com";
2119
- } catch {
2120
- return false;
2121
- }
2122
- }
2123
- function sameHost(a, b) {
2124
- try {
2125
- return new URL(a).host === new URL(b).host;
2126
- } catch {
2127
- return false;
2128
- }
2129
- }
2130
- function httpsGetWithMeta(url, maxRedirects = 5, headers = {}) {
2131
- return new Promise((resolve, reject) => {
2132
- https.get(url, { headers: { "User-Agent": "dm-lens/1.0", ...headers } }, (res) => {
2133
- if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
2134
- if (maxRedirects === 0)
2135
- return reject(new Error("Too many redirects"));
2136
- const location = res.headers.location;
2137
- const nextHeaders = sameHost(url, location) ? headers : (() => {
2138
- const { Authorization: _drop, ...rest } = headers;
2139
- return rest;
2140
- })();
2141
- return resolve(httpsGetWithMeta(location, maxRedirects - 1, nextHeaders));
2142
- }
2143
- const etag = typeof res.headers.etag === "string" ? res.headers.etag : undefined;
2144
- if (res.statusCode === 304) {
2145
- res.resume();
2146
- return resolve({ statusCode: 304, body: Buffer.alloc(0), etag });
2147
- }
2148
- if (res.statusCode !== 200) {
2149
- res.resume();
2150
- return reject(isGitHubApiUrl(url) ? new GitHubHttpError(res.statusCode ?? 0, headers, res.headers) : new HttpStatusError(res.statusCode ?? 0, url));
2151
- }
2152
- const chunks = [];
2153
- res.on("data", (chunk) => chunks.push(chunk));
2154
- res.on("end", () => resolve({ statusCode: 200, body: Buffer.concat(chunks), etag }));
2155
- res.on("error", reject);
2156
- }).on("error", reject);
2157
- });
2158
- }
2159
- async function httpsGet(url, maxRedirects = 5, headers = {}) {
2160
- const response = await httpsGetWithMeta(url, maxRedirects, headers);
2161
- if (response.statusCode !== 200) {
2162
- throw new Error(`HTTP ${response.statusCode} for ${url}`);
2163
- }
2164
- return response.body;
2165
- }
2166
- function runCommand(command, args, cwd) {
2167
- return new Promise((resolve) => {
2168
- let proc;
2169
- try {
2170
- proc = spawn(command, args, {
2171
- cwd,
2172
- stdio: "ignore",
2173
- shell: process.platform === "win32"
2174
- });
2175
- } catch {
2176
- resolve(false);
2177
- return;
2178
- }
2179
- proc.on("exit", (code) => resolve(code === 0));
2180
- proc.on("error", () => resolve(false));
2181
- });
2182
- }
2183
- async function installGitHubTool(tool, prefetchedRelease) {
2184
- const spec = tool.github;
2185
- if (!spec)
2186
- return;
2187
- const platform = process.platform;
2188
- const arch = process.arch;
2189
- const assetSubstring = spec.assetMatch(platform, arch);
2190
- if (!assetSubstring) {
2191
- logSessionStart(`github-install ${tool.id}: unsupported platform=${platform} arch=${arch}`);
2192
- return;
2193
- }
2194
- let releaseJson;
2195
- if (prefetchedRelease) {
2196
- releaseJson = prefetchedRelease;
2197
- logSessionStart(`github-install ${tool.id}: using caller-supplied release metadata for ${spec.repo} (${prefetchedRelease.tag_name ?? "untagged"})`);
2198
- } else {
2199
- logSessionStart(`github-install ${tool.id}: fetching release metadata from ${spec.repo}`);
2200
- try {
2201
- const body = await httpsGet(`https://api.github.com/repos/${spec.repo}/releases/latest`, 5, await githubApiAuthHeaders());
2202
- releaseJson = JSON.parse(body.toString("utf8"));
2203
- } catch (err) {
2204
- const reason = err instanceof GitHubHttpError && err.anonymousRateLimitExhausted ? "GitHub API rate limit exhausted; authenticate with `gh auth login` or set GITHUB_TOKEN/GH_TOKEN and retry" : `release fetch failed: ${err.message}`;
2205
- if (err instanceof GitHubHttpError && err.anonymousRateLimitExhausted) {
2206
- recordDegradationOnce({
2207
- kind: "github-api-rate-limit",
2208
- subject: tool.id,
2209
- reason
2210
- });
2211
- }
2212
- logSessionStart(`github-install ${tool.id}: ${reason}`);
2213
- return;
2214
- }
2215
- }
2216
- if (releaseJson.tag_name) {
2217
- lastInstallResolutionId.set(tool.id, releaseJson.tag_name);
2218
- }
2219
- if (!Array.isArray(releaseJson.assets)) {
2220
- logSessionStart(`github-install ${tool.id}: release metadata has no assets array`);
2221
- return;
2222
- }
2223
- const asset = pickReleaseAsset(releaseJson.assets, assetSubstring) ?? deriveHashiCorpReleaseAsset(tool, releaseJson.tag_name, assetSubstring);
2224
- if (!asset) {
2225
- logSessionStart(`github-install ${tool.id}: no asset matched "${assetSubstring}"`);
2226
- return;
2227
- }
2228
- logSessionStart(`github-install ${tool.id}: downloading ${asset.name}`);
2229
- debugLog(`[github] downloading ${asset.name} from ${asset.browser_download_url}`);
2230
- const downloadStart = Date.now();
2231
- let assetBuffer;
2232
- try {
2233
- assetBuffer = await httpsGet(asset.browser_download_url);
2234
- logSessionStart(`github-install ${tool.id}: downloaded ${asset.name} (${assetBuffer.length} bytes, ${Date.now() - downloadStart}ms)`);
2235
- } catch (err) {
2236
- logSessionStart(`github-install ${tool.id}: download failed: ${err.message}`);
2237
- return;
2238
- }
2239
- await fs.mkdir(GITHUB_BIN_DIR, { recursive: true });
2240
- const binaryName = tool.binaryName ?? tool.id;
2241
- const isWindows = platform === "win32";
2242
- const finalBinaryName = getGitHubInstalledBinaryName(binaryName, platform, asset.name);
2243
- let destPath = path.join(GITHUB_BIN_DIR, finalBinaryName);
2244
- const assetName = asset.name;
2245
- const launcherRuntime = launcherForGitHubAsset(spec, assetName);
2246
- if (launcherRuntime && !await isCommandAvailable(launcherRuntime, ["--version"])) {
2247
- logSessionStart(`github-install ${tool.id}: ${launcherRuntime} not found — asset requires a runtime launcher`);
2248
- return;
2249
- }
2250
- try {
2251
- if (launcherRuntime) {
2252
- const assetPath = path.join(GITHUB_BIN_DIR, `${tool.id}${assetName.endsWith(".phar") ? ".phar" : ".jar"}`);
2253
- await writeFileAtomicAsync(assetPath, assetBuffer, {
2254
- bestEffort: false,
2255
- mode: 488
2256
- });
2257
- const launcherName = isWindows ? `${binaryName}.bat` : binaryName;
2258
- destPath = path.join(GITHUB_BIN_DIR, launcherName);
2259
- const runtimeTarget = assetName.endsWith(".phar") ? `${tool.id}.phar` : `${tool.id}.jar`;
2260
- const command = isWindows ? `@echo off\r
2261
- ${launcherRuntime} "%~dp0${runtimeTarget}" %*\r
2262
- ` : `#!/bin/sh
2263
- exec ${launcherRuntime} "$(dirname "$0")/${runtimeTarget}" "$@"
2264
- `;
2265
- await writeFileAtomicAsync(destPath, command, {
2266
- bestEffort: false,
2267
- mode: isWindows ? undefined : 488
2268
- });
2269
- } else if (assetName.endsWith(".gz") && !assetName.endsWith(".tar.gz")) {
2270
- const decompressed = await new Promise((resolve, reject) => {
2271
- const gunzip = createGunzip();
2272
- const chunks = [];
2273
- gunzip.on("data", (chunk) => chunks.push(chunk));
2274
- gunzip.on("end", () => resolve(Buffer.concat(chunks)));
2275
- gunzip.on("error", reject);
2276
- gunzip.end(assetBuffer);
2277
- });
2278
- await writeFileAtomicAsync(destPath, decompressed, {
2279
- bestEffort: false,
2280
- mode: 488
2281
- });
2282
- } else if (assetName.endsWith(".tar.gz") || assetName.endsWith(".tar.xz")) {
2283
- const tmpArchive = path.join(GITHUB_BIN_DIR, `_tmp_${assetName}`);
2284
- await fs.writeFile(tmpArchive, assetBuffer);
2285
- const tmpDir = path.join(GITHUB_BIN_DIR, `_tmp_extract_${tool.id}`);
2286
- await fs.mkdir(tmpDir, { recursive: true });
2287
- const extracted = await runCommand("tar", ["xf", tmpArchive, "-C", tmpDir], GITHUB_BIN_DIR);
2288
- await fs.rm(tmpArchive, { force: true });
2289
- if (!extracted) {
2290
- await fs.rm(tmpDir, { recursive: true, force: true });
2291
- logSessionStart(`github-install ${tool.id}: tar extraction failed for ${assetName}`);
2292
- return;
2293
- }
2294
- const tarBinaryName = spec.binaryInArchive ?? binaryName;
2295
- const tarSrcBinary = await findFirstFileRecursive(tmpDir, getArchiveBinaryCandidates(tarBinaryName, platform, assetName));
2296
- if (!tarSrcBinary) {
2297
- await fs.rm(tmpDir, { recursive: true, force: true });
2298
- logSessionStart(`github-install ${tool.id}: binary candidates ${JSON.stringify(getArchiveBinaryCandidates(tarBinaryName, platform, assetName))} not found in tar ${assetName}`);
2299
- return;
2300
- }
2301
- await fs.rename(tarSrcBinary, destPath);
2302
- await fs.rm(tmpDir, { recursive: true, force: true });
2303
- if (!isWindows)
2304
- await fs.chmod(destPath, 488);
2305
- } else if (assetName.endsWith(".zip")) {
2306
- const tmpArchive = path.join(GITHUB_BIN_DIR, `_tmp_${assetName}`);
2307
- await fs.writeFile(tmpArchive, assetBuffer);
2308
- const tmpDir = path.join(GITHUB_BIN_DIR, `_tmp_extract_${tool.id}`);
2309
- await fs.mkdir(tmpDir, { recursive: true });
2310
- const extracted = isWindows ? await runCommand("powershell", [
2311
- "-NoProfile",
2312
- "-Command",
2313
- `Expand-Archive -LiteralPath '${tmpArchive}' -DestinationPath '${tmpDir}' -Force`
2314
- ], GITHUB_BIN_DIR) : await runCommand("unzip", ["-q", "-o", tmpArchive, "-d", tmpDir], GITHUB_BIN_DIR);
2315
- await fs.rm(tmpArchive, { force: true });
2316
- if (!extracted) {
2317
- await fs.rm(tmpDir, { recursive: true, force: true });
2318
- logSessionStart(`github-install ${tool.id}: zip extraction failed for ${assetName}`);
2319
- return;
2320
- }
2321
- const archiveBinaryName = spec.binaryInArchive ?? binaryName;
2322
- const srcBinary = await findFirstFileRecursive(tmpDir, getArchiveBinaryCandidates(archiveBinaryName, platform, assetName));
2323
- if (!srcBinary) {
2324
- await fs.rm(tmpDir, { recursive: true, force: true });
2325
- logSessionStart(`github-install ${tool.id}: binary candidates ${JSON.stringify(getArchiveBinaryCandidates(archiveBinaryName, platform, assetName))} not found in zip ${assetName}`);
2326
- return;
2327
- }
2328
- await fs.rename(srcBinary, destPath);
2329
- await fs.rm(tmpDir, { recursive: true, force: true });
2330
- if (!isWindows)
2331
- await fs.chmod(destPath, 488);
2332
- } else {
2333
- await writeFileAtomicAsync(destPath, assetBuffer, {
2334
- bestEffort: false,
2335
- mode: 488
2336
- });
2337
- }
2338
- } catch (err) {
2339
- logSessionStart(`github-install ${tool.id}: install failed: ${err.message}`);
2340
- return;
2341
- }
2342
- for (const extraName of spec.extraAssets?.(platform, arch) ?? []) {
2343
- const extraAsset = releaseJson.assets.find((a) => a.name === extraName);
2344
- if (!extraAsset) {
2345
- logSessionStart(`github-install ${tool.id}: required extra asset "${extraName}" not found`);
2346
- return;
2347
- }
2348
- try {
2349
- const extraBuffer = await httpsGet(extraAsset.browser_download_url);
2350
- await writeFileAtomicAsync(path.join(GITHUB_BIN_DIR, extraName), extraBuffer, { bestEffort: false, mode: 488 });
2351
- logSessionStart(`github-install ${tool.id}: installed extra asset ${extraName} (${extraBuffer.length} bytes)`);
2352
- } catch (err) {
2353
- logSessionStart(`github-install ${tool.id}: extra asset ${extraName} download failed: ${err.message}`);
2354
- return;
2355
- }
2356
- }
2357
- debugLog(`[github] installed ${tool.name} → ${destPath}`);
2358
- logSessionStart(`github-install ${tool.id}: installed → ${destPath}`);
2359
- return destPath;
2360
- }
2361
- async function findFirstFileRecursive(dir, names) {
2362
- const wanted = new Set(names.map((name) => name.toLowerCase()));
2363
- const entries = await fs.readdir(dir, { withFileTypes: true });
2364
- for (const entry of entries) {
2365
- const full = path.join(dir, entry.name);
2366
- if (entry.isDirectory()) {
2367
- const found = await findFirstFileRecursive(full, names);
2368
- if (found)
2369
- return found;
2370
- } else if (wanted.has(entry.name.toLowerCase())) {
2371
- return full;
2372
- }
2373
- }
2374
- return;
2375
- }
2376
- const NEEDS_POSTINSTALL = new Set([
2377
- "@biomejs/biome",
2378
- "@ast-grep/cli",
2379
- "@ast-grep/napi",
2380
- "esbuild",
2381
- "intelephense"
2382
- ]);
2383
- export function npmToolNeedsPostinstall(packageName) {
2384
- return NEEDS_POSTINSTALL.has(packageName);
2385
- }
2386
- export function getRefreshableManagedNpmTools() {
2387
- const seenPackages = new Set;
2388
- return getRefreshableManagedTools().filter((candidate) => candidate.strategy === "npm").map((candidate) => ({
2389
- toolId: candidate.toolId,
2390
- packageName: candidate.packageName,
2391
- binaryName: candidate.binaryName
2392
- })).filter((candidate) => {
2393
- if (seenPackages.has(candidate.packageName))
2394
- return false;
2395
- seenPackages.add(candidate.packageName);
2396
- return true;
2397
- });
2398
- }
2399
- function mavenCoordinate(spec) {
2400
- return [
2401
- spec.repoBaseUrl ?? MAVEN_CENTRAL_BASE,
2402
- spec.groupId,
2403
- spec.artifactId,
2404
- spec.version,
2405
- spec.classifier ?? ""
2406
- ].join(":");
2407
- }
2408
- export function getRefreshableManagedTools() {
2409
- const refreshable = [];
2410
- for (const tool of TOOLS) {
2411
- switch (tool.installStrategy) {
2412
- case "npm": {
2413
- if (!tool.packageName)
2414
- continue;
2415
- if (parsePinnedVersion(tool.packageName) !== undefined)
2416
- continue;
2417
- if (!tool.binaryName)
2418
- continue;
2419
- refreshable.push({
2420
- toolId: tool.id,
2421
- strategy: "npm",
2422
- checkArgs: tool.checkArgs,
2423
- packageName: tool.packageName,
2424
- binaryName: tool.binaryName,
2425
- verificationTimeoutMs: tool.verificationTimeoutMs,
2426
- packageEntryOf: packageEntryVerification(tool)
2427
- });
2428
- break;
2429
- }
2430
- case "pip":
2431
- case "gem": {
2432
- if (!tool.packageName)
2433
- continue;
2434
- refreshable.push({
2435
- toolId: tool.id,
2436
- strategy: tool.installStrategy,
2437
- checkArgs: tool.checkArgs,
2438
- packageName: tool.packageName
2439
- });
2440
- break;
2441
- }
2442
- case "github": {
2443
- if (!tool.github)
2444
- continue;
2445
- refreshable.push({
2446
- toolId: tool.id,
2447
- strategy: "github",
2448
- checkArgs: tool.checkArgs
2449
- });
2450
- break;
2451
- }
2452
- case "maven": {
2453
- if (!tool.maven)
2454
- continue;
2455
- refreshable.push({
2456
- toolId: tool.id,
2457
- strategy: "maven",
2458
- checkArgs: tool.checkArgs,
2459
- pinnedCoordinate: mavenCoordinate(tool.maven)
2460
- });
2461
- break;
2462
- }
2463
- case "archive": {
2464
- if (!tool.archive)
2465
- continue;
2466
- const url = resolveArchiveUrl(tool.archive);
2467
- if (!url)
2468
- continue;
2469
- refreshable.push({
2470
- toolId: tool.id,
2471
- strategy: "archive",
2472
- checkArgs: tool.checkArgs,
2473
- pinnedCoordinate: url
2474
- });
2475
- break;
2476
- }
2477
- }
2478
- }
2479
- return refreshable;
2480
- }
2481
- export async function isManagedToolPresent(toolId) {
2482
- const tool = TOOLS.find((t) => t.id === toolId);
2483
- if (!tool)
2484
- return false;
2485
- switch (tool.installStrategy) {
2486
- case "npm": {
2487
- if (!tool.packageName)
2488
- return false;
2489
- try {
2490
- await fs.access(path.join(TOOLS_DIR, "node_modules", tool.packageName, "package.json"));
2491
- return true;
2492
- } catch {
2493
- return false;
2494
- }
2495
- }
2496
- case "github":
2497
- case "maven":
2498
- return await findGitHubToolPath(tool.binaryName ?? tool.id) !== undefined;
2499
- case "archive":
2500
- return await findGitHubToolPath(tool.binaryName ?? tool.id) !== undefined || await getArchiveTreeBundlePath(tool) !== undefined;
2501
- case "pip":
2502
- case "gem": {
2503
- const cached = (await readProbeCache())[toolId];
2504
- return cached?.path !== undefined && existsSync(cached.path);
2505
- }
2506
- default:
2507
- return false;
2508
- }
2509
- }
2510
- async function probeManagedToolVersion(tool) {
2511
- const cached = (await readProbeCache())[tool.id];
2512
- if (!cached?.path || !existsSync(cached.path))
2513
- return;
2514
- try {
2515
- const result = await probeToolAsync(cached.path, tool.checkArgs, {
2516
- timeout: getToolVerificationTimeout(tool),
2517
- input: "",
2518
- ignoreAmbientSignal: true,
2519
- resourceLabel: `tool-refresh-version:${tool.id}`
2520
- });
2521
- if (result.status !== 0)
2522
- return;
2523
- return extractVersionToken(`${result.stdout}
2524
- ${result.stderr}`);
2525
- } catch {
2526
- return;
2527
- }
2528
- }
2529
- export async function acquireManagedInstallGate(context) {
2530
- if (process.env.DM_LENS_DISABLE_TOOL_INSTALL === "1") {
2531
- return {
2532
- ok: false,
2533
- reason: "installation disabled by DM_LENS_DISABLE_TOOL_INSTALL=1"
2534
- };
2535
- }
2536
- if (!assertInstallAllowed(context)) {
2537
- return {
2538
- ok: false,
2539
- reason: `project trust: ${projectTrustDenialReason()}`
2540
- };
2541
- }
2542
- const lock = await acquireInstallLock();
2543
- if (!lock.release) {
2544
- return { ok: false, reason: lock.reason ?? "install lock held" };
2545
- }
2546
- return { ok: true, release: lock.release };
2547
- }
2548
- export async function refreshManagedTool(toolId, known = {}) {
2549
- const tool = TOOLS.find((t) => t.id === toolId);
2550
- if (!tool) {
2551
- return { ok: false, unchanged: true, reason: "unknown tool id" };
2552
- }
2553
- if (tool.installStrategy !== "github" && tool.installStrategy !== "pip" && tool.installStrategy !== "gem" && tool.installStrategy !== "maven" && tool.installStrategy !== "archive") {
2554
- return {
2555
- ok: false,
2556
- unchanged: true,
2557
- reason: `strategy ${tool.installStrategy} is not refreshable here`
2558
- };
2559
- }
2560
- const strategy = tool.installStrategy;
2561
- const gate = await acquireManagedInstallGate(`managed tool refresh: ${toolId}`);
2562
- if (!gate.ok) {
2563
- return { ok: false, unchanged: true, declined: true, reason: gate.reason };
2564
- }
2565
- try {
2566
- switch (strategy) {
2567
- case "github":
2568
- return await refreshGitHubManagedTool(tool, known);
2569
- case "pip":
2570
- case "gem":
2571
- return await refreshPackageManagerManagedTool(tool);
2572
- case "maven":
2573
- case "archive":
2574
- return await refreshPinnedManagedTool(tool, known);
2575
- default:
2576
- return {
2577
- ok: false,
2578
- unchanged: true,
2579
- reason: `strategy ${strategy} is not refreshable here`
2580
- };
2581
- }
2582
- } finally {
2583
- await gate.release?.();
2584
- }
2585
- }
2586
- async function refreshGitHubManagedTool(tool, known) {
2587
- const spec = tool.github;
2588
- if (!spec)
2589
- return { ok: false, unchanged: true, reason: "no github spec" };
2590
- let response;
2591
- try {
2592
- response = await httpsGetWithMeta(`https://api.github.com/repos/${spec.repo}/releases/latest`, 5, {
2593
- ...await githubApiAuthHeaders(),
2594
- ...known.etag ? { "If-None-Match": known.etag } : {}
2595
- });
2596
- } catch (err) {
2597
- const reason = err instanceof GitHubHttpError && err.anonymousRateLimitExhausted ? "GitHub API rate limit exhausted; authenticate with `gh auth login` or set GITHUB_TOKEN/GH_TOKEN and retry" : `release query failed: ${err.message}`;
2598
- if (err instanceof GitHubHttpError && err.anonymousRateLimitExhausted) {
2599
- recordDegradationOnce({
2600
- kind: "github-api-rate-limit",
2601
- subject: tool.id,
2602
- reason
2603
- });
2604
- }
2605
- return {
2606
- ok: false,
2607
- unchanged: true,
2608
- reason
2609
- };
2610
- }
2611
- if (response.statusCode === 304) {
2612
- return {
2613
- ok: true,
2614
- unchanged: true,
2615
- resolutionId: known.resolutionId,
2616
- etag: known.etag,
2617
- version: known.version
2618
- };
2619
- }
2620
- let release;
2621
- try {
2622
- const parsed = JSON.parse(response.body.toString("utf8"));
2623
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
2624
- throw new Error("release metadata is not an object");
2625
- }
2626
- release = parsed;
2627
- } catch (err) {
2628
- return {
2629
- ok: false,
2630
- unchanged: true,
2631
- reason: `release metadata unparseable: ${err.message}`
2632
- };
2633
- }
2634
- const tag = release.tag_name;
2635
- if (!tag) {
2636
- return { ok: false, unchanged: true, reason: "release has no tag_name" };
2637
- }
2638
- if (known.resolutionId === tag) {
2639
- return {
2640
- ok: true,
2641
- unchanged: true,
2642
- resolutionId: tag,
2643
- etag: response.etag ?? known.etag,
2644
- version: known.version ?? tag
2645
- };
2646
- }
2647
- const installed = await installGitHubTool(tool, release);
2648
- if (!installed) {
2649
- return {
2650
- ok: false,
2651
- unchanged: true,
2652
- resolutionId: known.resolutionId,
2653
- version: known.version,
2654
- reason: `install from release ${tag} failed`
2655
- };
2656
- }
2657
- const verified = await verifyRefreshedArtifact(tool, installed);
2658
- if (!verified) {
2659
- return {
2660
- ok: false,
2661
- unchanged: false,
2662
- resolutionId: known.resolutionId,
2663
- version: known.version,
2664
- reason: `release ${tag} installed but its binary does not run`
2665
- };
2666
- }
2667
- return {
2668
- ok: true,
2669
- unchanged: false,
2670
- resolutionId: tag,
2671
- etag: response.etag,
2672
- version: tag
2673
- };
2674
- }
2675
- async function verifyRefreshedArtifact(tool, installedPath) {
2676
- if (tool.installStrategy === "archive" && !tool.archive?.launcher) {
2677
- await updateProbeCache(tool.id, installedPath);
2678
- return true;
2679
- }
2680
- if (!await verifyToolBinary(installedPath, undefined, undefined, getToolVerificationTimeout(tool), tool.checkArgs)) {
2681
- logSessionStart(`managed-tool-refresh ${tool.id}: refreshed artifact at ${installedPath} failed its verification check`);
2682
- return false;
2683
- }
2684
- await updateProbeCache(tool.id, installedPath);
2685
- return true;
2686
- }
2687
- async function refreshPackageManagerManagedTool(tool) {
2688
- if (!tool.packageName) {
2689
- return { ok: false, unchanged: true, reason: "no package name" };
2690
- }
2691
- const previous = await probeManagedToolVersion(tool);
2692
- const installed = tool.installStrategy === "pip" ? await installPipTool(tool.id, tool.packageName, tool.binaryName ?? tool.id, {
2693
- upgrade: true
2694
- }) : await installGemTool(tool.id, tool.packageName);
2695
- if (!installed) {
2696
- return {
2697
- ok: false,
2698
- unchanged: true,
2699
- version: previous,
2700
- reason: `${tool.installStrategy} upgrade failed`
2701
- };
2702
- }
2703
- const current = await probeManagedToolVersion(tool);
2704
- if (previous !== undefined && current === undefined) {
2705
- return {
2706
- ok: false,
2707
- unchanged: false,
2708
- version: previous,
2709
- reason: `${tool.installStrategy} upgrade left the binary unable to report a version`
2710
- };
2711
- }
2712
- return {
2713
- ok: true,
2714
- unchanged: previous === undefined || current === undefined || previous === current,
2715
- version: current ?? previous
2716
- };
2717
- }
2718
- async function refreshPinnedManagedTool(tool, known) {
2719
- const coordinate = tool.installStrategy === "maven" ? tool.maven && mavenCoordinate(tool.maven) : tool.archive && resolveArchiveUrl(tool.archive);
2720
- if (!coordinate) {
2721
- return {
2722
- ok: false,
2723
- unchanged: true,
2724
- reason: "no coordinate for this platform"
2725
- };
2726
- }
2727
- if (known.resolutionId === coordinate) {
2728
- return {
2729
- ok: true,
2730
- unchanged: true,
2731
- resolutionId: coordinate,
2732
- version: known.version
2733
- };
2734
- }
2735
- const installed = tool.installStrategy === "maven" ? await installMavenTool(tool) : await installArchiveTool(tool);
2736
- if (!installed) {
2737
- return {
2738
- ok: false,
2739
- unchanged: true,
2740
- resolutionId: known.resolutionId,
2741
- version: known.version,
2742
- reason: `reinstall from ${coordinate} failed`
2743
- };
2744
- }
2745
- if (!await verifyRefreshedArtifact(tool, installed)) {
2746
- return {
2747
- ok: false,
2748
- unchanged: false,
2749
- resolutionId: known.resolutionId,
2750
- version: known.version,
2751
- reason: `reinstall from ${coordinate} produced an artifact that does not run`
2752
- };
2753
- }
2754
- return {
2755
- ok: true,
2756
- unchanged: known.resolutionId === undefined,
2757
- resolutionId: coordinate,
2758
- version: coordinate
2759
- };
2760
- }
2761
- export function resolveManagedNpmBinPath(binaryName) {
2762
- const binBase = path.join(TOOLS_DIR, "node_modules", ".bin", binaryName);
2763
- return installerPlatform() === "win32" ? `${binBase}.cmd` : binBase;
2764
- }
2765
- export function invalidateManagedToolResolution(toolId) {
2766
- resolvedPathCache.delete(toolId);
2767
- if (_probeCache !== null)
2768
- delete _probeCache[toolId];
2769
- markProbeCacheChange(toolId, null);
2770
- }
2771
- const MAVEN_CENTRAL_BASE = "https://repo1.maven.org/maven2";
2772
- async function installMavenTool(tool) {
2773
- const spec = tool.maven;
2774
- if (!spec)
2775
- return;
2776
- const binaryName = tool.binaryName ?? tool.id;
2777
- const isWindows = process.platform === "win32";
2778
- if (!await isCommandAvailable("java", ["-version"])) {
2779
- logSessionStart(`maven-install ${tool.id}: java not found — a JAR tool can't run without a JRE`);
2780
- return;
2781
- }
2782
- let base = spec.repoBaseUrl ?? MAVEN_CENTRAL_BASE;
2783
- while (base.endsWith("/"))
2784
- base = base.slice(0, -1);
2785
- const groupPath = spec.groupId.replace(/\./g, "/");
2786
- const jarFile = `${spec.artifactId}-${spec.version}${spec.classifier ? `-${spec.classifier}` : ""}.jar`;
2787
- const url = `${base}/${groupPath}/${spec.artifactId}/${spec.version}/${jarFile}`;
2788
- lastInstallResolutionId.set(tool.id, mavenCoordinate(spec));
2789
- logSessionStart(`maven-install ${tool.id}: downloading ${url}`);
2790
- let jarBuffer;
2791
- try {
2792
- jarBuffer = await httpsGet(url);
2793
- } catch (err) {
2794
- logSessionStart(`maven-install ${tool.id}: download failed: ${err.message}`);
2795
- return;
2796
- }
2797
- try {
2798
- await fs.mkdir(GITHUB_BIN_DIR, { recursive: true });
2799
- const jarPath = path.join(GITHUB_BIN_DIR, `${tool.id}.jar`);
2800
- await writeFileAtomicAsync(jarPath, jarBuffer, { bestEffort: false });
2801
- const launcherName = isWindows ? `${binaryName}.bat` : binaryName;
2802
- const launcherPath = path.join(GITHUB_BIN_DIR, launcherName);
2803
- if (isWindows) {
2804
- await writeFileAtomicAsync(launcherPath, `@echo off\r
2805
- java -jar "%~dp0${tool.id}.jar" %*\r
2806
- `, { bestEffort: false });
2807
- } else {
2808
- await writeFileAtomicAsync(launcherPath, `#!/bin/sh
2809
- exec java -jar "$(dirname "$0")/${tool.id}.jar" "$@"
2810
- `, { bestEffort: false, mode: 488 });
2811
- }
2812
- logSessionStart(`maven-install ${tool.id}: installed → ${launcherPath} (${jarBuffer.length} bytes)`);
2813
- debugLog(`[maven] installed ${tool.name} → ${launcherPath}`);
2814
- return launcherPath;
2815
- } catch (err) {
2816
- logSessionStart(`maven-install ${tool.id}: install failed: ${err.message}`);
2817
- return;
2818
- }
2819
- }
2820
- export function resolveArchiveUrl(spec, platform = installerPlatform(), arch = process.arch) {
2821
- return typeof spec.url === "function" ? spec.url(platform, arch) : spec.url;
2822
- }
2823
- export function resolveArchiveKind(spec, platform = installerPlatform(), arch = process.arch) {
2824
- return typeof spec.kind === "function" ? spec.kind(platform, arch) : spec.kind;
2825
- }
2826
- function recordArchiveExtractionDegradation(toolId, format, reason) {
2827
- recordDegradationOnce({
2828
- kind: "managed-tool-install",
2829
- subject: `${toolId}:${format}`,
2830
- reason: `archive extraction ${reason}`
2831
- });
2832
- }
2833
- async function stripExtractedArchiveRoot(dir, components) {
2834
- for (let i = 0;i < components; i++) {
2835
- const entries = await fs.readdir(dir, { withFileTypes: true });
2836
- const [entry] = entries;
2837
- if (entries.length !== 1 || !entry?.isDirectory())
2838
- return false;
2839
- const root = path.join(dir, entry.name);
2840
- for (const child of await fs.readdir(root))
2841
- await fs.rename(path.join(root, child), path.join(dir, child));
2842
- await fs.rm(root, { recursive: true, force: true });
2843
- }
2844
- return true;
2845
- }
2846
- export async function swapExtractedDir(toolId, tmpDir, finalDir) {
2847
- const backupDir = `${finalDir}.rollback`;
2848
- await fs.rm(backupDir, { recursive: true, force: true });
2849
- let hadPrevious = false;
2850
- try {
2851
- await fs.rename(finalDir, backupDir);
2852
- hadPrevious = true;
2853
- } catch (err) {
2854
- if (err.code !== "ENOENT")
2855
- throw err;
2856
- }
2857
- try {
2858
- await fs.rename(tmpDir, finalDir);
2859
- } catch (err) {
2860
- if (hadPrevious) {
2861
- try {
2862
- await fs.rename(backupDir, finalDir);
2863
- } catch (rollbackErr) {
2864
- recordDegradationOnce({
2865
- kind: "managed-tool-refresh",
2866
- subject: toolId,
2867
- reason: `swap rollback failed after a failed install: working copy orphaned at ${backupDir} (${rollbackErr.message})`
2868
- });
2869
- logSessionStart(`archive-install ${toolId}: swap rollback failed — working copy orphaned at ${backupDir} (${rollbackErr.message})`);
2870
- }
2871
- }
2872
- throw err;
2873
- }
2874
- if (hadPrevious) {
2875
- await fs.rm(backupDir, { recursive: true, force: true }).catch(() => {});
2876
- }
2877
- }
2878
- async function installArchiveTool(tool) {
2879
- const spec = tool.archive;
2880
- if (!spec)
2881
- return;
2882
- const binaryName = tool.binaryName ?? tool.id;
2883
- const platform = installerPlatform();
2884
- const isWindows = platform === "win32";
2885
- const archiveKind = resolveArchiveKind(spec, platform, process.arch);
2886
- const url = resolveArchiveUrl(spec, platform, process.arch);
2887
- if (!url) {
2888
- logSessionStart(`archive-install ${tool.id}: no archive for ${process.platform}/${process.arch} — unsupported, skipping`);
2889
- return;
2890
- }
2891
- lastInstallResolutionId.set(tool.id, url);
2892
- logSessionStart(`archive-install ${tool.id}: downloading ${url}`);
2893
- let archiveBuffer;
2894
- try {
2895
- archiveBuffer = await httpsGet(url);
2896
- } catch (err) {
2897
- logSessionStart(`archive-install ${tool.id}: download failed: ${err.message}`);
2898
- return;
2899
- }
2900
- const extractName = tool.id;
2901
- const tmpExtractName = `${extractName}.refresh-tmp`;
2902
- const archiveName = `${tool.id}.download.${archiveKind === "zip" ? "zip" : "tgz"}`;
2903
- const extractDir = path.join(TOOLS_DIR, extractName);
2904
- const tmpExtractDir = path.join(TOOLS_DIR, tmpExtractName);
2905
- const tmpArchive = path.join(TOOLS_DIR, archiveName);
2906
- try {
2907
- await fs.mkdir(TOOLS_DIR, { recursive: true });
2908
- await fs.rm(tmpExtractDir, { recursive: true, force: true });
2909
- await fs.mkdir(tmpExtractDir, { recursive: true });
2910
- await fs.writeFile(tmpArchive, archiveBuffer);
2911
- const stripComponents = spec.stripComponents ?? 1;
2912
- const extractionArgs = archiveKind === "zip" && isWindows ? [
2913
- "-NoProfile",
2914
- "-Command",
2915
- `Expand-Archive -LiteralPath '${archiveName}' -DestinationPath '${tmpExtractName}' -Force`
2916
- ] : archiveKind === "zip" ? ["-q", "-o", archiveName, "-d", tmpExtractName] : [
2917
- "-xzf",
2918
- archiveName,
2919
- "-C",
2920
- tmpExtractName,
2921
- ...stripComponents > 0 ? [`--strip-components=${stripComponents}`] : []
2922
- ];
2923
- const extractor = archiveKind === "zip" && isWindows ? `${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe` : archiveKind === "zip" ? "unzip" : isWindows ? `${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\tar.exe` : "tar";
2924
- const extractionResult = await safeSpawnAsync(extractor, extractionArgs, {
2925
- cwd: TOOLS_DIR,
2926
- timeout: 120000,
2927
- ignoreAmbientSignal: true,
2928
- lifetimeCoupled: true
2929
- });
2930
- const extracted = {
2931
- ok: extractionResult.status === 0,
2932
- reason: extractionResult.spawnFailure?.kind === "tool-not-found" ? "extractor-unavailable" : extractionResult.spawnFailure ? "extractor-error" : "extractor-exit"
2933
- };
2934
- await fs.rm(tmpArchive, { force: true });
2935
- if (!extracted.ok) {
2936
- recordArchiveExtractionDegradation(tool.id, archiveKind, extracted.reason);
2937
- logSessionStart(`archive-install ${tool.id}: ${archiveKind} extraction failed (${extracted.reason}) — keeping installed version`);
2938
- await fs.rm(tmpExtractDir, { recursive: true, force: true }).catch(() => {});
2939
- return;
2940
- }
2941
- if (archiveKind === "zip" && !await stripExtractedArchiveRoot(tmpExtractDir, stripComponents)) {
2942
- recordArchiveExtractionDegradation(tool.id, archiveKind, "layout-invalid");
2943
- logSessionStart(`archive-install ${tool.id}: ${archiveKind} layout invalid — keeping installed version`);
2944
- await fs.rm(tmpExtractDir, { recursive: true, force: true }).catch(() => {});
2945
- return;
2946
- }
2947
- if (!spec.launcher) {
2948
- const tmpMarker = spec.treeMarker ? path.join(tmpExtractDir, ...spec.treeMarker.split("/")) : tmpExtractDir;
2949
- try {
2950
- await fs.access(tmpMarker);
2951
- } catch {
2952
- recordArchiveExtractionDegradation(tool.id, archiveKind, "marker-missing");
2953
- logSessionStart(`archive-install ${tool.id}: ${archiveKind} marker missing after extraction — keeping installed version`);
2954
- await fs.rm(tmpExtractDir, { recursive: true, force: true }).catch(() => {});
2955
- return;
2956
- }
2957
- await swapExtractedDir(tool.id, tmpExtractDir, extractDir);
2958
- logSessionStart(`archive-install ${tool.id}: installed tree bundle → ${extractDir} (extracted ${archiveBuffer.length} bytes)`);
2959
- debugLog(`[archive] installed ${tool.name} bundle → ${extractDir}`);
2960
- return extractDir;
2961
- }
2962
- const launcherParts = spec.launcher.split("/").map((p) => p);
2963
- const tmpInnerLauncher = path.join(tmpExtractDir, ...launcherParts);
2964
- const tmpResolvedInner = isWindows ? `${tmpInnerLauncher}.bat` : tmpInnerLauncher;
2965
- try {
2966
- await fs.access(tmpResolvedInner);
2967
- } catch {
2968
- recordArchiveExtractionDegradation(tool.id, archiveKind, "launcher-missing");
2969
- logSessionStart(`archive-install ${tool.id}: ${archiveKind} launcher missing after extraction — keeping installed version`);
2970
- await fs.rm(tmpExtractDir, { recursive: true, force: true }).catch(() => {});
2971
- return;
2972
- }
2973
- if (!isWindows)
2974
- await fs.chmod(tmpResolvedInner, 488).catch(() => {});
2975
- await swapExtractedDir(tool.id, tmpExtractDir, extractDir);
2976
- const innerLauncher = path.join(extractDir, ...launcherParts);
2977
- const resolvedInner = isWindows ? `${innerLauncher}.bat` : innerLauncher;
2978
- await fs.mkdir(GITHUB_BIN_DIR, { recursive: true });
2979
- const launcherName = isWindows ? `${binaryName}.bat` : binaryName;
2980
- const shimPath = path.join(GITHUB_BIN_DIR, launcherName);
2981
- if (isWindows) {
2982
- await writeFileAtomicAsync(shimPath, `@echo off\r
2983
- call "${resolvedInner}" %*\r
2984
- `, { bestEffort: false });
2985
- } else {
2986
- await writeFileAtomicAsync(shimPath, `#!/bin/sh
2987
- exec "${resolvedInner}" "$@"
2988
- `, { bestEffort: false, mode: 488 });
2989
- }
2990
- logSessionStart(`archive-install ${tool.id}: installed → ${shimPath} (extracted ${archiveBuffer.length} bytes)`);
2991
- debugLog(`[archive] installed ${tool.name} → ${shimPath}`);
2992
- return shimPath;
2993
- } catch (err) {
2994
- await fs.rm(tmpArchive, { force: true }).catch(() => {});
2995
- await fs.rm(tmpExtractDir, { recursive: true, force: true }).catch(() => {});
2996
- logSessionStart(`archive-install ${tool.id}: install failed: ${err.message} — keeping installed version`);
2997
- return;
2998
- }
2999
- }
3000
- function recordPackageManagerInstallException(toolId, strategyLabel, packageName, err) {
3001
- const message = boundInstallError(err.message);
3002
- logSessionStart(`auto-install ${strategyLabel} ${packageName}: exception: ${message}`);
3003
- installFailureReasons.set(toolId, message);
3004
- return;
3005
- }
3006
- const INSTALL_ERROR_LINE_LIMIT = 1000;
3007
- const INSTALL_CANDIDATE_ERROR_LIMIT = 200;
3008
- function boundInstallError(value, limit = INSTALL_ERROR_LINE_LIMIT) {
3009
- const line = value.replace(/[\r\n]+/g, " ").trim();
3010
- return line.length > limit ? `${line.slice(0, limit - 3)}...` : line;
3011
- }
3012
- async function installNpmTool(toolId, packageName, binaryName, verificationArgs = ["--version"], verificationTimeoutMs = 1e4, packageEntryOf) {
3013
- try {
3014
- await fs.mkdir(TOOLS_DIR, { recursive: true });
3015
- const packageJsonPath = path.join(TOOLS_DIR, "package.json");
3016
- try {
3017
- await fs.access(packageJsonPath);
3018
- } catch {
3019
- await writeFileAtomicAsync(packageJsonPath, JSON.stringify({ name: "dm-lens-tools", version: "1.0.0" }, null, 2), { bestEffort: false });
3020
- }
3021
- const isWindows = installerPlatform() === "win32";
3022
- const pm = await resolveNodePackageManager(TOOLS_DIR);
3023
- const testNpmScript = process.env.DM_LENS_TEST_MODE === "1" ? process.env.DM_LENS_TEST_NPM_SCRIPT : undefined;
3024
- const pmCommand = testNpmScript ? process.execPath : pmBinary(pm);
3025
- const needsScripts = NEEDS_POSTINSTALL.has(packageName);
3026
- const baseInstallArgs = installArgs(pm, packageName, {
3027
- ignoreScripts: !needsScripts
3028
- });
3029
- const INSTALL_TIMEOUT_MS = Number(process.env.DM_LENS_INSTALL_TIMEOUT_MS) || 120000;
3030
- const runInstallAttempt = async (args) => {
3031
- const result = await safeSpawnAsync(pmCommand, args, {
3032
- cwd: TOOLS_DIR,
3033
- timeout: INSTALL_TIMEOUT_MS,
3034
- ignoreAmbientSignal: true,
3035
- lifetimeCoupled: true
3036
- });
3037
- return {
3038
- ok: result.status === 0,
3039
- stderr: result.error?.message ?? result.stderr
3040
- };
3041
- };
3042
- let outcome = await runInstallAttempt([
3043
- ...testNpmScript ? [testNpmScript] : [],
3044
- ...baseInstallArgs
3045
- ]);
3046
- const erResolve = outcome.ok === false && /npm\s+error\s+ERESOLVE|\bERESOLVE\b|could not resolve/i.test(outcome.stderr);
3047
- if (pm === "npm" && erResolve) {
3048
- const retryArgs = installArgs(pm, packageName, {
3049
- ignoreScripts: !needsScripts,
3050
- legacyPeerDeps: true
3051
- });
3052
- logSessionStart(`auto-install npm ${packageName}: retry with --legacy-peer-deps after ERESOLVE`);
3053
- outcome = await runInstallAttempt([
3054
- ...testNpmScript ? [testNpmScript] : [],
3055
- ...retryArgs
3056
- ]);
3057
- }
3058
- if (!outcome.ok) {
3059
- throw new Error(`Failed to install ${packageName}: ${outcome.stderr}`);
3060
- }
3061
- const binBase = path.join(TOOLS_DIR, "node_modules", ".bin", binaryName);
3062
- const binPath = installerPlatform() === "win32" ? `${binBase}.cmd` : binBase;
3063
- if (installerPlatform() !== "win32") {
3064
- try {
3065
- await fs.chmod(binPath, 488);
3066
- } catch {}
3067
- }
3068
- await new Promise((r) => setTimeout(r, 500));
3069
- debugLog(`Verifying ${binaryName}...`);
3070
- let isValid = false;
3071
- let lastAttemptTransient = false;
3072
- let lastAttemptInconclusive = false;
3073
- for (let attempt = 1;attempt <= 3; attempt++) {
3074
- lastAttemptTransient = false;
3075
- lastAttemptInconclusive = false;
3076
- isValid = await verifyToolBinary(binPath, undefined, () => {
3077
- lastAttemptTransient = true;
3078
- }, verificationTimeoutMs, verificationArgs, packageEntryOf, () => {
3079
- lastAttemptInconclusive = true;
3080
- });
3081
- if (isValid)
3082
- break;
3083
- if (attempt < 3) {
3084
- logSessionStart(`auto-install verify ${binaryName}: attempt ${attempt} failed, retrying in ${attempt}s`);
3085
- await new Promise((r) => setTimeout(r, 1000 * attempt));
3086
- }
3087
- }
3088
- if (!isValid && lastAttemptInconclusive) {
3089
- if (await verifyNpmPackageEntry(binPath, packageName)) {
3090
- logSessionStart(`auto-install ${packageName}: verification inconclusive (output truncated before the transport-required marker) but the installed tree is intact; keeping installation for re-probe`);
3091
- return;
3092
- }
3093
- logSessionStart(`auto-install ${packageName}: verification inconclusive AND the installed tree is incomplete; cleaning up so the next install can repair it`);
3094
- }
3095
- if (!isValid && lastAttemptTransient) {
3096
- logSessionStart(`auto-install ${packageName}: verification inconclusive (transient); keeping installation for re-probe`);
3097
- return;
3098
- }
3099
- if (!isValid) {
3100
- logSessionStart(`auto-install ${packageName}: installed but verification failed, cleaning up`);
3101
- try {
3102
- const packagePath = path.join(TOOLS_DIR, "node_modules", packageName);
3103
- await fs.rm(packagePath, { recursive: true, force: true });
3104
- await fs.rm(binBase, { force: true });
3105
- if (isWindows) {
3106
- await fs.rm(`${binBase}.cmd`, { force: true });
3107
- await fs.rm(`${binBase}.ps1`, { force: true });
3108
- }
3109
- } catch {}
3110
- return;
3111
- }
3112
- return binPath;
3113
- } catch (err) {
3114
- return recordPackageManagerInstallException(toolId, "npm", packageName, err);
3115
- }
3116
- }
3117
- export function pipCommandCandidates() {
3118
- return process.platform === "win32" ? ["pip", "py", "python"] : ["pip3", "pip", "python3", "python"];
3119
- }
3120
- export function pipScriptsDir(base, platform = installerPlatform()) {
3121
- return path.join(base, platform === "win32" ? "Scripts" : "bin");
3122
- }
3123
- const pipPep668LoggedRefusals = createGenerationMap("installer-pep668-log");
3124
- async function pipConstraintEnvFor(toolId) {
3125
- const constraints = TOOLS.find((t) => t.id === toolId)?.pipConstraints;
3126
- if (!constraints || constraints.length === 0)
3127
- return {};
3128
- const directories = [
3129
- path.join(getGlobalDmLensDir(), "pip-constraints"),
3130
- path.join(os.tmpdir(), "dm-lens-pip-constraints")
3131
- ];
3132
- const directory = directories.find((candidate) => !/\s/.test(candidate));
3133
- if (!directory) {
3134
- recordDegradationOnce({
3135
- kind: "pip-constraint-path-unusable",
3136
- subject: toolId,
3137
- reason: `no whitespace-free directory for the constraints file (tried ${directories.join(", ")}); installing unconstrained`
3138
- });
3139
- return {};
3140
- }
3141
- const file = path.join(directory, `${toolId}.txt`);
3142
- try {
3143
- await fs.mkdir(path.dirname(file), { recursive: true });
3144
- await writeFileAtomicAsync(file, `${constraints.join(`
3145
- `)}
3146
- `, {
3147
- bestEffort: false
3148
- });
3149
- } catch (err) {
3150
- recordDegradationOnce({
3151
- kind: "pip-constraint-file-unwritable",
3152
- subject: toolId,
3153
- reason: `${file}: ${err instanceof Error ? err.message : String(err)}`
3154
- });
3155
- return {};
3156
- }
3157
- logSessionStart(`auto-install pip ${toolId}: constraining resolution with ${constraints.join(", ")} (${file})`);
3158
- return { PIP_CONSTRAINT: file, UV_CONSTRAINT: file };
3159
- }
3160
- async function installPipTool(toolId, packageName, binaryName, options = {}) {
3161
- try {
3162
- const isWindows = installerPlatform() === "win32";
3163
- const verb = options.upgrade ? ["install", "-U"] : ["install"];
3164
- const pipConstraintEnv = await pipConstraintEnvFor(toolId);
3165
- const pipCandidates = pipCommandCandidates().map((command) => ({
3166
- command,
3167
- args: command === "pip" || command === "pip3" ? [...verb, packageName] : ["-m", "pip", ...verb, packageName]
3168
- }));
3169
- const pep668 = /externally-managed-environment/i;
3170
- const refuse = (strategy, reason) => {
3171
- if (!pep668.test(reason))
3172
- return;
3173
- const subject = `${toolId}:${strategy}`;
3174
- recordDegradationOnce({
3175
- kind: "pip-pep668-strategy-refused",
3176
- subject,
3177
- reason
3178
- });
3179
- const logKey = `${getDegradationLedgerGeneration()}:${subject}`;
3180
- if (pipPep668LoggedRefusals.current(logKey) === 0) {
3181
- pipPep668LoggedRefusals.bump(logKey);
3182
- logSessionStart(`auto-install pip ${packageName}: ${strategy} refused by PEP 668 (${boundInstallError(reason)})`);
3183
- }
3184
- };
3185
- const succeeded = (strategy, binaryPath) => {
3186
- recordDegradationOnce({
3187
- kind: "pip-install-strategy-succeeded",
3188
- subject: `${toolId}:${strategy}`,
3189
- reason: binaryPath
3190
- });
3191
- return binaryPath;
3192
- };
3193
- const run = (command, args, env) => {
3194
- const spawnEnv = Object.keys(pipConstraintEnv).length > 0 ? { ...env ?? process.env, ...pipConstraintEnv } : env;
3195
- return safeSpawnAsync(command, args, {
3196
- timeout: 120000,
3197
- ignoreAmbientSignal: true,
3198
- lifetimeCoupled: true,
3199
- cwd: resolveToolCwd("runner", toolId, getGlobalDmLensDir(), {
3200
- cwd: getGlobalDmLensDir(),
3201
- suppressTelemetry: true
3202
- }).cwd,
3203
- ...spawnEnv ? { env: spawnEnv } : {}
3204
- });
3205
- };
3206
- const addBinToPath = async (binDir) => {
3207
- try {
3208
- await fs.access(binDir);
3209
- } catch {
3210
- return;
3211
- }
3212
- const currentPath = process.env.PATH || process.env.Path || "";
3213
- const separator = isWindows ? ";" : path.delimiter;
3214
- if (!currentPath.toLowerCase().split(separator).includes(binDir.toLowerCase())) {
3215
- const updatedPath = `${binDir}${separator}${currentPath}`;
3216
- process.env.PATH = updatedPath;
3217
- if (isWindows)
3218
- process.env.Path = updatedPath;
3219
- }
3220
- const names = isWindows ? [`${binaryName}.exe`, `${binaryName}.cmd`, binaryName] : [binaryName];
3221
- for (const name of names) {
3222
- const candidate = path.join(binDir, name);
3223
- try {
3224
- await fs.access(candidate);
3225
- return candidate;
3226
- } catch {}
3227
- }
3228
- return;
3229
- };
3230
- if (await isCommandAvailable("pipx")) {
3231
- const result = await run("pipx", options.upgrade ? ["upgrade", packageName] : ["install", "--force", packageName]);
3232
- const error = (result.error?.message ?? result.stderr).trim();
3233
- if (result.status === 0) {
3234
- const location = await run("pipx", [
3235
- "environment",
3236
- "--value",
3237
- "PIPX_BIN_DIR"
3238
- ]);
3239
- const binDir = location.status === 0 && location.stdout.trim() ? location.stdout.trim() : path.join(os.homedir(), ".local", "bin");
3240
- const binaryPath = await addBinToPath(binDir);
3241
- if (binaryPath)
3242
- return succeeded("pipx", binaryPath);
3243
- throw new Error(`pipx installed ${packageName} but ${binaryName} is not resolvable`);
3244
- }
3245
- refuse("pipx", error);
3246
- }
3247
- const pythonCandidates = pipCandidates.filter(({ command }) => command === "python3" || command === "python" || command === "py");
3248
- const pythonAvailability = await Promise.all(pythonCandidates.map(({ command }) => isCommandAvailable(command)));
3249
- const availablePythonCandidates = pythonCandidates.filter((_, index) => pythonAvailability[index]);
3250
- const venvRoot = path.join(getGlobalDmLensDir(), "pip-tools");
3251
- for (const candidate of availablePythonCandidates) {
3252
- const venvBin = pipScriptsDir(venvRoot, installerPlatform());
3253
- let venvPip = path.join(venvBin, isWindows ? "pip.exe" : "pip");
3254
- try {
3255
- await fs.access(venvPip);
3256
- } catch {
3257
- const created = await run(candidate.command, ["-m", "venv", venvRoot]);
3258
- const error = (created.error?.message ?? created.stderr).trim();
3259
- if (created.status !== 0) {
3260
- refuse("venv", error || "python venv module unavailable");
3261
- continue;
3262
- }
3263
- }
3264
- try {
3265
- await fs.access(venvPip);
3266
- } catch {
3267
- venvPip = path.join(venvBin, isWindows ? "pip.cmd" : "pip3");
3268
- try {
3269
- await fs.access(venvPip);
3270
- } catch {
3271
- continue;
3272
- }
3273
- }
3274
- const result = await run(venvPip, [...verb, packageName]);
3275
- const error = (result.error?.message ?? result.stderr).trim();
3276
- if (result.status === 0) {
3277
- const binaryPath = await addBinToPath(venvBin);
3278
- if (binaryPath)
3279
- return succeeded("venv", binaryPath);
3280
- throw new Error(`venv installed ${packageName} but ${binaryName} is not resolvable`);
3281
- }
3282
- refuse("venv", error);
3283
- }
3284
- const candidateErrors = [];
3285
- let userRefused = false;
3286
- for (const candidate of pipCandidates) {
3287
- const args = candidate.command === "pip" || candidate.command === "pip3" ? [...verb, "--user", packageName] : ["-m", "pip", ...verb, "--user", packageName];
3288
- const result = await run(candidate.command, args);
3289
- const error = (result.error?.message ?? result.stderr).trim();
3290
- if (result.status === 0) {
3291
- const base = await new Promise((resolve) => {
3292
- let probe;
3293
- try {
3294
- probe = spawn(candidate.command, ["-m", "site", "--user-base"], {
3295
- stdio: ["ignore", "pipe", "pipe"],
3296
- shell: isWindows
3297
- });
3298
- } catch {
3299
- resolve("");
3300
- return;
3301
- }
3302
- const stdout = createBoundedOutputSink();
3303
- probe.stdout?.on("data", (data) => stdout.append(data));
3304
- probe.on("exit", (code) => resolve(userBaseProbeResult(candidate.command, code, stdout)));
3305
- probe.on("error", () => resolve(""));
3306
- });
3307
- const binaryPath = base ? await addBinToPath(pipScriptsDir(base, installerPlatform())) : undefined;
3308
- return succeeded("user", binaryPath ?? packageName);
3309
- }
3310
- const candidateError = `${candidate.command} ${args.join(" ")}: ${boundInstallError(error, INSTALL_CANDIDATE_ERROR_LIMIT)}`;
3311
- candidateErrors.push(candidateError);
3312
- if (pep668.test(error)) {
3313
- userRefused = true;
3314
- refuse("user", error);
3315
- }
3316
- }
3317
- if (!userRefused)
3318
- throw new Error(`pip install failed: ${candidateErrors.join(" | ") || "unknown error"}`);
3319
- const privateBase = path.join(getGlobalDmLensDir(), "pip-user");
3320
- const privateEnv = { ...process.env, PYTHONUSERBASE: privateBase };
3321
- for (const candidate of pipCandidates) {
3322
- const args = candidate.command === "pip" || candidate.command === "pip3" ? [...verb, "--user", "--break-system-packages", packageName] : [
3323
- "-m",
3324
- "pip",
3325
- ...verb,
3326
- "--user",
3327
- "--break-system-packages",
3328
- packageName
3329
- ];
3330
- const result = await run(candidate.command, args, privateEnv);
3331
- const error = (result.error?.message ?? result.stderr).trim();
3332
- if (result.status !== 0) {
3333
- const candidateError = `${candidate.command} ${args.join(" ")}: ${boundInstallError(error, INSTALL_CANDIDATE_ERROR_LIMIT)}`;
3334
- candidateErrors.push(candidateError);
3335
- continue;
3336
- }
3337
- const binaryPath = await addBinToPath(pipScriptsDir(privateBase, installerPlatform()));
3338
- if (binaryPath)
3339
- return succeeded("private-prefix", binaryPath);
3340
- throw new Error(`private-prefix pip installed ${packageName} but ${binaryName} is not resolvable`);
3341
- }
3342
- throw new Error(`pip install failed: ${candidateErrors.join(" | ") || "unknown error"}`);
3343
- } catch (err) {
3344
- return recordPackageManagerInstallException(toolId, "pip", packageName, err);
3345
- }
3346
- }
3347
- async function installGemTool(toolId, packageName) {
3348
- try {
3349
- const gemResult = await safeSpawnAsync("gem", ["install", packageName, "--no-document"], {
3350
- timeout: 120000,
3351
- ignoreAmbientSignal: true,
3352
- lifetimeCoupled: true
3353
- });
3354
- const outcome = {
3355
- ok: gemResult.status === 0,
3356
- error: (gemResult.error?.message ?? gemResult.stderr).trim()
3357
- };
3358
- if (!outcome.ok) {
3359
- throw new Error(`Failed to install ${packageName} via gem: ${outcome.error}`);
3360
- }
3361
- return packageName;
3362
- } catch (err) {
3363
- return recordPackageManagerInstallException(toolId, "gem", packageName, err);
3364
- }
3365
- }
3366
- async function stampInstallResolution(toolId) {
3367
- const tool = TOOLS.find((t) => t.id === toolId);
3368
- if (tool?.installStrategy === "npm")
3369
- return;
3370
- try {
3371
- const resolutionId = lastInstallResolutionId.get(toolId);
3372
- const refresh = await import("./managed-tool-refresh.js");
3373
- await refresh.stampManagedToolInstall(toolId, resolutionId);
3374
- } catch {}
3375
- }
3376
- async function finishInstallAttempt(toolId, ok, startedAt) {
3377
- logSessionStart(`auto-install ${toolId}: ${ok ? "success" : "failed"} (${Date.now() - startedAt}ms)`);
3378
- if (ok)
3379
- await stampInstallResolution(toolId);
3380
- noteInstallAttempt(toolId, ok ? "succeeded" : "failed", ok ? undefined : installFailureReasons.get(toolId) ?? "install failed");
3381
- if (ok) {
3382
- resetSafeSpawnWindowsCommandCache();
3383
- try {
3384
- const { resetMadgeManagedPathMemo } = await import("../dependency-checker.js");
3385
- resetMadgeManagedPathMemo();
3386
- } catch (err) {
3387
- logSessionStart(`auto-install ${toolId}: madge memo reset failed: ${err.message}`);
3388
- }
3389
- }
3390
- return ok;
3391
- }
3392
- export async function installTool(toolId) {
3393
- if (process.env.DM_LENS_DISABLE_TOOL_INSTALL === "1") {
3394
- installFailureReasons.set(toolId, "installation disabled by DM_LENS_DISABLE_TOOL_INSTALL=1");
3395
- noteInstallAttempt(toolId, "declined", "installation disabled by DM_LENS_DISABLE_TOOL_INSTALL=1");
3396
- logSessionStart(`auto-install ${toolId}: refused — DM_LENS_DISABLE_TOOL_INSTALL=1`);
3397
- return false;
3398
- }
3399
- const tool = TOOLS.find((t) => t.id === toolId);
3400
- if (!tool) {
3401
- noteInstallAttempt(toolId, "declined", "unknown tool id");
3402
- logSessionStart(`auto-install ${toolId}: unknown tool id`);
3403
- return false;
3404
- }
3405
- const startedAt = Date.now();
3406
- logSessionStart(`auto-install ${tool.id}: start strategy=${tool.installStrategy} package=${tool.packageName ?? "n/a"}`);
3407
- try {
3408
- switch (tool.installStrategy) {
3409
- case "npm": {
3410
- if (!tool.packageName || !tool.binaryName)
3411
- return false;
3412
- const npmPath = await installNpmTool(tool.id, tool.packageName, tool.binaryName, tool.checkArgs, getToolVerificationTimeout(tool), packageEntryVerification(tool));
3413
- if (npmPath !== undefined) {
3414
- await import("./managed-tool-refresh.js").then((m) => m.stampManagedToolInstalled(tool.id, tool.packageName)).catch(() => {});
3415
- }
3416
- return finishInstallAttempt(tool.id, npmPath !== undefined, startedAt);
3417
- }
3418
- case "pip": {
3419
- if (!tool.packageName)
3420
- return false;
3421
- const pipPath = await installPipTool(tool.id, tool.packageName, tool.binaryName ?? tool.id);
3422
- return finishInstallAttempt(tool.id, pipPath !== undefined, startedAt);
3423
- }
3424
- case "gem": {
3425
- if (!tool.packageName)
3426
- return false;
3427
- const gemPath = await installGemTool(tool.id, tool.packageName);
3428
- return finishInstallAttempt(tool.id, gemPath !== undefined, startedAt);
3429
- }
3430
- case "github": {
3431
- if (!tool.github)
3432
- return false;
3433
- if (!tool.github.assetMatch(process.platform, process.arch)) {
3434
- const reason = `unsupported platform=${process.platform} arch=${process.arch}`;
3435
- noteInstallAttempt(tool.id, "unavailable", reason);
3436
- logSessionStart(`auto-install ${tool.id}: ${reason}`);
3437
- return false;
3438
- }
3439
- const ghPath = await installGitHubTool(tool);
3440
- return finishInstallAttempt(tool.id, ghPath !== undefined, startedAt);
3441
- }
3442
- case "maven": {
3443
- if (!tool.maven)
3444
- return false;
3445
- const mavenPath = await installMavenTool(tool);
3446
- return finishInstallAttempt(tool.id, mavenPath !== undefined, startedAt);
3447
- }
3448
- case "archive": {
3449
- if (!tool.archive)
3450
- return false;
3451
- if (!resolveArchiveUrl(tool.archive)) {
3452
- const reason = `unsupported platform=${process.platform} arch=${process.arch}`;
3453
- noteInstallAttempt(tool.id, "unavailable", reason);
3454
- logSessionStart(`auto-install ${tool.id}: ${reason}`);
3455
- return false;
3456
- }
3457
- const archivePath = await installArchiveTool(tool);
3458
- return finishInstallAttempt(tool.id, archivePath !== undefined, startedAt);
3459
- }
3460
- default:
3461
- logSessionStart(`auto-install ${tool.id}: unsupported strategy`);
3462
- return false;
3463
- }
3464
- } catch (err) {
3465
- logSessionStart(`auto-install ${tool.id}: exception ${err.message} (${Date.now() - startedAt}ms)`);
3466
- return false;
3467
- }
3468
- }
3469
- export async function ensureTool(toolId, opts) {
3470
- if (opts?.allowInstall !== false && !assertInstallAllowed(`managed tool ensure: ${toolId}`)) {
3471
- logSessionStart(`auto-install ensure ${toolId}: install gated — ${projectTrustDenialReason()}; discovery only`);
3472
- const denialReason = projectTrustDenialReason();
3473
- const discovered = await ensureToolResolved(toolId, {
3474
- ...opts,
3475
- allowInstall: false
3476
- });
3477
- noteInstallAttempt(toolId, "declined", `project trust: ${denialReason}`);
3478
- return discovered;
3479
- }
3480
- return ensureToolResolved(toolId, opts);
3481
- }
3482
- async function ensureToolResolved(toolId, opts) {
3483
- installFailureReasons.delete(toolId);
3484
- installAttempts.delete(toolId);
3485
- lastEnsureResolutionSource.delete(toolId);
3486
- const cacheResolvedPath = (result) => {
3487
- if (result) {
3488
- resolvedPathCache.set(toolId, result);
3489
- updateProbeCache(toolId, result, wasLastResolveTransient(toolId));
3490
- }
3491
- return result;
3492
- };
3493
- if (opts?.forceReinstall) {
3494
- const ensureStartMs = Date.now();
3495
- logSessionStart(`auto-install ensure ${toolId}: force reinstall — clearing caches`);
3496
- resolvedPathCache.delete(toolId);
3497
- try {
3498
- const probeCache = await readProbeCache();
3499
- delete probeCache[toolId];
3500
- markProbeCacheChange(toolId, null);
3501
- } catch {}
3502
- if (opts.allowInstall === false) {
3503
- noteInstallAttempt(toolId, "declined", "install disabled by caller");
3504
- logSessionStart(`auto-install ensure ${toolId}: force reinstall blocked — install disabled, discovery only (${Date.now() - ensureStartMs}ms)`);
3505
- return cacheResolvedPath(await getToolPath(toolId));
3506
- }
3507
- if (process.env.DM_LENS_DISABLE_TOOL_INSTALL === "1") {
3508
- installFailureReasons.set(toolId, "installation disabled by DM_LENS_DISABLE_TOOL_INSTALL=1");
3509
- noteInstallAttempt(toolId, "declined", "installation disabled by DM_LENS_DISABLE_TOOL_INSTALL=1");
3510
- logSessionStart(`auto-install ensure ${toolId}: refused — DM_LENS_DISABLE_TOOL_INSTALL=1`);
3511
- return;
3512
- }
3513
- const lock = await acquireInstallLock();
3514
- if (!lock.release) {
3515
- noteInstallAttempt(toolId, "skipped", lock.reason ?? "install lock held");
3516
- logSessionStart(`auto-install ensure ${toolId}: ${lock.reason}`);
3517
- return;
3518
- }
3519
- let installed;
3520
- try {
3521
- installed = await installTool(toolId);
3522
- } finally {
3523
- await lock.release();
3524
- }
3525
- if (!installed) {
3526
- noteInstallAttemptIfUnrecorded(toolId, "failed", "install failed");
3527
- logSessionStart(`auto-install ensure ${toolId}: force reinstall failed (${Date.now() - ensureStartMs}ms)`);
3528
- return;
3529
- }
3530
- const result = cacheResolvedPath(await getToolPath(toolId));
3531
- if (result) {
3532
- logSessionStart(`auto-install ensure ${toolId}: force reinstall success at ${result} (${Date.now() - ensureStartMs}ms)`);
3533
- }
3534
- return result;
3535
- }
3536
- const cached = resolvedPathCache.get(toolId);
3537
- if (cached) {
3538
- if (!isFullyQualified(cached)) {
3539
- lastEnsureResolutionSource.set(toolId, "session-cache");
3540
- return cached;
3541
- }
3542
- try {
3543
- await fs.access(cached);
3544
- lastEnsureResolutionSource.set(toolId, "session-cache");
3545
- return cached;
3546
- } catch {}
3547
- resolvedPathCache.delete(toolId);
3548
- const probeCache = await readProbeCache();
3549
- delete probeCache[toolId];
3550
- markProbeCacheChange(toolId, null);
3551
- logSessionStart(`auto-install ensure ${toolId}: cached path disappeared; re-probing`);
3552
- }
3553
- const diskCached = await checkProbeCache(toolId);
3554
- if (diskCached) {
3555
- resolvedPathCache.set(toolId, diskCached);
3556
- lastEnsureResolutionSource.set(toolId, "probe-cache");
3557
- logSessionStart(`auto-install ensure ${toolId}: probe cache hit → ${diskCached}`);
3558
- return diskCached;
3559
- }
3560
- const inFlightKey = opts?.allowInstall === false ? `${toolId}:discovery-only` : toolId;
3561
- const inFlight = ensureInFlight.get(inFlightKey);
3562
- if (inFlight) {
3563
- logSessionStart(`auto-install ensure ${toolId}: waiting for in-flight ensure (${inFlightKey})`);
3564
- return inFlight;
3565
- }
3566
- const ensureStartMs = Date.now();
3567
- const ensurePromise = (async () => {
3568
- logSessionStart(`auto-install ensure ${toolId}: start`);
3569
- const existingPath = await getToolPath(toolId);
3570
- if (existingPath) {
3571
- const tool = TOOLS.find((t) => t.id === toolId);
3572
- const pinnedVersion = tool?.installStrategy === "npm" && tool.packageName ? parsePinnedVersion(tool.packageName) : undefined;
3573
- if (pinnedVersion) {
3574
- const seenVersion = lastManagedInstallVersion.get(toolId);
3575
- if (seenVersion && seenVersion !== pinnedVersion) {
3576
- lastManagedInstallVersion.delete(toolId);
3577
- logSessionStart(`auto-install ensure ${toolId}: version drift (installed ${seenVersion} != pinned ${pinnedVersion}) — forcing reinstall (${Date.now() - ensureStartMs}ms)`);
3578
- return ensureTool(toolId, {
3579
- forceReinstall: true,
3580
- allowInstall: opts?.allowInstall
3581
- });
3582
- }
3583
- }
3584
- resolvedPathCache.set(toolId, existingPath);
3585
- updateProbeCache(toolId, existingPath, wasLastResolveTransient(toolId));
3586
- lastEnsureResolutionSource.set(toolId, "path");
3587
- logSessionStart(`auto-install ensure ${toolId}: already available at ${existingPath} (${Date.now() - ensureStartMs}ms)`);
3588
- return existingPath;
3589
- }
3590
- if (opts?.allowInstall === false) {
3591
- noteInstallAttempt(toolId, "declined", "install disabled by caller");
3592
- logSessionStart(`auto-install ensure ${toolId}: install disabled — discovery only, not found (${Date.now() - ensureStartMs}ms)`);
3593
- return;
3594
- }
3595
- if (process.env.DM_LENS_DISABLE_TOOL_INSTALL === "1") {
3596
- installFailureReasons.set(toolId, "installation disabled by DM_LENS_DISABLE_TOOL_INSTALL=1");
3597
- noteInstallAttempt(toolId, "declined", "installation disabled by DM_LENS_DISABLE_TOOL_INSTALL=1");
3598
- logSessionStart(`auto-install ensure ${toolId}: refused — DM_LENS_DISABLE_TOOL_INSTALL=1 (${Date.now() - ensureStartMs}ms)`);
3599
- return;
3600
- }
3601
- const lock = await acquireInstallLock();
3602
- if (!lock.release) {
3603
- installFailureReasons.set(toolId, lock.reason ?? "install lock failed");
3604
- noteInstallAttempt(toolId, "skipped", lock.reason ?? "install lock held");
3605
- logSessionStart(`auto-install ensure ${toolId}: ${lock.reason}`);
3606
- return;
3607
- }
3608
- let installed;
3609
- try {
3610
- const installedByPeer = await getToolPath(toolId);
3611
- if (installedByPeer) {
3612
- noteInstallAttempt(toolId, "succeeded", "installed by a concurrent process");
3613
- resolvedPathCache.set(toolId, installedByPeer);
3614
- updateProbeCache(toolId, installedByPeer, wasLastResolveTransient(toolId));
3615
- return installedByPeer;
3616
- }
3617
- installed = await installTool(toolId);
3618
- } finally {
3619
- await lock.release();
3620
- }
3621
- if (!installed) {
3622
- noteInstallAttemptIfUnrecorded(toolId, "failed", "install failed");
3623
- logSessionStart(`auto-install ensure ${toolId}: unavailable (${Date.now() - ensureStartMs}ms)`);
3624
- return;
3625
- }
3626
- const result = await getToolPath(toolId);
3627
- if (result) {
3628
- resolvedPathCache.set(toolId, result);
3629
- updateProbeCache(toolId, result, wasLastResolveTransient(toolId));
3630
- logSessionStart(`auto-install ensure ${toolId}: success at ${result} (${Date.now() - ensureStartMs}ms)`);
3631
- } else {
3632
- logSessionStart(`auto-install ensure ${toolId}: unavailable (${Date.now() - ensureStartMs}ms)`);
3633
- }
3634
- return result;
3635
- })();
3636
- ensureInFlight.set(inFlightKey, ensurePromise);
3637
- try {
3638
- return await ensurePromise;
3639
- } finally {
3640
- if (ensureInFlight.get(inFlightKey) === ensurePromise) {
3641
- ensureInFlight.delete(inFlightKey);
3642
- }
3643
- }
3644
- }
3645
- export async function getToolEnvironment() {
3646
- const localBin = path.join(TOOLS_DIR, "node_modules", ".bin");
3647
- const currentPath = process.env.PATH || process.env.Path || process.env.path || "";
3648
- const separator = process.platform === "win32" ? ";" : ":";
3649
- const nodeDir = path.dirname(process.execPath);
3650
- const withNode = nodeDir ? `${nodeDir}${separator}${currentPath}` : currentPath;
3651
- const augmentedPath = `${GITHUB_BIN_DIR}${separator}${localBin}${separator}${withNode}`;
3652
- const env = {
3653
- ...process.env,
3654
- PATH: augmentedPath
3655
- };
3656
- if (process.platform === "win32") {
3657
- env.Path = augmentedPath;
3658
- }
3659
- return env;
3660
- }
3661
- export async function checkAllTools() {
3662
- const results = [];
3663
- for (const tool of TOOLS) {
3664
- const path = await getToolPath(tool.id);
3665
- results.push({
3666
- id: tool.id,
3667
- name: tool.name,
3668
- installed: path !== undefined,
3669
- path
3670
- });
3671
- }
3672
- return results;
3673
- }
3674
- export function isKnownToolId(toolId) {
3675
- return TOOLS.some((tool) => tool.id === toolId);
3676
- }
3677
- export function getToolInstallStrategy(toolId) {
3678
- return TOOLS.find((tool) => tool.id === toolId)?.installStrategy;
3679
- }
3680
- export const GITHUB_TOOLS = [
3681
- "cljfmt",
3682
- "php-cs-fixer",
3683
- "shellcheck",
3684
- "shfmt",
3685
- "rust-analyzer",
3686
- "golangci-lint",
3687
- "ktlint",
3688
- "actionlint",
3689
- "zizmor",
3690
- "typos-lsp",
3691
- "tflint",
3692
- "terragrunt",
3693
- "terraform-ls",
3694
- "zls",
3695
- "hadolint",
3696
- "helm",
3697
- "gitleaks",
3698
- "taplo",
3699
- "vale",
3700
- "opengrep",
3701
- "deno",
3702
- "clojure-lsp",
3703
- "cue",
3704
- "gleam",
3705
- "typstyle",
3706
- "tinymist",
3707
- "marksman",
3708
- "expert"
3709
- ];
3710
- export function resolveGitHubAsset(toolId, platform, arch) {
3711
- const tool = TOOLS.find((t) => t.id === toolId);
3712
- return tool?.github?.assetMatch(platform, arch);
3713
- }
3714
- export function resolveGitHubAssetLauncher(toolId, _platform, assetName) {
3715
- const tool = TOOLS.find((t) => t.id === toolId);
3716
- return tool?.github ? launcherForGitHubAsset(tool.github, assetName) : undefined;
3717
- }
3718
- export function resolveGitHubInstalledBinaryName(toolId, platform, assetName) {
3719
- const tool = TOOLS.find((t) => t.id === toolId);
3720
- if (!tool)
3721
- return;
3722
- return getGitHubInstalledBinaryName(tool.binaryName ?? tool.id, platform, assetName);
3723
- }
3724
- export function resolveGitHubArchiveBinaryCandidates(toolId, platform, assetName) {
3725
- const tool = TOOLS.find((t) => t.id === toolId);
3726
- if (!tool)
3727
- return;
3728
- const binaryName = tool.github?.binaryInArchive ?? tool.binaryName ?? tool.id;
3729
- return getArchiveBinaryCandidates(binaryName, platform, assetName);
3730
- }
3731
- const ASSET_SIDECAR_SUFFIXES = [
3732
- ".asc",
3733
- ".sig",
3734
- ".minisig",
3735
- ".pem",
3736
- ".cert",
3737
- ".sbom",
3738
- ".sha256",
3739
- ".sha256sum",
3740
- ".md5"
3741
- ];
3742
- function isAssetSidecar(name) {
3743
- const lower = name.toLowerCase();
3744
- return ASSET_SIDECAR_SUFFIXES.some((suffix) => lower.endsWith(suffix));
3745
- }
3746
- export function pickReleaseAsset(assets, assetSubstring) {
3747
- return assets.find((a) => a.name === assetSubstring) ?? assets.find((a) => a.name.includes(assetSubstring) && !isAssetSidecar(a.name));
3748
- }
3749
- function deriveHashiCorpReleaseAsset(tool, tagName, assetSubstring) {
3750
- const product = tool.github?.hashiCorpReleaseProduct;
3751
- if (!product || !tagName)
3752
- return;
3753
- const version = tagName.replace(/^v/, "").trim();
3754
- if (!version)
3755
- return;
3756
- const assetName = `${product}_${version}_${assetSubstring}`;
3757
- return {
3758
- name: assetName,
3759
- browser_download_url: `https://releases.hashicorp.com/${product}/${version}/${assetName}`
3760
- };
3761
- }
3762
- export function resolveDerivedHashiCorpReleaseAsset(toolId, tagName, platform, arch) {
3763
- const tool = TOOLS.find((t) => t.id === toolId);
3764
- if (!tool)
3765
- return;
3766
- const assetSubstring = tool.github?.assetMatch(platform, arch);
3767
- if (!assetSubstring)
3768
- return;
3769
- return deriveHashiCorpReleaseAsset(tool, tagName, assetSubstring);
3770
- }