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 +1 -1
- package/dist/commands/flush.js +13 -15
- package/dist/commands/ls.js +18 -20
- package/dist/commands/restart.js +62 -65
- package/dist/commands/start.js +23 -24
- package/dist/commands/stop.js +19 -22
- package/dist/components/ProcessTUI.js +125 -38
- package/dist/helpers.js +1 -2
- package/dist/services/ConfigManager.js +1 -2
- package/dist/services/PidManager.js +1 -2
- package/dist/services/ProcessManager.js +2 -4
- package/package.json +2 -1
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.
|
|
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
|
package/dist/commands/flush.js
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
+
});
|
package/dist/commands/ls.js
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
+
});
|
package/dist/commands/restart.js
CHANGED
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// Get all running processes
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
+
});
|
package/dist/commands/start.js
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
+
});
|
package/dist/commands/stop.js
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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: `
|
|
291
|
+
label: ` ├─ Restart ${proc.name}`,
|
|
241
292
|
value: `restart:${proc.name}`
|
|
242
293
|
});
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
294
|
items.push({
|
|
246
|
-
label:
|
|
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(() =>
|
|
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:
|
|
265
|
-
React.createElement(Box, { borderStyle:
|
|
266
|
-
React.createElement(Text, { bold: true, color:
|
|
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 },
|
|
270
|
-
React.createElement(
|
|
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
|
@@ -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.
|
|
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",
|