├── .gitignore ├── screenshots ├── new-host.gif ├── clone-host.gif ├── delete-host.gif ├── edit-host.gif └── new-parameters.gif ├── bin └── sshcon ├── .npmignore ├── src ├── Enums │ ├── HostOperations.ts │ └── MainMenu.ts ├── Questions │ └── index.ts └── index.ts ├── tslint.json ├── tsconfig.json ├── package.json ├── LICENSE ├── README.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /screenshots/new-host.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzhaninan/sshcon/HEAD/screenshots/new-host.gif -------------------------------------------------------------------------------- /screenshots/clone-host.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzhaninan/sshcon/HEAD/screenshots/clone-host.gif -------------------------------------------------------------------------------- /screenshots/delete-host.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzhaninan/sshcon/HEAD/screenshots/delete-host.gif -------------------------------------------------------------------------------- /screenshots/edit-host.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzhaninan/sshcon/HEAD/screenshots/edit-host.gif -------------------------------------------------------------------------------- /screenshots/new-parameters.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oguzhaninan/sshcon/HEAD/screenshots/new-parameters.gif -------------------------------------------------------------------------------- /bin/sshcon: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * sshcon 5 | * 6 | * @author Oğuzhan İNAN 7 | */ 8 | 9 | require('../dist/index.js'); -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.lock 3 | *.DS_Store 4 | *.swp 5 | *.out 6 | 7 | src/ 8 | screenshots/ 9 | node_modules/ 10 | test/ 11 | .travis.yml 12 | tsconfig.json 13 | tslint.json -------------------------------------------------------------------------------- /src/Enums/HostOperations.ts: -------------------------------------------------------------------------------- 1 | enum HostOperations { 2 | EditHost = 'EditHost', 3 | DeleteHost = 'DeleteHost', 4 | CloneHost = 'CloneHost', 5 | AddNewParametersToHost = 'AddNewParametersToHost', 6 | } 7 | 8 | export default HostOperations; -------------------------------------------------------------------------------- /src/Enums/MainMenu.ts: -------------------------------------------------------------------------------- 1 | enum MainMenu { 2 | AddNewHost = 'AddNewHost', 3 | EditHost = 'EditHost', 4 | DeleteHost = 'DeleteHost', 5 | CloneHost = 'CloneHost', 6 | AddNewParametersToHost = 'AddNewParametersToHost', 7 | Settings = 'Settings', 8 | } 9 | 10 | export default MainMenu; -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "quotemark": false, 9 | "no-console": false, 10 | "object-literal-sort-keys": false, 11 | "align": false, 12 | "member-ordering": false 13 | }, 14 | "rulesDirectory": [] 15 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noImplicitAny": false, 5 | "removeComments": true, 6 | "preserveConstEnums": true, 7 | "sourceMap": true, 8 | "outDir": "dist", 9 | "target": "es5", 10 | "experimentalDecorators": true, 11 | "lib": [ 12 | "es2015" 13 | ], 14 | "types": [ 15 | "node" 16 | ] 17 | }, 18 | "include": [ 19 | "src/**/*" 20 | ], 21 | "exclude": [ 22 | "node_modules" 23 | ] 24 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sshcon", 3 | "version": "1.0.0", 4 | "description": "Quick and simple SSH config management tool", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "start": "yarn run build && clear && node dist/index.js", 8 | "build": "tsc", 9 | "lint": "tslint -c tslint.json src/**/*.ts src/*.ts", 10 | "lint:fix": "yarn run lint --fix", 11 | "clean": "rm -rf dist/*" 12 | }, 13 | "preferGlobal": true, 14 | "bin": { 15 | "sshcon": "./bin/sshcon" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/oguzhaninan/sshcon.git" 20 | }, 21 | "author": { 22 | "name": "Oğuzhan İNAN", 23 | "email": "oguzhan3488@gmail.com", 24 | "url": "https://oguzhaninan.gitlab.io" 25 | }, 26 | "license": "MIT", 27 | "dependencies": { 28 | "chalk-pipe": "^2.0.0", 29 | "enquirer": "^2.2.0", 30 | "ssh-config": "^1.1.5", 31 | "typescript": "^3.7.5" 32 | }, 33 | "devDependencies": { 34 | "@types/node": "^12.0.12" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Oguzhan Inan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sshcon 2 | > Quick and simple SSH config management tool. 3 | 4 | 5 | Patreon 6 | 7 | 8 | [![npm](https://img.shields.io/npm/v/sshcon.svg)](https://www.npmjs.com/package/sshcon) 9 | [![npm](https://img.shields.io/npm/l/sshcon.svg)](https://github.com/oguzhaninan/sshcon/blob/master/LICENSE) 10 | [![npm](https://img.shields.io/npm/dt/sshcon.svg)](https://www.npmjs.com/package/sshcon) 11 | 12 | 13 | ## Getting started 14 | 15 | ## Installation 16 | You need to install [Node.js](https://nodejs.org/en/download/) first, then install the tool globally using this command: 17 | 18 | ### NPM 19 | ```bash 20 | sudo npm install -g sshcon 21 | ``` 22 | ### YARN 23 | ```bash 24 | yarn global add sshcon 25 | ``` 26 | 27 | ## Features 28 | * [Add New Host](#add-new-host) 29 | * [Add New Parameters to Host](#add-new-parameters-to-host) 30 | * [Delete Host](#delete-host) 31 | * [Edit Host](#edit-host) 32 | * [Clone Host](#clone-host) 33 | 34 | 35 | ## Add New Host 36 |

37 | 38 | ## Add New Parameters to Host 39 |

40 | 41 | ## Delete Host 42 |

43 | 44 | ## Edit Host 45 |

46 | 47 | ## Clone Host 48 |

49 | 50 | # License 51 | This project is under the MIT license. 52 | -------------------------------------------------------------------------------- /src/Questions/index.ts: -------------------------------------------------------------------------------- 1 | import MainMenu from "../Enums/MainMenu"; 2 | 3 | export default { 4 | MainMenuQuestion: { 5 | name: 'selectedMenu', 6 | type: 'select', 7 | message: 'Main Menu', 8 | choices: [ 9 | { message: 'Add New Host', value: MainMenu.AddNewHost }, 10 | { message: 'Add New Parameters to Host', value: MainMenu.AddNewParametersToHost }, 11 | { message: 'Delete Host', value: MainMenu.DeleteHost }, 12 | { message: 'Edit Host', value: MainMenu.EditHost }, 13 | { message: 'Clone Host', value: MainMenu.CloneHost }, 14 | { message: 'Settings', value: MainMenu.Settings }, 15 | ], 16 | }, 17 | HostParams: { 18 | name: 'selectedParams', 19 | type: 'multiselect', 20 | message: 'Select the parameters to use', 21 | hint: '(Use to select, to submit)', 22 | choices: [ 23 | { name: 'HostName', value: 'HostName' }, 24 | { name: 'User', value: 'User' }, 25 | { name: 'Port', value: 'Port' }, 26 | { name: 'IdentityFile', value: 'IdentityFile' }, 27 | { name: 'IdentitiesOnly', value: 'IdentitiesOnly' }, 28 | { name: 'ServerAliveInterval', value: 'ServerAliveInterval' }, 29 | { name: 'PreferredAuthentications', value: 'PreferredAuthentications' }, 30 | { name: 'PasswordAuthentication', value: 'PasswordAuthentication' }, 31 | { name: 'LogLevel', value: 'LogLevel' }, 32 | { name: 'StrictHostKeyChecking', value: 'StrictHostKeyChecking' }, 33 | { name: 'UserKnownHostsFile', value: 'UserKnownHostsFile' }, 34 | { name: 'VisualHostKey', value: 'VisualHostKey' }, 35 | { name: 'Compression', value: 'Compression' }, 36 | { name: 'LocalForward', value: 'LocalForward' }, 37 | { name: 'RemoteForward', value: 'RemoteForward' }, 38 | { name: 'DynamicForward', value: 'DynamicForward' }, 39 | { name: 'ForwardAgent', value: 'ForwardAgent' }, 40 | { name: 'ForwardX11', value: 'ForwardX11' }, 41 | { name: 'ControlMaster', value: 'ControlMaster' }, 42 | { name: 'ControlPath', value: 'ControlPath' }, 43 | { name: 'ControlPersist', value: 'ControlPersist' }, 44 | ], 45 | }, 46 | HostList: { 47 | name: 'selectedHost', 48 | type: 'select', 49 | message: 'Select a host', 50 | choices: [], 51 | }, 52 | SettingsForm: { 53 | name: 'settings', 54 | type: 'form', 55 | message: 'Edit Settings.', 56 | choices: [ 57 | { name: 'configPath', message: 'Config Path', initial: '' }, 58 | ] 59 | } 60 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/node@^12.0.12": 6 | version "12.12.27" 7 | resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.27.tgz#d7506f73160ad30fcebbcf5b8b7d2d976e649e42" 8 | integrity sha512-odQFl/+B9idbdS0e8IxDl2ia/LP8KZLXhV3BUeI98TrZp0uoIzQPhGd+5EtzHmT0SMOIaPd7jfz6pOHLWTtl7A== 9 | 10 | ansi-colors@^3.2.1: 11 | version "3.2.3" 12 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" 13 | integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== 14 | 15 | ansi-styles@^3.2.1: 16 | version "3.2.1" 17 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 18 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 19 | dependencies: 20 | color-convert "^1.9.0" 21 | 22 | chalk-pipe@^2.0.0: 23 | version "2.0.0" 24 | resolved "https://registry.yarnpkg.com/chalk-pipe/-/chalk-pipe-2.0.0.tgz#628b57f973c644ee94e83113d522af1401f5aec2" 25 | integrity sha512-+sflG/DXM9q47GMiNQIGKuzUr86FHrZfTO+Em6+s90jBJqCp4fv4O/ZOu2Uj4G5PVPXW6LqCqg3HJlBkPus4Pw== 26 | dependencies: 27 | chalk "^2.4.1" 28 | css-color-names "0.0.4" 29 | 30 | chalk@^2.4.1: 31 | version "2.4.1" 32 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 33 | integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== 34 | dependencies: 35 | ansi-styles "^3.2.1" 36 | escape-string-regexp "^1.0.5" 37 | supports-color "^5.3.0" 38 | 39 | color-convert@^1.9.0: 40 | version "1.9.3" 41 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 42 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 43 | dependencies: 44 | color-name "1.1.3" 45 | 46 | color-name@1.1.3: 47 | version "1.1.3" 48 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 49 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 50 | 51 | css-color-names@0.0.4: 52 | version "0.0.4" 53 | resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" 54 | integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= 55 | 56 | enquirer@^2.2.0: 57 | version "2.2.0" 58 | resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.2.0.tgz#beb4415e6293a6214de0fd405398e994da775d66" 59 | integrity sha512-etPgLOWzaJ7qgJyQEVvSG+vOjkldCYJ3ZnDsY/ZF++O/K30rbWUTyUdoz03fg1EF7WtJHIKWdQDHSSYtzNqtTQ== 60 | dependencies: 61 | ansi-colors "^3.2.1" 62 | 63 | escape-string-regexp@^1.0.5: 64 | version "1.0.5" 65 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 66 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 67 | 68 | has-flag@^3.0.0: 69 | version "3.0.0" 70 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 71 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 72 | 73 | ssh-config@^1.1.5: 74 | version "1.1.5" 75 | resolved "https://registry.yarnpkg.com/ssh-config/-/ssh-config-1.1.5.tgz#a2d398c611a65c60d101e5b0f976c01f4739a28e" 76 | integrity sha512-j3Yo++WWfc+2eFrSFclQZ79RRyMF2BOkDLboaUxyysYNomrLjIoOWanFbulHH5fLErRuW8YrO31p7MKpI4XrZw== 77 | 78 | supports-color@^5.3.0: 79 | version "5.5.0" 80 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 81 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 82 | dependencies: 83 | has-flag "^3.0.0" 84 | 85 | typescript@^3.7.5: 86 | version "3.7.5" 87 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" 88 | integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== 89 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as SSHConfig from 'ssh-config'; 2 | import * as fs from 'fs'; 3 | import * as os from 'os'; 4 | import * as path from 'path'; 5 | import * as chalk from 'chalk-pipe'; 6 | import { prompt } from 'enquirer'; 7 | import Questions from './Questions'; 8 | import HostOperations from './Enums/HostOperations'; 9 | import MainMenu from './Enums/MainMenu'; 10 | 11 | interface Settings { 12 | configPath: string, 13 | } 14 | 15 | export default class SSHCon { 16 | 17 | private settings: Settings; 18 | private settingsPath: string; 19 | 20 | private selectedHost: string; 21 | private parsedSSHConfigFile: any[]; 22 | 23 | constructor() { 24 | this.settingsPath = path.join(os.homedir(), '.sshcon_settings.json'); 25 | 26 | if (fs.existsSync(this.settingsPath)) { 27 | try { 28 | this.settings = JSON.parse(fs.readFileSync(this.settingsPath, 'utf8')); 29 | } catch(err) { 30 | 31 | } 32 | } else { 33 | this.settings = { 34 | configPath: path.join(os.homedir(), '.ssh/config'), 35 | }; 36 | fs.writeFileSync(this.settingsPath, JSON.stringify(this.settings)); 37 | } 38 | } 39 | 40 | public async main(): Promise { 41 | console.log(chalk('green')(`sshcon version ${this.getVersion()}`)); 42 | 43 | this.showMainMenu().then(() => { 44 | console.log(chalk('green')(`Successfully updated.`)); 45 | }) 46 | .catch(_err => { 47 | console.log(chalk('red')('Process canceled.')) 48 | }); 49 | 50 | } 51 | 52 | private async showMainMenu(): Promise { 53 | const mainMenuAnswer: any = await prompt(Questions.MainMenuQuestion) 54 | 55 | switch (mainMenuAnswer.selectedMenu) { 56 | case MainMenu.DeleteHost: 57 | case MainMenu.CloneHost: 58 | case MainMenu.EditHost: 59 | case MainMenu.AddNewParametersToHost: { 60 | await this.askHostOperations(mainMenuAnswer.selectedMenu); 61 | } break; 62 | case MainMenu.AddNewHost: { 63 | try { 64 | await this.newHost(); 65 | } catch (err) { 66 | console.log(err, 'addnewhost'); 67 | } 68 | } break; 69 | case MainMenu.Settings: { 70 | await this.editSettings(); 71 | } break; 72 | } 73 | } 74 | 75 | private async editSettings(): Promise { 76 | Questions.SettingsForm.choices[0].initial = this.settings.configPath; 77 | const formPrompt: any = await prompt(Questions.SettingsForm); 78 | 79 | fs.writeFileSync(this.settingsPath, JSON.stringify(formPrompt.settings)); 80 | } 81 | 82 | private async addNewParametersToHost(index: number): Promise { 83 | const answer: { selectedParams: string[] } = await prompt(Questions.HostParams); 84 | 85 | const formPrompt: any = await prompt({ 86 | name: 'newParams', 87 | type: 'form', 88 | message: 'Fill form fields.', 89 | choices: answer.selectedParams 90 | .map((item: any) => ({ 91 | name: item, 92 | message: item, 93 | })), 94 | }); 95 | 96 | this.parsedSSHConfigFile[index].config.push( 97 | ...Object.keys(formPrompt.newParams) 98 | .map((param: string) => ({ 99 | type: 1, 100 | param: param, 101 | separator: ' ', 102 | value: formPrompt.newParams[param], 103 | before: '\t', 104 | after: '\n' 105 | })) 106 | ); 107 | } 108 | 109 | private async newHost(): Promise { 110 | const answer: { selectedParams: string[] } = await prompt(Questions.HostParams); 111 | 112 | const formPrompt: any = await prompt({ 113 | name: 'newHost', 114 | type: 'form', 115 | message: 'Add Host', 116 | choices: ['Host', ...answer.selectedParams] 117 | .map((item: any) => ({ 118 | name: item, 119 | message: item, 120 | })), 121 | }); 122 | 123 | this.parseHostFile(); 124 | 125 | const { newHost } = formPrompt; 126 | 127 | this.parsedSSHConfigFile.push({ 128 | type: 1, 129 | param: 'Host', 130 | separator: ' ', 131 | value: newHost.Host, 132 | before: '\n', 133 | after: '\n', 134 | config: Object.keys(newHost).slice(1) 135 | .map((param: string) => ({ 136 | type: 1, 137 | param: param, 138 | separator: ' ', 139 | value: newHost[param], 140 | before: '\t', 141 | after: '\n' 142 | })), 143 | }); 144 | 145 | this.writeConfigFile(); 146 | } 147 | 148 | private writeConfigFile(): void { 149 | const configContent: string = SSHConfig.stringify(this.parsedSSHConfigFile); 150 | fs.writeFileSync(this.settings.configPath, configContent); 151 | } 152 | 153 | private parseHostFile(): void { 154 | if (fs.existsSync(this.settings.configPath)) { 155 | const configContent = fs.readFileSync(this.settings.configPath, 'utf8'); 156 | this.parsedSSHConfigFile = SSHConfig.parse(configContent); 157 | } else { 158 | console.log(chalk('red')(`SSH Config file does not exists: ${this.settings.configPath}`)); 159 | process.exit(1); 160 | } 161 | } 162 | 163 | private async hostList(): Promise { 164 | this.parseHostFile(); 165 | 166 | const questionHostList = { ...Questions.HostList }; 167 | questionHostList.choices = this.parsedSSHConfigFile 168 | .filter((item: any) => item.param.trim() === 'Host') 169 | .map((item: any) => ({ 170 | message: item.value, 171 | value: item.value, 172 | })); 173 | 174 | const answer: any = await prompt(questionHostList); 175 | this.selectedHost = answer.selectedHost; 176 | } 177 | 178 | private async askHostOperations(operation: HostOperations): Promise { 179 | await this.hostList(); 180 | 181 | const index: number = this.parsedSSHConfigFile.findIndex((item: any) => item.value === this.selectedHost); 182 | 183 | await this.hostOperation(operation, index); 184 | 185 | // write changes to file 186 | this.writeConfigFile(); 187 | } 188 | 189 | private async hostOperation(operation: HostOperations, index: number) { 190 | switch (operation) { 191 | /** 192 | * Delete selected host 193 | */ 194 | case HostOperations.DeleteHost: { 195 | this.parsedSSHConfigFile.splice(index, 1); 196 | } 197 | break; 198 | /** 199 | * Add new parameters to host 200 | */ 201 | case HostOperations.AddNewParametersToHost: { 202 | await this.addNewParametersToHost(index); 203 | } 204 | break; 205 | /** 206 | * Edit selected host 207 | */ 208 | case HostOperations.EditHost: { 209 | const formPrompt: any = await prompt({ 210 | name: 'params', 211 | type: 'form', 212 | message: 'Edit host', 213 | choices: [{ 214 | param: 'Host', 215 | value: this.parsedSSHConfigFile[index].value, 216 | }, ...this.parsedSSHConfigFile[index].config] 217 | .map((item: any) => ({ 218 | name: item.param, 219 | message: item.param, 220 | initial: item.value, 221 | })), 222 | }); 223 | 224 | const { params } = formPrompt; 225 | 226 | this.parsedSSHConfigFile[index] = { 227 | type: 1, 228 | param: 'Host', 229 | separator: ' ', 230 | value: params.Host, 231 | before: '\n', 232 | after: '\n', 233 | config: Object.keys(params).slice(1) 234 | .map((param: string) => ({ 235 | type: 1, 236 | param: param, 237 | separator: ' ', 238 | value: params[param], 239 | before: '\t', 240 | after: '\n' 241 | })), 242 | }; 243 | } 244 | break; 245 | /** 246 | * Clone selected host 247 | */ 248 | case HostOperations.CloneHost: { 249 | const length: number = this.parsedSSHConfigFile.push({ ...this.parsedSSHConfigFile[index] }); 250 | await this.hostOperation(HostOperations.EditHost, length - 1); 251 | } 252 | } 253 | } 254 | 255 | private getVersion(): number { 256 | return require('../package.json').version; 257 | } 258 | } 259 | 260 | new SSHCon().main(); --------------------------------------------------------------------------------