g360-cli 1.12.0 → 1.13.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 +141 -12
- package/package.json +1 -1
- package/src/assets/brand/brand.json +1 -3
- package/src/assets/templates/ARCHITECTURE.md +50 -0
- package/src/assets/templates/lit-web/README.md +47 -0
- package/src/assets/templates/python-cli/README.md +47 -0
- package/src/assets/templates/python-customtkinter/README.md +45 -34
- package/src/assets/templates/python-flet/README.md +46 -66
- package/src/assets/templates/python-flet-migrate/README.md +35 -55
- package/src/assets/templates/solid-web/README.md +47 -0
- package/src/assets/templates/svelte-web/README.md +47 -0
- package/src/assets/templates/web-pwa/README.md +47 -0
- package/src/cli.js +16 -0
- package/src/commands/clean.js +2 -23
- package/src/commands/convert.js +1 -23
- package/src/commands/docs.js +815 -0
- package/src/commands/init.js +46 -4
- package/src/commands/lint.js +578 -0
- package/src/lib/file-utils.js +27 -0
- package/src/assets/brand/g360/logotypes/logo_g360_dark_b64.txt +0 -1
- package/src/assets/brand/g360/logotypes/logo_g360_light_b64.txt +0 -1
- package/src/lib/global-config.js +0 -260
- package/src/lib/i18n.js +0 -238
- package/src/lib/python_runner.js +0 -89
package/src/lib/global-config.js
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import { logger } from './logger.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} GlobalConfig
|
|
8
|
-
* @property {string} defaultSkill - Skill por defecto
|
|
9
|
-
* @property {string} defaultTemplate - Plantilla por defecto
|
|
10
|
-
* @property {Object} preferences - Preferencias del usuario
|
|
11
|
-
* @property {string[]} recentProjects - Proyectos recientes
|
|
12
|
-
* @property {Object} cache - Configuración de caché
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Ruta del directorio de configuración global
|
|
17
|
-
* @type {string}
|
|
18
|
-
*/
|
|
19
|
-
const G360_CONFIG_DIR = path.join(os.homedir(), '.g360');
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Ruta del archivo de configuración global
|
|
23
|
-
* @type {string}
|
|
24
|
-
*/
|
|
25
|
-
const G360_CONFIG_FILE = path.join(G360_CONFIG_DIR, 'config.json');
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Configuración por defecto
|
|
29
|
-
* @type {GlobalConfig}
|
|
30
|
-
*/
|
|
31
|
-
const DEFAULT_CONFIG = {
|
|
32
|
-
defaultSkill: 'corporativo-movil',
|
|
33
|
-
defaultTemplate: 'web-pwa',
|
|
34
|
-
preferences: {
|
|
35
|
-
language: 'es',
|
|
36
|
-
theme: 'dark',
|
|
37
|
-
verbose: false
|
|
38
|
-
},
|
|
39
|
-
recentProjects: [],
|
|
40
|
-
cache: {
|
|
41
|
-
enabled: true,
|
|
42
|
-
maxSize: 100
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Módulo de configuración global para G360-CLI
|
|
48
|
-
* @namespace globalConfig
|
|
49
|
-
*/
|
|
50
|
-
export const globalConfig = {
|
|
51
|
-
/**
|
|
52
|
-
* Obtiene la ruta del directorio de configuración
|
|
53
|
-
* @returns {string} Ruta del directorio de configuración
|
|
54
|
-
* @example
|
|
55
|
-
* const configDir = globalConfig.getConfigDir();
|
|
56
|
-
* console.log('Config dir:', configDir);
|
|
57
|
-
*/
|
|
58
|
-
getConfigDir() {
|
|
59
|
-
return G360_CONFIG_DIR;
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Obtiene la ruta del archivo de configuración
|
|
64
|
-
* @returns {string} Ruta del archivo de configuración
|
|
65
|
-
* @example
|
|
66
|
-
* const configFile = globalConfig.getConfigFile();
|
|
67
|
-
* console.log('Config file:', configFile);
|
|
68
|
-
*/
|
|
69
|
-
getConfigFile() {
|
|
70
|
-
return G360_CONFIG_FILE;
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Inicializa el directorio de configuración global
|
|
75
|
-
* @returns {Promise<void>}
|
|
76
|
-
* @example
|
|
77
|
-
* await globalConfig.init();
|
|
78
|
-
*/
|
|
79
|
-
async init() {
|
|
80
|
-
try {
|
|
81
|
-
await fs.ensureDir(G360_CONFIG_DIR);
|
|
82
|
-
|
|
83
|
-
if (!fs.existsSync(G360_CONFIG_FILE)) {
|
|
84
|
-
await this.save(DEFAULT_CONFIG);
|
|
85
|
-
logger.info('Global config initialized', { path: G360_CONFIG_FILE });
|
|
86
|
-
}
|
|
87
|
-
} catch (error) {
|
|
88
|
-
logger.error('Failed to initialize global config', { error: error.message });
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Carga la configuración global
|
|
95
|
-
* @returns {Promise<GlobalConfig>} Configuración global cargada
|
|
96
|
-
* @example
|
|
97
|
-
* const config = await globalConfig.load();
|
|
98
|
-
* console.log('Default skill:', config.defaultSkill);
|
|
99
|
-
*/
|
|
100
|
-
async load() {
|
|
101
|
-
try {
|
|
102
|
-
if (!fs.existsSync(G360_CONFIG_FILE)) {
|
|
103
|
-
await this.init();
|
|
104
|
-
return { ...DEFAULT_CONFIG };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const config = await fs.readJson(G360_CONFIG_FILE);
|
|
108
|
-
return { ...DEFAULT_CONFIG, ...config };
|
|
109
|
-
} catch (error) {
|
|
110
|
-
logger.error('Failed to load global config', { error: error.message });
|
|
111
|
-
return { ...DEFAULT_CONFIG };
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Guarda la configuración global
|
|
117
|
-
* @param {GlobalConfig} config - Configuración a guardar
|
|
118
|
-
* @returns {Promise<void>}
|
|
119
|
-
* @example
|
|
120
|
-
* await globalConfig.save({ defaultSkill: 'moderno' });
|
|
121
|
-
*/
|
|
122
|
-
async save(config) {
|
|
123
|
-
try {
|
|
124
|
-
await fs.ensureDir(G360_CONFIG_DIR);
|
|
125
|
-
await fs.writeJson(G360_CONFIG_FILE, config, { spaces: 2 });
|
|
126
|
-
logger.debug('Global config saved');
|
|
127
|
-
} catch (error) {
|
|
128
|
-
logger.error('Failed to save global config', { error: error.message });
|
|
129
|
-
throw error;
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Obtiene un valor de configuración específico
|
|
135
|
-
* @param {string} key - Clave de configuración (notación de puntos)
|
|
136
|
-
* @param {*} defaultValue - Valor por defecto si no existe
|
|
137
|
-
* @returns {Promise<*>} Valor de configuración
|
|
138
|
-
* @example
|
|
139
|
-
* const skill = await globalConfig.get('defaultSkill');
|
|
140
|
-
* const theme = await globalConfig.get('preferences.theme', 'dark');
|
|
141
|
-
*/
|
|
142
|
-
async get(key, defaultValue = undefined) {
|
|
143
|
-
const config = await this.load();
|
|
144
|
-
|
|
145
|
-
const keys = key.split('.');
|
|
146
|
-
let value = config;
|
|
147
|
-
|
|
148
|
-
for (const k of keys) {
|
|
149
|
-
if (value && typeof value === 'object' && k in value) {
|
|
150
|
-
value = value[k];
|
|
151
|
-
} else {
|
|
152
|
-
return defaultValue;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return value;
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Establece un valor de configuración específico
|
|
161
|
-
* @param {string} key - Clave de configuración (notación de puntos)
|
|
162
|
-
* @param {*} value - Valor a establecer
|
|
163
|
-
* @returns {Promise<void>}
|
|
164
|
-
* @example
|
|
165
|
-
* await globalConfig.set('defaultSkill', 'moderno');
|
|
166
|
-
* await globalConfig.set('preferences.theme', 'light');
|
|
167
|
-
*/
|
|
168
|
-
async set(key, value) {
|
|
169
|
-
const config = await this.load();
|
|
170
|
-
|
|
171
|
-
const keys = key.split('.');
|
|
172
|
-
let current = config;
|
|
173
|
-
|
|
174
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
175
|
-
const k = keys[i];
|
|
176
|
-
if (!current[k] || typeof current[k] !== 'object') {
|
|
177
|
-
current[k] = {};
|
|
178
|
-
}
|
|
179
|
-
current = current[k];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
current[keys[keys.length - 1]] = value;
|
|
183
|
-
await this.save(config);
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Agrega un proyecto a la lista de proyectos recientes
|
|
188
|
-
* @param {string} projectPath - Ruta del proyecto
|
|
189
|
-
* @param {Object} metadata - Metadatos del proyecto
|
|
190
|
-
* @returns {Promise<void>}
|
|
191
|
-
* @example
|
|
192
|
-
* await globalConfig.addRecentProject('/my/project', {
|
|
193
|
-
* template: 'web-pwa',
|
|
194
|
-
* skill: 'corporativo-movil'
|
|
195
|
-
* });
|
|
196
|
-
*/
|
|
197
|
-
async addRecentProject(projectPath, metadata = {}) {
|
|
198
|
-
const config = await this.load();
|
|
199
|
-
|
|
200
|
-
// Eliminar el proyecto si ya existe
|
|
201
|
-
config.recentProjects = config.recentProjects.filter(
|
|
202
|
-
p => p.path !== projectPath
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
// Agregar al inicio
|
|
206
|
-
config.recentProjects.unshift({
|
|
207
|
-
path: projectPath,
|
|
208
|
-
lastAccessed: new Date().toISOString(),
|
|
209
|
-
...metadata
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
// Mantener solo los últimos 10 proyectos
|
|
213
|
-
config.recentProjects = config.recentProjects.slice(0, 10);
|
|
214
|
-
|
|
215
|
-
await this.save(config);
|
|
216
|
-
logger.debug('Project added to recent projects', { path: projectPath });
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Obtiene la lista de proyectos recientes
|
|
221
|
-
* @returns {Promise<Array>} Lista de proyectos recientes
|
|
222
|
-
* @example
|
|
223
|
-
* const recent = await globalConfig.getRecentProjects();
|
|
224
|
-
* recent.forEach(project => console.log(project.path));
|
|
225
|
-
*/
|
|
226
|
-
async getRecentProjects() {
|
|
227
|
-
const config = await this.load();
|
|
228
|
-
return config.recentProjects || [];
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Limpia la configuración global
|
|
233
|
-
* @returns {Promise<void>}
|
|
234
|
-
* @example
|
|
235
|
-
* await globalConfig.clear();
|
|
236
|
-
*/
|
|
237
|
-
async clear() {
|
|
238
|
-
try {
|
|
239
|
-
if (fs.existsSync(G360_CONFIG_FILE)) {
|
|
240
|
-
await fs.remove(G360_CONFIG_FILE);
|
|
241
|
-
logger.info('Global config cleared');
|
|
242
|
-
}
|
|
243
|
-
} catch (error) {
|
|
244
|
-
logger.error('Failed to clear global config', { error: error.message });
|
|
245
|
-
throw error;
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Verifica si la configuración global existe
|
|
251
|
-
* @returns {boolean} true si existe, false en caso contrario
|
|
252
|
-
* @example
|
|
253
|
-
* if (await globalConfig.exists()) {
|
|
254
|
-
* console.log('Config exists');
|
|
255
|
-
* }
|
|
256
|
-
*/
|
|
257
|
-
async exists() {
|
|
258
|
-
return fs.existsSync(G360_CONFIG_FILE);
|
|
259
|
-
}
|
|
260
|
-
};
|
package/src/lib/i18n.js
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file i18n.js
|
|
3
|
-
* @description Sistema de internacionalización para G360-CLI
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Traducciones disponibles
|
|
8
|
-
* @type {Object.<string, Object.<string, string>>}
|
|
9
|
-
*/
|
|
10
|
-
const translations = {
|
|
11
|
-
es: {
|
|
12
|
-
// Comandos
|
|
13
|
-
'command.init.title': '🚀 Inicialización de Proyecto G360',
|
|
14
|
-
'command.init.project': 'Proyecto',
|
|
15
|
-
'command.init.template': 'Plantilla',
|
|
16
|
-
'command.init.skill': 'Skill',
|
|
17
|
-
'command.init.target': 'Destino',
|
|
18
|
-
'command.init.success': '✅ Proyecto creado exitosamente',
|
|
19
|
-
'command.init.portable': '📦 Versión portable habilitada',
|
|
20
|
-
'command.init.next_steps': 'Próximos pasos',
|
|
21
|
-
|
|
22
|
-
'command.list.title': '📋 Assets G360',
|
|
23
|
-
'command.list.templates': '📁 Plantillas',
|
|
24
|
-
'command.list.components': '🧩 Componentes',
|
|
25
|
-
'command.list.skills': '⚡ Skills',
|
|
26
|
-
'command.list.snippets': '📝 Snippets',
|
|
27
|
-
'command.list.no_templates': 'No se encontraron plantillas',
|
|
28
|
-
'command.list.no_components': 'No se encontraron componentes',
|
|
29
|
-
'command.list.no_skills': 'No se encontraron skills',
|
|
30
|
-
'command.list.no_snippets': 'No se encontraron snippets',
|
|
31
|
-
|
|
32
|
-
'command.set-skill.title': '🎨 Selector de Skill G360',
|
|
33
|
-
'command.set-skill.success': '✅ Skill configurado correctamente',
|
|
34
|
-
'command.set-skill.not_found': '❌ Skill no encontrado',
|
|
35
|
-
'command.set-skill.already_exists': '⚠️ El proyecto ya tiene un skill configurado',
|
|
36
|
-
'command.set-skill.use_force': 'Usa --force para sobrescribir',
|
|
37
|
-
|
|
38
|
-
'command.audit.title': '🔍 Auditoría de Proyecto G360',
|
|
39
|
-
'command.audit.results': '📊 Resultados de Auditoría',
|
|
40
|
-
'command.audit.passed': 'Pasaron',
|
|
41
|
-
'command.audit.failed': 'Fallaron',
|
|
42
|
-
'command.audit.warnings': 'Advertencias',
|
|
43
|
-
'command.audit.issues': 'Issues Encontrados',
|
|
44
|
-
'command.audit.compliant': '✅ El proyecto es compliant con G360',
|
|
45
|
-
'command.audit.issues_found': '⚠️ Algunos issues necesitan atención',
|
|
46
|
-
|
|
47
|
-
'command.health.title': '🏥 Verificación de Salud G360',
|
|
48
|
-
'command.health.status': 'Estado del Sistema',
|
|
49
|
-
'command.health.healthy': '✅ El sistema está saludable',
|
|
50
|
-
'command.health.failed': '❌ Algunos checks fallaron',
|
|
51
|
-
|
|
52
|
-
'command.clean.title': '🧹 Limpieza de Proyecto G360',
|
|
53
|
-
'command.clean.preview': '📋 DRY RUN - No se eliminarán archivos',
|
|
54
|
-
'command.clean.success': '✅ Limpieza completada',
|
|
55
|
-
|
|
56
|
-
// Errores
|
|
57
|
-
'error.templates_not_found': '❌ Plantillas no encontradas. Ejecuta: g360 update',
|
|
58
|
-
'error.template_not_found': '❌ Plantilla no encontrada',
|
|
59
|
-
'error.directory_exists': '❌ El directorio ya existe. Usa --force para sobrescribir',
|
|
60
|
-
'error.path_not_found': '❌ Ruta no encontrada',
|
|
61
|
-
'error.config_not_found': '❌ No se pudo cargar la configuración de skills',
|
|
62
|
-
|
|
63
|
-
// General
|
|
64
|
-
'general.available': 'Disponibles',
|
|
65
|
-
'general.device': 'Dispositivo',
|
|
66
|
-
'general.description': 'Descripción',
|
|
67
|
-
'general.language': 'Lenguaje',
|
|
68
|
-
'general.loading': 'Cargando...',
|
|
69
|
-
'general.done': 'Completado',
|
|
70
|
-
'general.cancelled': 'Cancelado',
|
|
71
|
-
'general.error': 'Error',
|
|
72
|
-
'general.warning': 'Advertencia',
|
|
73
|
-
'general.info': 'Información',
|
|
74
|
-
'general.success': 'Éxito'
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
en: {
|
|
78
|
-
// Commands
|
|
79
|
-
'command.init.title': '🚀 G360 Project Initialization',
|
|
80
|
-
'command.init.project': 'Project',
|
|
81
|
-
'command.init.template': 'Template',
|
|
82
|
-
'command.init.skill': 'Skill',
|
|
83
|
-
'command.init.target': 'Target',
|
|
84
|
-
'command.init.success': '✅ Project created successfully',
|
|
85
|
-
'command.init.portable': '📦 Portable version enabled',
|
|
86
|
-
'command.init.next_steps': 'Next steps',
|
|
87
|
-
|
|
88
|
-
'command.list.title': '📋 G360 Assets',
|
|
89
|
-
'command.list.templates': '📁 Templates',
|
|
90
|
-
'command.list.components': '🧩 Components',
|
|
91
|
-
'command.list.skills': '⚡ Skills',
|
|
92
|
-
'command.list.snippets': '📝 Snippets',
|
|
93
|
-
'command.list.no_templates': 'No templates found',
|
|
94
|
-
'command.list.no_components': 'No components found',
|
|
95
|
-
'command.list.no_skills': 'No skills found',
|
|
96
|
-
'command.list.no_snippets': 'No snippets found',
|
|
97
|
-
|
|
98
|
-
'command.set-skill.title': '🎨 G360 Skill Selector',
|
|
99
|
-
'command.set-skill.success': '✅ Skill configured successfully',
|
|
100
|
-
'command.set-skill.not_found': '❌ Skill not found',
|
|
101
|
-
'command.set-skill.already_exists': '⚠️ Project already has a skill configured',
|
|
102
|
-
'command.set-skill.use_force': 'Use --force to overwrite',
|
|
103
|
-
|
|
104
|
-
'command.audit.title': '🔍 G360 Project Audit',
|
|
105
|
-
'command.audit.results': '📊 Audit Results',
|
|
106
|
-
'command.audit.passed': 'Passed',
|
|
107
|
-
'command.audit.failed': 'Failed',
|
|
108
|
-
'command.audit.warnings': 'Warnings',
|
|
109
|
-
'command.audit.issues': 'Issues Found',
|
|
110
|
-
'command.audit.compliant': '✅ Project is G360 compliant',
|
|
111
|
-
'command.audit.issues_found': '⚠️ Some issues need attention',
|
|
112
|
-
|
|
113
|
-
'command.health.title': '🏥 G360 Health Check',
|
|
114
|
-
'command.health.status': 'System Status',
|
|
115
|
-
'command.health.healthy': '✅ System is healthy',
|
|
116
|
-
'command.health.failed': '❌ Some checks failed',
|
|
117
|
-
|
|
118
|
-
'command.clean.title': '🧹 G360 Project Clean',
|
|
119
|
-
'command.clean.preview': '📋 DRY RUN - No files will be removed',
|
|
120
|
-
'command.clean.success': '✅ Cleanup completed',
|
|
121
|
-
|
|
122
|
-
// Errors
|
|
123
|
-
'error.templates_not_found': '❌ Templates not found. Run: g360 update',
|
|
124
|
-
'error.template_not_found': '❌ Template not found',
|
|
125
|
-
'error.directory_exists': '❌ Directory already exists. Use --force to overwrite',
|
|
126
|
-
'error.path_not_found': '❌ Path not found',
|
|
127
|
-
'error.config_not_found': '❌ Could not load skills configuration',
|
|
128
|
-
|
|
129
|
-
// General
|
|
130
|
-
'general.available': 'Available',
|
|
131
|
-
'general.device': 'Device',
|
|
132
|
-
'general.description': 'Description',
|
|
133
|
-
'general.language': 'Language',
|
|
134
|
-
'general.loading': 'Loading...',
|
|
135
|
-
'general.done': 'Done',
|
|
136
|
-
'general.cancelled': 'Cancelled',
|
|
137
|
-
'general.error': 'Error',
|
|
138
|
-
'general.warning': 'Warning',
|
|
139
|
-
'general.info': 'Info',
|
|
140
|
-
'general.success': 'Success'
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Idioma actual
|
|
146
|
-
* @type {string}
|
|
147
|
-
*/
|
|
148
|
-
let currentLanguage = 'es';
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Módulo de internacionalización para G360-CLI
|
|
152
|
-
* @namespace i18n
|
|
153
|
-
*/
|
|
154
|
-
export const i18n = {
|
|
155
|
-
/**
|
|
156
|
-
* Establece el idioma actual
|
|
157
|
-
* @param {string} language - Código de idioma ('es' | 'en')
|
|
158
|
-
* @returns {void}
|
|
159
|
-
* @example
|
|
160
|
-
* i18n.setLanguage('en');
|
|
161
|
-
*/
|
|
162
|
-
setLanguage(language) {
|
|
163
|
-
if (translations[language]) {
|
|
164
|
-
currentLanguage = language;
|
|
165
|
-
} else {
|
|
166
|
-
console.warn(`Language '${language}' not supported, using 'es'`);
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Obtiene el idioma actual
|
|
172
|
-
* @returns {string} Código de idioma actual
|
|
173
|
-
* @example
|
|
174
|
-
* const lang = i18n.getLanguage();
|
|
175
|
-
*/
|
|
176
|
-
getLanguage() {
|
|
177
|
-
return currentLanguage;
|
|
178
|
-
},
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Traduce una clave de texto
|
|
182
|
-
* @param {string} key - Clave de traducción
|
|
183
|
-
* @param {Object} [params] - Parámetros para interpolación
|
|
184
|
-
* @returns {string} Texto traducido
|
|
185
|
-
* @example
|
|
186
|
-
* const text = i18n.t('command.init.success');
|
|
187
|
-
* const text = i18n.t('command.init.project', { name: 'my-project' });
|
|
188
|
-
*/
|
|
189
|
-
t(key, params = {}) {
|
|
190
|
-
const lang = translations[currentLanguage] || translations.es;
|
|
191
|
-
let text = lang[key] || translations.es[key] || key;
|
|
192
|
-
|
|
193
|
-
// Interpolación de parámetros
|
|
194
|
-
Object.keys(params).forEach(param => {
|
|
195
|
-
text = text.replace(`{${param}}`, params[param]);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
return text;
|
|
199
|
-
},
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Verifica si una clave de traducción existe
|
|
203
|
-
* @param {string} key - Clave de traducción
|
|
204
|
-
* @returns {boolean} true si existe, false en caso contrario
|
|
205
|
-
* @example
|
|
206
|
-
* if (i18n.has('command.init.success')) {
|
|
207
|
-
* console.log(i18n.t('command.init.success'));
|
|
208
|
-
* }
|
|
209
|
-
*/
|
|
210
|
-
has(key) {
|
|
211
|
-
const lang = translations[currentLanguage] || translations.es;
|
|
212
|
-
return key in lang;
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Obtiene todos los idiomas disponibles
|
|
217
|
-
* @returns {string[]} Lista de códigos de idioma
|
|
218
|
-
* @example
|
|
219
|
-
* const languages = i18n.getAvailableLanguages();
|
|
220
|
-
* console.log('Available languages:', languages);
|
|
221
|
-
*/
|
|
222
|
-
getAvailableLanguages() {
|
|
223
|
-
return Object.keys(translations);
|
|
224
|
-
},
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Obtiene las traducciones para un idioma específico
|
|
228
|
-
* @param {string} language - Código de idioma
|
|
229
|
-
* @returns {Object|null} Objeto de traducciones o null si no existe
|
|
230
|
-
* @example
|
|
231
|
-
* const esTranslations = i18n.getTranslations('es');
|
|
232
|
-
*/
|
|
233
|
-
getTranslations(language) {
|
|
234
|
-
return translations[language] || null;
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
export default i18n;
|
package/src/lib/python_runner.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Utilidad para obtener la ruta al módulo Python empaquetado.
|
|
4
|
-
* Funciona tanto en desarrollo como en ejecutables empaquetados con pkg.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import { fileURLToPath } from 'url';
|
|
9
|
-
import fs from 'fs-extra';
|
|
10
|
-
|
|
11
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Retorna la ruta al directorio src/g360_core del módulo Python.
|
|
15
|
-
*/
|
|
16
|
-
export function getPythonModulePath() {
|
|
17
|
-
// Ruta de desarrollo: py/src/g360_core relativo a este archivo
|
|
18
|
-
const devPath = path.join(__dirname, '..', 'py', 'src');
|
|
19
|
-
if (fs.existsSync(devPath)) {
|
|
20
|
-
return devPath;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// En pkg (ejecutable empaquetado)
|
|
24
|
-
const basePath = process.execPath || process.argv[0];
|
|
25
|
-
const pkgPath = path.join(basePath, '..', 'resources', 'py', 'src');
|
|
26
|
-
if (fs.existsSync(pkgPath)) {
|
|
27
|
-
return pkgPath;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Fallback: directorio actual
|
|
31
|
-
const cwdPath = path.join(process.cwd(), 'py', 'src');
|
|
32
|
-
if (fs.existsSync(cwdPath)) {
|
|
33
|
-
return cwdPath;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return devPath;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Genera código Python que configura sys.path correctamente.
|
|
41
|
-
*/
|
|
42
|
-
export function wrapPythonCode(pyCode) {
|
|
43
|
-
const modulePath = getPythonModulePath().replace(/\\/g, '\\\\');
|
|
44
|
-
|
|
45
|
-
return `
|
|
46
|
-
import sys
|
|
47
|
-
import os
|
|
48
|
-
from pathlib import Path
|
|
49
|
-
|
|
50
|
-
module_path = r"${modulePath}"
|
|
51
|
-
if module_path not in sys.path:
|
|
52
|
-
sys.path.insert(0, module_path)
|
|
53
|
-
|
|
54
|
-
${pyCode}
|
|
55
|
-
`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Ejecuta código Python y retorna su salida.
|
|
60
|
-
*/
|
|
61
|
-
export async function runPython(pyCode) {
|
|
62
|
-
const fullCode = wrapPythonCode(pyCode);
|
|
63
|
-
const pyExec = process.env.PYTHON || 'python3';
|
|
64
|
-
|
|
65
|
-
const { spawn } = await import('child_process');
|
|
66
|
-
const proc = spawn(pyExec, ['-c', fullCode], {
|
|
67
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
68
|
-
env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
let stdout = '';
|
|
72
|
-
let stderr = '';
|
|
73
|
-
|
|
74
|
-
await new Promise((resolve, reject) => {
|
|
75
|
-
proc.stdout?.on('data', (data) => { stdout += data.toString(); });
|
|
76
|
-
proc.stderr?.on('data', (data) => { stderr += data.toString(); });
|
|
77
|
-
|
|
78
|
-
proc.on('close', (code) => {
|
|
79
|
-
if (code === 0) resolve();
|
|
80
|
-
else reject(new Error(stderr || `Python código ${code}`));
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
proc.on('error', (err) => {
|
|
84
|
-
reject(new Error(`Python no disponible: ${err.message}`));
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
return { stdout, stderr };
|
|
89
|
-
}
|