@abhiseck/zssh 1.0.0 → 1.2.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.
@@ -1,47 +1,13 @@
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
- const uuid_1 = require("uuid");
43
9
  async function addCommand(configManager) {
44
- console.log(chalk.blue("Adding new SSH connection..."));
10
+ console.log(chalk_1.default.blue("Adding new SSH connection..."));
45
11
  const answers = await inquirer_1.default.prompt([
46
12
  {
47
13
  type: "input",
@@ -93,8 +59,12 @@ async function addCommand(configManager) {
93
59
  message: "Description (optional):",
94
60
  },
95
61
  ]);
62
+ const connections = configManager.getConnections();
63
+ const nextId = connections.length > 0
64
+ ? (Math.max(...connections.map((c) => parseInt(c.id) || 0)) + 1).toString()
65
+ : "1";
96
66
  const connection = {
97
- id: (0, uuid_1.v4)().split("-")[0], // Short ID
67
+ id: nextId,
98
68
  alias: answers.alias,
99
69
  host: answers.host,
100
70
  port: parseInt(answers.port),
@@ -105,8 +75,8 @@ async function addCommand(configManager) {
105
75
  createdAt: new Date().toISOString(),
106
76
  };
107
77
  configManager.addConnection(connection);
108
- console.log(chalk.green(`\nConnection added successfully!`));
109
- console.log(`ID: ${chalk.cyan(connection.id)}`);
78
+ console.log(chalk_1.default.green(`\nConnection added successfully!`));
79
+ console.log(`ID: ${chalk_1.default.cyan(connection.id)}`);
110
80
  if (connection.alias)
111
- console.log(`Alias: ${chalk.cyan(connection.alias)}`);
81
+ console.log(`Alias: ${chalk_1.default.cyan(connection.alias)}`);
112
82
  }
@@ -1,56 +1,31 @@
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");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const ssh2_1 = require("ssh2");
39
13
  async function isSshpassInstalled() {
40
14
  return new Promise((resolve) => {
41
- const check = (0, child_process_1.spawn)("command", ["-v", "sshpass"]);
42
- check.on("close", (code) => {
43
- resolve(code === 0);
15
+ const isWin = process.platform === "win32";
16
+ const checkCommand = isWin ? "where sshpass" : "command -v sshpass";
17
+ (0, child_process_1.exec)(checkCommand, (error) => {
18
+ resolve(!error);
44
19
  });
45
20
  });
46
21
  }
47
22
  async function connectCommand(configManager, idOrAlias) {
48
23
  const conn = configManager.getConnection(idOrAlias);
49
24
  if (!conn) {
50
- console.log(chalk.red(`Connection not found: ${idOrAlias}`));
25
+ console.log(chalk_1.default.red(`Connection not found: ${idOrAlias}`));
51
26
  return;
52
27
  }
53
- console.log(chalk.blue(`Connecting to ${conn.alias || conn.id} (${conn.username}@${conn.host})...`));
28
+ console.log(chalk_1.default.blue(`Connecting to ${conn.alias || conn.id} (${conn.username}@${conn.host})...`));
54
29
  const args = [];
55
30
  // Port
56
31
  if (conn.port) {
@@ -58,7 +33,50 @@ async function connectCommand(configManager, idOrAlias) {
58
33
  }
59
34
  // Identity file
60
35
  if (conn.privateKeyPath) {
61
- args.push("-i", conn.privateKeyPath);
36
+ let keyPath = conn.privateKeyPath;
37
+ if (keyPath.toLowerCase().endsWith(".ppk")) {
38
+ try {
39
+ let realPath = keyPath;
40
+ if (realPath.startsWith("~/")) {
41
+ realPath = path_1.default.join(os_1.default.homedir(), realPath.slice(2));
42
+ }
43
+ const keyData = fs_1.default.readFileSync(realPath);
44
+ const parsedKeys = ssh2_1.utils.parseKey(keyData);
45
+ if (parsedKeys instanceof Error) {
46
+ console.log(chalk_1.default.red(`Failed to parse PPK file: ${parsedKeys.message}`));
47
+ return;
48
+ }
49
+ const parsedKey = Array.isArray(parsedKeys)
50
+ ? parsedKeys[0]
51
+ : parsedKeys;
52
+ const tempKeyPath = path_1.default.join(os_1.default.tmpdir(), `sshc_key_${conn.id}.pem`);
53
+ fs_1.default.writeFileSync(tempKeyPath, parsedKey.getPrivatePEM(), {
54
+ mode: 0o600,
55
+ });
56
+ keyPath = tempKeyPath;
57
+ const cleanUp = () => {
58
+ try {
59
+ if (fs_1.default.existsSync(tempKeyPath))
60
+ fs_1.default.unlinkSync(tempKeyPath);
61
+ }
62
+ catch (e) { }
63
+ };
64
+ process.on("exit", cleanUp);
65
+ process.on("SIGINT", () => {
66
+ cleanUp();
67
+ process.exit(0);
68
+ });
69
+ process.on("SIGTERM", () => {
70
+ cleanUp();
71
+ process.exit(0);
72
+ });
73
+ }
74
+ catch (err) {
75
+ console.log(chalk_1.default.red(`Error processing PPK file: ${err.message}`));
76
+ return;
77
+ }
78
+ }
79
+ args.push("-i", keyPath);
62
80
  }
63
81
  // Destination
64
82
  args.push(`${conn.username}@${conn.host}`);
@@ -77,9 +95,9 @@ async function connectCommand(configManager, idOrAlias) {
77
95
  finalArgs = ["-e", "ssh", ...args]; // -e means take password from env
78
96
  }
79
97
  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.'));
98
+ console.log(chalk_1.default.yellow('Warning: "sshpass" is not installed.'));
99
+ console.log(chalk_1.default.yellow("Cannot auto-fill password. You will be prompted by SSH."));
100
+ console.log(chalk_1.default.dim('Install sshpass (e.g. "brew install sshpass" on Mac) to enable auto-login.'));
83
101
  }
84
102
  }
85
103
  const child = (0, child_process_1.spawn)(command, finalArgs, {
@@ -88,10 +106,10 @@ async function connectCommand(configManager, idOrAlias) {
88
106
  });
89
107
  child.on("close", (code) => {
90
108
  if (code !== 0) {
91
- console.log(chalk.red(`\nSSH session ended with code ${code}`));
109
+ console.log(chalk_1.default.red(`\nSSH session ended with code ${code}`));
92
110
  }
93
111
  else {
94
- console.log(chalk.green("\nDisconnected."));
112
+ console.log(chalk_1.default.green("\nDisconnected."));
95
113
  }
96
114
  });
97
115
  }
@@ -1,57 +1,25 @@
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"),
22
+ chalk_1.default.cyan("Description"),
55
23
  ],
56
24
  style: { head: [], border: [] },
57
25
  });
@@ -62,6 +30,7 @@ async function listCommand(configManager) {
62
30
  conn.host,
63
31
  conn.username,
64
32
  conn.port,
33
+ conn.description || "",
65
34
  ]);
66
35
  });
67
36
  console.log(table.toString());
@@ -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": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -1,6 +1,5 @@
1
- import * as chalk from "chalk";
1
+ import chalk from "chalk";
2
2
  import inquirer from "inquirer";
3
- import { v4 as uuidv4 } from "uuid";
4
3
  import { ConfigManager } from "../lib/config";
5
4
  import { SSHConnection } from "../lib/types";
6
5
 
@@ -60,8 +59,16 @@ export async function addCommand(configManager: ConfigManager) {
60
59
  },
61
60
  ]);
62
61
 
62
+ const connections = configManager.getConnections();
63
+ const nextId =
64
+ connections.length > 0
65
+ ? (
66
+ Math.max(...connections.map((c) => parseInt(c.id) || 0)) + 1
67
+ ).toString()
68
+ : "1";
69
+
63
70
  const connection: SSHConnection = {
64
- id: uuidv4().split("-")[0], // Short ID
71
+ id: nextId,
65
72
  alias: answers.alias,
66
73
  host: answers.host,
67
74
  port: parseInt(answers.port),
@@ -1,12 +1,18 @@
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
+ import fs from "fs";
4
+ import os from "os";
5
+ import path from "path";
6
+ import { utils } from "ssh2";
3
7
  import { ConfigManager } from "../lib/config";
4
8
 
5
9
  async function isSshpassInstalled(): Promise<boolean> {
6
10
  return new Promise((resolve) => {
7
- const check = spawn("command", ["-v", "sshpass"]);
8
- check.on("close", (code) => {
9
- resolve(code === 0);
11
+ const isWin = process.platform === "win32";
12
+ const checkCommand = isWin ? "where sshpass" : "command -v sshpass";
13
+
14
+ exec(checkCommand, (error) => {
15
+ resolve(!error);
10
16
  });
11
17
  });
12
18
  }
@@ -37,7 +43,53 @@ export async function connectCommand(
37
43
 
38
44
  // Identity file
39
45
  if (conn.privateKeyPath) {
40
- args.push("-i", conn.privateKeyPath);
46
+ let keyPath = conn.privateKeyPath;
47
+ if (keyPath.toLowerCase().endsWith(".ppk")) {
48
+ try {
49
+ let realPath = keyPath;
50
+ if (realPath.startsWith("~/")) {
51
+ realPath = path.join(os.homedir(), realPath.slice(2));
52
+ }
53
+ const keyData = fs.readFileSync(realPath);
54
+ const parsedKeys = utils.parseKey(keyData);
55
+
56
+ if (parsedKeys instanceof Error) {
57
+ console.log(
58
+ chalk.red(`Failed to parse PPK file: ${parsedKeys.message}`),
59
+ );
60
+ return;
61
+ }
62
+
63
+ const parsedKey = Array.isArray(parsedKeys)
64
+ ? parsedKeys[0]
65
+ : parsedKeys;
66
+ const tempKeyPath = path.join(os.tmpdir(), `sshc_key_${conn.id}.pem`);
67
+
68
+ fs.writeFileSync(tempKeyPath, parsedKey.getPrivatePEM(), {
69
+ mode: 0o600,
70
+ });
71
+ keyPath = tempKeyPath;
72
+
73
+ const cleanUp = () => {
74
+ try {
75
+ if (fs.existsSync(tempKeyPath)) fs.unlinkSync(tempKeyPath);
76
+ } catch (e) {}
77
+ };
78
+ process.on("exit", cleanUp);
79
+ process.on("SIGINT", () => {
80
+ cleanUp();
81
+ process.exit(0);
82
+ });
83
+ process.on("SIGTERM", () => {
84
+ cleanUp();
85
+ process.exit(0);
86
+ });
87
+ } catch (err: any) {
88
+ console.log(chalk.red(`Error processing PPK file: ${err.message}`));
89
+ return;
90
+ }
91
+ }
92
+ args.push("-i", keyPath);
41
93
  }
42
94
 
43
95
  // Destination
@@ -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
 
@@ -19,6 +19,7 @@ export async function listCommand(configManager: ConfigManager) {
19
19
  chalk.cyan("Host"),
20
20
  chalk.cyan("User"),
21
21
  chalk.cyan("Port"),
22
+ chalk.cyan("Description"),
22
23
  ],
23
24
  style: { head: [], border: [] },
24
25
  });
@@ -30,6 +31,7 @@ export async function listCommand(configManager: ConfigManager) {
30
31
  conn.host,
31
32
  conn.username,
32
33
  conn.port,
34
+ conn.description || "",
33
35
  ]);
34
36
  });
35
37
 
@@ -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
 
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
@@ -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
+ }
package/test-ppk.js ADDED
@@ -0,0 +1,4 @@
1
+ const fs = require('fs');
2
+ const { utils } = require('ssh2');
3
+ // just check if utils.parseKey exists
4
+ console.log(typeof utils.parseKey);