ssh-client-wrapper 2.12.4 → 2.13.0
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.
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/release.yml +29 -0
- package/.github/workflows/run_test.yml +2 -5
- package/.husky/_pre-commit +1 -0
- package/.husky/pre-commit +0 -3
- package/.releaserc.json +16 -0
- package/CHANGELOG.md +13 -0
- package/README.md +99 -0
- package/eslint.config.mjs +77 -102
- package/lib/fork.js +10 -14
- package/lib/index.js +23 -21
- package/lib/rsyncExec.js +13 -17
- package/lib/sshExec.js +15 -23
- package/lib/util.js +22 -36
- package/package.json +28 -25
- package/test/getSshOption.js +6 -6
- package/test/interface.js +30 -27
- package/test/rsyncExec.js +17 -17
- package/test/sanityCheck.js +6 -6
- package/test/sshExec.js +17 -13
- package/test/testUtil/hostInfo.js +1 -2
- package/test/testUtil/testFiles.js +16 -19
- package/.eslintrc.json +0 -34
- package/.github/workflows/npm_publish.yml +0 -22
- package/test/.eslintrc.json +0 -28
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "" # See documentation for possible values
|
|
9
|
+
directory: "/" # Location of package manifests
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Release and Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
uses: ./.github/workflows/run_test.yml
|
|
11
|
+
|
|
12
|
+
release:
|
|
13
|
+
needs: test
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
issues: write
|
|
18
|
+
pull-requests: write
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: 20
|
|
24
|
+
- run: npm ci
|
|
25
|
+
- name: Semantic Release
|
|
26
|
+
uses: cycjimmy/semantic-release-action@v4
|
|
27
|
+
env:
|
|
28
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
29
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
package/.husky/pre-commit
CHANGED
package/.releaserc.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": ["main"],
|
|
3
|
+
"plugins": [
|
|
4
|
+
"@semantic-release/commit-analyzer",
|
|
5
|
+
"@semantic-release/release-notes-generator",
|
|
6
|
+
"@semantic-release/changelog",
|
|
7
|
+
["@semantic-release/npm", {
|
|
8
|
+
"npmPublish": true
|
|
9
|
+
}],
|
|
10
|
+
["@semantic-release/git", {
|
|
11
|
+
"assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
|
|
12
|
+
"message": "chore(release): ${nextRelease.version} [skip ci]\\n\\n${nextRelease.notes}"
|
|
13
|
+
}],
|
|
14
|
+
"@semantic-release/github"
|
|
15
|
+
]
|
|
16
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# [2.13.0](https://github.com/so5/ssh-client-wrapper/compare/v2.12.4...v2.13.0) (2025-08-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* upgrade debug from 4.4.0 to 4.4.1 ([35f966a](https://github.com/so5/ssh-client-wrapper/commit/35f966a7212d6bb9677964688bb94ae06f869ada))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Improve DX and automate releases ([805f2ea](https://github.com/so5/ssh-client-wrapper/commit/805f2ea7756f6ad61238a4a21d86575a17e748d9))
|
|
12
|
+
* Rewrite in ESM ([c5a95c1](https://github.com/so5/ssh-client-wrapper/commit/c5a95c18401e7b373249d3707609f2a9576f7400))
|
|
13
|
+
* Rewrite in ESM ([71ed6f5](https://github.com/so5/ssh-client-wrapper/commit/71ed6f56af43cac1471cbf4a4ed3016f7e55ab4d))
|
package/README.md
CHANGED
|
@@ -4,3 +4,102 @@
|
|
|
4
4
|
[](https://codeclimate.com/github/so5/ssh-client-wrapper/maintainability)
|
|
5
5
|
# ssh-client-wrapper
|
|
6
6
|
open ssh client wrapper library for nodejs
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
```bash
|
|
10
|
+
npm install ssh-client-wrapper
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
Here is a simple example of how to use `ssh-client-wrapper` to execute a command on a remote server.
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import SshClientWrapper from 'ssh-client-wrapper';
|
|
18
|
+
|
|
19
|
+
const hostInfo = {
|
|
20
|
+
host: 'remote.server.com',
|
|
21
|
+
port: 22,
|
|
22
|
+
user: 'username',
|
|
23
|
+
password: 'password'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const ssh = new SshClientWrapper(hostInfo);
|
|
27
|
+
|
|
28
|
+
async function run() {
|
|
29
|
+
try {
|
|
30
|
+
await ssh.canConnect();
|
|
31
|
+
console.log('Connection successful!');
|
|
32
|
+
|
|
33
|
+
const { output, rt } = await ssh.execAndGetOutput('ls -l /home');
|
|
34
|
+
if (rt === 0) {
|
|
35
|
+
console.log('Directory listing:');
|
|
36
|
+
output.forEach(line => console.log(line));
|
|
37
|
+
} else {
|
|
38
|
+
console.error('Error executing command');
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error('Connection or command failed:', err);
|
|
42
|
+
} finally {
|
|
43
|
+
ssh.disconnect();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
run();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API Guide
|
|
51
|
+
|
|
52
|
+
### `new SshClientWrapper(hostInfo)`
|
|
53
|
+
Creates a new SSH client instance.
|
|
54
|
+
|
|
55
|
+
* `hostInfo` (Object): Connection details for the remote host.
|
|
56
|
+
* `host` (string): The hostname or IP address of the server.
|
|
57
|
+
* `user` (string): The username for authentication.
|
|
58
|
+
* `port` (number, optional): The port number. Defaults to 22.
|
|
59
|
+
* `password` (string | Function, optional): The password for authentication, or a function that returns the password.
|
|
60
|
+
* `keyFile` (string, optional): The path to the private key file for key-based authentication.
|
|
61
|
+
* `passphrase` (string | Function, optional): The passphrase for the private key, or a function that returns it.
|
|
62
|
+
* `noStrictHostKeyChecking` (boolean, optional): If `true`, bypasses strict host key checking.
|
|
63
|
+
* And more... see `lib/index.js` for all available options.
|
|
64
|
+
|
|
65
|
+
### `.exec(cmd, [timeout], [outputCallback], [rcfile], [prependCmd])`
|
|
66
|
+
Executes a command on the remote host.
|
|
67
|
+
|
|
68
|
+
* Returns: `Promise<number>` - The return code of the command.
|
|
69
|
+
|
|
70
|
+
### `.execAndGetOutput(cmd, [timeout], [rcfile], [prependCmd])`
|
|
71
|
+
Executes a command and returns its standard output.
|
|
72
|
+
|
|
73
|
+
* Returns: `Promise<{output: string[], rt: number}>` - An object containing the output as an array of strings and the return code.
|
|
74
|
+
|
|
75
|
+
### `.ls(target, [lsOpt], [timeout])`
|
|
76
|
+
Executes the `ls` command on the remote host.
|
|
77
|
+
|
|
78
|
+
* Returns: `Promise<string[]>` - The output of the `ls` command as an array of strings.
|
|
79
|
+
|
|
80
|
+
### `.expect(cmd, expects, [timeout])`
|
|
81
|
+
Executes a command and interacts with it, similar to the `expect` tool.
|
|
82
|
+
|
|
83
|
+
* Returns: `Promise<number>` - The return code of the command.
|
|
84
|
+
|
|
85
|
+
### `.send(src, dst, [opt], [timeout])`
|
|
86
|
+
Uploads files or directories to the remote host using `rsync`.
|
|
87
|
+
|
|
88
|
+
* `src` (string[]): An array of local paths to send.
|
|
89
|
+
* `dst` (string): The remote destination path.
|
|
90
|
+
* Returns: `Promise<void>`
|
|
91
|
+
|
|
92
|
+
### `.recv(src, dst, [opt], [timeout])`
|
|
93
|
+
Downloads files or directories from the remote host using `rsync`.
|
|
94
|
+
|
|
95
|
+
* `src` (string[]): An array of remote paths to receive.
|
|
96
|
+
* `dst` (string): The local destination path.
|
|
97
|
+
* Returns: `Promise<void>`
|
|
98
|
+
|
|
99
|
+
### `.canConnect([timeout])`
|
|
100
|
+
Checks if a connection to the remote host can be established.
|
|
101
|
+
|
|
102
|
+
* Returns: `Promise<boolean>` - Resolves with `true` on success.
|
|
103
|
+
|
|
104
|
+
### `.disconnect()`
|
|
105
|
+
Closes the master SSH connection to the remote host.
|
package/eslint.config.mjs
CHANGED
|
@@ -1,39 +1,25 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
import js from "@eslint/js";
|
|
3
2
|
import globals from "globals";
|
|
4
3
|
import stylistic from "@stylistic/eslint-plugin";
|
|
5
4
|
import jsdoc from "eslint-plugin-jsdoc";
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
const jsdocRules = {
|
|
10
|
-
"jsdoc/require-jsdoc": ["warn", { enableFixer: false }],
|
|
11
|
-
"jsdoc/require-param": ["warn", { enableFixer: false }],
|
|
12
|
-
"jsdoc/require-param-description": "warn",
|
|
13
|
-
"jsdoc/require-param-name": "warn",
|
|
14
|
-
"jsdoc/require-param-type": "warn",
|
|
15
|
-
"jsdoc/require-hyphen-before-param-description": [
|
|
16
|
-
"warn",
|
|
17
|
-
"always",
|
|
18
|
-
],
|
|
19
|
-
"jsdoc/require-returns": "warn",
|
|
20
|
-
"jsdoc/require-returns-check": "error",
|
|
21
|
-
};
|
|
5
|
+
import eslintPluginChaiFriendly from "eslint-plugin-chai-friendly";
|
|
6
|
+
import eslintPluginChaiExpect from "eslint-plugin-chai-expect";
|
|
22
7
|
|
|
23
8
|
const styleRules = {
|
|
9
|
+
"arrow-body-style": ["error", "always"],
|
|
24
10
|
"@stylistic/arrow-spacing": [
|
|
25
11
|
"error",
|
|
26
12
|
{
|
|
27
13
|
after: false,
|
|
28
|
-
before: false
|
|
14
|
+
before: false
|
|
29
15
|
}],
|
|
30
16
|
"@stylistic/comma-dangle": [
|
|
31
17
|
"error",
|
|
32
|
-
"never"
|
|
18
|
+
"never"
|
|
33
19
|
],
|
|
34
20
|
"@stylistic/spaced-comment": [
|
|
35
21
|
"error",
|
|
36
|
-
"never"
|
|
22
|
+
"never"
|
|
37
23
|
],
|
|
38
24
|
"@stylistic/lines-around-comment": [
|
|
39
25
|
"error",
|
|
@@ -42,32 +28,32 @@ const styleRules = {
|
|
|
42
28
|
afterBlockComment: false,
|
|
43
29
|
beforeLineComment: false,
|
|
44
30
|
afterLineComment: false,
|
|
45
|
-
allowBlockStart: true
|
|
46
|
-
}
|
|
31
|
+
allowBlockStart: true
|
|
32
|
+
}
|
|
47
33
|
],
|
|
48
34
|
"@stylistic/lines-between-class-members": [
|
|
49
35
|
"error",
|
|
50
36
|
"always",
|
|
51
37
|
{
|
|
52
|
-
exceptAfterSingleLine: true
|
|
53
|
-
}
|
|
38
|
+
exceptAfterSingleLine: true
|
|
39
|
+
}
|
|
54
40
|
],
|
|
55
41
|
"@stylistic/newline-per-chained-call": [
|
|
56
42
|
"error",
|
|
57
43
|
{
|
|
58
|
-
ignoreChainWithDepth: 2
|
|
59
|
-
}
|
|
44
|
+
ignoreChainWithDepth: 2
|
|
45
|
+
}
|
|
60
46
|
],
|
|
61
47
|
"@stylistic/padded-blocks": [
|
|
62
48
|
"error",
|
|
63
|
-
"never"
|
|
49
|
+
"never"
|
|
64
50
|
],
|
|
65
51
|
"@stylistic/brace-style": [
|
|
66
52
|
"error",
|
|
67
53
|
"1tbs",
|
|
68
54
|
{
|
|
69
|
-
allowSingleLine: true
|
|
70
|
-
}
|
|
55
|
+
allowSingleLine: true
|
|
56
|
+
}
|
|
71
57
|
],
|
|
72
58
|
"@stylistic/padding-line-between-statements": [
|
|
73
59
|
"error",
|
|
@@ -76,9 +62,9 @@ const styleRules = {
|
|
|
76
62
|
prev: [
|
|
77
63
|
"const",
|
|
78
64
|
"let",
|
|
79
|
-
"var"
|
|
65
|
+
"var"
|
|
80
66
|
],
|
|
81
|
-
next: "*"
|
|
67
|
+
next: "*"
|
|
82
68
|
},
|
|
83
69
|
{
|
|
84
70
|
blankLine: "always",
|
|
@@ -90,8 +76,8 @@ const styleRules = {
|
|
|
90
76
|
"function",
|
|
91
77
|
"switch",
|
|
92
78
|
"try",
|
|
93
|
-
"while"
|
|
94
|
-
]
|
|
79
|
+
"while"
|
|
80
|
+
]
|
|
95
81
|
},
|
|
96
82
|
{
|
|
97
83
|
blankLine: "any",
|
|
@@ -103,7 +89,7 @@ const styleRules = {
|
|
|
103
89
|
"while",
|
|
104
90
|
"do",
|
|
105
91
|
"block-like",
|
|
106
|
-
"multiline-block-like"
|
|
92
|
+
"multiline-block-like"
|
|
107
93
|
],
|
|
108
94
|
next: [
|
|
109
95
|
"block-like",
|
|
@@ -112,95 +98,84 @@ const styleRules = {
|
|
|
112
98
|
"multiline-block-like",
|
|
113
99
|
"switch",
|
|
114
100
|
"try",
|
|
115
|
-
"while"
|
|
116
|
-
]
|
|
117
|
-
}
|
|
101
|
+
"while"
|
|
102
|
+
]
|
|
103
|
+
}
|
|
118
104
|
],
|
|
105
|
+
"@stylistic/quotes": [
|
|
106
|
+
"error",
|
|
107
|
+
"double"
|
|
108
|
+
],
|
|
109
|
+
"@stylistic/semi": [
|
|
110
|
+
"error",
|
|
111
|
+
"always"
|
|
112
|
+
]
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const jsdocRules = {
|
|
116
|
+
"jsdoc/require-jsdoc": ["warn", { enableFixer: false }],
|
|
117
|
+
"jsdoc/require-param": ["warn", { enableFixer: false }],
|
|
118
|
+
"jsdoc/require-param-description": "warn",
|
|
119
|
+
"jsdoc/require-param-name": "warn",
|
|
120
|
+
"jsdoc/require-param-type": "warn",
|
|
121
|
+
"jsdoc/require-hyphen-before-param-description": [
|
|
122
|
+
"warn",
|
|
123
|
+
"always"
|
|
124
|
+
],
|
|
125
|
+
"jsdoc/require-returns": "warn",
|
|
126
|
+
"jsdoc/require-returns-check": "error"
|
|
119
127
|
};
|
|
120
128
|
|
|
121
129
|
export default [
|
|
130
|
+
// Global configuration
|
|
131
|
+
{
|
|
132
|
+
ignores: ["coverage-report.lcov", "node_modules/"]
|
|
133
|
+
},
|
|
122
134
|
js.configs.recommended,
|
|
123
|
-
jsdoc.configs["flat/recommended"],
|
|
124
|
-
stylistic.configs["disable-legacy"],
|
|
125
|
-
stylistic.configs.customize({
|
|
126
|
-
indent: 2,
|
|
127
|
-
quotes: "double",
|
|
128
|
-
semi: true,
|
|
129
|
-
arrowParens: true,
|
|
130
|
-
}),
|
|
131
135
|
{
|
|
132
|
-
|
|
136
|
+
plugins: {
|
|
137
|
+
"@stylistic": stylistic
|
|
138
|
+
}
|
|
133
139
|
},
|
|
140
|
+
// Lib configuration
|
|
134
141
|
{
|
|
135
|
-
files: ["
|
|
142
|
+
files: ["lib/**/*.js"],
|
|
136
143
|
plugins: {
|
|
137
|
-
|
|
144
|
+
jsdoc
|
|
138
145
|
},
|
|
139
146
|
languageOptions: {
|
|
147
|
+
ecmaVersion: "latest",
|
|
148
|
+
sourceType: "module",
|
|
140
149
|
globals: {
|
|
141
|
-
...globals.
|
|
142
|
-
...globals.node
|
|
143
|
-
|
|
144
|
-
describe: "readonly",
|
|
145
|
-
before: "readonly",
|
|
146
|
-
after: "readonly",
|
|
147
|
-
beforeEach: "readonly",
|
|
148
|
-
afterEach: "readonly",
|
|
149
|
-
},
|
|
150
|
+
...globals.es2021,
|
|
151
|
+
...globals.node
|
|
152
|
+
}
|
|
150
153
|
},
|
|
154
|
+
rules: {
|
|
155
|
+
...styleRules,
|
|
156
|
+
...jsdocRules
|
|
157
|
+
}
|
|
151
158
|
},
|
|
159
|
+
// Test configuration
|
|
152
160
|
{
|
|
153
|
-
files: ["
|
|
161
|
+
files: ["test/**/*.js"],
|
|
154
162
|
plugins: {
|
|
155
|
-
|
|
156
|
-
"
|
|
157
|
-
jsdoc,
|
|
163
|
+
"chai-friendly": eslintPluginChaiFriendly,
|
|
164
|
+
"chai-expect": eslintPluginChaiExpect
|
|
158
165
|
},
|
|
159
166
|
languageOptions: {
|
|
167
|
+
ecmaVersion: "latest",
|
|
168
|
+
sourceType: "module",
|
|
160
169
|
globals: {
|
|
161
|
-
...globals.
|
|
170
|
+
...globals.es2021,
|
|
162
171
|
...globals.node,
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
...globals.mocha
|
|
173
|
+
}
|
|
165
174
|
},
|
|
166
175
|
rules: {
|
|
167
176
|
...styleRules,
|
|
168
|
-
|
|
169
|
-
"
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
],
|
|
173
|
-
"no-nested-ternary": "off",
|
|
174
|
-
"no-param-reassign": "warn",
|
|
175
|
-
"camelcase": [
|
|
176
|
-
"error",
|
|
177
|
-
{
|
|
178
|
-
properties: "never",
|
|
179
|
-
},
|
|
180
|
-
],
|
|
181
|
-
"eqeqeq": [
|
|
182
|
-
"error",
|
|
183
|
-
"always",
|
|
184
|
-
{
|
|
185
|
-
null: "ignore",
|
|
186
|
-
},
|
|
187
|
-
],
|
|
188
|
-
"func-style": [
|
|
189
|
-
"error",
|
|
190
|
-
"declaration",
|
|
191
|
-
{
|
|
192
|
-
allowArrowFunctions: true,
|
|
193
|
-
},
|
|
194
|
-
],
|
|
195
|
-
"no-use-before-define": [
|
|
196
|
-
"error",
|
|
197
|
-
{
|
|
198
|
-
functions: false,
|
|
199
|
-
},
|
|
200
|
-
],
|
|
201
|
-
"no-warning-comments": "warn",
|
|
202
|
-
"require-unicode-regexp": "off",
|
|
203
|
-
|
|
204
|
-
},
|
|
205
|
-
},
|
|
177
|
+
"no-unused-expressions": "off",
|
|
178
|
+
"chai-friendly/no-unused-expressions": "error"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
206
181
|
];
|
package/lib/fork.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const debugVerbose = require("debug")("sshClientWrapper:verbose:fork");
|
|
4
|
-
const debugSendPty = require("debug")("sshClientWrapper:insecure:fork");
|
|
5
|
-
const { spawn } = require("node-pty");
|
|
1
|
+
import Debug from "debug";
|
|
2
|
+
import { spawn } from "node-pty-prebuilt-multiarch";
|
|
6
3
|
|
|
7
|
-
|
|
4
|
+
import { rePwPrompt, rePhPrompt, reNewHostPrompt, reRemoteHostIdentificationHasChanged, buff2String, sendPty, watchDogTimer } from "./util.js";
|
|
5
|
+
|
|
6
|
+
const debug = Debug("sshClientWrapper:debug:fork");
|
|
7
|
+
const debugVerbose = Debug("sshClientWrapper:verbose:fork");
|
|
8
|
+
const debugSendPty = Debug("sshClientWrapper:insecure:fork");
|
|
8
9
|
|
|
9
10
|
const nodePtyOpt = {
|
|
10
11
|
shell: process.platform === "win32" ? process.env.ComSpec : process.env.SHELL,
|
|
11
12
|
windowsHide: true
|
|
12
13
|
};
|
|
13
14
|
|
|
14
|
-
function sshLoginCallback(output, ptyProcess, pw, ph, logger = debugSendPty) {
|
|
15
|
+
export function sshLoginCallback(output, ptyProcess, pw, ph, logger = debugSendPty) {
|
|
15
16
|
return new Promise((resolve, reject)=>{
|
|
16
17
|
if (reRemoteHostIdentificationHasChanged.test(output)) {
|
|
17
18
|
debug("REMOTE HOST IDENTIFICATION HAS CHANGED");
|
|
@@ -70,7 +71,7 @@ function sshLoginCallback(output, ptyProcess, pw, ph, logger = debugSendPty) {
|
|
|
70
71
|
/**
|
|
71
72
|
* create pty for master ssh session and store to hostInfo.masterPty
|
|
72
73
|
*/
|
|
73
|
-
function createMasterPty(hostInfo) {
|
|
74
|
+
export function createMasterPty(hostInfo) {
|
|
74
75
|
debug("fork pty for master connection");
|
|
75
76
|
|
|
76
77
|
if (hostInfo.masterPty) {
|
|
@@ -81,7 +82,7 @@ function createMasterPty(hostInfo) {
|
|
|
81
82
|
hostInfo.masterPty = ptyProcess;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
async function fork(hostInfo, cmd, args, timeout, outputCallback, retryableExitCodes = [], expects = []) {
|
|
85
|
+
export async function fork(hostInfo, cmd, args, timeout, outputCallback, retryableExitCodes = [], expects = []) {
|
|
85
86
|
return new Promise((resolve, reject)=>{
|
|
86
87
|
debug(`cmd=${cmd}: args=${args}`);
|
|
87
88
|
const ptyProcess = spawn(cmd, args, nodePtyOpt);
|
|
@@ -150,8 +151,3 @@ async function fork(hostInfo, cmd, args, timeout, outputCallback, retryableExitC
|
|
|
150
151
|
});
|
|
151
152
|
});
|
|
152
153
|
}
|
|
153
|
-
module.exports = {
|
|
154
|
-
fork,
|
|
155
|
-
createMasterPty,
|
|
156
|
-
sshLoginCallback
|
|
157
|
-
};
|
package/lib/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import Debug from "debug";
|
|
4
|
+
import * as sshExecModule from "./sshExec.js";
|
|
5
|
+
import * as rsyncExecModule from "./rsyncExec.js";
|
|
6
|
+
import { isArrayOfString, sanityCheck } from "./util.js";
|
|
7
|
+
|
|
8
|
+
const debug = Debug("sshClientWrapper:debug:interface");
|
|
9
|
+
const debugVerbose = Debug("sshClientWrapper:verbose:interface");
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Integer means integer number but it is defined as Object for now
|
|
@@ -25,10 +26,10 @@ const { isArrayOfString, sanityCheck } = require("./util.js");
|
|
|
25
26
|
* @param {string | Function} hostInfo.passphrase - passphrase or function which return passphrase of private key
|
|
26
27
|
* @param {string} hostInfo.keyFile - private key filename
|
|
27
28
|
* @param {boolean} hostInfo.noStrictHostKeyChecking - bypass host key checking if true
|
|
28
|
-
* @param {Integer} hostInfo.ControlPersist
|
|
29
|
-
* @param {Integer} hostInfo.ConnectTimeout
|
|
30
|
-
* @param {Integer} hostInfo.maxRetry
|
|
31
|
-
* @param {Integer} hostInfo.retryDuration
|
|
29
|
+
* @param {Integer} hostInfo.ControlPersist - how long keep master connection after last client connection has been closed (sec).
|
|
30
|
+
* @param {Integer} hostInfo.ConnectTimeout - timeout used when connectiong to ths SSH server
|
|
31
|
+
* @param {Integer} hostInfo.maxRetry - max number of retry
|
|
32
|
+
* @param {Integer} hostInfo.retryDuration - duration time (msec) between each retry
|
|
32
33
|
* @param {string} hostInfo.rcfile - rcfile path which will be sourced before executing actual command
|
|
33
34
|
* @param {string} hostInfo.prependCmd - command string which will be executed before executing actual command
|
|
34
35
|
* @param {string[]} hostInfo.sshOpt - additional options for ssh
|
|
@@ -44,13 +45,14 @@ const logAndReject = (message)=>{
|
|
|
44
45
|
* @class
|
|
45
46
|
*/
|
|
46
47
|
class SshClientWrapper {
|
|
47
|
-
constructor(hostInfo) {
|
|
48
|
+
constructor(hostInfo, deps = { ...sshExecModule, ...rsyncExecModule }) {
|
|
48
49
|
debug("constructor called for", hostInfo.host);
|
|
49
50
|
debugVerbose("hostInfo=", hostInfo);
|
|
50
51
|
this.hostInfo = { ...hostInfo };
|
|
51
52
|
sanityCheck(this.hostInfo);
|
|
52
53
|
this.hostInfo.masterPty = null;
|
|
53
54
|
this.hostInfo.rsyncVersion = null;
|
|
55
|
+
this.deps = deps;
|
|
54
56
|
|
|
55
57
|
if (process.env.HOME) {
|
|
56
58
|
try {
|
|
@@ -80,7 +82,7 @@ class SshClientWrapper {
|
|
|
80
82
|
if (typeof cmd !== "string" || cmd === "") {
|
|
81
83
|
return logAndReject("cmd must be string");
|
|
82
84
|
}
|
|
83
|
-
return sshExec(this.hostInfo, cmd, timeout, outputCallback, rcfile, prependCmd);
|
|
85
|
+
return this.deps.sshExec(this.hostInfo, cmd, timeout, outputCallback, rcfile, prependCmd);
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
/**
|
|
@@ -99,7 +101,7 @@ class SshClientWrapper {
|
|
|
99
101
|
};
|
|
100
102
|
let rt = null;
|
|
101
103
|
try {
|
|
102
|
-
rt = await sshExec(this.hostInfo, cmd, timeout, cb, rcfile, prependCmd);
|
|
104
|
+
rt = await this.deps.sshExec(this.hostInfo, cmd, timeout, cb, rcfile, prependCmd);
|
|
103
105
|
} catch (e) {
|
|
104
106
|
//non-zero retrun should be allowed
|
|
105
107
|
if (typeof e.rt === "undefined" || e.rt < 0) {
|
|
@@ -138,7 +140,7 @@ class SshClientWrapper {
|
|
|
138
140
|
*/
|
|
139
141
|
async expect(cmd, expects, timeout = 180) {
|
|
140
142
|
debug("expect called", expects);
|
|
141
|
-
return expect(this.hostInfo, cmd, expects, timeout);
|
|
143
|
+
return this.deps.expect(this.hostInfo, cmd, expects, timeout);
|
|
142
144
|
}
|
|
143
145
|
|
|
144
146
|
/**
|
|
@@ -169,7 +171,7 @@ class SshClientWrapper {
|
|
|
169
171
|
if (typeof opt !== "undefined" && !isArrayOfString(opt)) {
|
|
170
172
|
return logAndReject("opt must be array of string");
|
|
171
173
|
}
|
|
172
|
-
return send(this.hostInfo, src, dst, opt, timeout);
|
|
174
|
+
return this.deps.send(this.hostInfo, src, dst, opt, timeout);
|
|
173
175
|
}
|
|
174
176
|
|
|
175
177
|
/**
|
|
@@ -200,7 +202,7 @@ class SshClientWrapper {
|
|
|
200
202
|
if (typeof opt !== "undefined" && !isArrayOfString(opt)) {
|
|
201
203
|
return logAndReject("opt must be array of string");
|
|
202
204
|
}
|
|
203
|
-
return recv(this.hostInfo, src, dst, opt, timeout);
|
|
205
|
+
return this.deps.recv(this.hostInfo, src, dst, opt, timeout);
|
|
204
206
|
}
|
|
205
207
|
|
|
206
208
|
/**
|
|
@@ -215,7 +217,7 @@ class SshClientWrapper {
|
|
|
215
217
|
if (!Number.isInteger(timeout) || timeout <= 0) {
|
|
216
218
|
return logAndReject("timeout must be positive integer");
|
|
217
219
|
}
|
|
218
|
-
return canConnect(this.hostInfo, timeout);
|
|
220
|
+
return this.deps.canConnect(this.hostInfo, timeout);
|
|
219
221
|
}
|
|
220
222
|
|
|
221
223
|
/**
|
|
@@ -223,8 +225,8 @@ class SshClientWrapper {
|
|
|
223
225
|
*/
|
|
224
226
|
async disconnect() {
|
|
225
227
|
debug(`disconnect from ${this.hostInfo.host} called`);
|
|
226
|
-
disconnect(this.hostInfo);
|
|
228
|
+
this.deps.disconnect(this.hostInfo);
|
|
227
229
|
}
|
|
228
230
|
}
|
|
229
231
|
|
|
230
|
-
|
|
232
|
+
export default SshClientWrapper;
|