g360-cli 1.3.0 → 1.5.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 +209 -5
- package/package.json +15 -8
- package/src/assets/brand/brand.json +43 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_borde.png +0 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_borde.svg +9 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_solid.png +0 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_solid.svg +9 -0
- package/src/assets/brand/g360/logotypes/logo-g360-dark.svg +19 -0
- package/src/assets/brand/g360/logotypes/logo-g360-light.svg +19 -0
- package/src/assets/brand/g360/logotypes/logo_g360_dark_b64.txt +1 -0
- package/src/assets/brand/g360/logotypes/logo_g360_light_b64.txt +1 -0
- package/src/assets/config/g360-skills.json +112 -0
- package/src/assets/config/project-types.json +16 -0
- package/src/assets/config/skills.json +12 -0
- package/src/assets/snippets/snippets.json +293 -24
- package/src/assets/templates/lit-web/src/core/skill.json +12 -5
- package/src/assets/templates/python-cli/build-portable.bat +43 -0
- package/src/assets/templates/python-cli/src/core/skill.json +25 -6
- package/src/assets/templates/python-cli/src/main.py +107 -3
- package/src/assets/templates/python-customtkinter/README.md +48 -0
- package/src/assets/templates/python-customtkinter/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-customtkinter/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-customtkinter/build.bat +50 -0
- package/src/assets/templates/python-customtkinter/pyproject.toml +26 -0
- package/src/assets/templates/python-customtkinter/run.bat +20 -0
- package/src/assets/templates/python-customtkinter/src/core/g360_theme.py +101 -0
- package/src/assets/templates/python-customtkinter/src/core/skill.json +35 -0
- package/src/assets/templates/python-customtkinter/src/main.py +103 -0
- package/src/assets/templates/python-flet/README.md +89 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.png +0 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.svg +9 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.png +0 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.svg +9 -0
- package/src/assets/templates/python-flet/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-flet/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-flet/build.bat +36 -0
- package/src/assets/templates/python-flet/pyproject.toml +26 -0
- package/src/assets/templates/python-flet/run.bat +20 -0
- package/src/assets/templates/python-flet/src/core/__init__.py +0 -0
- package/src/assets/templates/python-flet/src/core/g360_theme.py +186 -0
- package/src/assets/templates/python-flet/src/core/skill.json +41 -0
- package/src/assets/templates/python-flet/src/export/__init__.py +0 -0
- package/src/assets/templates/python-flet/src/main.py +146 -0
- package/src/assets/templates/python-flet/src/test_app.py +107 -0
- package/src/assets/templates/python-flet/src/ui/__init__.py +0 -0
- package/src/assets/templates/python-flet-migrate/README.md +70 -0
- package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-flet-migrate/build.bat +36 -0
- package/src/assets/templates/python-flet-migrate/pyproject.toml +26 -0
- package/src/assets/templates/python-flet-migrate/run.bat +20 -0
- package/src/assets/templates/python-flet-migrate/src/core/__init__.py +0 -0
- package/src/assets/templates/python-flet-migrate/src/core/g360_theme.py +186 -0
- package/src/assets/templates/python-flet-migrate/src/core/skill.json +34 -0
- package/src/assets/templates/python-flet-migrate/src/main.py +97 -0
- package/src/assets/templates/python-flet-migrate/src/migrate_tkinter.py +56 -0
- package/src/assets/templates/solid-web/src/core/skill.json +12 -5
- package/src/assets/templates/svelte-web/src/core/skill.json +12 -5
- package/src/assets/templates/web-pwa/index.html +2 -7
- package/src/assets/templates/web-pwa/public/favicon.svg +4 -0
- package/src/assets/templates/web-pwa/public/manifest.json +11 -0
- package/src/assets/templates/web-pwa/src/App.jsx +22 -0
- package/src/assets/templates/web-pwa/src/components/FooterSignature.jsx +9 -0
- package/src/assets/templates/web-pwa/src/components/Header.jsx +12 -0
- package/src/assets/templates/web-pwa/src/components/LoadingOverlay.jsx +10 -0
- package/src/assets/templates/web-pwa/src/core/skill.json +25 -0
- package/src/assets/templates/web-pwa/src/main.jsx +9 -0
- package/src/assets/templates/web-pwa/src/styles/main.css +133 -0
- package/src/assets/templates/web-pwa/vite.config.js +12 -0
- package/src/cli.js +8 -4
- package/src/commands/bring.js +81 -6
- package/src/commands/clean.js +51 -4
- package/src/commands/init.js +60 -8
- package/src/commands/list.js +79 -9
- package/src/commands/signature.js +12 -8
- package/src/lib/asset-validator.js +145 -0
- package/src/lib/auditor.js +48 -0
- package/src/lib/global-config.js +260 -0
- package/src/lib/i18n.js +238 -0
- package/src/lib/logger.js +100 -0
- package/src/lib/manifest.js +70 -1
- package/src/lib/validator.js +39 -0
- package/src/schemas/skills-schema.json +1 -0
- package/src/schemas/snippets-schema.json +1 -0
- package/src/assets/templates/web-pwa/app.js +0 -1
- package/src/assets/templates/web-pwa/styles.css +0 -28
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { logger } from './logger.js';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} ValidationResult
|
|
11
|
+
* @property {boolean} valid - Indica si la validación fue exitosa
|
|
12
|
+
* @property {string[]} errors - Lista de errores encontrados
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Módulo de validación de assets con JSON Schema
|
|
17
|
+
* @namespace assetValidator
|
|
18
|
+
*/
|
|
19
|
+
export const assetValidator = {
|
|
20
|
+
/**
|
|
21
|
+
* Instancia de AJV para validación de schemas
|
|
22
|
+
* @type {Ajv}
|
|
23
|
+
*/
|
|
24
|
+
ajv: new Ajv({ allErrors: true }),
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Cache de schemas compilados
|
|
28
|
+
* @type {Map<string, any>}
|
|
29
|
+
*/
|
|
30
|
+
schemaCache: new Map(),
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Carga y compila un schema JSON
|
|
34
|
+
* @param {string} schemaName - Nombre del schema a cargar
|
|
35
|
+
* @returns {Promise<any>} Schema compilado
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
async loadSchema(schemaName) {
|
|
39
|
+
if (this.schemaCache.has(schemaName)) {
|
|
40
|
+
return this.schemaCache.get(schemaName);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const schemaPath = path.join(__dirname, '../schemas', `${schemaName}.json`);
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(schemaPath)) {
|
|
46
|
+
throw new Error(`Schema not found: ${schemaName}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const schema = await fs.readJson(schemaPath);
|
|
50
|
+
const compiled = this.ajv.compile(schema);
|
|
51
|
+
|
|
52
|
+
this.schemaCache.set(schemaName, compiled);
|
|
53
|
+
return compiled;
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Valida un objeto contra un schema específico
|
|
58
|
+
* @param {string} schemaName - Nombre del schema a usar
|
|
59
|
+
* @param {Object} data - Datos a validar
|
|
60
|
+
* @returns {Promise<ValidationResult>} Resultado de la validación
|
|
61
|
+
* @example
|
|
62
|
+
* const result = await assetValidator.validate('skills-schema', skillsData);
|
|
63
|
+
* if (result.valid) {
|
|
64
|
+
* console.log('Skills válidos');
|
|
65
|
+
* } else {
|
|
66
|
+
* console.error('Errores:', result.errors);
|
|
67
|
+
* }
|
|
68
|
+
*/
|
|
69
|
+
async validate(schemaName, data) {
|
|
70
|
+
try {
|
|
71
|
+
const validate = await this.loadSchema(schemaName);
|
|
72
|
+
const valid = validate(data);
|
|
73
|
+
|
|
74
|
+
if (valid) {
|
|
75
|
+
logger.debug(`Validation passed for schema: ${schemaName}`);
|
|
76
|
+
return { valid: true, errors: [] };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const errors = validate.errors.map(err =>
|
|
80
|
+
`${err.instancePath} ${err.message}`
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
logger.warn(`Validation failed for schema: ${schemaName}`, { errors });
|
|
84
|
+
return { valid: false, errors };
|
|
85
|
+
} catch (error) {
|
|
86
|
+
logger.error(`Error validating against schema: ${schemaName}`, { error: error.message });
|
|
87
|
+
return {
|
|
88
|
+
valid: false,
|
|
89
|
+
errors: [`Validation error: ${error.message}`]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Valida la configuración de skills
|
|
96
|
+
* @param {Object} skillsData - Datos de skills a validar
|
|
97
|
+
* @returns {Promise<ValidationResult>} Resultado de la validación
|
|
98
|
+
* @example
|
|
99
|
+
* const result = await assetValidator.validateSkills(skillsConfig);
|
|
100
|
+
*/
|
|
101
|
+
async validateSkills(skillsData) {
|
|
102
|
+
return this.validate('skills-schema', skillsData);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Valida la configuración de snippets
|
|
107
|
+
* @param {Object} snippetsData - Datos de snippets a validar
|
|
108
|
+
* @returns {Promise<ValidationResult>} Resultado de la validación
|
|
109
|
+
* @example
|
|
110
|
+
* const result = await assetValidator.validateSnippets(snippetsConfig);
|
|
111
|
+
*/
|
|
112
|
+
async validateSnippets(snippetsData) {
|
|
113
|
+
return this.validate('snippets-schema', snippetsData);
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Valida un asset individual
|
|
118
|
+
* @param {string} assetType - Tipo de asset ('skill' | 'snippet')
|
|
119
|
+
* @param {Object} assetData - Datos del asset a validar
|
|
120
|
+
* @returns {Promise<ValidationResult>} Resultado de la validación
|
|
121
|
+
* @example
|
|
122
|
+
* const result = await assetValidator.validateAsset('skill', skillData);
|
|
123
|
+
*/
|
|
124
|
+
async validateAsset(assetType, assetData) {
|
|
125
|
+
const schemaName = assetType === 'skill' ? 'skills-schema' : 'snippets-schema';
|
|
126
|
+
|
|
127
|
+
// Para validación individual, envolvemos el asset en la estructura esperada
|
|
128
|
+
const wrapper = assetType === 'skill'
|
|
129
|
+
? { skills: [assetData] }
|
|
130
|
+
: { snippets: [assetData] };
|
|
131
|
+
|
|
132
|
+
const result = await this.validate(schemaName, wrapper);
|
|
133
|
+
|
|
134
|
+
return result;
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Limpia el cache de schemas
|
|
139
|
+
* @returns {void}
|
|
140
|
+
*/
|
|
141
|
+
clearCache() {
|
|
142
|
+
this.schemaCache.clear();
|
|
143
|
+
logger.debug('Schema cache cleared');
|
|
144
|
+
}
|
|
145
|
+
};
|
package/src/lib/auditor.js
CHANGED
|
@@ -2,7 +2,37 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} AuditIssue
|
|
7
|
+
* @property {string} file - Archivo donde se encontró el issue
|
|
8
|
+
* @property {string} message - Mensaje descriptivo del issue
|
|
9
|
+
* @property {string} severity - Severidad del issue ('error' | 'warning')
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} AuditResult
|
|
14
|
+
* @property {number} passed - Cantidad de checks que pasaron
|
|
15
|
+
* @property {number} failed - Cantidad de checks que fallaron
|
|
16
|
+
* @property {number} warnings - Cantidad de advertencias
|
|
17
|
+
* @property {number} total - Cantidad total de checks
|
|
18
|
+
* @property {AuditIssue[]} issues - Lista de issues encontrados
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Módulo de auditoría para proyectos G360
|
|
23
|
+
* @namespace auditor
|
|
24
|
+
*/
|
|
5
25
|
export const auditor = {
|
|
26
|
+
/**
|
|
27
|
+
* Ejecuta auditoría completa del proyecto
|
|
28
|
+
* @param {string} projectDir - Directorio del proyecto a auditar
|
|
29
|
+
* @param {Object} options - Opciones de auditoría
|
|
30
|
+
* @param {boolean} [options.verbose=false] - Mostrar información detallada
|
|
31
|
+
* @returns {Promise<AuditResult>} Resultado de la auditoría
|
|
32
|
+
* @example
|
|
33
|
+
* const result = await auditor.audit('/my/project', { verbose: true });
|
|
34
|
+
* console.log(`Passed: ${result.passed}, Failed: ${result.failed}`);
|
|
35
|
+
*/
|
|
6
36
|
async audit(projectDir, options = {}) {
|
|
7
37
|
const { verbose = false } = options;
|
|
8
38
|
const results = {
|
|
@@ -41,6 +71,12 @@ export const auditor = {
|
|
|
41
71
|
return results;
|
|
42
72
|
},
|
|
43
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Verifica si existe el manifiesto del proyecto
|
|
76
|
+
* @param {string} projectDir - Directorio del proyecto
|
|
77
|
+
* @returns {Object} Resultado del check
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
44
80
|
checkManifest(projectDir) {
|
|
45
81
|
const manifestPath = path.join(projectDir, 'g360-manifest.json');
|
|
46
82
|
if (fs.existsSync(manifestPath)) {
|
|
@@ -67,6 +103,12 @@ export const auditor = {
|
|
|
67
103
|
return { status: 'warn', issue: { file: 'g360-manifest.json', message: 'No manifest found - run "g360 bring" to initialize' } };
|
|
68
104
|
},
|
|
69
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Verifica la estructura del proyecto
|
|
108
|
+
* @param {string} projectDir - Directorio del proyecto
|
|
109
|
+
* @returns {Object} Resultado del check
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
70
112
|
checkStructure(projectDir) {
|
|
71
113
|
const g360Dir = path.join(projectDir, 'g360');
|
|
72
114
|
if (fs.existsSync(g360Dir)) {
|
|
@@ -94,6 +136,12 @@ export const auditor = {
|
|
|
94
136
|
return { status: 'warn', issue: { file: 'g360/', message: 'No G360 assets found - run "g360 bring" to initialize assets' } };
|
|
95
137
|
},
|
|
96
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Verifica la configuración del proyecto
|
|
141
|
+
* @param {string} projectDir - Directorio del proyecto
|
|
142
|
+
* @returns {Object} Resultado del check
|
|
143
|
+
* @private
|
|
144
|
+
*/
|
|
97
145
|
checkConfig(projectDir) {
|
|
98
146
|
const configPath = path.join(projectDir, 'g360', 'config');
|
|
99
147
|
if (fs.existsSync(configPath)) {
|
|
@@ -0,0 +1,260 @@
|
|
|
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
|
+
};
|