npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

scp-next

v1.0.20

Published

A SCP CLI and library for secure SSH file transfers using SFTP.

Readme

scp-next

npm version license node version module type docs

scp-next is an SCP-style command-line tool and library for secure file transfers over SSH. It uses SFTP internally through ssh2-sftp-client instead of implementing SCP or SFTP protocols manually. Developer documentation is available at GitHub Pages.

Features

  • Upload and download files or directories recursively.
  • CLI usage with clear <source> <destination> operands.
  • ESM import and CommonJS require support.
  • JSON configuration files, named profiles, and configured jobs.
  • Reusable transfer client.

Contents

Installation

CLI:

npm install --global scp-next

Library:

npm install scp-next

Quick Start

Upload a directory:

scp-next upload ./dist /var/www/example \
  --host your-host \
  --username your-username \
  --password your-password \
  --recursive

Download a file:

scp-next download /var/log/example.log ./logs/example.log \
  --host your-host \
  --username your-username \
  --password your-password

Output:

Downloading: /var/log/example.log 1.0 MB / 1.0 MB (100%)

Password arguments are convenient, but they may be exposed through shell history and process listings. Prefer SCP_NEXT_PASSWORD environment variable in shared or production environments. For key authentication, use a protected private-key file.

Use the library:

import { upload } from "scp-next";

await upload({
  host: process.env.SCP_NEXT_HOST,
  username: process.env.SCP_NEXT_USERNAME,
  password: process.env.SCP_NEXT_PASSWORD,
  localPath: "./dist",
  remotePath: "/var/www/example",
  recursive: true,
  overwrite: true
});

CLI Usage

CLI Syntax

scp-next upload <source> <destination> [options]
scp-next download <source> <destination> [options]
scp-next run <job> [source] [destination] [options]

CLI commands use <source> <destination>. Programmatic upload and download APIs use localPath and remotePath.

| Operation | Source | Destination | | --------- | ------ | ----------- | | Upload | Local | Remote | | Download | Remote | Local |

Destination paths follow familiar cp/scp behavior. If the destination exists as a directory or ends with a path separator, scp-next places the source inside that directory using the source basename. Missing destination directories are created by default.

Upload Examples

Preview a recursive deploy before connecting:

scp-next upload ./dist /var/www/example \
  --host your-host \
  --username your-username \
  --password your-password \
  --recursive \
  --dry-run

Deploy with overwrite and verbose diagnostics:

scp-next upload ./dist /var/www/example \
  --host your-host \
  --username your-username \
  --password your-password \
  --recursive \
  --overwrite \
  --verbose

Use an encrypted private key:

scp-next upload ./dist /var/www/example \
  --host your-host \
  --username your-username \
  --private-key-file ~/.ssh/id_ed25519 \
  --passphrase your-passphrase \
  --recursive

Download Examples

Download a remote directory recursively:

scp-next download /var/log/example ./logs/example \
  --host your-host \
  --username your-username \
  --password your-password \
  --recursive

Download and overwrite an existing local file:

scp-next download /var/log/example.log ./logs/example.log \
  --host your-host \
  --username your-username \
  --password your-password \
  --overwrite

Run a configured download job:

scp-next run download-logs --config ./scp-next.config.json

CLI Options

| Option | Description | | ------------------------------------- | ---------------------------------------------------------------------- | | --host <host> | SSH server host. | | --port <port> | SSH server port. Defaults to 22. | | --username <username> | SSH username. | | --password <password> | SSH password. | | --private-key <privateKey> | Private-key content. Redacted from logs and errors. | | --private-key-file <privateKeyFile> | Private-key file path. Supports ~ expansion. | | --passphrase <passphrase> | Passphrase for an encrypted private key. | | --config <path> | Explicit configuration file path. | | --profile <name> | Named server profile from the configuration file. | | --recursive | Transfer directories recursively. | | --overwrite | Allow replacing existing destination files. | | --create-directories | Create missing destination directories. Enabled by default. | | --no-create-directories | Disable automatic destination directory creation. | | --dry-run | Resolve and validate the operation without connecting or transferring. | | --timeout <milliseconds> | SSH connection ready timeout in milliseconds. | | --verbose | Print non-sensitive diagnostic details. | | --quiet | Disable progress and non-error output. | | --help | Show command help. | | --version | Show the package version. |

--timeout maps to the SSH readyTimeout, so it controls how long to wait for the connection handshake. It does not currently limit per-file or total transfer duration.

Library Usage

ESM Upload

import { upload } from "scp-next";

await upload({
  host: process.env.SCP_NEXT_HOST,
  username: process.env.SCP_NEXT_USERNAME,
  password: process.env.SCP_NEXT_PASSWORD,
  localPath: "./dist",
  remotePath: "/var/www/example",
  recursive: true,
  overwrite: true
});

CommonJS Download

const { download } = require("scp-next");

async function main() {
  await download({
    host: process.env.SCP_NEXT_HOST,
    username: process.env.SCP_NEXT_USERNAME,
    password: process.env.SCP_NEXT_PASSWORD,
    remotePath: "/var/log/example.log",
    localPath: "./logs/example.log"
  });
}

main().catch((error) => {
  console.error(error.message);
  process.exitCode = 1;
});

Reusable Client

import { createClient } from "scp-next";

const client = createClient({
  host: process.env.SCP_NEXT_HOST,
  username: process.env.SCP_NEXT_USERNAME,
  password: process.env.SCP_NEXT_PASSWORD
});

try {
  await client.connect();
  await client.upload("./dist", "/var/www/example", { recursive: true });
  await client.download("/var/log/example.log", "./logs/example.log");
} finally {
  await client.close();
}

Use a Configuration File

The CLI loads configuration files automatically. The library API receives options directly, so load JSON in your application and pass the relevant server and transfer values.

import { readFile } from "node:fs/promises";

import { upload, type ScpNextConfig } from "scp-next";

const config = JSON.parse(
  await readFile(new URL("./scp-next.config.json", import.meta.url), "utf8")
) as ScpNextConfig;

await upload({
  ...config.server,
  ...config.transfer,
  localPath: "./dist",
  remotePath: "/var/www/example"
});

Library Options

| Field | Used by | Description | | ------------------- | --------------- | ------------------------------------------------------------ | | host | server | SSH server host. | | port | server | SSH server port. Defaults to 22. | | username | server | SSH username. | | password | server | SSH password. | | privateKey | server | Private-key content as a string or Buffer. | | privateKeyFile | server | Private-key file path. | | passphrase | server | Passphrase for an encrypted private key. | | agent | server | SSH agent socket path. | | hostFingerprint | server | Expected server host-key SHA-256 fingerprint. | | knownHostsFile | server | Known-hosts file for host verification. | | localPath | upload/download | Local source for upload or local destination for download. | | remotePath | upload/download | Remote destination for upload or remote source for download. | | recursive | transfer | Transfer directories recursively. Defaults to false. | | overwrite | transfer | Allow replacing existing files. | | createDirectories | transfer | Create missing destination directories. Defaults to true. | | dryRun | transfer | Validate and plan without modifying local or remote files. | | timeout | server/transfer | SSH connection ready timeout in milliseconds. | | onProgress | transfer | Progress callback for file and directory transfers. |

Advanced Usage

Configuration Files

Use scp-next.config.json in the current directory. scp-next also auto-detects these rc-style filenames: .scp-nextrc, .scp-nextrc.json.

scp-next.config.json

Use --config for an explicit path:

scp-next upload ./dist /var/www/example --config ./deploy/scp-next.json

Use --profile with a configuration file:

scp-next upload ./dist /var/www/example \
  --config ./scp-next.config.json \
  --profile production

Example:

{
  "server": {
    "host": "your-host",
    "port": 22,
    "username": "your-username",
    "password": "your-password"
  },
  "transfer": {
    "recursive": true,
    "overwrite": true,
    "createDirectories": true,
    "timeout": 30000
  }
}

Do not commit configuration files containing real passwords. Prefer SCP_NEXT_PASSWORD or another protected secret source for shared repositories and deployment environments.

Configuration File Options

| Key | Description | | ---------------- | -------------------------------------------------------------------- | | server | SSH connection options such as host, port, and username. | | transfer | Transfer defaults such as recursive, overwrite, and directories. | | defaultProfile | Profile name used when --profile or SCP_NEXT_PROFILE is absent. | | profiles | Named server connection profiles. | | jobs | Reusable upload or download jobs for scp-next run <job>. |

Root-level server options such as host, username, privateKeyFile, knownHostsFile, and hostFingerprint are also supported for simple configuration files, but server keeps connection values grouped and easier to read.

Named Profiles

{
  "defaultProfile": "production",
  "profiles": {
    "production": {
      "host": "your-host-a",
      "port": 22,
      "username": "your-username-a",
      "password": "your-password-a"
    },
    "staging": {
      "host": "your-host-b",
      "port": 22,
      "username": "your-username-b",
      "privateKeyFile": "~/.ssh/id_ed25519"
    }
  }
}
scp-next upload ./dist /var/www/example --profile production

Configured Jobs

Jobs use source and destination; the operation determines which path is local or remote.

{
  "profiles": {
    "production": {
      "host": "your-host",
      "username": "your-username",
      "password": "your-password"
    }
  },
  "jobs": {
    "deploy": {
      "operation": "upload",
      "profile": "production",
      "source": "./dist",
      "destination": "/var/www/example",
      "recursive": true,
      "overwrite": true
    },
    "download-logs": {
      "operation": "download",
      "profile": "production",
      "source": "/var/log/example",
      "destination": "./logs",
      "recursive": true
    }
  }
}
scp-next run deploy
scp-next run download-logs

scp-next run <job> [source] [destination] permits explicit path overrides.

Environment Variables

SCP_NEXT_HOST
SCP_NEXT_PORT
SCP_NEXT_USERNAME
SCP_NEXT_PASSWORD
SCP_NEXT_PRIVATE_KEY
SCP_NEXT_PRIVATE_KEY_FILE
SCP_NEXT_PASSPHRASE
SCP_NEXT_TIMEOUT
SCP_NEXT_PROFILE
SSH_AUTH_SOCK

Bash:

export SCP_NEXT_HOST="your-host"
export SCP_NEXT_USERNAME="your-username"
export SCP_NEXT_PASSWORD="your-password"
scp-next upload ./dist /var/www/example --recursive

PowerShell:

$env:SCP_NEXT_HOST = "your-host"
$env:SCP_NEXT_USERNAME = "your-username"
$env:SCP_NEXT_PASSWORD = "your-password"
scp-next upload .\dist /var/www/example --recursive

Configuration Precedence

Highest to lowest:

  1. Explicit CLI options
  2. Positional CLI operands
  3. Environment variables
  4. Selected configuration profile
  5. Root-level configuration values
  6. Configured job values
  7. Internal defaults

For run, job values are loaded first, then root configuration, selected profile, environment variables, explicit positional overrides, and CLI options.

Authentication

Supported methods:

  • Password authentication
  • Private-key content
  • Private-key file
  • Private-key passphrase
  • SSH agent authentication through agent or SSH_AUTH_SOCK

SSH agent CLI example:

ssh-add ~/.ssh/id_ed25519

export SCP_NEXT_HOST="your-host"
export SCP_NEXT_USERNAME="your-username"

scp-next upload ./dist /var/www/example --recursive

Library example:

import { upload } from "scp-next";

await upload({
  host: process.env.SCP_NEXT_HOST,
  username: process.env.SCP_NEXT_USERNAME,
  agent: process.env.SSH_AUTH_SOCK,
  localPath: "./dist",
  remotePath: "/var/www/example",
  recursive: true
});

Host Verification

hostFingerprint compares the server host key SHA-256 fingerprint. knownHostsFile supports plain OpenSSH known_hosts entries for exact host names. When neither is supplied, scp-next reads ~/.ssh/known_hosts. Hashed host names and every OpenSSH marker variant are not currently parsed.

scp-next fails closed if it cannot establish a host verifier. Use hostFingerprint for CI or deployments where a known-hosts file is not available.

Progress Reporting

Interactive terminals display concise progress. Progress is disabled with --quiet or when stderr is not a TTY, which keeps CI logs readable. Library users can pass onProgress.

Dry Run

--dry-run resolves configuration and validates local paths and transfer direction without connecting to the remote server or modifying local/remote files.

Dry run: upload
Source: ./dist
Destination: your-username@your-host:/var/www/example
Recursive: yes
Overwrite: yes

Error Handling

Public typed errors:

  • ScpNextError
  • ConfigurationError
  • ValidationError
  • AuthenticationError
  • ConnectionError
  • TransferError
  • FileSystemError
  • HostVerificationError

Each error includes a stable code, readable message, optional cause, and redacted non-sensitive context.

import { AuthenticationError, upload } from "scp-next";

try {
  await upload({
    host: process.env.SCP_NEXT_HOST,
    username: process.env.SCP_NEXT_USERNAME,
    password: process.env.SCP_NEXT_PASSWORD,
    localPath: "./dist",
    remotePath: "/var/www/example"
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error("SSH authentication failed.");
  }
  throw error;
}

API Reference

Primary functions:

upload(options: UploadOptions): Promise<void>
download(options: DownloadOptions): Promise<void>
createClient(options: ScpServerOptions): ScpNextClient
copy(options: CopyOptions): Promise<void>

Upload and download APIs use explicit localPath and remotePath names. The optional generic copy() API accepts typed local/remote endpoint objects.

User Guides

Development

npm install
npm run typecheck
npm run lint
npm test
npm run build
npm pack --dry-run

prepublishOnly runs clean, typecheck, lint, tests, and build.

Publishing

The published package includes dist, README, license, changelog, and docs. The CLI entry is dist/cli/index.js and contains a Node.js shebang.

Transfer Mechanism

Despite the package name, normal transfers use SFTP through ssh2-sftp-client, which is built on ssh2. This avoids remote shell execution for regular transfers and gives better support for progress reporting and recursive directory traversal than shelling out to an SCP command.