@orxataguy/tyr 1.0.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/bin/tyr.js +10 -7
- package/package.json +3 -3
- package/src/commands/install.tyr.ts +61 -135
- package/src/core/Kernel.ts +206 -165
- package/src/core/sys/config.ts +161 -0
- package/src/core/sys/gen.ts +73 -72
- package/src/core/sys/rem.ts +61 -57
- package/src/lib/SQLManager.ts +3 -6
package/bin/tyr.js
CHANGED
|
@@ -2,24 +2,27 @@
|
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import { dirname, resolve, join } from 'path';
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
5
6
|
|
|
6
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
8
|
const __dirname = dirname(__filename);
|
|
8
9
|
const packageRoot = resolve(__dirname, '..');
|
|
9
|
-
const isWindows = process.platform === 'win32';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// Locate tsx's CLI entry directly from its package.json — no shell, no .cmd wrappers
|
|
12
|
+
const tsxPkg = JSON.parse(readFileSync(join(packageRoot, 'node_modules', 'tsx', 'package.json'), 'utf-8'));
|
|
13
|
+
const tsxBinField = tsxPkg.bin;
|
|
14
|
+
const tsxBinRelative = typeof tsxBinField === 'string' ? tsxBinField : (tsxBinField.tsx ?? tsxBinField['tsx']);
|
|
15
|
+
const tsxEntry = join(packageRoot, 'node_modules', 'tsx', tsxBinRelative);
|
|
12
16
|
const entry = join(__dirname, 'tyr.ts');
|
|
13
17
|
|
|
14
|
-
const child = spawn(
|
|
15
|
-
stdio: 'inherit'
|
|
16
|
-
shell: isWindows
|
|
18
|
+
const child = spawn(process.execPath, [tsxEntry, entry, ...process.argv.slice(2)], {
|
|
19
|
+
stdio: 'inherit'
|
|
17
20
|
});
|
|
18
21
|
|
|
19
22
|
child.on('exit', (code) => process.exit(code ?? 0));
|
|
20
23
|
child.on('error', (err) => {
|
|
21
24
|
console.error(`Error: Could not start tyr. ${err.message}`);
|
|
22
|
-
console.error(`tsx not found at: ${
|
|
23
|
-
console.error(
|
|
25
|
+
console.error(`tsx not found at: ${tsxEntry}`);
|
|
26
|
+
console.error('Try reinstalling: npm install -g @orxataguy/tyr');
|
|
24
27
|
process.exit(1);
|
|
25
28
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orxataguy/tyr",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tyr": "./bin/tyr.js"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"test:watch": "vitest",
|
|
17
17
|
"test:ui": "vitest --ui",
|
|
18
18
|
"test:coverage": "vitest run --coverage",
|
|
19
|
-
"prepare": "node -e \"const{existsSync}=require('fs');if(!process.env.CI&&existsSync('.git'))
|
|
19
|
+
"prepare": "node -e \"const{existsSync}=require('fs'),cp=require('child_process'),p=require('path');if(!process.env.CI&&existsSync('.git')){const b=p.join('node_modules','.bin','husky'+(process.platform==='win32'?'.cmd':''));cp.execSync(b,{stdio:'inherit',shell:process.platform==='win32'});}\"",
|
|
20
20
|
"release:patch": "npm version patch && git push --follow-tags",
|
|
21
21
|
"release:minor": "npm version minor && git push --follow-tags",
|
|
22
22
|
"release:major": "npm version major && git push --follow-tags",
|
|
@@ -57,4 +57,4 @@
|
|
|
57
57
|
"vite": "^7.3.1",
|
|
58
58
|
"vitest": "^3.2.4"
|
|
59
59
|
}
|
|
60
|
-
}
|
|
60
|
+
}
|
|
@@ -1,135 +1,61 @@
|
|
|
1
|
-
import { TyrContext } from '../core/Kernel';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
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
|
-
if (!fs.exists(pluginsTemplatePath)) {
|
|
66
|
-
fail(
|
|
67
|
-
`Template de plugins no encontrado: ${pluginsTemplatePath}`,
|
|
68
|
-
'Verifica que el framework de Avantio esté correctamente instalado'
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
logger.success('Templates encontrados');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
await task('Creando carpeta local', async () => {
|
|
76
|
-
await fs.createDir(addonsPath);
|
|
77
|
-
logger.success(`Carpeta creada: ${addonsPath}`);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
await task('Copiando aliases.template.sh', async () => {
|
|
81
|
-
const aliasesContent = await fs.read(aliasesTemplatePath);
|
|
82
|
-
if (!aliasesContent) {
|
|
83
|
-
fail('No se pudo leer el contenido de aliases.template.sh');
|
|
84
|
-
}
|
|
85
|
-
await fs.write(path.join(addonsPath, 'aliases.sh'), aliasesContent!);
|
|
86
|
-
logger.success(`Archivo creado: ${path.join(addonsPath, 'aliases.sh')}`);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
await task('Copiando plugins.template.sh', async () => {
|
|
90
|
-
const pluginsContent = await fs.read(pluginsTemplatePath);
|
|
91
|
-
if (!pluginsContent) {
|
|
92
|
-
fail('No se pudo leer el contenido de plugins.template.sh');
|
|
93
|
-
}
|
|
94
|
-
await fs.write(path.join(addonsPath, 'plugins.sh'), pluginsContent!);
|
|
95
|
-
logger.success(`Archivo creado: ${path.join(addonsPath, 'plugins.sh')}`);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (isWindows) {
|
|
99
|
-
await task('Configurando perfil de PowerShell', async () => {
|
|
100
|
-
const psProfile = await getPowerShellProfile(shell);
|
|
101
|
-
if (psProfile) {
|
|
102
|
-
const sourceLine = `. "${path.join(addonsPath, 'aliases.sh')}"`;
|
|
103
|
-
await fs.ensureLine(psProfile, sourceLine);
|
|
104
|
-
logger.success(`Aliases añadidos al perfil de PowerShell: ${psProfile}`);
|
|
105
|
-
logger.info('Reinicia PowerShell para aplicar los cambios');
|
|
106
|
-
} else {
|
|
107
|
-
logger.warn('No se pudo detectar el perfil de PowerShell.');
|
|
108
|
-
logger.info(`Añade manualmente a tu perfil: . "${path.join(addonsPath, 'aliases.sh')}"`);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
} else {
|
|
112
|
-
await task('Configurando shell', async () => {
|
|
113
|
-
const rcFile = detectShellRcFile(homeDir);
|
|
114
|
-
if (!rcFile) {
|
|
115
|
-
logger.warn('No se pudo detectar el archivo de configuración de shell. Configura manualmente.');
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const sourceLine = `source "${path.join(addonsPath, 'aliases.sh')}"`;
|
|
120
|
-
await fs.ensureLine(rcFile, sourceLine);
|
|
121
|
-
logger.success(`Aliases añadidos a: ${rcFile}`);
|
|
122
|
-
logger.info(`Ejecuta: source ${rcFile} (o abre una nueva terminal)`);
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
logger.success('\nEstructura de addons configurada exitosamente');
|
|
127
|
-
logger.info(`\nArchivos creados en: ${addonsPath}`);
|
|
128
|
-
logger.info(' - aliases.sh');
|
|
129
|
-
logger.info(' - plugins.sh');
|
|
130
|
-
};
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// export const Test = {
|
|
134
|
-
// args: []
|
|
135
|
-
// }
|
|
1
|
+
import { TyrContext } from '../core/Kernel';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
|
|
5
|
+
export default ({ task, fail, logger, fs }: TyrContext) => {
|
|
6
|
+
return async (_args: string[]) => {
|
|
7
|
+
const homeDir = homedir();
|
|
8
|
+
const userRoot = path.join(homeDir, '.tyr');
|
|
9
|
+
|
|
10
|
+
const aliasesTemplatePath = path.join(homeDir, 'avantio', 'framework', 'core', 'include', 'bin', 'aliases.template.sh');
|
|
11
|
+
const pluginsTemplatePath = path.join(homeDir, 'avantio', 'framework', 'core', 'include', 'bin', 'plugins.template.sh');
|
|
12
|
+
|
|
13
|
+
const aliasesTarget = path.join(userRoot, 'aliases');
|
|
14
|
+
const pluginsTarget = path.join(userRoot, 'plugins');
|
|
15
|
+
|
|
16
|
+
await task('Verificando configuración de Tyr', async () => {
|
|
17
|
+
if (!fs.exists(userRoot)) {
|
|
18
|
+
fail(
|
|
19
|
+
'El directorio ~/.tyr no existe.',
|
|
20
|
+
"Ejecuta 'tyr --config' antes de continuar."
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
logger.success(`Directorio ~/.tyr encontrado: ${userRoot}`);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await task('Verificando templates de Avantio', async () => {
|
|
27
|
+
if (!fs.exists(aliasesTemplatePath)) {
|
|
28
|
+
fail(
|
|
29
|
+
`Template de aliases no encontrado: ${aliasesTemplatePath}`,
|
|
30
|
+
'Verifica que el framework de Avantio esté correctamente instalado.'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (!fs.exists(pluginsTemplatePath)) {
|
|
34
|
+
fail(
|
|
35
|
+
`Template de plugins no encontrado: ${pluginsTemplatePath}`,
|
|
36
|
+
'Verifica que el framework de Avantio esté correctamente instalado.'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
logger.success('Templates de Avantio encontrados.');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await task('Copiando aliases de Avantio', async () => {
|
|
43
|
+
const content = await fs.read(aliasesTemplatePath);
|
|
44
|
+
if (!content) fail('No se pudo leer aliases.template.sh');
|
|
45
|
+
await fs.write(aliasesTarget, content!);
|
|
46
|
+
logger.success(`Aliases copiados a: ${aliasesTarget}`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await task('Copiando plugins de Avantio', async () => {
|
|
50
|
+
const content = await fs.read(pluginsTemplatePath);
|
|
51
|
+
if (!content) fail('No se pudo leer plugins.template.sh');
|
|
52
|
+
await fs.write(pluginsTarget, content!);
|
|
53
|
+
logger.success(`Plugins copiados a: ${pluginsTarget}`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
logger.success('\nInstalación de Avantio completada.');
|
|
57
|
+
logger.info(` aliases → ${aliasesTarget}`);
|
|
58
|
+
logger.info(` plugins → ${pluginsTarget}`);
|
|
59
|
+
logger.warn('\nRecuerda recargar tu shell para aplicar los cambios.');
|
|
60
|
+
};
|
|
61
|
+
};
|
package/src/core/Kernel.ts
CHANGED
|
@@ -1,165 +1,206 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import yaml from 'js-yaml';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
import
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
import { Container } from './Container';
|
|
7
|
+
|
|
8
|
+
import gen from './sys/gen';
|
|
9
|
+
import rem from './sys/rem';
|
|
10
|
+
import doc from './sys/doc';
|
|
11
|
+
import ai from './sys/ai';
|
|
12
|
+
import config from './sys/config';
|
|
13
|
+
|
|
14
|
+
import { TyrError } from './TyrError';
|
|
15
|
+
|
|
16
|
+
interface TyrConfig {
|
|
17
|
+
commands: Record<string, string>;
|
|
18
|
+
aliases?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TyrContext {
|
|
22
|
+
frameworkRoot: string;
|
|
23
|
+
userRoot: string;
|
|
24
|
+
logger: any;
|
|
25
|
+
shell: any;
|
|
26
|
+
fs: any;
|
|
27
|
+
docker?: any;
|
|
28
|
+
run: (commandName: string, args?: string[]) => Promise<void>;
|
|
29
|
+
task: <T>(description: string, action: () => Promise<T> | T, next?: boolean, onFail?: () => void) => Promise<T | undefined>;
|
|
30
|
+
fail: (msg: string, suggestion?: string) => never;
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type CommandFunction = (args: string[]) => Promise<void>;
|
|
35
|
+
type CommandFactory = (context: TyrContext) => CommandFunction;
|
|
36
|
+
|
|
37
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
38
|
+
const __dirname = path.dirname(__filename);
|
|
39
|
+
|
|
40
|
+
export class Kernel {
|
|
41
|
+
private container: Container;
|
|
42
|
+
private config: TyrConfig | null;
|
|
43
|
+
private frameworkRoot: string;
|
|
44
|
+
private userRoot: string;
|
|
45
|
+
|
|
46
|
+
constructor() {
|
|
47
|
+
this.container = new Container();
|
|
48
|
+
this.config = null;
|
|
49
|
+
this.frameworkRoot = path.resolve(__dirname, '../../');
|
|
50
|
+
this.userRoot = path.join(homedir(), '.tyr');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async boot(args: string[]): Promise<void> {
|
|
54
|
+
const isDebug = args.includes('--debug');
|
|
55
|
+
await this.container.init(isDebug);
|
|
56
|
+
|
|
57
|
+
// All commands live in ~/.tyr/map.yml — the framework ships no runtime commands
|
|
58
|
+
this.config = { commands: {}, aliases: {} };
|
|
59
|
+
|
|
60
|
+
const userConfigPath = path.join(this.userRoot, 'map.yml');
|
|
61
|
+
if (fs.existsSync(userConfigPath)) {
|
|
62
|
+
try {
|
|
63
|
+
const raw = yaml.load(fs.readFileSync(userConfigPath, 'utf8')) as TyrConfig;
|
|
64
|
+
for (const [name, cmdPath] of Object.entries(raw.commands ?? {})) {
|
|
65
|
+
// Absolute paths used as-is; relative paths resolved from userRoot
|
|
66
|
+
this.config.commands[name] = path.isAbsolute(cmdPath)
|
|
67
|
+
? cmdPath
|
|
68
|
+
: path.resolve(this.userRoot, cmdPath);
|
|
69
|
+
}
|
|
70
|
+
this.config.aliases = raw.aliases ?? {};
|
|
71
|
+
} catch {
|
|
72
|
+
console.error(`Warning: could not load user config at ${userConfigPath}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public async handle(args: string[]): Promise<void> {
|
|
78
|
+
const commandName = args[0];
|
|
79
|
+
|
|
80
|
+
if (!commandName) {
|
|
81
|
+
console.log('Usage: tyr <command> [args...]');
|
|
82
|
+
console.log(' tyr --config Configure Tyr for the first time');
|
|
83
|
+
console.log(' tyr --version Show version');
|
|
84
|
+
console.log(' tyr --update Update Tyr to the latest version');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// --version / -v
|
|
89
|
+
if (commandName === '--version' || commandName === '-v') {
|
|
90
|
+
const pkgPath = path.resolve(this.frameworkRoot, 'package.json');
|
|
91
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
92
|
+
console.log(`tyr v${pkg.version}`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// --update
|
|
97
|
+
if (commandName === '--update') {
|
|
98
|
+
const pkgPath = path.resolve(this.frameworkRoot, 'package.json');
|
|
99
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
100
|
+
const shell = this.container.get().shell;
|
|
101
|
+
console.log(`Updating ${pkg.name}...`);
|
|
102
|
+
await shell.exec(`npm update -g ${pkg.name}`);
|
|
103
|
+
console.log('Update complete. Run tyr --version to confirm.');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const runInternal = async (cmd: string, cmdArgs: string[] = []) => {
|
|
108
|
+
await this.handle([cmd, ...cmdArgs]);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const task = async <T>(description: string, action: () => Promise<T> | T, next: boolean = false, onFail?: () => void): Promise<T | undefined> => {
|
|
112
|
+
try {
|
|
113
|
+
return await action();
|
|
114
|
+
} catch (e) {
|
|
115
|
+
if (onFail) onFail();
|
|
116
|
+
if (!next) {
|
|
117
|
+
throw new TyrError(
|
|
118
|
+
`Task failed: "${description}"`,
|
|
119
|
+
e,
|
|
120
|
+
'Check the previous logs or the configuration.'
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const context: TyrContext = {
|
|
127
|
+
...this.container.get(),
|
|
128
|
+
frameworkRoot: this.frameworkRoot,
|
|
129
|
+
userRoot: this.userRoot,
|
|
130
|
+
run: runInternal,
|
|
131
|
+
task,
|
|
132
|
+
fail: (msg: string, suggestion?: string) => { throw new TyrError(msg, null, suggestion, commandName); }
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// --config (needs context for fs/logger)
|
|
136
|
+
if (commandName === '--config') {
|
|
137
|
+
await config(context)([]);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const systemCommands: Record<string, CommandFactory> = {
|
|
142
|
+
gen,
|
|
143
|
+
rem,
|
|
144
|
+
doc,
|
|
145
|
+
ai,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
if (systemCommands[commandName]) {
|
|
149
|
+
await systemCommands[commandName](context)(args.slice(1));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!this.config) {
|
|
154
|
+
throw new Error('Kernel has not been initialized (run boot first).');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let scriptPath = this.config.commands[commandName];
|
|
158
|
+
|
|
159
|
+
if (!scriptPath && this.config.aliases?.[commandName]) {
|
|
160
|
+
const aliasTarget = this.config.aliases[commandName];
|
|
161
|
+
scriptPath = this.config.commands[aliasTarget];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!scriptPath) {
|
|
165
|
+
context.logger?.error(`Command '${commandName}' not found.`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
// Absolute paths (user commands) are used directly; relative paths resolve from frameworkRoot
|
|
171
|
+
const absolutePath = path.isAbsolute(scriptPath)
|
|
172
|
+
? scriptPath
|
|
173
|
+
: path.resolve(this.frameworkRoot, scriptPath);
|
|
174
|
+
|
|
175
|
+
const module = await import(absolutePath);
|
|
176
|
+
|
|
177
|
+
if (typeof module.default !== 'function') {
|
|
178
|
+
throw new Error(`File ${scriptPath} does not export a default function.`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const commandFactory: CommandFactory = module.default;
|
|
182
|
+
const command = commandFactory(context);
|
|
183
|
+
await command(args.slice(1));
|
|
184
|
+
|
|
185
|
+
} catch (error: any) {
|
|
186
|
+
this.handleError(error, args);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private handleError(error: unknown, args: string[]): void {
|
|
191
|
+
const isDebug = args.includes('--debug');
|
|
192
|
+
const logger = this.container.get().logger;
|
|
193
|
+
const commandName = args[0];
|
|
194
|
+
|
|
195
|
+
if (error instanceof TyrError) {
|
|
196
|
+
const enriched = error.commandName
|
|
197
|
+
? error
|
|
198
|
+
: new TyrError(error.message, error.originalError, error.suggestion, commandName);
|
|
199
|
+
enriched.handle(isDebug, logger);
|
|
200
|
+
} else {
|
|
201
|
+
(new TyrError('Unhandled critical error', error, undefined, commandName)).handle(isDebug, logger);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import yaml from 'js-yaml';
|
|
3
|
+
import { homedir, platform } from 'os';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
import type { TyrContext } from '../Kernel';
|
|
6
|
+
|
|
7
|
+
function detectShellRcFile(homeDir: string): string | null {
|
|
8
|
+
const shell = process.env.SHELL || '';
|
|
9
|
+
|
|
10
|
+
if (shell.includes('zsh')) return path.join(homeDir, '.zshrc');
|
|
11
|
+
if (shell.includes('fish')) return path.join(homeDir, '.config', 'fish', 'config.fish');
|
|
12
|
+
if (shell.includes('bash')) {
|
|
13
|
+
const candidates = [path.join(homeDir, '.bash_profile'), path.join(homeDir, '.bashrc')];
|
|
14
|
+
return candidates.find(p => existsSync(p)) ?? path.join(homeDir, '.bashrc');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const fallbacks = [
|
|
18
|
+
path.join(homeDir, '.zshrc'),
|
|
19
|
+
path.join(homeDir, '.bashrc'),
|
|
20
|
+
path.join(homeDir, '.bash_profile'),
|
|
21
|
+
];
|
|
22
|
+
return fallbacks.find(p => existsSync(p)) ?? path.join(homeDir, '.bashrc');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const SH_ALIASES_TEMPLATE = `# ~/.tyr/aliases
|
|
26
|
+
# Añade aquí tus aliases personalizados.
|
|
27
|
+
# Este archivo se carga automáticamente por tu shell.
|
|
28
|
+
#
|
|
29
|
+
# Ejemplos:
|
|
30
|
+
# alias gs='git status'
|
|
31
|
+
# alias tyr-deploy='tyr deploy'
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const SH_PLUGINS_TEMPLATE = `# ~/.tyr/plugins
|
|
35
|
+
# Añade aquí tus plugins de shell.
|
|
36
|
+
# Compatible con zsh, bash y otros shells POSIX.
|
|
37
|
+
#
|
|
38
|
+
# Ejemplos (zsh):
|
|
39
|
+
# source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const PS_ALIASES_TEMPLATE = `# ~/.tyr/aliases.ps1
|
|
43
|
+
# Añade aquí tus aliases personalizados para PowerShell.
|
|
44
|
+
#
|
|
45
|
+
# Ejemplos:
|
|
46
|
+
# Set-Alias gs git-status
|
|
47
|
+
# function tyr-deploy { tyr deploy @args }
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const PS_PLUGINS_TEMPLATE = `# ~/.tyr/plugins.ps1
|
|
51
|
+
# Añade aquí tus módulos y plugins de PowerShell.
|
|
52
|
+
#
|
|
53
|
+
# Ejemplos:
|
|
54
|
+
# Import-Module posh-git
|
|
55
|
+
# Import-Module PSReadLine
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
export default function config({ logger, fs: tyrFs, frameworkRoot }: TyrContext) {
|
|
59
|
+
return async (_args: string[]) => {
|
|
60
|
+
const homeDir = homedir();
|
|
61
|
+
const userRoot = path.join(homeDir, '.tyr');
|
|
62
|
+
const isWindows = platform() === 'win32';
|
|
63
|
+
const ext = isWindows ? '.ps1' : '';
|
|
64
|
+
|
|
65
|
+
logger.info('Iniciando configuración de Tyr...\n');
|
|
66
|
+
|
|
67
|
+
// 1. ~/.tyr/commands/
|
|
68
|
+
await tyrFs.createDir(path.join(userRoot, 'commands'));
|
|
69
|
+
logger.success(`Directorio creado: ${path.join(userRoot, 'commands')}`);
|
|
70
|
+
|
|
71
|
+
// 2. ~/.tyr/aliases(.ps1)
|
|
72
|
+
const aliasesPath = path.join(userRoot, `aliases${ext}`);
|
|
73
|
+
if (!tyrFs.exists(aliasesPath)) {
|
|
74
|
+
await tyrFs.write(aliasesPath, isWindows ? PS_ALIASES_TEMPLATE : SH_ALIASES_TEMPLATE);
|
|
75
|
+
logger.success(`Archivo creado: ${aliasesPath}`);
|
|
76
|
+
} else {
|
|
77
|
+
logger.info(`Ya existe: ${aliasesPath}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 3. ~/.tyr/plugins(.ps1)
|
|
81
|
+
const pluginsPath = path.join(userRoot, `plugins${ext}`);
|
|
82
|
+
if (!tyrFs.exists(pluginsPath)) {
|
|
83
|
+
await tyrFs.write(pluginsPath, isWindows ? PS_PLUGINS_TEMPLATE : SH_PLUGINS_TEMPLATE);
|
|
84
|
+
logger.success(`Archivo creado: ${pluginsPath}`);
|
|
85
|
+
} else {
|
|
86
|
+
logger.info(`Ya existe: ${pluginsPath}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 4. ~/.tyr/map.yml — create or update, registering framework commands with absolute paths
|
|
90
|
+
const mapPath = path.join(userRoot, 'map.yml');
|
|
91
|
+
const currentRaw = await tyrFs.read(mapPath);
|
|
92
|
+
const userConfig: { commands: Record<string, string> } =
|
|
93
|
+
(yaml.load(currentRaw ?? '') as any) ?? { commands: {} };
|
|
94
|
+
if (!userConfig.commands) userConfig.commands = {};
|
|
95
|
+
|
|
96
|
+
const frameworkMapPath = path.join(frameworkRoot, 'config', 'map.yml');
|
|
97
|
+
if (existsSync(frameworkMapPath)) {
|
|
98
|
+
const frameworkRaw = await tyrFs.read(frameworkMapPath);
|
|
99
|
+
const frameworkConfig = (yaml.load(frameworkRaw ?? '') as any) ?? {};
|
|
100
|
+
for (const [name, relPath] of Object.entries(frameworkConfig.commands ?? {})) {
|
|
101
|
+
const absPath = path.resolve(frameworkRoot, relPath as string);
|
|
102
|
+
if (existsSync(absPath) && !userConfig.commands[name]) {
|
|
103
|
+
userConfig.commands[name] = absPath;
|
|
104
|
+
logger.info(` Comando registrado: ${name}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await tyrFs.write(mapPath, yaml.dump(userConfig, { indent: 2, lineWidth: -1 }));
|
|
110
|
+
logger.success(`Configuración guardada: ${mapPath}`);
|
|
111
|
+
|
|
112
|
+
// 5. Configure shell to source aliases and plugins
|
|
113
|
+
logger.info('\nConfigurando shell...');
|
|
114
|
+
if (isWindows) {
|
|
115
|
+
await configureWindowsShell(tyrFs, logger, aliasesPath, pluginsPath);
|
|
116
|
+
} else {
|
|
117
|
+
await configureUnixShell(tyrFs, logger, homeDir, aliasesPath, pluginsPath);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
logger.success('\nTyr configurado correctamente.');
|
|
121
|
+
logger.info(`\nDirectorio de configuración: ${userRoot}`);
|
|
122
|
+
logger.info('\nPróximos pasos:');
|
|
123
|
+
logger.info(' tyr gen <nombre> <archivo> Crear un nuevo comando');
|
|
124
|
+
logger.info(' tyr doc Ver documentación de la API');
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function configureUnixShell(
|
|
129
|
+
tyrFs: any, logger: any,
|
|
130
|
+
homeDir: string, aliasesPath: string, pluginsPath: string
|
|
131
|
+
): Promise<void> {
|
|
132
|
+
const rcFile = detectShellRcFile(homeDir);
|
|
133
|
+
if (!rcFile) {
|
|
134
|
+
logger.warn('No se pudo detectar el archivo de configuración del shell.');
|
|
135
|
+
logger.info(`Añade manualmente:\n source "${aliasesPath}"\n source "${pluginsPath}"`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
await tyrFs.ensureLine(rcFile, `source "${aliasesPath}"`);
|
|
139
|
+
await tyrFs.ensureLine(rcFile, `source "${pluginsPath}"`);
|
|
140
|
+
logger.success(`Shell configurado: ${rcFile}`);
|
|
141
|
+
logger.info(`Ejecuta: source ${rcFile} (o abre una nueva terminal)`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function configureWindowsShell(
|
|
145
|
+
tyrFs: any, logger: any,
|
|
146
|
+
aliasesPath: string, pluginsPath: string
|
|
147
|
+
): Promise<void> {
|
|
148
|
+
const psProfile = process.env.USERPROFILE
|
|
149
|
+
? path.join(process.env.USERPROFILE, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1')
|
|
150
|
+
: null;
|
|
151
|
+
|
|
152
|
+
if (!psProfile) {
|
|
153
|
+
logger.warn('No se pudo detectar el perfil de PowerShell.');
|
|
154
|
+
logger.info(`Añade manualmente:\n . "${aliasesPath}"\n . "${pluginsPath}"`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
await tyrFs.ensureLine(psProfile, `. "${aliasesPath}"`);
|
|
158
|
+
await tyrFs.ensureLine(psProfile, `. "${pluginsPath}"`);
|
|
159
|
+
logger.success(`Perfil de PowerShell configurado: ${psProfile}`);
|
|
160
|
+
logger.info('Reinicia PowerShell para aplicar los cambios.');
|
|
161
|
+
}
|
package/src/core/sys/gen.ts
CHANGED
|
@@ -1,72 +1,73 @@
|
|
|
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
|
-
export
|
|
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
|
-
config.commands[commandName] = `./
|
|
62
|
-
|
|
63
|
-
const newYaml = yaml.dump(config, { indent: 2, lineWidth: -1 });
|
|
64
|
-
await fs.write(
|
|
65
|
-
|
|
66
|
-
logger.success(
|
|
67
|
-
|
|
68
|
-
|
|
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 = `export default ({ run, task, fail, logger, shell, fs }: any) => {
|
|
11
|
+
return async (args: string[]) => {
|
|
12
|
+
logger.info("Ejecutando comando: %s");
|
|
13
|
+
|
|
14
|
+
// Tu lógica aquí...
|
|
15
|
+
// Ejecuta "tyr doc" para ver la documentación de managers disponibles
|
|
16
|
+
|
|
17
|
+
logger.success("¡Comando %s finalizado!");
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Test = { args: [] };
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export default function gen({ logger, fs, userRoot }: TyrContext) {
|
|
25
|
+
return async (args: string[]) => {
|
|
26
|
+
const commandName = args[0];
|
|
27
|
+
const fileName = args[1];
|
|
28
|
+
|
|
29
|
+
if (!commandName || !fileName) {
|
|
30
|
+
logger.error('Uso incorrecto.');
|
|
31
|
+
logger.info('Sintaxis: tyr gen [nombre-comando] [nombre-archivo]');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
logger.info(`Creando nuevo comando: '${commandName}' -> '${fileName}.tyr.ts'`);
|
|
36
|
+
|
|
37
|
+
const commandsDir = path.join(userRoot, 'commands');
|
|
38
|
+
const filePath = path.join(commandsDir, `${fileName}.tyr.ts`);
|
|
39
|
+
|
|
40
|
+
if (fs.exists(filePath)) {
|
|
41
|
+
logger.error(`El archivo ${fileName}.tyr.ts ya existe. Abortando.`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const templateFilled = template.replaceAll('%s', commandName);
|
|
46
|
+
await fs.write(filePath, templateFilled.trim());
|
|
47
|
+
|
|
48
|
+
// Register in ~/.tyr/map.yml
|
|
49
|
+
const mapPath = path.join(userRoot, 'map.yml');
|
|
50
|
+
try {
|
|
51
|
+
const currentConfigRaw = await fs.read(mapPath);
|
|
52
|
+
const config = (yaml.load(currentConfigRaw ?? 'commands: {}') ?? { commands: {} }) as TyrConfig;
|
|
53
|
+
|
|
54
|
+
if (!config.commands) config.commands = {};
|
|
55
|
+
|
|
56
|
+
if (config.commands[commandName]) {
|
|
57
|
+
logger.warn(`El comando '${commandName}' ya existía. Actualizando ruta...`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Store path relative to userRoot so it remains portable
|
|
61
|
+
config.commands[commandName] = `./commands/${fileName}.tyr.ts`;
|
|
62
|
+
|
|
63
|
+
const newYaml = yaml.dump(config, { indent: 2, lineWidth: -1 });
|
|
64
|
+
await fs.write(mapPath, newYaml);
|
|
65
|
+
|
|
66
|
+
logger.success(`Comando '${commandName}' creado en ${filePath}`);
|
|
67
|
+
logger.success(`Registrado en ${mapPath}`);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
logger.error('Error al actualizar la configuración.');
|
|
70
|
+
console.error(e);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
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/lib/SQLManager.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import 'dotenv/config';
|
|
2
1
|
import dotenv from 'dotenv';
|
|
3
2
|
import path from 'path';
|
|
4
|
-
import {
|
|
3
|
+
import { homedir } from 'os';
|
|
5
4
|
import sql, { config as SQLConfig } from 'mssql';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
|
|
6
|
+
// Load credentials from ~/.tyr/.env — silently, optional
|
|
7
|
+
(dotenv as any).config({ path: path.join(homedir(), '.tyr', '.env'), quiet: true });
|
|
11
8
|
|
|
12
9
|
/**
|
|
13
10
|
* @class SQLManager
|