CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ssh2-sftp-client

Promise-based SFTP client for Node.js that wraps the ssh2 module

Pending
Overview
Eval results
Files

file-management.mddocs/

File Management

File management operations including deletion, renaming, permission changes, and remote file copying with advanced features like atomic operations.

Capabilities

Delete File

Deletes a file from the remote server with optional error suppression for missing files.

/**
 * Delete a file on the remote SFTP server
 * @param remotePath - Path to file to delete
 * @param notFoundOK - If true, ignore "file not found" errors (default: false)
 * @param addListeners - Whether to add event listeners (default: true)
 * @returns Promise resolving to success message
 */
delete(remotePath, notFoundOK = false, addListeners = true): Promise<String>;

Usage Examples:

// Delete existing file
await sftp.delete('/remote/old-file.txt');

// Delete file, ignore if it doesn't exist
await sftp.delete('/remote/maybe-exists.txt', true);

// Safe deletion with existence check
const exists = await sftp.exists('/remote/temp-file.dat');
if (exists === '-') {
  await sftp.delete('/remote/temp-file.dat');
  console.log('Temporary file deleted');
}

// Bulk deletion with error handling
const filesToDelete = ['/remote/file1.txt', '/remote/file2.txt', '/remote/file3.txt'];
for (const file of filesToDelete) {
  try {
    await sftp.delete(file, true); // Ignore missing files
    console.log(`Deleted: ${file}`);
  } catch (err) {
    console.error(`Failed to delete ${file}: ${err.message}`);
  }
}

Rename File

Renames or moves a file/directory on the remote server.

/**
 * Rename a file or directory on the remote server
 * @param fromPath - Current path of file/directory
 * @param toPath - New path for file/directory
 * @param addListeners - Whether to add event listeners (default: true)
 * @returns Promise resolving to success message
 */
rename(fromPath, toPath, addListeners = true): Promise<String>;

Usage Examples:

// Simple file rename
await sftp.rename('/remote/old-name.txt', '/remote/new-name.txt');

// Move file to different directory
await sftp.rename('/remote/temp/file.dat', '/remote/archive/file.dat');

// Rename directory
await sftp.rename('/remote/old-folder', '/remote/new-folder');

// Safe rename with existence checks
const source = '/remote/source.txt';
const destination = '/remote/destination.txt';

const sourceExists = await sftp.exists(source);
const destExists = await sftp.exists(destination);

if (sourceExists === '-' && destExists === false) {
  await sftp.rename(source, destination);
  console.log('File renamed successfully');
} else if (destExists !== false) {
  console.error('Destination already exists');
} else {
  console.error('Source file does not exist');
}

POSIX Rename

Performs atomic rename using the POSIX rename extension (SSH 4.8+).

/**
 * Rename using POSIX atomic rename extension
 * @param fromPath - Current path of file/directory
 * @param toPath - New path for file/directory  
 * @param addListeners - Whether to add event listeners (default: true)
 * @returns Promise resolving to success message
 */
posixRename(fromPath, toPath, addListeners = true): Promise<String>;

Important Note: This method requires SSH server version 4.8 or higher with the posix-rename@openssh.com extension.

Usage Examples:

// Atomic file rename (overwrites destination if exists)
await sftp.posixRename('/remote/temp.txt', '/remote/final.txt');

// Atomic directory rename
await sftp.posixRename('/remote/temp-dir', '/remote/final-dir');

// Safe atomic rename with server capability check
try {
  await sftp.posixRename('/remote/source.dat', '/remote/target.dat');
  console.log('Atomic rename completed');
} catch (err) {
  if (err.message.includes('not supported')) {
    console.log('Server does not support POSIX rename, using regular rename');
    await sftp.rename('/remote/source.dat', '/remote/target.dat');
  } else {
    throw err;
  }
}

Change Permissions

Changes file or directory permissions on the remote server.

/**
 * Change permissions of remote file or directory
 * @param rPath - Path to remote file/directory
 * @param mode - New permissions (octal number or string)
 * @param addListeners - Whether to add event listeners (default: true)
 * @returns Promise resolving to success message
 */
chmod(rPath, mode, addListeners = true): Promise<String>;

Usage Examples:

// Set file permissions using octal notation
await sftp.chmod('/remote/script.sh', 0o755); // rwxr-xr-x

// Set permissions using string notation
await sftp.chmod('/remote/data.txt', '644'); // rw-r--r--

// Make file executable
await sftp.chmod('/remote/program', 0o755);

// Remove write permissions for group and others
await sftp.chmod('/remote/private.txt', 0o600); // rw-------

// Bulk permission changes
const scripts = await sftp.list('/remote/scripts');
for (const file of scripts) {
  if (file.type === '-' && file.name.endsWith('.sh')) {
    await sftp.chmod(`/remote/scripts/${file.name}`, 0o755);
    console.log(`Made ${file.name} executable`);
  }
}

Remote Copy

Creates a copy of a remote file on the remote server without downloading/uploading.

/**
 * Create a remote copy of a remote file
 * @param src - Source file path on remote server
 * @param dst - Destination file path on remote server
 * @returns Promise resolving to success message
 */
rcopy(src, dst): Promise<String>;

Usage Examples:

// Create backup copy
await sftp.rcopy('/remote/important.txt', '/remote/important.txt.backup');

// Copy to different directory
await sftp.rcopy('/remote/config.json', '/remote/backup/config.json');

// Copy with timestamp
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupName = `/remote/data-${timestamp}.json`;
await sftp.rcopy('/remote/data.json', backupName);

// Safe copy with checks
const source = '/remote/master.conf';
const backup = '/remote/master.conf.bak';

const sourceExists = await sftp.exists(source);
const backupExists = await sftp.exists(backup);

if (sourceExists === '-') {
  if (backupExists !== false) {
    // Remove old backup first
    await sftp.delete(backup);
  }
  await sftp.rcopy(source, backup);
  console.log('Backup created successfully');
}

Permission Management

Octal Permission Values

Common permission patterns:

// File permissions
0o644  // rw-r--r-- (readable by all, writable by owner)
0o600  // rw------- (readable/writable by owner only)
0o755  // rwxr-xr-x (executable by all, writable by owner)
0o700  // rwx------ (full access by owner only)

// Directory permissions
0o755  // rwxr-xr-x (standard directory permissions)
0o750  // rwxr-x--- (accessible by owner and group)
0o700  // rwx------ (accessible by owner only)

Permission Checking and Setting

// Check current permissions before changing
const stats = await sftp.stat('/remote/file.txt');
const currentMode = stats.mode & parseInt('777', 8);
console.log(`Current permissions: ${currentMode.toString(8)}`);

// Set restrictive permissions for sensitive files
if (stats.isFile && currentMode !== 0o600) {
  await sftp.chmod('/remote/file.txt', 0o600);
  console.log('Set restrictive permissions');
}

Advanced File Management Patterns

Atomic File Updates

// Safe file update pattern using temporary file and rename
const targetFile = '/remote/config.json';
const tempFile = `/remote/config.json.tmp.${Date.now()}`;

try {
  // Upload new content to temporary file
  await sftp.put('/local/new-config.json', tempFile);
  
  // Verify upload succeeded
  const exists = await sftp.exists(tempFile);
  if (exists === '-') {
    // Atomically replace original file
    await sftp.posixRename(tempFile, targetFile);
    console.log('File updated atomically');
  }
} catch (err) {
  // Clean up temporary file on error
  await sftp.delete(tempFile, true);
  throw err;
}

Backup Before Modify

// Create backup before modifying important files
const originalFile = '/remote/database.conf';
const backupFile = `/remote/database.conf.backup.${Date.now()}`;

// Create backup
await sftp.rcopy(originalFile, backupFile);

try {
  // Modify original file
  await sftp.put('/local/new-database.conf', originalFile);
  console.log('Configuration updated successfully');
} catch (err) {
  // Restore from backup on error
  console.log('Update failed, restoring backup');
  await sftp.rename(backupFile, originalFile);
  throw err;
}

Cleanup Operations

// Clean up old backup files
const backupPattern = /\.backup\.\d+$/;
const files = await sftp.list('/remote/backups');

const oldBackups = files.filter(file => {
  if (file.type !== '-') return false;
  if (!backupPattern.test(file.name)) return false;
  
  // Keep backups newer than 7 days
  const weekOld = Date.now() - (7 * 24 * 60 * 60 * 1000);
  return file.modifyTime < weekOld;
});

for (const backup of oldBackups) {
  await sftp.delete(`/remote/backups/${backup.name}`);
  console.log(`Deleted old backup: ${backup.name}`);
}

Error Handling

File management errors provide detailed context:

  • ENOENT: File or directory does not exist
  • EACCES: Permission denied
  • EEXIST: Destination already exists (rename)
  • EISDIR: Operation not supported on directory
  • ENOTDIR: Parent is not a directory
  • EXDEV: Cross-device operation not supported
  • Server-specific errors for unsupported operations

Install with Tessl CLI

npx tessl i tessl/npm-ssh2-sftp-client

docs

connection-management.md

directory-operations.md

file-management.md

file-operations.md

filesystem-info.md

index.md

stream-operations.md

tile.json