@milaboratories/pl-deployments 2.15.17 → 2.15.19

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 (104) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +29 -0
  2. package/dist/common/os_and_arch.cjs +22 -28
  3. package/dist/common/os_and_arch.cjs.map +1 -1
  4. package/dist/common/os_and_arch.js +23 -26
  5. package/dist/common/os_and_arch.js.map +1 -1
  6. package/dist/common/pl_binary.cjs +35 -33
  7. package/dist/common/pl_binary.cjs.map +1 -1
  8. package/dist/common/pl_binary.d.ts +13 -18
  9. package/dist/common/pl_binary.js +33 -31
  10. package/dist/common/pl_binary.js.map +1 -1
  11. package/dist/common/pl_binary_download.cjs +143 -155
  12. package/dist/common/pl_binary_download.cjs.map +1 -1
  13. package/dist/common/pl_binary_download.d.ts +15 -48
  14. package/dist/common/pl_binary_download.js +138 -133
  15. package/dist/common/pl_binary_download.js.map +1 -1
  16. package/dist/common/pl_version.cjs +5 -6
  17. package/dist/common/pl_version.cjs.map +1 -1
  18. package/dist/common/pl_version.d.ts +4 -1
  19. package/dist/common/pl_version.js +5 -4
  20. package/dist/common/pl_version.js.map +1 -1
  21. package/dist/index.cjs +21 -25
  22. package/dist/index.d.ts +6 -6
  23. package/dist/index.js +7 -6
  24. package/dist/local/pid.cjs +14 -13
  25. package/dist/local/pid.cjs.map +1 -1
  26. package/dist/local/pid.js +11 -11
  27. package/dist/local/pid.js.map +1 -1
  28. package/dist/local/pl.cjs +194 -223
  29. package/dist/local/pl.cjs.map +1 -1
  30. package/dist/local/pl.d.ts +65 -65
  31. package/dist/local/pl.js +190 -202
  32. package/dist/local/pl.js.map +1 -1
  33. package/dist/local/process.cjs +37 -59
  34. package/dist/local/process.cjs.map +1 -1
  35. package/dist/local/process.d.ts +10 -10
  36. package/dist/local/process.js +36 -57
  37. package/dist/local/process.js.map +1 -1
  38. package/dist/local/trace.cjs +14 -17
  39. package/dist/local/trace.cjs.map +1 -1
  40. package/dist/local/trace.d.ts +6 -7
  41. package/dist/local/trace.js +15 -15
  42. package/dist/local/trace.js.map +1 -1
  43. package/dist/package.cjs +12 -0
  44. package/dist/package.cjs.map +1 -0
  45. package/dist/package.js +6 -0
  46. package/dist/package.js.map +1 -0
  47. package/dist/ssh/connection_info.cjs +36 -53
  48. package/dist/ssh/connection_info.cjs.map +1 -1
  49. package/dist/ssh/connection_info.d.ts +691 -713
  50. package/dist/ssh/connection_info.js +35 -51
  51. package/dist/ssh/connection_info.js.map +1 -1
  52. package/dist/ssh/pl.cjs +551 -638
  53. package/dist/ssh/pl.cjs.map +1 -1
  54. package/dist/ssh/pl.d.ts +120 -117
  55. package/dist/ssh/pl.js +548 -636
  56. package/dist/ssh/pl.js.map +1 -1
  57. package/dist/ssh/pl_paths.cjs +22 -24
  58. package/dist/ssh/pl_paths.cjs.map +1 -1
  59. package/dist/ssh/pl_paths.js +21 -19
  60. package/dist/ssh/pl_paths.js.map +1 -1
  61. package/dist/ssh/ssh.cjs +554 -618
  62. package/dist/ssh/ssh.cjs.map +1 -1
  63. package/dist/ssh/ssh.d.ts +139 -136
  64. package/dist/ssh/ssh.js +548 -616
  65. package/dist/ssh/ssh.js.map +1 -1
  66. package/dist/ssh/ssh_errors.cjs +45 -60
  67. package/dist/ssh/ssh_errors.cjs.map +1 -1
  68. package/dist/ssh/ssh_errors.js +45 -58
  69. package/dist/ssh/ssh_errors.js.map +1 -1
  70. package/dist/ssh/supervisord.cjs +50 -68
  71. package/dist/ssh/supervisord.cjs.map +1 -1
  72. package/dist/ssh/supervisord.d.ts +11 -21
  73. package/dist/ssh/supervisord.js +50 -66
  74. package/dist/ssh/supervisord.js.map +1 -1
  75. package/package.json +10 -10
  76. package/dist/common/os_and_arch.d.ts +0 -9
  77. package/dist/common/os_and_arch.d.ts.map +0 -1
  78. package/dist/common/pl_binary.d.ts.map +0 -1
  79. package/dist/common/pl_binary_download.d.ts.map +0 -1
  80. package/dist/common/pl_version.d.ts.map +0 -1
  81. package/dist/index.cjs.map +0 -1
  82. package/dist/index.d.ts.map +0 -1
  83. package/dist/index.js.map +0 -1
  84. package/dist/local/options.d.ts +0 -31
  85. package/dist/local/options.d.ts.map +0 -1
  86. package/dist/local/pid.d.ts +0 -4
  87. package/dist/local/pid.d.ts.map +0 -1
  88. package/dist/local/pl.d.ts.map +0 -1
  89. package/dist/local/process.d.ts.map +0 -1
  90. package/dist/local/trace.d.ts.map +0 -1
  91. package/dist/package.json.cjs +0 -8
  92. package/dist/package.json.cjs.map +0 -1
  93. package/dist/package.json.js +0 -6
  94. package/dist/package.json.js.map +0 -1
  95. package/dist/ssh/__tests__/common-utils.d.ts +0 -12
  96. package/dist/ssh/__tests__/common-utils.d.ts.map +0 -1
  97. package/dist/ssh/connection_info.d.ts.map +0 -1
  98. package/dist/ssh/pl.d.ts.map +0 -1
  99. package/dist/ssh/pl_paths.d.ts +0 -20
  100. package/dist/ssh/pl_paths.d.ts.map +0 -1
  101. package/dist/ssh/ssh.d.ts.map +0 -1
  102. package/dist/ssh/ssh_errors.d.ts +0 -29
  103. package/dist/ssh/ssh_errors.d.ts.map +0 -1
  104. package/dist/ssh/supervisord.d.ts.map +0 -1
package/dist/ssh/ssh.cjs CHANGED
@@ -1,627 +1,563 @@
1
- 'use strict';
2
-
3
- var ssh = require('ssh2');
4
- var net = require('node:net');
5
- var dns = require('node:dns');
6
- var fs = require('node:fs');
7
- var fsp = require('node:fs/promises');
8
- var upath = require('upath');
9
- var tsHelpers = require('@milaboratories/ts-helpers');
10
- var node_crypto = require('node:crypto');
11
- var ssh_errors = require('./ssh_errors.cjs');
1
+ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
2
+ const require_ssh_errors = require('./ssh_errors.cjs');
3
+ let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
4
+ let node_fs = require("node:fs");
5
+ node_fs = require_runtime.__toESM(node_fs);
6
+ let node_fs_promises = require("node:fs/promises");
7
+ let upath = require("upath");
8
+ upath = require_runtime.__toESM(upath);
9
+ let ssh2 = require("ssh2");
10
+ ssh2 = require_runtime.__toESM(ssh2);
11
+ let node_net = require("node:net");
12
+ node_net = require_runtime.__toESM(node_net);
13
+ let node_dns = require("node:dns");
14
+ node_dns = require_runtime.__toESM(node_dns);
15
+ let node_crypto = require("node:crypto");
12
16
 
17
+ //#region src/ssh/ssh.ts
13
18
  const defaultConfig = {
14
- keepaliveInterval: 60000,
15
- keepaliveCountMax: 10,
19
+ keepaliveInterval: 6e4,
20
+ keepaliveCountMax: 10
21
+ };
22
+ var SshClient = class SshClient {
23
+ config;
24
+ homeDir;
25
+ forwardedServers = [];
26
+ constructor(logger, client) {
27
+ this.logger = logger;
28
+ this.client = client;
29
+ }
30
+ /**
31
+ * Initializes the SshClient and establishes a connection using the provided configuration.
32
+ * @param config - The connection configuration object for the SSH client.
33
+ * @returns A new instance of SshClient with an active connection.
34
+ */
35
+ static async init(logger, config) {
36
+ const withDefaults = {
37
+ ...defaultConfig,
38
+ ...config
39
+ };
40
+ const client = new SshClient(logger, new ssh2.Client());
41
+ await client.connect(withDefaults);
42
+ return client;
43
+ }
44
+ getForwardedServers() {
45
+ return this.forwardedServers;
46
+ }
47
+ getFullHostName() {
48
+ return `${this.config?.host}:${this.config?.port}`;
49
+ }
50
+ getUserName() {
51
+ return this.config?.username;
52
+ }
53
+ /**
54
+ * Connects to the SSH server using the specified configuration.
55
+ * @param config - The connection configuration object for the SSH client.
56
+ * @returns A promise that resolves when the connection is established or rejects on error.
57
+ */
58
+ async connect(config) {
59
+ this.config = config;
60
+ return await connect(this.client, config, () => {}, () => {});
61
+ }
62
+ /**
63
+ * Executes a command on the SSH server.
64
+ * @param command - The command to execute on the remote server.
65
+ * @returns A promise resolving with the command's stdout and stderr outputs.
66
+ */
67
+ async exec(command) {
68
+ return new Promise((resolve, reject) => {
69
+ this.client.exec(command, (err, stream) => {
70
+ if (err) return reject(/* @__PURE__ */ new Error(`ssh.exec: ${command}: ${err}`));
71
+ let stdout = "";
72
+ let stderr = "";
73
+ stream.on("close", (code) => {
74
+ if (code === 0) resolve({
75
+ stdout,
76
+ stderr
77
+ });
78
+ else reject(/* @__PURE__ */ new Error(`Command ${command} exited with code ${code}, stdout: ${stdout}, stderr: ${stderr}`));
79
+ }).on("data", (data) => {
80
+ stdout += data.toString();
81
+ }).stderr.on("data", (data) => {
82
+ stderr += data.toString();
83
+ });
84
+ });
85
+ });
86
+ }
87
+ /**
88
+ * Retrieves the supported authentication methods for a given host and port.
89
+ * @param host - The hostname or IP address of the server.
90
+ * @param port - The port number to connect to on the server.
91
+ * @returns 'publickey' | 'password'[] A promise resolving with a list of supported authentication methods.
92
+ */
93
+ static async getAuthTypes(host, port) {
94
+ return new Promise((resolve) => {
95
+ let stdout = "";
96
+ const conn = new ssh2.Client();
97
+ conn.on("ready", () => {
98
+ conn.end();
99
+ const types = this.extractAuthMethods(stdout);
100
+ resolve(types.length === 0 ? ["publickey", "password"] : types);
101
+ });
102
+ conn.on("error", () => {
103
+ conn.end();
104
+ resolve(["publickey", "password"]);
105
+ });
106
+ conn.connect({
107
+ host,
108
+ port,
109
+ username: (/* @__PURE__ */ new Date()).getTime().toString(),
110
+ debug: (err) => {
111
+ stdout += `${err}\n`;
112
+ }
113
+ });
114
+ });
115
+ }
116
+ /**
117
+ * Extracts authentication methods from debug logs.
118
+ * @param log - The debug log output containing authentication information.
119
+ * @returns An array of extracted authentication methods.
120
+ */
121
+ static extractAuthMethods(log) {
122
+ const match = log.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);
123
+ return match && match[1] ? match[1].split(",").map((method) => method.trim()) : [];
124
+ }
125
+ /**
126
+ * Sets up port forwarding between a remote port on the SSH server and a local port.
127
+ * A new connection is used for this operation instead of an existing one.
128
+ * @param ports - An object specifying the remote and local port configuration.
129
+ * @param config - Optional connection configuration for the SSH client.
130
+ * @returns { server: net.Server } A promise resolving with the created server instance.
131
+ */
132
+ async forwardPort(ports, config) {
133
+ const log = `ssh.forward:${ports.localPort}:${ports.remotePort}.id_${(0, node_crypto.randomBytes)(1).toString("hex")}`;
134
+ config = config ?? this.config;
135
+ const persistentClient = new _milaboratories_ts_helpers.RetryablePromise((p) => {
136
+ return new Promise((resolve, reject) => {
137
+ const client = new ssh2.Client();
138
+ client.on("ready", () => {
139
+ this.logger.info(`${log}.client.ready`);
140
+ resolve(client);
141
+ });
142
+ client.on("error", (err) => {
143
+ this.logger.info(`${log}.client.error: ${err}`);
144
+ p.reset();
145
+ reject(err);
146
+ });
147
+ client.on("close", () => {
148
+ this.logger.info(`${log}.client.closed`);
149
+ p.reset();
150
+ });
151
+ client.connect(config);
152
+ });
153
+ });
154
+ await persistentClient.ensure();
155
+ return new Promise((resolve, reject) => {
156
+ const server = node_net.default.createServer({ pauseOnConnect: true }, async (localSocket) => {
157
+ const sockLog = `${log}.sock_${(0, node_crypto.randomBytes)(1).toString("hex")}`;
158
+ let conn;
159
+ try {
160
+ conn = await persistentClient.ensure();
161
+ } catch (e) {
162
+ this.logger.info(`${sockLog}.persistentClient.catch: ${e}`);
163
+ localSocket.end();
164
+ return;
165
+ }
166
+ conn.setNoDelay(true);
167
+ localSocket.setNoDelay(true);
168
+ let stream;
169
+ try {
170
+ stream = await forwardOut(this.logger, conn, "127.0.0.1", 0, "127.0.0.1", ports.remotePort);
171
+ } catch (e) {
172
+ this.logger.error(`${sockLog}.forwardOut.err: ${e}`);
173
+ localSocket.end();
174
+ return;
175
+ }
176
+ localSocket.pipe(stream);
177
+ stream.pipe(localSocket);
178
+ localSocket.resume();
179
+ stream.on("error", (err) => {
180
+ this.logger.error(`${sockLog}.stream.error: ${err}`);
181
+ localSocket.end();
182
+ stream.end();
183
+ });
184
+ stream.on("close", () => {
185
+ localSocket.end();
186
+ stream.end();
187
+ });
188
+ localSocket.on("close", () => {
189
+ this.logger.info(`${sockLog}.localSocket: closed`);
190
+ localSocket.end();
191
+ stream.end();
192
+ });
193
+ });
194
+ server.listen(ports.localPort, "127.0.0.1", () => {
195
+ this.logger.info(`${log}.server: started listening`);
196
+ this.forwardedServers.push(server);
197
+ resolve({ server });
198
+ });
199
+ server.on("error", (err) => {
200
+ server.close();
201
+ reject(/* @__PURE__ */ new Error(`${log}.server: error: ${JSON.stringify(err)}`));
202
+ });
203
+ server.on("close", () => {
204
+ this.logger.info(`${log}.server: closed ${JSON.stringify(ports)}`);
205
+ this.forwardedServers = this.forwardedServers.filter((s) => s !== server);
206
+ });
207
+ });
208
+ }
209
+ closeForwardedPorts() {
210
+ this.logger.info("[SSH] Closing all forwarded ports...");
211
+ this.forwardedServers.forEach((server) => {
212
+ const rawAddress = server.address();
213
+ if (rawAddress && typeof rawAddress !== "string") {
214
+ const address = rawAddress;
215
+ this.logger.info(`[SSH] Closing port forward for server ${address.address}:${address.port}`);
216
+ }
217
+ server.close();
218
+ });
219
+ this.forwardedServers = [];
220
+ }
221
+ /**
222
+ * Checks if a specified host is available by performing a DNS lookup.
223
+ * @param hostname - The hostname or IP address to check.
224
+ * @returns A promise resolving with `true` if the host is reachable, otherwise `false`.
225
+ */
226
+ static async checkHostAvailability(hostname) {
227
+ return new Promise((resolve) => {
228
+ node_dns.default.lookup(hostname, (err) => {
229
+ resolve(!err);
230
+ });
231
+ });
232
+ }
233
+ /**
234
+ * Determines whether a private key requires a passphrase for use.
235
+ * @param privateKey - The private key content to check.
236
+ * @returns A promise resolving with `true` if a passphrase is required, otherwise `false`.
237
+ */
238
+ static async isPassphraseRequiredForKey(privateKey) {
239
+ return new Promise((resolve, reject) => {
240
+ try {
241
+ if (ssh2.default.utils.parseKey(privateKey) instanceof Error) resolve(true);
242
+ return resolve(false);
243
+ } catch (err) {
244
+ console.log("Error parsing privateKey");
245
+ reject(/* @__PURE__ */ new Error(`ssh.isPassphraseRequiredForKey: err ${err}`));
246
+ }
247
+ });
248
+ }
249
+ /**
250
+ * Uploads a local file to a remote server via SFTP.
251
+ * This function creates new SFTP connection
252
+ * @param localPath - The local file path.
253
+ * @param remotePath - The remote file path on the server.
254
+ * @returns A promise resolving with `true` if the file was successfully uploaded.
255
+ */
256
+ async uploadFile(localPath, remotePath) {
257
+ return await (0, _milaboratories_ts_helpers.retry)(async () => {
258
+ return await this.withSftp(async (sftp) => {
259
+ return new Promise((resolve, reject) => {
260
+ sftp.fastPut(localPath, remotePath, (err) => {
261
+ if (err) return reject(new require_ssh_errors.SFTPUploadError(err, localPath, remotePath));
262
+ resolve(true);
263
+ });
264
+ });
265
+ });
266
+ }, _milaboratories_ts_helpers.Retry3TimesWithDelay, (e) => require_ssh_errors.SFTPError.from(e)?.isGenericFailure ?? false);
267
+ }
268
+ async withSftp(callback) {
269
+ return new Promise((resolve, reject) => {
270
+ this.client.sftp((err, sftp) => {
271
+ if (err) return reject(new Error(`ssh.withSftp: sftp err: ${err}`, { cause: err }));
272
+ callback(sftp).then(resolve).catch((err) => {
273
+ reject(new Error(`ssh.withSftp.callback: err ${err}`, { cause: err }));
274
+ }).finally(() => {
275
+ sftp?.end();
276
+ });
277
+ });
278
+ });
279
+ }
280
+ async writeFileOnTheServer(remotePath, data, mode = 432) {
281
+ return this.withSftp(async (sftp) => {
282
+ return this.writeFile(sftp, remotePath, data, mode);
283
+ });
284
+ }
285
+ async getForderStructure(sftp, remotePath, data = {
286
+ files: [],
287
+ directories: []
288
+ }) {
289
+ return new Promise((resolve, reject) => {
290
+ sftp.readdir(remotePath, async (err, items) => {
291
+ if (err) return reject(err);
292
+ for (const item of items) {
293
+ const itemPath = `${remotePath}/${item.filename}`;
294
+ if (item.attrs.isDirectory()) {
295
+ data.directories.push(itemPath);
296
+ try {
297
+ await this.getForderStructure(sftp, itemPath, data);
298
+ } catch (error) {
299
+ return reject(error instanceof Error ? error : new Error(String(error)));
300
+ }
301
+ } else data.files.push(itemPath);
302
+ }
303
+ resolve(data);
304
+ });
305
+ });
306
+ }
307
+ rmdir(sftp, path) {
308
+ return new Promise((resolve, reject) => {
309
+ sftp.rmdir(path, (err) => err ? reject(err) : resolve(true));
310
+ });
311
+ }
312
+ unlink(sftp, path) {
313
+ return new Promise((resolve, reject) => {
314
+ sftp.unlink(path, (err) => err ? reject(err) : resolve(true));
315
+ });
316
+ }
317
+ async deleteFolder(path) {
318
+ return this.withSftp(async (sftp) => {
319
+ try {
320
+ const list = await this.getForderStructure(sftp, path);
321
+ this.logger.info(`ssh.deleteFolder list of files and directories`);
322
+ this.logger.info(`ssh.deleteFolder list of files: ${list.files}`);
323
+ this.logger.info(`ssh.deleteFolder list of directories: ${list.directories}`);
324
+ for (const filePath of list.files) {
325
+ this.logger.info(`ssh.deleteFolder unlink file ${filePath}`);
326
+ await this.unlink(sftp, filePath);
327
+ }
328
+ list.directories.sort((a, b) => b.length - a.length);
329
+ for (const directoryPath of list.directories) {
330
+ this.logger.info(`ssh.deleteFolder rmdir ${directoryPath}`);
331
+ await this.rmdir(sftp, directoryPath);
332
+ }
333
+ await this.rmdir(sftp, path);
334
+ return true;
335
+ } catch (e) {
336
+ this.logger.error(e);
337
+ const message = e instanceof Error ? e.message : "";
338
+ throw new Error(`ssh.deleteFolder: path: ${path}, message: ${message}`);
339
+ }
340
+ });
341
+ }
342
+ async readFile(remotePath) {
343
+ return this.withSftp(async (sftp) => {
344
+ return new Promise((resolve, reject) => {
345
+ sftp.readFile(remotePath, (err, buffer) => {
346
+ if (err) return reject(/* @__PURE__ */ new Error(`ssh.readFile: ${err}`));
347
+ resolve(buffer.toString());
348
+ });
349
+ });
350
+ });
351
+ }
352
+ async chmod(path, mode) {
353
+ return this.withSftp(async (sftp) => {
354
+ return new Promise((resolve, reject) => {
355
+ sftp.chmod(path, mode, (err) => {
356
+ if (err) return reject(/* @__PURE__ */ new Error(`ssh.chmod: ${err}, path: ${path}, mode: ${mode}`));
357
+ return resolve(void 0);
358
+ });
359
+ });
360
+ });
361
+ }
362
+ async checkFileExists(remotePath) {
363
+ return this.withSftp(async (sftp) => {
364
+ return new Promise((resolve, reject) => {
365
+ sftp.stat(remotePath, (err, stats) => {
366
+ if (err) {
367
+ if (err?.code === 2) return resolve(false);
368
+ return reject(/* @__PURE__ */ new Error(`ssh.checkFileExists: err ${err}`));
369
+ }
370
+ resolve(stats.isFile());
371
+ });
372
+ });
373
+ });
374
+ }
375
+ async checkPathExists(remotePath) {
376
+ return this.withSftp(async (sftp) => {
377
+ return new Promise((resolve, reject) => {
378
+ sftp.stat(remotePath, (err, stats) => {
379
+ if (err) {
380
+ if (err.code === 2) return resolve({
381
+ exists: false,
382
+ isFile: false,
383
+ isDirectory: false
384
+ });
385
+ return reject(/* @__PURE__ */ new Error(`ssh.checkPathExists: ${err}`));
386
+ }
387
+ resolve({
388
+ exists: true,
389
+ isFile: stats.isFile(),
390
+ isDirectory: stats.isDirectory()
391
+ });
392
+ });
393
+ });
394
+ });
395
+ }
396
+ async writeFile(sftp, remotePath, data, mode = 432) {
397
+ return new Promise((resolve, reject) => {
398
+ sftp.writeFile(remotePath, data, { mode }, (err) => {
399
+ if (err) return reject(/* @__PURE__ */ new Error(`ssh.writeFile: err ${err}, remotePath: ${remotePath}`));
400
+ resolve(true);
401
+ });
402
+ });
403
+ }
404
+ uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode = 432) {
405
+ return new Promise((resolve, reject) => {
406
+ (0, node_fs_promises.readFile)(localPath).then(async (result) => {
407
+ return this.writeFile(sftp, remotePath, result, mode).then(() => {
408
+ resolve(void 0);
409
+ }).catch((err) => {
410
+ const msg = `uploadFileUsingExistingSftp: ${err}`;
411
+ this.logger.error(msg);
412
+ reject(new Error(msg));
413
+ });
414
+ });
415
+ });
416
+ }
417
+ async __uploadDirectory(sftp, localDir, remoteDir, mode = 432) {
418
+ return new Promise((resolve, reject) => {
419
+ node_fs.default.readdir(localDir, async (err, files) => {
420
+ if (err) return reject(/* @__PURE__ */ new Error(`ssh.__uploadDir: err ${err}, localDir: ${localDir}, remoteDir: ${remoteDir}`));
421
+ try {
422
+ await this.__createRemoteDirectory(sftp, remoteDir);
423
+ for (const file of files) {
424
+ const localPath = upath.default.join(localDir, file);
425
+ const remotePath = `${remoteDir}/${file}`;
426
+ if (node_fs.default.lstatSync(localPath).isDirectory()) await this.__uploadDirectory(sftp, localPath, remotePath, mode);
427
+ else await this.uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode);
428
+ }
429
+ resolve();
430
+ } catch (err) {
431
+ const msg = `ssh.__uploadDir: catched err ${err}`;
432
+ this.logger.error(msg);
433
+ reject(new Error(msg));
434
+ }
435
+ });
436
+ });
437
+ }
438
+ /**
439
+ * Uploads a local directory and its contents (including subdirectories) to the remote server via SFTP.
440
+ * @param localDir - The path to the local directory to upload.
441
+ * @param remoteDir - The path to the remote directory on the server.
442
+ * @returns A promise that resolves when the directory and its contents are uploaded.
443
+ */
444
+ async uploadDirectory(localDir, remoteDir, mode = 432) {
445
+ return new Promise((resolve, reject) => {
446
+ this.withSftp(async (sftp) => {
447
+ try {
448
+ await this.__uploadDirectory(sftp, localDir, remoteDir, mode);
449
+ resolve();
450
+ } catch (e) {
451
+ reject(/* @__PURE__ */ new Error(`ssh.uploadDirectory: ${e}`));
452
+ }
453
+ });
454
+ });
455
+ }
456
+ /**
457
+ * Ensures that a remote directory and all its parent directories exist.
458
+ * @param sftp - The SFTP wrapper.
459
+ * @param remotePath - The path to the remote directory.
460
+ * @returns A promise that resolves when the directory is created.
461
+ */
462
+ __createRemoteDirectory(sftp, remotePath) {
463
+ return new Promise((resolve, reject) => {
464
+ const directories = remotePath.split("/");
465
+ let currentPath = "";
466
+ const createNext = (index) => {
467
+ if (index >= directories.length) return resolve();
468
+ currentPath += `${directories[index]}/`;
469
+ sftp.stat(currentPath, (err) => {
470
+ if (err) sftp.mkdir(currentPath, (err) => {
471
+ if (err) return reject(/* @__PURE__ */ new Error(`ssh.__createRemDir: err ${err}, remotePath: ${remotePath}`));
472
+ createNext(index + 1);
473
+ });
474
+ else createNext(index + 1);
475
+ });
476
+ };
477
+ createNext(0);
478
+ });
479
+ }
480
+ /**
481
+ * Ensures that a remote directory and all its parent directories exist.
482
+ * @param sftp - The SFTP wrapper.
483
+ * @param remotePath - The path to the remote directory.
484
+ * @returns A promise that resolves when the directory is created.
485
+ */
486
+ ensureRemoteDirCreated(remotePath, mode = 493) {
487
+ return this.withSftp(async (sftp) => {
488
+ const directories = remotePath.split("/");
489
+ let currentPath = "";
490
+ for (const directory of directories) {
491
+ currentPath += `${directory}/`;
492
+ try {
493
+ await new Promise((resolve, reject) => {
494
+ sftp.stat(currentPath, (err) => {
495
+ if (!err) return resolve();
496
+ sftp.mkdir(currentPath, { mode }, (err) => {
497
+ if (err) return reject(/* @__PURE__ */ new Error(`ssh.createRemoteDir: err ${err}, remotePath: ${remotePath}`));
498
+ resolve();
499
+ });
500
+ });
501
+ });
502
+ } catch (error) {
503
+ console.error(`Failed to create directory: ${currentPath}`, error);
504
+ throw error;
505
+ }
506
+ }
507
+ });
508
+ }
509
+ /**
510
+ * Downloads a file from the remote server to a local path via SFTP.
511
+ * @param remotePath - The remote file path on the server.
512
+ * @param localPath - The local file path to save the file.
513
+ * @returns A promise resolving with `true` if the file was successfully downloaded.
514
+ */
515
+ async downloadFile(remotePath, localPath) {
516
+ return this.withSftp(async (sftp) => {
517
+ return new Promise((resolve, reject) => {
518
+ sftp.fastGet(remotePath, localPath, (err) => {
519
+ if (err) return reject(/* @__PURE__ */ new Error(`ssh.downloadFile: err ${err}, remotePath: ${remotePath}, localPath: ${localPath}`));
520
+ resolve(true);
521
+ });
522
+ });
523
+ });
524
+ }
525
+ /**
526
+ * Closes the SSH client connection and forwarded ports.
527
+ */
528
+ close() {
529
+ this.closeForwardedPorts();
530
+ this.client.end();
531
+ }
16
532
  };
17
- class SshClient {
18
- logger;
19
- client;
20
- config;
21
- homeDir;
22
- forwardedServers = [];
23
- constructor(logger, client) {
24
- this.logger = logger;
25
- this.client = client;
26
- }
27
- /**
28
- * Initializes the SshClient and establishes a connection using the provided configuration.
29
- * @param config - The connection configuration object for the SSH client.
30
- * @returns A new instance of SshClient with an active connection.
31
- */
32
- static async init(logger, config) {
33
- const withDefaults = {
34
- ...defaultConfig,
35
- ...config,
36
- };
37
- const client = new SshClient(logger, new ssh.Client());
38
- await client.connect(withDefaults);
39
- return client;
40
- }
41
- getForwardedServers() {
42
- return this.forwardedServers;
43
- }
44
- getFullHostName() {
45
- return `${this.config?.host}:${this.config?.port}`;
46
- }
47
- getUserName() {
48
- return this.config?.username;
49
- }
50
- /**
51
- * Connects to the SSH server using the specified configuration.
52
- * @param config - The connection configuration object for the SSH client.
53
- * @returns A promise that resolves when the connection is established or rejects on error.
54
- */
55
- async connect(config) {
56
- this.config = config;
57
- return await connect(this.client, config);
58
- }
59
- /**
60
- * Executes a command on the SSH server.
61
- * @param command - The command to execute on the remote server.
62
- * @returns A promise resolving with the command's stdout and stderr outputs.
63
- */
64
- async exec(command) {
65
- return new Promise((resolve, reject) => {
66
- this.client.exec(command, (err, stream) => {
67
- if (err) {
68
- return reject(new Error(`ssh.exec: ${command}: ${err}`));
69
- }
70
- let stdout = "";
71
- let stderr = "";
72
- stream
73
- .on("close", (code) => {
74
- if (code === 0) {
75
- resolve({ stdout, stderr });
76
- }
77
- else {
78
- reject(new Error(`Command ${command} exited with code ${code}, stdout: ${stdout}, stderr: ${stderr}`));
79
- }
80
- })
81
- .on("data", (data) => {
82
- stdout += data.toString();
83
- })
84
- .stderr.on("data", (data) => {
85
- stderr += data.toString();
86
- });
87
- });
88
- });
89
- }
90
- /**
91
- * Retrieves the supported authentication methods for a given host and port.
92
- * @param host - The hostname or IP address of the server.
93
- * @param port - The port number to connect to on the server.
94
- * @returns 'publickey' | 'password'[] A promise resolving with a list of supported authentication methods.
95
- */
96
- static async getAuthTypes(host, port) {
97
- return new Promise((resolve) => {
98
- let stdout = "";
99
- const conn = new ssh.Client();
100
- conn.on("ready", () => {
101
- conn.end();
102
- const types = this.extractAuthMethods(stdout);
103
- resolve(types.length === 0 ? ["publickey", "password"] : types);
104
- });
105
- conn.on("error", () => {
106
- conn.end();
107
- resolve(["publickey", "password"]);
108
- });
109
- conn.connect({
110
- host,
111
- port,
112
- username: new Date().getTime().toString(),
113
- debug: (err) => {
114
- stdout += `${err}\n`;
115
- },
116
- });
117
- });
118
- }
119
- /**
120
- * Extracts authentication methods from debug logs.
121
- * @param log - The debug log output containing authentication information.
122
- * @returns An array of extracted authentication methods.
123
- */
124
- static extractAuthMethods(log) {
125
- const match = log.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);
126
- return match && match[1] ? match[1].split(",").map((method) => method.trim()) : [];
127
- }
128
- /**
129
- * Sets up port forwarding between a remote port on the SSH server and a local port.
130
- * A new connection is used for this operation instead of an existing one.
131
- * @param ports - An object specifying the remote and local port configuration.
132
- * @param config - Optional connection configuration for the SSH client.
133
- * @returns { server: net.Server } A promise resolving with the created server instance.
134
- */
135
- async forwardPort(ports, config) {
136
- const log = `ssh.forward:${ports.localPort}:${ports.remotePort}.id_${node_crypto.randomBytes(1).toString("hex")}`;
137
- config = config ?? this.config;
138
- // we make this thing persistent so that if the connection
139
- // drops (it happened in the past because of lots of errors and forwardOut opened channels),
140
- // we'll recreate it here.
141
- const persistentClient = new tsHelpers.RetryablePromise((p) => {
142
- return new Promise((resolve, reject) => {
143
- const client = new ssh.Client();
144
- client.on("ready", () => {
145
- this.logger.info(`${log}.client.ready`);
146
- resolve(client);
147
- });
148
- client.on("error", (err) => {
149
- this.logger.info(`${log}.client.error: ${err}`);
150
- p.reset();
151
- reject(err);
152
- });
153
- client.on("close", () => {
154
- this.logger.info(`${log}.client.closed`);
155
- p.reset();
156
- });
157
- client.connect(config);
158
- });
159
- });
160
- await persistentClient.ensure(); // warm up a connection
161
- return new Promise((resolve, reject) => {
162
- const server = net.createServer({ pauseOnConnect: true }, async (localSocket) => {
163
- const sockLog = `${log}.sock_${node_crypto.randomBytes(1).toString("hex")}`;
164
- // this.logger.info(`${sockLog}.localSocket: start connection`);
165
- let conn;
166
- try {
167
- conn = await persistentClient.ensure();
168
- }
169
- catch (e) {
170
- this.logger.info(`${sockLog}.persistentClient.catch: ${e}`);
171
- localSocket.end();
172
- return;
173
- }
174
- // Remove TCP buffering.
175
- // Although it means less throughput (bad), it also less latency (good).
176
- // It could help when we have
177
- // small messages like in our grpc transactions.
178
- // And it also could help when we have tcp forwarding to not buffer messages in the middle.
179
- conn.setNoDelay(true);
180
- localSocket.setNoDelay(true);
181
- let stream;
182
- try {
183
- stream = await forwardOut(this.logger, conn, "127.0.0.1", 0, "127.0.0.1", ports.remotePort);
184
- }
185
- catch (e) {
186
- this.logger.error(`${sockLog}.forwardOut.err: ${e}`);
187
- localSocket.end();
188
- return;
189
- }
190
- localSocket.pipe(stream);
191
- stream.pipe(localSocket);
192
- localSocket.resume();
193
- // this.logger.info(`${sockLog}.forwardOut: connected`);
194
- stream.on("error", (err) => {
195
- this.logger.error(`${sockLog}.stream.error: ${err}`);
196
- localSocket.end();
197
- stream.end();
198
- });
199
- stream.on("close", () => {
200
- // this.logger.info(`${sockLog}.stream.close: closed`);
201
- localSocket.end();
202
- stream.end();
203
- });
204
- localSocket.on("close", () => {
205
- this.logger.info(`${sockLog}.localSocket: closed`);
206
- localSocket.end();
207
- stream.end();
208
- });
209
- });
210
- server.listen(ports.localPort, "127.0.0.1", () => {
211
- this.logger.info(`${log}.server: started listening`);
212
- this.forwardedServers.push(server);
213
- resolve({ server });
214
- });
215
- server.on("error", (err) => {
216
- server.close();
217
- reject(new Error(`${log}.server: error: ${JSON.stringify(err)}`));
218
- });
219
- server.on("close", () => {
220
- this.logger.info(`${log}.server: closed ${JSON.stringify(ports)}`);
221
- this.forwardedServers = this.forwardedServers.filter((s) => s !== server);
222
- });
223
- });
224
- }
225
- closeForwardedPorts() {
226
- this.logger.info("[SSH] Closing all forwarded ports...");
227
- this.forwardedServers.forEach((server) => {
228
- const rawAddress = server.address();
229
- if (rawAddress && typeof rawAddress !== "string") {
230
- const address = rawAddress;
231
- this.logger.info(`[SSH] Closing port forward for server ${address.address}:${address.port}`);
232
- }
233
- server.close();
234
- });
235
- this.forwardedServers = [];
236
- }
237
- /**
238
- * Checks if a specified host is available by performing a DNS lookup.
239
- * @param hostname - The hostname or IP address to check.
240
- * @returns A promise resolving with `true` if the host is reachable, otherwise `false`.
241
- */
242
- static async checkHostAvailability(hostname) {
243
- return new Promise((resolve) => {
244
- dns.lookup(hostname, (err) => {
245
- resolve(!err);
246
- });
247
- });
248
- }
249
- /**
250
- * Determines whether a private key requires a passphrase for use.
251
- * @param privateKey - The private key content to check.
252
- * @returns A promise resolving with `true` if a passphrase is required, otherwise `false`.
253
- */
254
- static async isPassphraseRequiredForKey(privateKey) {
255
- return new Promise((resolve, reject) => {
256
- try {
257
- const keyOrError = ssh.utils.parseKey(privateKey);
258
- if (keyOrError instanceof Error) {
259
- resolve(true);
260
- }
261
- return resolve(false);
262
- }
263
- catch (err) {
264
- console.log("Error parsing privateKey");
265
- reject(new Error(`ssh.isPassphraseRequiredForKey: err ${err}`));
266
- }
267
- });
268
- }
269
- /**
270
- * Uploads a local file to a remote server via SFTP.
271
- * This function creates new SFTP connection
272
- * @param localPath - The local file path.
273
- * @param remotePath - The remote file path on the server.
274
- * @returns A promise resolving with `true` if the file was successfully uploaded.
275
- */
276
- async uploadFile(localPath, remotePath) {
277
- return await tsHelpers.retry(async () => {
278
- return await this.withSftp(async (sftp) => {
279
- return new Promise((resolve, reject) => {
280
- sftp.fastPut(localPath, remotePath, (err) => {
281
- if (err) {
282
- const newErr = new ssh_errors.SFTPUploadError(err, localPath, remotePath);
283
- return reject(newErr);
284
- }
285
- resolve(true);
286
- });
287
- });
288
- });
289
- }, tsHelpers.Retry3TimesWithDelay, (e) => ssh_errors.SFTPError.from(e)?.isGenericFailure ?? false);
290
- }
291
- async withSftp(callback) {
292
- return new Promise((resolve, reject) => {
293
- this.client.sftp((err, sftp) => {
294
- if (err) {
295
- return reject(new Error(`ssh.withSftp: sftp err: ${err}`, { cause: err }));
296
- }
297
- callback(sftp)
298
- .then(resolve)
299
- .catch((err) => {
300
- reject(new Error(`ssh.withSftp.callback: err ${err}`, { cause: err }));
301
- })
302
- .finally(() => {
303
- sftp?.end();
304
- });
305
- });
306
- });
307
- }
308
- async writeFileOnTheServer(remotePath, data, mode = 0o660) {
309
- return this.withSftp(async (sftp) => {
310
- return this.writeFile(sftp, remotePath, data, mode);
311
- });
312
- }
313
- async getForderStructure(sftp, remotePath, data = { files: [], directories: [] }) {
314
- return new Promise((resolve, reject) => {
315
- sftp.readdir(remotePath, async (err, items) => {
316
- if (err) {
317
- return reject(err);
318
- }
319
- for (const item of items) {
320
- const itemPath = `${remotePath}/${item.filename}`;
321
- if (item.attrs.isDirectory()) {
322
- data.directories.push(itemPath);
323
- try {
324
- await this.getForderStructure(sftp, itemPath, data);
325
- }
326
- catch (error) {
327
- return reject(error instanceof Error ? error : new Error(String(error)));
328
- }
329
- }
330
- else {
331
- data.files.push(itemPath);
332
- }
333
- }
334
- resolve(data);
335
- });
336
- });
337
- }
338
- rmdir(sftp, path) {
339
- return new Promise((resolve, reject) => {
340
- sftp.rmdir(path, (err) => (err ? reject(err) : resolve(true)));
341
- });
342
- }
343
- unlink(sftp, path) {
344
- return new Promise((resolve, reject) => {
345
- sftp.unlink(path, (err) => (err ? reject(err) : resolve(true)));
346
- });
347
- }
348
- async deleteFolder(path) {
349
- return this.withSftp(async (sftp) => {
350
- try {
351
- const list = await this.getForderStructure(sftp, path);
352
- this.logger.info(`ssh.deleteFolder list of files and directories`);
353
- this.logger.info(`ssh.deleteFolder list of files: ${list.files}`);
354
- this.logger.info(`ssh.deleteFolder list of directories: ${list.directories}`);
355
- for (const filePath of list.files) {
356
- this.logger.info(`ssh.deleteFolder unlink file ${filePath}`);
357
- await this.unlink(sftp, filePath);
358
- }
359
- list.directories.sort((a, b) => b.length - a.length);
360
- for (const directoryPath of list.directories) {
361
- this.logger.info(`ssh.deleteFolder rmdir ${directoryPath}`);
362
- await this.rmdir(sftp, directoryPath);
363
- }
364
- await this.rmdir(sftp, path);
365
- return true;
366
- }
367
- catch (e) {
368
- this.logger.error(e);
369
- const message = e instanceof Error ? e.message : "";
370
- throw new Error(`ssh.deleteFolder: path: ${path}, message: ${message}`);
371
- }
372
- });
373
- }
374
- async readFile(remotePath) {
375
- return this.withSftp(async (sftp) => {
376
- return new Promise((resolve, reject) => {
377
- sftp.readFile(remotePath, (err, buffer) => {
378
- if (err) {
379
- return reject(new Error(`ssh.readFile: ${err}`));
380
- }
381
- resolve(buffer.toString());
382
- });
383
- });
384
- });
385
- }
386
- async chmod(path, mode) {
387
- return this.withSftp(async (sftp) => {
388
- return new Promise((resolve, reject) => {
389
- sftp.chmod(path, mode, (err) => {
390
- if (err) {
391
- return reject(new Error(`ssh.chmod: ${err}, path: ${path}, mode: ${mode}`));
392
- }
393
- return resolve(undefined);
394
- });
395
- });
396
- });
397
- }
398
- async checkFileExists(remotePath) {
399
- return this.withSftp(async (sftp) => {
400
- return new Promise((resolve, reject) => {
401
- sftp.stat(remotePath, (err, stats) => {
402
- if (err) {
403
- if (err?.code === 2) {
404
- return resolve(false);
405
- }
406
- return reject(new Error(`ssh.checkFileExists: err ${err}`));
407
- }
408
- resolve(stats.isFile());
409
- });
410
- });
411
- });
412
- }
413
- async checkPathExists(remotePath) {
414
- return this.withSftp(async (sftp) => {
415
- return new Promise((resolve, reject) => {
416
- sftp.stat(remotePath, (err, stats) => {
417
- if (err) {
418
- if (err.code === 2) {
419
- return resolve({ exists: false, isFile: false, isDirectory: false });
420
- }
421
- return reject(new Error(`ssh.checkPathExists: ${err}`));
422
- }
423
- resolve({
424
- exists: true,
425
- isFile: stats.isFile(),
426
- isDirectory: stats.isDirectory(),
427
- });
428
- });
429
- });
430
- });
431
- }
432
- async writeFile(sftp, remotePath, data, mode = 0o660) {
433
- return new Promise((resolve, reject) => {
434
- sftp.writeFile(remotePath, data, { mode }, (err) => {
435
- if (err) {
436
- return reject(new Error(`ssh.writeFile: err ${err}, remotePath: ${remotePath}`));
437
- }
438
- resolve(true);
439
- });
440
- });
441
- }
442
- uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode = 0o660) {
443
- return new Promise((resolve, reject) => {
444
- void fsp.readFile(localPath).then(async (result) => {
445
- return this.writeFile(sftp, remotePath, result, mode)
446
- .then(() => {
447
- resolve(undefined);
448
- })
449
- .catch((err) => {
450
- const msg = `uploadFileUsingExistingSftp: ${err}`;
451
- this.logger.error(msg);
452
- reject(new Error(msg));
453
- });
454
- });
455
- });
456
- }
457
- async __uploadDirectory(sftp, localDir, remoteDir, mode = 0o660) {
458
- return new Promise((resolve, reject) => {
459
- fs.readdir(localDir, async (err, files) => {
460
- if (err) {
461
- return reject(new Error(`ssh.__uploadDir: err ${err}, localDir: ${localDir}, remoteDir: ${remoteDir}`));
462
- }
463
- try {
464
- await this.__createRemoteDirectory(sftp, remoteDir);
465
- for (const file of files) {
466
- const localPath = upath.join(localDir, file);
467
- const remotePath = `${remoteDir}/${file}`;
468
- if (fs.lstatSync(localPath).isDirectory()) {
469
- await this.__uploadDirectory(sftp, localPath, remotePath, mode);
470
- }
471
- else {
472
- await this.uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode);
473
- }
474
- }
475
- resolve();
476
- }
477
- catch (err) {
478
- const msg = `ssh.__uploadDir: catched err ${err}`;
479
- this.logger.error(msg);
480
- reject(new Error(msg));
481
- }
482
- });
483
- });
484
- }
485
- /**
486
- * Uploads a local directory and its contents (including subdirectories) to the remote server via SFTP.
487
- * @param localDir - The path to the local directory to upload.
488
- * @param remoteDir - The path to the remote directory on the server.
489
- * @returns A promise that resolves when the directory and its contents are uploaded.
490
- */
491
- async uploadDirectory(localDir, remoteDir, mode = 0o660) {
492
- return new Promise((resolve, reject) => {
493
- void this.withSftp(async (sftp) => {
494
- try {
495
- await this.__uploadDirectory(sftp, localDir, remoteDir, mode);
496
- resolve();
497
- }
498
- catch (e) {
499
- reject(new Error(`ssh.uploadDirectory: ${e}`));
500
- }
501
- });
502
- });
503
- }
504
- /**
505
- * Ensures that a remote directory and all its parent directories exist.
506
- * @param sftp - The SFTP wrapper.
507
- * @param remotePath - The path to the remote directory.
508
- * @returns A promise that resolves when the directory is created.
509
- */
510
- __createRemoteDirectory(sftp, remotePath) {
511
- return new Promise((resolve, reject) => {
512
- const directories = remotePath.split("/");
513
- let currentPath = "";
514
- const createNext = (index) => {
515
- if (index >= directories.length) {
516
- return resolve();
517
- }
518
- currentPath += `${directories[index]}/`;
519
- sftp.stat(currentPath, (err) => {
520
- if (err) {
521
- sftp.mkdir(currentPath, (err) => {
522
- if (err) {
523
- return reject(new Error(`ssh.__createRemDir: err ${err}, remotePath: ${remotePath}`));
524
- }
525
- createNext(index + 1);
526
- });
527
- }
528
- else {
529
- createNext(index + 1);
530
- }
531
- });
532
- };
533
- createNext(0);
534
- });
535
- }
536
- /**
537
- * Ensures that a remote directory and all its parent directories exist.
538
- * @param sftp - The SFTP wrapper.
539
- * @param remotePath - The path to the remote directory.
540
- * @returns A promise that resolves when the directory is created.
541
- */
542
- ensureRemoteDirCreated(remotePath, mode = 0o755) {
543
- return this.withSftp(async (sftp) => {
544
- const directories = remotePath.split("/");
545
- let currentPath = "";
546
- for (const directory of directories) {
547
- currentPath += `${directory}/`;
548
- try {
549
- await new Promise((resolve, reject) => {
550
- sftp.stat(currentPath, (err) => {
551
- if (!err)
552
- return resolve();
553
- sftp.mkdir(currentPath, { mode }, (err) => {
554
- if (err) {
555
- return reject(new Error(`ssh.createRemoteDir: err ${err}, remotePath: ${remotePath}`));
556
- }
557
- resolve();
558
- });
559
- });
560
- });
561
- }
562
- catch (error) {
563
- console.error(`Failed to create directory: ${currentPath}`, error);
564
- throw error;
565
- }
566
- }
567
- });
568
- }
569
- /**
570
- * Downloads a file from the remote server to a local path via SFTP.
571
- * @param remotePath - The remote file path on the server.
572
- * @param localPath - The local file path to save the file.
573
- * @returns A promise resolving with `true` if the file was successfully downloaded.
574
- */
575
- async downloadFile(remotePath, localPath) {
576
- return this.withSftp(async (sftp) => {
577
- return new Promise((resolve, reject) => {
578
- sftp.fastGet(remotePath, localPath, (err) => {
579
- if (err) {
580
- return reject(new Error(`ssh.downloadFile: err ${err}, remotePath: ${remotePath}, localPath: ${localPath}`));
581
- }
582
- resolve(true);
583
- });
584
- });
585
- });
586
- }
587
- /**
588
- * Closes the SSH client connection and forwarded ports.
589
- */
590
- close() {
591
- this.closeForwardedPorts();
592
- this.client.end();
593
- }
594
- }
595
533
  async function connect(client, config, onError, onClose) {
596
- return new Promise((resolve, reject) => {
597
- client.on("ready", () => {
598
- resolve(client);
599
- });
600
- client.on("error", (err) => {
601
- reject(new Error(`ssh.connect: ${err}`));
602
- });
603
- client.on("close", () => {
604
- });
605
- client.connect(config);
606
- // Remove TCP buffering.
607
- // Although it means less throughput (bad), it also means less latency (good).
608
- // It could help when we have
609
- // small messages like in our grpc transactions.
610
- // And it also could help when we have tcp forwarding to not buffer messages in the middle.
611
- client.setNoDelay(true);
612
- });
534
+ return new Promise((resolve, reject) => {
535
+ client.on("ready", () => {
536
+ resolve(client);
537
+ });
538
+ client.on("error", (err) => {
539
+ onError(err);
540
+ reject(/* @__PURE__ */ new Error(`ssh.connect: ${err}`));
541
+ });
542
+ client.on("close", () => {
543
+ onClose();
544
+ });
545
+ client.connect(config);
546
+ client.setNoDelay(true);
547
+ });
613
548
  }
614
549
  async function forwardOut(logger, conn, localHost, localPort, remoteHost, remotePort) {
615
- return new Promise((resolve, reject) => {
616
- conn.forwardOut(localHost, localPort, remoteHost, remotePort, (err, stream) => {
617
- if (err) {
618
- logger.error(`forwardOut.error: ${err}`);
619
- return reject(err);
620
- }
621
- return resolve(stream);
622
- });
623
- });
550
+ return new Promise((resolve, reject) => {
551
+ conn.forwardOut(localHost, localPort, remoteHost, remotePort, (err, stream) => {
552
+ if (err) {
553
+ logger.error(`forwardOut.error: ${err}`);
554
+ return reject(err);
555
+ }
556
+ return resolve(stream);
557
+ });
558
+ });
624
559
  }
625
560
 
561
+ //#endregion
626
562
  exports.SshClient = SshClient;
627
- //# sourceMappingURL=ssh.cjs.map
563
+ //# sourceMappingURL=ssh.cjs.map