dsh-remote 0.8.46 → 0.8.48

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 (391) hide show
  1. package/LICENSE +21 -21
  2. package/README.en.md +361 -361
  3. package/README.md +368 -368
  4. package/cordis.patch.yml +9 -9
  5. package/lib/binding.js +172 -172
  6. package/lib/client.js +3637 -3637
  7. package/lib/credential.js +177 -177
  8. package/lib/deploy-tasks.js +142 -142
  9. package/lib/errors.js +87 -87
  10. package/lib/file-reference.js +457 -457
  11. package/lib/forwards.js +258 -258
  12. package/lib/hostkey.js +108 -108
  13. package/lib/http-transport.js +196 -196
  14. package/lib/ignore.js +202 -156
  15. package/lib/index.js +3779 -3747
  16. package/lib/openssh.js +838 -838
  17. package/lib/paths.js +225 -225
  18. package/lib/pool.js +757 -757
  19. package/lib/registry.js +95 -95
  20. package/lib/remote-fs.js +148 -143
  21. package/lib/routes-fs.js +233 -233
  22. package/lib/search.js +235 -230
  23. package/lib/sftp-surface.js +76 -76
  24. package/lib/sshconfig.js +454 -454
  25. package/lib/sync.js +275 -275
  26. package/lib/tasks.js +95 -95
  27. package/lib/telemetry.js +213 -213
  28. package/lib/update.js +472 -472
  29. package/lib/web-attach.js +850 -850
  30. package/lib/web-deploy-skill.js +205 -205
  31. package/lib/web-deploy.js +694 -694
  32. package/node_modules/asn1/Jenkinsfile +65 -0
  33. package/node_modules/asn1/LICENSE +19 -0
  34. package/node_modules/asn1/README.md +50 -0
  35. package/node_modules/asn1/lib/ber/errors.js +13 -0
  36. package/node_modules/asn1/lib/ber/index.js +27 -0
  37. package/node_modules/asn1/lib/ber/reader.js +262 -0
  38. package/node_modules/asn1/lib/ber/types.js +36 -0
  39. package/node_modules/asn1/lib/ber/writer.js +317 -0
  40. package/node_modules/asn1/lib/index.js +20 -0
  41. package/node_modules/asn1/package.json +31 -0
  42. package/node_modules/bcrypt-pbkdf/CONTRIBUTING.md +13 -0
  43. package/node_modules/bcrypt-pbkdf/LICENSE +66 -0
  44. package/node_modules/bcrypt-pbkdf/README.md +45 -0
  45. package/node_modules/bcrypt-pbkdf/index.js +556 -0
  46. package/node_modules/bcrypt-pbkdf/package.json +15 -0
  47. package/node_modules/buildcheck/.eslintrc.js +5 -0
  48. package/node_modules/buildcheck/.github/workflows/ci.yml +88 -0
  49. package/node_modules/buildcheck/.github/workflows/lint.yml +27 -0
  50. package/node_modules/buildcheck/.npmignore +3 -0
  51. package/node_modules/buildcheck/LICENSE +19 -0
  52. package/node_modules/buildcheck/README.md +106 -0
  53. package/node_modules/buildcheck/deps/Find-VisualStudio.cs +250 -0
  54. package/node_modules/buildcheck/lib/findvs.js +361 -0
  55. package/node_modules/buildcheck/lib/index.js +738 -0
  56. package/node_modules/buildcheck/package.json +36 -0
  57. package/node_modules/buildcheck/test/test.js +8 -0
  58. package/node_modules/cpu-features/.eslintrc.js +5 -0
  59. package/node_modules/cpu-features/.github/workflows/ci.yml +74 -0
  60. package/node_modules/cpu-features/.github/workflows/lint.yml +27 -0
  61. package/node_modules/cpu-features/LICENSE +19 -0
  62. package/node_modules/cpu-features/README.md +59 -0
  63. package/node_modules/cpu-features/binding.gyp +16 -0
  64. package/node_modules/cpu-features/buildcheck.js +32 -0
  65. package/node_modules/cpu-features/deps/cpu_features/.clang-format +4 -0
  66. package/node_modules/cpu-features/deps/cpu_features/.dockerignore +31 -0
  67. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/Dockerfile +5 -0
  68. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/aarch64_linux_cmake.yml +30 -0
  69. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_freebsd_cmake.yml +22 -0
  70. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_linux_bazel.yml +26 -0
  71. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_linux_cmake.yml +31 -0
  72. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_macos_cmake.yml +43 -0
  73. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_windows_cmake.yml +25 -0
  74. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/arm_linux_cmake.yml +31 -0
  75. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/clang_format.yml +24 -0
  76. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/mips_linux_cmake.yml +30 -0
  77. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/power_linux_cmake.yml +29 -0
  78. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/riscv_linux_cmake.yml +28 -0
  79. package/node_modules/cpu-features/deps/cpu_features/.github/workflows/s390x_linux_cmake.yml +27 -0
  80. package/node_modules/cpu-features/deps/cpu_features/.grenrc.yml +21 -0
  81. package/node_modules/cpu-features/deps/cpu_features/BUILD.bazel +329 -0
  82. package/node_modules/cpu-features/deps/cpu_features/CMakeLists.txt +261 -0
  83. package/node_modules/cpu-features/deps/cpu_features/CONTRIBUTING.md +23 -0
  84. package/node_modules/cpu-features/deps/cpu_features/LICENSE +230 -0
  85. package/node_modules/cpu-features/deps/cpu_features/README.md +272 -0
  86. package/node_modules/cpu-features/deps/cpu_features/WORKSPACE +19 -0
  87. package/node_modules/cpu-features/deps/cpu_features/bazel/ci/README.md +5 -0
  88. package/node_modules/cpu-features/deps/cpu_features/bazel/platforms.bzl +11 -0
  89. package/node_modules/cpu-features/deps/cpu_features/cmake/CpuFeaturesConfig.cmake.in +3 -0
  90. package/node_modules/cpu-features/deps/cpu_features/cmake/CpuFeaturesNdkCompatConfig.cmake.in +3 -0
  91. package/node_modules/cpu-features/deps/cpu_features/cmake/README.md +30 -0
  92. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/Makefile +252 -0
  93. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/README.md +40 -0
  94. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/doc/docker.dot +64 -0
  95. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/doc/docker.svg +312 -0
  96. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/doc/generate_image.sh +7 -0
  97. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/docker/amd64/Dockerfile +48 -0
  98. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/docker/toolchain/Dockerfile +34 -0
  99. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/sample/CMakeLists.txt +22 -0
  100. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/sample/main.cpp +11 -0
  101. package/node_modules/cpu-features/deps/cpu_features/cmake/ci/vagrant/freebsd/Vagrantfile +107 -0
  102. package/node_modules/cpu-features/deps/cpu_features/cmake/googletest.CMakeLists.txt.in +15 -0
  103. package/node_modules/cpu-features/deps/cpu_features/cpu_features.gyp +105 -0
  104. package/node_modules/cpu-features/deps/cpu_features/include/cpu_features_cache_info.h +54 -0
  105. package/node_modules/cpu-features/deps/cpu_features/include/cpu_features_macros.h +384 -0
  106. package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_aarch64.h +259 -0
  107. package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_arm.h +121 -0
  108. package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_mips.h +74 -0
  109. package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_ppc.h +149 -0
  110. package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_riscv.h +72 -0
  111. package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_s390x.h +108 -0
  112. package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_x86.h +288 -0
  113. package/node_modules/cpu-features/deps/cpu_features/include/internal/bit_utils.h +40 -0
  114. package/node_modules/cpu-features/deps/cpu_features/include/internal/cpuid_x86.h +37 -0
  115. package/node_modules/cpu-features/deps/cpu_features/include/internal/filesystem.h +39 -0
  116. package/node_modules/cpu-features/deps/cpu_features/include/internal/hwcaps.h +240 -0
  117. package/node_modules/cpu-features/deps/cpu_features/include/internal/stack_line_reader.h +49 -0
  118. package/node_modules/cpu-features/deps/cpu_features/include/internal/string_view.h +110 -0
  119. package/node_modules/cpu-features/deps/cpu_features/include/internal/windows_utils.h +70 -0
  120. package/node_modules/cpu-features/deps/cpu_features/ndk_compat/CMakeLists.txt +60 -0
  121. package/node_modules/cpu-features/deps/cpu_features/ndk_compat/README.md +4 -0
  122. package/node_modules/cpu-features/deps/cpu_features/ndk_compat/cpu-features.c +205 -0
  123. package/node_modules/cpu-features/deps/cpu_features/ndk_compat/cpu-features.h +320 -0
  124. package/node_modules/cpu-features/deps/cpu_features/ndk_compat/ndk-compat-test.c +12 -0
  125. package/node_modules/cpu-features/deps/cpu_features/patches/0001-Add-Apple-Silicon-Support.patch +456 -0
  126. package/node_modules/cpu-features/deps/cpu_features/scripts/generate_badges.d +165 -0
  127. package/node_modules/cpu-features/deps/cpu_features/scripts/make_release.sh +75 -0
  128. package/node_modules/cpu-features/deps/cpu_features/scripts/run_integration.sh +490 -0
  129. package/node_modules/cpu-features/deps/cpu_features/scripts/test_integration.sh +79 -0
  130. package/node_modules/cpu-features/deps/cpu_features/src/copy.inl +19 -0
  131. package/node_modules/cpu-features/deps/cpu_features/src/define_introspection.inl +86 -0
  132. package/node_modules/cpu-features/deps/cpu_features/src/define_introspection_and_hwcaps.inl +26 -0
  133. package/node_modules/cpu-features/deps/cpu_features/src/equals.inl +22 -0
  134. package/node_modules/cpu-features/deps/cpu_features/src/filesystem.c +62 -0
  135. package/node_modules/cpu-features/deps/cpu_features/src/hwcaps.c +169 -0
  136. package/node_modules/cpu-features/deps/cpu_features/src/impl_aarch64__base_implementation.inl +88 -0
  137. package/node_modules/cpu-features/deps/cpu_features/src/impl_aarch64_linux_or_android.c +79 -0
  138. package/node_modules/cpu-features/deps/cpu_features/src/impl_aarch64_macos_or_iphone.c +82 -0
  139. package/node_modules/cpu-features/deps/cpu_features/src/impl_aarch64_windows.c +138 -0
  140. package/node_modules/cpu-features/deps/cpu_features/src/impl_arm_linux_or_android.c +212 -0
  141. package/node_modules/cpu-features/deps/cpu_features/src/impl_mips_linux_or_android.c +93 -0
  142. package/node_modules/cpu-features/deps/cpu_features/src/impl_ppc_linux.c +163 -0
  143. package/node_modules/cpu-features/deps/cpu_features/src/impl_riscv_linux.c +111 -0
  144. package/node_modules/cpu-features/deps/cpu_features/src/impl_s390x_linux.c +120 -0
  145. package/node_modules/cpu-features/deps/cpu_features/src/impl_x86__base_implementation.inl +2055 -0
  146. package/node_modules/cpu-features/deps/cpu_features/src/impl_x86_freebsd.c +68 -0
  147. package/node_modules/cpu-features/deps/cpu_features/src/impl_x86_linux_or_android.c +58 -0
  148. package/node_modules/cpu-features/deps/cpu_features/src/impl_x86_macos.c +57 -0
  149. package/node_modules/cpu-features/deps/cpu_features/src/impl_x86_windows.c +58 -0
  150. package/node_modules/cpu-features/deps/cpu_features/src/stack_line_reader.c +132 -0
  151. package/node_modules/cpu-features/deps/cpu_features/src/string_view.c +192 -0
  152. package/node_modules/cpu-features/deps/cpu_features/src/utils/list_cpu_features.c +461 -0
  153. package/node_modules/cpu-features/deps/cpu_features/test/CMakeLists.txt +114 -0
  154. package/node_modules/cpu-features/deps/cpu_features/test/bit_utils_test.cc +53 -0
  155. package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_aarch64_test.cc +396 -0
  156. package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_arm_test.cc +384 -0
  157. package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_mips_test.cc +176 -0
  158. package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_ppc_test.cc +131 -0
  159. package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_riscv_test.cc +180 -0
  160. package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_s390x_test.cc +82 -0
  161. package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_x86_test.cc +1729 -0
  162. package/node_modules/cpu-features/deps/cpu_features/test/filesystem_for_testing.cc +103 -0
  163. package/node_modules/cpu-features/deps/cpu_features/test/filesystem_for_testing.h +61 -0
  164. package/node_modules/cpu-features/deps/cpu_features/test/hwcaps_for_testing.cc +52 -0
  165. package/node_modules/cpu-features/deps/cpu_features/test/hwcaps_for_testing.h +31 -0
  166. package/node_modules/cpu-features/deps/cpu_features/test/stack_line_reader_test.cc +132 -0
  167. package/node_modules/cpu-features/deps/cpu_features/test/string_view_test.cc +202 -0
  168. package/node_modules/cpu-features/lib/index.js +5 -0
  169. package/node_modules/cpu-features/package.json +40 -0
  170. package/node_modules/cpu-features/src/binding.cc +151 -0
  171. package/node_modules/cpu-features/test/test.js +12 -0
  172. package/node_modules/nan/.github/workflows/ci.yml +46 -0
  173. package/node_modules/nan/.pre-commit-config.yaml +8 -0
  174. package/node_modules/nan/CHANGELOG.md +605 -0
  175. package/node_modules/nan/CMakeLists.txt +138 -0
  176. package/node_modules/nan/LICENSE.md +9 -0
  177. package/node_modules/nan/README.md +457 -0
  178. package/node_modules/nan/doc/asyncworker.md +146 -0
  179. package/node_modules/nan/doc/buffers.md +54 -0
  180. package/node_modules/nan/doc/callback.md +76 -0
  181. package/node_modules/nan/doc/converters.md +41 -0
  182. package/node_modules/nan/doc/errors.md +226 -0
  183. package/node_modules/nan/doc/json.md +62 -0
  184. package/node_modules/nan/doc/maybe_types.md +583 -0
  185. package/node_modules/nan/doc/methods.md +689 -0
  186. package/node_modules/nan/doc/new.md +147 -0
  187. package/node_modules/nan/doc/node_misc.md +123 -0
  188. package/node_modules/nan/doc/object_wrappers.md +263 -0
  189. package/node_modules/nan/doc/persistent.md +296 -0
  190. package/node_modules/nan/doc/scopes.md +73 -0
  191. package/node_modules/nan/doc/script.md +58 -0
  192. package/node_modules/nan/doc/string_bytes.md +81 -0
  193. package/node_modules/nan/doc/v8_internals.md +211 -0
  194. package/node_modules/nan/doc/v8_misc.md +85 -0
  195. package/node_modules/nan/include_dirs.js +1 -0
  196. package/node_modules/nan/nan.h +3202 -0
  197. package/node_modules/nan/nan_callbacks.h +141 -0
  198. package/node_modules/nan/nan_callbacks_12_inl.h +730 -0
  199. package/node_modules/nan/nan_callbacks_pre_12_inl.h +528 -0
  200. package/node_modules/nan/nan_converters.h +72 -0
  201. package/node_modules/nan/nan_converters_43_inl.h +68 -0
  202. package/node_modules/nan/nan_converters_pre_43_inl.h +42 -0
  203. package/node_modules/nan/nan_define_own_property_helper.h +29 -0
  204. package/node_modules/nan/nan_implementation_12_inl.h +446 -0
  205. package/node_modules/nan/nan_implementation_pre_12_inl.h +263 -0
  206. package/node_modules/nan/nan_json.h +166 -0
  207. package/node_modules/nan/nan_maybe_43_inl.h +360 -0
  208. package/node_modules/nan/nan_maybe_pre_43_inl.h +268 -0
  209. package/node_modules/nan/nan_new.h +340 -0
  210. package/node_modules/nan/nan_object_wrap.h +156 -0
  211. package/node_modules/nan/nan_persistent_12_inl.h +132 -0
  212. package/node_modules/nan/nan_persistent_pre_12_inl.h +242 -0
  213. package/node_modules/nan/nan_private.h +73 -0
  214. package/node_modules/nan/nan_scriptorigin.h +97 -0
  215. package/node_modules/nan/nan_string_bytes.h +305 -0
  216. package/node_modules/nan/nan_typedarray_contents.h +96 -0
  217. package/node_modules/nan/nan_weak.h +453 -0
  218. package/node_modules/nan/package.json +38 -0
  219. package/node_modules/nan/tools/1to2.js +412 -0
  220. package/node_modules/nan/tools/README.md +14 -0
  221. package/node_modules/nan/tools/package.json +19 -0
  222. package/node_modules/safer-buffer/LICENSE +21 -0
  223. package/node_modules/safer-buffer/Porting-Buffer.md +268 -0
  224. package/node_modules/safer-buffer/Readme.md +156 -0
  225. package/node_modules/safer-buffer/dangerous.js +58 -0
  226. package/node_modules/safer-buffer/package.json +34 -0
  227. package/node_modules/safer-buffer/safer.js +77 -0
  228. package/node_modules/safer-buffer/tests.js +406 -0
  229. package/node_modules/ssh2/.eslintignore +4 -0
  230. package/node_modules/ssh2/.eslintrc.js +5 -0
  231. package/node_modules/ssh2/.github/workflows/ci.yml +110 -0
  232. package/node_modules/ssh2/.github/workflows/lint.yml +27 -0
  233. package/node_modules/ssh2/LICENSE +19 -0
  234. package/node_modules/ssh2/README.md +1529 -0
  235. package/node_modules/ssh2/SFTP.md +413 -0
  236. package/node_modules/ssh2/examples/server-chat.js +238 -0
  237. package/node_modules/ssh2/examples/sftp-server-download-only.js +134 -0
  238. package/node_modules/ssh2/install.js +27 -0
  239. package/node_modules/ssh2/lib/Channel.js +295 -0
  240. package/node_modules/ssh2/lib/agent.js +1123 -0
  241. package/node_modules/ssh2/lib/client.js +2176 -0
  242. package/node_modules/ssh2/lib/http-agents.js +84 -0
  243. package/node_modules/ssh2/lib/index.js +44 -0
  244. package/node_modules/ssh2/lib/keygen.js +582 -0
  245. package/node_modules/ssh2/lib/protocol/Protocol.js +2136 -0
  246. package/node_modules/ssh2/lib/protocol/SFTP.js +4052 -0
  247. package/node_modules/ssh2/lib/protocol/constants.js +356 -0
  248. package/node_modules/ssh2/lib/protocol/crypto/binding.gyp +23 -0
  249. package/node_modules/ssh2/lib/protocol/crypto/poly1305.js +43 -0
  250. package/node_modules/ssh2/lib/protocol/crypto/src/binding.cc +2300 -0
  251. package/node_modules/ssh2/lib/protocol/crypto.js +1602 -0
  252. package/node_modules/ssh2/lib/protocol/handlers.js +16 -0
  253. package/node_modules/ssh2/lib/protocol/handlers.misc.js +1285 -0
  254. package/node_modules/ssh2/lib/protocol/kex.js +1908 -0
  255. package/node_modules/ssh2/lib/protocol/keyParser.js +1484 -0
  256. package/node_modules/ssh2/lib/protocol/node-fs-compat.js +115 -0
  257. package/node_modules/ssh2/lib/protocol/utils.js +356 -0
  258. package/node_modules/ssh2/lib/protocol/zlib.js +255 -0
  259. package/node_modules/ssh2/lib/server.js +1380 -0
  260. package/node_modules/ssh2/lib/utils.js +336 -0
  261. package/node_modules/ssh2/package.json +49 -0
  262. package/node_modules/ssh2/test/common.js +316 -0
  263. package/node_modules/ssh2/test/fixtures/bad_rsa_private_key +26 -0
  264. package/node_modules/ssh2/test/fixtures/https_cert.pem +33 -0
  265. package/node_modules/ssh2/test/fixtures/https_key.pem +52 -0
  266. package/node_modules/ssh2/test/fixtures/id_dsa +12 -0
  267. package/node_modules/ssh2/test/fixtures/id_ecdsa +5 -0
  268. package/node_modules/ssh2/test/fixtures/id_rsa +15 -0
  269. package/node_modules/ssh2/test/fixtures/id_rsa.ppk +26 -0
  270. package/node_modules/ssh2/test/fixtures/id_rsa_enc +30 -0
  271. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa +21 -0
  272. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.pub +1 -0
  273. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.pub.result +7 -0
  274. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.result +7 -0
  275. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc +22 -0
  276. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.pub +1 -0
  277. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.pub.result +7 -0
  278. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.result +7 -0
  279. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm +23 -0
  280. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.pub +1 -0
  281. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.pub.result +7 -0
  282. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.result +7 -0
  283. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa +9 -0
  284. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.pub +1 -0
  285. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.pub.result +7 -0
  286. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.result +7 -0
  287. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc +10 -0
  288. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.pub +1 -0
  289. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.pub.result +8 -0
  290. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.result +7 -0
  291. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm +10 -0
  292. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.pub +1 -0
  293. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.pub.result +8 -0
  294. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.result +7 -0
  295. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519 +7 -0
  296. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.pub +1 -0
  297. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.pub.result +7 -0
  298. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.result +7 -0
  299. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa +27 -0
  300. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.pub +1 -0
  301. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.pub.result +7 -0
  302. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.result +7 -0
  303. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc +28 -0
  304. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.pub +1 -0
  305. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.pub.result +8 -0
  306. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.result +7 -0
  307. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm +29 -0
  308. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.pub +1 -0
  309. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.pub.result +8 -0
  310. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.result +7 -0
  311. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa +12 -0
  312. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.pub +1 -0
  313. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.pub.result +7 -0
  314. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.result +7 -0
  315. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc +15 -0
  316. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.pub +1 -0
  317. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.pub.result +8 -0
  318. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.result +7 -0
  319. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa +5 -0
  320. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.pub +1 -0
  321. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.pub.result +7 -0
  322. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.result +7 -0
  323. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc +8 -0
  324. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.pub +1 -0
  325. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.pub.result +8 -0
  326. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.result +7 -0
  327. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa +27 -0
  328. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.pub +1 -0
  329. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.pub.result +7 -0
  330. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.result +7 -0
  331. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc +30 -0
  332. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.pub +1 -0
  333. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.pub.result +8 -0
  334. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.result +7 -0
  335. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256 +54 -0
  336. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.pub +1 -0
  337. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.pub.result +6 -0
  338. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.result +6 -0
  339. package/node_modules/ssh2/test/fixtures/keyParser/ppk_dsa_enc +17 -0
  340. package/node_modules/ssh2/test/fixtures/keyParser/ppk_dsa_enc.result +7 -0
  341. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa +26 -0
  342. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa.result +7 -0
  343. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa_enc +18 -0
  344. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa_enc.result +7 -0
  345. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa.pub +9 -0
  346. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa.pub.result +7 -0
  347. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa2.pub +10 -0
  348. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa2.pub.result +7 -0
  349. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa3.pub +11 -0
  350. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa3.pub.result +7 -0
  351. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa4.pub +11 -0
  352. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa4.pub.result +7 -0
  353. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa5.pub +8 -0
  354. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa5.pub.result +7 -0
  355. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa6.pub +13 -0
  356. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa6.pub.result +7 -0
  357. package/node_modules/ssh2/test/fixtures/openssh_new_rsa +27 -0
  358. package/node_modules/ssh2/test/fixtures/ssh_host_dsa_key +12 -0
  359. package/node_modules/ssh2/test/fixtures/ssh_host_ecdsa_key +5 -0
  360. package/node_modules/ssh2/test/fixtures/ssh_host_rsa_key +15 -0
  361. package/node_modules/ssh2/test/test-exec.js +578 -0
  362. package/node_modules/ssh2/test/test-integration-openssh.js +486 -0
  363. package/node_modules/ssh2/test/test-keygen.js +60 -0
  364. package/node_modules/ssh2/test/test-misc-client-server.js +1460 -0
  365. package/node_modules/ssh2/test/test-openssh.js +261 -0
  366. package/node_modules/ssh2/test/test-protocol-crypto.js +631 -0
  367. package/node_modules/ssh2/test/test-protocol-keyparser.js +177 -0
  368. package/node_modules/ssh2/test/test-server-hostkeys.js +138 -0
  369. package/node_modules/ssh2/test/test-sftp.js +842 -0
  370. package/node_modules/ssh2/test/test-shell.js +109 -0
  371. package/node_modules/ssh2/test/test-userauth-agent-openssh.js +110 -0
  372. package/node_modules/ssh2/test/test-userauth-agent.js +171 -0
  373. package/node_modules/ssh2/test/test-userauth.js +611 -0
  374. package/node_modules/ssh2/test/test-worker-imports.js +25 -0
  375. package/node_modules/ssh2/test/test.js +20 -0
  376. package/node_modules/ssh2/util/build_pagent.bat +2 -0
  377. package/node_modules/ssh2/util/pagent.c +88 -0
  378. package/node_modules/ssh2/util/pagent.exe +0 -0
  379. package/node_modules/tweetnacl/.npmignore +4 -0
  380. package/node_modules/tweetnacl/AUTHORS.md +28 -0
  381. package/node_modules/tweetnacl/CHANGELOG.md +221 -0
  382. package/node_modules/tweetnacl/LICENSE +24 -0
  383. package/node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md +20 -0
  384. package/node_modules/tweetnacl/README.md +459 -0
  385. package/node_modules/tweetnacl/nacl-fast.js +2388 -0
  386. package/node_modules/tweetnacl/nacl-fast.min.js +2 -0
  387. package/node_modules/tweetnacl/nacl.d.ts +98 -0
  388. package/node_modules/tweetnacl/nacl.js +1175 -0
  389. package/node_modules/tweetnacl/nacl.min.js +1 -0
  390. package/node_modules/tweetnacl/package.json +58 -0
  391. package/package.json +96 -89
package/lib/search.js CHANGED
@@ -1,230 +1,235 @@
1
- // dsh-remote — portable recursive search over SFTP.
2
- // Replaces `find … -exec grep` (which needs a POSIX shell and dies on
3
- // Windows remotes). Walks the remote tree over SFTP readdir/stat/readFile with
4
- // the same bounded-parallelism + ignore rules as the mirror sync.
5
- import { joinRemotePath, shq } from './paths.js'
6
-
7
- function mapLimit(items, limit, fn) {
8
- const limitN = Math.max(1, Math.min(Math.floor(limit) || 1, items.length))
9
- let next = 0
10
- const worker = async () => {
11
- while (next < items.length) {
12
- const i = next++
13
- try { await fn(items[i], i) } catch { /* per-item errors are the fn's concern */ }
14
- }
15
- }
16
- const workers = []
17
- for (let w = 0; w < limitN; w++) workers.push(worker())
18
- return Promise.all(workers)
19
- }
20
-
21
- /** fnmatch-lite for the `glob` filter (matches the entry BASENAME). */
22
- export function matchGlob(name, glob) {
23
- let re = ''
24
- let i = 0
25
- const g = String(glob || '')
26
- while (i < g.length) {
27
- const c = g[i]
28
- if (c === '*') {
29
- if (g[i + 1] === '*') { re += '.*'; i += 2; continue }
30
- re += '[^/]*'; i++; continue
31
- }
32
- if (c === '?') { re += '[^/]'; i++; continue }
33
- if ('\\^$+{}[]()|.'.includes(c)) re += '\\' + c
34
- else re += c
35
- i++
36
- }
37
- return new RegExp('^' + re + '$').test(String(name))
38
- }
39
-
40
- const BINARY_SNIFF = 8192
41
- function looksBinary(buf) {
42
- const n = Math.min(buf.length, BINARY_SNIFF)
43
- for (let i = 0; i < n; i++) if (buf[i] === 0) return true
44
- return false
45
- }
46
-
47
- /**
48
- * Recursively search a remote directory.
49
- *
50
- * Cancellation (issue #44): the walk takes an AbortSignal and checks it at every
51
- * directory and every entry. Before this it was completely uninterruptible —
52
- * no signal was accepted and no flag was consulted — so a large-tree search
53
- * could never be stopped: DSH tool cancellation is *cooperative*
54
- * (@deepseek-ai/dsh-tool-call-timeout-policy can only ask a tool to stop), which
55
- * meant `session/cancel`, steering, and the tool timeout policy all had no
56
- * effect and the turn stayed `running` forever. On abort we return the partial
57
- * result instead of throwing, so the model still gets whatever was found.
58
- *
59
- * @param {object} sftp - pooled SFTP client
60
- * @param {string} rootDir
61
- * @param {object} opts - `signal` (AbortSignal), `maxDurationMs`, plus the caps below
62
- * @returns {Promise<{matches: Array<{path, line, text}>, scanned: number, truncated: boolean, cancelled?: boolean}>}
63
- */
64
- export async function searchTree(sftp, rootDir, opts = {}) {
65
- const {
66
- regex, // RegExp (already compiled with ignoreCase where asked)
67
- glob, // optional basename glob filter
68
- contextLines = 0,
69
- maxMatches = 500,
70
- maxDepth = 12,
71
- maxFiles = 20000,
72
- maxScanBytes = 1024 * 1024,
73
- maxDurationMs = 0, // 0 = no wall-clock budget
74
- isIgnored = () => false,
75
- signal,
76
- } = opts
77
-
78
- const matches = []
79
- let scanned = 0
80
- let truncated = false
81
- let cancelled = false
82
- const startedAt = Date.now()
83
-
84
- /** True when the caller asked us to stop (abort, or the time budget is spent). */
85
- const shouldStop = () => {
86
- if (cancelled || truncated) return true
87
- if (signal && signal.aborted) { cancelled = true; return true }
88
- if (maxDurationMs > 0 && Date.now() - startedAt >= maxDurationMs) { truncated = true; return true }
89
- return false
90
- }
91
-
92
- const walk = async (dir, depth) => {
93
- if (shouldStop() || scanned >= maxFiles) return
94
- let entries = []
95
- try { entries = (await sftp.readdir(dir)) || [] } catch { return }
96
- const dirs = []
97
- for (const e of entries) {
98
- const name = String(e.filename)
99
- if (name === '.' || name === '..') continue
100
- const isDir = !!(e.attrs && e.attrs.isDirectory && e.attrs.isDirectory())
101
- if (isDir) dirs.push(name)
102
- }
103
- // Depth-first, dirs first, but stop as soon as we hit caps.
104
- for (const name of dirs) {
105
- if (shouldStop() || scanned >= maxFiles) return
106
- if (depth <= 0) continue
107
- const sub = joinRemotePath(dir, name)
108
- if (isIgnored(name, true)) continue
109
- await walk(sub, depth - 1)
110
- }
111
- if (shouldStop() || scanned >= maxFiles) return
112
-
113
- const fileNames = entries
114
- .filter((e) => !(e.attrs && e.attrs.isDirectory && e.attrs.isDirectory()))
115
- .map((e) => String(e.filename))
116
- .filter((n) => n !== '.' && n !== '..')
117
- await mapLimit(fileNames, 4, async (name) => {
118
- // Checked per entry: this is the loop that used to run for hours.
119
- if (shouldStop() || scanned >= maxFiles || matches.length >= maxMatches) return
120
- if (glob && !matchGlob(name, glob)) return
121
- if (isIgnored(name, false)) return
122
- const fp = joinRemotePath(dir, name)
123
- scanned++
124
- let st
125
- try { st = await sftp.stat(fp) } catch { return }
126
- if (shouldStop()) return
127
- if (st.size > maxScanBytes) return
128
- let buf
129
- try { buf = await sftp.readFile(fp) } catch { return }
130
- if (shouldStop()) return
131
- if (looksBinary(buf)) return
132
- const content = buf.toString('utf8').replace(/\r\n/g, '\n')
133
- const lines = content.split('\n')
134
- const hitLines = new Set()
135
- for (let i = 0; i < lines.length; i++) {
136
- if (regex.test(lines[i])) {
137
- regex.lastIndex = 0
138
- for (let c = Math.max(0, i - contextLines); c <= Math.min(lines.length - 1, i + contextLines); c++) {
139
- hitLines.add(c)
140
- }
141
- if (matches.length >= maxMatches) { truncated = true; return }
142
- }
143
- }
144
- if (hitLines.size) {
145
- for (const ln of [...hitLines].sort((a, b) => a - b)) {
146
- if (matches.length >= maxMatches) { truncated = true; return }
147
- matches.push({ path: fp, line: ln + 1, text: lines[ln] })
148
- }
149
- }
150
- })
151
- }
152
-
153
- await walk(rootDir, maxDepth)
154
- if (scanned >= maxFiles) truncated = true
155
- return { matches, scanned, truncated, cancelled }
156
- }
157
-
158
- function parseGrepLines(stdout, maxMatches) {
159
- const matches = []
160
- let truncated = false
161
- for (const line of String(stdout || '').split('\n')) {
162
- if (!line) continue
163
- const m = line.match(/^(.*?):(\d+):(.*)$/)
164
- if (!m) continue
165
- if (matches.length >= maxMatches) { truncated = true; break }
166
- matches.push({ path: m[1], line: Number(m[2]), text: m[3] })
167
- }
168
- return { matches, truncated }
169
- }
170
-
171
- /**
172
- * POSIX fast path: `rg` then `grep -R`. Returns null when the remote has
173
- * neither, so the caller can fall back to an SFTP walk (Windows / no grep).
174
- */
175
- export async function searchViaShell(pool, rootDir, opts = {}) {
176
- const {
177
- pattern,
178
- ignoreCase = true,
179
- glob,
180
- contextLines = 0,
181
- maxMatches = 500,
182
- signal,
183
- } = opts
184
- // Honour an already-aborted signal before spawning a remote grep on a huge tree.
185
- if (signal && signal.aborted) {
186
- return { matches: [], scanned: 0, truncated: false, cancelled: true, via: 'none' }
187
- }
188
- const ic = ignoreCase ? ' -i' : ''
189
- const globFlag = glob ? ` --glob ${shq(glob)}` : ''
190
- const ctx = contextLines > 0 ? ` -C ${Number(contextLines)}` : ''
191
- const rg = `rg -n --no-heading -I${ic}${globFlag}${ctx} --max-count ${maxMatches} -- ${shq(pattern)} ${shq(rootDir)}`
192
- const grepGlob = glob ? ` --include=${shq(glob)}` : ''
193
- const grep = `grep -R -n -I -E${ic}${grepGlob}${ctx} -- ${shq(pattern)} ${shq(rootDir)}`
194
- const script = `if command -v rg >/dev/null 2>&1; then ${rg}; elif command -v grep >/dev/null 2>&1; then ${grep}; else exit 127; fi`
195
- const res = await pool.exec(script, { timeoutMs: opts.timeoutMs, signal })
196
- // A cancelled call must return as cancelled (with whatever partial output the
197
- // remote produced), NOT throw: throwing would make searchRemote fall through
198
- // to the slower SFTP walk, i.e. keep running after the caller said stop.
199
- if (res.signal === 'ABORTED') {
200
- const partial = parseGrepLines(res.stdout, maxMatches)
201
- return { matches: partial.matches, scanned: partial.matches.length, truncated: false, cancelled: true, via: 'none' }
202
- }
203
- // 0 = hits, 1 = no hits; other codes mean the tools are missing / failed.
204
- if (res.signal === 'TIMEOUT') throw new Error('search timed out')
205
- if (res.code !== 0 && res.code !== 1) throw new Error(res.stderr || `search exit ${res.code}`)
206
- const parsed = parseGrepLines(res.stdout, maxMatches)
207
- return {
208
- matches: parsed.matches,
209
- scanned: parsed.matches.length,
210
- truncated: parsed.truncated || /truncated|max-count/i.test(String(res.stderr || '')),
211
- via: /rg/.test(String(res.stderr || '')) ? 'rg' : 'grep',
212
- }
213
- }
214
-
215
- /** Prefer a POSIX grep/rg, fall back to the portable SFTP walk. */
216
- export async function searchRemote(pool, rootDir, opts = {}) {
217
- if (pool && pool.platform !== 'windows') {
218
- try {
219
- const fast = await searchViaShell(pool, rootDir, opts)
220
- if (fast && Array.isArray(fast.matches)) return fast
221
- } catch (e) {
222
- // An abort is terminal, not a "try the slower fallback" signal.
223
- if (opts.signal && opts.signal.aborted) {
224
- return { matches: [], scanned: 0, truncated: false, cancelled: true, via: 'none' }
225
- }
226
- }
227
- }
228
- const sftp = await pool.sftp()
229
- return searchTree(sftp, rootDir, opts)
230
- }
1
+ // dsh-remote — portable recursive search over SFTP.
2
+ // Replaces `find … -exec grep` (which needs a POSIX shell and dies on
3
+ // Windows remotes). Walks the remote tree over SFTP readdir/stat/readFile with
4
+ // the same bounded-parallelism + ignore rules as the mirror sync.
5
+ import { joinRemotePath, relPathUnder, shq } from './paths.js'
6
+
7
+ function mapLimit(items, limit, fn) {
8
+ const limitN = Math.max(1, Math.min(Math.floor(limit) || 1, items.length))
9
+ let next = 0
10
+ const worker = async () => {
11
+ while (next < items.length) {
12
+ const i = next++
13
+ try { await fn(items[i], i) } catch { /* per-item errors are the fn's concern */ }
14
+ }
15
+ }
16
+ const workers = []
17
+ for (let w = 0; w < limitN; w++) workers.push(worker())
18
+ return Promise.all(workers)
19
+ }
20
+
21
+ /** fnmatch-lite for the `glob` filter (matches the entry BASENAME). */
22
+ export function matchGlob(name, glob) {
23
+ let re = ''
24
+ let i = 0
25
+ const g = String(glob || '')
26
+ while (i < g.length) {
27
+ const c = g[i]
28
+ if (c === '*') {
29
+ if (g[i + 1] === '*') { re += '.*'; i += 2; continue }
30
+ re += '[^/]*'; i++; continue
31
+ }
32
+ if (c === '?') { re += '[^/]'; i++; continue }
33
+ if ('\\^$+{}[]()|.'.includes(c)) re += '\\' + c
34
+ else re += c
35
+ i++
36
+ }
37
+ return new RegExp('^' + re + '$').test(String(name))
38
+ }
39
+
40
+ const BINARY_SNIFF = 8192
41
+ function looksBinary(buf) {
42
+ const n = Math.min(buf.length, BINARY_SNIFF)
43
+ for (let i = 0; i < n; i++) if (buf[i] === 0) return true
44
+ return false
45
+ }
46
+
47
+ /**
48
+ * Recursively search a remote directory.
49
+ *
50
+ * Cancellation (issue #44): the walk takes an AbortSignal and checks it at every
51
+ * directory and every entry. Before this it was completely uninterruptible —
52
+ * no signal was accepted and no flag was consulted — so a large-tree search
53
+ * could never be stopped: DSH tool cancellation is *cooperative*
54
+ * (@deepseek-ai/dsh-tool-call-timeout-policy can only ask a tool to stop), which
55
+ * meant `session/cancel`, steering, and the tool timeout policy all had no
56
+ * effect and the turn stayed `running` forever. On abort we return the partial
57
+ * result instead of throwing, so the model still gets whatever was found.
58
+ *
59
+ * @param {object} sftp - pooled SFTP client
60
+ * @param {string} rootDir
61
+ * @param {object} opts - `signal` (AbortSignal), `maxDurationMs`, plus the caps below
62
+ * @returns {Promise<{matches: Array<{path, line, text}>, scanned: number, truncated: boolean, cancelled?: boolean}>}
63
+ */
64
+ export async function searchTree(sftp, rootDir, opts = {}) {
65
+ const {
66
+ regex, // RegExp (already compiled with ignoreCase where asked)
67
+ glob, // optional basename glob filter
68
+ contextLines = 0,
69
+ maxMatches = 500,
70
+ maxDepth = 12,
71
+ maxFiles = 20000,
72
+ maxScanBytes = 1024 * 1024,
73
+ maxDurationMs = 0, // 0 = no wall-clock budget
74
+ isIgnored = () => false,
75
+ signal,
76
+ } = opts
77
+
78
+ const matches = []
79
+ let scanned = 0
80
+ let truncated = false
81
+ let cancelled = false
82
+ const startedAt = Date.now()
83
+
84
+ /** True when the caller asked us to stop (abort, or the time budget is spent). */
85
+ const shouldStop = () => {
86
+ if (cancelled || truncated) return true
87
+ if (signal && signal.aborted) { cancelled = true; return true }
88
+ if (maxDurationMs > 0 && Date.now() - startedAt >= maxDurationMs) { truncated = true; return true }
89
+ return false
90
+ }
91
+
92
+ const walk = async (dir, depth) => {
93
+ if (shouldStop() || scanned >= maxFiles) return
94
+ let entries = []
95
+ try { entries = (await sftp.readdir(dir)) || [] } catch { return }
96
+ const dirs = []
97
+ for (const e of entries) {
98
+ const name = String(e.filename)
99
+ if (name === '.' || name === '..') continue
100
+ const isDir = !!(e.attrs && e.attrs.isDirectory && e.attrs.isDirectory())
101
+ if (isDir) dirs.push(name)
102
+ }
103
+ // Depth-first, dirs first, but stop as soon as we hit caps.
104
+ for (const name of dirs) {
105
+ if (shouldStop() || scanned >= maxFiles) return
106
+ if (depth <= 0) continue
107
+ const sub = joinRemotePath(dir, name)
108
+ // isIgnored receives the path RELATIVE to the search root (the same
109
+ // contract sync has), so root-anchored ignore rules — `/proc/` when the
110
+ // workspace is the filesystem root (issue #60), `Library/Caches/` in the
111
+ // default search set — actually match here; a bare basename would never
112
+ // satisfy an anchored pattern and those rules were silently dead.
113
+ if (isIgnored(relPathUnder(rootDir, sub) || name, true)) continue
114
+ await walk(sub, depth - 1)
115
+ }
116
+ if (shouldStop() || scanned >= maxFiles) return
117
+
118
+ const fileNames = entries
119
+ .filter((e) => !(e.attrs && e.attrs.isDirectory && e.attrs.isDirectory()))
120
+ .map((e) => String(e.filename))
121
+ .filter((n) => n !== '.' && n !== '..')
122
+ await mapLimit(fileNames, 4, async (name) => {
123
+ // Checked per entry: this is the loop that used to run for hours.
124
+ if (shouldStop() || scanned >= maxFiles || matches.length >= maxMatches) return
125
+ if (glob && !matchGlob(name, glob)) return
126
+ const fp = joinRemotePath(dir, name)
127
+ if (isIgnored(relPathUnder(rootDir, fp) || name, false)) return
128
+ scanned++
129
+ let st
130
+ try { st = await sftp.stat(fp) } catch { return }
131
+ if (shouldStop()) return
132
+ if (st.size > maxScanBytes) return
133
+ let buf
134
+ try { buf = await sftp.readFile(fp) } catch { return }
135
+ if (shouldStop()) return
136
+ if (looksBinary(buf)) return
137
+ const content = buf.toString('utf8').replace(/\r\n/g, '\n')
138
+ const lines = content.split('\n')
139
+ const hitLines = new Set()
140
+ for (let i = 0; i < lines.length; i++) {
141
+ if (regex.test(lines[i])) {
142
+ regex.lastIndex = 0
143
+ for (let c = Math.max(0, i - contextLines); c <= Math.min(lines.length - 1, i + contextLines); c++) {
144
+ hitLines.add(c)
145
+ }
146
+ if (matches.length >= maxMatches) { truncated = true; return }
147
+ }
148
+ }
149
+ if (hitLines.size) {
150
+ for (const ln of [...hitLines].sort((a, b) => a - b)) {
151
+ if (matches.length >= maxMatches) { truncated = true; return }
152
+ matches.push({ path: fp, line: ln + 1, text: lines[ln] })
153
+ }
154
+ }
155
+ })
156
+ }
157
+
158
+ await walk(rootDir, maxDepth)
159
+ if (scanned >= maxFiles) truncated = true
160
+ return { matches, scanned, truncated, cancelled }
161
+ }
162
+
163
+ function parseGrepLines(stdout, maxMatches) {
164
+ const matches = []
165
+ let truncated = false
166
+ for (const line of String(stdout || '').split('\n')) {
167
+ if (!line) continue
168
+ const m = line.match(/^(.*?):(\d+):(.*)$/)
169
+ if (!m) continue
170
+ if (matches.length >= maxMatches) { truncated = true; break }
171
+ matches.push({ path: m[1], line: Number(m[2]), text: m[3] })
172
+ }
173
+ return { matches, truncated }
174
+ }
175
+
176
+ /**
177
+ * POSIX fast path: `rg` then `grep -R`. Returns null when the remote has
178
+ * neither, so the caller can fall back to an SFTP walk (Windows / no grep).
179
+ */
180
+ export async function searchViaShell(pool, rootDir, opts = {}) {
181
+ const {
182
+ pattern,
183
+ ignoreCase = true,
184
+ glob,
185
+ contextLines = 0,
186
+ maxMatches = 500,
187
+ signal,
188
+ } = opts
189
+ // Honour an already-aborted signal before spawning a remote grep on a huge tree.
190
+ if (signal && signal.aborted) {
191
+ return { matches: [], scanned: 0, truncated: false, cancelled: true, via: 'none' }
192
+ }
193
+ const ic = ignoreCase ? ' -i' : ''
194
+ const globFlag = glob ? ` --glob ${shq(glob)}` : ''
195
+ const ctx = contextLines > 0 ? ` -C ${Number(contextLines)}` : ''
196
+ const rg = `rg -n --no-heading -I${ic}${globFlag}${ctx} --max-count ${maxMatches} -- ${shq(pattern)} ${shq(rootDir)}`
197
+ const grepGlob = glob ? ` --include=${shq(glob)}` : ''
198
+ const grep = `grep -R -n -I -E${ic}${grepGlob}${ctx} -- ${shq(pattern)} ${shq(rootDir)}`
199
+ const script = `if command -v rg >/dev/null 2>&1; then ${rg}; elif command -v grep >/dev/null 2>&1; then ${grep}; else exit 127; fi`
200
+ const res = await pool.exec(script, { timeoutMs: opts.timeoutMs, signal })
201
+ // A cancelled call must return as cancelled (with whatever partial output the
202
+ // remote produced), NOT throw: throwing would make searchRemote fall through
203
+ // to the slower SFTP walk, i.e. keep running after the caller said stop.
204
+ if (res.signal === 'ABORTED') {
205
+ const partial = parseGrepLines(res.stdout, maxMatches)
206
+ return { matches: partial.matches, scanned: partial.matches.length, truncated: false, cancelled: true, via: 'none' }
207
+ }
208
+ // 0 = hits, 1 = no hits; other codes mean the tools are missing / failed.
209
+ if (res.signal === 'TIMEOUT') throw new Error('search timed out')
210
+ if (res.code !== 0 && res.code !== 1) throw new Error(res.stderr || `search exit ${res.code}`)
211
+ const parsed = parseGrepLines(res.stdout, maxMatches)
212
+ return {
213
+ matches: parsed.matches,
214
+ scanned: parsed.matches.length,
215
+ truncated: parsed.truncated || /truncated|max-count/i.test(String(res.stderr || '')),
216
+ via: /rg/.test(String(res.stderr || '')) ? 'rg' : 'grep',
217
+ }
218
+ }
219
+
220
+ /** Prefer a POSIX grep/rg, fall back to the portable SFTP walk. */
221
+ export async function searchRemote(pool, rootDir, opts = {}) {
222
+ if (pool && pool.platform !== 'windows') {
223
+ try {
224
+ const fast = await searchViaShell(pool, rootDir, opts)
225
+ if (fast && Array.isArray(fast.matches)) return fast
226
+ } catch (e) {
227
+ // An abort is terminal, not a "try the slower fallback" signal.
228
+ if (opts.signal && opts.signal.aborted) {
229
+ return { matches: [], scanned: 0, truncated: false, cancelled: true, via: 'none' }
230
+ }
231
+ }
232
+ }
233
+ const sftp = await pool.sftp()
234
+ return searchTree(sftp, rootDir, opts)
235
+ }
@@ -1,76 +1,76 @@
1
- // dsh-remote — the promisified SFTP surface every backend presents.
2
- //
3
- // Both SSH transports (the native ssh2 pool in lib/pool.js and the
4
- // system-OpenSSH pool in lib/openssh.js) must expose the SAME object to
5
- // callers: lib/sync.js, lib/search.js, lib/routes-fs.js, lib/remote-fs.js and
6
- // the rw_* tools all consume this exact shape. Keeping one implementation
7
- // matters more than it looks: the timeout, dead-channel and range-read rules
8
- // below encode failures that were expensive to find (issue #44), and a second
9
- // hand-copied version would drift away from the fixed one.
10
- //
11
- // The two rules that are easy to get wrong:
12
- // • A dead channel must fail FAST. ssh2's own EOF handling only rejects the
13
- // requests pending at that moment; every later request is registered and
14
- // never answered, so each waits out the full commandTimeoutMs. Over a tree
15
- // walk that is N × 2 × timeout — days on a large directory.
16
- // • `readPartial` exists so an oversized preview never downloads the whole
17
- // file to show its head.
18
- import { toSftpPath } from './paths.js'
19
-
20
- /**
21
- * Wrap a live SFTP channel (ssh2's SFTP instance) in the promisified surface
22
- * the plugin's callers expect.
23
- *
24
- * @param {object} sftp - an ssh2 SFTP channel (already negotiated/ready).
25
- * @param {object} opts
26
- * @param {number} opts.commandTimeoutMs - per-operation timeout.
27
- * @returns {object} promisified sftp surface.
28
- */
29
- export function buildSftpSurface(sftp, { commandTimeoutMs }) {
30
- // A dead channel must fail fast (see the header). ssh2's SFTP instance and the
31
- // system-OpenSSH engine both emit these, so one hook covers both backends.
32
- let dead = false
33
- const markDead = () => { dead = true }
34
- if (typeof sftp.on === 'function') {
35
- sftp.on('end', markDead)
36
- sftp.on('close', markDead)
37
- sftp.on('error', markDead)
38
- }
39
- const withTimeout = (fn) => (...args) =>
40
- new Promise((resolve, reject) => {
41
- if (dead) return reject(new Error('sftp channel closed'))
42
- const timer = setTimeout(() => reject(new Error('sftp operation timed out')), commandTimeoutMs)
43
- const finish = (err, value) => {
44
- clearTimeout(timer)
45
- err ? reject(err) : resolve(value)
46
- }
47
- try { fn(...args, finish) } catch (err) { clearTimeout(timer); reject(err) }
48
- })
49
- const P = (p) => toSftpPath(p)
50
- return {
51
- readdir: (dir) => withTimeout((d, cb) => sftp.readdir(d, cb))(P(dir)),
52
- stat: (p) => withTimeout((d, cb) => sftp.stat(d, cb))(P(p)),
53
- lstat: (p) => withTimeout((d, cb) => sftp.lstat(d, cb))(P(p)),
54
- mkdir: (dir) => withTimeout((d, cb) => sftp.mkdir(d, cb))(P(dir)),
55
- rmdir: (dir) => withTimeout((d, cb) => sftp.rmdir(d, cb))(P(dir)),
56
- unlink: (p) => withTimeout((d, cb) => sftp.unlink(d, cb))(P(p)),
57
- rename: (p, d) => withTimeout((a, b, cb) => sftp.rename(a, b, cb))(P(p), P(d)),
58
- realpath: (p) => withTimeout((d, cb) => sftp.realpath(d, cb))(P(p)),
59
- readFile: (p) => withTimeout((d, cb) => sftp.readFile(d, cb))(P(p)),
60
- writeFile: (p, data) => withTimeout((d, data2, cb) => sftp.writeFile(d, data2, cb))(P(p), data),
61
- fastGet: (p, lp) => withTimeout((d, l, cb) => sftp.fastGet(d, l, cb))(P(p), lp),
62
- fastPut: (lp, p) => withTimeout((l, d, cb) => sftp.fastPut(l, d, cb))(lp, P(p)),
63
- // Range read for oversized previews: never downloads the whole file.
64
- readPartial: (p, offset, length) => withTimeout((d, off, len, cb) => {
65
- sftp.open(d, 'r', (e, handle) => {
66
- if (e) return cb(e)
67
- const buf = Buffer.alloc(len)
68
- sftp.read(handle, buf, 0, len, off, (e2, bytesRead, data) => {
69
- sftp.close(handle, () => {})
70
- if (e2) return cb(e2)
71
- cb(null, Buffer.isBuffer(data) ? data.subarray(0, bytesRead) : buf.subarray(0, bytesRead))
72
- })
73
- })
74
- })(P(p), offset, length),
75
- }
76
- }
1
+ // dsh-remote — the promisified SFTP surface every backend presents.
2
+ //
3
+ // Both SSH transports (the native ssh2 pool in lib/pool.js and the
4
+ // system-OpenSSH pool in lib/openssh.js) must expose the SAME object to
5
+ // callers: lib/sync.js, lib/search.js, lib/routes-fs.js, lib/remote-fs.js and
6
+ // the rw_* tools all consume this exact shape. Keeping one implementation
7
+ // matters more than it looks: the timeout, dead-channel and range-read rules
8
+ // below encode failures that were expensive to find (issue #44), and a second
9
+ // hand-copied version would drift away from the fixed one.
10
+ //
11
+ // The two rules that are easy to get wrong:
12
+ // • A dead channel must fail FAST. ssh2's own EOF handling only rejects the
13
+ // requests pending at that moment; every later request is registered and
14
+ // never answered, so each waits out the full commandTimeoutMs. Over a tree
15
+ // walk that is N × 2 × timeout — days on a large directory.
16
+ // • `readPartial` exists so an oversized preview never downloads the whole
17
+ // file to show its head.
18
+ import { toSftpPath } from './paths.js'
19
+
20
+ /**
21
+ * Wrap a live SFTP channel (ssh2's SFTP instance) in the promisified surface
22
+ * the plugin's callers expect.
23
+ *
24
+ * @param {object} sftp - an ssh2 SFTP channel (already negotiated/ready).
25
+ * @param {object} opts
26
+ * @param {number} opts.commandTimeoutMs - per-operation timeout.
27
+ * @returns {object} promisified sftp surface.
28
+ */
29
+ export function buildSftpSurface(sftp, { commandTimeoutMs }) {
30
+ // A dead channel must fail fast (see the header). ssh2's SFTP instance and the
31
+ // system-OpenSSH engine both emit these, so one hook covers both backends.
32
+ let dead = false
33
+ const markDead = () => { dead = true }
34
+ if (typeof sftp.on === 'function') {
35
+ sftp.on('end', markDead)
36
+ sftp.on('close', markDead)
37
+ sftp.on('error', markDead)
38
+ }
39
+ const withTimeout = (fn) => (...args) =>
40
+ new Promise((resolve, reject) => {
41
+ if (dead) return reject(new Error('sftp channel closed'))
42
+ const timer = setTimeout(() => reject(new Error('sftp operation timed out')), commandTimeoutMs)
43
+ const finish = (err, value) => {
44
+ clearTimeout(timer)
45
+ err ? reject(err) : resolve(value)
46
+ }
47
+ try { fn(...args, finish) } catch (err) { clearTimeout(timer); reject(err) }
48
+ })
49
+ const P = (p) => toSftpPath(p)
50
+ return {
51
+ readdir: (dir) => withTimeout((d, cb) => sftp.readdir(d, cb))(P(dir)),
52
+ stat: (p) => withTimeout((d, cb) => sftp.stat(d, cb))(P(p)),
53
+ lstat: (p) => withTimeout((d, cb) => sftp.lstat(d, cb))(P(p)),
54
+ mkdir: (dir) => withTimeout((d, cb) => sftp.mkdir(d, cb))(P(dir)),
55
+ rmdir: (dir) => withTimeout((d, cb) => sftp.rmdir(d, cb))(P(dir)),
56
+ unlink: (p) => withTimeout((d, cb) => sftp.unlink(d, cb))(P(p)),
57
+ rename: (p, d) => withTimeout((a, b, cb) => sftp.rename(a, b, cb))(P(p), P(d)),
58
+ realpath: (p) => withTimeout((d, cb) => sftp.realpath(d, cb))(P(p)),
59
+ readFile: (p) => withTimeout((d, cb) => sftp.readFile(d, cb))(P(p)),
60
+ writeFile: (p, data) => withTimeout((d, data2, cb) => sftp.writeFile(d, data2, cb))(P(p), data),
61
+ fastGet: (p, lp) => withTimeout((d, l, cb) => sftp.fastGet(d, l, cb))(P(p), lp),
62
+ fastPut: (lp, p) => withTimeout((l, d, cb) => sftp.fastPut(l, d, cb))(lp, P(p)),
63
+ // Range read for oversized previews: never downloads the whole file.
64
+ readPartial: (p, offset, length) => withTimeout((d, off, len, cb) => {
65
+ sftp.open(d, 'r', (e, handle) => {
66
+ if (e) return cb(e)
67
+ const buf = Buffer.alloc(len)
68
+ sftp.read(handle, buf, 0, len, off, (e2, bytesRead, data) => {
69
+ sftp.close(handle, () => {})
70
+ if (e2) return cb(e2)
71
+ cb(null, Buffer.isBuffer(data) ? data.subarray(0, bytesRead) : buf.subarray(0, bytesRead))
72
+ })
73
+ })
74
+ })(P(p), offset, length),
75
+ }
76
+ }