@abhiseck/zssh 0.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # sshc - SSH Chain
1
+ # zssh - SSH Chain
2
2
 
3
- **sshc** is a modern, cross-platform CLI tool to manage and connect to your SSH servers with encrypted local storage and cloud sync capabilities.
3
+ **zssh** is a modern, cross-platform CLI tool to manage and connect to your SSH servers with encrypted local storage and cloud sync capabilities.
4
4
 
5
5
  ## Features
6
6
 
@@ -13,7 +13,7 @@
13
13
  ## Installation
14
14
 
15
15
  ```bash
16
- npm install -g sshc
16
+ npm install -g @abhiseck/zssh
17
17
  ```
18
18
 
19
19
  ## Usage
@@ -21,7 +21,7 @@ npm install -g sshc
21
21
  ### Add a Connection
22
22
 
23
23
  ```bash
24
- sshc add
24
+ zssh add
25
25
  ```
26
26
 
27
27
  Follow the interactive prompts to save your server details.
@@ -29,7 +29,7 @@ Follow the interactive prompts to save your server details.
29
29
  ### List Connections
30
30
 
31
31
  ```bash
32
- sshc list
32
+ zssh list
33
33
  ```
34
34
 
35
35
  Displays a table of all saved connections.
@@ -37,15 +37,15 @@ Displays a table of all saved connections.
37
37
  ### Connect
38
38
 
39
39
  ```bash
40
- sshc connect <alias_or_id>
40
+ zssh connect <alias_or_id>
41
41
  # Example:
42
- sshc connect my-server
42
+ zssh connect my-server
43
43
  ```
44
44
 
45
45
  ### Remove a Connection
46
46
 
47
47
  ```bash
48
- sshc remove <alias_or_id>
48
+ zssh remove <alias_or_id>
49
49
  ```
50
50
 
51
51
  ### Cloud Sync
@@ -55,18 +55,18 @@ To enable cloud sync, you need a GitHub Personal Access Token (PAT) with `gist`
55
55
  **Save to Cloud:**
56
56
 
57
57
  ```bash
58
- sshc save
58
+ zssh save
59
59
  ```
60
60
 
61
61
  **Sync from Cloud:**
62
62
 
63
63
  ```bash
64
- sshc sync
64
+ zssh sync
65
65
  ```
66
66
 
67
67
  ## Security
68
68
 
69
- Your configuration is stored in `~/.sshc.json`. The entire content is encrypted. You will be prompted for your master password when running commands.
69
+ Your configuration is stored in `~/.zssh.json`. The entire content is encrypted. You will be prompted for your master password when running commands.
70
70
 
71
71
  ## Requirements
72
72
 
@@ -1,47 +1,14 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.addCommand = addCommand;
40
- const chalk = __importStar(require("chalk"));
7
+ const chalk_1 = __importDefault(require("chalk"));
41
8
  const inquirer_1 = __importDefault(require("inquirer"));
42
9
  const uuid_1 = require("uuid");
43
10
  async function addCommand(configManager) {
44
- console.log(chalk.blue("Adding new SSH connection..."));
11
+ console.log(chalk_1.default.blue("Adding new SSH connection..."));
45
12
  const answers = await inquirer_1.default.prompt([
46
13
  {
47
14
  type: "input",
@@ -105,8 +72,8 @@ async function addCommand(configManager) {
105
72
  createdAt: new Date().toISOString(),
106
73
  };
107
74
  configManager.addConnection(connection);
108
- console.log(chalk.green(`\nConnection added successfully!`));
109
- console.log(`ID: ${chalk.cyan(connection.id)}`);
75
+ console.log(chalk_1.default.green(`\nConnection added successfully!`));
76
+ console.log(`ID: ${chalk_1.default.cyan(connection.id)}`);
110
77
  if (connection.alias)
111
- console.log(`Alias: ${chalk.cyan(connection.alias)}`);
78
+ console.log(`Alias: ${chalk_1.default.cyan(connection.alias)}`);
112
79
  }
@@ -1,56 +1,27 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
35
5
  Object.defineProperty(exports, "__esModule", { value: true });
36
6
  exports.connectCommand = connectCommand;
37
- const chalk = __importStar(require("chalk"));
7
+ const chalk_1 = __importDefault(require("chalk"));
38
8
  const child_process_1 = require("child_process");
39
9
  async function isSshpassInstalled() {
40
10
  return new Promise((resolve) => {
41
- const check = (0, child_process_1.spawn)("command", ["-v", "sshpass"]);
42
- check.on("close", (code) => {
43
- resolve(code === 0);
11
+ const isWin = process.platform === "win32";
12
+ const checkCommand = isWin ? "where sshpass" : "command -v sshpass";
13
+ (0, child_process_1.exec)(checkCommand, (error) => {
14
+ resolve(!error);
44
15
  });
45
16
  });
46
17
  }
47
18
  async function connectCommand(configManager, idOrAlias) {
48
19
  const conn = configManager.getConnection(idOrAlias);
49
20
  if (!conn) {
50
- console.log(chalk.red(`Connection not found: ${idOrAlias}`));
21
+ console.log(chalk_1.default.red(`Connection not found: ${idOrAlias}`));
51
22
  return;
52
23
  }
53
- console.log(chalk.blue(`Connecting to ${conn.alias || conn.id} (${conn.username}@${conn.host})...`));
24
+ console.log(chalk_1.default.blue(`Connecting to ${conn.alias || conn.id} (${conn.username}@${conn.host})...`));
54
25
  const args = [];
55
26
  // Port
56
27
  if (conn.port) {
@@ -77,9 +48,9 @@ async function connectCommand(configManager, idOrAlias) {
77
48
  finalArgs = ["-e", "ssh", ...args]; // -e means take password from env
78
49
  }
79
50
  else {
80
- console.log(chalk.yellow('Warning: "sshpass" is not installed.'));
81
- console.log(chalk.yellow("Cannot auto-fill password. You will be prompted by SSH."));
82
- console.log(chalk.dim('Install sshpass (e.g. "brew install sshpass" on Mac) to enable auto-login.'));
51
+ console.log(chalk_1.default.yellow('Warning: "sshpass" is not installed.'));
52
+ console.log(chalk_1.default.yellow("Cannot auto-fill password. You will be prompted by SSH."));
53
+ console.log(chalk_1.default.dim('Install sshpass (e.g. "brew install sshpass" on Mac) to enable auto-login.'));
83
54
  }
84
55
  }
85
56
  const child = (0, child_process_1.spawn)(command, finalArgs, {
@@ -88,10 +59,10 @@ async function connectCommand(configManager, idOrAlias) {
88
59
  });
89
60
  child.on("close", (code) => {
90
61
  if (code !== 0) {
91
- console.log(chalk.red(`\nSSH session ended with code ${code}`));
62
+ console.log(chalk_1.default.red(`\nSSH session ended with code ${code}`));
92
63
  }
93
64
  else {
94
- console.log(chalk.green("\nDisconnected."));
65
+ console.log(chalk_1.default.green("\nDisconnected."));
95
66
  }
96
67
  });
97
68
  }
@@ -1,57 +1,24 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.listCommand = listCommand;
40
- const chalk = __importStar(require("chalk"));
7
+ const chalk_1 = __importDefault(require("chalk"));
41
8
  const cli_table3_1 = __importDefault(require("cli-table3"));
42
9
  async function listCommand(configManager) {
43
10
  const connections = configManager.getConnections();
44
11
  if (connections.length === 0) {
45
- console.log(chalk.yellow('No connections found. Use "sshc add" to add one.'));
12
+ console.log(chalk_1.default.yellow('No connections found. Use "zssh add" to add one.'));
46
13
  return;
47
14
  }
48
15
  const table = new cli_table3_1.default({
49
16
  head: [
50
- chalk.cyan("ID"),
51
- chalk.cyan("Alias"),
52
- chalk.cyan("Host"),
53
- chalk.cyan("User"),
54
- chalk.cyan("Port"),
17
+ chalk_1.default.cyan("ID"),
18
+ chalk_1.default.cyan("Alias"),
19
+ chalk_1.default.cyan("Host"),
20
+ chalk_1.default.cyan("User"),
21
+ chalk_1.default.cyan("Port"),
55
22
  ],
56
23
  style: { head: [], border: [] },
57
24
  });
@@ -1,48 +1,15 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.removeCommand = removeCommand;
40
- const chalk = __importStar(require("chalk"));
7
+ const chalk_1 = __importDefault(require("chalk"));
41
8
  const inquirer_1 = __importDefault(require("inquirer"));
42
9
  async function removeCommand(configManager, idOrAlias) {
43
10
  const conn = configManager.getConnection(idOrAlias);
44
11
  if (!conn) {
45
- console.log(chalk.red(`Connection not found: ${idOrAlias}`));
12
+ console.log(chalk_1.default.red(`Connection not found: ${idOrAlias}`));
46
13
  return;
47
14
  }
48
15
  const { confirm } = await inquirer_1.default.prompt([
@@ -55,6 +22,6 @@ async function removeCommand(configManager, idOrAlias) {
55
22
  ]);
56
23
  if (confirm) {
57
24
  configManager.removeConnection(idOrAlias);
58
- console.log(chalk.green("Connection removed."));
25
+ console.log(chalk_1.default.green("Connection removed."));
59
26
  }
60
27
  }
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -39,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.saveCommand = saveCommand;
40
7
  exports.syncCommand = syncCommand;
41
8
  const axios_1 = __importDefault(require("axios"));
42
- const chalk = __importStar(require("chalk"));
9
+ const chalk_1 = __importDefault(require("chalk"));
43
10
  const inquirer_1 = __importDefault(require("inquirer"));
44
11
  const GITHUB_API = "https://api.github.com";
45
12
  async function getGithubToken(configManager) {
@@ -63,28 +30,28 @@ async function saveCommand(configManager) {
63
30
  const config = configManager.getConfig();
64
31
  const encryptedContent = configManager.getEncryptedContent(); // We need a way to get raw encrypted string
65
32
  if (!encryptedContent) {
66
- console.log(chalk.red("Error: Could not retrieve encrypted config."));
33
+ console.log(chalk_1.default.red("Error: Could not retrieve encrypted config."));
67
34
  return;
68
35
  }
69
36
  const files = {
70
- "sshc_config.lock": {
37
+ "zssh_config.lock": {
71
38
  content: encryptedContent,
72
39
  },
73
40
  };
74
41
  try {
75
42
  if (config.gistId) {
76
43
  // Update existing Gist
77
- console.log(chalk.blue("Updating existing Gist..."));
44
+ console.log(chalk_1.default.blue("Updating existing Gist..."));
78
45
  await axios_1.default.patch(`${GITHUB_API}/gists/${config.gistId}`, { files }, {
79
46
  headers: { Authorization: `token ${token}` },
80
47
  });
81
- console.log(chalk.green("Config saved to Gist successfully."));
48
+ console.log(chalk_1.default.green("Config saved to Gist successfully."));
82
49
  }
83
50
  else {
84
51
  // Create new Gist
85
- console.log(chalk.blue("Creating new Gist..."));
52
+ console.log(chalk_1.default.blue("Creating new Gist..."));
86
53
  const response = await axios_1.default.post(`${GITHUB_API}/gists`, {
87
- description: "SSHC Encrypted Config",
54
+ description: "ZSSH Encrypted Config",
88
55
  public: false,
89
56
  files,
90
57
  }, {
@@ -92,12 +59,12 @@ async function saveCommand(configManager) {
92
59
  });
93
60
  const gistId = response.data.id;
94
61
  configManager.setGistId(gistId);
95
- console.log(chalk.green(`Gist created with ID: ${gistId}`));
96
- console.log(chalk.green("Config saved to Gist successfully."));
62
+ console.log(chalk_1.default.green(`Gist created with ID: ${gistId}`));
63
+ console.log(chalk_1.default.green("Config saved to Gist successfully."));
97
64
  }
98
65
  }
99
66
  catch (error) {
100
- console.error(chalk.red("Failed to save to Gist:"), error.response?.data?.message || error.message);
67
+ console.error(chalk_1.default.red("Failed to save to Gist:"), error.response?.data?.message || error.message);
101
68
  }
102
69
  }
103
70
  async function syncCommand(configManager) {
@@ -115,13 +82,13 @@ async function syncCommand(configManager) {
115
82
  configManager.setGistId(gistId);
116
83
  }
117
84
  try {
118
- console.log(chalk.blue("Fetching Gist..."));
85
+ console.log(chalk_1.default.blue("Fetching Gist..."));
119
86
  const response = await axios_1.default.get(`${GITHUB_API}/gists/${gistId}`, {
120
87
  headers: { Authorization: `token ${token}` },
121
88
  });
122
- const file = response.data.files["sshc_config.lock"];
89
+ const file = response.data.files["zssh_config.lock"];
123
90
  if (!file) {
124
- console.log(chalk.red("Invalid Gist: sshc_config.lock not found."));
91
+ console.log(chalk_1.default.red("Invalid Gist: zssh_config.lock not found."));
125
92
  return;
126
93
  }
127
94
  const encryptedContent = file.content;
@@ -133,13 +100,13 @@ async function syncCommand(configManager) {
133
100
  // Use `save` to push.
134
101
  const merged = configManager.mergeEncrypted(encryptedContent);
135
102
  if (merged) {
136
- console.log(chalk.green("Sync completed. Remote changes merged into local config."));
103
+ console.log(chalk_1.default.green("Sync completed. Remote changes merged into local config."));
137
104
  }
138
105
  else {
139
- console.log(chalk.yellow("Sync completed but no changes were made or merge failed."));
106
+ console.log(chalk_1.default.yellow("Sync completed but no changes were made or merge failed."));
140
107
  }
141
108
  }
142
109
  catch (error) {
143
- console.error(chalk.red("Failed to sync with Gist:"), error.response?.data?.message || error.message);
110
+ console.error(chalk_1.default.red("Failed to sync with Gist:"), error.response?.data?.message || error.message);
144
111
  }
145
112
  }
package/dist/index.js CHANGED
@@ -37,13 +37,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
- const chalk = __importStar(require("chalk"));
40
+ const chalk_1 = __importDefault(require("chalk"));
41
41
  const commander_1 = require("commander");
42
42
  const inquirer_1 = __importDefault(require("inquirer"));
43
43
  const config_1 = require("./lib/config");
44
+ const session_1 = require("./lib/session");
44
45
  const program = new commander_1.Command();
45
46
  const configManager = new config_1.ConfigManager();
46
- async function promptForMasterPassword(isNew = false) {
47
+ async function promptForMasterPassword(isNew = false, forcePrompt = false) {
48
+ if (!isNew && !forcePrompt) {
49
+ const sessionPass = await (0, session_1.getSessionPassword)();
50
+ if (sessionPass)
51
+ return sessionPass;
52
+ }
47
53
  const { password } = await inquirer_1.default.prompt([
48
54
  {
49
55
  type: "password",
@@ -56,20 +62,22 @@ async function promptForMasterPassword(isNew = false) {
56
62
  ]);
57
63
  return password;
58
64
  }
59
- async function initConfig() {
65
+ async function initConfig(requirePassword = false) {
60
66
  if (configManager.isConfigExists()) {
61
- const password = await promptForMasterPassword();
67
+ const password = await promptForMasterPassword(false, requirePassword);
62
68
  configManager.setMasterKey(password);
63
69
  try {
64
70
  configManager.load();
71
+ await (0, session_1.saveSessionPassword)(password);
65
72
  }
66
73
  catch (e) {
67
- console.error(chalk.red("Invalid password or corrupted config."));
74
+ await (0, session_1.clearSession)();
75
+ console.error(chalk_1.default.red("Invalid password or corrupted config."));
68
76
  process.exit(1);
69
77
  }
70
78
  }
71
79
  else {
72
- console.log(chalk.yellow("No existing config found. Initializing..."));
80
+ console.log(chalk_1.default.yellow("No existing config found. Initializing..."));
73
81
  const password = await promptForMasterPassword(true);
74
82
  const { confirm } = await inquirer_1.default.prompt([
75
83
  {
@@ -80,12 +88,13 @@ async function initConfig() {
80
88
  },
81
89
  ]);
82
90
  if (password !== confirm) {
83
- console.error(chalk.red("Passwords do not match."));
91
+ console.error(chalk_1.default.red("Passwords do not match."));
84
92
  process.exit(1);
85
93
  }
86
94
  configManager.setMasterKey(password);
87
95
  configManager.save(); // Create empty encrypted file
88
- console.log(chalk.green("Config initialized!"));
96
+ await (0, session_1.saveSessionPassword)(password);
97
+ console.log(chalk_1.default.green("Config initialized!"));
89
98
  }
90
99
  }
91
100
  program
@@ -137,7 +146,7 @@ program
137
146
  .command("save")
138
147
  .description("Save config to GitHub Gist")
139
148
  .action(async () => {
140
- await initConfig();
149
+ await initConfig(true);
141
150
  const { saveCommand } = await Promise.resolve().then(() => __importStar(require("./commands/sync")));
142
151
  await saveCommand(configManager);
143
152
  });
@@ -145,7 +154,7 @@ program
145
154
  .command("sync")
146
155
  .description("Sync config from GitHub Gist")
147
156
  .action(async () => {
148
- await initConfig();
157
+ await initConfig(true);
149
158
  const { syncCommand } = await Promise.resolve().then(() => __importStar(require("./commands/sync")));
150
159
  await syncCommand(configManager);
151
160
  });
@@ -38,7 +38,7 @@ const fs = __importStar(require("fs"));
38
38
  const os = __importStar(require("os"));
39
39
  const path = __importStar(require("path"));
40
40
  const crypto_1 = require("./crypto");
41
- const CONFIG_PATH = path.join(os.homedir(), ".sshc.json");
41
+ const CONFIG_PATH = path.join(os.homedir(), ".zssh.json");
42
42
  class ConfigManager {
43
43
  constructor() {
44
44
  this.masterKey = null;
@@ -158,7 +158,7 @@ class ConfigManager {
158
158
  this.save();
159
159
  }
160
160
  getConnection(idOrAlias) {
161
- return this.config.connections.find((c) => c.id === idOrAlias || c.alias === idOrAlias);
161
+ return this.config.connections.find((c) => c.id === idOrAlias || c.alias.toLowerCase() === idOrAlias.toLowerCase());
162
162
  }
163
163
  isConfigExists() {
164
164
  return fs.existsSync(CONFIG_PATH);
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getSessionPassword = getSessionPassword;
37
+ exports.saveSessionPassword = saveSessionPassword;
38
+ exports.clearSession = clearSession;
39
+ const keytar = __importStar(require("keytar"));
40
+ const SERVICE_NAME = "zssh";
41
+ const ACCOUNT_NAME = "session";
42
+ const TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
43
+ async function getSessionPassword() {
44
+ try {
45
+ const dataString = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);
46
+ if (!dataString)
47
+ return null;
48
+ const data = JSON.parse(dataString);
49
+ if (Date.now() > data.expiresAt) {
50
+ await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
51
+ return null;
52
+ }
53
+ // Refresh expiration smoothly
54
+ data.expiresAt = Date.now() + TIMEOUT_MS;
55
+ await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(data));
56
+ return data.password;
57
+ }
58
+ catch (e) {
59
+ return null;
60
+ }
61
+ }
62
+ async function saveSessionPassword(password) {
63
+ try {
64
+ const data = {
65
+ password,
66
+ expiresAt: Date.now() + TIMEOUT_MS,
67
+ };
68
+ await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(data));
69
+ }
70
+ catch (e) {
71
+ // safely ignore keychain errors
72
+ }
73
+ }
74
+ async function clearSession() {
75
+ try {
76
+ await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
77
+ }
78
+ catch (e) { }
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abhiseck/zssh",
3
- "version": "0.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -23,12 +23,12 @@
23
23
  ],
24
24
  "repository": {
25
25
  "type": "git",
26
- "url": "git+https://github.com/iamalipe/sshc.git"
26
+ "url": "git+https://github.com/iamalipe/zssh.git"
27
27
  },
28
28
  "bugs": {
29
- "url": "https://github.com/iamalipe/sshc/issues"
29
+ "url": "https://github.com/iamalipe/zssh/issues"
30
30
  },
31
- "homepage": "https://github.com/iamalipe/sshc#readme",
31
+ "homepage": "https://github.com/iamalipe/zssh#readme",
32
32
  "engines": {
33
33
  "node": ">=18"
34
34
  },
@@ -1,4 +1,4 @@
1
- import * as chalk from "chalk";
1
+ import chalk from "chalk";
2
2
  import inquirer from "inquirer";
3
3
  import { v4 as uuidv4 } from "uuid";
4
4
  import { ConfigManager } from "../lib/config";
@@ -1,12 +1,14 @@
1
- import * as chalk from "chalk";
2
- import { spawn } from "child_process";
1
+ import chalk from "chalk";
2
+ import { exec, spawn } from "child_process";
3
3
  import { ConfigManager } from "../lib/config";
4
4
 
5
5
  async function isSshpassInstalled(): Promise<boolean> {
6
6
  return new Promise((resolve) => {
7
- const check = spawn("command", ["-v", "sshpass"]);
8
- check.on("close", (code) => {
9
- resolve(code === 0);
7
+ const isWin = process.platform === "win32";
8
+ const checkCommand = isWin ? "where sshpass" : "command -v sshpass";
9
+
10
+ exec(checkCommand, (error) => {
11
+ resolve(!error);
10
12
  });
11
13
  });
12
14
  }
@@ -1,4 +1,4 @@
1
- import * as chalk from "chalk";
1
+ import chalk from "chalk";
2
2
  import Table from "cli-table3";
3
3
  import { ConfigManager } from "../lib/config";
4
4
 
@@ -7,7 +7,7 @@ export async function listCommand(configManager: ConfigManager) {
7
7
 
8
8
  if (connections.length === 0) {
9
9
  console.log(
10
- chalk.yellow('No connections found. Use "sshc add" to add one.'),
10
+ chalk.yellow('No connections found. Use "zssh add" to add one.'),
11
11
  );
12
12
  return;
13
13
  }
@@ -1,4 +1,4 @@
1
- import * as chalk from "chalk";
1
+ import chalk from "chalk";
2
2
  import inquirer from "inquirer";
3
3
  import { ConfigManager } from "../lib/config";
4
4
 
@@ -1,5 +1,5 @@
1
1
  import axios from "axios";
2
- import * as chalk from "chalk";
2
+ import chalk from "chalk";
3
3
  import inquirer from "inquirer";
4
4
  import { ConfigManager } from "../lib/config";
5
5
 
@@ -35,7 +35,7 @@ export async function saveCommand(configManager: ConfigManager) {
35
35
  }
36
36
 
37
37
  const files = {
38
- "sshc_config.lock": {
38
+ "zssh_config.lock": {
39
39
  content: encryptedContent,
40
40
  },
41
41
  };
@@ -58,7 +58,7 @@ export async function saveCommand(configManager: ConfigManager) {
58
58
  const response = await axios.post(
59
59
  `${GITHUB_API}/gists`,
60
60
  {
61
- description: "SSHC Encrypted Config",
61
+ description: "ZSSH Encrypted Config",
62
62
  public: false,
63
63
  files,
64
64
  },
@@ -102,9 +102,9 @@ export async function syncCommand(configManager: ConfigManager) {
102
102
  headers: { Authorization: `token ${token}` },
103
103
  });
104
104
 
105
- const file = response.data.files["sshc_config.lock"];
105
+ const file = response.data.files["zssh_config.lock"];
106
106
  if (!file) {
107
- console.log(chalk.red("Invalid Gist: sshc_config.lock not found."));
107
+ console.log(chalk.red("Invalid Gist: zssh_config.lock not found."));
108
108
  return;
109
109
  }
110
110
 
package/src/index.ts CHANGED
@@ -1,15 +1,26 @@
1
1
  #!/usr/bin/env node
2
- import * as chalk from "chalk";
2
+ import chalk from "chalk";
3
3
  import { Command } from "commander";
4
4
  import inquirer from "inquirer";
5
5
  import { ConfigManager } from "./lib/config";
6
+ import {
7
+ clearSession,
8
+ getSessionPassword,
9
+ saveSessionPassword,
10
+ } from "./lib/session";
6
11
 
7
12
  const program = new Command();
8
13
  const configManager = new ConfigManager();
9
14
 
10
15
  async function promptForMasterPassword(
11
16
  isNew: boolean = false,
17
+ forcePrompt: boolean = false,
12
18
  ): Promise<string> {
19
+ if (!isNew && !forcePrompt) {
20
+ const sessionPass = await getSessionPassword();
21
+ if (sessionPass) return sessionPass;
22
+ }
23
+
13
24
  const { password } = await inquirer.prompt([
14
25
  {
15
26
  type: "password",
@@ -23,13 +34,15 @@ async function promptForMasterPassword(
23
34
  return password;
24
35
  }
25
36
 
26
- async function initConfig() {
37
+ async function initConfig(requirePassword = false) {
27
38
  if (configManager.isConfigExists()) {
28
- const password = await promptForMasterPassword();
39
+ const password = await promptForMasterPassword(false, requirePassword);
29
40
  configManager.setMasterKey(password);
30
41
  try {
31
42
  configManager.load();
43
+ await saveSessionPassword(password);
32
44
  } catch (e) {
45
+ await clearSession();
33
46
  console.error(chalk.red("Invalid password or corrupted config."));
34
47
  process.exit(1);
35
48
  }
@@ -52,6 +65,7 @@ async function initConfig() {
52
65
 
53
66
  configManager.setMasterKey(password);
54
67
  configManager.save(); // Create empty encrypted file
68
+ await saveSessionPassword(password);
55
69
  console.log(chalk.green("Config initialized!"));
56
70
  }
57
71
  }
@@ -113,7 +127,7 @@ program
113
127
  .command("save")
114
128
  .description("Save config to GitHub Gist")
115
129
  .action(async () => {
116
- await initConfig();
130
+ await initConfig(true);
117
131
  const { saveCommand } = await import("./commands/sync");
118
132
  await saveCommand(configManager);
119
133
  });
@@ -122,7 +136,7 @@ program
122
136
  .command("sync")
123
137
  .description("Sync config from GitHub Gist")
124
138
  .action(async () => {
125
- await initConfig();
139
+ await initConfig(true);
126
140
  const { syncCommand } = await import("./commands/sync");
127
141
  await syncCommand(configManager);
128
142
  });
package/src/lib/config.ts CHANGED
@@ -4,7 +4,7 @@ import * as path from "path";
4
4
  import { decrypt, encrypt } from "./crypto";
5
5
  import { Config, SSHConnection } from "./types";
6
6
 
7
- const CONFIG_PATH = path.join(os.homedir(), ".sshc.json");
7
+ const CONFIG_PATH = path.join(os.homedir(), ".zssh.json");
8
8
 
9
9
  export class ConfigManager {
10
10
  private config: Config;
@@ -150,7 +150,8 @@ export class ConfigManager {
150
150
 
151
151
  public getConnection(idOrAlias: string): SSHConnection | undefined {
152
152
  return this.config.connections.find(
153
- (c) => c.id === idOrAlias || c.alias === idOrAlias,
153
+ (c) =>
154
+ c.id === idOrAlias || c.alias.toLowerCase() === idOrAlias.toLowerCase(),
154
155
  );
155
156
  }
156
157
 
@@ -0,0 +1,49 @@
1
+ import * as keytar from "keytar";
2
+
3
+ const SERVICE_NAME = "zssh";
4
+ const ACCOUNT_NAME = "session";
5
+ const TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
6
+
7
+ interface SessionData {
8
+ password: string;
9
+ expiresAt: number;
10
+ }
11
+
12
+ export async function getSessionPassword(): Promise<string | null> {
13
+ try {
14
+ const dataString = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);
15
+ if (!dataString) return null;
16
+
17
+ const data: SessionData = JSON.parse(dataString);
18
+ if (Date.now() > data.expiresAt) {
19
+ await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
20
+ return null;
21
+ }
22
+
23
+ // Refresh expiration smoothly
24
+ data.expiresAt = Date.now() + TIMEOUT_MS;
25
+ await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(data));
26
+
27
+ return data.password;
28
+ } catch (e) {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ export async function saveSessionPassword(password: string): Promise<void> {
34
+ try {
35
+ const data: SessionData = {
36
+ password,
37
+ expiresAt: Date.now() + TIMEOUT_MS,
38
+ };
39
+ await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(data));
40
+ } catch (e) {
41
+ // safely ignore keychain errors
42
+ }
43
+ }
44
+
45
+ export async function clearSession(): Promise<void> {
46
+ try {
47
+ await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
48
+ } catch (e) {}
49
+ }