ezpm2gui 1.3.2 → 1.4.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.
Files changed (37) hide show
  1. package/README.md +12 -11
  2. package/dist/server/config/cron-jobs.json +1 -18
  3. package/dist/server/config/remote-connections.json +1 -20
  4. package/dist/server/daemon/ezpm2gui.err.log +414 -0
  5. package/dist/server/daemon/ezpm2gui.exe +0 -0
  6. package/dist/server/daemon/ezpm2gui.exe.config +6 -0
  7. package/dist/server/daemon/ezpm2gui.out.log +289 -0
  8. package/dist/server/daemon/ezpm2gui.wrapper.log +172 -0
  9. package/dist/server/daemon/ezpm2gui.xml +32 -0
  10. package/dist/server/index.js +72 -86
  11. package/dist/server/routes/logStreaming.js +20 -13
  12. package/dist/server/routes/modules.js +89 -69
  13. package/dist/server/routes/remoteConnections.js +19 -40
  14. package/dist/server/utils/encryption.js +0 -12
  15. package/dist/server/utils/pm2-connection.d.ts +1 -1
  16. package/dist/server/utils/pm2-connection.js +1 -3
  17. package/dist/server/utils/remote-connection.d.ts +18 -3
  18. package/dist/server/utils/remote-connection.js +100 -79
  19. package/package.json +4 -2
  20. package/src/client/build/asset-manifest.json +6 -6
  21. package/src/client/build/index.html +1 -1
  22. package/src/client/build/static/css/main.c506cba5.css +5 -0
  23. package/src/client/build/static/css/main.c506cba5.css.map +1 -0
  24. package/src/client/build/static/js/main.5278cddd.js +3 -0
  25. package/src/client/build/static/js/main.5278cddd.js.map +1 -0
  26. package/dist/server/config/cron-scripts/6d8d5e1d-2bc8-463f-82a6-6c294f2b9dbe.sh +0 -2
  27. package/dist/server/config/project-configs.json +0 -236
  28. package/dist/server/logs/deployment.log +0 -12
  29. package/dist/server/utils/dialog.d.ts +0 -1
  30. package/dist/server/utils/dialog.js +0 -16
  31. package/dist/server/utils/upload.d.ts +0 -3
  32. package/dist/server/utils/upload.js +0 -39
  33. package/src/client/build/static/css/main.d46bc75c.css +0 -5
  34. package/src/client/build/static/css/main.d46bc75c.css.map +0 -1
  35. package/src/client/build/static/js/main.b0e1c9b1.js +0 -3
  36. package/src/client/build/static/js/main.b0e1c9b1.js.map +0 -1
  37. /package/src/client/build/static/js/{main.b0e1c9b1.js.LICENSE.txt → main.5278cddd.js.LICENSE.txt} +0 -0
@@ -29,7 +29,16 @@ function createServer() {
29
29
  cors: {
30
30
  origin: '*',
31
31
  methods: ['GET', 'POST']
32
- }
32
+ },
33
+ // Increase ping timeout to prevent false positive disconnections
34
+ pingTimeout: 10000, // How long to wait for a pong response (default: 5000ms)
35
+ pingInterval: 25000, // How often to send ping packets (default: 25000ms)
36
+ // Allow reconnection attempts
37
+ allowEIO3: true,
38
+ // Transport configuration
39
+ transports: ['websocket', 'polling'],
40
+ // Upgrade timeout
41
+ upgradeTimeout: 10000
33
42
  });
34
43
  // Configure middleware
35
44
  app.use(express_1.default.json());
@@ -73,42 +82,36 @@ function createServer() {
73
82
  }
74
83
  });
75
84
  // Action endpoints (start, stop, restart, delete)
76
- app.post('/api/process/:id/:action', (req, res) => {
85
+ app.post('/api/process/:id/:action', async (req, res) => {
77
86
  const { id, action } = req.params;
78
- pm2_1.default.connect((err) => {
79
- if (err) {
80
- console.error(err);
81
- res.status(500).json({ error: 'Failed to connect to PM2' });
82
- return;
83
- }
84
- const processAction = (actionName, cb) => {
85
- switch (actionName) {
87
+ const validActions = ['start', 'stop', 'restart', 'delete'];
88
+ if (!validActions.includes(action)) {
89
+ res.status(400).json({ error: 'Invalid action' });
90
+ return;
91
+ }
92
+ try {
93
+ await (0, pm2_connection_1.executePM2Command)((callback) => {
94
+ switch (action) {
86
95
  case 'start':
87
- pm2_1.default.start(id, cb);
96
+ pm2_1.default.start(id, callback);
88
97
  break;
89
98
  case 'stop':
90
- pm2_1.default.stop(id, cb);
99
+ pm2_1.default.stop(id, callback);
91
100
  break;
92
101
  case 'restart':
93
- pm2_1.default.restart(id, cb);
102
+ pm2_1.default.restart(id, callback);
94
103
  break;
95
104
  case 'delete':
96
- pm2_1.default.delete(id, cb);
105
+ pm2_1.default.delete(id, callback);
97
106
  break;
98
- default:
99
- cb(new Error('Invalid action'));
100
- }
101
- };
102
- processAction(action, (err) => {
103
- pm2_1.default.disconnect();
104
- if (err) {
105
- console.error(err);
106
- res.status(500).json({ error: `Failed to ${action} process` });
107
- return;
108
107
  }
109
- res.json({ success: true, message: `Process ${id} ${action} request received` });
110
108
  });
111
- });
109
+ res.json({ success: true, message: `Process ${id} ${action} request received` });
110
+ }
111
+ catch (err) {
112
+ console.error(err);
113
+ res.status(500).json({ error: `Failed to ${action} process` });
114
+ }
112
115
  });
113
116
  // Get system metrics
114
117
  app.get('/api/metrics', (req, res) => {
@@ -125,75 +128,58 @@ function createServer() {
125
128
  res.json(metrics);
126
129
  });
127
130
  // Get process logs
128
- app.get('/api/logs/:id/:type', (req, res) => {
131
+ app.get('/api/logs/:id/:type', async (req, res) => {
132
+ var _a, _b;
129
133
  const { id, type } = req.params;
130
134
  const logType = type === 'err' ? 'err' : 'out';
131
- pm2_1.default.connect((err) => {
132
- if (err) {
133
- console.error(err);
134
- res.status(500).json({ error: 'Failed to connect to PM2' });
135
+ try {
136
+ const processDesc = await (0, pm2_connection_1.executePM2Command)((callback) => {
137
+ pm2_1.default.describe(id, callback);
138
+ });
139
+ if (!processDesc || processDesc.length === 0) {
140
+ res.status(404).json({ error: 'Process not found' });
135
141
  return;
136
142
  }
137
- pm2_1.default.describe(id, (err, processDesc) => {
138
- var _a, _b;
139
- if (err || !processDesc || processDesc.length === 0) {
140
- pm2_1.default.disconnect();
141
- res.status(404).json({ error: 'Process not found' });
142
- return;
143
- }
144
- const logPath = (_b = (_a = processDesc[0]) === null || _a === void 0 ? void 0 : _a.pm2_env) === null || _b === void 0 ? void 0 : _b[`pm_${logType}_log_path`];
145
- if (!logPath) {
146
- pm2_1.default.disconnect();
147
- res.status(404).json({ error: `Log file for ${logType} not found` });
148
- return;
149
- }
150
- try {
151
- // Read the log file using Node.js fs instead of exec
152
- const fs = require('fs');
153
- let logContent = '';
154
- if (fs.existsSync(logPath)) {
155
- // Read the last part of the file (up to 10KB to avoid huge responses)
156
- const stats = fs.statSync(logPath);
157
- const fileSize = stats.size;
158
- const readSize = Math.min(fileSize, 10 * 1024); // 10KB max
159
- const position = Math.max(0, fileSize - readSize);
160
- const buffer = Buffer.alloc(readSize);
161
- const fd = fs.openSync(logPath, 'r');
162
- fs.readSync(fd, buffer, 0, readSize, position);
163
- fs.closeSync(fd);
164
- logContent = buffer.toString('utf8');
165
- }
166
- const logs = logContent.split('\n').filter((line) => line.trim() !== '');
167
- pm2_1.default.disconnect();
168
- res.json({ logs });
169
- }
170
- catch (error) {
171
- console.error(`Error reading log file: ${error}`);
172
- pm2_1.default.disconnect();
173
- res.status(500).json({ error: 'Failed to read log file' });
174
- }
175
- });
176
- });
143
+ const logPath = (_b = (_a = processDesc[0]) === null || _a === void 0 ? void 0 : _a.pm2_env) === null || _b === void 0 ? void 0 : _b[`pm_${logType}_log_path`];
144
+ if (!logPath) {
145
+ res.status(404).json({ error: `Log file for ${logType} not found` });
146
+ return;
147
+ }
148
+ const fs = require('fs');
149
+ let logContent = '';
150
+ if (fs.existsSync(logPath)) {
151
+ const stats = fs.statSync(logPath);
152
+ const fileSize = stats.size;
153
+ const readSize = Math.min(fileSize, 10 * 1024); // 10KB max
154
+ const position = Math.max(0, fileSize - readSize);
155
+ const buffer = Buffer.alloc(readSize);
156
+ const fd = fs.openSync(logPath, 'r');
157
+ fs.readSync(fd, buffer, 0, readSize, position);
158
+ fs.closeSync(fd);
159
+ logContent = buffer.toString('utf8');
160
+ }
161
+ const logs = logContent.split('\n').filter((line) => line.trim() !== '');
162
+ res.json({ logs });
163
+ }
164
+ catch (err) {
165
+ console.error(`Error reading log file: ${err}`);
166
+ res.status(500).json({ error: 'Failed to read log file' });
167
+ }
177
168
  });
178
169
  // WebSocket for real-time updates
179
170
  io.on('connection', (socket) => {
180
171
  console.log('Client connected');
181
- // Send process updates every 3 seconds
182
- const processInterval = setInterval(() => {
183
- pm2_1.default.connect((err) => {
184
- if (err) {
185
- console.error(err);
186
- return;
187
- }
188
- pm2_1.default.list((err, processList) => {
189
- pm2_1.default.disconnect();
190
- if (err) {
191
- console.error(err);
192
- return;
193
- }
194
- socket.emit('processes', processList);
172
+ // Send process updates every 3 seconds using shared pooled connection
173
+ const processInterval = setInterval(async () => {
174
+ try {
175
+ const processList = await (0, pm2_connection_1.executePM2Command)((callback) => {
176
+ pm2_1.default.list(callback);
195
177
  });
196
- });
178
+ socket.emit('processes', processList);
179
+ }
180
+ catch (err) {
181
+ console.error('Failed to list PM2 processes:', err);
182
+ }
197
183
  }, 3000);
198
184
  // Send system metrics every 2 seconds
199
185
  const metricsInterval = setInterval(() => {
@@ -38,8 +38,8 @@ const getLogStream = (io, processId, logType) => {
38
38
  tail.stdout.on('data', (data) => {
39
39
  const lines = data.toString().split('\n').filter((line) => line.trim() !== '');
40
40
  lines.forEach((line) => {
41
- // Emit log line to all connected clients
42
- io.emit('log-line', {
41
+ // Emit only to clients subscribed to this specific log stream
42
+ io.to(streamKey).emit('log-line', {
43
43
  processId,
44
44
  logType,
45
45
  line
@@ -70,17 +70,23 @@ const getRemoteLogStream = async (io, connectionId, processId) => {
70
70
  if (!connection || !connection.isConnected()) {
71
71
  throw new Error('Connection not found or not connected');
72
72
  }
73
- // Get process info to find log paths
73
+ // Get process info using the multi-path fallback so pm2 is found regardless of PATH
74
74
  console.log(`Getting process info for: ${processId}`);
75
- const processInfoResult = await connection.executeCommand(`pm2 jlist`, false); // Don't use sudo for listing
75
+ const processInfoResult = await connection.executePM2Command('jlist');
76
76
  if (processInfoResult.code !== 0) {
77
77
  throw new Error(`Failed to get process list: ${processInfoResult.stderr}`);
78
78
  }
79
+ // Extract JSON from output (pm2 jlist may prefix with non-JSON lines)
80
+ let rawOutput = processInfoResult.stdout.trim();
81
+ const startIdx = rawOutput.indexOf('[');
82
+ const endIdx = rawOutput.lastIndexOf(']') + 1;
83
+ if (startIdx !== -1 && endIdx > 0) {
84
+ rawOutput = rawOutput.substring(startIdx, endIdx);
85
+ }
79
86
  let processInfo;
80
87
  try {
81
- const processList = JSON.parse(processInfoResult.stdout);
88
+ const processList = JSON.parse(rawOutput);
82
89
  console.log(`Found ${processList.length} processes`);
83
- // Find the process by ID
84
90
  processInfo = processList.find((proc) => proc.pm_id === parseInt(processId));
85
91
  if (!processInfo) {
86
92
  throw new Error(`Process with ID ${processId} not found`);
@@ -96,10 +102,10 @@ const getRemoteLogStream = async (io, connectionId, processId) => {
96
102
  const streams = {};
97
103
  try {
98
104
  console.log(`Setting up pm2 logs stream for process: ${processName} (ID: ${processId})`);
99
- // Use pm2 logs with --lines 0 --raw to stream only new logs
100
- // Use sudo since PM2 processes are running as root
101
- const pm2LogsCommand = `pm2 logs ${processId} --lines 0 --raw`;
102
- console.log(`About to create pm2 log stream with command: ${pm2LogsCommand} (using sudo)`);
105
+ // Use the resolved pm2 invocation (cached from the jlist call above) so the
106
+ // same shell/path that successfully ran pm2 jlist is reused here.
107
+ const pm2LogsCommand = connection.buildPM2StreamCommand(`logs ${processId} --lines 0 --raw`);
108
+ console.log(`About to create pm2 log stream with command: ${pm2LogsCommand}`);
103
109
  const logStream = await connection.createLogStream(pm2LogsCommand, true);
104
110
  console.log(`Successfully created pm2 logs stream for ${processName}`);
105
111
  logStream.on('data', (data) => {
@@ -154,7 +160,8 @@ const getRemoteLogStream = async (io, connectionId, processId) => {
154
160
  cleanLine = logMatch[1];
155
161
  }
156
162
  console.log(`Emitting remote-log-line for ${connectionId}-${processId} (${logType}):`, cleanLine);
157
- io.emit('remote-log-line', {
163
+ // Emit only to clients subscribed to this specific remote log stream
164
+ io.to(streamKey).emit('remote-log-line', {
158
165
  connectionId,
159
166
  processId,
160
167
  processName,
@@ -165,7 +172,7 @@ const getRemoteLogStream = async (io, connectionId, processId) => {
165
172
  });
166
173
  logStream.on('error', (error) => {
167
174
  console.error(`Error in pm2 logs stream for ${processName}:`, error);
168
- io.emit('remote-log-error', {
175
+ io.to(streamKey).emit('remote-log-error', {
169
176
  connectionId,
170
177
  processId,
171
178
  processName,
@@ -179,7 +186,7 @@ const getRemoteLogStream = async (io, connectionId, processId) => {
179
186
  }
180
187
  catch (error) {
181
188
  console.error(`Failed to create pm2 logs stream for ${processName}:`, error);
182
- io.emit('remote-log-error', {
189
+ io.to(streamKey).emit('remote-log-error', {
183
190
  connectionId,
184
191
  processId,
185
192
  processName,
@@ -1,106 +1,126 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  const express_1 = require("express");
4
7
  const child_process_1 = require("child_process");
8
+ const pm2_1 = __importDefault(require("pm2"));
9
+ const pm2_connection_1 = require("../utils/pm2-connection");
5
10
  const router = (0, express_1.Router)();
6
- // List installed modules
7
- router.get('/', (req, res) => {
8
- const pm2Command = (0, child_process_1.spawn)('pm2', ['module:list']);
9
- let output = '';
10
- let errorOutput = '';
11
- pm2Command.stdout.on('data', (data) => {
12
- output += data.toString();
13
- });
14
- pm2Command.stderr.on('data', (data) => {
15
- errorOutput += data.toString();
16
- });
17
- pm2Command.on('close', (code) => {
11
+ // @group Utilities : Resolve pm2 binary path via shell
12
+ const runPM2CLI = (args) => new Promise((resolve, reject) => {
13
+ var _a, _b;
14
+ // Use shell so the OS resolves the global pm2 binary from PATH
15
+ const child = (0, child_process_1.exec)(`pm2 ${args}`);
16
+ let out = '';
17
+ let err = '';
18
+ (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (d) => { out += d.toString(); });
19
+ (_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (d) => { err += d.toString(); });
20
+ child.on('close', (code) => {
18
21
  if (code !== 0) {
19
- return res.status(500).json({
20
- error: 'Failed to list PM2 modules',
21
- details: errorOutput
22
- });
22
+ reject(new Error(err || `pm2 exited with code ${code}`));
23
+ }
24
+ else {
25
+ resolve({ stdout: out, stderr: err });
23
26
  }
24
- // Parse the output to get module information
27
+ });
28
+ child.on('error', (e) => reject(e));
29
+ });
30
+ // @group APIEndpoints : List installed PM2 modules
31
+ router.get('/', async (_req, res) => {
32
+ try {
33
+ // Use PM2 Node.js API — modules appear as regular processes with module metadata
34
+ const processList = await (0, pm2_connection_1.executePM2Command)((cb) => pm2_1.default.list(cb));
35
+ // PM2 modules have pm2_env.pmx_module === true
36
+ const modules = processList
37
+ .filter((proc) => { var _a; return ((_a = proc.pm2_env) === null || _a === void 0 ? void 0 : _a.pmx_module) === true; })
38
+ .map((proc) => {
39
+ var _a, _b, _c;
40
+ return ({
41
+ name: proc.name,
42
+ version: ((_a = proc.pm2_env) === null || _a === void 0 ? void 0 : _a.version) || ((_b = proc.pm2_env) === null || _b === void 0 ? void 0 : _b.MODULE_VERSION) || 'N/A',
43
+ status: ((_c = proc.pm2_env) === null || _c === void 0 ? void 0 : _c.status) || 'unknown',
44
+ pid: proc.pid,
45
+ pm_id: proc.pm_id,
46
+ });
47
+ });
48
+ res.json(modules);
49
+ }
50
+ catch (error) {
51
+ // Fallback: parse pm2 module:list CLI output
25
52
  try {
26
- const moduleLines = output.split('\n').filter(line => line.includes('│') && !line.includes('Module') && line.trim() !== '');
27
- const modules = moduleLines.map(line => {
53
+ const { stdout } = await runPM2CLI('module:list');
54
+ const moduleLines = stdout
55
+ .split('\n')
56
+ .filter(line => line.includes('│') && !line.includes('Module') && line.trim() !== '');
57
+ const modules = moduleLines
58
+ .map(line => {
28
59
  const parts = line.split('│').map(part => part.trim()).filter(Boolean);
29
60
  if (parts.length >= 3) {
30
- return {
31
- name: parts[0],
32
- version: parts[1],
33
- status: parts[2]
34
- };
61
+ return { name: parts[0], version: parts[1], status: parts[2] };
35
62
  }
36
63
  return null;
37
- }).filter(Boolean);
64
+ })
65
+ .filter(Boolean);
38
66
  res.json(modules);
39
67
  }
40
- catch (error) {
68
+ catch (fallbackError) {
41
69
  res.status(500).json({
42
- error: 'Failed to parse PM2 modules',
43
- details: error
70
+ error: 'Failed to list PM2 modules',
71
+ details: fallbackError.message
44
72
  });
45
73
  }
46
- });
74
+ }
47
75
  });
48
- // Install a module
49
- router.post('/install', (req, res) => {
76
+ // @group APIEndpoints : Install a PM2 module
77
+ router.post('/install', async (req, res) => {
50
78
  const { moduleName } = req.body;
51
79
  if (!moduleName) {
52
80
  return res.status(400).json({ error: 'Module name is required' });
53
81
  }
54
- const pm2Command = (0, child_process_1.spawn)('pm2', ['install', moduleName]);
55
- let output = '';
56
- let errorOutput = '';
57
- pm2Command.stdout.on('data', (data) => {
58
- output += data.toString();
59
- });
60
- pm2Command.stderr.on('data', (data) => {
61
- errorOutput += data.toString();
62
- });
63
- pm2Command.on('close', (code) => {
64
- if (code !== 0) {
65
- return res.status(500).json({
66
- error: `Failed to install module: ${moduleName}`,
67
- details: errorOutput
68
- });
69
- }
82
+ // Basic validation only allow alphanumeric, @, /, -, .
83
+ if (!/^[@a-zA-Z0-9/_\-.]+$/.test(moduleName)) {
84
+ return res.status(400).json({ error: 'Invalid module name' });
85
+ }
86
+ try {
87
+ const { stdout } = await runPM2CLI(`install ${moduleName}`);
70
88
  res.json({
71
89
  success: true,
72
90
  message: `Successfully installed module: ${moduleName}`,
73
- details: output
91
+ details: stdout
74
92
  });
75
- });
93
+ }
94
+ catch (error) {
95
+ res.status(500).json({
96
+ error: `Failed to install module: ${moduleName}`,
97
+ details: error.message
98
+ });
99
+ }
76
100
  });
77
- // Uninstall a module
78
- router.delete('/:moduleName', (req, res) => {
101
+ // @group APIEndpoints : Uninstall a PM2 module
102
+ router.delete('/:moduleName', async (req, res) => {
79
103
  const { moduleName } = req.params;
80
104
  if (!moduleName) {
81
105
  return res.status(400).json({ error: 'Module name is required' });
82
106
  }
83
- const pm2Command = (0, child_process_1.spawn)('pm2', ['uninstall', moduleName]);
84
- let output = '';
85
- let errorOutput = '';
86
- pm2Command.stdout.on('data', (data) => {
87
- output += data.toString();
88
- });
89
- pm2Command.stderr.on('data', (data) => {
90
- errorOutput += data.toString();
91
- });
92
- pm2Command.on('close', (code) => {
93
- if (code !== 0) {
94
- return res.status(500).json({
95
- error: `Failed to uninstall module: ${moduleName}`,
96
- details: errorOutput
97
- });
98
- }
107
+ // Basic validation
108
+ if (!/^[@a-zA-Z0-9/_\-.]+$/.test(moduleName)) {
109
+ return res.status(400).json({ error: 'Invalid module name' });
110
+ }
111
+ try {
112
+ const { stdout } = await runPM2CLI(`uninstall ${moduleName}`);
99
113
  res.json({
100
114
  success: true,
101
115
  message: `Successfully uninstalled module: ${moduleName}`,
102
- details: output
116
+ details: stdout
103
117
  });
104
- });
118
+ }
119
+ catch (error) {
120
+ res.status(500).json({
121
+ error: `Failed to uninstall module: ${moduleName}`,
122
+ details: error.message
123
+ });
124
+ }
105
125
  });
106
126
  exports.default = router;
@@ -20,14 +20,11 @@ router.post('/:connectionId/connect', async (req, res) => {
20
20
  error: 'Connection not found'
21
21
  });
22
22
  }
23
- // Connect to the remote server
23
+ // Establish the SSH connection only — PM2 detection happens lazily
24
+ // when processes are first requested, avoiding a slow multi-command
25
+ // pre-check that causes browser timeouts on the connect button.
24
26
  await connection.connect();
25
- // Check if PM2 is installed
26
- const isPM2Installed = await connection.checkPM2Installation();
27
- res.json({
28
- success: true,
29
- isPM2Installed
30
- });
27
+ res.json({ success: true });
31
28
  }
32
29
  catch (error) {
33
30
  console.error('Connection error:', error);
@@ -481,29 +478,36 @@ router.get('/:connectionId/logs/:processId', async (req, res) => {
481
478
  success: false,
482
479
  error: 'Connection not established'
483
480
  });
484
- } // Get log paths from PM2 process info
485
- const processInfoResult = await connection.executeCommand(`pm2 show ${processId} --json`);
481
+ }
482
+ // Get log paths from PM2 process info — use the PATH-fallback executor so
483
+ // pm2 is found regardless of the remote shell environment (nvm, npm-global, etc.)
484
+ const processInfoResult = await connection.executePM2Command('jlist');
486
485
  if (processInfoResult.code !== 0) {
487
486
  return res.status(500).json({
488
487
  success: false,
489
- error: 'Failed to get process info'
488
+ error: 'Failed to get PM2 process list'
490
489
  });
491
490
  }
492
491
  let processInfo;
493
492
  try {
494
- processInfo = JSON.parse(processInfoResult.stdout);
495
- if (!Array.isArray(processInfo) || processInfo.length === 0) {
493
+ let raw = processInfoResult.stdout.trim();
494
+ const start = raw.indexOf('[');
495
+ const end = raw.lastIndexOf(']') + 1;
496
+ if (start !== -1 && end > 0)
497
+ raw = raw.substring(start, end);
498
+ const processList = JSON.parse(raw);
499
+ processInfo = processList.find((p) => p.pm_id === parseInt(processId, 10) || p.name === processId);
500
+ if (!processInfo) {
496
501
  return res.status(404).json({
497
502
  success: false,
498
503
  error: 'Process not found'
499
504
  });
500
505
  }
501
- processInfo = processInfo[0];
502
506
  }
503
507
  catch (parseError) {
504
508
  return res.status(500).json({
505
509
  success: false,
506
- error: 'Failed to parse process info'
510
+ error: 'Failed to parse PM2 process list'
507
511
  });
508
512
  }
509
513
  const outLogPath = (_a = processInfo.pm2_env) === null || _a === void 0 ? void 0 : _a.pm_out_log_path;
@@ -561,7 +565,7 @@ router.get('/connections', async (req, res) => {
561
565
  host: conn.host,
562
566
  port: conn.port,
563
567
  username: conn.username,
564
- isConnected: conn.isConnected(),
568
+ connected: conn.isConnected(),
565
569
  isPM2Installed: conn.isPM2Installed
566
570
  }));
567
571
  res.json(connectionsList);
@@ -711,29 +715,4 @@ router.post('/:connectionId/install-pm2', async (req, res) => {
711
715
  });
712
716
  }
713
717
  });
714
- /**
715
- * Delete a connection configuration
716
- * DELETE /api/remote/connections/:connectionId
717
- */
718
- router.delete('/connections/:connectionId', async (req, res) => {
719
- try {
720
- const { connectionId } = req.params;
721
- // Disconnect if connected
722
- await remote_connection_1.remoteConnectionManager.closeConnection(connectionId);
723
- // Delete the connection from the manager
724
- const success = remote_connection_1.remoteConnectionManager.deleteConnection(connectionId);
725
- if (success) {
726
- res.json({ success: true, message: 'Connection deleted' });
727
- }
728
- else {
729
- res.status(404).json({ success: false, error: 'Connection not found' });
730
- }
731
- }
732
- catch (error) {
733
- res.status(500).json({
734
- success: false,
735
- error: `Server error: ${error.message}`
736
- });
737
- }
738
- });
739
718
  exports.default = router;
@@ -44,29 +44,17 @@ function encrypt(text) {
44
44
  function decrypt(encryptedText) {
45
45
  if (!encryptedText)
46
46
  return '';
47
- // Special handling for manually entered passwords in the config file
48
- if (encryptedText === '11342b0ca35d70c17955d874e5d4b0a26547521f705a6f74320b5d7bfeb56369') {
49
- console.log('Using hardcoded credential for specific password hash');
50
- return 'test1234';
51
- }
52
47
  try {
53
- console.log(`Attempting to decrypt: ${encryptedText.substring(0, 10)}...`);
54
48
  // Ensure key and IV are the correct length
55
49
  const key = crypto_1.default.createHash('sha256').update(String(ENCRYPTION_KEY)).digest('base64').slice(0, 32);
56
50
  const iv = crypto_1.default.createHash('sha256').update(String(ENCRYPTION_IV)).digest('base64').slice(0, 16);
57
51
  const decipher = crypto_1.default.createDecipheriv(ALGORITHM, key, iv);
58
52
  let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
59
53
  decrypted += decipher.final('utf8');
60
- console.log('Decryption successful');
61
54
  return decrypted;
62
55
  }
63
56
  catch (error) {
64
57
  console.error('Decryption error:', error);
65
- // For development only - return a value even if decryption fails
66
- if (encryptedText && encryptedText.length > 10) {
67
- console.log('Returning fallback credential for development');
68
- return 'test1234';
69
- }
70
58
  return '';
71
59
  }
72
60
  }
@@ -13,4 +13,4 @@ export declare const disconnectFromPM2: () => Promise<void>;
13
13
  * This will connect to PM2 if needed, run the command,
14
14
  * and properly handle the result
15
15
  */
16
- export declare const executePM2Command: <T>(command: (callback: (err: Error | null, result?: T) => void) => void) => Promise<T>;
16
+ export declare const executePM2Command: <T = any>(command: (callback: (err: Error | null, result?: T) => void) => void) => Promise<T>;
@@ -103,6 +103,7 @@ exports.disconnectFromPM2 = disconnectFromPM2;
103
103
  * This will connect to PM2 if needed, run the command,
104
104
  * and properly handle the result
105
105
  */
106
+ // @group ConnectionPool : Execute a PM2 command using the shared pooled connection
106
107
  const executePM2Command = async (command) => {
107
108
  try {
108
109
  await (0, exports.connectToPM2)();
@@ -111,9 +112,6 @@ const executePM2Command = async (command) => {
111
112
  if (err) {
112
113
  reject(err);
113
114
  }
114
- else if (result === undefined) {
115
- reject(new Error('PM2 command returned undefined result'));
116
- }
117
115
  else {
118
116
  resolve(result);
119
117
  }