Promise-based SFTP client for Node.js that wraps the ssh2 module
—
File and directory information retrieval including existence checking, attribute inspection, path resolution, and working directory management.
Tests whether a remote file or directory exists and returns its type.
/**
* Test if remote object exists and return its type
* @param remotePath - Path to remote object
* @returns Promise resolving to type string or false
* 'd' = directory, '-' = file, 'l' = symbolic link, false = does not exist
*/
exists(remotePath): Promise<Boolean|String>;Usage Examples:
// Check if file exists
const fileExists = await sftp.exists('/remote/file.txt');
if (fileExists === '-') {
console.log('File exists');
} else if (fileExists === false) {
console.log('File does not exist');
}
// Check if directory exists
const dirExists = await sftp.exists('/remote/folder');
if (dirExists === 'd') {
console.log('Directory exists');
}
// Check for symbolic link
const linkExists = await sftp.exists('/remote/symlink');
if (linkExists === 'l') {
console.log('Symbolic link exists');
}
// Conditional operations based on existence
const target = '/remote/config.json';
const exists = await sftp.exists(target);
if (exists === '-') {
// File exists, download it
await sftp.get(target, '/local/config.json');
} else if (exists === false) {
// File doesn't exist, create default
await sftp.put('/local/default-config.json', target);
}Retrieves detailed attributes for a remote file or directory, following symbolic links.
/**
* Get file/directory attributes (follows symbolic links)
* @param remotePath - Path to remote object
* @returns Promise resolving to file statistics object
*/
stat(remotePath): Promise<Object>;Usage Examples:
// Get file statistics
const stats = await sftp.stat('/remote/file.txt');
console.log(`Size: ${stats.size} bytes`);
console.log(`Modified: ${new Date(stats.modifyTime)}`);
console.log(`Is file: ${stats.isFile}`);
console.log(`Permissions: ${stats.mode.toString(8)}`);
// Check if path is directory
const dirStats = await sftp.stat('/remote/folder');
if (dirStats.isDirectory) {
console.log('Path is a directory');
}
// File size comparison
const fileStats = await sftp.stat('/remote/large-file.dat');
if (fileStats.size > 100 * 1024 * 1024) {
console.log('File is larger than 100MB, using fastGet');
await sftp.fastGet('/remote/large-file.dat', '/local/large-file.dat');
}Retrieves detailed attributes for a remote file or directory, NOT following symbolic links.
/**
* Get file/directory attributes (does not follow symbolic links)
* @param remotePath - Path to remote object
* @returns Promise resolving to file statistics object
*/
lstat(remotePath): Promise<Object>;Usage Examples:
// Get symbolic link info (not target info)
const linkStats = await sftp.lstat('/remote/symlink');
if (linkStats.isSymbolicLink) {
console.log('This is a symbolic link');
// Get target info separately if needed
const targetStats = await sftp.stat('/remote/symlink');
console.log(`Link points to ${targetStats.isFile ? 'file' : 'directory'}`);
}
// Compare link vs target attributes
const linkInfo = await sftp.lstat('/remote/link-to-file');
const targetInfo = await sftp.stat('/remote/link-to-file');
console.log(`Link size: ${linkInfo.size}, Target size: ${targetInfo.size}`);Returns the absolute path on the remote server, resolving relative paths and symbolic links.
/**
* Get absolute path on remote server, resolving . and .. components
* @param remotePath - Path to resolve (can be relative)
* @param addListeners - Whether to add event listeners (default: true)
* @returns Promise resolving to absolute path string or empty string if not found
*/
realPath(remotePath, addListeners = true): Promise<String>;Usage Examples:
// Resolve relative path
const absolutePath = await sftp.realPath('../documents/file.txt');
console.log(`Absolute path: ${absolutePath}`);
// Resolve current directory
const currentDir = await sftp.realPath('.');
console.log(`Current directory: ${currentDir}`);
// Resolve symbolic link to actual path
const realPath = await sftp.realPath('/remote/symlink-to-file');
if (realPath) {
console.log(`Link resolves to: ${realPath}`);
} else {
console.log('Path does not exist');
}
// Path validation
const userInput = '../../../etc/passwd';
const resolved = await sftp.realPath(userInput);
if (resolved.startsWith('/home/user/')) {
console.log('Path is within allowed directory');
} else {
console.log('Path is outside allowed directory');
}Returns the current working directory path on the remote server.
/**
* Get current working directory path
* @returns Promise resolving to current directory path
*/
cwd(): Promise<String>;Usage Examples:
// Get current directory
const currentDir = await sftp.cwd();
console.log(`Working directory: ${currentDir}`);
// Store current directory before changing
const originalDir = await sftp.cwd();
// ... perform operations in different directories ...
// Note: SFTP doesn't have a built-in cd command,
// but you can use absolute paths or realPath for navigation
// Build relative paths from current directory
const workingDir = await sftp.cwd();
const configFile = `${workingDir}/config/app.json`;
const exists = await sftp.exists(configFile);interface FileStats {
mode: number; // File mode/permissions
uid: number; // Owner user ID
gid: number; // Owner group ID
size: number; // Size in bytes
accessTime: number; // Last access time (milliseconds since epoch)
modifyTime: number; // Last modify time (milliseconds since epoch)
isDirectory: boolean; // True if directory
isFile: boolean; // True if regular file
isBlockDevice: boolean; // True if block device
isCharacterDevice: boolean; // True if character device
isSymbolicLink: boolean; // True if symbolic link
isFIFO: boolean; // True if FIFO/named pipe
isSocket: boolean; // True if socket
}// Check before download
const remoteFile = '/remote/data.json';
const exists = await sftp.exists(remoteFile);
if (exists === '-') {
const stats = await sftp.stat(remoteFile);
if (stats.size > 0) {
await sftp.get(remoteFile, '/local/data.json');
} else {
console.log('Remote file is empty');
}
} else {
console.log('Remote file does not exist');
}// Navigate and inspect directories
const currentDir = await sftp.cwd();
console.log(`Starting in: ${currentDir}`);
const targetDir = './subdirectory';
const absoluteTarget = await sftp.realPath(targetDir);
if (absoluteTarget) {
const stats = await sftp.stat(absoluteTarget);
if (stats.isDirectory) {
const files = await sftp.list(absoluteTarget);
console.log(`Directory contains ${files.length} items`);
}
}// Find large files modified recently
const files = await sftp.list('/remote/data');
const largeFreshFiles = [];
for (const file of files) {
if (file.type === '-') { // Regular file
const stats = await sftp.stat(`/remote/data/${file.name}`);
const dayOld = Date.now() - (24 * 60 * 60 * 1000);
const sizeMB = stats.size / (1024 * 1024);
if (stats.modifyTime > dayOld && sizeMB > 10) {
largeFreshFiles.push({
name: file.name,
size: sizeMB,
modified: new Date(stats.modifyTime)
});
}
}
}// Check file permissions
const stats = await sftp.stat('/remote/script.sh');
const permissions = stats.mode & parseInt('777', 8);
const isExecutable = (permissions & parseInt('111', 8)) !== 0;
console.log(`Permissions: ${permissions.toString(8)}`);
console.log(`Executable: ${isExecutable}`);File system information errors provide specific context:
ENOENT: File or directory does not existEACCES: Permission denied for stat operationERR_BAD_PATH: Invalid path formatERR_NOT_CONNECTED: No active SFTP connectionInstall with Tessl CLI
npx tessl i tessl/npm-ssh2-sftp-client