dsh-remote 0.8.46 → 0.8.47

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 +156 -156
  15. package/lib/index.js +3771 -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 +230 -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 +95 -89
package/lib/credential.js CHANGED
@@ -1,177 +1,177 @@
1
- // dsh-remote — optional OS-keychain password storage.
2
- // Machines keep their password in the OS credential store instead of the
3
- // plaintext machines.json when the user enables "加密保存密码" per machine.
4
- // Every backend is best-effort: any failure resolves to {ok:false} and the
5
- // caller (persistPassword) falls back to plaintext + a user-visible warning
6
- // (the feature must never block connecting, and must never silently drop the
7
- // credential — issue #30).
8
- //
9
- // Backends:
10
- // darwin → `security` (login keychain, generic password)
11
- // win32 → DPAPI via PowerShell (CurrentUser scope), files under
12
- // $DSH_HOME/remote-workspaces/.secrets/
13
- // linux → `secret-tool` (libsecret / gnome-keyring), optional
14
- // else → unsupported (plain)
15
- import { execFile } from 'node:child_process'
16
- import { mkdirSync, writeFileSync, readFileSync, unlinkSync } from 'node:fs'
17
- import { join as joinPath } from 'node:path'
18
-
19
- const SERVICE = 'dsh-remote'
20
- const TIMEOUT = 8000
21
-
22
- // Windows PowerShell 5.1 does not preload System.Security, so
23
- // [System.Security.Cryptography.ProtectedData] is "not found" until the assembly
24
- // is added (issue #30: without this the whole DPAPI call failed and the caller
25
- // silently lost the password). `-ErrorAction SilentlyContinue` keeps the line
26
- // harmless on PowerShell 7, where the type is already available.
27
- const PS_SECURITY_ASSEMBLY = 'Add-Type -AssemblyName System.Security -ErrorAction SilentlyContinue;'
28
-
29
- function run(bin, args, input) {
30
- return new Promise((resolve, reject) => {
31
- execFile(bin, args, { timeout: TIMEOUT, maxBuffer: 1 << 20, ...(input != null ? { input } : {}) }, (err, stdout, stderr) => {
32
- if (err) return reject(err)
33
- resolve(String(stdout || '').trim())
34
- })
35
- })
36
- }
37
-
38
- export function platformBackend() {
39
- if (process.platform === 'darwin') return 'keychain'
40
- if (process.platform === 'win32') return 'windows'
41
- return 'secret' // linux (secret-tool may be absent → falls back)
42
- }
43
-
44
- function account(machineId) {
45
- // Only machine ids are stored; sanitize just in case.
46
- return String(machineId || '').replace(/[^a-zA-Z0-9._-]/g, '_')
47
- }
48
-
49
- /** Redact a failed command's diagnostics: execFile puts the FULL command line
50
- * (which carries the password as an argv) into `err.message`, and this string is
51
- * shown in the settings UI — so the secret must never survive it. */
52
- function safeReason(err, password) {
53
- const raw = [err && err.stderr, err && err.message, err && err.code]
54
- .filter(Boolean).map(String).join(' ')
55
- const secret = String(password == null ? '' : password)
56
- const scrubbed = secret ? raw.split(secret).join('***') : raw
57
- return scrubbed.replace(/\s+/g, ' ').trim().slice(0, 300)
58
- }
59
-
60
- /** PowerShell one-liner that DPAPI-protects a password (CurrentUser scope).
61
- * Exported because the issue #30 fix (the mandatory Add-Type preamble) is only
62
- * observable here — a Windows host cannot be exercised from the test suite. */
63
- export function dpapiProtectScript(password) {
64
- return PS_SECURITY_ASSEMBLY +
65
- `$p='${String(password).replace(/'/g, "''")}';` +
66
- `$s=[System.Security.Cryptography.ProtectedData]::Protect([Text.Encoding]::UTF8.GetBytes($p),$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);` +
67
- `[Convert]::ToBase64String($s)`
68
- }
69
-
70
- /** PowerShell one-liner that reverses dpapiProtectScript(). */
71
- export function dpapiUnprotectScript(b64) {
72
- return PS_SECURITY_ASSEMBLY +
73
- `$s=[Convert]::FromBase64String('${String(b64).replace(/'/g, "''")}');` +
74
- `[Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect($s,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser))`
75
- }
76
-
77
- /** Save a password to the OS store. Resolves {ok, backend, error} — ok:false on
78
- * any failure (caller falls back to plaintext). `error` is a password-free
79
- * diagnostic for the UI. */
80
- export async function saveSecret(machineId, password, secretsDir) {
81
- const acc = account(machineId)
82
- try {
83
- if (process.platform === 'darwin') {
84
- await run('security', ['add-generic-password', '-U', '-s', SERVICE, '-a', acc, '-w', String(password)])
85
- return { ok: true, backend: 'keychain', error: '' }
86
- }
87
- if (process.platform === 'win32') {
88
- // DPAPI-encrypt and store under the harness home.
89
- const script = dpapiProtectScript(password)
90
- const b64 = await run('powershell', ['-NoProfile', '-STA', '-Command', script])
91
- if (!b64) return { ok: false, backend: 'windows', error: 'DPAPI returned no ciphertext' }
92
- mkdirSync(secretsDir || '.', { recursive: true })
93
- writeFileSync(joinPath(secretsDir || '.', acc + '.bin'), b64, 'utf8')
94
- return { ok: true, backend: 'windows', error: '' }
95
- }
96
- if (process.platform === 'linux') {
97
- await run('secret-tool', ['store', '--label=' + SERVICE, 'service', SERVICE, 'account', acc], String(password) + '\n')
98
- return { ok: true, backend: 'secret', error: '' }
99
- }
100
- } catch (err) {
101
- return { ok: false, backend: platformBackend(), error: safeReason(err, password) || 'secret store write failed' }
102
- }
103
- return { ok: false, backend: platformBackend(), error: `unsupported platform: ${process.platform}` }
104
- }
105
-
106
- /** Decide where a machine password is persisted (issue #30).
107
- *
108
- * The contract the settings UI relies on: a password is NEVER dropped. When the
109
- * requested OS store fails (missing DPAPI assembly, locked keychain, absent
110
- * secret-tool), this falls back to plaintext in machines.json and reports
111
- * `warning: 'secret-store-failed'` so the caller can tell the user instead of
112
- * silently saving a credential-less machine.
113
- *
114
- * `save` is injectable so the fallback is unit-testable on any platform.
115
- *
116
- * @returns {Promise<{stored:'secret'|'plain'|'none', credentialBackend:string, password:string, warning:string, error:string}>}
117
- */
118
- export async function persistPassword({ backend, machineId, password, secretsDir, save = saveSecret }) {
119
- const pw = password == null ? '' : String(password)
120
- const want = backend && backend !== 'plain' ? backend : 'plain'
121
- if (!pw) return { stored: 'none', credentialBackend: want, password: '', warning: '', error: '' }
122
- if (want === 'plain') return { stored: 'plain', credentialBackend: 'plain', password: pw, warning: '', error: '' }
123
- let res
124
- try {
125
- res = await save(machineId, pw, secretsDir)
126
- } catch (err) {
127
- res = { ok: false, error: safeReason(err, pw) }
128
- }
129
- if (res && res.ok) {
130
- return { stored: 'secret', credentialBackend: res.backend || want, password: '', warning: '', error: '' }
131
- }
132
- return {
133
- stored: 'plain',
134
- credentialBackend: 'plain',
135
- password: pw,
136
- warning: 'secret-store-failed',
137
- error: String((res && res.error) || '') || 'secret store write failed',
138
- }
139
- }
140
-
141
- /** Fetch a stored password. Resolves string|null. */
142
- export async function getSecret(machineId, secretsDir) {
143
- const acc = account(machineId)
144
- try {
145
- if (process.platform === 'darwin') {
146
- return await run('security', ['find-generic-password', '-s', SERVICE, '-a', acc, '-w'])
147
- }
148
- if (process.platform === 'win32') {
149
- const b64 = readFileSync(joinPath(secretsDir || '.', acc + '.bin'), 'utf8').trim()
150
- if (!b64) return null
151
- const script = dpapiUnprotectScript(b64)
152
- return await run('powershell', ['-NoProfile', '-STA', '-Command', script])
153
- }
154
- if (process.platform === 'linux') {
155
- return await run('secret-tool', ['lookup', 'service', SERVICE, 'account', acc])
156
- }
157
- } catch { /* fall through */ }
158
- return null
159
- }
160
-
161
- /** Delete a stored password (idempotent). */
162
- export async function deleteSecret(machineId, secretsDir) {
163
- const acc = account(machineId)
164
- try {
165
- if (process.platform === 'darwin') {
166
- await run('security', ['delete-generic-password', '-s', SERVICE, '-a', acc]).catch(() => {})
167
- return
168
- }
169
- if (process.platform === 'win32') {
170
- try { unlinkSync(joinPath(secretsDir || '.', acc + '.bin')) } catch { /* absent */ }
171
- return
172
- }
173
- if (process.platform === 'linux') {
174
- await run('secret-tool', ['clear', 'service', SERVICE, 'account', acc]).catch(() => {})
175
- }
176
- } catch { /* best effort */ }
177
- }
1
+ // dsh-remote — optional OS-keychain password storage.
2
+ // Machines keep their password in the OS credential store instead of the
3
+ // plaintext machines.json when the user enables "加密保存密码" per machine.
4
+ // Every backend is best-effort: any failure resolves to {ok:false} and the
5
+ // caller (persistPassword) falls back to plaintext + a user-visible warning
6
+ // (the feature must never block connecting, and must never silently drop the
7
+ // credential — issue #30).
8
+ //
9
+ // Backends:
10
+ // darwin → `security` (login keychain, generic password)
11
+ // win32 → DPAPI via PowerShell (CurrentUser scope), files under
12
+ // $DSH_HOME/remote-workspaces/.secrets/
13
+ // linux → `secret-tool` (libsecret / gnome-keyring), optional
14
+ // else → unsupported (plain)
15
+ import { execFile } from 'node:child_process'
16
+ import { mkdirSync, writeFileSync, readFileSync, unlinkSync } from 'node:fs'
17
+ import { join as joinPath } from 'node:path'
18
+
19
+ const SERVICE = 'dsh-remote'
20
+ const TIMEOUT = 8000
21
+
22
+ // Windows PowerShell 5.1 does not preload System.Security, so
23
+ // [System.Security.Cryptography.ProtectedData] is "not found" until the assembly
24
+ // is added (issue #30: without this the whole DPAPI call failed and the caller
25
+ // silently lost the password). `-ErrorAction SilentlyContinue` keeps the line
26
+ // harmless on PowerShell 7, where the type is already available.
27
+ const PS_SECURITY_ASSEMBLY = 'Add-Type -AssemblyName System.Security -ErrorAction SilentlyContinue;'
28
+
29
+ function run(bin, args, input) {
30
+ return new Promise((resolve, reject) => {
31
+ execFile(bin, args, { timeout: TIMEOUT, maxBuffer: 1 << 20, ...(input != null ? { input } : {}) }, (err, stdout, stderr) => {
32
+ if (err) return reject(err)
33
+ resolve(String(stdout || '').trim())
34
+ })
35
+ })
36
+ }
37
+
38
+ export function platformBackend() {
39
+ if (process.platform === 'darwin') return 'keychain'
40
+ if (process.platform === 'win32') return 'windows'
41
+ return 'secret' // linux (secret-tool may be absent → falls back)
42
+ }
43
+
44
+ function account(machineId) {
45
+ // Only machine ids are stored; sanitize just in case.
46
+ return String(machineId || '').replace(/[^a-zA-Z0-9._-]/g, '_')
47
+ }
48
+
49
+ /** Redact a failed command's diagnostics: execFile puts the FULL command line
50
+ * (which carries the password as an argv) into `err.message`, and this string is
51
+ * shown in the settings UI — so the secret must never survive it. */
52
+ function safeReason(err, password) {
53
+ const raw = [err && err.stderr, err && err.message, err && err.code]
54
+ .filter(Boolean).map(String).join(' ')
55
+ const secret = String(password == null ? '' : password)
56
+ const scrubbed = secret ? raw.split(secret).join('***') : raw
57
+ return scrubbed.replace(/\s+/g, ' ').trim().slice(0, 300)
58
+ }
59
+
60
+ /** PowerShell one-liner that DPAPI-protects a password (CurrentUser scope).
61
+ * Exported because the issue #30 fix (the mandatory Add-Type preamble) is only
62
+ * observable here — a Windows host cannot be exercised from the test suite. */
63
+ export function dpapiProtectScript(password) {
64
+ return PS_SECURITY_ASSEMBLY +
65
+ `$p='${String(password).replace(/'/g, "''")}';` +
66
+ `$s=[System.Security.Cryptography.ProtectedData]::Protect([Text.Encoding]::UTF8.GetBytes($p),$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);` +
67
+ `[Convert]::ToBase64String($s)`
68
+ }
69
+
70
+ /** PowerShell one-liner that reverses dpapiProtectScript(). */
71
+ export function dpapiUnprotectScript(b64) {
72
+ return PS_SECURITY_ASSEMBLY +
73
+ `$s=[Convert]::FromBase64String('${String(b64).replace(/'/g, "''")}');` +
74
+ `[Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect($s,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser))`
75
+ }
76
+
77
+ /** Save a password to the OS store. Resolves {ok, backend, error} — ok:false on
78
+ * any failure (caller falls back to plaintext). `error` is a password-free
79
+ * diagnostic for the UI. */
80
+ export async function saveSecret(machineId, password, secretsDir) {
81
+ const acc = account(machineId)
82
+ try {
83
+ if (process.platform === 'darwin') {
84
+ await run('security', ['add-generic-password', '-U', '-s', SERVICE, '-a', acc, '-w', String(password)])
85
+ return { ok: true, backend: 'keychain', error: '' }
86
+ }
87
+ if (process.platform === 'win32') {
88
+ // DPAPI-encrypt and store under the harness home.
89
+ const script = dpapiProtectScript(password)
90
+ const b64 = await run('powershell', ['-NoProfile', '-STA', '-Command', script])
91
+ if (!b64) return { ok: false, backend: 'windows', error: 'DPAPI returned no ciphertext' }
92
+ mkdirSync(secretsDir || '.', { recursive: true })
93
+ writeFileSync(joinPath(secretsDir || '.', acc + '.bin'), b64, 'utf8')
94
+ return { ok: true, backend: 'windows', error: '' }
95
+ }
96
+ if (process.platform === 'linux') {
97
+ await run('secret-tool', ['store', '--label=' + SERVICE, 'service', SERVICE, 'account', acc], String(password) + '\n')
98
+ return { ok: true, backend: 'secret', error: '' }
99
+ }
100
+ } catch (err) {
101
+ return { ok: false, backend: platformBackend(), error: safeReason(err, password) || 'secret store write failed' }
102
+ }
103
+ return { ok: false, backend: platformBackend(), error: `unsupported platform: ${process.platform}` }
104
+ }
105
+
106
+ /** Decide where a machine password is persisted (issue #30).
107
+ *
108
+ * The contract the settings UI relies on: a password is NEVER dropped. When the
109
+ * requested OS store fails (missing DPAPI assembly, locked keychain, absent
110
+ * secret-tool), this falls back to plaintext in machines.json and reports
111
+ * `warning: 'secret-store-failed'` so the caller can tell the user instead of
112
+ * silently saving a credential-less machine.
113
+ *
114
+ * `save` is injectable so the fallback is unit-testable on any platform.
115
+ *
116
+ * @returns {Promise<{stored:'secret'|'plain'|'none', credentialBackend:string, password:string, warning:string, error:string}>}
117
+ */
118
+ export async function persistPassword({ backend, machineId, password, secretsDir, save = saveSecret }) {
119
+ const pw = password == null ? '' : String(password)
120
+ const want = backend && backend !== 'plain' ? backend : 'plain'
121
+ if (!pw) return { stored: 'none', credentialBackend: want, password: '', warning: '', error: '' }
122
+ if (want === 'plain') return { stored: 'plain', credentialBackend: 'plain', password: pw, warning: '', error: '' }
123
+ let res
124
+ try {
125
+ res = await save(machineId, pw, secretsDir)
126
+ } catch (err) {
127
+ res = { ok: false, error: safeReason(err, pw) }
128
+ }
129
+ if (res && res.ok) {
130
+ return { stored: 'secret', credentialBackend: res.backend || want, password: '', warning: '', error: '' }
131
+ }
132
+ return {
133
+ stored: 'plain',
134
+ credentialBackend: 'plain',
135
+ password: pw,
136
+ warning: 'secret-store-failed',
137
+ error: String((res && res.error) || '') || 'secret store write failed',
138
+ }
139
+ }
140
+
141
+ /** Fetch a stored password. Resolves string|null. */
142
+ export async function getSecret(machineId, secretsDir) {
143
+ const acc = account(machineId)
144
+ try {
145
+ if (process.platform === 'darwin') {
146
+ return await run('security', ['find-generic-password', '-s', SERVICE, '-a', acc, '-w'])
147
+ }
148
+ if (process.platform === 'win32') {
149
+ const b64 = readFileSync(joinPath(secretsDir || '.', acc + '.bin'), 'utf8').trim()
150
+ if (!b64) return null
151
+ const script = dpapiUnprotectScript(b64)
152
+ return await run('powershell', ['-NoProfile', '-STA', '-Command', script])
153
+ }
154
+ if (process.platform === 'linux') {
155
+ return await run('secret-tool', ['lookup', 'service', SERVICE, 'account', acc])
156
+ }
157
+ } catch { /* fall through */ }
158
+ return null
159
+ }
160
+
161
+ /** Delete a stored password (idempotent). */
162
+ export async function deleteSecret(machineId, secretsDir) {
163
+ const acc = account(machineId)
164
+ try {
165
+ if (process.platform === 'darwin') {
166
+ await run('security', ['delete-generic-password', '-s', SERVICE, '-a', acc]).catch(() => {})
167
+ return
168
+ }
169
+ if (process.platform === 'win32') {
170
+ try { unlinkSync(joinPath(secretsDir || '.', acc + '.bin')) } catch { /* absent */ }
171
+ return
172
+ }
173
+ if (process.platform === 'linux') {
174
+ await run('secret-tool', ['clear', 'service', SERVICE, 'account', acc]).catch(() => {})
175
+ }
176
+ } catch { /* best effort */ }
177
+ }
@@ -1,142 +1,142 @@
1
- // dsh-remote — host-side deploy task manager.
2
- //
3
- // WHY THIS EXISTS: `install` used to be a single long HTTP request (npm can
4
- // legitimately take minutes). If the browser tab closed — or the fetch was
5
- // cancelled — the RESPONSE was lost and, with it, every trace of what the
6
- // deploy did: the user could not see the progress again, could not tell whether
7
- // it succeeded, and clicking the button again would start a SECOND concurrent
8
- // npm install into the same prefix. High availability here means:
9
- //
10
- // · the deploy runs on the HOST, not tied to any request lifecycle;
11
- // · every step's outcome is recorded as it happens;
12
- // · the UI can re-attach at any time and see the live state;
13
- // · a second click while one is running RE-ATTACHES instead of restarting;
14
- // · the result (including the machine's webAttachCommand) is recorded even
15
- // if no client ever polls for it.
16
- //
17
- // One deploy per machine at a time — npm --prefix into the same directory from
18
- // two processes corrupts the tree. A NEW request for a machine that already has
19
- // a running or finished deploy returns the EXISTING task (resume semantics);
20
- // `force: true` starts a fresh one after the previous is finished.
21
-
22
- /** A deploy task's lifecycle: queued → running → done | failed | cancelled. */
23
- export class DeployTasks {
24
- constructor() {
25
- /** @type {Map<string, object>} machineId → task */
26
- this.byMachine = new Map()
27
- /** @type {Map<string, object>} taskId → task (for stable polling) */
28
- this.byId = new Map()
29
- this.nextId = 1
30
- }
31
-
32
- /**
33
- * The task for a machine, if one exists in ANY state.
34
- * @returns {object|null}
35
- */
36
- forMachine(machineId) {
37
- return this.byMachine.get(String(machineId || '')) || null
38
- }
39
-
40
- get(id) {
41
- return this.byId.get(String(id || '')) || null
42
- }
43
-
44
- /**
45
- * Start a deploy for a machine, unless one is already running.
46
- *
47
- * @param {string} machineId
48
- * @param {Function} fn - async (ctx) => result. ctx = { progress(patch) }.
49
- * @returns {{task: object, reused: boolean}} the task plus whether it already
50
- * existed (caller reports "resumed" so the UI does not claim a fresh start).
51
- */
52
- start(machineId, label, fn) {
53
- const mid = String(machineId || '')
54
- const existing = this.byMachine.get(mid)
55
- if (existing && (existing.status === 'queued' || existing.status === 'running')) {
56
- return { task: existing, reused: true }
57
- }
58
- const id = `deploy-${this.nextId++}`
59
- const task = {
60
- id,
61
- machineId: mid,
62
- kind: 'deploy',
63
- label: String(label || ''),
64
- status: 'queued',
65
- // Steps are appended as they finish; `steps` is what the UI renders.
66
- steps: [],
67
- // The current in-flight step, so a re-attached UI can show "正在安装…"
68
- // rather than a stale list with no hint that more is coming.
69
- current: '',
70
- progress: {},
71
- result: null,
72
- error: null,
73
- startedAt: null,
74
- finishedAt: null,
75
- cancelled: false,
76
- }
77
- this.byMachine.set(mid, task)
78
- this.byId.set(id, task)
79
- // Deliberately NOT queued through the shared TaskManager: that one is
80
- // single-flight across ALL tasks (one sync at a time), while deploys to
81
- // DIFFERENT machines must run in parallel.
82
- task.status = 'running'
83
- task.startedAt = new Date().toISOString()
84
- const ctx = {
85
- progress: (p) => { task.progress = { ...task.progress, ...p } },
86
- get cancelled() { return !!task.cancelled },
87
- }
88
- Promise.resolve()
89
- .then(() => fn(ctx))
90
- .then((result) => {
91
- task.result = result || null
92
- task.status = task.cancelled ? 'cancelled' : 'done'
93
- })
94
- .catch((err) => {
95
- task.error = String((err && err.message) || err)
96
- // A step-level failure carries the step list (and hints) as structured
97
- // data so the UI can render exactly what the old single-response flow
98
- // rendered — re-attaching must lose nothing.
99
- if (err && err.detail && typeof err.detail === 'object') task.detail = err.detail
100
- task.status = task.cancelled ? 'cancelled' : 'failed'
101
- })
102
- .finally(() => {
103
- task.current = ''
104
- task.finishedAt = new Date().toISOString()
105
- })
106
- return { task, reused: false }
107
- }
108
-
109
- /** Cooperative cancel (a running npm step still finishes; later steps stop). */
110
- cancel(machineId) {
111
- const t = this.byMachine.get(String(machineId || ''))
112
- if (!t) return false
113
- if (t.status === 'queued' || t.status === 'running') {
114
- t.cancelled = true
115
- return true
116
- }
117
- return false
118
- }
119
-
120
- /** Serializable view for the UI. */
121
- describe(t) {
122
- if (!t) return null
123
- return {
124
- id: t.id,
125
- machineId: t.machineId,
126
- kind: t.kind,
127
- status: t.status, // queued | running | done | failed | cancelled
128
- steps: t.steps, // [{id,title,ok,code,output}]
129
- current: t.current, // in-flight step title ('' when none)
130
- progress: t.progress,
131
- result: t.result,
132
- error: t.error,
133
- detail: t.detail || null,
134
- startedAt: t.startedAt,
135
- finishedAt: t.finishedAt,
136
- }
137
- }
138
-
139
- list() {
140
- return [...this.byMachine.values()].map((t) => this.describe(t))
141
- }
142
- }
1
+ // dsh-remote — host-side deploy task manager.
2
+ //
3
+ // WHY THIS EXISTS: `install` used to be a single long HTTP request (npm can
4
+ // legitimately take minutes). If the browser tab closed — or the fetch was
5
+ // cancelled — the RESPONSE was lost and, with it, every trace of what the
6
+ // deploy did: the user could not see the progress again, could not tell whether
7
+ // it succeeded, and clicking the button again would start a SECOND concurrent
8
+ // npm install into the same prefix. High availability here means:
9
+ //
10
+ // · the deploy runs on the HOST, not tied to any request lifecycle;
11
+ // · every step's outcome is recorded as it happens;
12
+ // · the UI can re-attach at any time and see the live state;
13
+ // · a second click while one is running RE-ATTACHES instead of restarting;
14
+ // · the result (including the machine's webAttachCommand) is recorded even
15
+ // if no client ever polls for it.
16
+ //
17
+ // One deploy per machine at a time — npm --prefix into the same directory from
18
+ // two processes corrupts the tree. A NEW request for a machine that already has
19
+ // a running or finished deploy returns the EXISTING task (resume semantics);
20
+ // `force: true` starts a fresh one after the previous is finished.
21
+
22
+ /** A deploy task's lifecycle: queued → running → done | failed | cancelled. */
23
+ export class DeployTasks {
24
+ constructor() {
25
+ /** @type {Map<string, object>} machineId → task */
26
+ this.byMachine = new Map()
27
+ /** @type {Map<string, object>} taskId → task (for stable polling) */
28
+ this.byId = new Map()
29
+ this.nextId = 1
30
+ }
31
+
32
+ /**
33
+ * The task for a machine, if one exists in ANY state.
34
+ * @returns {object|null}
35
+ */
36
+ forMachine(machineId) {
37
+ return this.byMachine.get(String(machineId || '')) || null
38
+ }
39
+
40
+ get(id) {
41
+ return this.byId.get(String(id || '')) || null
42
+ }
43
+
44
+ /**
45
+ * Start a deploy for a machine, unless one is already running.
46
+ *
47
+ * @param {string} machineId
48
+ * @param {Function} fn - async (ctx) => result. ctx = { progress(patch) }.
49
+ * @returns {{task: object, reused: boolean}} the task plus whether it already
50
+ * existed (caller reports "resumed" so the UI does not claim a fresh start).
51
+ */
52
+ start(machineId, label, fn) {
53
+ const mid = String(machineId || '')
54
+ const existing = this.byMachine.get(mid)
55
+ if (existing && (existing.status === 'queued' || existing.status === 'running')) {
56
+ return { task: existing, reused: true }
57
+ }
58
+ const id = `deploy-${this.nextId++}`
59
+ const task = {
60
+ id,
61
+ machineId: mid,
62
+ kind: 'deploy',
63
+ label: String(label || ''),
64
+ status: 'queued',
65
+ // Steps are appended as they finish; `steps` is what the UI renders.
66
+ steps: [],
67
+ // The current in-flight step, so a re-attached UI can show "正在安装…"
68
+ // rather than a stale list with no hint that more is coming.
69
+ current: '',
70
+ progress: {},
71
+ result: null,
72
+ error: null,
73
+ startedAt: null,
74
+ finishedAt: null,
75
+ cancelled: false,
76
+ }
77
+ this.byMachine.set(mid, task)
78
+ this.byId.set(id, task)
79
+ // Deliberately NOT queued through the shared TaskManager: that one is
80
+ // single-flight across ALL tasks (one sync at a time), while deploys to
81
+ // DIFFERENT machines must run in parallel.
82
+ task.status = 'running'
83
+ task.startedAt = new Date().toISOString()
84
+ const ctx = {
85
+ progress: (p) => { task.progress = { ...task.progress, ...p } },
86
+ get cancelled() { return !!task.cancelled },
87
+ }
88
+ Promise.resolve()
89
+ .then(() => fn(ctx))
90
+ .then((result) => {
91
+ task.result = result || null
92
+ task.status = task.cancelled ? 'cancelled' : 'done'
93
+ })
94
+ .catch((err) => {
95
+ task.error = String((err && err.message) || err)
96
+ // A step-level failure carries the step list (and hints) as structured
97
+ // data so the UI can render exactly what the old single-response flow
98
+ // rendered — re-attaching must lose nothing.
99
+ if (err && err.detail && typeof err.detail === 'object') task.detail = err.detail
100
+ task.status = task.cancelled ? 'cancelled' : 'failed'
101
+ })
102
+ .finally(() => {
103
+ task.current = ''
104
+ task.finishedAt = new Date().toISOString()
105
+ })
106
+ return { task, reused: false }
107
+ }
108
+
109
+ /** Cooperative cancel (a running npm step still finishes; later steps stop). */
110
+ cancel(machineId) {
111
+ const t = this.byMachine.get(String(machineId || ''))
112
+ if (!t) return false
113
+ if (t.status === 'queued' || t.status === 'running') {
114
+ t.cancelled = true
115
+ return true
116
+ }
117
+ return false
118
+ }
119
+
120
+ /** Serializable view for the UI. */
121
+ describe(t) {
122
+ if (!t) return null
123
+ return {
124
+ id: t.id,
125
+ machineId: t.machineId,
126
+ kind: t.kind,
127
+ status: t.status, // queued | running | done | failed | cancelled
128
+ steps: t.steps, // [{id,title,ok,code,output}]
129
+ current: t.current, // in-flight step title ('' when none)
130
+ progress: t.progress,
131
+ result: t.result,
132
+ error: t.error,
133
+ detail: t.detail || null,
134
+ startedAt: t.startedAt,
135
+ finishedAt: t.finishedAt,
136
+ }
137
+ }
138
+
139
+ list() {
140
+ return [...this.byMachine.values()].map((t) => this.describe(t))
141
+ }
142
+ }