{"id":13406572,"url":"https://github.com/steelbrain/node-ssh","last_synced_at":"2025-05-14T20:08:15.670Z","repository":{"id":24978385,"uuid":"28396795","full_name":"steelbrain/node-ssh","owner":"steelbrain","description":"SSH2 with Promises","archived":false,"fork":false,"pushed_at":"2025-03-20T00:09:19.000Z","size":872,"stargazers_count":973,"open_issues_count":58,"forks_count":96,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-05-09T11:16:52.541Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://node-ssh.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/steelbrain.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-12-23T12:24:32.000Z","updated_at":"2025-04-28T17:40:08.000Z","dependencies_parsed_at":"2024-10-26T02:39:39.267Z","dependency_job_id":"cb96fa9f-68d6-411f-a8fc-21660c1c3748","html_url":"https://github.com/steelbrain/node-ssh","commit_stats":null,"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steelbrain%2Fnode-ssh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steelbrain%2Fnode-ssh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steelbrain%2Fnode-ssh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steelbrain%2Fnode-ssh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/steelbrain","download_url":"https://codeload.github.com/steelbrain/node-ssh/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254219373,"owners_count":22034397,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T19:02:33.858Z","updated_at":"2025-05-14T20:08:15.628Z","avatar_url":"https://github.com/steelbrain.png","language":"TypeScript","readme":"Node-SSH - SSH2 with Promises\n=========\n\nNode-SSH is an extremely lightweight Promise wrapper for [ssh2][ssh2].\n\n#### Installation\n\n```sh\n$ npm install node-ssh # If you're using npm\n$ yarn add node-ssh # If you're using Yarn\n```\n\n#### Example\n\n```js\nconst fs = require('fs')\nconst path = require('path')\nconst {NodeSSH} = require('node-ssh')\n\nconst ssh = new NodeSSH()\n\nssh.connect({\n  host: 'localhost',\n  username: 'steel',\n  privateKeyPath: '/home/steel/.ssh/id_rsa'\n})\n\n// or with inline privateKey\n\nssh.connect({\n  host: 'localhost',\n  username: 'steel',\n  privateKey: Buffer.from('...')\n})\n.then(function() {\n  // Local, Remote\n  ssh.putFile('/home/steel/Lab/localPath/fileName', '/home/steel/Lab/remotePath/fileName').then(function() {\n    console.log(\"The File thing is done\")\n  }, function(error) {\n    console.log(\"Something's wrong\")\n    console.log(error)\n  })\n  // Array\u003cShape('local' =\u003e string, 'remote' =\u003e string)\u003e\n  ssh.putFiles([{ local: '/home/steel/Lab/localPath/fileName', remote: '/home/steel/Lab/remotePath/fileName' }]).then(function() {\n    console.log(\"The File thing is done\")\n  }, function(error) {\n    console.log(\"Something's wrong\")\n    console.log(error)\n  })\n  // Local, Remote\n  ssh.getFile('/home/steel/Lab/localPath', '/home/steel/Lab/remotePath').then(function(Contents) {\n    console.log(\"The File's contents were successfully downloaded\")\n  }, function(error) {\n    console.log(\"Something's wrong\")\n    console.log(error)\n  })\n  // Putting entire directories\n  const failed = []\n  const successful = []\n  ssh.putDirectory('/home/steel/Lab', '/home/steel/Lab', {\n    recursive: true,\n    concurrency: 10,\n    // ^ WARNING: Not all servers support high concurrency\n    // try a bunch of values and see what works on your server\n    validate: function(itemPath) {\n      const baseName = path.basename(itemPath)\n      return baseName.substr(0, 1) !== '.' \u0026\u0026 // do not allow dot files\n             baseName !== 'node_modules' // do not allow node_modules\n    },\n    tick: function(localPath, remotePath, error) {\n      if (error) {\n        failed.push(localPath)\n      } else {\n        successful.push(localPath)\n      }\n    }\n  }).then(function(status) {\n    console.log('the directory transfer was', status ? 'successful' : 'unsuccessful')\n    console.log('failed transfers', failed.join(', '))\n    console.log('successful transfers', successful.join(', '))\n  })\n  // Command\n  ssh.execCommand('hh_client --json', { cwd:'/var/www' }).then(function(result) {\n    console.log('STDOUT: ' + result.stdout)\n    console.log('STDERR: ' + result.stderr)\n  })\n  // Command with escaped params\n  ssh.exec('hh_client', ['--json'], { cwd: '/var/www', stream: 'stdout', options: { pty: true } }).then(function(result) {\n    console.log('STDOUT: ' + result)\n  })\n  // With streaming stdout/stderr callbacks\n  ssh.exec('hh_client', ['--json'], {\n    cwd: '/var/www',\n    onStdout(chunk) {\n      console.log('stdoutChunk', chunk.toString('utf8'))\n    },\n    onStderr(chunk) {\n      console.log('stderrChunk', chunk.toString('utf8'))\n    },\n  })\n})\n```\n\n#### API\n\n```ts\n// API reference in Typescript typing format:\nimport SSH2, {\n  AcceptConnection,\n  Channel,\n  ClientChannel,\n  ConnectConfig,\n  ExecOptions,\n  Prompt,\n  PseudoTtyOptions,\n  RejectConnection,\n  SFTPWrapper,\n  ShellOptions,\n  TcpConnectionDetails,\n  TransferOptions,\n  UNIXConnectionDetails,\n} from 'ssh2'\nimport stream from 'stream'\n\n// ^ You do NOT need to import these package, these are here for reference of where the\n// types are coming from.\n\nexport type Config = ConnectConfig \u0026 {\n  password?: string\n  privateKey?: string\n  privateKeyPath?: string\n  tryKeyboard?: boolean\n  onKeyboardInteractive?: (\n    name: string,\n    instructions: string,\n    lang: string,\n    prompts: Prompt[],\n    finish: (responses: string[]) =\u003e void,\n  ) =\u003e void\n}\nexport interface SSHExecCommandOptions {\n  cwd?: string\n  stdin?: string | stream.Readable\n  execOptions?: ExecOptions\n  encoding?: BufferEncoding\n  noTrim?: boolean\n  onChannel?: (clientChannel: ClientChannel) =\u003e void\n  onStdout?: (chunk: Buffer) =\u003e void\n  onStderr?: (chunk: Buffer) =\u003e void\n}\nexport interface SSHExecCommandResponse {\n  stdout: string\n  stderr: string\n  code: number | null\n  signal: string | null\n}\nexport interface SSHExecOptions extends SSHExecCommandOptions {\n  stream?: 'stdout' | 'stderr' | 'both'\n}\nexport interface SSHPutFilesOptions {\n  sftp?: SFTPWrapper | null\n  concurrency?: number\n  transferOptions?: TransferOptions\n}\nexport interface SSHGetPutDirectoryOptions extends SSHPutFilesOptions {\n  tick?: (localFile: string, remoteFile: string, error: Error | null) =\u003e void\n  validate?: (path: string) =\u003e boolean\n  recursive?: boolean\n}\nexport type SSHMkdirMethod = 'sftp' | 'exec'\nexport type SSHForwardInListener = (\n  details: TcpConnectionDetails,\n  accept: AcceptConnection\u003cClientChannel\u003e,\n  reject: RejectConnection,\n) =\u003e void\nexport interface SSHForwardInDetails {\n  dispose(): Promise\u003cvoid\u003e\n  port: number\n}\nexport type SSHForwardInStreamLocalListener = (\n  info: UNIXConnectionDetails,\n  accept: AcceptConnection,\n  reject: RejectConnection,\n) =\u003e void\nexport interface SSHForwardInStreamLocalDetails {\n  dispose(): Promise\u003cvoid\u003e\n}\nexport class SSHError extends Error {\n  code: string | null\n  constructor(message: string, code?: string | null)\n}\n\nexport class NodeSSH {\n  connection: SSH2.Client | null\n\n  connect(config: Config): Promise\u003cthis\u003e\n  isConnected(): boolean\n  requestShell(options?: PseudoTtyOptions | ShellOptions | false): Promise\u003cClientChannel\u003e\n  withShell(\n    callback: (channel: ClientChannel) =\u003e Promise\u003cvoid\u003e,\n    options?: PseudoTtyOptions | ShellOptions | false,\n  ): Promise\u003cvoid\u003e\n  requestSFTP(): Promise\u003cSFTPWrapper\u003e\n  withSFTP(callback: (sftp: SFTPWrapper) =\u003e Promise\u003cvoid\u003e): Promise\u003cvoid\u003e\n  execCommand(givenCommand: string, options?: SSHExecCommandOptions): Promise\u003cSSHExecCommandResponse\u003e\n  exec(\n    command: string,\n    parameters: string[],\n    options?: SSHExecOptions \u0026 {\n      stream?: 'stdout' | 'stderr'\n    },\n  ): Promise\u003cstring\u003e\n  exec(\n    command: string,\n    parameters: string[],\n    options?: SSHExecOptions \u0026 {\n      stream: 'both'\n    },\n  ): Promise\u003cSSHExecCommandResponse\u003e\n  mkdir(path: string, method?: SSHMkdirMethod, givenSftp?: SFTPWrapper | null): Promise\u003cvoid\u003e\n  getFile(\n    localFile: string,\n    remoteFile: string,\n    givenSftp?: SFTPWrapper | null,\n    transferOptions?: TransferOptions | null,\n  ): Promise\u003cvoid\u003e\n  putFile(\n    localFile: string,\n    remoteFile: string,\n    givenSftp?: SFTPWrapper | null,\n    transferOptions?: TransferOptions | null,\n  ): Promise\u003cvoid\u003e\n  putFiles(\n    files: {\n      local: string\n      remote: string\n    }[],\n    { concurrency, sftp: givenSftp, transferOptions }?: SSHPutFilesOptions,\n  ): Promise\u003cvoid\u003e\n  putDirectory(\n    localDirectory: string,\n    remoteDirectory: string,\n    { concurrency, sftp: givenSftp, transferOptions, recursive, tick, validate }?: SSHGetPutDirectoryOptions,\n  ): Promise\u003cboolean\u003e\n  getDirectory(\n    localDirectory: string,\n    remoteDirectory: string,\n    { concurrency, sftp: givenSftp, transferOptions, recursive, tick, validate }?: SSHGetPutDirectoryOptions,\n  ): Promise\u003cboolean\u003e\n  forwardIn(remoteAddr: string, remotePort: number, onConnection?: SSHForwardInListener): Promise\u003cSSHForwardInDetails\u003e\n  forwardOut(srcIP: string, srcPort: number, dstIP: string, dstPort: number): Promise\u003cChannel\u003e\n  forwardInStreamLocal(\n    socketPath: string,\n    onConnection?: SSHForwardInStreamLocalListener,\n  ): Promise\u003cSSHForwardInStreamLocalDetails\u003e\n  forwardOutStreamLocal(socketPath: string): Promise\u003cChannel\u003e\n  dispose(): void\n}\n```\n\n### Typescript support\n\n`node-ssh` requires extra dependencies while working under Typescript. Please install them as shown below\n\n```\nyarn add --dev @types/ssh2\n# OR\nnpm install --save-dev @types/ssh2\n```\n\nIf you're still running into issues, try adding these to your `tsconfig.json`\n\n```json\n{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"allowSyntheticDefaultImports\": true\n  }\n}\n```\n\n### Keyboard-interactive user authentication\n\nIn some cases you have to enable keyboard-interactive user authentication.\nOtherwise you will get an `All configured authentication methods failed` error.\n\n#### Example:\n\n```js\nconst password = 'test'\n\nssh.connect({\n  host: 'localhost',\n  username: 'steel',\n  port: 22,\n  password,\n  tryKeyboard: true,\n})\n\n// Or if you want to add some custom keyboard-interactive logic:\n\nssh.connect({\n  host: 'localhost',\n  username: 'steel',\n  port: 22,\n  tryKeyboard: true,\n  onKeyboardInteractive(name, instructions, instructionsLang, prompts, finish) {\n    if (prompts.length \u003e 0 \u0026\u0026 prompts[0].prompt.toLowerCase().includes('password')) {\n      finish([password])\n    }\n  }\n})\n```\n\nFor further information see: https://github.com/mscdex/ssh2/issues/604\n\n### License\nThis project is licensed under the terms of MIT license. See the LICENSE file for more info.\n\n[ssh2]:https://github.com/mscdex/ssh2\n","funding_links":[],"categories":["TypeScript","Uncategorized"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteelbrain%2Fnode-ssh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsteelbrain%2Fnode-ssh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteelbrain%2Fnode-ssh/lists"}