@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/core/sys/gen.ts
CHANGED
|
@@ -1,72 +1,75 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import yaml from 'js-yaml';
|
|
3
|
-
import type { TyrContext } from '../Kernel';
|
|
4
|
-
|
|
5
|
-
interface TyrConfig {
|
|
6
|
-
commands: Record<string, string>;
|
|
7
|
-
aliases?: Record<string, string>;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const template = `
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
logger.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const filePath = path.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
logger.
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import yaml from 'js-yaml';
|
|
3
|
+
import type { TyrContext } from '../Kernel';
|
|
4
|
+
|
|
5
|
+
interface TyrConfig {
|
|
6
|
+
commands: Record<string, string>;
|
|
7
|
+
aliases?: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const template = `import type { TyrContext } from '@orxataguy/tyr';
|
|
11
|
+
|
|
12
|
+
export default ({ run, task, fail, logger, shell, fs }: TyrContext) => {
|
|
13
|
+
return async (args: string[]) => {
|
|
14
|
+
logger.info("Ejecutando comando: %s");
|
|
15
|
+
|
|
16
|
+
// Tu lógica aquí...
|
|
17
|
+
// Ejecuta "tyr doc" para ver la documentación de managers disponibles
|
|
18
|
+
|
|
19
|
+
logger.success("¡Comando %s finalizado!");
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Test = { args: [] };
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export default function gen({ logger, fs, userRoot }: TyrContext) {
|
|
27
|
+
return async (args: string[]) => {
|
|
28
|
+
const commandName = args[0];
|
|
29
|
+
const fileName = args[1];
|
|
30
|
+
|
|
31
|
+
if (!commandName || !fileName) {
|
|
32
|
+
logger.error('Uso incorrecto.');
|
|
33
|
+
logger.info('Sintaxis: tyr gen [nombre-comando] [nombre-archivo]');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
logger.info(`Creando nuevo comando: '${commandName}' -> '${fileName}.tyr.ts'`);
|
|
38
|
+
|
|
39
|
+
const commandsDir = path.join(userRoot, 'commands');
|
|
40
|
+
const filePath = path.join(commandsDir, `${fileName}.tyr.ts`);
|
|
41
|
+
|
|
42
|
+
if (fs.exists(filePath)) {
|
|
43
|
+
logger.error(`El archivo ${fileName}.tyr.ts ya existe. Abortando.`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const templateFilled = template.replaceAll('%s', commandName);
|
|
48
|
+
await fs.write(filePath, templateFilled.trim());
|
|
49
|
+
|
|
50
|
+
// Register in ~/.tyr/map.yml
|
|
51
|
+
const mapPath = path.join(userRoot, 'map.yml');
|
|
52
|
+
try {
|
|
53
|
+
const currentConfigRaw = await fs.read(mapPath);
|
|
54
|
+
const config = (yaml.load(currentConfigRaw ?? 'commands: {}') ?? { commands: {} }) as TyrConfig;
|
|
55
|
+
|
|
56
|
+
if (!config.commands) config.commands = {};
|
|
57
|
+
|
|
58
|
+
if (config.commands[commandName]) {
|
|
59
|
+
logger.warn(`El comando '${commandName}' ya existía. Actualizando ruta...`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Store path relative to userRoot so it remains portable
|
|
63
|
+
config.commands[commandName] = `./commands/${fileName}.tyr.ts`;
|
|
64
|
+
|
|
65
|
+
const newYaml = yaml.dump(config, { indent: 2, lineWidth: -1 });
|
|
66
|
+
await fs.write(mapPath, newYaml);
|
|
67
|
+
|
|
68
|
+
logger.success(`Comando '${commandName}' creado en ${filePath}`);
|
|
69
|
+
logger.success(`Registrado en ${mapPath}`);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
logger.error('Error al actualizar la configuración.');
|
|
72
|
+
console.error(e);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
package/src/core/sys/rem.ts
CHANGED
|
@@ -1,57 +1,61 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import yaml from 'js-yaml';
|
|
3
|
-
import type { TyrContext } from '../Kernel';
|
|
4
|
-
|
|
5
|
-
interface TyrConfig {
|
|
6
|
-
commands: Record<string, string>;
|
|
7
|
-
aliases?: Record<string, string>;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export default function rem({ logger, fs,
|
|
11
|
-
return async (args: string[]) => {
|
|
12
|
-
const commandName = args[0];
|
|
13
|
-
|
|
14
|
-
if (!commandName) {
|
|
15
|
-
logger.error(
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
logger.info(`Iniciando eliminación del comando: '${commandName}'`);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
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
|
-
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import yaml from 'js-yaml';
|
|
3
|
+
import type { TyrContext } from '../Kernel';
|
|
4
|
+
|
|
5
|
+
interface TyrConfig {
|
|
6
|
+
commands: Record<string, string>;
|
|
7
|
+
aliases?: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function rem({ logger, fs, userRoot }: TyrContext) {
|
|
11
|
+
return async (args: string[]) => {
|
|
12
|
+
const commandName = args[0];
|
|
13
|
+
|
|
14
|
+
if (!commandName) {
|
|
15
|
+
logger.error('Falta el nombre del comando a eliminar.');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
logger.info(`Iniciando eliminación del comando: '${commandName}'`);
|
|
20
|
+
|
|
21
|
+
const mapPath = path.join(userRoot, 'map.yml');
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const currentConfigRaw = await fs.read(mapPath);
|
|
25
|
+
if (!currentConfigRaw) {
|
|
26
|
+
logger.error(`No se encontró ${mapPath}. Ejecuta 'tyr --config' primero.`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const config = yaml.load(currentConfigRaw) as TyrConfig;
|
|
31
|
+
|
|
32
|
+
if (!config.commands?.[commandName]) {
|
|
33
|
+
logger.error(`El comando '${commandName}' no existe en ~/.tyr/map.yml.`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const relativeScriptPath = config.commands[commandName];
|
|
38
|
+
const absoluteScriptPath = path.resolve(userRoot, relativeScriptPath);
|
|
39
|
+
|
|
40
|
+
await fs.delete(absoluteScriptPath);
|
|
41
|
+
delete config.commands[commandName];
|
|
42
|
+
|
|
43
|
+
if (config.aliases) {
|
|
44
|
+
for (const [alias, target] of Object.entries(config.aliases)) {
|
|
45
|
+
if (target === commandName) {
|
|
46
|
+
delete config.aliases[alias];
|
|
47
|
+
logger.info(`Alias '${alias}' eliminado.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const newYaml = yaml.dump(config, { indent: 2, lineWidth: -1 });
|
|
53
|
+
await fs.write(mapPath, newYaml);
|
|
54
|
+
|
|
55
|
+
logger.success(`Comando '${commandName}' eliminado.`);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
logger.error('Error crítico durante la eliminación.');
|
|
58
|
+
console.error(e);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
package/src/index.ts
ADDED
package/src/lib/DockerManager.ts
CHANGED
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
import { ShellManager } from './ShellManager.js';
|
|
2
|
-
import { Logger } from '../core/Logger.js';
|
|
3
|
-
import { TyrError } from '../core/TyrError.js';
|
|
4
|
-
|
|
5
|
-
export interface DockerRunOptions {
|
|
6
|
-
image: string;
|
|
7
|
-
name: string;
|
|
8
|
-
port?: string;
|
|
9
|
-
env?: string[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @class DockerManager
|
|
14
|
-
* @description Low-level manager for interacting with the Docker Daemon.
|
|
15
|
-
* Allows starting individual containers, checking states, and managing Docker Compose stacks.
|
|
16
|
-
*/
|
|
17
|
-
export class DockerManager {
|
|
18
|
-
private shell: ShellManager;
|
|
19
|
-
private logger: Logger;
|
|
20
|
-
|
|
21
|
-
constructor(shell: ShellManager, logger: Logger) {
|
|
22
|
-
this.shell = shell;
|
|
23
|
-
this.logger = logger;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @method isRunning
|
|
28
|
-
* @description Checks whether the Docker service is active and responding on the host system.
|
|
29
|
-
* @returns {Promise<boolean>} True if Docker is running.
|
|
30
|
-
* @example
|
|
31
|
-
* const active = await docker.isRunning();
|
|
32
|
-
* if (!active) fail('Start Docker first.');
|
|
33
|
-
*/
|
|
34
|
-
public async isRunning(): Promise<boolean> {
|
|
35
|
-
try {
|
|
36
|
-
await this.shell.exec('docker info');
|
|
37
|
-
return true;
|
|
38
|
-
} catch (e) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @method run
|
|
45
|
-
* @description Deploys an individual container in detached mode. If it already exists, restarts it.
|
|
46
|
-
* @param {DockerRunOptions} config - Deployment configuration.
|
|
47
|
-
* @example
|
|
48
|
-
* await docker.run({ name: 'my-db', image: 'mongo:latest', port: '27017:27017' });
|
|
49
|
-
*/
|
|
50
|
-
public async run({ image, name, port, env = [] }: DockerRunOptions): Promise<void> {
|
|
51
|
-
this.logger.info(`Starting container: ${name} (${image})...`);
|
|
52
|
-
try {
|
|
53
|
-
if (await this.containerExists(name)) {
|
|
54
|
-
this.logger.warn(`Container ${name} already exists. Restarting...`);
|
|
55
|
-
await this.shell.exec(`docker rm -f ${name}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const envFlags = env.map(e => `-e ${e}`).join(' ');
|
|
59
|
-
const portMapping = port ? `-p ${port}` : '';
|
|
60
|
-
const containerId = await this.shell.exec(`docker run -d --name ${name} ${portMapping} ${envFlags} ${image}`);
|
|
61
|
-
this.logger.success(`Container active. ID: ${containerId.substring(0, 12)}`);
|
|
62
|
-
} catch (e) {
|
|
63
|
-
if (e instanceof TyrError) throw e;
|
|
64
|
-
throw new TyrError(`Could not start container: ${name}`, e, 'Check that Docker is running and the image exists.');
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @method containerExists
|
|
70
|
-
* @description Checks whether a specific container exists (running or stopped).
|
|
71
|
-
* @param {string} name - The container name to look for.
|
|
72
|
-
* @returns {Promise<boolean>} True if it exists.
|
|
73
|
-
* @example
|
|
74
|
-
* if (await docker.containerExists('my-app')) { ... }
|
|
75
|
-
*/
|
|
76
|
-
public async containerExists(name: string): Promise<boolean> {
|
|
77
|
-
try {
|
|
78
|
-
await this.shell.exec(`docker inspect ${name}`);
|
|
79
|
-
return true;
|
|
80
|
-
} catch (e) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* @method composeUp
|
|
87
|
-
* @description Starts a full stack using a docker-compose file.
|
|
88
|
-
* @param {string} file - Relative path to the compose file.
|
|
89
|
-
* @example
|
|
90
|
-
* await docker.composeUp('infrastructure/db-compose.yml');
|
|
91
|
-
*/
|
|
92
|
-
public async composeUp(file: string = 'docker-compose.yml'): Promise<void> {
|
|
93
|
-
this.logger.info(`Starting stack from ${file}...`);
|
|
94
|
-
try {
|
|
95
|
-
await this.shell.exec(`docker-compose -f ${file} up -d`);
|
|
96
|
-
this.logger.success('Stack deployed successfully.');
|
|
97
|
-
} catch (e) {
|
|
98
|
-
if (e instanceof TyrError) throw e;
|
|
99
|
-
throw new TyrError(`Could not start Docker Compose stack from: ${file}`, e, 'Check that the compose file exists and is valid.');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export const DockerManagerTests = {
|
|
105
|
-
// isRunning: {},
|
|
106
|
-
// run: { image: 'alpine:latest', name: 'tyr-test-container', env: [] },
|
|
107
|
-
// containerExists: { name: 'tyr-test-container' },
|
|
108
|
-
};
|
|
1
|
+
import { ShellManager } from './ShellManager.js';
|
|
2
|
+
import { Logger } from '../core/Logger.js';
|
|
3
|
+
import { TyrError } from '../core/TyrError.js';
|
|
4
|
+
|
|
5
|
+
export interface DockerRunOptions {
|
|
6
|
+
image: string;
|
|
7
|
+
name: string;
|
|
8
|
+
port?: string;
|
|
9
|
+
env?: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @class DockerManager
|
|
14
|
+
* @description Low-level manager for interacting with the Docker Daemon.
|
|
15
|
+
* Allows starting individual containers, checking states, and managing Docker Compose stacks.
|
|
16
|
+
*/
|
|
17
|
+
export class DockerManager {
|
|
18
|
+
private shell: ShellManager;
|
|
19
|
+
private logger: Logger;
|
|
20
|
+
|
|
21
|
+
constructor(shell: ShellManager, logger: Logger) {
|
|
22
|
+
this.shell = shell;
|
|
23
|
+
this.logger = logger;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @method isRunning
|
|
28
|
+
* @description Checks whether the Docker service is active and responding on the host system.
|
|
29
|
+
* @returns {Promise<boolean>} True if Docker is running.
|
|
30
|
+
* @example
|
|
31
|
+
* const active = await docker.isRunning();
|
|
32
|
+
* if (!active) fail('Start Docker first.');
|
|
33
|
+
*/
|
|
34
|
+
public async isRunning(): Promise<boolean> {
|
|
35
|
+
try {
|
|
36
|
+
await this.shell.exec('docker info');
|
|
37
|
+
return true;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @method run
|
|
45
|
+
* @description Deploys an individual container in detached mode. If it already exists, restarts it.
|
|
46
|
+
* @param {DockerRunOptions} config - Deployment configuration.
|
|
47
|
+
* @example
|
|
48
|
+
* await docker.run({ name: 'my-db', image: 'mongo:latest', port: '27017:27017' });
|
|
49
|
+
*/
|
|
50
|
+
public async run({ image, name, port, env = [] }: DockerRunOptions): Promise<void> {
|
|
51
|
+
this.logger.info(`Starting container: ${name} (${image})...`);
|
|
52
|
+
try {
|
|
53
|
+
if (await this.containerExists(name)) {
|
|
54
|
+
this.logger.warn(`Container ${name} already exists. Restarting...`);
|
|
55
|
+
await this.shell.exec(`docker rm -f ${name}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const envFlags = env.map(e => `-e ${e}`).join(' ');
|
|
59
|
+
const portMapping = port ? `-p ${port}` : '';
|
|
60
|
+
const containerId = await this.shell.exec(`docker run -d --name ${name} ${portMapping} ${envFlags} ${image}`);
|
|
61
|
+
this.logger.success(`Container active. ID: ${containerId.substring(0, 12)}`);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
if (e instanceof TyrError) throw e;
|
|
64
|
+
throw new TyrError(`Could not start container: ${name}`, e, 'Check that Docker is running and the image exists.');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @method containerExists
|
|
70
|
+
* @description Checks whether a specific container exists (running or stopped).
|
|
71
|
+
* @param {string} name - The container name to look for.
|
|
72
|
+
* @returns {Promise<boolean>} True if it exists.
|
|
73
|
+
* @example
|
|
74
|
+
* if (await docker.containerExists('my-app')) { ... }
|
|
75
|
+
*/
|
|
76
|
+
public async containerExists(name: string): Promise<boolean> {
|
|
77
|
+
try {
|
|
78
|
+
await this.shell.exec(`docker inspect ${name}`);
|
|
79
|
+
return true;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @method composeUp
|
|
87
|
+
* @description Starts a full stack using a docker-compose file.
|
|
88
|
+
* @param {string} file - Relative path to the compose file.
|
|
89
|
+
* @example
|
|
90
|
+
* await docker.composeUp('infrastructure/db-compose.yml');
|
|
91
|
+
*/
|
|
92
|
+
public async composeUp(file: string = 'docker-compose.yml'): Promise<void> {
|
|
93
|
+
this.logger.info(`Starting stack from ${file}...`);
|
|
94
|
+
try {
|
|
95
|
+
await this.shell.exec(`docker-compose -f ${file} up -d`);
|
|
96
|
+
this.logger.success('Stack deployed successfully.');
|
|
97
|
+
} catch (e) {
|
|
98
|
+
if (e instanceof TyrError) throw e;
|
|
99
|
+
throw new TyrError(`Could not start Docker Compose stack from: ${file}`, e, 'Check that the compose file exists and is valid.');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const DockerManagerTests = {
|
|
105
|
+
// isRunning: {},
|
|
106
|
+
// run: { image: 'alpine:latest', name: 'tyr-test-container', env: [] },
|
|
107
|
+
// containerExists: { name: 'tyr-test-container' },
|
|
108
|
+
};
|