pomitu 1.2.0 → 1.3.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
@@ -98,7 +98,7 @@ If you want to contribute to Pomitu or run it in development mode:
98
98
  Dev installation test:
99
99
 
100
100
  ```sh
101
- npm run build && npm pack && npm install -g pomitu-1.2.0.tgz && rm pomitu-1.2.0.tgz
101
+ npm run build && npm pack && npm install -g pomitu-1.3.0.tgz && rm pomitu-1.3.0.tgz
102
102
  ```
103
103
 
104
104
  ### Linting
@@ -4,19 +4,17 @@ export const flush = new Command('flush')
4
4
  .description('flush logs')
5
5
  .argument('[name]', 'name of the app whose logs you want to flush')
6
6
  .action((name) => {
7
- try {
8
- const logManager = new LogManager();
9
- const flushedFiles = logManager.flushLogs(name);
10
- if (flushedFiles.length === 0) {
11
- console.log('No log files found to flush');
7
+ try {
8
+ const logManager = new LogManager();
9
+ const flushedFiles = logManager.flushLogs(name);
10
+ if (flushedFiles.length === 0) {
11
+ console.log('No log files found to flush');
12
+ } else {
13
+ console.log('Logs flushed');
14
+ }
15
+ } catch (error) {
16
+ const err = error;
17
+ console.error(`Error: ${err.message}`);
18
+ process.exit(1);
12
19
  }
13
- else {
14
- console.log('Logs flushed');
15
- }
16
- }
17
- catch (error) {
18
- const err = error;
19
- console.error(`Error: ${err.message}`);
20
- process.exit(1);
21
- }
22
- });
20
+ });
@@ -3,26 +3,24 @@ import { ProcessManager } from '../services/index.js';
3
3
  export const ls = new Command('ls')
4
4
  .description('list all running apps')
5
5
  .action(() => {
6
- try {
7
- const processManager = new ProcessManager();
8
- const processes = processManager.listRunningProcesses();
9
- if (processes.length === 0) {
10
- console.log('No running processes found');
11
- return;
12
- }
13
- console.log('Running processes:');
14
- for (const process of processes) {
15
- if (process.isRunning) {
16
- console.log(`- ${process.name} (pid: ${process.pid})`);
6
+ try {
7
+ const processManager = new ProcessManager();
8
+ const processes = processManager.listRunningProcesses();
9
+ if (processes.length === 0) {
10
+ console.log('No running processes found');
11
+ return;
17
12
  }
18
- else {
19
- console.warn(`- ${process.name} (pid: ${process.pid}) is not running`);
13
+ console.log('Running processes:');
14
+ for (const process of processes) {
15
+ if (process.isRunning) {
16
+ console.log(`- ${process.name} (pid: ${process.pid})`);
17
+ } else {
18
+ console.warn(`- ${process.name} (pid: ${process.pid}) is not running`);
19
+ }
20
20
  }
21
+ } catch (error) {
22
+ const err = error;
23
+ console.error(`Error: ${err.message}`);
24
+ process.exit(1);
21
25
  }
22
- }
23
- catch (error) {
24
- const err = error;
25
- console.error(`Error: ${err.message}`);
26
- process.exit(1);
27
- }
28
- });
26
+ });
@@ -1,65 +1,62 @@
1
- import { Command } from 'commander';
2
- import { ProcessManager, ConfigManager } from '../services/index.js';
3
- export const restart = new Command('restart')
4
- .description('restart a running app or all apps')
5
- .argument('<name>', 'name of the app to restart, "all" to restart all apps, or path to config file')
6
- .option('--no-daemon', 'do not daemonize the app')
7
- .option('--clear-logs', 'clear log files before restarting the app')
8
- .action(async (name, options) => {
9
- try {
10
- const processManager = new ProcessManager();
11
- const configManager = new ConfigManager();
12
- if (name === 'all') {
13
- // Get all running processes
14
- const runningProcesses = processManager.listRunningProcesses();
15
- if (runningProcesses.length === 0) {
16
- console.warn('No running processes found');
17
- return;
18
- }
19
- // Stop all processes
20
- const stoppedCount = await processManager.stopAllApps();
21
- console.log(`Stopped ${stoppedCount} process(es)`);
22
- // We need the config to restart, but we don't have it for individual apps
23
- console.warn('Note: Cannot restart apps without config file. Use "pomitu start <config-file>" to start them again.');
24
- }
25
- else {
26
- // Check if it's a config file path
27
- let isConfigFile = false;
28
- try {
29
- const config = configManager.readConfig(name);
30
- if (config && config.apps) {
31
- isConfigFile = true;
32
- // Restart all apps from the config
33
- for (const app of config.apps) {
34
- const success = await processManager.stopApp(app.name);
35
- if (success) {
36
- console.log(`Stopped ${app.name}`);
37
- }
38
- // Start the app again
39
- await processManager.startApp(app, {
40
- daemon: options.daemon,
41
- clearLogs: options.clearLogs
42
- });
43
- }
44
- }
45
- }
46
- catch (error) {
47
- // Not a config file, treat as app name
48
- }
49
- if (!isConfigFile) {
50
- // Treat as app name - stop it
51
- const success = await processManager.stopApp(name);
52
- if (!success) {
53
- console.warn(`No running process found for ${name}`);
54
- return;
55
- }
56
- console.warn(`Stopped ${name}. To restart, you need to use "pomitu start <config-file>" as app config is not stored.`);
57
- }
58
- }
59
- }
60
- catch (error) {
61
- const err = error;
62
- console.error(`Error: ${err.message}`);
63
- process.exit(1);
64
- }
65
- });
1
+ import { Command } from 'commander';
2
+ import { ProcessManager, ConfigManager } from '../services/index.js';
3
+ export const restart = new Command('restart')
4
+ .description('restart a running app or all apps')
5
+ .argument('<name>', 'name of the app to restart, "all" to restart all apps, or path to config file')
6
+ .option('--no-daemon', 'do not daemonize the app')
7
+ .option('--clear-logs', 'clear log files before restarting the app')
8
+ .action(async (name, options) => {
9
+ try {
10
+ const processManager = new ProcessManager();
11
+ const configManager = new ConfigManager();
12
+ if (name === 'all') {
13
+ // Get all running processes
14
+ const runningProcesses = processManager.listRunningProcesses();
15
+ if (runningProcesses.length === 0) {
16
+ console.warn('No running processes found');
17
+ return;
18
+ }
19
+ // Stop all processes
20
+ const stoppedCount = await processManager.stopAllApps();
21
+ console.log(`Stopped ${stoppedCount} process(es)`);
22
+ // We need the config to restart, but we don't have it for individual apps
23
+ console.warn('Note: Cannot restart apps without config file. Use "pomitu start <config-file>" to start them again.');
24
+ } else {
25
+ // Check if it's a config file path
26
+ let isConfigFile = false;
27
+ try {
28
+ const config = configManager.readConfig(name);
29
+ if (config && config.apps) {
30
+ isConfigFile = true;
31
+ // Restart all apps from the config
32
+ for (const app of config.apps) {
33
+ const success = await processManager.stopApp(app.name);
34
+ if (success) {
35
+ console.log(`Stopped ${app.name}`);
36
+ }
37
+ // Start the app again
38
+ await processManager.startApp(app, {
39
+ daemon: options.daemon,
40
+ clearLogs: options.clearLogs
41
+ });
42
+ }
43
+ }
44
+ } catch {
45
+ // Not a config file, treat as app name
46
+ }
47
+ if (!isConfigFile) {
48
+ // Treat as app name - stop it
49
+ const success = await processManager.stopApp(name);
50
+ if (!success) {
51
+ console.warn(`No running process found for ${name}`);
52
+ return;
53
+ }
54
+ console.warn(`Stopped ${name}. To restart, you need to use "pomitu start <config-file>" as app config is not stored.`);
55
+ }
56
+ }
57
+ } catch (error) {
58
+ const err = error;
59
+ console.error(`Error: ${err.message}`);
60
+ process.exit(1);
61
+ }
62
+ });
@@ -9,28 +9,27 @@ export const start = new Command('start')
9
9
  .option('--no-daemon', 'do not daemonize the app and show interactive TUI')
10
10
  .option('--clear-logs', 'clear log files before starting the app')
11
11
  .action(async (name, options) => {
12
- try {
13
- const configManager = new ConfigManager();
14
- const processManager = new ProcessManager();
15
- const config = configManager.readConfig(name);
16
- configManager.validateConfig(config);
17
- const runInteractive = options.daemon === false;
18
- for (const app of config.apps) {
19
- processManager.startApp(app, {
20
- daemon: options.daemon,
21
- clearLogs: options.clearLogs
22
- });
12
+ try {
13
+ const configManager = new ConfigManager();
14
+ const processManager = new ProcessManager();
15
+ const config = configManager.readConfig(name);
16
+ configManager.validateConfig(config);
17
+ const runInteractive = options.daemon === false;
18
+ for (const app of config.apps) {
19
+ processManager.startApp(app, {
20
+ daemon: options.daemon,
21
+ clearLogs: options.clearLogs
22
+ });
23
+ }
24
+ if (runInteractive) {
25
+ render(React.createElement(ProcessTUI, {
26
+ configPath: name,
27
+ clearLogs: options.clearLogs
28
+ }));
29
+ }
30
+ } catch (error) {
31
+ const err = error;
32
+ console.error(`Error: ${err.message}`);
33
+ process.exit(1);
23
34
  }
24
- if (runInteractive) {
25
- render(React.createElement(ProcessTUI, {
26
- configPath: name,
27
- clearLogs: options.clearLogs
28
- }));
29
- }
30
- }
31
- catch (error) {
32
- const err = error;
33
- console.error(`Error: ${err.message}`);
34
- process.exit(1);
35
- }
36
- });
35
+ });
@@ -4,27 +4,24 @@ export const stop = new Command('stop')
4
4
  .description('stop a running app or all apps')
5
5
  .argument('<name>', 'name of the app to stop or "all" to stop all apps')
6
6
  .action(async (name) => {
7
- try {
8
- const processManager = new ProcessManager();
9
- if (name === 'all') {
10
- const stoppedCount = await processManager.stopAllApps();
11
- if (stoppedCount === 0) {
12
- console.warn('No running processes found');
13
- }
14
- else {
15
- console.log(`Stopped ${stoppedCount} process(es)`);
16
- }
17
- }
18
- else {
19
- const success = await processManager.stopApp(name);
20
- if (!success) {
21
- console.warn(`No running process found for ${name}`);
7
+ try {
8
+ const processManager = new ProcessManager();
9
+ if (name === 'all') {
10
+ const stoppedCount = await processManager.stopAllApps();
11
+ if (stoppedCount === 0) {
12
+ console.warn('No running processes found');
13
+ } else {
14
+ console.log(`Stopped ${stoppedCount} process(es)`);
15
+ }
16
+ } else {
17
+ const success = await processManager.stopApp(name);
18
+ if (!success) {
19
+ console.warn(`No running process found for ${name}`);
20
+ }
22
21
  }
22
+ } catch (error) {
23
+ const err = error;
24
+ console.error(`Error: ${err.message}`);
25
+ process.exit(1);
23
26
  }
24
- }
25
- catch (error) {
26
- const err = error;
27
- console.error(`Error: ${err.message}`);
28
- process.exit(1);
29
- }
30
- });
27
+ });
@@ -1,9 +1,11 @@
1
1
  import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
2
2
  import readline from 'node:readline';
3
+ import { existsSync } from 'node:fs';
4
+ import open from 'open';
3
5
  import { Box, Text, useApp, useStdin } from 'ink';
4
6
  import SelectInput from 'ink-select-input';
5
7
  import { ProcessManager, ConfigManager } from '../services/index.js';
6
- import { getFileNameFriendlyName } from '../helpers.js';
8
+ import { getFileNameFriendlyName, getProcessLogOutFilePath, getProcessLogErrorFilePath } from '../helpers.js';
7
9
  export function ProcessTUI({ configPath, clearLogs }) {
8
10
  const { exit } = useApp();
9
11
  const { stdin, setRawMode } = useStdin();
@@ -13,11 +15,31 @@ export function ProcessTUI({ configPath, clearLogs }) {
13
15
  const [messageColor, setMessageColor] = useState('green');
14
16
  const [isProcessing, setIsProcessing] = useState(false);
15
17
  const [isReloading, setIsReloading] = useState(false);
18
+ const [searchMode, setSearchMode] = useState(false);
19
+ const [searchQuery, setSearchQuery] = useState('');
16
20
  const previousRawModeRef = useRef(false);
17
21
  const rawModeCapturedRef = useRef(false);
18
22
  // Create managers only once
19
23
  const processManager = useMemo(() => new ProcessManager(), []);
20
24
  const configManager = useMemo(() => new ConfigManager(), []);
25
+ // Function to open file in native app
26
+ const openFileInNativeApp = useCallback(async (filePath) => {
27
+ if (!existsSync(filePath)) {
28
+ setMessage(`Log file not found: ${filePath}`);
29
+ setMessageColor('red');
30
+ setTimeout(() => setMessage(''), 3000);
31
+ return;
32
+ }
33
+ try {
34
+ await open(filePath);
35
+ setMessage('Opening log file...');
36
+ setMessageColor('green');
37
+ } catch (error) {
38
+ setMessage(`Failed to open log: ${error instanceof Error ? error.message : String(error)}`);
39
+ setMessageColor('red');
40
+ }
41
+ setTimeout(() => setMessage(''), 3000);
42
+ }, []);
21
43
  const cleanExit = useCallback(() => {
22
44
  if (setRawMode) {
23
45
  setRawMode(previousRawModeRef.current);
@@ -66,8 +88,7 @@ export function ProcessTUI({ configPath, clearLogs }) {
66
88
  if (success) {
67
89
  stoppedApps.push(proc.name);
68
90
  }
69
- }
70
- catch (error) {
91
+ } catch (error) {
71
92
  // Continue even if stop fails
72
93
  console.error(`Failed to stop ${proc.name}:`, error);
73
94
  }
@@ -77,21 +98,17 @@ export function ProcessTUI({ configPath, clearLogs }) {
77
98
  setApps(config.apps);
78
99
  if (stoppedApps.length > 0) {
79
100
  setMessage(`Configuration reloaded. Stopped ${stoppedApps.length} running app(s): ${stoppedApps.join(', ')}`);
80
- }
81
- else if (removedApps.length > 0) {
101
+ } else if (removedApps.length > 0) {
82
102
  setMessage(`Configuration reloaded. ${removedApps.length} app(s) removed (none were running)`);
83
- }
84
- else {
103
+ } else {
85
104
  setMessage('Configuration reloaded successfully');
86
105
  }
87
106
  setMessageColor('green');
88
- }
89
- catch (error) {
107
+ } catch (error) {
90
108
  const err = error;
91
109
  setMessage(`Error reloading config: ${err.message}`);
92
110
  setMessageColor('red');
93
- }
94
- finally {
111
+ } finally {
95
112
  setIsReloading(false);
96
113
  // Clear message after 3 seconds
97
114
  setTimeout(() => setMessage(''), 3000);
@@ -103,8 +120,7 @@ export function ProcessTUI({ configPath, clearLogs }) {
103
120
  const config = configManager.readConfig(configPath);
104
121
  configManager.validateConfig(config);
105
122
  setApps(config.apps);
106
- }
107
- catch (error) {
123
+ } catch (error) {
108
124
  const err = error;
109
125
  setMessage(`Error loading config: ${err.message}`);
110
126
  setMessageColor('red');
@@ -130,12 +146,41 @@ export function ProcessTUI({ configPath, clearLogs }) {
130
146
  setRawMode(true);
131
147
  }
132
148
  const handleKeypress = (str, key) => {
149
+ // Handle search mode
150
+ if (searchMode) {
151
+ if (key?.name === 'escape') {
152
+ setSearchMode(false);
153
+ setSearchQuery('');
154
+ return;
155
+ }
156
+ if (key?.name === 'backspace') {
157
+ setSearchQuery(prev => prev.slice(0, -1));
158
+ return;
159
+ }
160
+ if (key?.name === 'return') {
161
+ setSearchMode(false);
162
+ return;
163
+ }
164
+ if (str && str.length === 1 && !key?.ctrl && !key?.meta) {
165
+ setSearchQuery(prev => prev + str);
166
+ return;
167
+ }
168
+ return;
169
+ }
170
+ // Normal mode
133
171
  if (str === 'q') {
134
172
  cleanExit();
135
173
  }
136
174
  if (str === 'r') {
137
175
  reloadConfig();
138
176
  }
177
+ if (str === '/') {
178
+ setSearchMode(true);
179
+ setSearchQuery('');
180
+ }
181
+ if (key?.name === 'escape' && searchQuery) {
182
+ setSearchQuery('');
183
+ }
139
184
  if (key?.ctrl && key.name === 'c') {
140
185
  cleanExit();
141
186
  }
@@ -148,7 +193,7 @@ export function ProcessTUI({ configPath, clearLogs }) {
148
193
  setRawMode(previousRawModeRef.current);
149
194
  }
150
195
  };
151
- }, [stdin, setRawMode, cleanExit, reloadConfig]);
196
+ }, [stdin, setRawMode, cleanExit, reloadConfig, searchMode, searchQuery]);
152
197
  // Handle Ctrl+C signal directly - use prependListener to be first
153
198
  useEffect(() => {
154
199
  const handleSigInt = () => {
@@ -179,19 +224,16 @@ export function ProcessTUI({ configPath, clearLogs }) {
179
224
  });
180
225
  setMessage(`Started ${appName}`);
181
226
  setMessageColor('green');
182
- }
183
- else if (action === 'stop') {
227
+ } else if (action === 'stop') {
184
228
  const success = await processManager.stopApp(appName, { quiet: true });
185
229
  if (success) {
186
230
  setMessage(`Stopped ${appName}`);
187
231
  setMessageColor('green');
188
- }
189
- else {
232
+ } else {
190
233
  setMessage(`Failed to stop ${appName}`);
191
234
  setMessageColor('red');
192
235
  }
193
- }
194
- else if (action === 'restart') {
236
+ } else if (action === 'restart') {
195
237
  const success = await processManager.stopApp(appName, { quiet: true });
196
238
  if (success) {
197
239
  // Wait a bit before restarting
@@ -202,20 +244,29 @@ export function ProcessTUI({ configPath, clearLogs }) {
202
244
  });
203
245
  setMessage(`Restarted ${appName}`);
204
246
  setMessageColor('green');
205
- }
206
- else {
247
+ } else {
207
248
  setMessage(`Failed to stop ${appName} for restart`);
208
249
  setMessageColor('red');
209
250
  }
251
+ } else if (action === 'viewout') {
252
+ const fileNameFriendly = getFileNameFriendlyName(appName);
253
+ const logPath = getProcessLogOutFilePath(fileNameFriendly);
254
+ openFileInNativeApp(logPath);
255
+ setIsProcessing(false);
256
+ return;
257
+ } else if (action === 'viewerr') {
258
+ const fileNameFriendly = getFileNameFriendlyName(appName);
259
+ const logPath = getProcessLogErrorFilePath(fileNameFriendly);
260
+ openFileInNativeApp(logPath);
261
+ setIsProcessing(false);
262
+ return;
210
263
  }
211
264
  setProcesses(computeStatuses());
212
- }
213
- catch (error) {
265
+ } catch (error) {
214
266
  const err = error;
215
267
  setMessage(`Error: ${err.message}`);
216
268
  setMessageColor('red');
217
- }
218
- finally {
269
+ } finally {
219
270
  setIsProcessing(false);
220
271
  }
221
272
  // Clear message after 3 seconds
@@ -237,37 +288,73 @@ export function ProcessTUI({ configPath, clearLogs }) {
237
288
  value: `stop:${proc.name}`
238
289
  });
239
290
  items.push({
240
- label: ` └─ Restart ${proc.name}`,
291
+ label: ` ├─ Restart ${proc.name}`,
241
292
  value: `restart:${proc.name}`
242
293
  });
243
- }
244
- else {
245
294
  items.push({
246
- label: ` └─ Start ${proc.name}`,
295
+ label: ' ├─ View Output Log',
296
+ value: `viewout:${proc.name}`
297
+ });
298
+ items.push({
299
+ label: ' └─ View Error Log',
300
+ value: `viewerr:${proc.name}`
301
+ });
302
+ } else {
303
+ items.push({
304
+ label: ` ├─ Start ${proc.name}`,
247
305
  value: `start:${proc.name}`
248
306
  });
307
+ items.push({
308
+ label: ' ├─ View Output Log',
309
+ value: `viewout:${proc.name}`
310
+ });
311
+ items.push({
312
+ label: ' └─ View Error Log',
313
+ value: `viewerr:${proc.name}`
314
+ });
249
315
  }
250
316
  });
251
317
  return items;
252
318
  }, [processes]);
253
- const items = useMemo(() => getMenuItems(), [getMenuItems]);
319
+ const items = useMemo(() => {
320
+ const allItems = getMenuItems();
321
+ if (!searchQuery)
322
+ return allItems;
323
+ const query = searchQuery.toLowerCase();
324
+ return allItems.filter(item => item.label.toLowerCase().includes(query) ||
325
+ item.value.toLowerCase().includes(query));
326
+ }, [getMenuItems, searchQuery]);
254
327
  const handleMenuSelect = useCallback((item) => {
255
328
  if (item.value === 'separator' || item.value.startsWith('info:')) {
256
329
  // Informational rows are read-only
257
- }
258
- else {
330
+ } else {
259
331
  handleSelect(item);
260
332
  }
261
333
  }, [handleSelect]);
262
334
  const notificationText = isProcessing ? 'Processing...' : (isReloading ? 'Reloading...' : message);
263
335
  const notificationColor = (isProcessing || isReloading) ? 'yellow' : message ? messageColor : undefined;
264
- return (React.createElement(Box, { flexDirection: "column" },
265
- React.createElement(Box, { borderStyle: "round", borderColor: "cyan", padding: 1, marginBottom: 1 },
266
- React.createElement(Text, { bold: true, color: "cyan" }, "Pomitu Process Manager - Interactive Mode")),
336
+ return (React.createElement(Box, { flexDirection: 'column' },
337
+ React.createElement(Box, { borderStyle: 'round', borderColor: 'cyan', padding: 1, marginBottom: 1 },
338
+ React.createElement(Text, { bold: true, color: 'cyan' }, 'Pomitu Process Manager - Interactive Mode')),
267
339
  processes.length > 0 ? (React.createElement(React.Fragment, null,
268
340
  React.createElement(Box, { marginBottom: 1 },
269
- React.createElement(Text, { dimColor: true }, "Use arrow keys to navigate, Enter to select, 'r' to reload config, 'q' or Ctrl+C to quit")),
270
- React.createElement(SelectInput, { items: items, onSelect: handleMenuSelect, isFocused: !isProcessing && !isReloading }))) : (React.createElement(Text, null, "Loading processes...")),
341
+ React.createElement(Text, { dimColor: true }, 'Use arrow keys to navigate, Enter to select, \'/\' to search, \'r\' to reload, \'q\' or Ctrl+C to quit')),
342
+ searchMode && (React.createElement(Box, { marginBottom: 1 },
343
+ React.createElement(Text, { color: 'yellow' },
344
+ 'Search: ',
345
+ searchQuery),
346
+ React.createElement(Text, { dimColor: true }, ' (ESC to cancel, Enter to apply)'))),
347
+ searchQuery && !searchMode && (React.createElement(Box, { marginBottom: 1 },
348
+ React.createElement(Text, { color: 'green' },
349
+ 'Filtering: ',
350
+ searchQuery),
351
+ React.createElement(Text, { dimColor: true }, ' (/ to edit, ESC to clear)'))),
352
+ items.length > 15 && (React.createElement(Box, { marginBottom: 1 },
353
+ React.createElement(Text, { dimColor: true },
354
+ 'Showing 15 of ',
355
+ items.length,
356
+ ' items - scroll with \u2191\u2193 arrows'))),
357
+ React.createElement(SelectInput, { items: items, onSelect: handleMenuSelect, isFocused: !isProcessing && !isReloading && !searchMode, limit: 15 }))) : (React.createElement(Text, null, 'Loading processes...')),
271
358
  React.createElement(Box, { marginTop: 1 },
272
359
  React.createElement(Text, { color: notificationColor ?? 'gray' }, notificationText ?? ' '))));
273
360
  }
package/dist/helpers.js CHANGED
@@ -24,8 +24,7 @@ export function pidIsRunning(pid) {
24
24
  try {
25
25
  process.kill(pid, 0);
26
26
  return true;
27
- }
28
- catch {
27
+ } catch {
29
28
  return false;
30
29
  }
31
30
  }
@@ -14,8 +14,7 @@ export class ConfigManager {
14
14
  throw new Error(`Invalid config file: ${error.message}`);
15
15
  }
16
16
  return config;
17
- }
18
- catch (error) {
17
+ } catch (error) {
19
18
  const err = error;
20
19
  throw new Error(`Error reading config file: ${err.message}`);
21
20
  }
@@ -18,8 +18,7 @@ export class PidManager {
18
18
  try {
19
19
  const pidContent = fs.readFileSync(pidFilePath, 'utf-8');
20
20
  return parseInt(pidContent);
21
- }
22
- catch {
21
+ } catch {
23
22
  return null;
24
23
  }
25
24
  }
@@ -56,8 +56,7 @@ export class ProcessManager {
56
56
  console.log(`${name} with pid ${pid} stopped`);
57
57
  }
58
58
  return true;
59
- }
60
- catch (error) {
59
+ } catch (error) {
61
60
  const err = error;
62
61
  console.error(`Error stopping ${name} with pid ${pid}: ${err.message}`);
63
62
  return false;
@@ -98,8 +97,7 @@ export class ProcessManager {
98
97
  console.log(`Stopping ${appName} at pid ${existingPid}`);
99
98
  try {
100
99
  process.kill(existingPid);
101
- }
102
- catch (error) {
100
+ } catch (error) {
103
101
  const err = error;
104
102
  console.error(`Error stopping ${appName}: ${err.message}`);
105
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pomitu",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Pomitu is a process manager inspired by PM2",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -34,6 +34,7 @@
34
34
  "commander": "^12.1.0",
35
35
  "ink": "^6.4.0",
36
36
  "ink-select-input": "^6.2.0",
37
+ "open": "^11.0.0",
37
38
  "react": "^19.2.0",
38
39
  "shell-quote": "^1.8.1",
39
40
  "yaml": "^2.5.1",