@ai-orchestration/core 0.1.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.
Files changed (199) hide show
  1. package/.eslintrc.json +27 -0
  2. package/README.md +516 -0
  3. package/dist/core/errors.d.ts +21 -0
  4. package/dist/core/errors.d.ts.map +1 -0
  5. package/dist/core/errors.js +38 -0
  6. package/dist/core/errors.js.map +1 -0
  7. package/dist/core/interfaces.d.ts +87 -0
  8. package/dist/core/interfaces.d.ts.map +1 -0
  9. package/dist/core/interfaces.js +5 -0
  10. package/dist/core/interfaces.js.map +1 -0
  11. package/dist/core/orchestrator.d.ts +55 -0
  12. package/dist/core/orchestrator.d.ts.map +1 -0
  13. package/dist/core/orchestrator.js +147 -0
  14. package/dist/core/orchestrator.js.map +1 -0
  15. package/dist/core/types.d.ts +72 -0
  16. package/dist/core/types.d.ts.map +1 -0
  17. package/dist/core/types.js +5 -0
  18. package/dist/core/types.js.map +1 -0
  19. package/dist/factory/index.d.ts +14 -0
  20. package/dist/factory/index.d.ts.map +1 -0
  21. package/dist/factory/index.js +184 -0
  22. package/dist/factory/index.js.map +1 -0
  23. package/dist/index.d.ts +14 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +15 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/providers/base.d.ts +30 -0
  28. package/dist/providers/base.d.ts.map +1 -0
  29. package/dist/providers/base.js +31 -0
  30. package/dist/providers/base.js.map +1 -0
  31. package/dist/providers/cerebras.d.ts +26 -0
  32. package/dist/providers/cerebras.d.ts.map +1 -0
  33. package/dist/providers/cerebras.js +190 -0
  34. package/dist/providers/cerebras.js.map +1 -0
  35. package/dist/providers/gemini.d.ts +26 -0
  36. package/dist/providers/gemini.d.ts.map +1 -0
  37. package/dist/providers/gemini.js +181 -0
  38. package/dist/providers/gemini.js.map +1 -0
  39. package/dist/providers/groq.d.ts +26 -0
  40. package/dist/providers/groq.d.ts.map +1 -0
  41. package/dist/providers/groq.js +225 -0
  42. package/dist/providers/groq.js.map +1 -0
  43. package/dist/providers/index.d.ts +15 -0
  44. package/dist/providers/index.d.ts.map +1 -0
  45. package/dist/providers/index.js +10 -0
  46. package/dist/providers/index.js.map +1 -0
  47. package/dist/providers/local.d.ts +27 -0
  48. package/dist/providers/local.d.ts.map +1 -0
  49. package/dist/providers/local.js +198 -0
  50. package/dist/providers/local.js.map +1 -0
  51. package/dist/providers/openrouter.d.ts +26 -0
  52. package/dist/providers/openrouter.d.ts.map +1 -0
  53. package/dist/providers/openrouter.js +172 -0
  54. package/dist/providers/openrouter.js.map +1 -0
  55. package/dist/strategies/base.d.ts +16 -0
  56. package/dist/strategies/base.d.ts.map +1 -0
  57. package/dist/strategies/base.js +21 -0
  58. package/dist/strategies/base.js.map +1 -0
  59. package/dist/strategies/fallback.d.ts +14 -0
  60. package/dist/strategies/fallback.d.ts.map +1 -0
  61. package/dist/strategies/fallback.js +29 -0
  62. package/dist/strategies/fallback.js.map +1 -0
  63. package/dist/strategies/health-aware.d.ts +20 -0
  64. package/dist/strategies/health-aware.d.ts.map +1 -0
  65. package/dist/strategies/health-aware.js +106 -0
  66. package/dist/strategies/health-aware.js.map +1 -0
  67. package/dist/strategies/index.d.ts +14 -0
  68. package/dist/strategies/index.d.ts.map +1 -0
  69. package/dist/strategies/index.js +10 -0
  70. package/dist/strategies/index.js.map +1 -0
  71. package/dist/strategies/priority.d.ts +18 -0
  72. package/dist/strategies/priority.d.ts.map +1 -0
  73. package/dist/strategies/priority.js +35 -0
  74. package/dist/strategies/priority.js.map +1 -0
  75. package/dist/strategies/round-robin.d.ts +10 -0
  76. package/dist/strategies/round-robin.d.ts.map +1 -0
  77. package/dist/strategies/round-robin.js +21 -0
  78. package/dist/strategies/round-robin.js.map +1 -0
  79. package/dist/strategies/weighted.d.ts +20 -0
  80. package/dist/strategies/weighted.d.ts.map +1 -0
  81. package/dist/strategies/weighted.js +58 -0
  82. package/dist/strategies/weighted.js.map +1 -0
  83. package/docs/ARCHITECTURE.md +126 -0
  84. package/docs/CHANGELOG.md +30 -0
  85. package/docs/CONTRIBUTING.md +198 -0
  86. package/examples/basic.ts +58 -0
  87. package/examples/chat-app/README.md +146 -0
  88. package/examples/chat-app/config.json +34 -0
  89. package/examples/chat-app/index.ts +330 -0
  90. package/examples/chat-app/package-lock.json +570 -0
  91. package/examples/chat-app/package.json +20 -0
  92. package/examples/config.example.json +42 -0
  93. package/examples/strategies.ts +112 -0
  94. package/examples/test-local.ts +150 -0
  95. package/examples/test-mock.ts +172 -0
  96. package/package/.eslintrc.json +27 -0
  97. package/package/CHANGELOG.md +31 -0
  98. package/package/CONTRIBUTING.md +156 -0
  99. package/package/PUBLISHING.md +213 -0
  100. package/package/README.md +515 -0
  101. package/package/dist/core/errors.d.ts +21 -0
  102. package/package/dist/core/errors.d.ts.map +1 -0
  103. package/package/dist/core/errors.js +38 -0
  104. package/package/dist/core/errors.js.map +1 -0
  105. package/package/dist/core/interfaces.d.ts +87 -0
  106. package/package/dist/core/interfaces.d.ts.map +1 -0
  107. package/package/dist/core/interfaces.js +5 -0
  108. package/package/dist/core/interfaces.js.map +1 -0
  109. package/package/dist/core/orchestrator.d.ts +55 -0
  110. package/package/dist/core/orchestrator.d.ts.map +1 -0
  111. package/package/dist/core/orchestrator.js +147 -0
  112. package/package/dist/core/orchestrator.js.map +1 -0
  113. package/package/dist/core/types.d.ts +72 -0
  114. package/package/dist/core/types.d.ts.map +1 -0
  115. package/package/dist/core/types.js +5 -0
  116. package/package/dist/core/types.js.map +1 -0
  117. package/package/dist/factory/index.d.ts +14 -0
  118. package/package/dist/factory/index.d.ts.map +1 -0
  119. package/package/dist/factory/index.js +184 -0
  120. package/package/dist/factory/index.js.map +1 -0
  121. package/package/dist/index.d.ts +14 -0
  122. package/package/dist/index.d.ts.map +1 -0
  123. package/package/dist/index.js +15 -0
  124. package/package/dist/index.js.map +1 -0
  125. package/package/dist/providers/base.d.ts +30 -0
  126. package/package/dist/providers/base.d.ts.map +1 -0
  127. package/package/dist/providers/base.js +31 -0
  128. package/package/dist/providers/base.js.map +1 -0
  129. package/package/dist/providers/cerebras.d.ts +26 -0
  130. package/package/dist/providers/cerebras.d.ts.map +1 -0
  131. package/package/dist/providers/cerebras.js +190 -0
  132. package/package/dist/providers/cerebras.js.map +1 -0
  133. package/package/dist/providers/gemini.d.ts +26 -0
  134. package/package/dist/providers/gemini.d.ts.map +1 -0
  135. package/package/dist/providers/gemini.js +181 -0
  136. package/package/dist/providers/gemini.js.map +1 -0
  137. package/package/dist/providers/groq.d.ts +26 -0
  138. package/package/dist/providers/groq.d.ts.map +1 -0
  139. package/package/dist/providers/groq.js +225 -0
  140. package/package/dist/providers/groq.js.map +1 -0
  141. package/package/dist/providers/index.d.ts +15 -0
  142. package/package/dist/providers/index.d.ts.map +1 -0
  143. package/package/dist/providers/index.js +10 -0
  144. package/package/dist/providers/index.js.map +1 -0
  145. package/package/dist/providers/local.d.ts +27 -0
  146. package/package/dist/providers/local.d.ts.map +1 -0
  147. package/package/dist/providers/local.js +198 -0
  148. package/package/dist/providers/local.js.map +1 -0
  149. package/package/dist/providers/openrouter.d.ts +26 -0
  150. package/package/dist/providers/openrouter.d.ts.map +1 -0
  151. package/package/dist/providers/openrouter.js +172 -0
  152. package/package/dist/providers/openrouter.js.map +1 -0
  153. package/package/dist/strategies/base.d.ts +16 -0
  154. package/package/dist/strategies/base.d.ts.map +1 -0
  155. package/package/dist/strategies/base.js +21 -0
  156. package/package/dist/strategies/base.js.map +1 -0
  157. package/package/dist/strategies/fallback.d.ts +14 -0
  158. package/package/dist/strategies/fallback.d.ts.map +1 -0
  159. package/package/dist/strategies/fallback.js +29 -0
  160. package/package/dist/strategies/fallback.js.map +1 -0
  161. package/package/dist/strategies/health-aware.d.ts +20 -0
  162. package/package/dist/strategies/health-aware.d.ts.map +1 -0
  163. package/package/dist/strategies/health-aware.js +106 -0
  164. package/package/dist/strategies/health-aware.js.map +1 -0
  165. package/package/dist/strategies/index.d.ts +14 -0
  166. package/package/dist/strategies/index.d.ts.map +1 -0
  167. package/package/dist/strategies/index.js +10 -0
  168. package/package/dist/strategies/index.js.map +1 -0
  169. package/package/dist/strategies/priority.d.ts +18 -0
  170. package/package/dist/strategies/priority.d.ts.map +1 -0
  171. package/package/dist/strategies/priority.js +35 -0
  172. package/package/dist/strategies/priority.js.map +1 -0
  173. package/package/dist/strategies/round-robin.d.ts +10 -0
  174. package/package/dist/strategies/round-robin.d.ts.map +1 -0
  175. package/package/dist/strategies/round-robin.js +21 -0
  176. package/package/dist/strategies/round-robin.js.map +1 -0
  177. package/package/dist/strategies/weighted.d.ts +20 -0
  178. package/package/dist/strategies/weighted.d.ts.map +1 -0
  179. package/package/dist/strategies/weighted.js +58 -0
  180. package/package/dist/strategies/weighted.js.map +1 -0
  181. package/package/docs/ARCHITECTURE.md +127 -0
  182. package/package/examples/basic.ts +58 -0
  183. package/package/examples/chat-app/README.md +146 -0
  184. package/package/examples/chat-app/config.json +34 -0
  185. package/package/examples/chat-app/index.ts +330 -0
  186. package/package/examples/chat-app/package-lock.json +570 -0
  187. package/package/examples/chat-app/package.json +20 -0
  188. package/package/examples/config.example.json +42 -0
  189. package/package/examples/strategies.ts +112 -0
  190. package/package/examples/test-local.ts +150 -0
  191. package/package/examples/test-mock.ts +172 -0
  192. package/package/package.json +62 -0
  193. package/package/scripts/check-types.ts +15 -0
  194. package/package/scripts/link.sh +30 -0
  195. package/package/scripts/pack.sh +36 -0
  196. package/package.json +62 -0
  197. package/scripts/check-types.ts +15 -0
  198. package/scripts/link.sh +30 -0
  199. package/scripts/pack.sh +36 -0
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Chat App - Ejemplo de uso de @ai-orchestration/core
4
+ *
5
+ * Setup:
6
+ * 1. npm link @ai-orchestration/core (desde este directorio)
7
+ * 2. Configurar .env con tus API keys
8
+ * 3. npm start
9
+ */
10
+
11
+ // Cargar variables de entorno desde .env
12
+ import { config } from 'dotenv';
13
+ import { resolve } from 'path';
14
+ import { readFileSync } from 'fs';
15
+
16
+ // Cargar .env desde el directorio actual
17
+ config({ path: resolve(process.cwd(), '.env') });
18
+
19
+ import { createOrchestrator, type Orchestrator } from '@ai-orchestration/core';
20
+ import type { ProviderConfig } from '@ai-orchestration/core';
21
+ import * as readline from 'readline';
22
+
23
+ // Cargar configuración desde JSON
24
+ interface ProviderConfigJson {
25
+ id: string;
26
+ type: string;
27
+ envKey: string;
28
+ model?: string;
29
+ baseURL?: string;
30
+ }
31
+
32
+ interface AppConfig {
33
+ providers: ProviderConfigJson[];
34
+ defaultStrategy: string;
35
+ chatOptions?: {
36
+ temperature?: number;
37
+ maxTokens?: number;
38
+ };
39
+ }
40
+
41
+ const configPath = resolve(process.cwd(), 'config.json');
42
+ const appConfig: AppConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
43
+
44
+ // Convertir configuración JSON a ProviderConfig, filtrando solo los que tienen API key
45
+ const providers = appConfig.providers
46
+ .map((providerConfig) => {
47
+ const apiKey = process.env[providerConfig.envKey];
48
+ if (!apiKey || apiKey.trim().length === 0) {
49
+ return null;
50
+ }
51
+
52
+ return {
53
+ id: providerConfig.id,
54
+ type: providerConfig.type,
55
+ apiKey,
56
+ model: providerConfig.model,
57
+ baseURL: providerConfig.baseURL,
58
+ } as ProviderConfig;
59
+ })
60
+ .filter((p): p is ProviderConfig => p !== null);
61
+
62
+ if (providers.length === 0) {
63
+ console.error('❌ No hay proveedores configurados.');
64
+ console.error('');
65
+ console.error(' 📝 Configura al menos una API key en el archivo .env:');
66
+ console.error('');
67
+ appConfig.providers.forEach((providerConfig) => {
68
+ console.error(` ${providerConfig.envKey}=tu-key-aqui`);
69
+ });
70
+ console.error('');
71
+ console.error(' 💡 Puedes copiar .env.example a .env y editarlo:');
72
+ console.error(' cp .env.example .env');
73
+ console.error('');
74
+ process.exit(1);
75
+ }
76
+
77
+ // Estado de la aplicación
78
+ let orchestrator: Orchestrator;
79
+ let currentStrategy = appConfig.defaultStrategy || 'round-robin';
80
+ let conversationHistory: Array<{ role: 'user' | 'assistant'; content: string }> = [];
81
+ const defaultChatOptions = appConfig.chatOptions || { temperature: 0.7, maxTokens: 1000 };
82
+
83
+ // Crear interfaz de línea de comandos
84
+ const rl = readline.createInterface({
85
+ input: process.stdin,
86
+ output: process.stdout,
87
+ });
88
+
89
+ // Inicializar orchestrator
90
+ async function initOrchestrator() {
91
+ try {
92
+ orchestrator = createOrchestrator({
93
+ providers,
94
+ strategy: {
95
+ type: currentStrategy,
96
+ ...(currentStrategy === 'priority' && {
97
+ priorities: Object.fromEntries(
98
+ providers.map((p, index) => [p.id, index + 1])
99
+ ),
100
+ }),
101
+ ...(currentStrategy === 'fallback' && {
102
+ order: providers.map((p) => p.id),
103
+ }),
104
+ },
105
+ });
106
+
107
+ const totalProviders = orchestrator.getAllProviders();
108
+ const availableProviders = await orchestrator.getAvailableProviders();
109
+
110
+ console.log(`✅ Orchestrator inicializado con estrategia: ${currentStrategy}`);
111
+ console.log(`✅ Proveedores configurados: ${totalProviders.length}`);
112
+
113
+ // Mostrar estado de cada proveedor
114
+ if (totalProviders.length > 0) {
115
+ console.log('\n📋 Estado de proveedores:');
116
+ for (const provider of totalProviders) {
117
+ const isAvailable = availableProviders.some(p => p.id === provider.id);
118
+ const status = isAvailable ? '✅' : '⚠️';
119
+ console.log(` ${status} ${provider.id} (${provider.metadata.name})`);
120
+ }
121
+ }
122
+
123
+ if (availableProviders.length === 0) {
124
+ console.log(`\n⚠️ Advertencia: Ningún proveedor está saludable actualmente.`);
125
+ console.log(` Ejecuta /health para más detalles.\n`);
126
+ } else if (availableProviders.length < totalProviders.length) {
127
+ console.log(`\n⚠️ Advertencia: ${totalProviders.length - availableProviders.length} proveedor(es) no están saludables.`);
128
+ console.log(` Ejecuta /health para más detalles.`);
129
+ console.log(` Los proveedores saludables seguirán funcionando normalmente.\n`);
130
+ } else {
131
+ console.log(`\n✅ Todos los proveedores están disponibles.\n`);
132
+ }
133
+ } catch (error) {
134
+ console.error('❌ Error al inicializar orchestrator:', error);
135
+ process.exit(1);
136
+ }
137
+ }
138
+
139
+ // Mostrar ayuda
140
+ function showHelp() {
141
+ console.log('\n📖 Comandos disponibles:');
142
+ console.log(' /help - Muestra esta ayuda');
143
+ console.log(' /strategy <tipo> - Cambia la estrategia (round-robin, priority, fallback)');
144
+ console.log(' /providers - Lista los proveedores disponibles');
145
+ console.log(' /health - Verifica la salud de los proveedores');
146
+ console.log(' /clear - Limpia el historial de conversación');
147
+ console.log(' /exit o /quit - Sale de la aplicación');
148
+ console.log(' <mensaje> - Envía un mensaje al asistente\n');
149
+ }
150
+
151
+ // Listar proveedores
152
+ function listProviders() {
153
+ const allProviders = orchestrator.getAllProviders();
154
+ console.log('\n📋 Proveedores configurados:');
155
+ allProviders.forEach((provider, index) => {
156
+ console.log(` ${index + 1}. ${provider.id} (${provider.metadata.name})`);
157
+ if (provider.metadata.model) {
158
+ console.log(` Modelo: ${provider.metadata.model}`);
159
+ }
160
+ });
161
+ console.log('');
162
+ }
163
+
164
+ // Verificar salud
165
+ async function checkHealth() {
166
+ console.log('\n🏥 Verificando salud de proveedores...');
167
+ const allProviders = orchestrator.getAllProviders();
168
+
169
+ if (allProviders.length === 0) {
170
+ console.log(' ⚠️ No hay proveedores configurados.\n');
171
+ return;
172
+ }
173
+
174
+ for (const provider of allProviders) {
175
+ try {
176
+ const health = await provider.checkHealth();
177
+ const status = health.healthy ? '✅' : '❌';
178
+ const latency = health.latency ? ` (${health.latency}ms)` : '';
179
+ const error = health.error ? ` - ${health.error.substring(0, 80)}${health.error.length > 80 ? '...' : ''}` : '';
180
+ console.log(` ${status} ${provider.id}${latency}${error}`);
181
+ } catch (error) {
182
+ console.log(` ❌ ${provider.id} - Error: ${error instanceof Error ? error.message : String(error)}`);
183
+ }
184
+ }
185
+
186
+ const available = await orchestrator.getAvailableProviders();
187
+ console.log(`\n 📊 Total: ${allProviders.length} configurados, ${available.length} disponibles\n`);
188
+ }
189
+
190
+ // Cambiar estrategia
191
+ async function changeStrategy(strategy: string) {
192
+ const validStrategies = ['round-robin', 'priority', 'fallback'];
193
+ if (!validStrategies.includes(strategy)) {
194
+ console.log(`❌ Estrategia inválida. Opciones: ${validStrategies.join(', ')}\n`);
195
+ return;
196
+ }
197
+
198
+ currentStrategy = strategy;
199
+ await initOrchestrator();
200
+ console.log(`✅ Estrategia cambiada a: ${currentStrategy}\n`);
201
+ }
202
+
203
+ // Enviar mensaje
204
+ async function sendMessage(message: string) {
205
+ if (!message.trim()) return;
206
+
207
+ conversationHistory.push({ role: 'user', content: message });
208
+
209
+ // Formatear mensajes para el API
210
+ const messages = [
211
+ ...conversationHistory.map(msg => ({
212
+ role: msg.role === 'assistant' ? 'assistant' as const : 'user' as const,
213
+ content: msg.content,
214
+ })),
215
+ ];
216
+
217
+ try {
218
+ // Verificar proveedores disponibles antes de intentar
219
+ const availableProviders = await orchestrator.getAvailableProviders();
220
+ if (availableProviders.length === 0) {
221
+ console.log('⚠️ No hay proveedores saludables disponibles.');
222
+ console.log(' Ejecuta /health para ver el estado de los proveedores.');
223
+ console.log(' Ejecuta /providers para ver los proveedores configurados.\n');
224
+ return;
225
+ }
226
+
227
+ process.stdout.write('🤔 Pensando... ');
228
+
229
+ const startTime = Date.now();
230
+ const response = await orchestrator.chat(messages, {
231
+ temperature: 0.7,
232
+ maxTokens: 1000,
233
+ });
234
+ const duration = Date.now() - startTime;
235
+
236
+ console.log(`\r✅ Respuesta (${duration}ms):\n`);
237
+ console.log(` ${response.content}\n`);
238
+
239
+ if (response.model) {
240
+ console.log(` 📊 Modelo: ${response.model}`);
241
+ }
242
+ if (response.usage) {
243
+ console.log(` 📊 Tokens: ${response.usage.totalTokens} (prompt: ${response.usage.promptTokens}, completion: ${response.usage.completionTokens})`);
244
+ }
245
+ console.log('');
246
+
247
+ conversationHistory.push({ role: 'assistant', content: response.content });
248
+ } catch (error) {
249
+ console.log(`\r❌ Error: ${error instanceof Error ? error.message : String(error)}\n`);
250
+
251
+ // Diagnóstico adicional
252
+ if (error instanceof Error && error.message.includes('No available providers')) {
253
+ console.log('💡 Sugerencias:');
254
+ console.log(' 1. Ejecuta /health para ver el estado de los proveedores');
255
+ console.log(' 2. Verifica que tus API keys son válidas en el archivo .env');
256
+ console.log(' 3. Verifica tu conexión a internet');
257
+ console.log(' 4. Algunos proveedores pueden tener rate limits\n');
258
+ }
259
+ }
260
+ }
261
+
262
+ // Procesar comando
263
+ async function processCommand(input: string) {
264
+ const [command, ...args] = input.trim().split(' ');
265
+
266
+ switch (command.toLowerCase()) {
267
+ case '/help':
268
+ showHelp();
269
+ break;
270
+ case '/strategy':
271
+ if (args.length === 0) {
272
+ console.log('❌ Especifica una estrategia: /strategy <tipo>\n');
273
+ } else {
274
+ await changeStrategy(args[0]);
275
+ }
276
+ break;
277
+ case '/providers':
278
+ listProviders();
279
+ break;
280
+ case '/health':
281
+ await checkHealth();
282
+ break;
283
+ case '/clear':
284
+ conversationHistory = [];
285
+ console.log('✅ Historial limpiado\n');
286
+ break;
287
+ case '/exit':
288
+ case '/quit':
289
+ console.log('\n👋 ¡Hasta luego!\n');
290
+ orchestrator.dispose();
291
+ rl.close();
292
+ process.exit(0);
293
+ break;
294
+ default:
295
+ // No es un comando, enviar como mensaje
296
+ await sendMessage(input);
297
+ break;
298
+ }
299
+ }
300
+
301
+ // Función principal
302
+ async function main() {
303
+ console.log('🚀 Chat App - Ejemplo con @ai-orchestration/core\n');
304
+ console.log('💡 Escribe /help para ver los comandos disponibles\n');
305
+
306
+ await initOrchestrator();
307
+
308
+ rl.setPrompt('💬 Tú: ');
309
+ rl.prompt();
310
+
311
+ rl.on('line', async (input) => {
312
+ await processCommand(input);
313
+ rl.prompt();
314
+ });
315
+
316
+ rl.on('close', () => {
317
+ console.log('\n👋 ¡Hasta luego!\n');
318
+ orchestrator.dispose();
319
+ process.exit(0);
320
+ });
321
+ }
322
+
323
+ // Manejar errores no capturados
324
+ process.on('unhandledRejection', (error) => {
325
+ console.error('\n❌ Error no manejado:', error);
326
+ rl.close();
327
+ process.exit(1);
328
+ });
329
+
330
+ main().catch(console.error);