@orxataguy/tyr 1.0.0 → 1.0.2
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/LICENSE +21 -21
- package/README.md +408 -408
- package/bin/tyr.js +10 -7
- package/bin/tyr.ts +13 -13
- package/config/map.yml +4 -7
- package/package.json +66 -60
- package/src/commands/di.tyr.ts +112 -112
- package/src/commands/dw.tyr.ts +115 -115
- package/src/commands/install.tyr.ts +30 -104
- package/src/core/Container.ts +56 -56
- package/src/core/Kernel.ts +213 -165
- package/src/core/Logger.ts +51 -48
- package/src/core/TyrError.ts +57 -57
- package/src/core/sys/ai.ts +160 -162
- package/src/core/sys/config.ts +231 -0
- package/src/core/sys/doc.ts +324 -324
- package/src/core/sys/gen.ts +75 -72
- package/src/core/sys/rem.ts +61 -57
- package/src/index.ts +5 -0
- package/src/lib/DockerManager.ts +108 -108
- package/src/lib/FileSystemManager.ts +152 -152
- package/src/lib/GitManager.ts +75 -75
- package/src/lib/PackageManager.ts +87 -87
- package/src/lib/SQLManager.ts +112 -120
- package/src/lib/ShellManager.ts +117 -117
- package/src/lib/SystemManager.ts +83 -83
- package/src/lib/WebManager.ts +62 -62
package/src/lib/SQLManager.ts
CHANGED
|
@@ -1,121 +1,113 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
urlObj
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* @object SQLManagerTests
|
|
115
|
-
* @description Parámetros de pruebas para validar la funcionalidad de SQLManager.
|
|
116
|
-
*/
|
|
117
|
-
export const SQLManagerTests = {
|
|
118
|
-
// init: {},
|
|
119
|
-
// select: { query: 'SELECT 1 as test_value' },
|
|
120
|
-
// connectionPool: { queries: ['SELECT 1 as q1', 'SELECT 2 as q2', 'SELECT 3 as q3'] }
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import sql, { config as SQLConfig } from 'mssql';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @class SQLManager
|
|
6
|
+
* @description Conector con la base de datos SQL Server.
|
|
7
|
+
*/
|
|
8
|
+
export class SQLManager {
|
|
9
|
+
private pool!: sql.ConnectionPool;
|
|
10
|
+
private connected = false;
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private async init(): Promise<void> {
|
|
16
|
+
if (!this.connected) {
|
|
17
|
+
|
|
18
|
+
const db_config: SQLConfig = {
|
|
19
|
+
user: process.env.MSSQL_USER,
|
|
20
|
+
password: process.env.MSSQL_PASSWORD,
|
|
21
|
+
server: process.env.MSSQL_SERVER || '',
|
|
22
|
+
database: process.env.MSSQL_DATABASE,
|
|
23
|
+
options: {
|
|
24
|
+
encrypt: false,
|
|
25
|
+
trustServerCertificate: true
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
this.pool = await sql.connect(db_config);
|
|
30
|
+
this.connected = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @method select
|
|
36
|
+
* @description Ejecuta un comando SELECT en SQL Server y devuelve el resultado como JSON.
|
|
37
|
+
* @param {string} query - El comando SELECT completo.
|
|
38
|
+
* @returns {Promise<any[]>} Los registros del resultado.
|
|
39
|
+
* @example
|
|
40
|
+
* await dbManager.init();
|
|
41
|
+
* const data = await dbManager.select('SELECT * FROM tabla');
|
|
42
|
+
*/
|
|
43
|
+
public async select(query: string): Promise<any[]> {
|
|
44
|
+
await this.init();
|
|
45
|
+
|
|
46
|
+
const result = await this.pool.request().query(query);
|
|
47
|
+
|
|
48
|
+
await this.close();
|
|
49
|
+
return result.recordset;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @method searchBrokerOnDB
|
|
54
|
+
* @description Busca broker por hostname usando la query encoded.
|
|
55
|
+
* @param {string | URL} url - URL o string para extraer hostname.
|
|
56
|
+
* @returns {Promise<string>} Nombre del broker.
|
|
57
|
+
* @example
|
|
58
|
+
* const broker = await db.searchBrokerOnDB('https://www.foo.com');
|
|
59
|
+
*/
|
|
60
|
+
public async searchBrokerOnDB(url: string | URL): Promise<string> {
|
|
61
|
+
let urlString = url.toString();
|
|
62
|
+
|
|
63
|
+
if (!urlString.startsWith("http://") && !urlString.startsWith("https://")) {
|
|
64
|
+
urlString = "https://" + urlString;
|
|
65
|
+
}
|
|
66
|
+
let urlObj = new URL(urlString).hostname;
|
|
67
|
+
|
|
68
|
+
if (urlObj.split('.').length < 3) {
|
|
69
|
+
urlObj = ['www', urlObj].join('.');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const isWeb = urlObj.startsWith('www') ||
|
|
73
|
+
urlObj.startsWith('horizon') ||
|
|
74
|
+
urlObj.startsWith('ambiance') ||
|
|
75
|
+
urlObj.startsWith('panorama') ||
|
|
76
|
+
urlObj.startsWith('flow') ||
|
|
77
|
+
urlObj.startsWith('panorama') ||
|
|
78
|
+
urlObj.startsWith('avantio') ||
|
|
79
|
+
urlObj.startsWith('demo');
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
const query = isWeb ? `SELECT basedir as BROKER from ftpUsers where CONCAT(prefijo, '.', dominio) = '${urlObj}'` : `SELECT LOGIN_DS AS BROKER from CR_CANALVENTAS WHERE WEB_DS = '${urlObj}'`;
|
|
83
|
+
|
|
84
|
+
await this.init();
|
|
85
|
+
|
|
86
|
+
const result = await this.pool.request().query(query);
|
|
87
|
+
|
|
88
|
+
await this.close();
|
|
89
|
+
|
|
90
|
+
if (!result.recordset[0] || !result.recordset[0].BROKER) {
|
|
91
|
+
throw new Error(`No se encontró broker para ${urlObj}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result.recordset[0].BROKER as string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private async close(): Promise<void> {
|
|
98
|
+
if (this.connected && this.pool) {
|
|
99
|
+
await this.pool.close();
|
|
100
|
+
this.connected = false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @object SQLManagerTests
|
|
107
|
+
* @description Parámetros de pruebas para validar la funcionalidad de SQLManager.
|
|
108
|
+
*/
|
|
109
|
+
export const SQLManagerTests = {
|
|
110
|
+
// init: {},
|
|
111
|
+
// select: { query: 'SELECT 1 as test_value' },
|
|
112
|
+
// connectionPool: { queries: ['SELECT 1 as q1', 'SELECT 2 as q2', 'SELECT 3 as q3'] }
|
|
121
113
|
};
|
package/src/lib/ShellManager.ts
CHANGED
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import { execa } from 'execa';
|
|
2
|
-
import { resolve } from 'path';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
|
-
|
|
6
|
-
import { TyrError } from '../core/TyrError.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @class ShellManager
|
|
10
|
-
* @description Terminal command executor. Maintains the working directory (CWD) state to chain commands in specific folders.
|
|
11
|
-
*/
|
|
12
|
-
export class ShellManager {
|
|
13
|
-
private cwd: string;
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
this.cwd = process.cwd();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @method exec
|
|
21
|
-
* @description Executes a command in the system shell and returns the standard output.
|
|
22
|
-
* @param {string} command - The full command to execute.
|
|
23
|
-
* @returns {Promise<string>} The command output (stdout) trimmed of extra whitespace.
|
|
24
|
-
* @example
|
|
25
|
-
* const version = await shell.exec('node -v');
|
|
26
|
-
*/
|
|
27
|
-
public async exec(command: string): Promise<string> {
|
|
28
|
-
try {
|
|
29
|
-
const result = await execa(command, { shell: true, cwd: this.cwd });
|
|
30
|
-
return result.stdout.trim();
|
|
31
|
-
} catch (e) {
|
|
32
|
-
throw new TyrError(`An error occurred while executing the command: ${command}`, e);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @method showLoader
|
|
38
|
-
* @description Displays a spinner loader in the terminal.
|
|
39
|
-
* @param {string} message - Informational text to show alongside the spinner.
|
|
40
|
-
* @returns {void}
|
|
41
|
-
* @example
|
|
42
|
-
* shell.showLoader('Loading...');
|
|
43
|
-
*/
|
|
44
|
-
public showLoader = (message: string): { stop: () => void } => {
|
|
45
|
-
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
46
|
-
let i = 0;
|
|
47
|
-
let stopped = false;
|
|
48
|
-
|
|
49
|
-
const interval = setInterval(() => {
|
|
50
|
-
if (!stopped) {
|
|
51
|
-
process.stdout.write(`\r${frames[i]} ${message}`);
|
|
52
|
-
i = (i + 1) % frames.length;
|
|
53
|
-
}
|
|
54
|
-
}, 80);
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
stop: () => {
|
|
58
|
-
stopped = true;
|
|
59
|
-
clearInterval(interval);
|
|
60
|
-
process.stdout.write('\r');
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @method input
|
|
67
|
-
* @description Prompts the user for a value via CLI.
|
|
68
|
-
* @param {string} question - Informational text shown as the prompt.
|
|
69
|
-
* @returns {Promise<string>} The value entered by the user.
|
|
70
|
-
* @example
|
|
71
|
-
* const name = await shell.input("What's your name?");
|
|
72
|
-
*/
|
|
73
|
-
public async input(question: string): Promise<string> {
|
|
74
|
-
try {
|
|
75
|
-
const result = await inquirer.prompt([
|
|
76
|
-
{
|
|
77
|
-
type: 'input',
|
|
78
|
-
name: 'value',
|
|
79
|
-
message: question,
|
|
80
|
-
},
|
|
81
|
-
]);
|
|
82
|
-
|
|
83
|
-
return result.value.trim();
|
|
84
|
-
} catch (e) {
|
|
85
|
-
throw new TyrError(`An error occurred while prompting the question: ${question}`, e);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* @method cd
|
|
91
|
-
* @description Changes the internal working directory for subsequent commands executed by this instance.
|
|
92
|
-
* @param {string} path - Absolute or relative path to change to.
|
|
93
|
-
* @example
|
|
94
|
-
* shell.cd('./backend');
|
|
95
|
-
* await shell.exec('npm install'); // Runs inside /backend
|
|
96
|
-
*/
|
|
97
|
-
public cd(path: string): void {
|
|
98
|
-
let expandedPath = path;
|
|
99
|
-
if (path.startsWith('~/')) {
|
|
100
|
-
expandedPath = path.replace('~', homedir());
|
|
101
|
-
} else if (path === '~') {
|
|
102
|
-
expandedPath = homedir();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
this.cwd = resolve(this.cwd, expandedPath);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* @object ShellManagerTests
|
|
111
|
-
* @description Test parameters to validate ShellManager functionality.
|
|
112
|
-
*/
|
|
113
|
-
export const ShellManagerTests = {
|
|
114
|
-
exec: { command: 'node -v' },
|
|
115
|
-
cd: { path: '/tmp' },
|
|
116
|
-
input: { question: 'Enter a test value:' },
|
|
117
|
-
showLoader: { message: 'Loading test...' }
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
|
|
6
|
+
import { TyrError } from '../core/TyrError.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @class ShellManager
|
|
10
|
+
* @description Terminal command executor. Maintains the working directory (CWD) state to chain commands in specific folders.
|
|
11
|
+
*/
|
|
12
|
+
export class ShellManager {
|
|
13
|
+
private cwd: string;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
this.cwd = process.cwd();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @method exec
|
|
21
|
+
* @description Executes a command in the system shell and returns the standard output.
|
|
22
|
+
* @param {string} command - The full command to execute.
|
|
23
|
+
* @returns {Promise<string>} The command output (stdout) trimmed of extra whitespace.
|
|
24
|
+
* @example
|
|
25
|
+
* const version = await shell.exec('node -v');
|
|
26
|
+
*/
|
|
27
|
+
public async exec(command: string): Promise<string> {
|
|
28
|
+
try {
|
|
29
|
+
const result = await execa(command, { shell: true, cwd: this.cwd });
|
|
30
|
+
return result.stdout.trim();
|
|
31
|
+
} catch (e) {
|
|
32
|
+
throw new TyrError(`An error occurred while executing the command: ${command}`, e);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @method showLoader
|
|
38
|
+
* @description Displays a spinner loader in the terminal.
|
|
39
|
+
* @param {string} message - Informational text to show alongside the spinner.
|
|
40
|
+
* @returns {void}
|
|
41
|
+
* @example
|
|
42
|
+
* shell.showLoader('Loading...');
|
|
43
|
+
*/
|
|
44
|
+
public showLoader = (message: string): { stop: () => void } => {
|
|
45
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
46
|
+
let i = 0;
|
|
47
|
+
let stopped = false;
|
|
48
|
+
|
|
49
|
+
const interval = setInterval(() => {
|
|
50
|
+
if (!stopped) {
|
|
51
|
+
process.stdout.write(`\r${frames[i]} ${message}`);
|
|
52
|
+
i = (i + 1) % frames.length;
|
|
53
|
+
}
|
|
54
|
+
}, 80);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
stop: () => {
|
|
58
|
+
stopped = true;
|
|
59
|
+
clearInterval(interval);
|
|
60
|
+
process.stdout.write('\r');
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @method input
|
|
67
|
+
* @description Prompts the user for a value via CLI.
|
|
68
|
+
* @param {string} question - Informational text shown as the prompt.
|
|
69
|
+
* @returns {Promise<string>} The value entered by the user.
|
|
70
|
+
* @example
|
|
71
|
+
* const name = await shell.input("What's your name?");
|
|
72
|
+
*/
|
|
73
|
+
public async input(question: string): Promise<string> {
|
|
74
|
+
try {
|
|
75
|
+
const result = await inquirer.prompt([
|
|
76
|
+
{
|
|
77
|
+
type: 'input',
|
|
78
|
+
name: 'value',
|
|
79
|
+
message: question,
|
|
80
|
+
},
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
return result.value.trim();
|
|
84
|
+
} catch (e) {
|
|
85
|
+
throw new TyrError(`An error occurred while prompting the question: ${question}`, e);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @method cd
|
|
91
|
+
* @description Changes the internal working directory for subsequent commands executed by this instance.
|
|
92
|
+
* @param {string} path - Absolute or relative path to change to.
|
|
93
|
+
* @example
|
|
94
|
+
* shell.cd('./backend');
|
|
95
|
+
* await shell.exec('npm install'); // Runs inside /backend
|
|
96
|
+
*/
|
|
97
|
+
public cd(path: string): void {
|
|
98
|
+
let expandedPath = path;
|
|
99
|
+
if (path.startsWith('~/')) {
|
|
100
|
+
expandedPath = path.replace('~', homedir());
|
|
101
|
+
} else if (path === '~') {
|
|
102
|
+
expandedPath = homedir();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.cwd = resolve(this.cwd, expandedPath);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @object ShellManagerTests
|
|
111
|
+
* @description Test parameters to validate ShellManager functionality.
|
|
112
|
+
*/
|
|
113
|
+
export const ShellManagerTests = {
|
|
114
|
+
exec: { command: 'node -v' },
|
|
115
|
+
cd: { path: '/tmp' },
|
|
116
|
+
input: { question: 'Enter a test value:' },
|
|
117
|
+
showLoader: { message: 'Loading test...' }
|
|
118
118
|
};
|
package/src/lib/SystemManager.ts
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
import { ShellManager } from './ShellManager.js';
|
|
2
|
-
import { Logger } from '../core/Logger.js';
|
|
3
|
-
import { TyrError } from '../core/TyrError.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @class SystemManager
|
|
7
|
-
* @description General OS maintenance and cleanup utilities.
|
|
8
|
-
*/
|
|
9
|
-
export class SystemManager {
|
|
10
|
-
private shell: ShellManager;
|
|
11
|
-
private logger: Logger;
|
|
12
|
-
|
|
13
|
-
constructor(shell: ShellManager, logger: Logger) {
|
|
14
|
-
this.shell = shell;
|
|
15
|
-
this.logger = logger;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @method killPort
|
|
20
|
-
* @description Identifies which process is occupying a port and force-kills it.
|
|
21
|
-
* @param {number|string} port - The port to free (e.g. 3000).
|
|
22
|
-
* @returns {Promise<boolean>} True if a process was killed, False if the port was already free.
|
|
23
|
-
* @example
|
|
24
|
-
* await sys.killPort(8080);
|
|
25
|
-
*/
|
|
26
|
-
public async killPort(port: number | string): Promise<boolean> {
|
|
27
|
-
try {
|
|
28
|
-
let pid: string | null = null;
|
|
29
|
-
|
|
30
|
-
if (process.platform === 'win32') {
|
|
31
|
-
const result = await this.shell.exec(`netstat -ano | findstr :${port}`).catch(() => null);
|
|
32
|
-
if (!result?.trim()) return false;
|
|
33
|
-
const lines = result.trim().split('\n').filter(l => l.includes(`LISTENING`));
|
|
34
|
-
if (!lines.length) return false;
|
|
35
|
-
pid = lines[0].trim().split(/\s+/).pop() ?? null;
|
|
36
|
-
} else {
|
|
37
|
-
pid = await this.shell.exec(`lsof -t -i:${port}`).catch(() => null);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (!pid?.trim()) return false;
|
|
41
|
-
|
|
42
|
-
this.logger.warn(`Port ${port} blocked by PID ${pid.trim()}. Killing...`);
|
|
43
|
-
|
|
44
|
-
if (process.platform === 'win32') {
|
|
45
|
-
await this.shell.exec(`taskkill /F /PID ${pid.trim()}`);
|
|
46
|
-
} else {
|
|
47
|
-
await this.shell.exec(`kill -9 ${pid.trim()}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
this.logger.success(`Port ${port} freed.`);
|
|
51
|
-
return true;
|
|
52
|
-
} catch (e) {
|
|
53
|
-
throw new TyrError(`Could not free port: ${port}`, e, 'Check that you have the necessary permissions.');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* @method nukeNodeModules
|
|
59
|
-
* @description Removes node_modules and package-lock.json from the current directory.
|
|
60
|
-
* Useful for fixing corrupted NPM installations.
|
|
61
|
-
* @example
|
|
62
|
-
* await sys.nukeNodeModules();
|
|
63
|
-
*/
|
|
64
|
-
public async nukeNodeModules(): Promise<void> {
|
|
65
|
-
this.logger.info('Cleaning up dependencies...');
|
|
66
|
-
try {
|
|
67
|
-
if (process.platform === 'win32') {
|
|
68
|
-
await this.shell.exec('if exist node_modules rmdir /s /q node_modules');
|
|
69
|
-
await this.shell.exec('if exist package-lock.json del /f /q package-lock.json');
|
|
70
|
-
} else {
|
|
71
|
-
await this.shell.exec('rm -rf node_modules package-lock.json');
|
|
72
|
-
}
|
|
73
|
-
this.logger.success('node_modules and package-lock.json removed.');
|
|
74
|
-
} catch (e) {
|
|
75
|
-
if (e instanceof TyrError) throw e;
|
|
76
|
-
throw new TyrError('Could not remove node_modules.', e, 'Check write permissions on the current directory.');
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export const SystemManagerTests = {
|
|
82
|
-
killPort: { port: 3000 },
|
|
83
|
-
};
|
|
1
|
+
import { ShellManager } from './ShellManager.js';
|
|
2
|
+
import { Logger } from '../core/Logger.js';
|
|
3
|
+
import { TyrError } from '../core/TyrError.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class SystemManager
|
|
7
|
+
* @description General OS maintenance and cleanup utilities.
|
|
8
|
+
*/
|
|
9
|
+
export class SystemManager {
|
|
10
|
+
private shell: ShellManager;
|
|
11
|
+
private logger: Logger;
|
|
12
|
+
|
|
13
|
+
constructor(shell: ShellManager, logger: Logger) {
|
|
14
|
+
this.shell = shell;
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @method killPort
|
|
20
|
+
* @description Identifies which process is occupying a port and force-kills it.
|
|
21
|
+
* @param {number|string} port - The port to free (e.g. 3000).
|
|
22
|
+
* @returns {Promise<boolean>} True if a process was killed, False if the port was already free.
|
|
23
|
+
* @example
|
|
24
|
+
* await sys.killPort(8080);
|
|
25
|
+
*/
|
|
26
|
+
public async killPort(port: number | string): Promise<boolean> {
|
|
27
|
+
try {
|
|
28
|
+
let pid: string | null = null;
|
|
29
|
+
|
|
30
|
+
if (process.platform === 'win32') {
|
|
31
|
+
const result = await this.shell.exec(`netstat -ano | findstr :${port}`).catch(() => null);
|
|
32
|
+
if (!result?.trim()) return false;
|
|
33
|
+
const lines = result.trim().split('\n').filter(l => l.includes(`LISTENING`));
|
|
34
|
+
if (!lines.length) return false;
|
|
35
|
+
pid = lines[0].trim().split(/\s+/).pop() ?? null;
|
|
36
|
+
} else {
|
|
37
|
+
pid = await this.shell.exec(`lsof -t -i:${port}`).catch(() => null);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!pid?.trim()) return false;
|
|
41
|
+
|
|
42
|
+
this.logger.warn(`Port ${port} blocked by PID ${pid.trim()}. Killing...`);
|
|
43
|
+
|
|
44
|
+
if (process.platform === 'win32') {
|
|
45
|
+
await this.shell.exec(`taskkill /F /PID ${pid.trim()}`);
|
|
46
|
+
} else {
|
|
47
|
+
await this.shell.exec(`kill -9 ${pid.trim()}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.logger.success(`Port ${port} freed.`);
|
|
51
|
+
return true;
|
|
52
|
+
} catch (e) {
|
|
53
|
+
throw new TyrError(`Could not free port: ${port}`, e, 'Check that you have the necessary permissions.');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @method nukeNodeModules
|
|
59
|
+
* @description Removes node_modules and package-lock.json from the current directory.
|
|
60
|
+
* Useful for fixing corrupted NPM installations.
|
|
61
|
+
* @example
|
|
62
|
+
* await sys.nukeNodeModules();
|
|
63
|
+
*/
|
|
64
|
+
public async nukeNodeModules(): Promise<void> {
|
|
65
|
+
this.logger.info('Cleaning up dependencies...');
|
|
66
|
+
try {
|
|
67
|
+
if (process.platform === 'win32') {
|
|
68
|
+
await this.shell.exec('if exist node_modules rmdir /s /q node_modules');
|
|
69
|
+
await this.shell.exec('if exist package-lock.json del /f /q package-lock.json');
|
|
70
|
+
} else {
|
|
71
|
+
await this.shell.exec('rm -rf node_modules package-lock.json');
|
|
72
|
+
}
|
|
73
|
+
this.logger.success('node_modules and package-lock.json removed.');
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (e instanceof TyrError) throw e;
|
|
76
|
+
throw new TyrError('Could not remove node_modules.', e, 'Check write permissions on the current directory.');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const SystemManagerTests = {
|
|
82
|
+
killPort: { port: 3000 },
|
|
83
|
+
};
|