scp-next 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial `scp-next` TypeScript CLI and library.
6
+ - Added upload, download, recursive directory transfer, dry-run mode, configuration files,
7
+ profiles, jobs, environment-variable support, typed errors, and dual ESM/CommonJS builds.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017-2026 Cheng
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,403 @@
1
+ # scp-next
2
+
3
+ `scp-next` is a SCP command-line tool and library for secure file transfers over SSH.
4
+ The package uses SFTP internally through `ssh2-sftp-client` instead of implementing SCP or SFTP protocols manually.
5
+
6
+ ## Features
7
+
8
+ - Upload and download files.
9
+ - Upload and download directories recursively.
10
+ - CLI usage with `<source> <destination>` operands.
11
+ - ESM `import` and CommonJS `require` library usage.
12
+ - Reusable transfer client.
13
+ - JSON configuration files, named profiles, and configured jobs.
14
+ - Environment-variable based credentials.
15
+ - Dry-run mode, progress callbacks, timeouts, overwrite control, and destination directory creation.
16
+ - Typed errors with stable error codes.
17
+ - Secret redaction for passwords, passphrases, and private-key contents.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install --global scp-next
23
+ npm install scp-next
24
+ ```
25
+
26
+ ## CLI Syntax
27
+
28
+ ```text
29
+ scp-next upload <source> <destination> [options]
30
+ scp-next download <source> <destination> [options]
31
+ scp-next run <job> [source] [destination] [options]
32
+ ```
33
+
34
+ Terminology is intentionally consistent:
35
+
36
+ ```text
37
+ Operation Source Destination
38
+ Upload Local Remote
39
+ Download Remote Local
40
+ ```
41
+
42
+ CLI commands use `<source> <destination>`. Programmatic upload and download APIs use
43
+ `localPath` and `remotePath`.
44
+
45
+ ## Upload Examples
46
+
47
+ ```bash
48
+ scp-next upload ./dist /var/www/example \
49
+ --host example.com \
50
+ --username deploy \
51
+ --private-key-file ~/.ssh/id_ed25519 \
52
+ --recursive \
53
+ --create-directories
54
+ ```
55
+
56
+ ## Download Examples
57
+
58
+ ```bash
59
+ scp-next download /var/log/example.log ./logs/example.log \
60
+ --host example.com \
61
+ --username deploy \
62
+ --private-key-file ~/.ssh/id_ed25519 \
63
+ --create-directories
64
+ ```
65
+
66
+ ## CLI Options
67
+
68
+ ```text
69
+ --host
70
+ --port
71
+ --username
72
+ --password
73
+ --private-key
74
+ --private-key-file
75
+ --passphrase
76
+ --config
77
+ --profile
78
+ --recursive
79
+ --overwrite
80
+ --create-directories
81
+ --dry-run
82
+ --timeout
83
+ --verbose
84
+ --quiet
85
+ --help
86
+ --version
87
+ ```
88
+
89
+ Avoid `--password` when possible because command-line arguments may be visible in shell
90
+ history and process listings. Prefer environment variables, SSH agents, or protected
91
+ private-key files.
92
+
93
+ ## Configuration Files
94
+
95
+ `scp-next` searches for these files in the current directory:
96
+
97
+ ```text
98
+ scp-next.config.json
99
+ .scp-nextrc
100
+ .scp-nextrc.json
101
+ ```
102
+
103
+ Use `--config` for an explicit path:
104
+
105
+ ```bash
106
+ scp-next upload ./dist /var/www/example --config ./deploy/scp-next.json
107
+ ```
108
+
109
+ Example:
110
+
111
+ ```json
112
+ {
113
+ "server": {
114
+ "host": "example.com",
115
+ "port": 22,
116
+ "username": "deploy",
117
+ "privateKeyFile": "~/.ssh/id_ed25519"
118
+ },
119
+ "transfer": {
120
+ "recursive": true,
121
+ "overwrite": true,
122
+ "createDirectories": true,
123
+ "timeout": 30000
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## Named Profiles
129
+
130
+ ```json
131
+ {
132
+ "defaultProfile": "production",
133
+ "profiles": {
134
+ "production": {
135
+ "host": "production.example.com",
136
+ "port": 22,
137
+ "username": "deploy",
138
+ "privateKeyFile": "~/.ssh/id_ed25519"
139
+ },
140
+ "staging": {
141
+ "host": "staging.example.com",
142
+ "port": 22,
143
+ "username": "deploy",
144
+ "privateKeyFile": "~/.ssh/id_ed25519"
145
+ }
146
+ }
147
+ }
148
+ ```
149
+
150
+ ```bash
151
+ scp-next upload ./dist /var/www/example --profile production
152
+ ```
153
+
154
+ ## Configured Jobs
155
+
156
+ Jobs use `source` and `destination`; the `operation` determines which path is local or remote.
157
+
158
+ ```json
159
+ {
160
+ "profiles": {
161
+ "production": {
162
+ "host": "production.example.com",
163
+ "username": "deploy",
164
+ "privateKeyFile": "~/.ssh/id_ed25519"
165
+ }
166
+ },
167
+ "jobs": {
168
+ "deploy": {
169
+ "operation": "upload",
170
+ "profile": "production",
171
+ "source": "./dist",
172
+ "destination": "/var/www/example",
173
+ "recursive": true,
174
+ "overwrite": true
175
+ },
176
+ "download-logs": {
177
+ "operation": "download",
178
+ "profile": "production",
179
+ "source": "/var/log/example",
180
+ "destination": "./logs",
181
+ "recursive": true
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ ```bash
188
+ scp-next run deploy
189
+ scp-next run download-logs
190
+ ```
191
+
192
+ `scp-next run <job> [source] [destination]` permits explicit path overrides.
193
+
194
+ ## Environment Variables
195
+
196
+ ```text
197
+ SCP_NEXT_HOST
198
+ SCP_NEXT_PORT
199
+ SCP_NEXT_USERNAME
200
+ SCP_NEXT_PASSWORD
201
+ SCP_NEXT_PRIVATE_KEY
202
+ SCP_NEXT_PRIVATE_KEY_FILE
203
+ SCP_NEXT_PASSPHRASE
204
+ SCP_NEXT_TIMEOUT
205
+ SCP_NEXT_PROFILE
206
+ ```
207
+
208
+ Bash:
209
+
210
+ ```bash
211
+ export SCP_NEXT_HOST="example.com"
212
+ export SCP_NEXT_USERNAME="deploy"
213
+ export SCP_NEXT_PRIVATE_KEY_FILE="$HOME/.ssh/id_ed25519"
214
+ scp-next upload ./dist /var/www/example --recursive
215
+ ```
216
+
217
+ PowerShell:
218
+
219
+ ```powershell
220
+ $env:SCP_NEXT_HOST = "example.com"
221
+ $env:SCP_NEXT_USERNAME = "deploy"
222
+ $env:SCP_NEXT_PRIVATE_KEY_FILE = "$HOME\.ssh\id_ed25519"
223
+ scp-next upload .\dist /var/www/example --recursive
224
+ ```
225
+
226
+ ## Configuration Precedence
227
+
228
+ Highest to lowest:
229
+
230
+ 1. Explicit CLI options
231
+ 2. Positional CLI operands
232
+ 3. Environment variables
233
+ 4. Selected configuration profile
234
+ 5. Root-level configuration values
235
+ 6. Configured job values
236
+ 7. Internal defaults
237
+
238
+ For `run`, job values are loaded first, then root configuration, selected profile,
239
+ environment variables, explicit positional overrides, and CLI options.
240
+
241
+ ## Library Usage
242
+
243
+ ESM upload:
244
+
245
+ ```ts
246
+ import { upload } from "scp-next";
247
+
248
+ await upload({
249
+ host: process.env.SCP_NEXT_HOST,
250
+ username: process.env.SCP_NEXT_USERNAME,
251
+ privateKeyFile: process.env.SCP_NEXT_PRIVATE_KEY_FILE,
252
+ localPath: "./dist",
253
+ remotePath: "/var/www/example",
254
+ recursive: true,
255
+ overwrite: true
256
+ });
257
+ ```
258
+
259
+ CommonJS download:
260
+
261
+ ```js
262
+ const { download } = require("scp-next");
263
+
264
+ async function main() {
265
+ await download({
266
+ host: process.env.SCP_NEXT_HOST,
267
+ username: process.env.SCP_NEXT_USERNAME,
268
+ privateKeyFile: process.env.SCP_NEXT_PRIVATE_KEY_FILE,
269
+ remotePath: "/var/log/example.log",
270
+ localPath: "./logs/example.log"
271
+ });
272
+ }
273
+
274
+ main().catch((error) => {
275
+ console.error(error.message);
276
+ process.exitCode = 1;
277
+ });
278
+ ```
279
+
280
+ Reusable client:
281
+
282
+ ```ts
283
+ import { createClient } from "scp-next";
284
+
285
+ const client = createClient({
286
+ host: process.env.SCP_NEXT_HOST,
287
+ username: process.env.SCP_NEXT_USERNAME,
288
+ privateKeyFile: process.env.SCP_NEXT_PRIVATE_KEY_FILE
289
+ });
290
+
291
+ try {
292
+ await client.connect();
293
+ await client.upload("./dist", "/var/www/example", { recursive: true });
294
+ await client.download("/var/log/example.log", "./logs/example.log");
295
+ } finally {
296
+ await client.close();
297
+ }
298
+ ```
299
+
300
+ ## Authentication
301
+
302
+ Supported methods:
303
+
304
+ - Password authentication
305
+ - Private-key content
306
+ - Private-key file
307
+ - Private-key passphrase
308
+ - SSH agent authentication through `agent` or `SSH_AUTH_SOCK`
309
+
310
+ ## Host Verification
311
+
312
+ `hostFingerprint` compares the server host key SHA-256 fingerprint. `knownHostsFile`
313
+ supports plain OpenSSH `known_hosts` entries for exact host names. When neither is supplied,
314
+ `scp-next` reads `~/.ssh/known_hosts`. Hashed host names and every OpenSSH marker variant are
315
+ not currently parsed.
316
+
317
+ `scp-next` fails closed if it cannot establish a host verifier. Use `hostFingerprint` for CI
318
+ or deployments where a known-hosts file is not available.
319
+
320
+ ## Progress Reporting
321
+
322
+ Interactive terminals display concise progress. Progress is disabled with `--quiet` or when
323
+ stderr is not a TTY, which keeps CI logs readable. Library users can pass `onProgress`.
324
+
325
+ ## Dry Run
326
+
327
+ `--dry-run` resolves configuration and validates local paths and transfer direction without
328
+ connecting to the remote server or modifying local/remote files.
329
+
330
+ ```text
331
+ Dry run: upload
332
+ Source: ./dist
333
+ Destination: deploy@example.com:/var/www/example
334
+ Recursive: yes
335
+ Overwrite: yes
336
+ ```
337
+
338
+ ## Error Handling
339
+
340
+ Public typed errors:
341
+
342
+ - `ScpNextError`
343
+ - `ConfigurationError`
344
+ - `ValidationError`
345
+ - `AuthenticationError`
346
+ - `ConnectionError`
347
+ - `TransferError`
348
+ - `FileSystemError`
349
+ - `HostVerificationError`
350
+
351
+ Each error includes a stable `code`, readable `message`, optional `cause`, and redacted
352
+ non-sensitive context.
353
+
354
+ ```ts
355
+ import { AuthenticationError, upload } from "scp-next";
356
+
357
+ try {
358
+ await upload({ localPath: "./dist", remotePath: "/var/www/example" });
359
+ } catch (error) {
360
+ if (error instanceof AuthenticationError) {
361
+ console.error("SSH authentication failed.");
362
+ }
363
+ throw error;
364
+ }
365
+ ```
366
+
367
+ ## API Reference
368
+
369
+ Primary functions:
370
+
371
+ ```ts
372
+ upload(options: UploadOptions): Promise<void>
373
+ download(options: DownloadOptions): Promise<void>
374
+ createClient(options: ScpServerOptions): ScpNextClient
375
+ copy(options: CopyOptions): Promise<void>
376
+ ```
377
+
378
+ Upload and download APIs use explicit `localPath` and `remotePath` names. The optional
379
+ generic `copy()` API accepts typed local/remote endpoint objects.
380
+
381
+ ## Development
382
+
383
+ ```bash
384
+ npm install
385
+ npm run typecheck
386
+ npm run lint
387
+ npm test
388
+ npm run build
389
+ npm pack --dry-run
390
+ ```
391
+
392
+ `prepublishOnly` runs clean, typecheck, lint, tests, and build.
393
+
394
+ ## Publishing
395
+
396
+ The published package includes `dist`, README, license, changelog, and docs. The CLI entry is
397
+ `dist/cli/index.js` and contains a Node.js shebang.
398
+
399
+ ## Transfer Mechanism
400
+
401
+ Despite the package name, normal transfers use SFTP through `ssh2-sftp-client`, which is built
402
+ on `ssh2`. This avoids remote shell execution for regular transfers and gives better support
403
+ for progress reporting and recursive directory traversal than shelling out to an SCP command.