Full Code of Natizyskunk/vscode-sftp for AI

develop 4dece7b57519
144 files
377.0 KB
100.3k tokens
Showing preview only (411K chars total). The displayed content is truncated. Use the JSON API for full output.
Repository: Natizyskunk/vscode-sftp
Branch: develop
Commit: 4dece7b57519
Files: 144
Total size: 377.0 KB

Directory structure:
gitextract_fvgxrgqp/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature_request.md
│   │   └── pull_request.md
│   ├── config.yml
│   ├── dependabot.yml
│   ├── holopin.yml
│   └── workflows/
│       ├── codeql-analysis.yml
│       └── devskim-analysis.yml
├── .gitignore
├── .vscode/
│   ├── launch.json
│   └── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── FAQ.md
├── LICENSE
├── README.md
├── __mocks__/
│   ├── fs.js
│   └── vscode.js
├── docs/
│   ├── commands.md
│   ├── common_configuration.md
│   ├── configuration.md
│   ├── ftp_configuration.md
│   ├── home.md
│   ├── setting.md
│   └── sftp_configuration.md
├── package.json
├── schema/
│   ├── config.schema.json
│   ├── definitions.json
│   ├── ftp.schema.json
│   ├── sftp.schema.json
│   └── simple-config.schema.json
├── src/
│   ├── app.ts
│   ├── commands/
│   │   ├── abstract/
│   │   │   ├── command.ts
│   │   │   └── createCommand.ts
│   │   ├── commandCancelAllTransfer.ts
│   │   ├── commandConfig.ts
│   │   ├── commandListActiveFolder.ts
│   │   ├── commandOpenSshConnection.ts
│   │   ├── commandSetProfile.ts
│   │   ├── commandToggleOutputPanel.ts
│   │   ├── commandUploadChangedFiles.ts
│   │   ├── fileCommandCreateFile.ts
│   │   ├── fileCommandCreateFolder.ts
│   │   ├── fileCommandDeleteRemote.ts
│   │   ├── fileCommandDiff.ts
│   │   ├── fileCommandDiffActiveFile.ts
│   │   ├── fileCommandDownload.ts
│   │   ├── fileCommandDownloadActiveFile.ts
│   │   ├── fileCommandDownloadActiveFolder.ts
│   │   ├── fileCommandDownloadFile.ts
│   │   ├── fileCommandDownloadFolder.ts
│   │   ├── fileCommandDownloadForce.ts
│   │   ├── fileCommandDownloadProject.ts
│   │   ├── fileCommandEditInLocal.ts
│   │   ├── fileCommandList.ts
│   │   ├── fileCommandListAll.ts
│   │   ├── fileCommandRevealInExplorer.ts
│   │   ├── fileCommandRevealInRemoteExplorer.ts
│   │   ├── fileCommandSyncBothDirections.ts
│   │   ├── fileCommandSyncLocalToRemote.ts
│   │   ├── fileCommandSyncRemoteToLocal.ts
│   │   ├── fileCommandUpload.ts
│   │   ├── fileCommandUploadActiveFile.ts
│   │   ├── fileCommandUploadActiveFolder.ts
│   │   ├── fileCommandUploadFile.ts
│   │   ├── fileCommandUploadFolder.ts
│   │   ├── fileCommandUploadForce.ts
│   │   ├── fileCommandUploadProject.ts
│   │   ├── fileMultiCommandUploadActiveFileToAllProfiles.ts
│   │   ├── fileMultiCommandUploadActiveFolderToAllProfiles.ts
│   │   ├── fileMultiCommandUploadFileToAllProfiles.ts
│   │   ├── fileMultiCommandUploadFolderToAllProfiles.ts
│   │   ├── fileMultiCommandUploadForceToAllProfiles.ts
│   │   ├── fileMultiCommandUploadProjectToAllProfiles.ts
│   │   ├── fileMultiCommandUploadToAllProfiles.ts
│   │   └── shared.ts
│   ├── constants.ts
│   ├── core/
│   │   ├── customError.ts
│   │   ├── fileBaseOperations.ts
│   │   ├── fileService.ts
│   │   ├── fs/
│   │   │   ├── fileSystem.ts
│   │   │   ├── ftpFileSystem.ts
│   │   │   ├── index.ts
│   │   │   ├── localFileSystem.ts
│   │   │   ├── remoteFileSystem.ts
│   │   │   └── sftpFileSystem.ts
│   │   ├── ignore.ts
│   │   ├── index.ts
│   │   ├── localFs.ts
│   │   ├── remote-client/
│   │   │   ├── ftpClient.ts
│   │   │   ├── index.ts
│   │   │   ├── remoteClient.ts
│   │   │   └── sshClient.ts
│   │   ├── remoteFs.ts
│   │   ├── scheduler.ts
│   │   ├── transferTask.ts
│   │   ├── uResource.ts
│   │   └── upath.ts
│   ├── extension.ts
│   ├── fileHandlers/
│   │   ├── create.ts
│   │   ├── createFileHandler.ts
│   │   ├── diff.ts
│   │   ├── index.ts
│   │   ├── option.ts
│   │   ├── remove.ts
│   │   ├── rename.ts
│   │   ├── shared.ts
│   │   └── transfer/
│   │       ├── __tests__/
│   │       │   └── transfer-test.ts
│   │       ├── index.ts
│   │       └── transfer.ts
│   ├── helper/
│   │   ├── error.ts
│   │   ├── file.ts
│   │   ├── index.ts
│   │   ├── paths.ts
│   │   └── select.ts
│   ├── host.ts
│   ├── initCommands.ts
│   ├── logger.ts
│   ├── modules/
│   │   ├── appState.ts
│   │   ├── config.ts
│   │   ├── ext.ts
│   │   ├── fileActivityMonitor.ts
│   │   ├── fileWatcher.ts
│   │   ├── git/
│   │   │   ├── git.d.ts
│   │   │   └── index.ts
│   │   ├── remoteExplorer/
│   │   │   ├── explorer.ts
│   │   │   ├── index.ts
│   │   │   └── treeDataProvider.ts
│   │   └── serviceManager/
│   │       ├── index.ts
│   │       └── trie.ts
│   ├── ui/
│   │   ├── output.ts
│   │   └── statusBarItem.ts
│   └── utils.ts
├── test/
│   ├── config.spec.js
│   ├── core/
│   │   └── scheduler.spec.js
│   ├── helper/
│   │   └── localRemoteFs.ts
│   ├── index.ts
│   ├── preprocessor.js
│   └── trie.spec.js
├── tsconfig.json
├── tslint.json
└── webpack.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: Natizyskunk
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://www.buymeacoffee.com/Natizyskunk', 'https://www.paypal.com/donate?business=DELD7APHHM3BC&no_recurring=0&currency_code=EUR', 'https://paypal.me/natanfourie']


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Do you read the FAQ?**
- [ ] Yes.
- [ ] [I am going to read now.](https://github.com/Natizyskunk/vscode-sftp/blob/master/FAQ.md)

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Run command '....'
3. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. Mac, Win, Linux]
 - VSCode Version [e.g. 1.51.0]
 - Extension Version [e.g. 1.7.0]

**Extension Logs from Startup** - *required*
  1.  Open User Settings.

      * On Windows/Linux - File > Preferences > Settings
      * On macOS - Code > Preferences > Settings
  2. Set `sftp.debug` to `true` and reload vscode.
  3. Reproduce the problem, get the logs from View > Output > sftp.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is this a similar or duplicate feature request?**
- [ ] I don't know. I will go check it.
- [ ] No.

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Does this project help you?**
- [x] Yes. SFTP IS AWESOME!


================================================
FILE: .github/ISSUE_TEMPLATE/pull_request.md
================================================
---
name: Pull request
about: Create a Pull Request on this project
title: ''
labels: ''
assignees: ''

---

**Description of the change**:

**Reason for the change**:

**Link to original source**:

<!--
If this pull request closes an issue, add in the issue number here
-->
Closes #


================================================
FILE: .github/config.yml
================================================
# Configuration for request-info - https://github.com/behaviorbot/request-info

# *OPTIONAL* Comment to reply with
# Can be either a string :
requestInfoReplyComment: >
  We would appreciate it if you could provide us with more info about this issue!
  
# *OPTIONAL* default titles to check against for lack of descriptiveness
# MUST BE ALL LOWERCASE
requestInfoDefaultTitles:
  - update readme.md
  - updates

# *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given
requestInfoLabelToAdd: needs-more-info

# *OPTIONAL* Require Issues to contain more information than what is provided in the issue templates
# Will fail if the issue's body is equal to a provided template
checkIssueTemplate: true

# *OPTIONAL* Require Pull Requests to contain more information than what is provided in the PR template
# Will fail if the pull request's body is equal to the provided template
checkPullRequestTemplate: true

# *OPTIONAL* Only warn about insufficient information on these events type
# Keys must be lowercase. Valid values are 'issue' and 'pullRequest'
requestInfoOn:
  pullRequest: true
  issue: true

# *OPTIONAL* Add a list of people whose Issues/PRs will not be commented on
# keys must be GitHub usernames
requestInfoUserstoExclude:
  - Natizyskunk


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "npm" # See documentation for possible values
    directory: "/" # Location of package manifests
    schedule:
      interval: "daily"


================================================
FILE: .github/holopin.yml
================================================
organization: Natan FOURIÉ EIRL
defaultSticker: cl8p9eem9629909kycm8gkafb
stickers:
  -
    id: cl8p9eem9629909kycm8gkafb
    alias: hacktoberfest-2022-registered
  -
   id: cla3qeh72120908mppm4t7dgp
   alias: hacktoberfest-2022-level-4
  -
   id: cla3qe9bp105308mp12ty479d
   alias: surfer-moby-dock
  -
   id: clarpdd11082308jl4uo3euvl
   alias: appwrite-hacktoberfest-2022


================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ master ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ master ]
  schedule:
    - cron: '34 9 * * 6'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'javascript' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
        # Learn more:
        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v1
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a configuration file.
        # By default, queries listed here will override any specified in a configuration file.
        # Prefix the list here with "+" to use these queries and those in the configuration file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v1

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v1


================================================
FILE: .github/workflows/devskim-analysis.yml
================================================
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: DevSkim

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
  schedule:
    - cron: '45 8 * * 1'

jobs:
  lint:
    name: DevSkim
    runs-on: ubuntu-20.04
    permissions:
      actions: read
      contents: read
      security-events: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Run DevSkim scanner
        uses: microsoft/DevSkim-Action@v1
        
      - name: Upload DevSkim scan results to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v1
        with:
          sarif_file: devskim-results.sarif


================================================
FILE: .gitignore
================================================
### Default ###
.DS_Store
out
dist
test/test.js
_debug
TODO.md
INSTALL_DEBUG.md

### Extensions ###
sftp-*.vsix

### VS Code ###
.vscode/**
!.vscode/tasks.json
!.vscode/launch.json

### Node ###
logs
*.log
node_modules
build/Release
.npm

### Yarn ###
.yarn/*
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
!.yarn/cache

### Bower ###
bower_components
.bower-cache
.bower-registry
.bower-tmp


================================================
FILE: .vscode/launch.json
================================================
// A launch configuration that compiles the extension and then opens it inside a new window
{
    "version": "0.1.0",
    "configurations": [
        {
            "name": "Launch Extension",
            "type": "extensionHost",
            "request": "launch",
            "runtimeExecutable": "${execPath}",
            "args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
            "stopOnEntry": false,
            "sourceMaps": true,
            "outFiles": [ "${workspaceRoot}/dist/**/*.js" ],
            "preLaunchTask": "npm"
        },
        {
            "name": "Launch Tests",
            "type": "extensionHost",
            "request": "launch",
            "runtimeExecutable": "${execPath}",
            "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
            "stopOnEntry": false,
            "sourceMaps": true,
            "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ],
            "preLaunchTask": "npm"
        }
    ]
}


================================================
FILE: .vscode/tasks.json
================================================
// Available variables which can be used inside of strings.
// ${workspaceRoot}: the root folder of the team
// ${file}: the current opened file
// ${fileBasename}: the current opened file's basename
// ${fileDirname}: the current opened file's dirname
// ${fileExtname}: the current opened file's extension
// ${cwd}: the current working directory of the spawned process

// A task runner that calls a custom npm script that compiles the extension.
{
  "version": "2.0.0",

  // we want to run npm
  "command": "npm",

  "type": "shell",

  "presentation": {
    "reveal": "silent"
  },

  // we run the custom script "dev" as defined in package.json
  "args": ["run", "dev", "--", "--display", "minimal"],

  // The dev is started in watching mode
  "isBackground": true,

  // use the standard tsc in watch mode problem matcher to find compile problems in the output.
  "problemMatcher": {
    "owner": "webpack",
    "fileLocation": "relative",
    "severity": "error",
    "pattern": [
      {
        "regexp": "^(.*)\\s+in\\s+(.*)\\s(\\d+):(\\d+)-(\\d+)$",
        "severity": 1,
        "file": 2,
        "line": 3,
        "column": 4,
        "endColumn": 5
      },
      {
        "regexp": "^(?!Hash|Time|Version|Built at).+:\\s+(.*)$",
        "message": 0
      }
    ],
    "background": {
      "activeOnStart": true,
      "beginsPattern": "^$",
      "endsPattern": "^$"
    }
  }
}


================================================
FILE: .vscodeignore
================================================
*
*/**
**/.DS_Store
!node_modules/**/*
!dist/extension.js
!package.json
!README.md
!CHANGELOG.md
!LICENSE
!resources/**/*
!schema/**/*

================================================
FILE: CHANGELOG.md
================================================
## 1.16.3 - 2023-06-16
* [#356] New Feature : Upload to all profiles (Pull request [#313](https://github.com/Natizyskunk/vscode-sftp/pull/313) from @wewawa vscode-sftp:create_multi_command).
* [#357] Fix : Correcting Typo 'avaliable' => 'available' (Pull request [#343](https://github.com/Natizyskunk/vscode-sftp/pull/343) from @kjo-sdds vscode-sftp:develop).
* [#358] Permissions : Add filePerm and dirPerm options for configuring permissions (Pull request [#347](https://github.com/Natizyskunk/vscode-sftp/pull/347) from @Jchase2 vscode-sftp:develop).
* [#359] Fix : Correcting sftp connection with public key (Pull request [#350](https://github.com/Natizyskunk/vscode-sftp/pull/350) from @inu1255 vscode-sftp:develop).
* Upgrade `ssh2` version to official v1.13.0 by @mscdex.

## pre-1.16.2 - 2022-11-30
* [#271] Fix case change of file name not sent correctly (Pull request [#249](https://github.com/Natizyskunk/vscode-sftp/pull/249) from @NyaPPuu vscode-sftp:fix_rename).
* [#272] Update npm `types/node` depedency to v9.6.51.

## 1.16.1 - 2022-11-02
* [#251] Add multiple select + Update `Download File` & `Downalod Folder` commands to the remote view + Add `Upload File` & `Upload Folder` commands to the remote view (Pull request [#221](https://github.com/Natizyskunk/vscode-sftp/pull/221) from @NyaPPuu vscode-sftp:add_multiple_select).

## 1.16.0 - 2022-10-29
* [#242] Add order option and fix typos in docs (Pull request [#157](https://github.com/Natizyskunk/vscode-sftp/pull/157) from @NyaPPuu vscode-sftp:add_order_option).
* [#243] Fix refresh when creating/deleting file/folder + Fix 'Reveal in Remote Explorer' and Refresh Button in Remote Explorer. (Pull request [#159](https://github.com/Natizyskunk/vscode-sftp/pull/159) from @NyaPPuu vscode-sftp:fix_refresh).
* [#244] Cleanup Text in Markdown Files (Pull request [#213](https://github.com/Natizyskunk/vscode-sftp/pull/213) from @BrayFlex vscode-sftp:develop).

## 1.15.20 - 2022-08-28
* Fix typo 'worksapce' to 'workspace' (Pull request [#158](https://github.com/Natizyskunk/vscode-sftp/pull/158) from @NyaPPuu vscode-sftp:fix_typo).
* Add `Download File` & `Downalod Folder` commands to the remote view (Thanks to @mrandrey on issue #97).
* Update npm `types/fs-extra` depedency to v9.0.13 (Pull request [#204](https://github.com/Natizyskunk/vscode-sftp/pull/204) from @dependabot vscode-sftp:dependabot/npm_and_yarn/types/fs-extra-9.0.13).
* Update npm `typescript-tslint-plugin` depedency to v1.0.2 (Pull request [#206](https://github.com/Natizyskunk/vscode-sftp/pull/206) from @dependabot vscode-sftp:dependabot/npm_and_yarn/typescript-tslint-plugin-1.0.2).
* Update npm `tslint` depedency to v6.1.3 (Pull request [#207](https://github.com/Natizyskunk/vscode-sftp/pull/207) from @dependabot vscode-sftp:dependabot/npm_and_yarn/tslint-6.1.3).
* Update npm `ts-loader` depedency to v9.4.1 (Pull request [#208](https://github.com/Natizyskunk/vscode-sftp/pull/208) from @dependabot vscode-sftp:dependabot/npm_and_yarn/ts-loader-9.4.1).
* Update npm `typescript` depedency to v3.9.7.
* Update npm `jest` depedency to v29.0.3.

## 1.15.19 - 2022-08-26
* [#72] Change `uploadOnSave` default value from true to false.

## 1.15.18 - 2022-08-26
* Update npm `async` depedency to v3.2.4.
* Update npm `fs-extra` depedency to v10.1.0.
* Update npm `tmp` depedency to v0.2.1.
* Update npm `upath` depedency to v2.0.1.

## 1.15.17 - 2022-08-26
* Upgrade `ssh2` version to official v1.11.0 by @mscdex.

## 1.15.16 - 2022-05-26
* Reorder cipher and serverHostKey algorithms.
* Update [FAQ.md](https://github.com/Natizyskunk/vscode-sftp/blob/master/FAQ.md), and [documentations](https://github.com/Natizyskunk/vscode-sftp/tree/master/docs).

## 1.15.15 - 2022-08-21
* Fix "Open SSH in Terminal" not working because "terminal.integrated.shell.windows" is deprecated and fix typo `src/commands/commandOpenSshConnection.ts`. (Pull request [#155](https://github.com/Natizyskunk/vscode-sftp/pull/155) from @mean-cj vscode-sftp:patch-2).

## 1.15.14 - 2022-05-06
* Update npm `async` depedency to v2.6.4.
* Update npm `minimist` depedency to v1.2.6.

## 1.15.13 - 2022-02-11
* Add support for OpenSSH v8.8 SSH private key by using SHA-2 instead of SHA-1 to fix SSH public key signatures. (See issue [#112](https://github.com/Natizyskunk/vscode-sftp/issues/112)).

## 1.15.12 - 2022-02-11
* Add deletions support to "Upload Changed files" command. (Pull request [#113](https://github.com/Natizyskunk/vscode-sftp/pull/113) from @brykov vscode-sftp:master merged inside [#117](https://github.com/Natizyskunk/vscode-sftp/pull/117)).

* ## 1.15.11 - 2022-02-09
* Enhance sftp interactiveAuth mode (See [Wiki](https://github.com/Natizyskunk/vscode-sftp/wiki/SFTP-only-Configuration#interactiveauth)). (Pull request [#94](https://github.com/Natizyskunk/vscode-sftp/pull/94) from @lacastorine vscode-sftp:lacastorine merged inside [#114](https://github.com/Natizyskunk/vscode-sftp/pull/114)).

## 1.15.10 - 2021-11-22
* Update npm `json-schema` devDepedency to v0.2.3.

## 1.15.9 - 2021-11-21
* Remove ssh configuration bug introduced in pull request [#69](https://github.com/Natizyskunk/vscode-sftp/pull/69) from @clemyan while we can find another solution.

## 1.15.8 - 2021-11-12
  * Fix 'Upload Changed Files' & 'No Such File' bugs (Commit [fix upload changed files](https://github.com/wandway/vscode-sftp/commit/775016788e4c59db901dc68a20c1f61ebcca7bc7#diff-20516d8841b4891f1926f1e40e447e99e0575a5e36ba6814f6b85b45db1b8fbb) from @wandway vscode-sftp:master).
  * Make the 'Upload Changed Files' command visible and add a default keyboard shortcut (Ctrl+Alt+U) to call it (Merged pull request [#84](https://github.com/Natizyskunk/vscode-sftp/pull/84) from @PaPa31 vscode-sftp:master). See [FAQ](https://github.com/Natizyskunk/vscode-sftp/blob/master/FAQ.md#clicking-upload-changed-files-does-not-work)).
  * Update Webpack from 4.39.2 to 5.0.0.
  * Update Webpack-cli from 3.3.7 to 4.7.0.

## 1.15.7 - 2021-11-12
  * Upgrade `ssh2` version to official v1.5.0 by @mscdex.

## 1.15.6 - 2021-10-27
  * Fix ssh configuration resolution (Merged pull request [#69](https://github.com/Natizyskunk/vscode-sftp/pull/69) from @clemyan vscode-sftp:fix-ssh-config).

## 1.15.5 - 2021-10-27
  * Update mtime after file was saved before upload (Merged pull request [#75](https://github.com/Natizyskunk/vscode-sftp/pull/75) from @viperet vscode-sftp:save_before_upload_mtime).
  * Add pull request issue template.
  * Add funding/sponsors page.
  * Add code scanning alert.

## 1.15.4 - 2021-10-04
  * Remove error message when calling sftp.sync.remoteToLocal command in vscode tasks.json.

## 1.15.3 - 2021-09-10
  * Upgrade `ssh2` version to official v1.4.0 bcy @mscdex.

## 1.15.2 - 2021-08-24
  * Fix the `useTempFile` bug (Merged pull request [#41](https://github.com/Natizyskunk/vscode-sftp/pull/41) from @kripper vscode-sftp:master).
  * Change `useTempFile` default value from true to false.
  * Fix the "Cannot read property 'handle' of undefined" bug (related to `useTempFile` bug) [TypeError: Cannot read property 'handle' of undefined](https://github.com/Natizyskunk/vscode-sftp/issues/43).
  * Fix the "fd argument must be of type number. Received undefined" bug (related to `useTempFile` bug) [TypeError since last update (The "fd" argument must be of type number.)](https://github.com/Natizyskunk/vscode-sftp/issues/34).
  * Fix the "Permission denied" bug when uploading.
  * New option [openSsh](https://github.com/Natizyskunk/vscode-sftp/wiki/Common-Configuration#openssh) (Pull request [#42](https://github.com/Natizyskunk/vscode-sftp/pull/42) from @kripper vscode-sftp:atomic-rename merged inside [#45](https://github.com/Natizyskunk/vscode-sftp/pull/45)).
  * Update of the wiki to add support for openSsh option.

## 1.15.1 - 2021-08-24
  * Add the `useTempFile` option to the test configuration spec.
  * Fix get target mode error && add more precise logger-infos for tranfer tasks (Merged pull request [#29](https://github.com/Natizyskunk/vscode-sftp/pull/29) from @kripper vscode-sftp:master).

## 1.15.0 - 2021-08-23
  * New option [useTempFile](https://github.com/Natizyskunk/vscode-sftp/wiki/Common-Configuration#usetempfile) (Merged pull request [#29](https://github.com/Natizyskunk/vscode-sftp/pull/29) from @kripper vscode-sftp:master).
  * Update of the wiki to add support for useTempFile option.

## 1.14.0 - 2021-08-06
  * Update of the FAQ to add support for old/legacy systems.
  * switching from beta to stable.

## 1.14.0-beta - 2021-07-15
  * Add `create remote file` and `create remote folder` commands (Merged pull request [#18](https://github.com/Natizyskunk/vscode-sftp/pull/18) from @mathsgod vscode-sftp:master).

## 1.13.6 - 2021-07-15
  * Fix syntax in `src\fileHandlers\transfer\__tests__\transfer-test.ts`.

## 1.13.5 - 2021-07-10
  * Reorder test parameters for `keepalive`.
  * Add v1.13.5-beta. Only use beta version if you still encounter the "REQUEST_FAILURE" error like described in those two issues : [Buffering on save file after 15 minute](https://github.com/Natizyskunk/vscode-sftp/issues/7) & [Infinite spinner on file save after server rest connection with client](https://github.com/Natizyskunk/vscode-sftp/issues/8).

## 1.13.4 - 2021-07-10
  * Fix "Error with the transfer direction."
  * Add loggers for transfer informations.

## 1.13.3 - 2021-07-09
  * re-add braces >=2.3.1 to package.json.
  * re-add yargs-parser ^20.2.4 to package.json.
  * Remove `yarn.lock`.
  * Add `package-lock.json`.
  * Fix Writing CHANNEL_DATA (0) / Writing FSETSTAT (Merged pull request [#12](https://github.com/Natizyskunk/vscode-sftp/pull/12) from @zarausto vscode-sftp:patch-1).
  * Fix transfer-test for Windows platform (Merged pull request [#11](https://github.com/Natizyskunk/vscode-sftp/pull/11) from @alex1504 vscode-sftp:fix-transfer-test).

## 1.13.2 - 2021-07-07
  * remove braces >=2.3.1 to package.json.
  * remove yargs-parser ^20.2.4 to package.json.
  * Remove the fix for the "No such file" error on VSCode 1.56 since it's been implementend in the new ssh2 v1.1.0 npm package (Commit [SFTP: explicitly set autoClose option for node 14+](https://github.com/mscdex/ssh2/commit/c0de05d186065ad4081b98d2f7aa0fe22161ec09) from @mscdex ssh2:master).

## 1.13.1 - 2021-07-06
  * Add braces >=2.3.1 to package.json.
  * Add node-notifier >=8.0.1 to package.json.
  * Add yargs-parser ^20.2.4 to package.json.
  * Changing publisher and repo links.
  * Fixed error "No such file" on VSCode 1.56.
  * Fixed issue with uploading of file which has unsaved changes.

## 1.13.0 - 2021-07-06
  * Upgrade `ssh2` version to official v1.1.0 by @mscdex.

## 1.12.10 - 2021-05-15
  * Improve sftp reliability.

## 1.12.3 - 2019-04-27
  * Minor improvements.
  * Bug fix.

## 1.12.1 - 2019-03-28
  * Fix [#510](https://github.com/liximomo/vscode-sftp/issues/510).

## 1.12.0 - 2019-03-21
  * new option [sshCustomParams](https://github.com/liximomo/vscode-sftp/wiki/SFTP-only-Configuration#sshcustomparams).

## 1.11.0 - 2019-03-15
  * Save before upload.
  * Fix [#490](https://github.com/liximomo/vscode-sftp/issues/490).

## 1.9.4 - 2019-02-26
  * Fix sshConfig file not work.
  * Open SSH in Terminal can enter to remote path.

## 1.9.3 - 2019-01-30
  * New icon for RemoteExplorer. Thanks [niccolomineo](https://github.com/niccolomineo) and [jonbp](https://github.com/jonbp).
  * Change `port` to number in the generated configuration.

## 1.9.2 - 2019-01-22
  * Fix [#388](https://github.com/liximomo/vscode-sftp/issues/388).
  * Fix [#456](https://github.com/liximomo/vscode-sftp/issues/456).
  * Fix [#459](https://github.com/liximomo/vscode-sftp/issues/459).

## 1.9.0 - 2019-01-08
  * Control files and folders to show or hide in Remote Explorer by `remoteExplorer.filesExclude`. [#410](https://github.com/liximomo/vscode-sftp/issues/410).
  * Suport new OpenSSH key format. [#391](https://github.com/liximomo/vscode-sftp/issues/391).
  * Improve performance.

## 1.8.4 - 2018-12-16
  * Fix ignore not work when use profile. [#428](https://github.com/liximomo/vscode-sftp/issues/428).

## 1.8.3 - 2018-12-14
  * Upgrade VSCode engine version.

## 1.8.2 - 2018-12-13
  * Add **Collapse All** action to RemoteExplorer.

## 1.8.0 - 2018-12-06
  * New command [Upload Changed Files](https://github.com/liximomo/vscode-sftp/wiki/Commands#sftp-upload-changed-files).
  * Fix bugs.

## 1.7.6 - 2018-11-22
  * Reduce *80%* startup time.
  * Fix [#396](https://github.com/liximomo/vscode-sftp/issues/396).

## 1.7.5 - 2018-11-15
  * Fix [#394](https://github.com/liximomo/vscode-sftp/issues/394).

## 1.7.4 - 2018-11-09
  * Fix [#362](https://github.com/liximomo/vscode-sftp/issues/362).
  * Don't upload the file when it's in downloading. [#390](https://github.com/liximomo/vscode-sftp/issues/390).

## 1.7.3 - 2018-11-03
  * New configuration [limitOpenFilesOnRemote](https://github.com/liximomo/vscode-sftp/wiki/Configuration#limitopenfilesonremote).
  * Show `upload file` context menu in SCM.

## 1.7.2 - 2018-10-29
  * New command [Open SSH in Terminal](https://github.com/liximomo/vscode-sftp/wiki/Commands#open-ssh-in-terminal).

## 1.7.1 - 2018-10-25
  * New setting [downloadwhenopeninremoteexplorer](https://github.com/liximomo/vscode-sftp/wiki/Setting#downloadwhenopeninremoteexplorer).
  * fix some bugs.

## 1.7.0 - 2018-10-19
### New Features
  * New command [Upload Active Folder](https://github.com/liximomo/vscode-sftp/wiki/Commands#sftp-upload-active-folder).
  * New command [Download Active Folder](https://github.com/liximomo/vscode-sftp/wiki/Commands#sftp-download-active-folder).
  * New command [List Active Folder](https://github.com/liximomo/vscode-sftp/wiki/Commands#sftp-list-active-folder).
  * New command [Cancel All Transfers](https://github.com/liximomo/vscode-sftp/wiki/Commands#cancel-all-transfers).
  * New configuration [remotetimeoffsetinhours](https://github.com/liximomo/vscode-sftp/wiki/Configuration#remotetimeoffsetinhours).

## 1.6.0 - 2018-10-12
### New Features
  * New command [Sync Local -> Remote](https://github.com/liximomo/vscode-sftp/wiki/Commands#sftp-sync-local---remote).
  * New command [Sync Remote -> Local](https://github.com/liximomo/vscode-sftp/wiki/Commands#sftp-sync-remote---local).
  * New command [Sync Both Directions](https://github.com/liximomo/vscode-sftp/wiki/Commands#sftp-sync-both-directions).
  * New configuration [syncOption](https://github.com/liximomo/vscode-sftp/wiki/Configuration#syncoption) for `Sync` command.

### Breaking Changes
  * Remove Command `SFTP: Sync To Remote`.
  * Remove Command `SFTP: Sync To Local`.
  * Remove configuration option `syncModel`.

## 1.5.13 - 2018-10-08
* Fix [#344](https://github.com/liximomo/vscode-sftp/issues/344).

## 1.5.12 - 2018-10-07
* New command `Diff Active File with Remote`.
* Command `Set Profile` can receive an argument from keybindings.

  ```json
  {
    "key": "ctrl+shift+cmd+d",
    "command": "sftp.setProfile",
    "args": "dev"
  }
  ```

## 1.5.10 - 2018-09-28
* Fix [#332](https://github.com/liximomo/vscode-sftp/issues/332).

## 1.5.9 - 2018-09-27
* Fix [#330](https://github.com/liximomo/vscode-sftp/issues/330).

## 1.5.8 - 2018-09-25
* Show name in the remote explorer. [#315](https://github.com/liximomo/vscode-sftp/issues/315).
* Fix [#308](https://github.com/liximomo/vscode-sftp/issues/308).

## 1.5.0 - 2018-09-13
### New Features
  * new [alt commands](https://github.com/liximomo/vscode-sftp#alt-commands) `Force Download` and `Force Upload`. This allow you to download/upload files but disregard ignore rules.

### Breaking Changes
  * Rename command `sftp.trans.remote(SFTP: Upload)` to `sftp.upload.activeFile` and command `sftp.trans.local(SFTP: Download)` to `sftp.download.activeFile`. Please update your keybinding if you've used one of these commands.

### Deprecated
  * Commands `SFTP: List` and `SFTP: List All` will be removed in favor of `Remote Explorer` in next release.

## 1.4.1 - 2018-09-03
### Feature
  * [Configuration in User Setting](https://github.com/liximomo/vscode-sftp#configuration-in-user-setting) Configuration your remote in User Setting.

### Fix
  * Fix sshConfig file not overwriting default configuration. [#305](https://github.com/liximomo/vscode-sftp/issues/305).

## 1.4.0 - 2018-08-27
### Feature
  * [Connection Hopping](https://github.com/liximomo/vscode-sftp#connection-hopping) allow you to connection to a target server through a proxy with ssh protocol.

## 1.3.9 - 2018-08-14
* Fix [#286](https://github.com/liximomo/vscode-sftp/issues/286).
* Fix [#287](https://github.com/liximomo/vscode-sftp/issues/287).

## 1.3.8 - 2018-08-13
* Fix [#285](https://github.com/liximomo/vscode-sftp/issues/285).

## 1.3.7 - 2018-08-10
* Fix bug in `remoteExplorer.refresh`.

## 1.3.0 - 2018-08-02
### New Features
  * [Remote Explorer](https://github.com/liximomo/vscode-sftp#remote-explorer).

## 1.2.7 - 2018-07-27
### New Features
  * `ignoreFile` [option](https://github.com/liximomo/vscode-sftp/wiki/Configuration#ignorefile).

## 1.2.3 - 2018-06-19
### New Features
  * [Swtichable Profiles](https://github.com/liximomo/vscode-sftp/#profiles).

## 1.2.0 - 2018-06-19
* Support [SSH configuration file](https://www.ssh.com/ssh/config/). The default ssh configuration file is `~/.ssh/config`. This can be changed by `sshConfigPath` option.

## 1.1.12 - 2018-06-08
* Fix [#200](https://github.com/liximomo/vscode-sftp/issues/200). Thanks for [Gergo Koos](https://github.com/gergokoos).

## 1.1.11 - 2018-05-21
* Fix [#198](https://github.com/liximomo/vscode-sftp/issues/198).

## 1.1.10 - 2018-05-18
* Show open folder prompt in `sftp:config` command.
* Fix [#174](https://github.com/liximomo/vscode-sftp/issues/174).

## 1.1.9 - 2018-05-17
* Add `confirm` option to `downloadOnOpen`.
* Fix [#160](https://github.com/liximomo/vscode-sftp/issues/160).
* Fix [#195](https://github.com/liximomo/vscode-sftp/issues/195).

## 1.1.8 - 2018-05-15
* Some UX improvements.
    * Only show `sftp` menu when extension get activated (Thanks [@mikolino](https://github.com/mikolino)).
    * Remove some unnecessary warning.
* Improve ftp reliability.
* Upgrade `ssh2` version.

## 1.1.7 - 2018-03-31
* `name` [configuration](https://github.com/liximomo/vscode-sftp#full-config).
* Fix bugs.

## 1.1.6 - 2018-03-24
* Better procedure message in status bar.
* Fix sync error when synced target is not exist.
* Fix [#146](https://github.com/liximomo/vscode-sftp/issues/146).

## 1.1.5 - 2018-03-23
* Improve stability of `ftp` protocol.
* Fix document don't show automatically after select a file through `list` command.
* Fix [#113](https://github.com/liximomo/vscode-sftp/issues/113).

## 1.1.4 - 2018-03-21
* `connectTimeout` [config](https://github.com/liximomo/vscode-sftp#full-config).
* `downloadOnOpen` [config](https://github.com/liximomo/vscode-sftp#full-config).
* Fix ftp unexpectedly traverse up director [#80](https://github.com/liximomo/vscode-sftp/issues/80). Thanks for [Andrey Orst](https://github.com/andreyorst)'s help.

## 1.1.3 - 2018-03-18
* Remove default ignore configuration. No files will be ignored if you don't explicitly configuration `ignore` option. Related isuse [#138](https://github.com/liximomo/vscode-sftp/issues/138).
* Fix [#133](https://github.com/liximomo/vscode-sftp/issues/133).
* Fix [#136](https://github.com/liximomo/vscode-sftp/issues/136).


## 1.1.0 - 2018-03-13
* `diff` command.
* Fix [#113](https://github.com/liximomo/vscode-sftp/issues/113).
* Fix [#124](https://github.com/liximomo/vscode-sftp/issues/124).

## 1.0.5 - 2018-02-24
* Support [multi select in the Explorer](https://code.visualstudio.com/updates/v1_20#_multi-select-in-the-explorer).
* Fix some bugs.

## 1.0.4 - 2018-02-08
* New configuration option `concurrency`.
* New configuration option `algorithms`.
* Fix [#103](https://github.com/liximomo/vscode-sftp/issues/103).

## 1.0.3 - 2018-02-05
* Simplify default configuration file's content when exec `sftp: config`.
* Configuration autocomplete.
* Fix watcher stop work after 'download' or 'sync to local'.

## 1.0.2 - 2018-01-30
* Add FTPS support.
* Add passphrase/password dialog support.
* Fix configuration not found error after configuration file changed.
* Fix `sftp config` failed to show created configuration file in vscode.

## 1.0.0 - 2018-01-26
🎉🎉🎉This release include some new features, bugfixs and improvements. It may be bring some new bugs, welcome to feedback.

### New Features
* `list` and `list all` command.
  * `list` will list all remote files except those match your ignore rules.
  * `list all` will list all remote files.

  The target will be dowmload after you select. And it will be open in vscode if the target is a file.
* When you download a folder through a command, the vscode explorer will be refreshed when the command finish.

### Breaking Changes
* Change to git ignore [spec](https://git-scm.com/docs/gitignore). It's more powerful and concise. You may need to change your ignore configuration.


## 0.9.4 - 2017-12-18
* `Context` now receives a relative path.
* Fix [#69](https://github.com/liximomo/vscode-sftp/issues/69), [#70](https://github.com/liximomo/vscode-sftp/issues/70).

## 0.9.0 - 2017-12-16
* Add a option to configuration a local path that correspond to a remote path.
* Support multiple configurations in one configuration file.
* Remove `.sftpConfig.json` configuration file support.
* Remove none-worksapce-root configuration files support.

## 0.8.11 - 2017-11-30
* Fix ftp can't preserve file permissions.

## 0.8.10 - 2017-11-20
* Disable create configuration at none-workspace-root-folder.

## 0.8.9 - 2017-11-17
* Preserve file permissions.
* Better README thanks [kataklys](https://github.com/kataklys).
* Fix Empty (0kb) files when download and uplaod. Thanks for [kataklys](https://github.com/kataklys)'s help ([#33](https://github.com/liximomo/vscode-sftp/issues/33))
* Show a waring for existing none-worksapce-root configuration files. Previously you can create multiple configuration files anywhere under workspace. So you won't need to open multiple vscode instances to make `sftp` working in different folders. Sincle vscode support [Multi-root Workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces). There is no necessary to support multiple configuration now. This will make `sftp` both simple and a bettern starup performace.

## 0.8.8 - 2017-11-11
### Bugfix
* Files is not correctly filtered at configuration setup.

## 0.8.7 - 2017-11-07
### Bugfix
* Configuration setup not work for directories whose name does end with `.vscode`.

## 0.8.6 - 2017-11-06
* Performance improvement.
* Show a waring to the old `.sftpConfig.json` file.

### Behaviour Change
Now `uploadOnSave` only happens on a vscode save opetarion. It used to happen on a disk save opetarion caused by anything.

## 0.8.5 - 2017-10-18
### Improvement
* support more cipher algorithms.

## 0.8.4 - 2017-10-10
### Improvement
* log more infos to output pannel.

## 0.8.3 - 2017-09-26
### Bugfix
* fix couldn't create configuration through file picker when no sub files in the directory.

## 0.8.2 - 2017-09-24
### Enhance
* Don't need to reload vscode after execute `SFTP: config` command.
* `SFTP: config` creates `sftp.json` now.

## 0.8.1 - 2017-09-22
### Bugfix
* WIN could not find configuration(path is not normalized).

## 0.8.0 - 2017-09-22
### Feature
* support multi-root workspace.

### Change
* Configuration file name is changing to `sftp.json` from `.sftpConfig.json` for concision.

### Bugfix
* fix a bug that always return the same ssh session when have multiple configurations in workspace.

## 0.7.11 - 2017-09-13
### Bugfix
* fix tribe retrive.

## 0.7.10 - 2017-09-13
### Bugfix
* fix configuration not found when have multiple configuration files in workspace.

## 0.7.9 - 2017-09-01
### Bugfix
* change tip text from uploading to sync when download and upload.

## 0.7.8 - 2017-08-20
### Bugfix
* Fix `command not found error` when no folder opened.

## 0.7.7 - 2017-07-25
### Bugfix
* Fix folder match of ignore.

## 0.7.6 - 2017-07-24
### Bugfix
* Fix [files in "ignored" directories are still uploaded](https://github.com/liximomo/vscode-sftp/issues/15). Thanks for [Tom Spence](https://github.com/tomjaimz)'s help.

## 0.7.5 - 2017-07-18
### Feature
* A new editor configuration `sftp.printDebugLog`, dafault with false.

## 0.7.4 - 2017-07-14
### Enhance
* Configuration validation failing at startup does not require a reload to make extension work.

## 0.7.3 - 2017-07-13
### Feature
* Configuration validation.

### Misc
* More accurate watcher description.

## 0.7.2 - 2017-07-04
### Feature
* Add a way to execute commands on all detected configuration root folders.(run commands throw command palette)

## 0.7.1 - 2017-07-04
### Bugfix
* Fix miss files because of throttle.

## 0.7.0 - 2017-06-30
### Breaking Change
* Now configuration files are located in .vscode folder. Just move every .sftpConfig.json to the .vscode folder of same hierarchy.

## 0.6.14 - 2017-06-29
### Enhance
* show authentication input as asterisk.

## 0.6.13 - 2017-06-28
### Feature
* ssh agent authentication.

## 0.6.12 - 2017-06-26
### Feature
* Interactive authentication.

## 0.6.11 - 2017-06-22
### Feature
* Ignore works for download/sync remote file to local.

## 0.6.10 - 2017-06-13
### Enhance
* Better log.

## 0.6.9 - 2017-06-11
### Bugfix
* Remove unnecessary error message.
* Sync blocks on symlink.

## 0.6.8 - 2017-06-09
### Enhance
* Activate the extension only when it needs to. You must have the vscode greater than 1.13.0.

## 0.6.7 - 2017-06-07
### Enhance
* Keeping active so you don't have to reload vscode to active sftp when create configuration file at the first time.

## 0.6.6 - 2017-06-06
### Bugfix
* Window can't auto create dir non-existing.

## 0.6.2 - 2017-06-05
### Bugfix
* Incorrectly configuration not found error popup.

## 0.6.1 - 2017-06-03
### Bugfix
* Don't watch file when there is no .sftpConfig file.

## 0.6.0 - 2017-06-02
### Feature
* Support ftp.

### Feedback
* More debug info.

### Bugfix
* Fix `SFTPFileSystem.rmdir` doesn't resolve correctly.
* Disable watcher on pulling files.
* Make true re-connect when it need to.

## 0.5.4 - 2017-05-30
### Feedback
* Better error log.
* Output debug info in sftp output channel.

### Bugfix
* Fix some files missed uploading when they has updated because of throttle.

## 0.5.3 - 2017-05-26
### Feature
* AutoSave now works even in external file update!🎉🎉🎉
* A new configuration `watcher`. Now there is a way to perceive external file change(create, delete).

## 0.5.2 - 2017-05-22
### Bugfix
* Running a command through shortcut couldn't find active document correctly.

### Feedback
* Show path that is relative to the workspace root instead of full path on status bar.

## 0.5.1 - 2017-05-22
### Enhance
* Provide a way to run command at the workspace root.

## 0.5.0 - 2017-05-19
### Feature
* Keep ssh connect alive (re-connect only when needed).

## 0.4.12 - 2017-05-18
### Bugfix
* Fix binary file upload.

## 0.4.11 - 2017-05-18
### Feedback
* Better status indication.

## 0.4.10 - 2017-05-18
### Bugfix
* Configuration file not found in windows.
* Check existence of privateKeyPath.

## 0.4.0 - 2017-05-17
### Configuration
* Add option `syncModel`.

### Command
* New command Upload.
* New command Download.


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to vscode-sftp

After you've created a branch on your fork with your changes, [open a pull request][pr-link]. 

*Please follow the guidelines given below while making a Pull Request to the vscode-sftp*

## Pull Request Guidelines

* The Description should not exceed 100 characters.
* Make sure the PR title is in the format of `Add/Remove/Fix <feature>` *for e.g.*: `Add OpenSSH`
* Use a short descriptive commit message. *for e.g.*: ❌`Update Readme.md`  ✔ `Add OpenSSH connection Method`
* Search previous Pull Requests or Issues before making a new one, as yours may be a duplicate.
* Please make sure the feature has proper documentation.
* Please make sure you squash all commits together before opening a pull request. If your pull request requires changes upon review, please be sure to squash all additional commits as well. [This wiki page][squash-link] outlines the squash process.
* Target your Pull Request to the `master` branch of the `vscode-sftp`

Once you've submitted a pull request, the collaborators can review your proposed changes and decide whether or not to incorporate (pull in) your changes.

### Pull Request Pro Tips

* [Fork][fork-link] the repository and [clone][clone-link] it locally.
Connect your local repository to the original `upstream` repository by adding it as a [remote][remote-link].
Pull in changes from `upstream` often so that you stay up to date and so when you submit your pull request,
merge conflicts will be less likely. See more detailed instructions [here][syncing-link].
* Create a [branch][branch-link] for your edits.
* Contribute in the style of the project as outlined above. This makes it easier for the collaborators to merge
and for others to understand and maintain in the future.

### Open Pull Requests

Once you've opened a pull request, a discussion will start around your proposed changes.

Other contributors and users may chime in, but ultimately the decision is made by the collaborators.

During the discussion, you may be asked to make some changes to your pull request.

If so, add more commits to your branch and push them – they will automatically go into the existing pull request. But don't forget to squash them.

Opening a pull request will trigger a build to check the validity of all links in the project. After the build completes, **please ensure that the build has passed**. If the build did not pass, please view the build logs and correct any errors that were found in your contribution. 

*Thanks for being a part of this project, and we look forward to hearing from you soon!*

[branch-link]: <http://guides.github.com/introduction/flow/>
[clone-link]: <https://help.github.com/articles/cloning-a-repository/>
[fork-link]: <http://guides.github.com/activities/forking/>
[oauth-link]: <https://en.wikipedia.org/wiki/OAuth>
[pr-link]: <https://help.github.com/articles/creating-a-pull-request/>
[remote-link]: <https://help.github.com/articles/configuring-a-remote-for-a-fork/>
[syncing-link]: <https://help.github.com/articles/syncing-a-fork>
[squash-link]: <https://github.com/todotxt/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit>



================================================
FILE: FAQ.md
================================================
- [Error: Failure](#error-failure)
	- [Error: Failure - Solution One](#error-failure---solution-one)
	- [Error: Failure - Solution Two](#error-failure---solution-two)
- [Error: Connection closed](#error-connection-closed)
- [Error: Clicking Upload Changed Files does not work](#error-clicking-upload-changed-files-does-not-work)
- [ENFILE: file table overflow ...](#enfile-file-table-overflow-)
	- [ENFILE: file table overflow ... - Solution for MacOS harsh limit](#enfile-file-table-overflow----solution-for-macos-harsh-limit)
- [How do I upload content inside a folder, but not the folder itself?](#how-do-i-upload-content-inside-a-folder-but-not-the-folder-itself)
- [How can I upload files as root?](#how-can-i-upload-files-as-root)
- [Automatically sync both ways without user interaction](#automatically-sync-both-ways-without-user-interaction)
- [Show dotfiles/hidden files in remote explorer](#show-dotfileshidden-files-in-remote-explorer)

## Error: Failure

The failure error message comes from the remote side and is more or less the default/generic error 
message that sftp server sends when a syscall fails or something similar happens.
To know what exactly is going wrong you could try to enable debug output for the sftp server 
and then execute your transfers again and see what (if anything) shows up in the logs there.

### Error: Failure - Solution One

Change `remotePath` to the actual path if it's a symlink.

### Error: Failure - Solution Two

The problem could be that your server runs out of file descriptors.
You should try to increase the file descriptors limit.
If you don't have the permission to do this, set [limitOpenFilesOnRemote](https://github.com/Natizyskunk/vscode-sftp/wiki/Configuration#limitopenfilesonremote) option in your config.

## Error: Connection closed

The problem could be that the SFTP extension keeps closing the connection for those who use more legacy/old systems.
You'll have to Explicitly override the default transport layer algorithms used for the connection to remove the new `"diffie-hellman-group-exchange-sha256"` algorithm that cause the problem from the `kex` section. Just add this in your `sftp.json` configuration file, which should make it work.
```json
{
	"algorithms": {
		"kex": [
			"ecdh-sha2-nistp256", 
			"ecdh-sha2-nistp384", 
			"ecdh-sha2-nistp521"
		],
		"cipher": [
			"aes128-gcm",
			"aes128-gcm@openssh.com",
			"aes256-gcm",
			"aes256-gcm@openssh.com",
			"aes128-cbc",
			"aes192-cbc",
			"aes256-cbc",
			"aes128-ctr",
			"aes192-ctr",
			"aes256-ctr"
		],
		"serverHostKey": [
			"ssh-rsa", 
			"ssh-dss",
			"ssh-ed25519",
			"ecdsa-sha2-nistp256", 
			"ecdsa-sha2-nistp384", 
			"ecdsa-sha2-nistp521",
			"rsa-sha2-256",
			"rsa-sha2-512"
		],
		"hmac": [
			"hmac-sha2-256", 
			"hmac-sha2-512"
		]
	}
}
```

## Error: Clicking Upload Changed Files does not work

See [vscode-sftp issue #854](https://github.com/liximomo/vscode-sftp/issues/854).

**@PaPa31** added a fix to make the 'Upload Changed Files' command visible and added a default keyboard shortcut to call it.
<!-- **danieleiobbi** has a workaround to create a keyboard shortcut. -->

![upload changed files keyboard shortcut](assets/faq/upload_changed_files_shortcut.png)

## ENFILE: file table overflow ...

MacOS have a harsh limit on number of open files.

### ENFILE: file table overflow ... - Solution for MacOS harsh limit

Run those command:
```sh
echo kern.maxfiles=65536 | sudo tee -a /etc/sysctl.conf
echo kern.maxfilesperproc=65536 | sudo tee -a /etc/sysctl.conf
sudo sysctl -w kern.maxfiles=65536
sudo sysctl -w kern.maxfilesperproc=65536
ulimit -n 65536
```

## How do I upload content inside a folder, but not the folder itself?

See [vscode-sftp issue #852](https://github.com/liximomo/vscode-sftp/issues/852).

As quoted from **raoul2000**, "as long as you set the `context` property to `./[path]` (e.g., `./build`), it
will work."

Example configuration (where all JS and HTML files in `./build` will be copied to `/folder1/folder2/folder3`):
```json
{
  "name": "My Server",
  "host": "<host_ip_address>",
  "protocol": "sftp",
  "port": 22,
  "username": "user1",
  "remotePath": "/folder1/folder2/folder3",
  "context": "./build",
  "uploadOnSave": false,
  "watcher": {
    "files": "*.{js,html}",
    "autoUpload": true,
    "autoDelete": false
  }
}
```

## How can I upload files as root?

See [vscode-sftp issue #559](https://github.com/liximomo/vscode-sftp/issues/559).

**Yevhen-development** has a workaround, but it may not work for everyone.  In `sftp.json`, set the
following:
```json
"sshCustomParams": "sudo su -;"
```

## Automatically sync both ways without user interaction

See [vscode-sftp issue #136](https://github.com/Natizyskunk/vscode-sftp/issues/136).

> *This can also be used with **GIT** this way when you're checking out a branch or reverting changes/commits, your server will also be updated.*

```json
{
  "name": "My Server",
  "host": "<host_ip_address>",
  "protocol": "sftp",
  "port": 22,
  "username": "user1",
  "remotePath": "/folder1/folder2/folder3",
  "uploadOnSave": false, // Set to false if watcher `autoUpload` is set to true & `files` is set to "**/*".
  "watcher": {
    "files": "**/*",
    "autoUpload": true,
    "autoDelete": true
  }
  "syncOption": {
    "delete": true // Delete extraneous files from destination directories.
  },
}
```

## Show dotfiles/hidden files in remote explorer

### If using proftpd

Please edit the config file `proftpd.conf`. Depending on your installation, the default location for this file can be one of those :
- `/etc/proftpd.conf`
- `/etc/proftpd/proftpd.conf`
- `/usr/local/etc/proftpd.conf`
- `/usr/local/etc/proftpd/proftpd.conf`

Search for the `ListOptions` parameter and change it from `"-l"` to `"-la"`.

It should look like this : 
```conf
#Global settings
<Global>
[...]
ListOptions 		"-la"
[...]
</Global>
```


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018-present, Natizyskunk(Natan FOURIÉ)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

THE ONLY REQUIREMENT IS TO MENTION/CREDIT ALL ORIGINAL/PRECEDENT WORK.


================================================
FILE: README.md
================================================
# sftp sync extension for VS Code

New maintained and updated version by [@Natizyskunk](https://github.com/Natizyskunk/) 😀 <!-- and [@satiromarra](https://github.com/satiromarra) --> <br>
(Forked from the no longer maintained [liximomo's SFTP plugin](https://github.com/liximomo/vscode-sftp.git))

- VS Code marketplace : https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp <br>
- VSIX release : https://github.com/Natizyskunk/vscode-sftp/releases/

✳ I would be more than happy to have you participate in one way or another to this project. You can do so by simply following the [templates](https://github.com/Natizyskunk/vscode-sftp/issues/new/choose) when you open a new issue or a new pull request.

## ℹ INFOS - 2025/03/13
I've tried to keep this extension up-to-date as much as I can and added a lot of new relevant features. Saddly, for the last year and a half I wasn't really able to work on the project because of personal reasons and I'm really not sure if and when I'll be able to get more time to work on it again. So for now consider the [v1.16.3](https://github.com/Natizyskunk/vscode-sftp/releases/tag/v1.16.3) as the latest official stable release available.

## ℹ INFOS - 2023/06/23
This is the main repository for the SFTP extension since [@liximomo](https://github.com/liximomo) has set his own to deprecated in favor of this one in the VSCode marketplace.
There are also other forks that are available. Feel free to try them.

A lot of work as been brought to fix bugs, add new features and more than 50 updates have been released with a lot of improvements and stability fixes for almost two years now. 😎

I've been working hard to fix a lot of things and I've updated more than 50 new releases with a lot of improvements and stability fixes and I've brought new features for almost three years now. 

---

VSCode-SFTP enables you to add, edit or delete files within a local directory and have it sync to a remote server directory using different transfer protocols like FTP or SSH. The most basic setup requires only a few lines of configuration with a wide array of specific settings also available to meet the needs of any user. Both powerful and fast, it helps developers save time by allowing the use of a familiar editor and environment.

- Features
  - [Browser remote with Remote Explorer](#remote-explorer)
  - Diff local and remote
  - Sync directory
  - Upload/Download
  - Upload on save
  - File Watcher
  - Multiple configurations
  - Switchable profiles
  - Temp File support
- [Commands](https://github.com/Natizyskunk/vscode-sftp/wiki/Commands)
- [Debug](#debug)
- [FAQ](#FAQ)

## Installation

### Method 1 (Recommended : Auto update)
1. Select Extensions (Ctrl + Shift + X).
2. Uninstall current sftp extension from @liximomo.
3. Install new extension directly from VS Code Marketplace : https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp.
4. Voilà!

### Method 2 (Manual update)
To install just follow these steps from within VSCode:
1. Select Extensions (Ctrl + Shift + X).
2. Uninstall current sftp extension from @liximomo.
3. Open "More Action" menu(ellipsis on the top) and click "Install from VSIX…".
4. Locate VSIX file and select.
5. Reload VSCode.
6. Voilà!

## Documentation
- [Home](https://github.com/Natizyskunk/vscode-sftp/wiki)
- [Settings](https://github.com/Natizyskunk/vscode-sftp/wiki/Setting)
- [Common configuration](https://github.com/Natizyskunk/vscode-sftp/wiki/Common-Configuration)
- [SFTP configuration](https://github.com/Natizyskunk/vscode-sftp/wiki/SFTP-only-Configuration)
- [FTP confriguration](https://github.com/Natizyskunk/vscode-sftp/wiki/FTP(s)-only-Configuration)
- [Commands](https://github.com/Natizyskunk/vscode-sftp/wiki/Commands)

## Usage
If the latest files are already on a remote server, you can start with an empty local folder,
then download your project, and from that point sync.

1. In `VS Code`, open a local directory you wish to sync to the remote server (or create an empty directory
that you wish to first download the contents of a remote server folder in order to edit locally).
2. `Ctrl+Shift+P` on Windows/Linux or `Cmd+Shift+P` on Mac open command palette, run `SFTP: config` command.
3. A basic configuration file will appear named `sftp.json` under the `.vscode` directory, open and edit the configuration parameters with your remote server information.

For instance:
```json
{
    "name": "Profile Name",
    "host": "name_of_remote_host",
    "protocol": "ftp",
    "port": 21,
    "secure": true,
    "username": "username",
    "remotePath": "/public_html/project", // <--- This is the path which will be downloaded if you "Download Project"
    "password": "password",
    "uploadOnSave": false
}
```
The password parameter in `sftp.json` is optional, if left out you will be prompted for a password on sync.
_Note:_ backslashes and other special characters must be escaped with a backslash.

4. Save and close the `sftp.json` file.
5. `Ctrl+Shift+P` on Windows/Linux or `Cmd+Shift+P` on Mac open command palette.
6. Type `sftp` and you'll now see a number of other commands. You can also access many of the commands from the project's file explorer context menus.
7. A good one to start with if you want to sync with a remote folder is `SFTP: Download Project`.  This will download the directory shown in the `remotePath` setting in `sftp.json` to your local open directory.
8. Done - you can now edit locally and after each save it will upload to sync your remote file with the local copy.
9. Enjoy!

For detailed explanations please go to [wiki](https://github.com/Natizyskunk/vscode-sftp/wiki).

## Example configurations
You can see the full list of configuration options [here](https://github.com/Natizyskunk/vscode-sftp/wiki/configuration).

- [sftp sync extension for VS Code](#sftp-sync-extension-for-vs-code)
  - [Installation](#installation)
    - [Method 1 (Recommended : Auto update)](#method-1-recommended--auto-update)
    - [Method 2 (Manual update)](#method-2-manual-update)
  - [Documentation](#documentation)
  - [Usage](#usage)
  - [Example configurations](#example-configurations)
    - [Simple](#simple)
    - [Profiles](#profiles)
    - [Multiple Context](#multiple-context)
    - [Connection Hopping](#connection-hopping)
      - [Single Hop](#single-hop)
      - [Multiple Hop](#multiple-hop)
    - [Configuration in User Setting](#configuration-in-user-setting)
  - [Remote Explorer](#remote-explorer)
    - [Multiple Select](#multiple-select)
    - [Order](#order)
  - [Debug](#debug)
  - [FAQ](#faq)
  - [Donation](#donation)
    - [Buy Me a Coffee](#buy-me-a-coffee)
    - [PayPal](#paypal)

### Simple
```json
{
  "host": "host",
  "username": "username",
  "remotePath": "/remote/workspace"
}
```

### Profiles
```json
{
  "username": "username",
  "password": "password",
  "remotePath": "/remote/workspace/a",
  "watcher": {
    "files": "dist/*.{js,css}",
    "autoUpload": false,
    "autoDelete": false
  },
  "profiles": {
    "dev": {
      "host": "dev-host",
      "remotePath": "/dev",
      "uploadOnSave": true
    },
    "prod": {
      "host": "prod-host",
      "remotePath": "/prod"
    }
  },
  "defaultProfile": "dev"
}
```

_Note:_ `context` and `watcher` are only available at root level.

Use `SFTP: Set Profile` to switch profile.

### Multiple Context
The context must **not be same**.
```json
[
  {
    "name": "server1",
    "context": "project/build",
    "host": "host",
    "username": "username",
    "password": "password",
    "remotePath": "/remote/project/build"
  },
  {
    "name": "server2",
    "context": "project/src",
    "host": "host",
    "username": "username",
    "password": "password",
    "remotePath": "/remote/project/src"
  }
]
```

_Note:_ `name` is required in this mode.

### Connection Hopping
You can connect to a target server through a proxy with ssh protocol.

_Note:_ Variable substitution is not working in a hop configuration.

#### Single Hop
local -> hop -> target
```json
{
  "name": "target",
  "remotePath": "/path/in/target",

  // hop
  "host": "hopHost",
  "username": "hopUsername",
  "privateKeyPath": "/Users/localUser/.ssh/id_rsa", // <-- The key file is assumed on the local.

  "hop": {
    // target
    "host": "targetHost",
    "username": "targetUsername",
    "privateKeyPath": "/Users/hopUser/.ssh/id_rsa", // <-- The key file is assumed on the hop.
  }
}
```

#### Multiple Hop
local -> hopa -> hopb -> target
```json
{
  "name": "target",
  "remotePath": "/path/in/target",

  // hopa
  "host": "hopAHost",
  "username": "hopAUsername",
  "privateKeyPath": "/Users/hopAUsername/.ssh/id_rsa" // <-- The key file is assumed on the local.

  "hop": [
    // hopb
    {
      "host": "hopBHost",
      "username": "hopBUsername",
      "privateKeyPath": "/Users/hopaUser/.ssh/id_rsa" // <-- The key file is assumed on the hopa.
    },

    // target
    {
      "host": "targetHost",
      "username": "targetUsername",
      "privateKeyPath": "/Users/hopbUser/.ssh/id_rsa", // <-- The key file is assumed on the hopb.
    }
  ]
}
```

### Configuration in User Setting
You can use `remote` to tell sftp to get the configuration from [remote-fs](https://github.com/liximomo/vscode-remote-fs).

In User Setting:
```json
"remotefs.remote": {
  "dev": {
    "scheme": "sftp",
    "host": "host",
    "username": "username",
    "rootPath": "/path/to/somewhere"
  },
  "projectX": {
    "scheme": "sftp",
    "host": "host",
    "username": "username",
    "privateKeyPath": "/Users/xx/.ssh/id_rsa",
    "rootPath": "/home/foo/some/projectx"
  }
}
```

In sftp.json:
```json
{
  "remote": "dev",
  "remotePath": "/home/xx/",
  "uploadOnSave": false,
  "ignore": [".vscode", ".git", ".DS_Store"]
}
```

## Remote Explorer
![remote-explorer-preview](https://raw.githubusercontent.com/Natizyskunk/vscode-sftp/master/assets/showcase/remote-explorer.png)

Remote Explorer lets you explore files in remote. You can open Remote Explorer by:

1. Run Command `View: Show SFTP`.
2. Click SFTP view in Activity Bar.

You can only view a files content with Remote Explorer. Run command `SFTP: Edit in Local` to edit it in local.

### Multiple Select
You are able to select multiple files/folders at once on the remote server to download and upload. You can do it simply by holding down Ctrl or Shift while selecting all desired files, just like on the regular explorer view.

_Note:_ You need to manually refresh the parent folder after you **delete** a file if the explorer isn't correctly updated.

### Order
You can order the remote Explorer by adding the `remoteExplorer.order` parameter inside your `sftp.json` config file.

In sftp.json:
```json
{
  "remoteExplorer": {
    "order": 1 // <-- Default value is 0.
  }
}
```

## Debug
1. Open User Settings.
  - On Windows/Linux - `File > Preferences > Settings`
  - On macOS - `Code > Preferences > Settings`
2. Set `sftp.debug` to `true` and reload vscode.
3. View the logs in `View > Output > sftp`.

## FAQ
You can see all the Frequently Asked Questions [here](./FAQ.md).

## Donation
If this project helped you reduce development time and you wish to contribute financially

### Buy Me a Coffee
[![Buy Me A Coffee](https://bmc-cdn.nyc3.digitaloceanspaces.com/BMC-button-images/custom_images/orange_img.png)](https://www.buymeacoffee.com/Natizyskunk)

### PayPal
<!-- [![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BY89QD47D7MPS&source=url) -->
[![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/donate?business=DELD7APHHM3BC&no_recurring=0&currency_code=EUR)
[![PayPal Me](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/natanfourie)


================================================
FILE: __mocks__/fs.js
================================================
const { fs } = require('memfs');

fs.__mock__ = true;
module.exports = fs;


================================================
FILE: __mocks__/vscode.js
================================================

const Nothing = (() => {
	const fn = () => Nothing
	fn.toString = fn.toLocaleString = fn[Symbol.toPrimitive] = () => ''
	fn.valueOf = () => false

	return new Proxy(fn, {
		get: (o, key) => o.hasOwnProperty(key) ? o[key] : Nothing
	})
})()

module.exports = Nothing;


================================================
FILE: docs/commands.md
================================================
## Common commands

### SFTP: Config
Create a new configuration file for a project.

### SFTP: Set Profile
Set the current profile.
           
#### KeyBindings Args
func(profileName: string)

### SFTP: Upload Active File
Upload the current file.

### SFTP: Upload Changed Files
Upload all files changed or created since the last commit to your Git.
Can be called by default keyboard shortcut `Ctrl+Alt+U`.

### SFTP: Upload Active Folder
Upload the entire folder the current file is located in.

### SFTP: Download Active File
Download the remote version of the current file and overwrite the local copy.

### SFTP: Download Active Folder
Download the entire folder the current file is located in.

### SFTP: Sync Local -> Remote
1. Any files that exist on both local and remote that have a different timestamp between local and remote are copied over.
2. Any files that only exist on the local are copied over.

You can change the default behavior by [syncOption](https://github.com/Natizyskunk/vscode-sftp/wiki/Configuration#syncoption).

### SFTP: Sync Remote -> Local
Same as `Sync Local -> Remote`, but in the opposite direction.

### SFTP: Sync Both Directions
Compare file modification times, and will always perform the action that causes the newest file to be present in both locations.

*Only [skipCreate](https://github.com/Natizyskunk/vscode-sftp/wiki/Configuration#syncoptionskipcreate) and [ignoreExisting](https://github.com/Natizyskunk/vscode-sftp/wiki/Configuration#syncoptionignoreexisting) are valid for this command.*

### SFTP: List Active Folder
List the folder the current file is located in.

### sftp.upload
Upload file or folders.

#### KeyBindings Args
func(fspaths: string[])

### sftp.download
Download file or folders.

#### KeyBindings Args
func(fspaths: string[])

### SFTP: Cancel All Transfers
Stop the current transfers (upload and download).

### SFTP: Open SSH in Terminal
Open a terminal in VSCode and auto login to a specific server.


## Alt commands
An alternative command can be found when pressing `Alt` while opening a menu.

### Force Download
Download file but disregard ignore rules.

### Force Upload
Upload file but disregard ignore rules.


================================================
FILE: docs/common_configuration.md
================================================
## Common configuration

### name
A string to identify your configuration.

| Key | Value |
| --- | --- |
| *name* | *string* |

```json
{
  "name": "My Server"
}
```

### context
A path relative to the workspace root folder. <br>
Use this when you want to map a subfolder to the `remotePath`.

| Key | Value | Default |
| --- | --- | --- |
| *context* | *string* | *The workspace root.* |

```json
{
  "context": "/_subfolder_"
}
```

### protocol
Protocol to be used.

| Key | Value | Default |
| --- | --- | --- |
| *protocol* | `sftp` *or* `ftp` | `sftp` |

```json
{
  "protocol": "sftp"
}
```

### host
Hostname or IP address of the server.

| Key | Value |
| --- | --- |
| *host* | *string* |

```json
{
  "host": "server.example.com"
}
```

### port
Port number of the server.

| Key | Value |
| --- | --- |
| *port* | *integer* |

```json
{
  "port": 22
}
```

### username
Username for authentication.

| Key | Value |
| --- | --- |
| *username* | *string* |

```json
{
  "username": "user1"
}
```

### password
[!WARNING]
**Passwords are stored as plain-text!**

The password for password-based user authentication.

| Key | Value |
| --- | --- |
| *password* | *string* |

```json
{
  "password": "Password123"
}
```

### remotePath
The absolute path on the remote host.

| Key | Value | Default |
| --- | --- | --- |
| *remotePath* | *string* | `/` |

```json
{
  "remotePath": "/_subfolder_"
}
```

### filePerm
Set octal file permissions for new files.

| Key | Value | Default |
| --- | --- | --- |
| *filePerm* | *number* | `false` |

```json
{
  "filePerm": 644
}
```
 
### dirPerm
Set octal directory permissions for new directories.

| Key | Value | Default |
| --- | --- | --- |
| *dirPerm* | *number* | `false` |

```json
{
  "dirPerm": 750
}
```

### uploadOnSave
Upload on every save operation of VSCode.

| Key | Value | Default |
| --- | --- | --- |
| *uploadOnSave* | *boolean* | `false` |

```json
{
  "uploadOnSave": true
}
```

### useTempFile
Upload temp file on every save operation of VSCode to avoid breaking a webpage when a user accesses it while the file is still being uploaded (is incomplete).

| Key | Value | Default |
| --- | --- | --- |
| *useTempFile* | *boolean* | `false` |

```json
{
  "useTempFile": true
}
```

### openSsh
Enable atomic file uploads (*only supported by openSSH servers*).

| 💡 Important |
| :--- |
| *If set to* `true`*, the* `useTempFile` *option must also be set to* `true`.|

| Key | Value | Default |
| --- | --- | --- |
| *openSsh* | *boolean* | `false` |

```json
{
  "openSsh": true,
  "useTempFile": true
}
```

### downloadOnOpen
Download the file from the remote server whenever it is opened.

| Key | Value | Default |
| --- | --- | --- |
| *downloadOnOpen* | *boolean* | `false` |

```json
{
  "downloadOnOpen": true
}
```

### syncOption
Configure the behavior of the `Sync` command.

| Key | Value | Default |
| --- | --- | --- |
| *syncOption* | *object* | `{}` |

#### syncOption.delete
Delete extraneous files from destination directories.

| Key | Value |
| --- | --- |
| *syncOption.delete* | *boolean* |

#### syncOption.skipCreate
Skip creating new files on the destination.

| Key | Value |
| --- | --- |
| *syncOption.skipCreate* | *boolean* |

#### syncOption.ignoreExisting
Skip updating files that exist on the destination.

| Key | Value |
| --- | --- |
| *syncOption.ignoreExisting* | *boolean* |

#### syncOption.update
Update the destination only if a newer version is on the source filesystem.

| Key | Value |
| --- | --- |
| *syncOption.update* | *boolean* |

```json
{
  "syncOption": {
    "delete": true,
    "skipCreate": false,
    "ignoreExisting": false,
    "update": true
  },
}
```

### ignore
Ignore can be used to ignore files and folders from sync, and even supports wildcards using `*`. <br>
This is the same behavior as gitignore, all paths relative to context of the current configuration.
 
| Key | Value | Default |
| --- | --- | --- |
| *ignore* | *string[]* | `[]` |
 
```json
{
  "ignore": [
    "/.vscode",
    "/.git",
    "/.cache",
    "/_subfolder_",
    ".DS_Store",
    "*.gz",
    "*.log"
  ],
}
```

### ignoreFile
Absolute path to the ignore file or Relative path relative to the workspace root folder.
 
| Key | Value |
| --- | --- |
| *ignoreFile* | *string* |
 
```json
{
  "ignoreFile": "/.vscode/sftp.json"
}
```

### watcher
Configure the behavior of the `watcher` command.

| Key | Value | Default |
| --- | --- | --- |
| *watcher* | *object* | `{}` |

#### watcher.files
Glob patterns that are watched and when edited outside of the VSCode editor are processed.

| 💡 Important |
| :--- |
| *Set* `uploadOnSave` *to* `false` *when you watch everything.*| 

| Key | Value |
| --- | --- |
| *watcher.files* | *string* |
 
#### watcher.autoUpload
Upload when the file changed.

| Key | Value |
| --- | --- |
| *watcher.autoUpload* | *boolean* |

#### watcher.autoDelete
Delete when the file is removed.

| Key | Value |
| --- | --- |
| *watcher.autoDelete* | *boolean* |
```json
{
  "watcher": {
    "files": "**/*",
    "autoUpload": true,
    "autoDelete": true
  },
}
```

### remoteTimeOffsetInHours
The number of hours difference between the local machine and the remote server (remote minus local).

| Key | Value | Default |
| --- | --- | --- |
| *remoteTimeOffsetInHours* | *number* | `0` |

```json
{
  "remoteTimeOffsetInHours": 3
}
```

### remoteExplorer
Configure the behavior of the `remoteExplorer` command.

| Key | Value | Default |
| --- | --- | --- | 
| *remoteExplorer* | *object* | `{}` |
 
#### remoteExplorer.filesExclude
Configure that patterns for excluding files and folders. <br>
The Remote Explorer decides which files and folders to show or hide based on this setting..

| Key | Value |
| --- | --- |
| *remoteExplorer.filesExclude* | *string[]* |

#### remoteExplorer.order

| Key | Value |
| --- | --- |
| *remoteExplorer.order* | *number* |
```json
{
  "remoteExplorer": {
    "filesExclude": [],
    "order": 0
  }
}
```

### concurrency
Lowering the concurrency could get more stability because some clients/servers have some sort of configured/hard coded limit.

| Key | Value | Default |
| --- | --- | --- |
| *concurrency* | *number* | `4` |

```json
{
  "concurrency": 3
}
```

### connectTimeout
The maximum connection time.

| Key | Value | Default |
| --- | --- | --- |
| *connectTimeout* | *number* | `10000` |

```json
{
  "connectTimeout": 15000
}
```

### limitOpenFilesOnRemote
Limit open file descriptors to the specific number in a remote server. <br>
Set to true for using default `limit(222)`.

| 💡 Important |
| :--- |
| *Do not set this unless you have to!* | 

| Key | Value | Default |
| --- | --- | --- |
| *limitOpenFilesOnRemote* | *mixed* | `false` |

```json
{
  "limitOpenFilesOnRemote": 15000
}
```


================================================
FILE: docs/configuration.md
================================================
# VSCode-SFTP

Configurations are stored in your project working directory under `../.vscode/sftp.json`. <br>
The configuration file can always be accessed with `CTRL` + `Shift` + `P`, and searching for `SFTP: Config`.

![image](https://github.com/user-attachments/assets/5ceff350-7678-4264-98d4-2741a98a9dbe)

## Table of Contents

### Configuration
- [name](#name)
- [context](#context)
- [protocol](#protocol)
- [host](#host)
- [port](#port)
- [username](#username)
- [password](#password)
- [remotePath](#remotepath)
- [filePerm](#fileperm)
- [dirPerm](#dirperm)
- [uploadOnSave](#uploadonsave)
- [useTempFile](#usetempfile)
- [openSsh](#openssh)
- [downloadOnOpen](#downloadonopen)
- [syncOption](#syncoption)
- [ignore](#ignore)
- [ignoreFile](#ignorefile)
- [watcher](#watcher)
- [remoteTimeOffsetInHours](#remotetimeoffsetinhours)
- [remoteExplorer](#remoteexplorer)
- [concurrency](#concurrency)
- [connectTimeout](#connecttimeout)
- [limitOpenFilesOnRemote](#limitopenfilesonremote)

### SFTP only configuration
- [agent](#agent)
- [privateKeyPath](#privatekeypath)
- [passphrase](#passphrase)
- [interactiveAuth](#interactiveauth)
- [algorithms](#algorithms)
- [sshConfigPath](#sshconfigpath)
- [sshCustomParams](#sshcustomparams)

### FTP(s) only configuration
- [secure](#secure)
- [secureOptions](#secureoptions)



## Configuration

### name
A string to identify your configuration.

| Key | Value |
| --- | --- |
| *name* | *string* |

```json
{
  "name": "My Server"
}
```

### context
A path relative to the workspace root folder. <br>
Use this when you want to map a subfolder to the `remotePath`.

| Key | Value | Default |
| --- | --- | --- |
| *context* | *string* | *The workspace root.* |

```json
{
  "context": "/_subfolder_"
}
```

### protocol
Protocol to be used.

| Key | Value | Default |
| --- | --- | --- |
| *protocol* | `sftp` *or* `ftp` | `sftp` |

```json
{
  "protocol": "sftp"
}
```

### host
Hostname or IP address of the server.

| Key | Value |
| --- | --- |
| *host* | *string* |

```json
{
  "host": "server.example.com"
}
```

### port
Port number of the server.

| Key | Value |
| --- | --- |
| *port* | *integer* |

```json
{
  "port": 22
}
```

### username
Username for authentication.

| Key | Value |
| --- | --- |
| *username* | *string* |

```json
{
  "username": "user1"
}
```

### password
[!WARNING]
**Passwords are stored as plain-text!**

The password for password-based user authentication.

| Key | Value |
| --- | --- |
| *password* | *string* |

```json
{
  "password": "Password123"
}
```

### remotePath
The absolute path on the remote host.

| Key | Value | Default |
| --- | --- | --- |
| *remotePath* | *string* | `/` |

```json
{
  "remotePath": "/_subfolder_"
}
```

### filePerm
Set octal file permissions for new files.

| Key | Value | Default |
| --- | --- | --- |
| *filePerm* | *number* | `false` |

```json
{
  "filePerm": 644
}
```
 
### dirPerm
Set octal directory permissions for new directories.

| Key | Value | Default |
| --- | --- | --- |
| *dirPerm* | *number* | `false` |

```json
{
  "dirPerm": 750
}
```

### uploadOnSave
Upload on every save operation of VSCode.

| Key | Value | Default |
| --- | --- | --- |
| *uploadOnSave* | *boolean* | `false` |

```json
{
  "uploadOnSave": true
}
```

### useTempFile
Upload temp file on every save operation of VSCode to avoid breaking a webpage when a user accesses it while the file is still being uploaded (is incomplete).

| Key | Value | Default |
| --- | --- | --- |
| *useTempFile* | *boolean* | `false` |

```json
{
  "useTempFile": true
}
```

### openSsh
Enable atomic file uploads (*only supported by openSSH servers*).

| 💡 Important |
| :--- |
| *If set to* `true`*, the* `useTempFile` *option must also be set to* `true`.|

| Key | Value | Default |
| --- | --- | --- |
| *openSsh* | *boolean* | `false` |

```json
{
  "openSsh": true,
  "useTempFile": true
}
```

### downloadOnOpen
Download the file from the remote server whenever it is opened.

| Key | Value | Default |
| --- | --- | --- |
| *downloadOnOpen* | *boolean* | `false` |

```json
{
  "downloadOnOpen": true
}
```

### syncOption
Configure the behavior of the `Sync` command.

| Key | Value | Default |
| --- | --- | --- |
| *syncOption* | *object* | `{}` |

#### syncOption.delete
Delete extraneous files from destination directories.

| Key | Value |
| --- | --- |
| *syncOption.delete* | *boolean* |

#### syncOption.skipCreate
Skip creating new files on the destination.

| Key | Value |
| --- | --- |
| *syncOption.skipCreate* | *boolean* |

#### syncOption.ignoreExisting
Skip updating files that exist on the destination.

| Key | Value |
| --- | --- |
| *syncOption.ignoreExisting* | *boolean* |

#### syncOption.update
Update the destination only if a newer version is on the source filesystem.

| Key | Value |
| --- | --- |
| *syncOption.update* | *boolean* |

```json
{
  "syncOption": {
    "delete": true,
    "skipCreate": false,
    "ignoreExisting": false,
    "update": true
  },
}
```

### useTempFile
Upload temp file on every save operation of VSCode to avoid breaking a webpage when a user accesses it while the file is still being uploaded (is incomplete).

| Key | Value | Default |
| --- | --- | --- |
| *useTempFile* | *boolean* | `false` |

```json
{
  "useTempFile": true
}
```

### ignore
Ignore can be used to ignore files and folders from sync, and even supports wildcards using `*`. <br>
This is the same behavior as gitignore, all paths relative to context of the current configuration.
 
| Key | Value | Default |
| --- | --- | --- |
| *ignore* | *string[]* | `[]` |
 
```json
{
  "ignore": [
    "/.vscode",
    "/.git",
    "/.cache",
    "/_subfolder_",
    ".DS_Store",
    "*.gz",
    "*.log"
  ],
}
```

### ignoreFile
Absolute path to the ignore file or Relative path relative to the workspace root folder.
 
| Key | Value |
| --- | --- |
| *ignoreFile* | *string* |
 
```json
{
  "ignoreFile": "/.vscode/sftp.json"
}
```

### watcher
Configure the behavior of the `watcher` command.

| Key | Value | Default |
| --- | --- | --- |
| *watcher* | *object* | `{}` |

#### watcher.files
Glob patterns that are watched and when edited outside of the VSCode editor are processed.

| 💡 Important |
| :--- |
| *Set* `uploadOnSave` *to* `false` *when you watch everything.*| 

| Key | Value |
| --- | --- |
| *watcher.files* | *string* |
 
#### watcher.autoUpload
Upload when the file changed.

| Key | Value |
| --- | --- |
| *watcher.autoUpload* | *boolean* |

#### watcher.autoDelete
Delete when the file is removed.

| Key | Value |
| --- | --- |
| *watcher.autoDelete* | *boolean* |
```json
{
  "watcher": {
    "files": "**/*",
    "autoUpload": true,
    "autoDelete": true
  },
}
```

### remoteTimeOffsetInHours
The number of hours difference between the local machine and the remote server (remote minus local).

| Key | Value | Default |
| --- | --- | --- |
| *remoteTimeOffsetInHours* | *number* | `0` |

```json
{
  "remoteTimeOffsetInHours": 3
}
```

### remoteExplorer
Configure the behavior of the `remoteExplorer` command.

| Key | Value | Default |
| --- | --- | --- | 
| *remoteExplorer* | *object* | `{}` |
 
#### remoteExplorer.filesExclude
Configure that patterns for excluding files and folders. <br>
The Remote Explorer decides which files and folders to show or hide based on this setting..

| Key | Value |
| --- | --- |
| *remoteExplorer.filesExclude* | *string[]* |

#### remoteExplorer.order

| Key | Value |
| --- | --- |
| *remoteExplorer.order* | *number* |
```json
{
  "remoteExplorer": {
    "filesExclude": [],
    "order": 0
  }
}
```

### concurrency
Lowering the concurrency could get more stability because some clients/servers have some sort of configured/hard coded limit.

| Key | Value | Default |
| --- | --- | --- |
| *concurrency* | *number* | `4` |

```json
{
  "concurrency": 3
}
```

### connectTimeout
The maximum connection time.

| Key | Value | Default |
| --- | --- | --- |
| *connectTimeout* | *number* | `10000` |

```json
{
  "connectTimeout": 15000
}
```

### limitOpenFilesOnRemote
Limit open file descriptors to the specific number in a remote server. <br>
Set to true for using default `limit(222)`.

| 💡 Important |
| :--- |
| *Do not set this unless you have to!* | 

| Key | Value | Default |
| --- | --- | --- |
| *limitOpenFilesOnRemote* | *mixed* | `false` |

```json
{
  "limitOpenFilesOnRemote": 15000
}
```


## SFTP only configuration

### agent
Path to ssh-agent's UNIX socket for ssh-agent-based user authentication. <br>
Windows users must set to 'pageant' for authenticating with Pagenat or (actual) path to a Cygwin "UNIX socket". <br>
It'd get more stability because some client/server have some sort of configured/hard coded limit.

| Key | Value |
| --- | --- |
| *agent* | *string* |

```json
{
  "agent": "/_subfolder_/agent"
}
```

### privateKeyPath
Absolute path to user private key.

| Key | Value |
| --- | --- |
| *privateKeyPath* | *string* |

```json
{
  "privateKeyPath": "/.ssh/key.pem"
}
```

### passphrase
For an encrypted private key, this is the passphrase string used to decrypt it. <br>
Set to 'true' for enable passphrase dialog. This will prevent from using cleartext passphrase in this config.

| Key | Value |
| --- | --- |
| *passphrase* | *mixed* |

```json
{
  "passphrase": true
}
```

### interactiveAuth
Enable keyboard interaction authentication mechanism. Set to 'true' to enable `verifyCode` dialog. <br>
For example using Google Authentication (multi-factor). Or pass array of predefined phrases to automatically enter them without user prompting.

| 💡 Note |
| :--- |
| *Requires the server to have keyboard-interactive authentication enabled.* | 

| Key | Value | Default |
| --- | --- | --- |
| *interactiveAuth* | *boolean*\|*string[]* | 'false' |

```json
{
  "interactiveAuth": true
}
```

### algorithms
Explicit overrides for the default transport layer algorithms used for the connection.

**Default**:
```json
{
  "algorithms": {
    "kex": [
      "ecdh-sha2-nistp256",
      "ecdh-sha2-nistp384",
      "ecdh-sha2-nistp521",
      "diffie-hellman-group-exchange-sha256"
    ],
    "cipher": [
      "aes128-gcm",
		"aes128-gcm@openssh.com",
		"aes256-gcm",
		"aes256-gcm@openssh.com",
		"aes128-cbc",
		"aes192-cbc",
		"aes256-cbc",
		"aes128-ctr",
		"aes192-ctr",
		"aes256-ctr"
    ],
    "serverHostKey": [
      "ssh-rsa",
      "ssh-dss",
      "ssh-ed25519",
      "ecdsa-sha2-nistp256",
      "ecdsa-sha2-nistp384",
      "ecdsa-sha2-nistp521",
      "rsa-sha2-512",
      "rsa-sha2-256"
    ],
    "hmac": [
      "hmac-sha2-256",
      "hmac-sha2-512"
    ]
  },
}
```

### sshConfigPath
Absolute path to your SSH configuration file.

| Key | Value | Default |
| --- | --- | --- |
| *sshConfigPath* | *string* | `~/.ssh/config` |

```json
{
  "sshConfigPath": "~/.ssh/config"
}
```

### sshCustomParams
Extra parameters appended to the SSH command used by "Open SSH in Terminal".

| Key | Value |
| --- | --- |
| *sshCustomParams* | *string* |

```json
{
  "sshCustomParams": "-g"
}
```


## FTP(s) only configuration

### secure
Set to true for both control and data connection encryption. <br>
Set to `control` for control encryption only, or `implicit` for implicitly encrypted control connection (this mode is deprecated in modern times, but usually uses port 990).

| Key | Value | Default |
| --- | --- | --- |
| *secure* | *mixed* | `false` |

```json
{
  "secure": control
}
```

### secureOptions
Additional options to be passed to `tls.connect()`.

| 💡 Note |
| :--- |
| *See [TLS connect options callback](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).* | 

| Key | Value |
| --- | --- |
| *secureOptions* | *object* |

```json
{
  "secureOptions": {
    "enableTrace": true
  }
}
```


================================================
FILE: docs/ftp_configuration.md
================================================
## FTP(s) configuration

### secure
Set to true for both control and data connection encryption. <br>
Set to `control` for control encryption only, or `implicit` for implicitly encrypted control connection (this mode is deprecated in modern times, but usually uses port 990).

| Key | Value | Default |
| --- | --- | --- |
| *secure* | *mixed* | `false` |

```json
{
  "secure": control
}
```

### secureOptions
Additional options to be passed to `tls.connect()`.

| 💡 Note |
| :--- |
| *See [TLS connect options callback](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).* | 

| Key | Value |
| --- | --- |
| *secureOptions* | *object* |

```json
{
  "secureOptions": {
    "enableTrace": true
  }
}
```


================================================
FILE: docs/home.md
================================================
# Home

1. [Setting](./setting.md)
2. [Config](./configuration.md)
    - [Common](./common_configuration.md)
    - [SFTP](./sftp_configuration.md)
    - [FTP(s)](./ftp_configuration.md)
3. [Commands](./commands.md)
4. [FAQ](./../FAQ.md)


================================================
FILE: docs/setting.md
================================================
## Setting

There are a handful of settings available for SFTP, and they can be changed:

- On Windows/Linux: File --> Preferences --> Settings
- On macOS: Code --> Preferences --> Settings

### debug
Adds debugging output to the SFTP output panel. <br>
You can view the login in `View --> Output --> SFTP`.  Changing this requires VSCode to be reloaded.

| Key | Value | Default |
| --- | --- | --- |
| *debug* | *boolean* | *false* |

```json
{
  "name": "My Server"
}
```

### downloadWhenOpenInRemoteExplorer
Change the default behavior from `View Content` to `Edit in Local` when opening files in the Remote Explorer.

| Key | Value | Default |
| --- | --- | --- |
| *debug* | *boolean* | *false* |

```json
{
  "name": "My Server"
}
```


================================================
FILE: docs/sftp_configuration.md
================================================
## SFTP configuration

### agent
Path to ssh-agent's UNIX socket for ssh-agent-based user authentication. <br>
Windows users must set to 'pageant' for authenticating with Pagenat or (actual) path to a Cygwin "UNIX socket". <br>
It'd get more stability because some client/server have some sort of configured/hard coded limit.

| Key | Value |
| --- | --- |
| *agent* | *string* |

```json
{
  "agent": "/_subfolder_/agent"
}
```

### privateKeyPath
Absolute path to user private key.

| Key | Value |
| --- | --- |
| *privateKeyPath* | *string* |

```json
{
  "privateKeyPath": "/.ssh/key.pem"
}
```

### passphrase
For an encrypted private key, this is the passphrase string used to decrypt it. <br>
Set to 'true' for enable passphrase dialog. This will prevent from using cleartext passphrase in this config.

| Key | Value |
| --- | --- |
| *passphrase* | *mixed* |

```json
{
  "passphrase": true
}
```

### interactiveAuth
Enable keyboard interaction authentication mechanism. Set to 'true' to enable `verifyCode` dialog. <br>
For example using Google Authentication (multi-factor). Or pass array of predefined phrases to automatically enter them without user prompting.

| 💡 Note |
| :--- |
| *Requires the server to have keyboard-interactive authentication enabled.* | 

| Key | Value | Default |
| --- | --- | --- |
| *interactiveAuth* | *boolean*\|*string[]* | 'false' |

```json
{
  "interactiveAuth": true
}
```

### algorithms
Explicit overrides for the default transport layer algorithms used for the connection.

**Default**:
```json
{
  "algorithms": {
    "kex": [
      "ecdh-sha2-nistp256",
      "ecdh-sha2-nistp384",
      "ecdh-sha2-nistp521",
      "diffie-hellman-group-exchange-sha256"
    ],
    "cipher": [
      "aes128-gcm",
		"aes128-gcm@openssh.com",
		"aes256-gcm",
		"aes256-gcm@openssh.com",
		"aes128-cbc",
		"aes192-cbc",
		"aes256-cbc",
		"aes128-ctr",
		"aes192-ctr",
		"aes256-ctr"
    ],
    "serverHostKey": [
      "ssh-rsa",
      "ssh-dss",
      "ssh-ed25519",
      "ecdsa-sha2-nistp256",
      "ecdsa-sha2-nistp384",
      "ecdsa-sha2-nistp521",
      "rsa-sha2-512",
      "rsa-sha2-256"
    ],
    "hmac": [
      "hmac-sha2-256",
      "hmac-sha2-512"
    ]
  },
}
```

### sshConfigPath
Absolute path to your SSH configuration file.

| Key | Value | Default |
| --- | --- | --- |
| *sshConfigPath* | *string* | `~/.ssh/config` |

```json
{
  "sshConfigPath": "~/.ssh/config"
}
```

### sshCustomParams
Extra parameters appended to the SSH command used by "Open SSH in Terminal".

| Key | Value |
| --- | --- |
| *sshCustomParams* | *string* |

```json
{
  "sshCustomParams": "-g"
}
```


================================================
FILE: package.json
================================================
{
  "name": "sftp",
  "displayName": "SFTP",
  "description": "SFTP/FTP sync",
  "version": "1.16.3",
  "publisher": "Natizyskunk",
  "author": "Natizyskunk <natan.fourie@hotmail.fr> (https://github.com/Natizyskunk)",
  "engines": {
    "vscode": "^1.64.2"
  },
  "bugs": {
    "url": "https://github.com/Natizyskunk/vscode-sftp/issues",
    "email": "natan.fourie@hotmail.fr"
  },
  "homepage": "https://github.com/Natizyskunk/vscode-sftp/blob/master/README.md",
  "repository": {
    "type": "git",
    "url": "https://github.com/Natizyskunk/vscode-sftp.git"
  },
  "license": "MIT",
  "categories": [
    "Other"
  ],
  "keywords": [
    "ftp",
    "sftp",
    "sync",
    "remote"
  ],
  "activationEvents": [
    "onCommand:sftp.config",
    "workspaceContains:.vscode/sftp.json"
  ],
  "icon": "resources/icon.png",
  "main": "./dist/extension",
  "contributes": {
    "viewsContainers": {
      "activitybar": [
        {
          "id": "sftp",
          "title": "SFTP",
          "icon": "resources/remote-explorer.svg"
        }
      ]
    },
    "views": {
      "sftp": [
        {
          "id": "remoteExplorer",
          "name": "Explorer",
          "when": "sftp.enabled"
        }
      ]
    },
    "configuration": {
      "type": "object",
      "title": "sftp configuration",
      "properties": {
        "sftp.printDebugLog": {
          "type": "boolean",
          "default": false,
          "description": "print debug log on sftp output channel.(reload vscode when change this)"
        },
        "sftp.debug": {
          "type": "boolean",
          "default": false,
          "description": "print debug log on sftp output channel.(reload vscode when change this)"
        },
        "sftp.downloadWhenOpenInRemoteExplorer": {
          "type": "boolean",
          "default": false,
          "description": "\"Download\" instead of \"View Content\" when open file in Remote Explorer"
        }
      }
    },
    "commands": [
      {
        "command": "sftp.config",
        "title": "Config",
        "category": "SFTP"
      },
      {
        "command": "sftp.setProfile",
        "title": "Set Profile",
        "category": "SFTP"
      },
      {
        "command": "sftp.openConnectInTerminal",
        "title": "Open SSH in Terminal",
        "category": "SFTP"
      },
      {
        "command": "sftp.cancelAllTransfer",
        "title": "Cancel All Transfers",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.file",
        "title": "Upload File",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.changedFiles",
        "title": "Upload Changed Files",
        "category": "SFTP",
        "icon": {
          "light": "resources/light/upload.svg",
          "dark": "resources/dark/upload.svg"
        }
      },
      {
        "command": "sftp.upload.activeFile",
        "title": "Upload Active File",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.folder",
        "title": "Upload Folder",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.activeFolder",
        "title": "Upload Active Folder",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.project",
        "title": "Upload Project",
        "category": "SFTP"
      },
      {
        "command": "sftp.forceUpload",
        "title": "Force Upload",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.file.to.allProfiles",
        "title": "Upload File To All Profiles",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.activeFile.to.allProfiles",
        "title": "Upload Active File To All Profiles",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.folder.to.allProfiles",
        "title": "Upload Folder To All Profiles",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.activeFolder.to.allProfiles",
        "title": "Upload Active Folder To All Profiles",
        "category": "SFTP"
      },
      {
        "command": "sftp.upload.project.to.allProfiles",
        "title": "Upload Project To All Profiles",
        "category": "SFTP"
      },
      {
        "command": "sftp.forceUpload.to.allProfiles",
        "title": "Force Upload To All Profiles",
        "category": "SFTP"
      },
      {
        "command": "sftp.download.file",
        "title": "Download File",
        "category": "SFTP",
        "icon": {
          "dark": "resources/dark/download-file.svg",
          "light": "resources/light/download-file.svg"
        }
      },
      {
        "command": "sftp.download.activeFile",
        "title": "Download Active File",
        "category": "SFTP"
      },
      {
        "command": "sftp.download.folder",
        "title": "Download Folder",
        "category": "SFTP",
        "icon": {
          "dark": "resources/dark/download-folder.svg",
          "light": "resources/light/download-folder.svg"
        }
      },
      {
        "command": "sftp.download.activeFolder",
        "title": "Download Active Folder",
        "category": "SFTP"
      },
      {
        "command": "sftp.download.project",
        "title": "Download Project",
        "category": "SFTP"
      },
      {
        "command": "sftp.forceDownload",
        "title": "Force Download",
        "category": "SFTP"
      },
      {
        "command": "sftp.sync.localToRemote",
        "title": "Sync Local -> Remote",
        "category": "SFTP"
      },
      {
        "command": "sftp.sync.remoteToLocal",
        "title": "Sync Remote -> Local",
        "category": "SFTP"
      },
      {
        "command": "sftp.sync.bothDirections",
        "title": "Sync Both Directions",
        "category": "SFTP"
      },
      {
        "command": "sftp.diff",
        "title": "Diff with Remote",
        "category": "SFTP"
      },
      {
        "command": "sftp.diff.activeFile",
        "title": "Diff Active File with Remote",
        "category": "SFTP"
      },
      {
        "command": "sftp.list",
        "title": "List",
        "category": "SFTP"
      },
      {
        "command": "sftp.listActiveFolder",
        "title": "List Active Folder",
        "category": "SFTP"
      },
      {
        "command": "sftp.listAll",
        "title": "List All",
        "category": "SFTP"
      },
      {
        "command": "sftp.delete.remote",
        "title": "Delete",
        "category": "SFTP"
      },
      {
        "command": "sftp.create.folder",
        "title": "Create Folder",
        "category": "SFTP"
      },
      {
        "command": "sftp.create.file",
        "title": "Create File",
        "category": "SFTP"
      },
      {
        "command": "sftp.revealInExplorer",
        "title": "Reveal in Explorer",
        "category": "SFTP"
      },
      {
        "command": "sftp.revealInRemoteExplorer",
        "title": "Reveal in Remote Explorer",
        "category": "SFTP"
      },
      {
        "command": "sftp.remoteExplorer.editInLocal",
        "title": "Edit in Local",
        "category": "SFTP"
      },
      {
        "command": "sftp.viewContent",
        "title": "View Content",
        "category": "SFTP"
      },
      {
        "command": "sftp.remoteExplorer.refresh",
        "title": "Refresh",
        "icon": {
          "light": "resources/light/refresh.svg",
          "dark": "resources/dark/refresh.svg"
        }
      },
      {
        "command": "sftp.remoteExplorer.refreshActiveFile",
        "title": "Refresh Active Remote File",
        "category": "SFTP",
        "icon": {
          "light": "resources/light/refresh.svg",
          "dark": "resources/dark/refresh.svg"
        }
      }
    ],
    "menus": {
      "commandPalette": [
        {
          "command": "sftp.setProfile",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.openConnectInTerminal",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.cancelAllTransfer",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.upload.activeFile",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.upload.activeFolder",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.upload.project",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.upload.activeFile.to.allProfiles",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.upload.activeFolder.to.allProfiles",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.upload.project.to.allProfiles",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.download.activeFile",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.download.activeFolder",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.download.project",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.sync.localToRemote",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.sync.remoteToLocal",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.sync.bothDirections",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.diff.activeFile",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.list",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.listActiveFolder",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.listAll",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.upload.file",
          "when": "false"
        },
        {
          "command": "sftp.upload.changedFiles",
          "when": "sftp.enabled"
        },
        {
          "command": "sftp.upload.folder",
          "when": "false"
        },
        {
          "command": "sftp.forceUpload",
          "when": "false"
        },
        {
          "command": "sftp.upload.file.to.allProfiles",
          "when": "false"
        },
        {
          "command": "sftp.upload.folder.to.allProfiles",
          "when": "false"
        },
        {
          "command": "sftp.forceUpload.to.allProfiles",
          "when": "false"
        },
        {
          "command": "sftp.download.file",
          "when": "false"
        },
        {
          "command": "sftp.download.folder",
          "when": "false"
        },
        {
          "command": "sftp.forceDownload",
          "when": "false"
        },
        {
          "command": "sftp.diff",
          "when": "false"
        },
        {
          "command": "sftp.delete.remote",
          "when": "false"
        },
        {
          "command": "sftp.create.folder",
          "when": "false"
        },
        {
          "command": "sftp.create.file",
          "when": "false"
        },
        {
          "command": "sftp.revealInExplorer",
          "when": "false"
        },
        {
          "command": "sftp.revealInRemoteExplorer",
          "when": "false"
        },
        {
          "command": "sftp.remoteExplorer.editInLocal",
          "when": "false"
        },
        {
          "command": "sftp.viewContent",
          "when": "false"
        },
        {
          "command": "sftp.remoteExplorer.refresh",
          "when": "false"
        }
      ],
      "explorer/context": [
        {
          "command": "sftp.revealInRemoteExplorer",
          "group": "navigation@20",
          "when": "sftp.enabled && !explorerResourceIsRoot"
        },
        {
          "command": "sftp.diff",
          "group": "3_compare",
          "when": "sftp.enabled && !explorerResourceIsRoot && !explorerResourceIsFolder"
        },
        {
          "command": "sftp.sync.localToRemote",
          "group": "sftp.sync@1",
          "when": "sftp.enabled && explorerResourceIsFolder"
        },
        {
          "command": "sftp.sync.remoteToLocal",
          "group": "sftp.sync@2",
          "when": "sftp.enabled && explorerResourceIsFolder"
        },
        {
          "command": "sftp.sync.bothDirections",
          "group": "sftp.sync@3",
          "when": "sftp.enabled && explorerResourceIsFolder"
        },
        {
          "command": "sftp.upload.file",
          "group": "sftp.trans@1",
          "when": "sftp.enabled && !explorerResourceIsRoot && !explorerResourceIsFolder"
        },
        {
          "command": "sftp.upload.folder",
          "group": "sftp.trans@1",
          "alt": "sftp.forceUpload",
          "when": "sftp.enabled && explorerResourceIsFolder"
        },
        {
          "command": "sftp.upload.file.to.allProfiles",
          "group": "sftp.trans@1",
          "when": "sftp.enabled && !explorerResourceIsRoot && !explorerResourceIsFolder"
        },
        {
          "command": "sftp.upload.folder.to.allProfiles",
          "group": "sftp.trans@1",
          "alt": "sftp.forceUpload.to.allProfiles",
          "when": "sftp.enabled && explorerResourceIsFolder"
        },
        {
          "command": "sftp.download.file",
          "group": "sftp.trans@2",
          "when": "sftp.enabled && !explorerResourceIsRoot && !explorerResourceIsFolder"
        },
        {
          "command": "sftp.download.folder",
          "group": "sftp.trans@2",
          "alt": "sftp.forceDownload",
          "when": "sftp.enabled && explorerResourceIsFolder"
        }
      ],
      "editor/context": [
        {
          "command": "sftp.upload.file",
          "group": "sftp.trans@1",
          "when": "sftp.enabled && resourceScheme == file"
        },
        {
          "command": "sftp.upload.file.to.allProfiles",
          "group": "sftp.trans@1",
          "when": "sftp.enabled && resourceScheme == file"
        },
        {
          "command": "sftp.download.file",
          "group": "sftp.trans@2",
          "when": "sftp.enabled && resourceScheme == file"
        },
        {
          "command": "sftp.diff",
          "group": "3_compare",
          "when": "sftp.enabled && resourceScheme == file"
        },
        {
          "command": "sftp.remoteExplorer.editInLocal",
          "group": "2_files",
          "when": "sftp.enabled && resourceScheme == remote"
        }
      ],
      "editor/title": [
        {
          "command": "sftp.remoteExplorer.refreshActiveFile",
          "group": "navigation",
          "when": "sftp.enabled && resourceScheme == remote"
        }
      ],
      "editor/title/context": [
        {
          "command": "sftp.revealInExplorer",
          "group": "2_files",
          "when": "sftp.enabled && resourceScheme == remote"
        },
        {
          "command": "sftp.revealInRemoteExplorer",
          "when": "sftp.enabled && resourceScheme == file",
          "group": "2_files"
        }
      ],
      "view/title": [
        {
          "command": "sftp.remoteExplorer.refresh",
          "when": "view == remoteExplorer",
          "group": "navigation"
        }
      ],
      "view/item/context": [
        {
          "command": "sftp.openConnectInTerminal",
          "group": "navigation",
          "when": "view == remoteExplorer && viewItem == root"
        },
        {
          "command": "sftp.remoteExplorer.editInLocal",
          "group": "2_files",
          "when": "view == remoteExplorer && viewItem == file && !config.sftp.downloadWhenOpenInRemoteExplorer"
        },
        {
          "command": "sftp.viewContent",
          "group": "2_files",
          "when": "view == remoteExplorer && viewItem == file && config.sftp.downloadWhenOpenInRemoteExplorer"
        },
        {
          "command": "sftp.revealInExplorer",
          "group": "2_files",
          "when": "view == remoteExplorer && viewItem != root"
        },
        {
          "command": "sftp.upload.folder",
          "group": "3_trans@1",
          "alt": "sftp.forceUpload",
          "when": "sftp.enabled && viewItem != file"
        },
        {
          "command": "sftp.upload.file",
          "group": "3_trans@1",
          "when": "view == remoteExplorer && viewItem != root && viewItem == file"
        },
        {
          "command": "sftp.upload.folder.to.allProfiles",
          "group": "3_trans@1",
          "alt": "sftp.forceUpload.to.allProfiles",
          "when": "sftp.enabled && viewItem != file"
        },
        {
          "command": "sftp.upload.file.to.allProfiles",
          "group": "3_trans@1",
          "when": "view == remoteExplorer && viewItem != root && viewItem == file"
        },
        {
          "command": "sftp.download.folder",
          "group": "3_trans@2",
          "alt": "sftp.forceDownload",
          "when": "sftp.enabled && viewItem != file"
        },
        {
          "command": "sftp.download.file",
          "group": "3_trans@2",
          "when": "view == remoteExplorer && viewItem != root && viewItem == file"
        },
        {
          "command": "sftp.delete.remote",
          "group": "7_modification",
          "when": "view == remoteExplorer && viewItem != root"
        },
        {
          "command": "sftp.create.folder",
          "group": "7_modification",
          "when": "view == remoteExplorer && viewItem != file"
        },
        {
          "command": "sftp.create.file",
          "group": "7_modification",
          "when": "view == remoteExplorer && viewItem != file"
        },
        {
          "command": "sftp.download.file",
          "group": "inline",
          "when": "view == remoteExplorer && viewItem == file"
        },
        {
          "command": "sftp.download.folder",
          "group": "inline",
          "when": "sftp.enabled && view == remoteExplorer && viewItem == folder"
        }
      ],
      "scm/title": [
        {
          "command": "sftp.upload.changedFiles",
          "group": "sftp",
          "when": "sftp.enabled && scmProvider == git && !gitFreshRepository"
        }
      ],
      "scm/resourceGroup/context": [
        {
          "command": "sftp.upload.changedFiles",
          "group": "inline",
          "when": "sftp.enabled && scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository"
        },
        {
          "command": "sftp.upload.changedFiles",
          "group": "sftp",
          "when": "sftp.enabled && scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository"
        },
        {
          "command": "sftp.upload.changedFiles",
          "group": "inline",
          "when": "sftp.enabled && scmProvider == git && scmResourceGroup == index && !gitFreshRepository"
        },
        {
          "command": "sftp.upload.changedFiles",
          "group": "sftp",
          "when": "sftp.enabled && scmProvider == git && scmResourceGroup == index && !gitFreshRepository"
        }
      ]
    },
    "keybindings": [
      {
        "command": "sftp.upload.changedFiles",
        "key": "ctrl+alt+u",
        "when": "sftp.enabled"
      }
    ],
    "jsonValidation": [
      {
        "fileMatch": ".vscode/sftp.json",
        "url": "./schema/config.schema.json"
      }
    ],
    "resourceLabelFormatters": [
      {
        "scheme": "remote",
        "formatting": {
          "label": "${path}",
          "separator": "/"
        }
      }
    ]
  },
  "scripts": {
    "vscode:prepublish": "npm run compile",
    "compile": "webpack --mode production",
    "dev": "webpack --mode development --watch",
    "test": "jest",
    "package": "vsce package",
    "pub:debug": "rm -rf _debug && mkdir _debug && vsce package --out _debug/sftp-debug.vsix && cd _debug && git init && git commit --allow-empty -m 'update debug package' && git checkout -b debug-pacakge && cp ../INSTALL_DEBUG.md README.md  && git add . && git commit -am 'update debug package' && git push git@github.com:Natizyskunk/vscode-sftp.git debug-pacakge --force"
  },
  "devDependencies": {
    "@types/fs-extra": "^9.0.13",
    "@types/jest": "^23.3.5",
    "@types/lru-cache": "^4.1.1",
    "@types/node": "^9.6.51",
    "@types/vscode": "1.40",
    "@types/webpack-env": "^1.13.6",
    "braces": ">=2.3.1",
    "glob-parent": ">=5.1.2",
    "jest": "^29.0.3",
    "json-schema": ">=0.4.0",
    "memfs": "^2.15.5",
    "merge": ">=2.1.1",
    "node-notifier": ">=8.0.1",
    "rimraf": "^2.7.1",
    "set-value": "^4.0.1",
    "ts-loader": "^9.4.1",
    "tslint": "^6.1.3",
    "typescript": "^3.9.7",
    "typescript-tslint-plugin": "^1.0.2",
    "webpack": "^5.0.0",
    "webpack-cli": "^4.7.0",
    "yargs-parser": "^20.2.4"
  },
  "dependencies": {
    "async": "^3.2.4",
    "fs-extra": "^10.1.0",
    "ftp": "^0.3.10",
    "ignore": "^5.1.4",
    "joi": "^10.6.0",
    "lodash.debounce": "^4.0.8",
    "lru-cache": "^4.1.3",
    "p-queue": "2.4.2",
    "ssh-config": "^1.1.3",
    "ssh2": "^1.13.0",
    "tmp": "^0.2.1",
    "upath": "^2.0.1"
  },
  "jest": {
    "verbose": true,
    "moduleFileExtensions": [
      "ts",
      "js"
    ],
    "transform": {
      "^.+\\.ts$": "<rootDir>/test/preprocessor.js"
    },
    "testMatch": [
      "<rootDir>/test/**/*.spec.js",
      "<rootDir>/**/*/__tests__/*.ts"
    ]
  }
}

================================================
FILE: schema/config.schema.json
================================================
{
  "oneOf": [
    {
      "type": "array",
      "items": {
        "$ref": "simple-config.schema.json"
      }
    },
    {
      "$ref": "simple-config.schema.json"
    }
  ]
}


================================================
FILE: schema/definitions.json
================================================
{
  "rootOption": {
    "properties": {
      "name": {
        "type": "string",
        "description": "A string to identify your config."
      },
      "context": {
        "type": "string",
        "description": "Relative path relative to the workspace root folder."
      },
      "watcher": {
        "type": "object",
        "description": "Watch external modification.",
        "properties": {
          "files": {
            "type": "string",
            "description": "A glob pattern.",
            "default": "**/*"
          },
          "autoUpload": {
            "type": "boolean",
            "description": "upload when file changed.",
            "default": true
          },
          "autoDelete": {
            "type": "boolean",
            "description": "delete when file changed.",
            "default": false
          }
        },
        "required": ["files"],
        "default": {
          "files": "**/*",
          "autoUpload": true,
          "autoDelete": false
        }
      },
      "defaultProfile": {
        "type": "string",
        "description": "Profile name you want to set as default."
      }
    },
    "required": ["name"]
  },
  "option": {
    "type": "object",
    "properties": {
      "remote": {
        "type": "string",
        "description": "Name of remote which configs with `remoteFs.remote` in User Setting. Configuration will get merged to this remote."
      },
      "uploadOnSave": {
        "type": "boolean",
        "description": "True to upload on every save operation of VS Code.",
        "default": false
      },
      "useTempFile": {
        "type": "boolean",
        "description": "True to upload temp file on every save operation of VS Code to avoid breaking a webpage when a user acceses it while the file is still being uploaded (is incomplete).",
        "default": false
      },
      "openSsh": {
        "type": "boolean",
        "description": "True to enable atomic file uploads (only supported by openSSH servers). if true, `useTempFile` must also be set to true.",
        "default": false
      },
      "downloadOnOpen": {
        "enum": ["confirm", true, false],
        "description": "True to download when a file opens.",
        "default": "confirm"
      },
      "syncOption": {
        "type": "object",
        "description": "Configuration the behavior of `Sync` command.",
        "properties": {
          "delete": {
            "type": "boolean",
            "description": "Delete extraneous files from dest dirs.",
            "default": true
          },
          "skipCreate": {
            "type": "boolean",
            "description": "Skip creating new files on dest.",
            "default": true
          },
          "ignoreExisting": {
            "type": "boolean",
            "description": "Skip updating files that exist on dest.",
            "default": true
          },
          "update": {
            "type": "boolean",
            "description": "Update the dest only if a newer version is on the src filesystem.",
            "default": true
          }
        }
      },
      "ignore": {
        "type": "array",
        "description": "Files to ignore. Same behavior as gitignore.",
        "items": {
          "type": "string",
          "description": "Ignore pattern."
        },
        "default": [".vscode", ".git", ".DS_Store"]
      },
      "ignoreFile": {
        "type": "string",
        "description": "Absolute path to the ignore file or Relative path relative to the workspace root folder.",
        "default": ".gitignore"
      },
      "remoteExplorer": {
        "type": "object",
        "description": "Remote Explorer Setting.",
        "properties": {
          "filesExclude": {
            "type": "array",
            "description": "Configure patterns for excluding files and folders. The Remote Explorer decides which files and folders to show or hide based on this setting.",
            "items": {
              "type": "string",
              "description": "Exclude pattern."
            },
            "default": []
          },
          "order": {
            "type": "number",
            "description": "Remote Explorer will ascending sorting by this value. If the values are the same, sort by name.",
            "default": 0
          }
        }
      },
      "remoteTimeOffsetInHours": {
        "type": "number",
        "description": "The number of hours difference between the local machine and remote/server. (remote minus local)"
      },
      "limitOpenFilesOnRemote": {
        "enum": ["number", true],
        "description": "Limit the open file descriptors to the specific number in a remote server. Set to true for using default limit(222). Do not set this unless you have to.",
        "default": true
      }
    }
  },

  "host": {
    "properties": {
      "host": {
        "type": "string",
        "description": "Hostname or IP address of the server.",
        "default": "localhost"
      },
      "port": {
        "type": "number",
        "description": "Port number of the server.",
        "default": 22
      },
      "username": {
        "type": "string",
        "description": "Username for authentication."
      },
      "password": {
        "type": "string",
        "description": "Password for password-based user authentication.",
        "default": ""
      },
      "remotePath": {
        "type": "string",
        "description": "The absolute path on remote.",
        "default": "/"
      },
      "connectTimeout": {
        "type": "number",
        "description": "How long (in milliseconds) to wait for the connect to complete.",
        "default": 10000
      }
    }
  },
  "sftp": {
    "type": "object",
    "properties": {
      "agent": {
        "type": "string",
        "description": "Path to ssh-agent's UNIX socket for ssh-agent-based user authentication.  Windows users: set to 'pageant' for authenticating with Pageant or (actual) path to a cygwin \"UNIX socket\"."
      },
      "privateKeyPath": {
        "type": "string",
        "description": "Absolute path to user private key."
      },
      "passphrase": {
        "oneOf": [
          {
            "type": "string"
          },
          {
            "enum": [true]
          }
        ],
        "description": "For an encrypted private key, this is the passphrase string used to decrypt it. Set to true for enable passphrase dialog. This will prevent from using cleartext passphrase in this config."
      },
      "interactiveAuth": {
        "oneOf": [
          {
            "type": "boolean",
            "description": "True to enable verifyCode dialog.",
            "default": false
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Array of predefined phrases to automatically pass them without user prompting."
          }
        ],
        "description": "Keyboard interaction authentication mechanism. For example using Google Authentication."
      },
      "algorithms": {
        "type": "object",
        "description": "Explicit overrides for the default transport layer algorithms used for the connection.",
        "properties": {
          "kex": {
            "type": "array",
            "items": {
              "enum": [
                "ecdh-sha2-nistp256",
                "ecdh-sha2-nistp384",
                "ecdh-sha2-nistp521",
                "diffie-hellman-group-exchange-sha256",
                "diffie-hellman-group14-sha1",
                "diffie-hellman-group-exchange-sha1",
                "diffie-hellman-group1-sha1"
              ]
            },
            "description": "Key exchange algorithms."
          },
          "cipher": {
            "type": "array",
            "items": {
              "enum": [
                "aes128-gcm",
                "aes128-gcm@openssh.com",
                "aes256-gcm",
                "aes256-gcm@openssh.com",
                "aes128-cbc",
                "aes192-cbc",
                "aes256-cbc",
                "aes128-ctr",
                "aes192-ctr",
                "aes256-ctr",
                "blowfish-cbc",
                "3des-cbc",
                "cast128-cbc",
                "arcfour",
                "arcfour128",
                "arcfour256"
              ]
            },
            "description": "Ciphers."
          },
          "serverHostKey": {
            "type": "array",
            "items": {
              "enum": [
                "ssh-rsa",
                "ssh-dss",
                "ssh-ed25519",
                "ecdsa-sha2-nistp256",
                "ecdsa-sha2-nistp384",
                "ecdsa-sha2-nistp521",
                "rsa-sha2-256",
                "rsa-sha2-512"
              ]
            },
            "description": "Server host key formats. In server mode, this list must agree with the host private keys set in the hostKeys configuration setting."
          },
          "hmac": {
            "type": "array",
            "items": {
              "enum": [
                "hmac-sha2-256",
                "hmac-sha2-512",
                "hmac-sha1",
                "hmac-md5",
                "hmac-sha2-256-96",
                "hmac-sha2-512-96",
                "hmac-ripemd160",
                "hmac-sha1-96",
                "hmac-md5-96"
              ]
            },
            "description": "(H)MAC algorithms."
          }
        }
      },
      "sshConfigPath": {
        "type": "string",
        "description": "Absolute path to your SSH configuration file (eg. ~/.ssh/config)"
      },
      "concurrency": {
        "type": "number",
        "description": "Concurrency number."
      },
      "sshCustomParams": {
        "type": "string",
        "description": "Extra parameters append to SSH command using by \"Open SSH in Terminal\"",
        "default": "\"cd \"${remotePath}\"; exec \\$SHELL -l\""
      }
    }
  },
  "ftp": {
    "type": "object",
    "properties": {
      "secure": {
        "type": ["string", "boolean"],
        "description": "Set to true for both control and data connection encryption, 'control' for control connection encryption only, or 'implicit' for\nimplicitly encrypted control connection (this mode is deprecated in modern times, but usually uses port 990).",
        "default": false
      },
      "secureOptions": {
        "type": "object",
        "description": "Options to be passed to tls.connect(). Default: (none)",
        "properties": {
          "host": {
            "type": "string",
            "description": "Host the client should connect to, defaults to 'localhost'."
          },
          "port": {
            "type": "number",
            "description": "Port the client should connect to."
          },
          "path": {
            "type": "string",
            "description": "Creates unix socket connection to path. If this option is specified, host and port are ignored."
          },
          "rejectUnauthorized": {
            "type": "boolean",
            "description": "If true, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails; err.code contains the OpenSSL error code. Defaults to true."
          },
          "NPNProtocols": {
            "type": "array",
            "description": "An array of strings or Buffers containing supported NPN protocols. Buffers should have the format [len][name][len][name]... e.g. 0x05hello0x05world, where the first byte is the length of the next protocol name. Passing an array is usually much simpler, e.g. ['hello', 'world'].",
            "items": {
              "type": "string"
            }
          },
          "ALPNProtocols": {
            "type": "array",
            "description": "An array of strings or Buffers containing the supported ALPN protocols. Buffers should have the format [len][name][len][name]... e.g. 0x05hello0x05world, where the first byte is the length of the next protocol name. Passing an array is usually much simpler: ['hello', 'world'].)",
            "items": {
              "type": "string"
            }
          },
          "servername": {
            "type": "string",
            "description": "Server name for the SNI (Server Name Indication) TLS extension."
          },
          "minDHSize": {
            "type": "number",
            "description": "Minimum size of the DH parameter in bits to accept a TLS connection. When a server offers a DH parameter with a size less than minDHSize, the TLS connection is destroyed and an error is thrown. Defaults to 1024."
          },
          "pfx": {
            "type": "string",
            "description": "Optional PFX or PKCS12 encoded private key and certificate chain. pfx is an alternative to providing key and cert individually. PFX is usually encrypted, if it is, passphrase will be used to decrypt it."
          },
          "key": {
            "type": ["string", "array"],
            "description": "Optional private keys in PEM format. PEM allows the option of private keys being encrypted. Encrypted keys will be decrypted with options.passphrase. Multiple keys using different algorithms can be provided either as an array of unencrypted key strings or buffers, or an array of objects in the form {pem: <string|buffer>[, passphrase: <string>]}. The object form can only occur in an array. object.passphrase is optional. Encrypted keys will be decrypted with object.passphrase if provided, or options.passphrase if it is not.",
            "items": {
              "type": "string"
            }
          },
          "passphrase": {
            "type": "string",
            "description": "Optional shared passphrase used for a single private key and/or a PFX."
          },
          "cert": {
            "type": ["string", "array"],
            "description": "Optional cert chains in PEM format. One cert chain should be provided per private key. Each cert chain should consist of the PEM formatted certificate for a provided private key, followed by the PEM formatted intermediate certificates (if any), in order, and not including the root CA (the root CA must be pre-known to the peer, see ca). When providing multiple cert chains, they do not have to be in the same order as their private keys in key. If the intermediate certificates are not provided, the peer will not be able to validate the certificate, and the handshake will fail.",
            "items": {
              "type": "string"
            }
          },
          "ca": {
            "type": ["string", "array"],
            "description": "Optionally override the trusted CA certificates. Default is to trust the well-known CAs curated by Mozilla. Mozilla's CAs are completely replaced when CAs are explicitly specified using this option. The value can be a string or Buffer, or an Array of strings and/or Buffers. Any string or Buffer can contain multiple PEM CAs concatenated together. The peer's certificate must be chainable to a CA trusted by the server for the connection to be authenticated. When using certificates that are not chainable to a well-known CA, the certificate's CA must be explicitly specified as a trusted or the connection will fail to authenticate. If the peer uses a certificate that doesn't match or chain to one of the default CAs, use the ca option to provide a CA certificate that the peer's certificate can match or chain to. For self-signed certificates, the certificate is its own CA, and must be provided.",
            "items": {
              "type": "string"
            }
          },
          "crl": {
            "type": ["string", "array"],
            "description": "Optional PEM formatted CRLs (Certificate Revocation Lists).",
            "items": {
              "type": "string"
            }
          },
          "ciphers": {
            "type": "string",
            "description": "Optional cipher suite specification, replacing the default. For more information, see modifying the default cipher suite."
          },
          "honorCipherOrder": {
            "type": "boolean",
            "description": "Attempt to use the server's cipher suite preferences instead of the client's. When true, causes SSL_OP_CIPHER_SERVER_PREFERENCE to be set in secureOptions, see OpenSSL Options for more information. Note: tls.createServer() sets the default value to true, other APIs that create secure contexts leave it unset."
          },
          "ecdhCurve": {
            "type": "string",
            "description": "A string describing a named curve to use for ECDH key agreement or false to disable ECDH. Defaults to tls.DEFAULT_ECDH_CURVE. Use crypto.getCurves() to obtain a list of available curve names. On recent releases, openssl ecparam -list_curves will also display the name and description of each available elliptic curve."
          },
          "dhparam": {
            "type": "string",
            "description": "Diffie Hellman parameters, required for Perfect Forward Secrecy. Use openssl dhparam to create the parameters. The key length must be greater than or equal to 1024 bits, otherwise an error will be thrown. It is strongly recommended to use 2048 bits or larger for stronger security. If omitted or invalid, the parameters are silently discarded and DHE ciphers will not be available."
          },
          "secureProtocol": {
            "type": "string",
            "description": "Optional SSL method to use, default is \"SSLv23_method\". The possible values are listed as SSL_METHODS, use the function names as strings. For example, \"SSLv3_method\" to force SSL version 3."
          },
          "secureOptions": {
            "type": "number",
            "description": "Optionally affect the OpenSSL protocol behaviour, which is not usually necessary. This should be used carefully if at all! Value is a numeric bitmask of the SSL_OP_* options from OpenSSL Options."
          },
          "sessionIdContext": {
            "type": "string",
            "description": "Optional opaque identifier used by servers to ensure session state is not shared between applications. Unused by clients. Note: tls.createServer() uses a 128 bit truncated SHA1 hash value generated from process.argv, other APIs that create secure contexts have no default value."
          }
        }
      }
    }
  }
}


================================================
FILE: schema/ftp.schema.json
================================================
{
  "allOf": [
    {
      "$ref": "definitions.json#/rootOption"
    },
    {
      "$ref": "definitions.json#/option"
    },
    {
      "$ref": "definitions.json#/host"
    },
    {
      "$ref": "definitions.json#/sftp"
    },
    {
      "type": "object",
      "properties": {
        "protocol": {
          "enum": ["ftp"],
          "description": "ftp connection protocol."
        },

        "profiles": {
          "patternProperties": {
            "^.*$": {
              "allOf": [
                {
                  "$ref": "definitions.json#/option"
                },
                {
                  "$ref": "definitions.json#/host"
                },
                {
                  "$ref": "definitions.json#/sftp"
                }
              ]
            }
          },
          "default": {
            "profileName": {}
          }
        }
      }
    }
  ]
}


================================================
FILE: schema/sftp.schema.json
================================================
{
  "allOf": [
    {
      "$ref": "definitions.json#/rootOption"
    },
    {
      "$ref": "definitions.json#/option"
    },
    {
      "$ref": "definitions.json#/host"
    },
    {
      "$ref": "definitions.json#/sftp"
    },
    {
      "type": "object",
      "properties": {
        "protocol": {
          "enum": ["sftp"],
          "description": "sftp connection protocol."
        },
        "hop": {
          "oneOf": [
            {
              "$ref": "#/definitions/hop"
            },
            {
              "type": "array",
              "items": {
                "$ref": "#/definitions/hop"
              }
            }
          ],
          "default": {
            "host": "bridgeHost",
            "port": "bridgePort"
          }
        },
        "profiles": {
          "description": "A collection of profiles. Using key as name. The profile will be merge to top level config.",
          "patternProperties": {
            "^.*$": {
              "allOf": [
                {
                  "$ref": "definitions.json#/option"
                },
                {
                  "$ref": "definitions.json#/host"
                },
                {
                  "$ref": "definitions.json#/sftp"
                }
              ]
            }
          },
          "default": {
            "profileName": {}
          }
        }
      }
    }
  ],
  "definitions": {
    "hop": {
      "allOf": [
        {
          "$ref": "definitions.json#/host"
        },
        {
          "$ref": "definitions.json#/sftp"
        }
      ]
    }
  }
}


================================================
FILE: schema/simple-config.schema.json
================================================
{
  "oneOf": [{ "$ref": "sftp.schema.json" }, { "$ref": "ftp.schema.json" }]
}


================================================
FILE: src/app.ts
================================================
import * as LRU from 'lru-cache';
import StatusBarItem from './ui/statusBarItem';
import { COMMAND_TOGGLE_OUTPUT } from './constants';
import AppState from './modules/appState';
import RemoteExplorer from './modules/remoteExplorer';

interface App {
  fsCache: LRU.Cache<string, string>;
  state: AppState;
  sftpBarItem: StatusBarItem;
  remoteExplorer: RemoteExplorer;
}

const app: App = Object.create(null);

app.state = new AppState();
app.sftpBarItem = new StatusBarItem(
  () => {
    if (app.state.profile) {
      return `SFTP: ${app.state.profile}`;
    } else {
      return 'SFTP';
    }
  },
  'SFTP@Natizyskunk',
  COMMAND_TOGGLE_OUTPUT
);
app.fsCache = LRU<string, string>({ max: 6 });

export default app;


================================================
FILE: src/commands/abstract/command.ts
================================================
import { reportError } from '../../helper';
import logger from '../../logger';

export interface ITarget {
  fsPath: string;
}

export interface CommandOption {
  [x: string]: any;
}

export default abstract class Command {
  id: string;
  name!: string;
  private _commandDoneListeners: Array<(...args: any[]) => void>;

  constructor() {
    this._commandDoneListeners = [];
  }

  onCommandDone(listener) {
    this._commandDoneListeners.push(listener);

    return () => {
      const index = this._commandDoneListeners.indexOf(listener);
      if (index > -1) this._commandDoneListeners.splice(index, 1);
    };
  }

  protected abstract async doCommandRun(...args: any[]);

  async run(...args) {
    logger.trace(`run command '${this.name}'`);
    try {
      await this.doCommandRun(...args);
    } catch (error) {
      reportError(error);
    } finally {
      this.commitCommandDone(...args);
    }
  }

  private commitCommandDone(...args: any[]) {
    this._commandDoneListeners.forEach(listener => listener(...args));
  }
}


================================================
FILE: src/commands/abstract/createCommand.ts
================================================
import { Uri, window } from 'vscode';
import logger from '../../logger';
import { reportError } from '../../helper';
import { handleCtxFromUri, allHandleCtxFromUri, FileHandlerContext } from '../../fileHandlers';
import Command from './command';

interface BaseCommandOption {
  id: string;
  name?: string;
}

interface CommandOption extends BaseCommandOption {
  handleCommand: (this: Command, ...args: any[]) => unknown | Promise<unknown>;
}

interface FileCommandOption extends BaseCommandOption {
  handleFile: (ctx: FileHandlerContext) => Promise<unknown>;
  getFileTarget: (...args: any[]) => undefined | Uri | Uri[] | Promise<undefined | Uri | Uri[]>;
}

function checkType<T>() {
  return (a: T) => a;
}

export const checkCommand = checkType<CommandOption>();
export const checkFileCommand = checkType<FileCommandOption>();

export function createCommand(commandOption: CommandOption & { name: string }) {
  return class NormalCommand extends Command {
    constructor() {
      super();
      this.id = commandOption.id;
      this.name = commandOption.name;
    }

    doCommandRun(...args) {
      commandOption.handleCommand.apply(this, args);
    }
  };
}

export function createFileCommand(commandOption: FileCommandOption & { name: string }) {
  return class FileCommand extends Command {
    constructor() {
      super();
      this.id = commandOption.id;
      this.name = commandOption.name;
    }

    protected async doCommandRun(...args) {
      if ((this.id === COMMAND_UPLOAD_FILE_TO_ALL_PROFILES || this.id === COMMAND_UPLOAD_FOLDER_TO_ALL_PROFILES) 
        && await window.showInformationMessage('Are you sure you want to upload to all profiles?', 'Yes', 'No').then(answer => answer !== 'Yes')) {
        return;
      }
      
      const target = await commandOption.getFileTarget(...args);
      if (!target) {
        logger.warn(`The "${this.name}" command get canceled because of missing targets.`);
        return;
      }

      const targetList: Uri[] = Array.isArray(target) ? target : [target];
      const pendingTasks = targetList.map(async uri => {
        try {
          await commandOption.handleFile(handleCtxFromUri(uri));
        } catch (error) {
          reportError(error);
        }
      });

      await Promise.all(pendingTasks);
    }
  };
}

export function createFileMultiCommand(commandOption: FileCommandOption & { name: string }) {
  return class FileCommand extends Command {
    constructor() {
      super();
      this.id = commandOption.id;
      this.name = commandOption.name;
    }

    protected async doCommandRun(...args) {
      if ((this.id === COMMAND_UPLOAD_FILE_TO_ALL_PROFILES || this.id === COMMAND_UPLOAD_FOLDER_TO_ALL_PROFILES) 
        && await window.showInformationMessage('Are you sure you want to upload to all profiles?', 'Yes', 'No').then(answer => answer !== 'Yes')) {
        return;
      }
      
      const target = await commandOption.getFileTarget(...args);
      if (!target) {
        logger.warn(`The "${this.name}" command get canceled because of missing targets.`);
        return;
      }

      const targetList: Uri[] = Array.isArray(target) ? target : [target];
      const pendingTasks = targetList.map(async uri => {
        try {
          await Promise.all(allHandleCtxFromUri(uri).map(commandOption.handleFile));
        } catch (error) {
          reportError(error);
        }
      });

      await Promise.all(pendingTasks);
    }
  };
}


================================================
FILE: src/commands/commandCancelAllTransfer.ts
================================================
import { COMMAND_CANCEL_ALL_TRANSFER } from '../constants';
import { checkCommand } from './abstract/createCommand';
import { findAllFileService } from '../modules/serviceManager';

export default checkCommand({
  id: COMMAND_CANCEL_ALL_TRANSFER,

  async handleCommand() {
    findAllFileService(f => f.isTransferring()).forEach(f => f.cancelTransferTasks());
  },
});


================================================
FILE: src/commands/commandConfig.ts
================================================
import * as vscode from 'vscode';
import { COMMAND_CONFIG } from '../constants';
import { newConfig } from '../modules/config';
import {
  getWorkspaceFolders,
  showConfirmMessage,
  showOpenDialog,
  openFolder,
  addWorkspaceFolder,
} from '../host';
import { checkCommand } from './abstract/createCommand';

export default checkCommand({
  id: COMMAND_CONFIG,

  async handleCommand() {
    const workspaceFolders = getWorkspaceFolders();
    if (!workspaceFolders) {
      const result = await showConfirmMessage(
        'SFTP expects to work at a folder.',
        'Open Folder',
        'Ok'
      );

      if (!result) {
        return;
      }

      return openFolder();
    }

    if (workspaceFolders.length <= 0) {
      const result = await showConfirmMessage(
        'There are no available folders in current workspace.',
        'Add Folder to Workspace',
        'Ok'
      );

      if (!result) {
        return;
      }

      const resources = await showOpenDialog({
        canSelectFiles: false,
        canSelectFolders: true,
        canSelectMany: true,
      });

      if (!resources) {
        return;
      }

      addWorkspaceFolder(...resources.map(uri => ({ uri })));
      return;
    }

    if (workspaceFolders.length === 1) {
      newConfig(workspaceFolders[0].uri.fsPath);
      return;
    }

    const initDirs = workspaceFolders.map(folder => ({
      value: folder.uri.fsPath,
      label: folder.name,
      description: folder.uri.fsPath,
    }));

    vscode.window
      .showQuickPick(initDirs, {
        placeHolder: 'Select a folder...',
      })
      .then(item => {
        if (item === undefined) {
          return;
        }

        newConfig(item.value);
      });
  },
});


================================================
FILE: src/commands/commandListActiveFolder.ts
================================================
import * as path from 'path';
import { Uri } from 'vscode';
import { COMMAND_LIST_ACTIVEFOLDER } from '../constants';
import { showTextDocument } from '../host';
import { FileType } from '../core';
import { downloadFile, downloadFolder } from '../fileHandlers';
import { checkCommand } from './abstract/createCommand';
import { getActiveFolder } from './shared';
import { handleCtxFromUri } from '../fileHandlers';
import { listFiles, toLocalPath } from '../helper';

export default checkCommand({
  id: COMMAND_LIST_ACTIVEFOLDER,

  async handleCommand() {
    const folderUri = getActiveFolder();
    if (!folderUri) {
      return;
    }

    const ctx = handleCtxFromUri(folderUri);
    const config = ctx.config;
    const remotefs = await ctx.fileService.getRemoteFileSystem(config);
    const fileEntry = await remotefs.list(ctx.target.remoteFsPath);
    const filter = config.ignore ? file => !config.ignore!(file.fsPath) : undefined;

    const listItems = fileEntry.map(file => ({
      name: path.basename(file.fspath) + (file.type === FileType.Directory ? '/' : ''),
      fsPath: file.fspath,
      type: file.type,
      description: '',
      getFs: remotefs,
      filter,
    }));
    const selected = await listFiles(listItems);
    if (!selected) {
      return;
    }

    const localUri = Uri.file(
      toLocalPath(selected.fsPath, config.remotePath, ctx.fileService.baseDir)
    );
    if (selected.type !== FileType.Directory) {
      await downloadFile(localUri);
      try {
        await showTextDocument(localUri);
      } catch (error) {
        // ignore
      }
    } else {
      await downloadFolder(localUri);
    }
  },
});


================================================
FILE: src/commands/commandOpenSshConnection.ts
================================================
import * as vscode from 'vscode';
import { COMMAND_OPEN_CONNECTION_IN_TERMINAL } from '../constants';
import { getAllFileService } from '../modules/serviceManager';
import { ExplorerRoot } from '../modules/remoteExplorer';
import { interpolate } from '../utils';
import { checkCommand } from './abstract/createCommand';

const isWindows = process.platform === 'win32';

function shouldUseAgent(config) {
  return typeof config.agent === 'string' && config.agent.length > 0;
}

function shouldUseKey(config) {
  return typeof config.privateKeyPath === 'string' && config.privateKeyPath.length > 0;
}

function adaptPath(filepath) {
  if (isWindows) {
    return filepath.replace(/\\\\/g, '\\');
  }

  // convert to unix style
  return filepath.replace(/\\\\/g, '/').replace(/\\/g, '/');
}

function getSshCommand(
  config: { host: string; port: number; username: string },
  extraOption?: string
) {
  let sshStr = `ssh -t ${config.username}@${config.host} -p ${config.port}`;
  if (extraOption) {
    sshStr += ` ${extraOption}`;
  }
  // sshStr += ` "cd \\"${config.workingDir}\\"; exec \\$SHELL -l"`;
  return sshStr;
}

export default checkCommand({
  id: COMMAND_OPEN_CONNECTION_IN_TERMINAL,

  async handleCommand(exploreItem?: ExplorerRoot) {
    let remoteConfig;
    if (exploreItem && exploreItem.explorerContext) {
      remoteConfig = exploreItem.explorerContext.config;
      if (remoteConfig.protocol !== 'sftp') {
        return;
      }
    } else {
      const remoteItems = getAllFileService().reduce<
        { label: string; description: string; config: any }[]
      >((result, fileService) => {
        const config = fileService.getConfig();
        if (config.protocol === 'sftp') {
          result.push({
            label: config.name || config.remotePath,
            description: config.host,
            config,
          });
        }
        return result;
      }, []);
      if (remoteItems.length <= 0) {
        return;
      }

      const item = await vscode.window.showQuickPick(remoteItems, {
        placeHolder: 'Select a folder...',
      });
      if (item === undefined) {
        return;
      }

      remoteConfig = item.config;
    }

    const sshConfig = {
      host: remoteConfig.host,
      port: remoteConfig.port,
      username: remoteConfig.username,
    };
    const terminal = vscode.window.createTerminal(remoteConfig.name);
    let sshCommand;
    if (shouldUseAgent(remoteConfig)) {
      sshCommand = getSshCommand(sshConfig);
    } else if (shouldUseKey(remoteConfig)) {
      sshCommand = getSshCommand(sshConfig, `-i "${adaptPath(remoteConfig.privateKeyPath)}"`);
    } else {
      sshCommand = getSshCommand(sshConfig);
    }

    if (remoteConfig.sshCustomParams) {
      sshCommand =
        sshCommand +
        ' ' +
        interpolate(remoteConfig.sshCustomParams, {
          remotePath: remoteConfig.remotePath,
        });
    }

    terminal.sendText(sshCommand);
    terminal.show();
  },
});


================================================
FILE: src/commands/commandSetProfile.ts
================================================
import * as vscode from 'vscode';
import { COMMAND_SET_PROFILE } from '../constants';
import { showInformationMessage } from '../host';
import app from '../app';
import logger from '../logger';
import { getAllFileService } from '../modules/serviceManager';
import { checkCommand } from './abstract/createCommand';

export default checkCommand({
  id: COMMAND_SET_PROFILE,

  async handleCommand(definedProfile) {
    const profiles = getAllFileService().reduce<
      Array<vscode.QuickPickItem & { value: string | null }>
    >(
      (acc, service) => {
        if (service.getAvailableProfiles().length <= 0) {
          return acc;
        }

        service.getAvailableProfiles().forEach(profile => {
          acc.push({
            value: profile,
            label: app.state.profile === profile ? `${profile} (active)` : profile,
          });
        });
        return acc;
      },
      [
        {
          value: null,
          label: 'UNSET',
        },
      ]
    );

    if (profiles.length <= 1) {
      showInformationMessage('No Available Profile.');
      return;
    }

    if (definedProfile !== undefined) {
      const index = profiles.findIndex(a => a.value === definedProfile);
      if (index !== -1) {
        app.state.profile = definedProfile;
      } else {
        app.state.profile = null;
        logger.warn(`try to set a unknown profile "${definedProfile}"`);
      }
      return;
    }

    const item = await vscode.window.showQuickPick(profiles, { placeHolder: 'select a profile' });
    if (item === undefined) return;
    app.state.profile = item.value;
  },
});


================================================
FILE: src/commands/commandToggleOutputPanel.ts
================================================
import { COMMAND_TOGGLE_OUTPUT } from '../constants';
import * as output from '../ui/output';
import { checkCommand } from './abstract/createCommand';

export default checkCommand({
  id: COMMAND_TOGGLE_OUTPUT,

  handleCommand() {
    output.toggle();
  },
});


================================================
FILE: src/commands/commandUploadChangedFiles.ts
================================================
import * as vscode from 'vscode';
import * as path from 'path';
import { COMMAND_UPLOAD_CHANGEDFILES } from '../constants';
import { getFileService } from '../modules/serviceManager';
import { uploadFile, renameRemote, removeRemote } from '../fileHandlers';
import { getGitService, GitAPI, Repository, Status, Change } from '../modules/git';
import { checkCommand } from './abstract/createCommand';
import logger from '../logger';
import { simplifyPath } from '../helper';

export default checkCommand({
  id: COMMAND_UPLOAD_CHANGEDFILES,

  async handleCommand(hint: any) {
    return handleCommand(hint);

    // resourceGroup.resourceStates.forEach(resourceState => {
    //   resourceState.
    //   console.log(resourceState.decorations);
    // });

    // try {
    //   await uploadFile(ctx, { ignore: null });
    // } catch (error) {
    //   // ignore error when try to upload a deleted file
    //   if (error.code !== 'ENOENT') {
    //     throw error;
    //   }
    // }
  },
});

function isRepository(object: any): object is Repository {
  return 'rootUri' in object;
}

function isSourceControlResourceGroup(object: any): object is vscode.SourceControlResourceGroup {
  return 'id' in object && 'resourceStates' in object;
}

async function handleCommand(hint: any) {
  let repository: Repository | undefined;
  let filterGroupId;
  const git = getGitService();

  if (!hint) {
    repository = await getRepository(git);
  } else if (isSourceControlResourceGroup(hint)) {
    repository = git.repositories.find(repo => repo.ui.selected);
    filterGroupId = hint.id;
  } else if (isRepository(hint)) {
    repository = git.repositories.find(repo => repo.ui.selected);
  }

  if (!repository) {
    return;
  }

  let changes: Change[];
  if (filterGroupId === 'index') {
    changes = repository.state.indexChanges;
  } else if (filterGroupId === 'workingTree') {
    changes = repository.state.workingTreeChanges;
  } else {
    changes = repository.state.indexChanges.concat(repository.state.workingTreeChanges);
  }

  const creates: Change[] = [];
  const uploads: Change[] = [];
  const renames: Change[] = [];
  const deletes: Change[] = [];
  for (const change of changes) {
    if (!getFileService(change.uri)) {
      continue;
    }

    switch (change.status) {
      case Status.INDEX_MODIFIED:
      case Status.MODIFIED:
        uploads.push(change);
        break;
      case Status.INDEX_ADDED:
      case Status.UNTRACKED:
        creates.push(change);
        break;
      case Status.INDEX_RENAMED:
        renames.push(change);
        break;
      case Status.INDEX_DELETED:
      case Status.DELETED:
        deletes.push(change);
        break;
      default:
        break;
    }
  }

  await Promise.all(creates.concat(uploads).map(change => {
    try {
      uploadFile(change.uri)
    } catch (e) {
      logger.error('Upload failed.', e);
    }
  }));
  await Promise.all(
    renames.map(change => {
      try {
        renameRemote(change.originalUri, { originPath: change.renameUri!.fsPath });
      } catch (e) {
        logger.error('Rename failed.', e);
      }
    })
  );
  await Promise.all(deletes.map(change => {
    try {
      removeRemote(change.uri)
    } catch (e) {
      logger.error('Deletion failed.', e);
    }
  }));

  logger.log('');
  logger.log('------ Upload Changed Files Result ------');
  outputGroup('create', creates, c => simplifyPath(c.uri.fsPath));
  outputGroup('upload', uploads, c => simplifyPath(c.uri.fsPath));
  outputGroup(
    'renamed',
    renames,
    c => `${simplifyPath(c.originalUri.fsPath)} ➞ ${simplifyPath(c.renameUri!.fsPath)}`
  );
  outputGroup('deleted', deletes, c => simplifyPath(c.uri.fsPath));
}

function outputGroup<T>(label: string, items: T[], formatItem: (x: T) => string) {
  if (items.length <= 0) {
    return;
  }

  logger.log(`${label.toUpperCase()}:`);
  logger.log(items.map(i => formatItem(i)).join('\n'));
  logger.log('');
}

async function getRepository(git: GitAPI): Promise<Repository | undefined> {
  if (git.repositories.length === 1) {
    return git.repositories[0];
  }

  if (git.repositories.length === 0) {
    throw new Error('There are no available repositories');
  }

  const picks = git.repositories.map(repo => {
    const label = path.basename(repo.rootUri.fsPath);
    const description = repo.state.HEAD ? repo.state.HEAD.name : '';

    return {
      label,
      description,
      repository: repo,
    };
  });

  const pick = await vscode.window.showQuickPick(picks, { placeHolder: 'Choose a repository' });

  return pick && pick.repository;
}


================================================
FILE: src/commands/fileCommandCreateFile.ts
================================================
import { COMMAND_CREATE_FILE } from '../constants';
import { createRemoteFile } from '../fileHandlers';
// import { showConfirmMessage } from '../host';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';
import { window, Uri } from 'vscode';

export default checkFileCommand({
  id: COMMAND_CREATE_FILE,
  async getFileTarget(item, items) {
    const targets = await uriFromExplorerContextOrEditorContext(item, items);

    if (!targets) {
      return;
    }
   /* const filename = Array.isArray(targets)
    ? targets.map(t => upath.basename(t.fsPath)).join(',')
    : upath.basename(targets.fsPath);
*/
    const result = await window.showInputBox({
        value: '',
        prompt: 'Please input file name',
    });


    if (result !== undefined) {
        // window.showInformationMessage(targets.toString() + '%252F' + result);

        return Uri.parse(targets.toString() + '/' + result);
    }

    
    return undefined;
    
/*
    const filename = Array.isArray(targets)
      ? targets.map(t => upath.basename(t.fsPath)).join(',')
      : upath.basename(targets.fsPath);
    const result = await showConfirmMessage(
      `Are you sure you want to delete '${filename}'?`,
      'Delete',
      'Cancel'
    );

    return result ? targets : undefined;*/
  },

  handleFile: createRemoteFile,
});


================================================
FILE: src/commands/fileCommandCreateFolder.ts
================================================
import { COMMAND_CREATE_FOLDER } from '../constants';
import { createRemoteFolder } from '../fileHandlers';
// import { showConfirmMessage } from '../host';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';
import { window, Uri } from 'vscode';

export default checkFileCommand({
  id: COMMAND_CREATE_FOLDER,
  async getFileTarget(item, items) {
    const targets = await uriFromExplorerContextOrEditorContext(item, items);

    if (!targets) {
      return;
    }
   /* const filename = Array.isArray(targets)
    ? targets.map(t => upath.basename(t.fsPath)).join(',')
    : upath.basename(targets.fsPath);
*/
    const result = await window.showInputBox({
        value: '',
        prompt: 'Please input folder name',
    });


    if (result !== undefined) {
     //   window.showInformationMessage(targets.toString() + '%252F' + result);

        return Uri.parse(targets.toString() + '/' + result);
    }

    
    return undefined;
    
/*
    const filename = Array.isArray(targets)
      ? targets.map(t => upath.basename(t.fsPath)).join(',')
      : upath.basename(targets.fsPath);
    const result = await showConfirmMessage(
      `Are you sure you want to delete '${filename}'?`,
      'Delete',
      'Cancel'
    );

    return result ? targets : undefined;*/
  },

  handleFile: createRemoteFolder,
});


================================================
FILE: src/commands/fileCommandDeleteRemote.ts
================================================
import { COMMAND_DELETE_REMOTE } from '../constants';
import { upath } from '../core';
import { removeRemote } from '../fileHandlers';
import { showConfirmMessage } from '../host';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';

export default checkFileCommand({
  id: COMMAND_DELETE_REMOTE,
  async getFileTarget(item, items) {
    const targets = await uriFromExplorerContextOrEditorContext(item, items);

    if (!targets) {
      return;
    }

    const filename = Array.isArray(targets)
      ? targets.map(t => upath.basename(t.fsPath)).join(',')
      : upath.basename(targets.fsPath);
    const result = await showConfirmMessage(
      `Are you sure you want to delete '${filename}'?`,
      'Delete',
      'Cancel'
    );

    return result ? targets : undefined;
  },

  handleFile: removeRemote,
});


================================================
FILE: src/commands/fileCommandDiff.ts
================================================
import { COMMAND_DIFF } from '../constants';
import { diff } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';

export default checkFileCommand({
  id: COMMAND_DIFF,
  getFileTarget: uriFromExplorerContextOrEditorContext,
  handleFile: diff,
});


================================================
FILE: src/commands/fileCommandDiffActiveFile.ts
================================================
import { COMMAND_DIFF_ACTIVEFILE } from '../constants';
import { diff } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { getActiveDocumentUri } from './shared';

export default checkFileCommand({
  id: COMMAND_DIFF_ACTIVEFILE,
  getFileTarget: getActiveDocumentUri,
  handleFile: diff,
});


================================================
FILE: src/commands/fileCommandDownload.ts
================================================
import { COMMAND_DOWNLOAD } from '../constants';
import { download } from '../fileHandlers';
import { uriFromfspath } from './shared';
import { checkFileCommand } from './abstract/createCommand';

export default checkFileCommand({
  id: COMMAND_DOWNLOAD,
  getFileTarget: uriFromfspath,

  async handleFile(ctx) {
    await download(ctx, { ignore: null });
  },
});


================================================
FILE: src/commands/fileCommandDownloadActiveFile.ts
================================================
import { COMMAND_DOWNLOAD_ACTIVEFILE } from '../constants';
import { downloadFile } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { getActiveDocumentUri } from './shared';

export default checkFileCommand({
  id: COMMAND_DOWNLOAD_ACTIVEFILE,
  getFileTarget: getActiveDocumentUri,

  async handleFile(ctx) {
    await downloadFile(ctx, { ignore: null });
  },
});


================================================
FILE: src/commands/fileCommandDownloadActiveFolder.ts
================================================
import { COMMAND_DOWNLOAD_ACTIVEFOLDER } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import { getActiveFolder } from './shared';
import fileCommandDownloadFolder from './fileCommandDownloadFolder';

export default checkFileCommand({
  ...fileCommandDownloadFolder,
  id: COMMAND_DOWNLOAD_ACTIVEFOLDER,
  getFileTarget: getActiveFolder,
});


================================================
FILE: src/commands/fileCommandDownloadFile.ts
================================================
import { COMMAND_DOWNLOAD_FILE } from '../constants';
import { downloadFile } from '../fileHandlers';
import { uriFromExplorerContextOrEditorContext } from './shared';
import { checkFileCommand } from './abstract/createCommand';

export default checkFileCommand({
  id: COMMAND_DOWNLOAD_FILE,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  async handleFile(ctx) {
    await downloadFile(ctx, { ignore: null });
  },
});


================================================
FILE: src/commands/fileCommandDownloadFolder.ts
================================================
import { COMMAND_DOWNLOAD_FOLDER } from '../constants';
import { downloadFolder } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';

export default checkFileCommand({
  id: COMMAND_DOWNLOAD_FOLDER,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  handleFile: downloadFolder,
});


================================================
FILE: src/commands/fileCommandDownloadForce.ts
================================================
import { COMMAND_FORCE_DOWNLOAD } from '../constants';
import { download } from '../fileHandlers';
import { uriFromExplorerContextOrEditorContext } from './shared';
import { checkFileCommand } from './abstract/createCommand';

export default checkFileCommand({
  id: COMMAND_FORCE_DOWNLOAD,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  async handleFile(ctx) {
    await download(ctx, { ignore: null });
  },
});


================================================
FILE: src/commands/fileCommandDownloadProject.ts
================================================
import { COMMAND_DOWNLOAD_PROJECT } from '../constants';
import { downloadFolder } from '../fileHandlers';
import { selectContext } from './shared';
import { checkFileCommand } from './abstract/createCommand';

export default checkFileCommand({
  id: COMMAND_DOWNLOAD_PROJECT,
  getFileTarget: selectContext,

  async handleFile(ctx) {
    await downloadFolder(ctx);
  },
});


================================================
FILE: src/commands/fileCommandEditInLocal.ts
================================================
import { COMMAND_REMOTEEXPLORER_EDITINLOCAL } from '../constants';
import { downloadFile } from '../fileHandlers';
import { showTextDocument } from '../host';
import { uriFromExplorerContextOrEditorContext } from './shared';
import { checkFileCommand } from './abstract/createCommand';

export default checkFileCommand({
  id: COMMAND_REMOTEEXPLORER_EDITINLOCAL,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  async handleFile(ctx) {
    await downloadFile(ctx, { ignore: null });
    await showTextDocument(ctx.target.localUri, { preview: true });
  },
});


================================================
FILE: src/commands/fileCommandList.ts
================================================
import { COMMAND_LIST } from '../constants';
import { showTextDocument } from '../host';
import { FileType } from '../core';
import { downloadFile, downloadFolder } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { selectFile } from './shared';

export default checkFileCommand({
  id: COMMAND_LIST,
  getFileTarget: selectFile,

  async handleFile(ctx) {
    const remotefs = await ctx.fileService.getRemoteFileSystem(ctx.config);
    const fileEntry = await remotefs.lstat(ctx.target.remoteFsPath);
    if (fileEntry.type !== FileType.Directory) {
      await downloadFile(ctx);
      try {
        await showTextDocument(ctx.target.localUri);
      } catch (error) {
        // ignore
      }
    } else {
      await downloadFolder(ctx);
    }
  },
});


================================================
FILE: src/commands/fileCommandListAll.ts
================================================
import { COMMAND_LIST_ALL } from '../constants';
import { showTextDocument } from '../host';
import { FileType } from '../core';
import { downloadFile, downloadFolder } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { selectFileFromAll } from './shared';

export default checkFileCommand({
  id: COMMAND_LIST_ALL,
  getFileTarget: selectFileFromAll,

  async handleFile(ctx) {
    const remotefs = await ctx.fileService.getRemoteFileSystem(ctx.config);
    const fileEntry = await remotefs.lstat(ctx.target.remoteFsPath);
    if (fileEntry.type !== FileType.Directory) {
      await downloadFile(ctx, { ignore: null });
      try {
        await showTextDocument(ctx.target.localUri);
      } catch (error) {
        // ignore
      }
    } else {
      await downloadFolder(ctx, { ignore: null });
    }
  },
});


================================================
FILE: src/commands/fileCommandRevealInExplorer.ts
================================================
import { COMMAND_REVEAL_IN_EXPLORER } from '../constants';
import { executeCommand } from '../host';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';

export default checkFileCommand({
  id: COMMAND_REVEAL_IN_EXPLORER,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  async handleFile({ target }) {
    await executeCommand('revealInExplorer', target.localUri);
  },
});


================================================
FILE: src/commands/fileCommandRevealInRemoteExplorer.ts
================================================
import { UResource } from '../core';
import app from '../app';
import { COMMAND_REVEAL_IN_REMOTE_EXPLORER } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';

export default checkFileCommand({
  id: COMMAND_REVEAL_IN_REMOTE_EXPLORER,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  async handleFile({ target }) {
    await app.remoteExplorer.reveal({
      resource: UResource.makeResource(target.remoteUri),
      isDirectory: false,
    });
  },
});


================================================
FILE: src/commands/fileCommandSyncBothDirections.ts
================================================
import { COMMAND_SYNC_BOTH_DIRECTIONS } from '../constants';
import { sync2Remote } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { selectFolderFallbackToConfigContext, uriFromfspath, applySelector } from './shared';

export default checkFileCommand({
  id: COMMAND_SYNC_BOTH_DIRECTIONS,
  getFileTarget: applySelector(uriFromfspath, selectFolderFallbackToConfigContext),

  handleFile(ctx) {
    return sync2Remote(ctx, { bothDiretions: true });
  },
});


================================================
FILE: src/commands/fileCommandSyncLocalToRemote.ts
================================================
import { COMMAND_SYNC_LOCAL_TO_REMOTE } from '../constants';
import { sync2Remote } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { selectFolderFallbackToConfigContext, uriFromfspath, applySelector } from './shared';

export default checkFileCommand({
  id: COMMAND_SYNC_LOCAL_TO_REMOTE,
  getFileTarget: applySelector(uriFromfspath, selectFolderFallbackToConfigContext),

  handleFile: sync2Remote,
});


================================================
FILE: src/commands/fileCommandSyncRemoteToLocal.ts
================================================
import { COMMAND_SYNC_REMOTE_TO_LOCAL } from '../constants';
import { sync2Local } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { selectFolderFallbackToConfigContext, uriFromfspath, applySelector } from './shared';

export default checkFileCommand({
  id: COMMAND_SYNC_REMOTE_TO_LOCAL,
  getFileTarget: applySelector(uriFromfspath, selectFolderFallbackToConfigContext),

  handleFile: sync2Local,
});


================================================
FILE: src/commands/fileCommandUpload.ts
================================================
import { COMMAND_UPLOAD } from '../constants';
import { upload } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromfspath } from './shared';

export default checkFileCommand({
  id: COMMAND_UPLOAD,
  getFileTarget: uriFromfspath,

  async handleFile(ctx) {
    await upload(ctx, { ignore: null });
  },
});


================================================
FILE: src/commands/fileCommandUploadActiveFile.ts
================================================
import { COMMAND_UPLOAD_ACTIVEFILE } from '../constants';
import { uploadFile } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { getActiveDocumentUri } from './shared';

export default checkFileCommand({
  id: COMMAND_UPLOAD_ACTIVEFILE,
  getFileTarget: getActiveDocumentUri,

  async handleFile(ctx) {
    await uploadFile(ctx, { ignore: null });
  },
});


================================================
FILE: src/commands/fileCommandUploadActiveFolder.ts
================================================
import { COMMAND_UPLOAD_ACTIVEFOLDER } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import { getActiveFolder } from './shared';
import fileCommandUploadFolder from './fileCommandUploadFolder';

export default checkFileCommand({
  ...fileCommandUploadFolder,
  id: COMMAND_UPLOAD_ACTIVEFOLDER,
  getFileTarget: getActiveFolder,
});


================================================
FILE: src/commands/fileCommandUploadFile.ts
================================================
import { COMMAND_UPLOAD_FILE } from '../constants';
import { uploadFile } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';

export default checkFileCommand({
  id: COMMAND_UPLOAD_FILE,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  async handleFile(ctx) {
    await uploadFile(ctx, { ignore: null });
  },
});


================================================
FILE: src/commands/fileCommandUploadFolder.ts
================================================
import { COMMAND_UPLOAD_FOLDER } from '../constants';
import { uploadFolder } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';

export default checkFileCommand({
  id: COMMAND_UPLOAD_FOLDER,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  handleFile: uploadFolder,
});


================================================
FILE: src/commands/fileCommandUploadForce.ts
================================================
import { COMMAND_FORCE_UPLOAD } from '../constants';
import { upload } from '../fileHandlers';
import { checkFileCommand } from './abstract/createCommand';
import { uriFromExplorerContextOrEditorContext } from './shared';

export default checkFileCommand({
  id: COMMAND_FORCE_UPLOAD,
  getFileTarget: uriFromExplorerContextOrEditorContext,

  async handleFile(ctx) {
    await upload(ctx, { ignore: null });
  },
});


================================================
FILE: src/commands/fileCommandUploadProject.ts
================================================
import { COMMAND_UPLOAD_PROJECT } from '../constants';
import { uploadFolder } from '../fileHandlers';
import { selectContext } from './shared';
import { checkFileCommand } from './abstract/createCommand';

export default checkFileCommand({
  id: COMMAND_UPLOAD_PROJECT,
  getFileTarget: selectContext,

  handleFile: uploadFolder,
});


================================================
FILE: src/commands/fileMultiCommandUploadActiveFileToAllProfiles.ts
================================================
import { COMMAND_UPLOAD_ACTIVEFILE_TO_ALL_PROFILES } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import fileCommandUploadActiveFile from './fileCommandUploadActiveFile';


export default checkFileCommand({
  ...fileCommandUploadActiveFile,
  id: COMMAND_UPLOAD_ACTIVEFILE_TO_ALL_PROFILES
});


================================================
FILE: src/commands/fileMultiCommandUploadActiveFolderToAllProfiles.ts
================================================
import { COMMAND_UPLOAD_ACTIVEFOLDER_TO_ALL_PROFILES } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import fileCommandUploadActiveFolder from './fileCommandUploadActiveFolder';

export default checkFileCommand({
  ...fileCommandUploadActiveFolder,
  id: COMMAND_UPLOAD_ACTIVEFOLDER_TO_ALL_PROFILES,
});


================================================
FILE: src/commands/fileMultiCommandUploadFileToAllProfiles.ts
================================================
import { COMMAND_UPLOAD_FILE_TO_ALL_PROFILES } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import fileCommandUploadFile from './fileCommandUploadFile';

export default checkFileCommand({
  ...fileCommandUploadFile,
  id: COMMAND_UPLOAD_FILE_TO_ALL_PROFILES
});


================================================
FILE: src/commands/fileMultiCommandUploadFolderToAllProfiles.ts
================================================
import { COMMAND_UPLOAD_FOLDER_TO_ALL_PROFILES } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import fileCommandUploadFolder from './fileCommandUploadFolder';

export default checkFileCommand({
  ...fileCommandUploadFolder,
  id: COMMAND_UPLOAD_FOLDER_TO_ALL_PROFILES
});


================================================
FILE: src/commands/fileMultiCommandUploadForceToAllProfiles.ts
================================================
import { COMMAND_FORCE_UPLOAD_TO_ALL_PROFILES } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import fileCommandUploadForce from './fileCommandUploadForce';

export default checkFileCommand({
  ...fileCommandUploadForce,
  id: COMMAND_FORCE_UPLOAD_TO_ALL_PROFILES
});


================================================
FILE: src/commands/fileMultiCommandUploadProjectToAllProfiles.ts
================================================
import { COMMAND_UPLOAD_PROJECT_TO_ALL_PROFILES } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import fileCommandUploadProject from './fileCommandUploadProject';

export default checkFileCommand({
  ...fileCommandUploadProject,
  id: COMMAND_UPLOAD_PROJECT_TO_ALL_PROFILES
});


================================================
FILE: src/commands/fileMultiCommandUploadToAllProfiles.ts
================================================
import { COMMAND_UPLOAD_TO_ALL_PROFILES } from '../constants';
import { checkFileCommand } from './abstract/createCommand';
import fileCommandUpload from './fileCommandUpload';

export default checkFileCommand({
  ...fileCommandUpload,
  id: COMMAND_UPLOAD_TO_ALL_PROFILES
});


================================================
FILE: src/commands/shared.ts
================================================
import * as path from 'path';
import { Uri, window } from 'vscode';
import { FileType } from '../core';
import { getAllFileService } from '../modules/serviceManager';
import { ExplorerItem } from '../modules/remoteExplorer';
import { getActiveTextEditor } from '../host';
import { listFiles, toLocalPath, simplifyPath } from '../helper';

function configIngoreFilterCreator(config) {
  if (!config || !config.ignore) {
    return;
  }

  return file => !config.ignore(file.fsPath);
}

function createFileSelector(filterCreator?) {
  return async (): Promise<Uri | undefined> => {
    const remoteItems = getAllFileService().map((fileService, index) => {
      const config = fileService.getConfig();
      return {
        name: config.name || config.remotePath,
        description: config.host,
        fsPath: config.remotePath,
        type: FileType.Directory,
        filter: filterCreator ? filterCreator(config) : undefined,
        getFs: () => fileService.getRemoteFileSystem(config),
        index,
        remoteBaseDir: config.remotePath,
        baseDir: fileService.baseDir,
      };
    });

    const selected = await listFiles(remoteItems);

    if (!selected) {
      return;
    }

    const rootItem = remoteItems[selected.index];
    const localTarget = toLocalPath(selected.fsPath, rootItem.remoteBaseDir, rootItem.baseDir);

    return Uri.file(localTarget);
  };
}

export function selectContext(): Promise<Uri | undefined> {
  return new Promise((resolve, reject) => {
    const sercives = getAllFileService();
    const projectsList = sercives
      .map(service => ({
        value: service.baseDir,
        label: service.name || simplifyPath(service.baseDir),
        description: '',
        detail: service.baseDir,
      }))
      .sort((l, r) => l.label.localeCompare(r.label));

    // if (projectsList.length === 1) {
    // return resolve(projectsList[0].value);
    // }

    window
      .showQuickPick(projectsList, {
        placeHolder: 'Select a folder...',
      })
      .then(selection => {
        if (selection) {
          return resolve(Uri.file(selection.value));
        }

        // cancel selection
        resolve();
      }, reject);
  });
}

export function applySelector<T>(...selectors: ((...args: any[]) => T | Promise<T>)[]) {
  return function combinedSelector(...args: any[]): T | Promise<T> {
    let result;
    for (const selector of selectors) {
      result = selector.apply(this, args);
      if (result) {
        break;
      }
    }

    return result;
  };
}

export function uriFromfspath(fileList: string[]): Uri[] | undefined {
  if (!Array.isArray(fileList) || typeof fileList[0] !== 'string') {
    return;
  }

  return fileList.map(file => Uri.file(file));
}

export function getActiveDocumentUri() {
  const active = getActiveTextEditor();
  if (!active || !active.document) {
    return;
  }

  return active.document.uri;
}

export function getActiveFolder() {
  const uri = getActiveDocumentUri();
  if (!uri) {
    return;
  }

  return Uri.file(path.dirname(uri.fsPath));
}

// selected file or activeTarget or configContext
export function uriFromExplorerContextOrEditorContext(item, items): undefined | Uri | Uri[] {
  // from explorer or editor context
  if (item instanceof Uri) {
    if (Array.isArray(items) && items[0] instanceof Uri) {
      // multi-select in explorer
      return items;
    } else {
      return item;
    }
  } else if ((item as ExplorerItem).resource) {
    // from remote explorer
    if (Array.isArray(items) && (items[0] as ExplorerItem).resource) {
      // multi-select in remote explorer
      return items.map(_ => _.resource.uri);
    } else {
      return item.resource.uri;
    }
  }

  return;
}

// selected folder or configContext
export function selectFolderFallbackToConfigContext(item, items): Promise<undefined | Uri | Uri[]> {
  // from explorer or editor context
  if (item) {
    if (item instanceof Uri) {
      if (Array.isArray(items) && items[0] instanceof Uri) {
        // multi-select in explorer
        return Promise.resolve(items);
      } else {
        return Promise.resolve(item);
      }
    } else if ((item as ExplorerItem).resource) {
      // from remote explorer
      return Promise.resolve(item.resource.uri);
    }
  }

  return selectContext();
}

// selected file from all remote files
export const selectFileFromAll = createFileSelector();

// selected file from remote files expect ignored
export const selectFile = createFileSelector(configIngoreFilterCreator);


================================================
FILE: src/constants.ts
================================================
import * as path from 'path';

const VENDOR_FOLDER = '.vscode';

export const EXTENSION_NAME = 'sftp';
export const SETTING_KEY_REMOTE = 'remotefs.remote';

export const REMOTE_SCHEME = 'remote';

export const CONGIF_FILENAME = 'sftp.json';
export const CONFIG_PATH = path.join(VENDOR_FOLDER, CONGIF_FILENAME);

// command not in package.json
export const COMMAND_TOGGLE_OUTPUT = 'sftp.toggleOutput';

// commands in package.json
export const COMMAND_CONFIG = 'sftp.config';
export const COMMAND_SET_PROFILE = 'sftp.setProfile';
export const COMMAND_CANCEL_ALL_TRANSFER = 'sftp.cancelAllTransfer';
export const COMMAND_OPEN_CONNECTION_IN_TERMINAL = 'sftp.openConnectInTerminal';

export const COMMAND_FORCE_UPLOAD = 'sftp.forceUpload';
export const COMMAND_UPLOAD = 'sftp.upload';
export const COMMAND_UPLOAD_FILE = 'sftp.upload.file';
export const COMMAND_UPLOAD_CHANGEDFILES = 'sftp.upload.changedFiles';
export const COMMAND_UPLOAD_ACTIVEFILE = 'sftp.upload.activeFile';
export const COMMAND_UPLOAD_FOLDER = 'sftp.upload.folder';
export const COMMAND_UPLOAD_ACTIVEFOLDER = 'sftp.upload.activeFolder';
export const COMMAND_UPLOAD_PROJECT = 'sftp.upload.project';

export const COMMAND_FORCE_UPLOAD_TO_ALL_PROFILES = 'sftp.forceUpload.to.allProfiles';
export const COMMAND_UPLOAD_TO_ALL_PROFILES = 'sftp.upload.to.allProfiles';
export const COMMAND_UPLOAD_FILE_TO_ALL_PROFILES = 'sftp.upload.file.to.allProfiles';
export const COMMAND_UPLOAD_ACTIVEFILE_TO_ALL_PROFILES = 'sftp.upload.activeFile.to.allProfiles';
export const COMMAND_UPLOAD_FOLDER_TO_ALL_PROFILES = 'sftp.upload.folder.to.allProfiles';
export const COMMAND_UPLOAD_ACTIVEFOLDER_TO_ALL_PROFILES = 'sftp.upload.activeFolder.to.allProfiles';
export const COMMAND_UPLOAD_PROJECT_TO_ALL_PROFILES = 'sftp.upload.project.to.allProfiles';

export const COMMAND_FORCE_DOWNLOAD = 'sftp.forceDownload';
export const COMMAND_DOWNLOAD = 'sftp.download';
export const COMMAND_DOWNLOAD_FILE = 'sftp.download.file';
export const COMMAND_DOWNLOAD_ACTIVEFILE = 'sftp.download.activeFile';
export const COMMAND_DOWNLOAD_FOLDER = 'sftp.download.folder';
export const COMMAND_DOWNLOAD_ACTIVEFOLDER = 'sftp.download.activeFolder';
export const COMMAND_DOWNLOAD_PROJECT = 'sftp.download.project';

export const COMMAND_SYNC_LOCAL_TO_REMOTE = 'sftp.sync.localToRemote';
export const COMMAND_SYNC_REMOTE_TO_LOCAL = 'sftp.sync.remoteToLocal';
export const COMMAND_SYNC_BOTH_DIRECTIONS = 'sftp.sync.bothDirections';

export const COMMAND_DIFF = 'sftp.diff';
export const COMMAND_DIFF_ACTIVEFILE = 'sftp.diff.activeFile';
export const COMMAND_LIST = 'sftp.list';
export const COMMAND_LIST_ACTIVEFOLDER = 'sftp.listActiveFolder';
export const COMMAND_LIST_ALL = 'sftp.listAll';
export const COMMAND_DELETE_REMOTE = 'sftp.delete.remote';
export const COMMAND_REVEAL_IN_EXPLORER = 'sftp.revealInExplorer';
export const COMMAND_REVEAL_IN_REMOTE_EXPLORER = 'sftp.revealInRemoteExplorer';

export const COMMAND_REMOTEEXPLORER_REFRESH = 'sftp.remoteExplorer.refresh';
export const COMMAND_REMOTEEXPLORER_REFRESH_ACTIVE_FILE = "sftp.remoteExplorer.refreshActiveFile"
export const COMMAND_REMOTEEXPLORER_EDITINLOCAL = 'sftp.remoteExplorer.editInLocal';
export const COMMAND_REMOTEEXPLORER_VIEW_CONTENT = 'sftp.viewContent';

export const COMMAND_CREATE_FOLDER = 'sftp.create.folder';
export const COMMAND_CREATE_FILE = 'sftp.create.file';


================================================
FILE: src/core/customError.ts
================================================
class CustomError extends Error {
  code: string;

  constructor(code, message) {
    // Pass remaining arguments (including vendor specific ones) to parent constructor
    super(message);

    // Maintains proper stack trace for where our error was thrown (only available on V8)
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, CustomError);
    }

    // Custom error properties
    this.code = code;
  }
}

export default CustomError;


================================================
FILE: src/core/fileBaseOperations.ts
================================================
import { FileSystem } from './fs';
import { window } from 'vscode';
import { Readable } from 'stream';
import logger from '../logger';

interface FileOption {
  mode?: number;
}

export async function transferFile(
  src: string,
  des: string,
  srcFs: FileSystem,
  desFs: FileSystem,
  option?: FileOption
): Promise<void> {
  const inputStream = await srcFs.get(src, option);
  await desFs.put(inputStream, des, option);
}

export function transferSymlink(
  src: string,
  des: string,
  srcFs: FileSystem,
  desFs: FileSystem,
  option: FileOption
): Promise<void> {
  return srcFs.readlink(src).then(targetPath => {
    return desFs.symlink(targetPath, des).catch(err => {
      // ignore file already exist
      if (err.code === 4 || err.code === 'EEXIST') {
        return;
      }
      throw err;
    });
  });
}

export function removeFile(path: string, fs: FileSystem, option): Promise<void> {
  return fs.unlink(path);
}

export function removeDir(path: string, fs: FileSystem, option): Promise<void> {
  return fs.rmdir(path, true);
}

export function rename(srcPath: string, destPath: string, fs: FileSystem): Promise<void> {
  return fs.rename(srcPath, destPath);
}

export function createDir(path: string, fs: FileSystem, option): Promise<void> {
  return fs.mkdir(path);
}

export async function createFile(path: string, fs: FileSystem, option): Promise<void> {
  try {
    await fs.lstat(path);
    logger.warn(`Can't create file becase file already exist`);
    window.showErrorMessage(`Can't create file becase file already exist`);
    return;
  } catch (error) {

  }

  const targetFd = await fs.open(path, 'w');
  const s = new Readable();
  s._read = () => { };
  s.push(null);
  return fs.put(s, path, { fd: targetFd });
}


================================================
FILE: src/core/fileService.ts
================================================
import { EventEmitter } from 'events';
import * as fs from 'fs';
import * as path from 'path';
import * as sshConfig from 'ssh-config';
import app from '../app';
import logger from '../logger';
import { getUserSetting } from '../host';
import { replaceHomePath, resolvePath } from '../helper';
import { SETTING_KEY_REMOTE } from '../constants';
import upath from './upath';
import Ignore from './ignore';
import { FileSystem } from './fs';
import Scheduler from './scheduler';
import { createRemoteIfNoneExist, removeRemoteFs } from './remoteFs';
import TransferTask from './transferTask';
import localFs from './localFs';

type Omit<T, U> = Pick<T, Exclude<keyof T, U>>;

interface Root {
  name: string;
  context: string;
  watcher: WatcherConfig;
  defaultProfile: string;
}

interface Host {
  host: string;
  port: number;
  username: string;
  password: string;
  remotePath: string;
  connectTimeout: number;
}

interface ServiceOption {
  protocol: string;
  remote?: string;
  uploadOnSave: boolean;
  useTempFile: boolean;
  openSsh: boolean;
  downloadOnOpen: boolean | 'confirm';
  filePerm?: number;
  dirPerm?: number;
  syncOption: {
    delete: boolean;
    skipCreate: boolean;
    ignoreExisting: boolean;
    update: boolean;
  };
  ignore: string[];
  ignoreFile: string;
  remoteExplorer: {
    filesExclude?: string[];
    order: number;
  };
  remoteTimeOffsetInHours: number;
  limitOpenFilesOnRemote: number | true;
}

interface WatcherConfig {
  files: false | string;
  autoUpload: boolean;
  autoDelete: boolean;
}

interface SftpOption {
  // sftp
  agent?: string;
  privateKeyPath?: string;
  passphrase: string | true;
  interactiveAuth: boolean | string[];
  algorithms: any;
  sshConfigPath?: string;
  concurrency: number;
  sshCustomParams?: string;
  hop: (Host & SftpOption)[] | (Host & SftpOption);
}

interface FtpOption {
  secure: boolean | 'control' | 'implicit';
  secureOptions: any;
}

export interface FileServiceConfig
  extends Root,
    Host,
    ServiceOption,
    SftpOption,
    FtpOption {
  profiles?: {
    [x: string]: FileServiceConfig;
  };
}

export interface ServiceConfig
  extends Root,
    Host,
    Omit<ServiceOption, 'ignore'>,
    SftpOption,
    FtpOption {
  ignore?: ((fsPath: string) => boolean) | null;
}

export interface WatcherService {
  create(watcherBase: string, watcherConfig: WatcherConfig): any;
  dispose(watcherBase: string): void;
}

interface TransferScheduler {
  // readonly _scheduler: Scheduler;
  size: number;
  add(x: TransferTask): void;
  run(): Promise<void>;
  stop(): void;
}

type ConfigValidator = (x: any) => { message: string };

const DEFAULT_SSHCONFIG_FILE = '~/.ssh/config';

function filesIgnoredFromConfig(config: FileServiceConfig): string[] {
  const cache = app.fsCache;
  const ignore: string[] =
    config.ignore && config.ignore.length ? config.ignore : [];

  const ignoreFile = config.ignoreFile;
  if (!ignoreFile) {
    return ignore;
  }

  let ignoreFromFile;
  if (cache.has(ignoreFile)) {
    ignoreFromFile = cache.get(ignoreFile);
  } else if (fs.existsSync(ignoreFile)) {
    ignoreFromFile = fs.readFileSync(ignoreFile).toString();
    cache.set(ignoreFile, ignoreFromFile);
  } else {
    throw new Error(
      `File ${ignoreFile} not found. Check your config of "ignoreFile"`
    );
  }

  return ignore.concat(ignoreFromFile.split(/\r?\n/g));
}

function getHostInfo(config) {
  const ignoreOptions = [
    'name',
    'remotePath',
    'uploadOnSave',
    'useTempFile',
    'openSsh',
    'downloadOnOpen',
    'ignore',
    'ignoreFile',
    'watcher',
    'concurrency',
    'syncOption',
    'sshConfigPath',
  ];

  return Object.keys(config).reduce((obj, key) => {
    if (ignoreOptions.indexOf(key) === -1) {
      obj[key] = config[key];
    }
    return obj;
  }, {});
}

function chooseDefaultPort(protocol) {
  return protocol === 'ftp' ? 21 : 22;
}

function setConfigValue(config, key, value) {
  if (config[key] === undefined) {
    if (key === 'port') {
      config[key] = parseInt(value, 10);
    } else {
      config[key] = value;
    }
  }
}

function mergeConfigWithExternalRefer(
  config: FileServiceConfig
): FileServiceConfig {
  const copyed = Object.assign({}, config);

  if (config.remote) {
    const remoteMap = getUserSetting(SETTING_KEY_REMOTE);
    const remote = remoteMap.get<Record<string, any>>(config.remote);
    if (!remote) {
      throw new Error(`Can\'t not find remote "${config.remote}"`);
    }
    const remoteKeyMapping = new Map([['scheme', 'protocol']]);

    const remoteKeyIgnored = new Map([['rootPath', 1]]);

    Object.keys(remote).forEach(key => {
      if (remoteKeyIgnored.has(key)) {
        return;
      }

      const targetKey = remoteKeyMapping.has(key)
        ? remoteKeyMapping.get(key)
        : key;
      setConfigValue(copyed, targetKey, remote[key]);
    });
  }

  if (config.protocol !== 'sftp') {
    return copyed;
  }

  const sshConfigPath = replaceHomePath(
    config.sshConfigPath || DEFAULT_SSHCONFIG_FILE
  );

  const cache = app.fsCache;
  let sshConfigContent;
  if (cache.has(sshConfigPath)) {
    sshConfigContent = cache.get(sshConfigPath);
  } else {
    try {
      sshConfigContent = fs.readFileSync(sshConfigPath, 'utf8');
    } catch (error) {
      logger.warn(error.message, `load ${sshConfigPath} failed`);
      sshConfigContent = '';
    }
    cache.set(sshConfigPath, sshConfigContent);
  }

  if (!sshConfigContent) {
    return copyed;
  }

  const parsedSSHConfig = sshConfig.parse(sshConfigContent);
  const section = parsedSSHConfig.find({
    Host: copyed.host,
  });

  if (section === null) {
    return copyed;
  }

  const mapping = new Map([
    ['hostname', 'host'],
    ['port', 'port'],
    ['user', 'username'],
    ['identityfile', 'privateKeyPath'],
    ['serveraliveinterval', 'keepalive'],
    ['connecttimeout', 'connTimeout'],
  ]);

  section.config.forEach(line => {
    if (!line.param) {
      return;
    }

    const key = mapping.get(line.param.toLowerCase());

    if (key !== undefined) {
      if (key === 'host') {
        copyed[key] = line.value;
      } else {
        setConfigValue(copyed, key, line.value);
      }
    }
  });

  // Bug introduced in pull request #69 : Fix ssh config resolution
  /* const parsedSSHConfig = sshConfig.parse(sshConfigContent);
  const computed = parsedSSHConfig.compute(copyed.host);

  const mapping = new Map([
    ['hostname', 'host'],
    ['port', 'port'],
    ['user', 'username'],
    ['serveraliveinterval', 'keepalive'],
    ['connecttimeout', 'connTimeout'],
  ]);

  Object.entries<any>(computed).forEach(([param, value]) => {
    if (param.toLowerCase() === 'identityfile') {
      setConfigValue(copyed, 'privateKeyPath', value[0]);
      return;
    }

    const key = mapping.get(param.toLowerCase());

    if (key !== undefined) {
      // don't need consider config priority, always set to the resolve host.
      if (key === 'host') {
        copyed[key] = value;
      } else {
        setConfigValue(copyed, key, value);
      }
    }
  }); */

  return copyed;
}

function getCompleteConfig(
  config: FileServiceConfig,
  workspace: string
): FileServiceConfig {
  const mergedConfig = mergeConfigWithExternalRefer(config);

  if (mergedConfig.agent && mergedConfig.privateKeyPath) {
    logger.warn(
      'Config Option Conflicted. You are specifing "agent" and "privateKey" at the same time, ' +
        'the later will be ignored.'
    );
  }

  // remove the './' part from a relative path
  mergedConfig.remotePath = upath.normalize(mergedConfig.remotePath);
  if (mergedConfig.privateKeyPath) {
    mergedConfig.privateKeyPath = resolvePath(
      workspace,
      mergedConfig.privateKeyPath
    );
  }

  if (mergedConfig.ignoreFile) {
    mergedConfig.ignoreFile = resolvePath(workspace, mergedConfig.ignoreFile);
  }

  // convert ingore config to ignore function
  if (mergedConfig.agent && mergedConfig.agent.startsWith('$')) {
    const evnVarName = mergedConfig.agent.slice(1);
    const val = process.env[evnVarName];
    if (!val) {
      throw new Error(`Environment variable "${evnVarName}" not found`);
    }
    mergedConfig.agent = val;
  }

  return mergedConfig;
}

function mergeProfile(
  target: FileServiceConfig,
  source: FileServiceConfig
): FileServiceConfig {
  const res = Object.assign({}, target);
  delete res.profiles;

  const keys = Object.keys(source);
  for (const key of keys) {
    if (key === 'ignore') {
      res.ignore = res.ignore.concat(source.ignore);
    } else {
      res[key] = source[key];
    }
  }

  return 
gitextract_fvgxrgqp/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── feature_request.md
│   │   └── pull_request.md
│   ├── config.yml
│   ├── dependabot.yml
│   ├── holopin.yml
│   └── workflows/
│       ├── codeql-analysis.yml
│       └── devskim-analysis.yml
├── .gitignore
├── .vscode/
│   ├── launch.json
│   └── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── FAQ.md
├── LICENSE
├── README.md
├── __mocks__/
│   ├── fs.js
│   └── vscode.js
├── docs/
│   ├── commands.md
│   ├── common_configuration.md
│   ├── configuration.md
│   ├── ftp_configuration.md
│   ├── home.md
│   ├── setting.md
│   └── sftp_configuration.md
├── package.json
├── schema/
│   ├── config.schema.json
│   ├── definitions.json
│   ├── ftp.schema.json
│   ├── sftp.schema.json
│   └── simple-config.schema.json
├── src/
│   ├── app.ts
│   ├── commands/
│   │   ├── abstract/
│   │   │   ├── command.ts
│   │   │   └── createCommand.ts
│   │   ├── commandCancelAllTransfer.ts
│   │   ├── commandConfig.ts
│   │   ├── commandListActiveFolder.ts
│   │   ├── commandOpenSshConnection.ts
│   │   ├── commandSetProfile.ts
│   │   ├── commandToggleOutputPanel.ts
│   │   ├── commandUploadChangedFiles.ts
│   │   ├── fileCommandCreateFile.ts
│   │   ├── fileCommandCreateFolder.ts
│   │   ├── fileCommandDeleteRemote.ts
│   │   ├── fileCommandDiff.ts
│   │   ├── fileCommandDiffActiveFile.ts
│   │   ├── fileCommandDownload.ts
│   │   ├── fileCommandDownloadActiveFile.ts
│   │   ├── fileCommandDownloadActiveFolder.ts
│   │   ├── fileCommandDownloadFile.ts
│   │   ├── fileCommandDownloadFolder.ts
│   │   ├── fileCommandDownloadForce.ts
│   │   ├── fileCommandDownloadProject.ts
│   │   ├── fileCommandEditInLocal.ts
│   │   ├── fileCommandList.ts
│   │   ├── fileCommandListAll.ts
│   │   ├── fileCommandRevealInExplorer.ts
│   │   ├── fileCommandRevealInRemoteExplorer.ts
│   │   ├── fileCommandSyncBothDirections.ts
│   │   ├── fileCommandSyncLocalToRemote.ts
│   │   ├── fileCommandSyncRemoteToLocal.ts
│   │   ├── fileCommandUpload.ts
│   │   ├── fileCommandUploadActiveFile.ts
│   │   ├── fileCommandUploadActiveFolder.ts
│   │   ├── fileCommandUploadFile.ts
│   │   ├── fileCommandUploadFolder.ts
│   │   ├── fileCommandUploadForce.ts
│   │   ├── fileCommandUploadProject.ts
│   │   ├── fileMultiCommandUploadActiveFileToAllProfiles.ts
│   │   ├── fileMultiCommandUploadActiveFolderToAllProfiles.ts
│   │   ├── fileMultiCommandUploadFileToAllProfiles.ts
│   │   ├── fileMultiCommandUploadFolderToAllProfiles.ts
│   │   ├── fileMultiCommandUploadForceToAllProfiles.ts
│   │   ├── fileMultiCommandUploadProjectToAllProfiles.ts
│   │   ├── fileMultiCommandUploadToAllProfiles.ts
│   │   └── shared.ts
│   ├── constants.ts
│   ├── core/
│   │   ├── customError.ts
│   │   ├── fileBaseOperations.ts
│   │   ├── fileService.ts
│   │   ├── fs/
│   │   │   ├── fileSystem.ts
│   │   │   ├── ftpFileSystem.ts
│   │   │   ├── index.ts
│   │   │   ├── localFileSystem.ts
│   │   │   ├── remoteFileSystem.ts
│   │   │   └── sftpFileSystem.ts
│   │   ├── ignore.ts
│   │   ├── index.ts
│   │   ├── localFs.ts
│   │   ├── remote-client/
│   │   │   ├── ftpClient.ts
│   │   │   ├── index.ts
│   │   │   ├── remoteClient.ts
│   │   │   └── sshClient.ts
│   │   ├── remoteFs.ts
│   │   ├── scheduler.ts
│   │   ├── transferTask.ts
│   │   ├── uResource.ts
│   │   └── upath.ts
│   ├── extension.ts
│   ├── fileHandlers/
│   │   ├── create.ts
│   │   ├── createFileHandler.ts
│   │   ├── diff.ts
│   │   ├── index.ts
│   │   ├── option.ts
│   │   ├── remove.ts
│   │   ├── rename.ts
│   │   ├── shared.ts
│   │   └── transfer/
│   │       ├── __tests__/
│   │       │   └── transfer-test.ts
│   │       ├── index.ts
│   │       └── transfer.ts
│   ├── helper/
│   │   ├── error.ts
│   │   ├── file.ts
│   │   ├── index.ts
│   │   ├── paths.ts
│   │   └── select.ts
│   ├── host.ts
│   ├── initCommands.ts
│   ├── logger.ts
│   ├── modules/
│   │   ├── appState.ts
│   │   ├── config.ts
│   │   ├── ext.ts
│   │   ├── fileActivityMonitor.ts
│   │   ├── fileWatcher.ts
│   │   ├── git/
│   │   │   ├── git.d.ts
│   │   │   └── index.ts
│   │   ├── remoteExplorer/
│   │   │   ├── explorer.ts
│   │   │   ├── index.ts
│   │   │   └── treeDataProvider.ts
│   │   └── serviceManager/
│   │       ├── index.ts
│   │       └── trie.ts
│   ├── ui/
│   │   ├── output.ts
│   │   └── statusBarItem.ts
│   └── utils.ts
├── test/
│   ├── config.spec.js
│   ├── core/
│   │   └── scheduler.spec.js
│   ├── helper/
│   │   └── localRemoteFs.ts
│   ├── index.ts
│   ├── preprocessor.js
│   └── trie.spec.js
├── tsconfig.json
├── tslint.json
└── webpack.config.js
Condensed preview — 144 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (414K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 740,
    "preview": "# These are supported funding model platforms\n\ngithub: Natizyskunk\npatreon: # Replace with a single Patreon username\nope..."
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1027,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Do you read th..."
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 666,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is this a s..."
  },
  {
    "path": ".github/ISSUE_TEMPLATE/pull_request.md",
    "chars": 284,
    "preview": "---\nname: Pull request\nabout: Create a Pull Request on this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Descripti..."
  },
  {
    "path": ".github/config.yml",
    "chars": 1291,
    "preview": "# Configuration for request-info - https://github.com/behaviorbot/request-info\n\n# *OPTIONAL* Comment to reply with\n# Can..."
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 178,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"npm\" # See documentation for possible values\n    directory: \"/\" # Location o..."
  },
  {
    "path": ".github/holopin.yml",
    "chars": 376,
    "preview": "organization: Natan FOURIÉ EIRL\ndefaultSticker: cl8p9eem9629909kycm8gkafb\nstickers:\n  -\n    id: cl8p9eem9629909kycm8gkaf..."
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2461,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y..."
  },
  {
    "path": ".github/workflows/devskim-analysis.yml",
    "chars": 804,
    "preview": "# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by..."
  },
  {
    "path": ".gitignore",
    "chars": 404,
    "preview": "### Default ###\n.DS_Store\nout\ndist\ntest/test.js\n_debug\nTODO.md\nINSTALL_DEBUG.md\n\n### Extensions ###\nsftp-*.vsix\n\n### VS..."
  },
  {
    "path": ".vscode/launch.json",
    "chars": 1021,
    "preview": "// A launch configuration that compiles the extension and then opens it inside a new window\n{\n    \"version\": \"0.1.0\",..."
  },
  {
    "path": ".vscode/tasks.json",
    "chars": 1403,
    "preview": "// Available variables which can be used inside of strings.\n// ${workspaceRoot}: the root folder of the team\n// ${file}:..."
  },
  {
    "path": ".vscodeignore",
    "chars": 134,
    "preview": "*\n*/**\n**/.DS_Store\n!node_modules/**/*\n!dist/extension.js\n!package.json\n!README.md\n!CHANGELOG.md\n!LICENSE\n!resources/**/..."
  },
  {
    "path": "CHANGELOG.md",
    "chars": 27942,
    "preview": "## 1.16.3 - 2023-06-16\r\n* [#356] New Feature : Upload to all profiles (Pull request [#313](https://github.com/Natizyskun..."
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3177,
    "preview": "# Contributing to vscode-sftp\n\nAfter you've created a branch on your fork with your changes, [open a pull request][pr-li..."
  },
  {
    "path": "FAQ.md",
    "chars": 6062,
    "preview": "- [Error: Failure](#error-failure)\r\n\t- [Error: Failure - Solution One](#error-failure---solution-one)\r\n\t- [Error: Failur..."
  },
  {
    "path": "LICENSE",
    "chars": 1163,
    "preview": "MIT License\n\nCopyright (c) 2018-present, Natizyskunk(Natan FOURIÉ)\n\nPermission is hereby granted, free of charge, to any..."
  },
  {
    "path": "README.md",
    "chars": 11800,
    "preview": "# sftp sync extension for VS Code\n\nNew maintained and updated version by [@Natizyskunk](https://github.com/Natizyskunk/)..."
  },
  {
    "path": "__mocks__/fs.js",
    "chars": 75,
    "preview": "const { fs } = require('memfs');\n\nfs.__mock__ = true;\nmodule.exports = fs;\n"
  },
  {
    "path": "__mocks__/vscode.js",
    "chars": 268,
    "preview": "\nconst Nothing = (() => {\n\tconst fn = () => Nothing\n\tfn.toString = fn.toLocaleString = fn[Symbol.toPrimitive] = () => ''..."
  },
  {
    "path": "docs/commands.md",
    "chars": 2261,
    "preview": "## Common commands\r\n\r\n### SFTP: Config\r\nCreate a new configuration file for a project.\r\n\r\n### SFTP: Set Profile\r\nSet the..."
  },
  {
    "path": "docs/common_configuration.md",
    "chars": 6794,
    "preview": "## Common configuration\n\n### name\nA string to identify your configuration.\n\n| Key | Value |\n| --- | --- |\n| *name* | *st..."
  },
  {
    "path": "docs/configuration.md",
    "chars": 11791,
    "preview": "# VSCode-SFTP\n\nConfigurations are stored in your project working directory under `../.vscode/sftp.json`. <br>\nThe config..."
  },
  {
    "path": "docs/ftp_configuration.md",
    "chars": 722,
    "preview": "## FTP(s) configuration\n\n### secure\nSet to true for both control and data connection encryption. <br>\nSet to `control` f..."
  },
  {
    "path": "docs/home.md",
    "chars": 237,
    "preview": "# Home\n\n1. [Setting](./setting.md)\n2. [Config](./configuration.md)\n    - [Common](./common_configuration.md)\n    - [SFTP..."
  },
  {
    "path": "docs/setting.md",
    "chars": 743,
    "preview": "## Setting\n\nThere are a handful of settings available for SFTP, and they can be changed:\n\n- On Windows/Linux: File --> P..."
  },
  {
    "path": "docs/sftp_configuration.md",
    "chars": 2636,
    "preview": "## SFTP configuration\n\n### agent\nPath to ssh-agent's UNIX socket for ssh-agent-based user authentication. <br>\nWindows u..."
  },
  {
    "path": "package.json",
    "chars": 22063,
    "preview": "{\r\n  \"name\": \"sftp\",\r\n  \"displayName\": \"SFTP\",\r\n  \"description\": \"SFTP/FTP sync\",\r\n  \"version\": \"1.16.3\",\r\n  \"publisher\"..."
  },
  {
    "path": "schema/config.schema.json",
    "chars": 180,
    "preview": "{\n  \"oneOf\": [\n    {\n      \"type\": \"array\",\n      \"items\": {\n        \"$ref\": \"simple-config.schema.json\"\n      }\n    },..."
  },
  {
    "path": "schema/definitions.json",
    "chars": 18461,
    "preview": "{\n  \"rootOption\": {\n    \"properties\": {\n      \"name\": {\n        \"type\": \"string\",\n        \"description\": \"A string to id..."
  },
  {
    "path": "schema/ftp.schema.json",
    "chars": 901,
    "preview": "{\n  \"allOf\": [\n    {\n      \"$ref\": \"definitions.json#/rootOption\"\n    },\n    {\n      \"$ref\": \"definitions.json#/option\"..."
  },
  {
    "path": "schema/sftp.schema.json",
    "chars": 1596,
    "preview": "{\n  \"allOf\": [\n    {\n      \"$ref\": \"definitions.json#/rootOption\"\n    },\n    {\n      \"$ref\": \"definitions.json#/option\"..."
  },
  {
    "path": "schema/simple-config.schema.json",
    "chars": 79,
    "preview": "{\n  \"oneOf\": [{ \"$ref\": \"sftp.schema.json\" }, { \"$ref\": \"ftp.schema.json\" }]\n}\n"
  },
  {
    "path": "src/app.ts",
    "chars": 722,
    "preview": "import * as LRU from 'lru-cache';\nimport StatusBarItem from './ui/statusBarItem';\nimport { COMMAND_TOGGLE_OUTPUT } from..."
  },
  {
    "path": "src/commands/abstract/command.ts",
    "chars": 1038,
    "preview": "import { reportError } from '../../helper';\nimport logger from '../../logger';\n\nexport interface ITarget {\n  fsPath: str..."
  },
  {
    "path": "src/commands/abstract/createCommand.ts",
    "chars": 3454,
    "preview": "import { Uri, window } from 'vscode';\nimport logger from '../../logger';\nimport { reportError } from '../../helper';\nimp..."
  },
  {
    "path": "src/commands/commandCancelAllTransfer.ts",
    "chars": 370,
    "preview": "import { COMMAND_CANCEL_ALL_TRANSFER } from '../constants';\nimport { checkCommand } from './abstract/createCommand';\nimp..."
  },
  {
    "path": "src/commands/commandConfig.ts",
    "chars": 1737,
    "preview": "import * as vscode from 'vscode';\nimport { COMMAND_CONFIG } from '../constants';\nimport { newConfig } from '../modules/c..."
  },
  {
    "path": "src/commands/commandListActiveFolder.ts",
    "chars": 1660,
    "preview": "import * as path from 'path';\nimport { Uri } from 'vscode';\nimport { COMMAND_LIST_ACTIVEFOLDER } from '../constants';\nim..."
  },
  {
    "path": "src/commands/commandOpenSshConnection.ts",
    "chars": 2974,
    "preview": "import * as vscode from 'vscode';\nimport { COMMAND_OPEN_CONNECTION_IN_TERMINAL } from '../constants';\nimport { getAllFil..."
  },
  {
    "path": "src/commands/commandSetProfile.ts",
    "chars": 1611,
    "preview": "import * as vscode from 'vscode';\nimport { COMMAND_SET_PROFILE } from '../constants';\nimport { showInformationMessage }..."
  },
  {
    "path": "src/commands/commandToggleOutputPanel.ts",
    "chars": 262,
    "preview": "import { COMMAND_TOGGLE_OUTPUT } from '../constants';\nimport * as output from '../ui/output';\nimport { checkCommand } fr..."
  },
  {
    "path": "src/commands/commandUploadChangedFiles.ts",
    "chars": 4762,
    "preview": "import * as vscode from 'vscode';\r\nimport * as path from 'path';\r\nimport { COMMAND_UPLOAD_CHANGEDFILES } from '../consta..."
  },
  {
    "path": "src/commands/fileCommandCreateFile.ts",
    "chars": 1387,
    "preview": "import { COMMAND_CREATE_FILE } from '../constants';\nimport { createRemoteFile } from '../fileHandlers';\n// import { show..."
  },
  {
    "path": "src/commands/fileCommandCreateFolder.ts",
    "chars": 1396,
    "preview": "import { COMMAND_CREATE_FOLDER } from '../constants';\nimport { createRemoteFolder } from '../fileHandlers';\n// import {..."
  },
  {
    "path": "src/commands/fileCommandDeleteRemote.ts",
    "chars": 893,
    "preview": "import { COMMAND_DELETE_REMOTE } from '../constants';\nimport { upath } from '../core';\nimport { removeRemote } from '../..."
  },
  {
    "path": "src/commands/fileCommandDiff.ts",
    "chars": 347,
    "preview": "import { COMMAND_DIFF } from '../constants';\nimport { diff } from '../fileHandlers';\nimport { checkFileCommand } from '...."
  },
  {
    "path": "src/commands/fileCommandDiffActiveFile.ts",
    "chars": 335,
    "preview": "import { COMMAND_DIFF_ACTIVEFILE } from '../constants';\nimport { diff } from '../fileHandlers';\nimport { checkFileComman..."
  },
  {
    "path": "src/commands/fileCommandDownload.ts",
    "chars": 366,
    "preview": "import { COMMAND_DOWNLOAD } from '../constants';\nimport { download } from '../fileHandlers';\nimport { uriFromfspath } fr..."
  },
  {
    "path": "src/commands/fileCommandDownloadActiveFile.ts",
    "chars": 410,
    "preview": "import { COMMAND_DOWNLOAD_ACTIVEFILE } from '../constants';\nimport { downloadFile } from '../fileHandlers';\nimport { che..."
  },
  {
    "path": "src/commands/fileCommandDownloadActiveFolder.ts",
    "chars": 378,
    "preview": "import { COMMAND_DOWNLOAD_ACTIVEFOLDER } from '../constants';\nimport { checkFileCommand } from './abstract/createCommand..."
  },
  {
    "path": "src/commands/fileCommandDownloadFile.ts",
    "chars": 432,
    "preview": "import { COMMAND_DOWNLOAD_FILE } from '../constants';\nimport { downloadFile } from '../fileHandlers';\nimport { uriFromEx..."
  },
  {
    "path": "src/commands/fileCommandDownloadFolder.ts",
    "chars": 390,
    "preview": "import { COMMAND_DOWNLOAD_FOLDER } from '../constants';\nimport { downloadFolder } from '../fileHandlers';\nimport { check..."
  },
  {
    "path": "src/commands/fileCommandDownloadForce.ts",
    "chars": 426,
    "preview": "import { COMMAND_FORCE_DOWNLOAD } from '../constants';\nimport { download } from '../fileHandlers';\nimport { uriFromExplo..."
  },
  {
    "path": "src/commands/fileCommandDownloadProject.ts",
    "chars": 376,
    "preview": "import { COMMAND_DOWNLOAD_PROJECT } from '../constants';\nimport { downloadFolder } from '../fileHandlers';\nimport { sele..."
  },
  {
    "path": "src/commands/fileCommandEditInLocal.ts",
    "chars": 570,
    "preview": "import { COMMAND_REMOTEEXPLORER_EDITINLOCAL } from '../constants';\nimport { downloadFile } from '../fileHandlers';\nimpor..."
  },
  {
    "path": "src/commands/fileCommandList.ts",
    "chars": 801,
    "preview": "import { COMMAND_LIST } from '../constants';\nimport { showTextDocument } from '../host';\nimport { FileType } from '../co..."
  },
  {
    "path": "src/commands/fileCommandListAll.ts",
    "chars": 859,
    "preview": "import { COMMAND_LIST_ALL } from '../constants';\nimport { showTextDocument } from '../host';\nimport { FileType } from '...."
  },
  {
    "path": "src/commands/fileCommandRevealInExplorer.ts",
    "chars": 459,
    "preview": "import { COMMAND_REVEAL_IN_EXPLORER } from '../constants';\nimport { executeCommand } from '../host';\nimport { checkFileC..."
  },
  {
    "path": "src/commands/fileCommandRevealInRemoteExplorer.ts",
    "chars": 561,
    "preview": "import { UResource } from '../core';\nimport app from '../app';\nimport { COMMAND_REVEAL_IN_REMOTE_EXPLORER } from '../con..."
  },
  {
    "path": "src/commands/fileCommandSyncBothDirections.ts",
    "chars": 502,
    "preview": "import { COMMAND_SYNC_BOTH_DIRECTIONS } from '../constants';\nimport { sync2Remote } from '../fileHandlers';\nimport { che..."
  },
  {
    "path": "src/commands/fileCommandSyncLocalToRemote.ts",
    "chars": 450,
    "preview": "import { COMMAND_SYNC_LOCAL_TO_REMOTE } from '../constants';\nimport { sync2Remote } from '../fileHandlers';\nimport { che..."
  },
  {
    "path": "src/commands/fileCommandSyncRemoteToLocal.ts",
    "chars": 448,
    "preview": "import { COMMAND_SYNC_REMOTE_TO_LOCAL } from '../constants';\nimport { sync2Local } from '../fileHandlers';\nimport { chec..."
  },
  {
    "path": "src/commands/fileCommandUpload.ts",
    "chars": 358,
    "preview": "import { COMMAND_UPLOAD } from '../constants';\nimport { upload } from '../fileHandlers';\nimport { checkFileCommand } fro..."
  },
  {
    "path": "src/commands/fileCommandUploadActiveFile.ts",
    "chars": 402,
    "preview": "import { COMMAND_UPLOAD_ACTIVEFILE } from '../constants';\nimport { uploadFile } from '../fileHandlers';\nimport { checkFi..."
  },
  {
    "path": "src/commands/fileCommandUploadActiveFolder.ts",
    "chars": 368,
    "preview": "import { COMMAND_UPLOAD_ACTIVEFOLDER } from '../constants';\nimport { checkFileCommand } from './abstract/createCommand';..."
  },
  {
    "path": "src/commands/fileCommandUploadFile.ts",
    "chars": 424,
    "preview": "import { COMMAND_UPLOAD_FILE } from '../constants';\nimport { uploadFile } from '../fileHandlers';\nimport { checkFileComm..."
  },
  {
    "path": "src/commands/fileCommandUploadFolder.ts",
    "chars": 382,
    "preview": "import { COMMAND_UPLOAD_FOLDER } from '../constants';\nimport { uploadFolder } from '../fileHandlers';\nimport { checkFile..."
  },
  {
    "path": "src/commands/fileCommandUploadForce.ts",
    "chars": 418,
    "preview": "import { COMMAND_FORCE_UPLOAD } from '../constants';\nimport { upload } from '../fileHandlers';\nimport { checkFileCommand..."
  },
  {
    "path": "src/commands/fileCommandUploadProject.ts",
    "chars": 336,
    "preview": "import { COMMAND_UPLOAD_PROJECT } from '../constants';\nimport { uploadFolder } from '../fileHandlers';\nimport { selectCo..."
  },
  {
    "path": "src/commands/fileMultiCommandUploadActiveFileToAllProfiles.ts",
    "chars": 330,
    "preview": "import { COMMAND_UPLOAD_ACTIVEFILE_TO_ALL_PROFILES } from '../constants';\nimport { checkFileCommand } from './abstract/c..."
  },
  {
    "path": "src/commands/fileMultiCommandUploadActiveFolderToAllProfiles.ts",
    "chars": 340,
    "preview": "import { COMMAND_UPLOAD_ACTIVEFOLDER_TO_ALL_PROFILES } from '../constants';\nimport { checkFileCommand } from './abstract..."
  },
  {
    "path": "src/commands/fileMultiCommandUploadFileToAllProfiles.ts",
    "chars": 299,
    "preview": "import { COMMAND_UPLOAD_FILE_TO_ALL_PROFILES } from '../constants';\nimport { checkFileCommand } from './abstract/createC..."
  },
  {
    "path": "src/commands/fileMultiCommandUploadFolderToAllProfiles.ts",
    "chars": 309,
    "preview": "import { COMMAND_UPLOAD_FOLDER_TO_ALL_PROFILES } from '../constants';\nimport { checkFileCommand } from './abstract/creat..."
  },
  {
    "path": "src/commands/fileMultiCommandUploadForceToAllProfiles.ts",
    "chars": 304,
    "preview": "import { COMMAND_FORCE_UPLOAD_TO_ALL_PROFILES } from '../constants';\nimport { checkFileCommand } from './abstract/create..."
  },
  {
    "path": "src/commands/fileMultiCommandUploadProjectToAllProfiles.ts",
    "chars": 314,
    "preview": "import { COMMAND_UPLOAD_PROJECT_TO_ALL_PROFILES } from '../constants';\nimport { checkFileCommand } from './abstract/crea..."
  },
  {
    "path": "src/commands/fileMultiCommandUploadToAllProfiles.ts",
    "chars": 277,
    "preview": "import { COMMAND_UPLOAD_TO_ALL_PROFILES } from '../constants';\nimport { checkFileCommand } from './abstract/createComman..."
  },
  {
    "path": "src/commands/shared.ts",
    "chars": 4528,
    "preview": "import * as path from 'path';\nimport { Uri, window } from 'vscode';\nimport { FileType } from '../core';\nimport { getAllF..."
  },
  {
    "path": "src/constants.ts",
    "chars": 3368,
    "preview": "import * as path from 'path';\n\nconst VENDOR_FOLDER = '.vscode';\n\nexport const EXTENSION_NAME = 'sftp';\nexport const SETT..."
  },
  {
    "path": "src/core/customError.ts",
    "chars": 460,
    "preview": "class CustomError extends Error {\n  code: string;\n\n  constructor(code, message) {\n    // Pass remaining arguments (inclu..."
  },
  {
    "path": "src/core/fileBaseOperations.ts",
    "chars": 1752,
    "preview": "import { FileSystem } from './fs';\nimport { window } from 'vscode';\nimport { Readable } from 'stream';\nimport logger fro..."
  },
  {
    "path": "src/core/fileService.ts",
    "chars": 16033,
    "preview": "import { EventEmitter } from 'events';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as sshConfig fro..."
  },
  {
    "path": "src/core/fs/fileSystem.ts",
    "chars": 3001,
    "preview": "import { Readable } from 'stream';\nimport * as fs from 'fs';\n\ninterface FileSystemError extends Error {\n  code: string;..."
  },
  {
    "path": "src/core/fs/ftpFileSystem.ts",
    "chars": 9991,
    "preview": "import * as PQueue from 'p-queue';\nimport { Readable } from 'stream';\nimport logger from '../../logger';\nimport { FileEn..."
  },
  {
    "path": "src/core/fs/index.ts",
    "chars": 382,
    "preview": "import FileSystem, { FileEntry, FileType } from './fileSystem';\nimport LocalFileSystem from './localFileSystem';\nimport..."
  },
  {
    "path": "src/core/fs/localFileSystem.ts",
    "chars": 4834,
    "preview": "import * as fs from 'fs';\nimport * as fse from 'fs-extra';\nimport FileSystem, { FileEntry, FileStats, FileOption } from..."
  },
  {
    "path": "src/core/fs/remoteFileSystem.ts",
    "chars": 2811,
    "preview": "import FileSystem, { FileOption } from './fileSystem';\nimport { RemoteClient, ConnectOption, RemoteClientConfig } from '..."
  },
  {
    "path": "src/core/fs/sftpFileSystem.ts",
    "chars": 9709,
    "preview": "import { Readable, Writable } from 'stream';\nimport FileSystem, {\n  FileEntry,\n  FileType,\n  FileStats,\n  FileOption,\n}..."
  },
  {
    "path": "src/core/ignore.ts",
    "chars": 448,
    "preview": "import GitIgnore from 'ignore';\n\nexport default class Ignore {\n  static from(pattern) {\n    return new Ignore(pattern);..."
  },
  {
    "path": "src/core/index.ts",
    "chars": 571,
    "preview": "import * as fileOperations from './fileBaseOperations';\nimport upath from './upath';\nimport FileService, { WatcherServic..."
  },
  {
    "path": "src/core/localFs.ts",
    "chars": 129,
    "preview": "import * as path from 'path';\nimport { LocalFileSystem } from './fs';\n\nconst fs = new LocalFileSystem(path);\n\nexport def..."
  },
  {
    "path": "src/core/remote-client/ftpClient.ts",
    "chars": 2830,
    "preview": "import * as Client from 'ftp';\nimport RemoteClient, { ConnectOption } from './remoteClient';\n\n// tslint:disable\nClient.p..."
  },
  {
    "path": "src/core/remote-client/index.ts",
    "chars": 272,
    "preview": "import RemoteClient, { ErrorCode, ConnectOption, Config } from './remoteClient';\nimport SSHClient from './sshClient';\nim..."
  },
  {
    "path": "src/core/remote-client/remoteClient.ts",
    "chars": 1863,
    "preview": "import CustomError from '../customError';\n\nexport interface ConnectOption {\n  // common\n  host: string;\n  port: number;..."
  },
  {
    "path": "src/core/remote-client/sshClient.ts",
    "chars": 10711,
    "preview": "import { Client } from 'ssh2';\nimport upath from '../upath';\nimport RemoteClient, { ErrorCode, ConnectOption, Config } f..."
  },
  {
    "path": "src/core/remoteFs.ts",
    "chars": 3436,
    "preview": "import upath from './upath';\nimport { promptForPassword } from '../host';\nimport logger from '../logger';\nimport app fro..."
  },
  {
    "path": "src/core/scheduler.ts",
    "chars": 4383,
    "preview": "// tslint:disable-next-line\n// modified from https://raw.githubusercontent.com/sindresorhus/p-queue/a202b25d3e2f8d0472f8..."
  },
  {
    "path": "src/core/transferTask.ts",
    "chars": 5741,
    "preview": "import { Readable } from 'stream';\nimport * as fileOperations from './fileBaseOperations';\nimport { FileSystem, FileType..."
  },
  {
    "path": "src/core/uResource.ts",
    "chars": 4254,
    "preview": "/* tslint:disable max-classes-per-file */\nimport * as querystring from 'querystring';\nimport { Uri } from 'vscode';\nimpo..."
  },
  {
    "path": "src/core/upath.ts",
    "chars": 55,
    "preview": "import * as upath from 'upath';\n\nexport default upath;\n"
  },
  {
    "path": "src/extension.ts",
    "chars": 2015,
    "preview": "'use strict';\n// The module 'vscode' contains the VS Code extensibility API\n// Import the module and reference it with t..."
  },
  {
    "path": "src/fileHandlers/create.ts",
    "chars": 2481,
    "preview": "import { refreshRemoteExplorer } from './shared';\nimport { fileOperations } from '../core';\nimport createFileHandler fro..."
  },
  {
    "path": "src/fileHandlers/createFileHandler.ts",
    "chars": 3409,
    "preview": "import { Uri } from 'vscode';\nimport app from '../app';\nimport { UResource, FileService, ServiceConfig } from '../core';..."
  },
  {
    "path": "src/fileHandlers/diff.ts",
    "chars": 882,
    "preview": "import * as path from 'path';\nimport { diffFiles } from '../host';\nimport { EXTENSION_NAME } from '../constants';\nimport..."
  },
  {
    "path": "src/fileHandlers/index.ts",
    "chars": 227,
    "preview": "export * from './transfer';\nexport * from './remove';\nexport * from './diff';\nexport * from './rename';\nexport * from '...."
  },
  {
    "path": "src/fileHandlers/option.ts",
    "chars": 89,
    "preview": "export interface FileHandleOption {\n  ignore?: ((filepath: string) => boolean) | null;\n}\n"
  },
  {
    "path": "src/fileHandlers/remove.ts",
    "chars": 1229,
    "preview": "import { refreshRemoteExplorer } from './shared';\nimport { fileOperations, FileType } from '../core';\nimport createFileH..."
  },
  {
    "path": "src/fileHandlers/rename.ts",
    "chars": 415,
    "preview": "import { fileOperations } from '../core';\nimport createFileHandler from './createFileHandler';\n\nexport const renameRemot..."
  },
  {
    "path": "src/fileHandlers/shared.ts",
    "chars": 802,
    "preview": "import { UResource, FileService, FileType } from '../core';\nimport app from '../app';\n\n// NEED_VSCODE_UPDATE: detect exp..."
  },
  {
    "path": "src/fileHandlers/transfer/__tests__/transfer-test.ts",
    "chars": 13562,
    "preview": "jest.mock('fs');\n\nimport { vol } from 'memfs';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { sync, Tr..."
  },
  {
    "path": "src/fileHandlers/transfer/index.ts",
    "chars": 6711,
    "preview": "import { refreshRemoteExplorer } from '../shared';\nimport createFileHandler, { FileHandlerContext } from '../createFileH..."
  },
  {
    "path": "src/fileHandlers/transfer/transfer.ts",
    "chars": 12894,
    "preview": "import {\n  FileSystem,\n  FileEntry,\n  FileType,\n  TransferTask,\n  TransferOption as TransferTaskTransferOption,\n  Transf..."
  },
  {
    "path": "src/helper/error.ts",
    "chars": 519,
    "preview": "import * as output from '../ui/output';\nimport logger from '../logger';\nimport { showErrorMessage } from '../host';\n\nexp..."
  },
  {
    "path": "src/helper/file.ts",
    "chars": 728,
    "preview": "import * as path from 'path';\nimport * as tmp from 'tmp';\nimport * as vscode from 'vscode';\nimport { CONGIF_FILENAME } f..."
  },
  {
    "path": "src/helper/index.ts",
    "chars": 100,
    "preview": "export * from './paths';\nexport * from './file';\nexport * from './error';\nexport * from './select';\n"
  },
  {
    "path": "src/helper/paths.ts",
    "chars": 2412,
    "preview": "import * as os from 'os';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { URI } from 'vscode-uri';\nimpo..."
  },
  {
    "path": "src/helper/select.ts",
    "chars": 4032,
    "preview": "import * as vscode from 'vscode';\nimport { FileSystem, FileType } from '../core';\nimport * as path from 'path';\n\nconst R..."
  },
  {
    "path": "src/host.ts",
    "chars": 3714,
    "preview": "import * as vscode from 'vscode';\nimport { EXTENSION_NAME } from './constants';\n\nexport function getOpenTextDocuments():..."
  },
  {
    "path": "src/initCommands.ts",
    "chars": 2446,
    "preview": "import { ExtensionContext } from 'vscode';\nimport logger from './logger';\nimport { registerCommand } from './host';\nimpo..."
  },
  {
    "path": "src/logger.ts",
    "chars": 1674,
    "preview": "import * as output from './ui/output';\nimport { getExtensionSetting } from './modules/ext';\n\nconst extSetting = getExten..."
  },
  {
    "path": "src/modules/appState.ts",
    "chars": 514,
    "preview": "class AppState {\n  private _profile: string | null = null;\n  private _observer: (x: any) => void;\n\n  get profile(): stri..."
  },
  {
    "path": "src/modules/config.ts",
    "chars": 4498,
    "preview": "import * as vscode from 'vscode';\nimport * as fse from 'fs-extra';\nimport * as path from 'path';\nimport * as Joi from 'j..."
  },
  {
    "path": "src/modules/ext.ts",
    "chars": 173,
    "preview": "import { getUserSetting } from '../host';\nimport { EXTENSION_NAME } from '../constants';\n\nexport function getExtensionSe..."
  },
  {
    "path": "src/modules/fileActivityMonitor.ts",
    "chars": 3455,
    "preview": "import * as vscode from 'vscode';\nimport logger from '../logger';\nimport { realpathSync } from 'fs';\nimport app from '....."
  },
  {
    "path": "src/modules/fileWatcher.ts",
    "chars": 3554,
    "preview": "import * as vscode from 'vscode';\nimport * as debounce from 'lodash.debounce';\nimport logger from '../logger';\nimport {..."
  },
  {
    "path": "src/modules/git/git.d.ts",
    "chars": 6107,
    "preview": "/*---------------------------------------------------------------------------------------------\n *  Copyright (c) Micros..."
  },
  {
    "path": "src/modules/git/index.ts",
    "chars": 356,
    "preview": "import * as vscode from 'vscode';\nimport { GitExtension, API, Status, Change, Repository } from './git';\n\nlet git: API;..."
  },
  {
    "path": "src/modules/remoteExplorer/explorer.ts",
    "chars": 3297,
    "preview": "import * as vscode from 'vscode';\nimport { registerCommand } from '../../host';\nimport {\n  COMMAND_REMOTEEXPLORER_REFRES..."
  },
  {
    "path": "src/modules/remoteExplorer/index.ts",
    "chars": 103,
    "preview": "export { default } from './explorer';\nexport { ExplorerItem, ExplorerRoot } from './treeDataProvider';\n"
  },
  {
    "path": "src/modules/remoteExplorer/treeDataProvider.ts",
    "chars": 8282,
    "preview": "import * as vscode from 'vscode';\nimport { showTextDocument } from '../../host';\nimport {\n  upath,\n  UResource,\n  Resour..."
  },
  {
    "path": "src/modules/serviceManager/index.ts",
    "chars": 5046,
    "preview": "import { Uri } from 'vscode';\nimport * as path from 'path';\nimport app from '../../app';\nimport logger from '../../logge..."
  },
  {
    "path": "src/modules/serviceManager/trie.ts",
    "chars": 5279,
    "preview": "/* tslint:disable:max-classes-per-file ... */\nconst defaultOption = {\n  delimiter: '/',\n};\n\ninterface ITrieNodeChildren<..."
  },
  {
    "path": "src/ui/output.ts",
    "chars": 901,
    "preview": "import * as vscode from 'vscode';\nimport app from '../app';\nimport { EXTENSION_NAME } from '../constants';\nimport Status..."
  },
  {
    "path": "src/ui/statusBarItem.ts",
    "chars": 3115,
    "preview": "import * as vscode from 'vscode';\n\nconst spinners = {\n  dots: {\n    interval: 80,\n    frames: ['⠋', '⠙', '⠹', '⠸', '⠼',..."
  },
  {
    "path": "src/utils.ts",
    "chars": 391,
    "preview": "export function flatten(items) {\n  const accumulater = (result, item) => result.concat(item);\n  return items.reduce(accu..."
  },
  {
    "path": "test/config.spec.js",
    "chars": 6418,
    "preview": "const Joi = require('joi');\n\nconst nullable = schema => schema.optional().allow(null);\n\nconst configScheme = {\n  context..."
  },
  {
    "path": "test/core/scheduler.spec.js",
    "chars": 6811,
    "preview": "const Scheduler = require('../../src/core/scheduler').default;\n\nconst randomInt = function(min, max) {\n  if (max === und..."
  },
  {
    "path": "test/helper/localRemoteFs.ts",
    "chars": 1305,
    "preview": "import * as fs from 'fs';\nimport * as fse from 'fs-extra';\nimport FileSystem, { FileStats } from '../../src/core/fs/file..."
  },
  {
    "path": "test/index.ts",
    "chars": 1017,
    "preview": "//\n// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING\n//\n// This file is providing the test runner to u..."
  },
  {
    "path": "test/preprocessor.js",
    "chars": 255,
    "preview": "const tsc = require('typescript');\nconst tsConfig = require('../tsconfig.json');\n\nmodule.exports = {\n  process(src, path..."
  },
  {
    "path": "test/trie.spec.js",
    "chars": 5687,
    "preview": "const Trie = require('../src/modules/serviceManager/trie').default;\n\ndescribe('Trie Tests', () => {\n  describe('find all..."
  },
  {
    "path": "tsconfig.json",
    "chars": 705,
    "preview": "{\n    \"compilerOptions\": {\n        \"module\": \"commonjs\",\n        \"target\": \"es6\",\n        \"outDir\": \"out\",\n        \"lib\"..."
  },
  {
    "path": "tslint.json",
    "chars": 1148,
    "preview": "{\n  \"extends\": \"tslint:recommended\",\n  \"rules\": {\n    \"object-literal-key-quotes\": false,\n    \"no-angle-bracket-type-ass..."
  },
  {
    "path": "webpack.config.js",
    "chars": 726,
    "preview": "//@ts-check\n\n'use strict';\n\nconst path = require('path');\n\n/**@type {import('webpack').Configuration}*/\nconst config = {..."
  }
]

About this extraction

This page contains the full source code of the Natizyskunk/vscode-sftp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 144 files (377.0 KB), approximately 100.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!