@icarusmx/creta 1.5.6 → 1.5.7
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/creta.js
CHANGED
|
@@ -7,6 +7,8 @@ import path from 'path'
|
|
|
7
7
|
import { fileURLToPath } from 'url'
|
|
8
8
|
import { CretaCodeSession } from '../lib/session.js'
|
|
9
9
|
import { PullRequestTutorial } from '../lib/pr-tutorial.js'
|
|
10
|
+
import { VimSetupTutorial } from '../lib/vim-setup-tutorial.js'
|
|
11
|
+
import { AWSGuideViewer } from '../lib/aws-guide-viewer.js'
|
|
10
12
|
import { CommandHelpExecutor } from '../lib/executors/CommandHelpExecutor.js'
|
|
11
13
|
import { PapersExecutor } from '../lib/papers/PapersExecutor.js'
|
|
12
14
|
import { ExercisesExecutor } from '../lib/executors/ExercisesExecutor.js'
|
|
@@ -112,6 +114,10 @@ if (command.startsWith('portafolio')) {
|
|
|
112
114
|
// Start papers selector
|
|
113
115
|
const executor = new PapersExecutor()
|
|
114
116
|
await executor.execute()
|
|
117
|
+
} else if (command === 'aws') {
|
|
118
|
+
// Show AWS billing detective guide
|
|
119
|
+
const viewer = new AWSGuideViewer()
|
|
120
|
+
await viewer.start()
|
|
115
121
|
} else if (command === 'help' || command === 'ayuda') {
|
|
116
122
|
// Show available commands
|
|
117
123
|
showHelp()
|
|
@@ -609,6 +615,7 @@ function showHelp() {
|
|
|
609
615
|
console.log(" creta portafolio-2 - Desbloquea nivel 2 (navbar + hero) 🔓")
|
|
610
616
|
console.log(" creta portafolio-3 - Desbloquea nivel 3 (solución completa) 🔓")
|
|
611
617
|
console.log(" creta code - Inicia sesión interactiva de programación 🤖")
|
|
618
|
+
console.log(" creta aws - Guía de AWS Billing Detective 🕵️")
|
|
612
619
|
console.log(" creta help - Muestra esta ayuda")
|
|
613
620
|
console.log("\n💡 Tip: Si estás dentro de un proyecto existente, los comandos")
|
|
614
621
|
console.log(" portafolio-1/2/3 actualizarán tus archivos directamente")
|
|
@@ -1306,7 +1313,8 @@ async function startProyectosSelectorInteractive() {
|
|
|
1306
1313
|
|
|
1307
1314
|
const projects = [
|
|
1308
1315
|
{ id: 1, title: "🔀 Construye una pull-request", type: 'pr' },
|
|
1309
|
-
{ id: 2, title: "🎨 Construye tu portafolio", type: 'portfolio' }
|
|
1316
|
+
{ id: 2, title: "🎨 Construye tu portafolio", type: 'portfolio' },
|
|
1317
|
+
{ id: 3, title: "⌨️ Configura Creta Vim", type: 'vim' }
|
|
1310
1318
|
]
|
|
1311
1319
|
|
|
1312
1320
|
let selectedIndex = 0
|
|
@@ -1365,6 +1373,8 @@ async function startProyectosSelectorInteractive() {
|
|
|
1365
1373
|
startPullRequestTutorial().then(resolve)
|
|
1366
1374
|
} else if (selectedProject.type === 'portfolio') {
|
|
1367
1375
|
startPortfolioSelector().then(resolve)
|
|
1376
|
+
} else if (selectedProject.type === 'vim') {
|
|
1377
|
+
startVimSetupTutorial().then(resolve)
|
|
1368
1378
|
}
|
|
1369
1379
|
return
|
|
1370
1380
|
}
|
|
@@ -1429,9 +1439,10 @@ async function startProyectosSelectorFallback() {
|
|
|
1429
1439
|
console.log("")
|
|
1430
1440
|
console.log("1. 🔀 Construye una pull-request")
|
|
1431
1441
|
console.log("2. 🎨 Construye tu portafolio")
|
|
1442
|
+
console.log("3. ⌨️ Configura Creta Vim")
|
|
1432
1443
|
console.log("")
|
|
1433
1444
|
|
|
1434
|
-
const respuesta = await askQuestion("Elige una opción (1-
|
|
1445
|
+
const respuesta = await askQuestion("Elige una opción (1-3) o 'q' para salir: ")
|
|
1435
1446
|
|
|
1436
1447
|
if (respuesta.toLowerCase() === 'q') {
|
|
1437
1448
|
console.log("Hecho con <3 por icarus.mx")
|
|
@@ -1447,8 +1458,11 @@ async function startProyectosSelectorFallback() {
|
|
|
1447
1458
|
} else if (opcionSeleccionada === 2) {
|
|
1448
1459
|
rl.close()
|
|
1449
1460
|
await startPortfolioSelector()
|
|
1461
|
+
} else if (opcionSeleccionada === 3) {
|
|
1462
|
+
rl.close()
|
|
1463
|
+
await startVimSetupTutorial()
|
|
1450
1464
|
} else {
|
|
1451
|
-
console.log("❌ Opción no válida. Elige 1 o
|
|
1465
|
+
console.log("❌ Opción no válida. Elige 1, 2 o 3.")
|
|
1452
1466
|
rl.close()
|
|
1453
1467
|
}
|
|
1454
1468
|
|
|
@@ -1648,6 +1662,33 @@ async function startPullRequestTutorial() {
|
|
|
1648
1662
|
}
|
|
1649
1663
|
}
|
|
1650
1664
|
|
|
1665
|
+
async function startVimSetupTutorial() {
|
|
1666
|
+
try {
|
|
1667
|
+
const tutorial = new VimSetupTutorial()
|
|
1668
|
+
await tutorial.start()
|
|
1669
|
+
|
|
1670
|
+
// Volver al menú principal después de completar el tutorial
|
|
1671
|
+
await returnToMainMenu()
|
|
1672
|
+
} catch (error) {
|
|
1673
|
+
console.error("\n❌ Error al ejecutar el tutorial:", error.message)
|
|
1674
|
+
console.log("\nSi el problema persiste, contacta al equipo de Creta.")
|
|
1675
|
+
|
|
1676
|
+
const rl = createInterface({
|
|
1677
|
+
input: process.stdin,
|
|
1678
|
+
output: process.stdout
|
|
1679
|
+
})
|
|
1680
|
+
|
|
1681
|
+
await new Promise((resolve) => {
|
|
1682
|
+
rl.question("\nPresiona Enter para volver al menú principal...", () => {
|
|
1683
|
+
rl.close()
|
|
1684
|
+
resolve()
|
|
1685
|
+
})
|
|
1686
|
+
})
|
|
1687
|
+
|
|
1688
|
+
await startMainMenu()
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1651
1692
|
// Helper function to detect command help requests
|
|
1652
1693
|
function isCommandHelpRequest(args) {
|
|
1653
1694
|
// List of supported commands for help
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { createInterface } from 'readline'
|
|
2
|
+
import { execSync } from 'child_process'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
const __dirname = path.dirname(__filename)
|
|
10
|
+
|
|
11
|
+
export class AWSGuideViewer {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.guidePath = path.join(__dirname, 'exercises', 'aws-billing-detective.md')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async start() {
|
|
17
|
+
console.clear()
|
|
18
|
+
this.showWelcome()
|
|
19
|
+
await this.showOptions()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
showWelcome() {
|
|
23
|
+
console.log(chalk.cyan('\n🕵️ AWS Billing Detective'))
|
|
24
|
+
console.log(chalk.gray('═'.repeat(60)))
|
|
25
|
+
console.log('\n' + chalk.bold('El misterio: "Apagué todo pero AWS me sigue cobrando"'))
|
|
26
|
+
console.log('\nGuía completa para encontrar cargos ocultos de AWS:')
|
|
27
|
+
console.log(' • Obtener credenciales de AWS (IAM)')
|
|
28
|
+
console.log(' • Instalar y configurar AWS CLI')
|
|
29
|
+
console.log(' • Comandos de auditoría región por región')
|
|
30
|
+
console.log(' • Los sospechosos habituales (EBS, Elastic IPs, NAT Gateways)')
|
|
31
|
+
console.log(' • Scripts de prevención')
|
|
32
|
+
console.log('\n' + chalk.yellow('📍 Ubicación: ') + this.guidePath)
|
|
33
|
+
console.log(chalk.gray('\n588 líneas de guía paso a paso\n'))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async showOptions() {
|
|
37
|
+
const rl = createInterface({
|
|
38
|
+
input: process.stdin,
|
|
39
|
+
output: process.stdout
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const askQuestion = (question) => {
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
rl.question(question, resolve)
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check if file exists
|
|
49
|
+
if (!fs.existsSync(this.guidePath)) {
|
|
50
|
+
console.log(chalk.red('❌ Error: No se encontró la guía de AWS'))
|
|
51
|
+
console.log(chalk.gray('\nAsegúrate de estar ejecutando Creta desde el directorio correcto'))
|
|
52
|
+
rl.close()
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(chalk.bold('📚 Opciones:'))
|
|
57
|
+
console.log('')
|
|
58
|
+
console.log('1. Abrir con nvim ' + chalk.gray('(recomendado, usa fold markers)'))
|
|
59
|
+
console.log('2. Mostrar la ruta ' + chalk.gray('(para copiar)'))
|
|
60
|
+
console.log('3. Ver primeros 30 líneas ' + chalk.gray('(preview)'))
|
|
61
|
+
console.log('4. Ver tabla de contenido')
|
|
62
|
+
console.log('')
|
|
63
|
+
|
|
64
|
+
const choice = await askQuestion(chalk.cyan('Elige una opción (1-4) o Enter para nvim: '))
|
|
65
|
+
rl.close()
|
|
66
|
+
|
|
67
|
+
if (choice === '' || choice === '1') {
|
|
68
|
+
await this.openWithNvim()
|
|
69
|
+
} else if (choice === '2') {
|
|
70
|
+
this.showPath()
|
|
71
|
+
} else if (choice === '3') {
|
|
72
|
+
this.showPreview()
|
|
73
|
+
} else if (choice === '4') {
|
|
74
|
+
this.showTableOfContents()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async openWithNvim() {
|
|
79
|
+
console.log('\n' + chalk.cyan('🚀 Abriendo con nvim...'))
|
|
80
|
+
console.log(chalk.gray('💡 Tip: Usa ') + chalk.yellow('zM') + chalk.gray(' para cerrar todos los folds'))
|
|
81
|
+
console.log(chalk.gray(' Usa ') + chalk.yellow('zR') + chalk.gray(' para abrir todos los folds'))
|
|
82
|
+
console.log(chalk.gray(' Usa ') + chalk.yellow('za') + chalk.gray(' para toggle fold en la línea actual'))
|
|
83
|
+
console.log('')
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
execSync(`nvim "${this.guidePath}"`, { stdio: 'inherit' })
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.log('\n' + chalk.yellow('⚠️ No se pudo abrir nvim. Intentando con el editor por defecto...'))
|
|
89
|
+
try {
|
|
90
|
+
execSync(`$EDITOR "${this.guidePath}" || vi "${this.guidePath}"`, { stdio: 'inherit' })
|
|
91
|
+
} catch (fallbackError) {
|
|
92
|
+
console.log('\n' + chalk.red('❌ No se pudo abrir ningún editor.'))
|
|
93
|
+
console.log(chalk.yellow('📍 Abre manualmente: ') + this.guidePath)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
showPath() {
|
|
99
|
+
console.log('\n' + chalk.cyan('📋 Ruta del archivo:'))
|
|
100
|
+
console.log(chalk.yellow(this.guidePath))
|
|
101
|
+
console.log('')
|
|
102
|
+
console.log(chalk.gray('💡 Tip: Usa este comando para abrirlo:'))
|
|
103
|
+
console.log(chalk.yellow(` nvim "${this.guidePath}"`))
|
|
104
|
+
console.log('')
|
|
105
|
+
|
|
106
|
+
// Try to copy to clipboard (macOS)
|
|
107
|
+
try {
|
|
108
|
+
execSync(`echo "${this.guidePath}" | pbcopy`, { stdio: 'ignore' })
|
|
109
|
+
console.log(chalk.green('✓ Ruta copiada al portapapeles (macOS)'))
|
|
110
|
+
} catch (error) {
|
|
111
|
+
// Silently fail if pbcopy not available
|
|
112
|
+
}
|
|
113
|
+
console.log('')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
showPreview() {
|
|
117
|
+
console.log('\n' + chalk.cyan('📄 Primeras 30 líneas:'))
|
|
118
|
+
console.log(chalk.gray('═'.repeat(60)))
|
|
119
|
+
try {
|
|
120
|
+
const preview = execSync(`head -30 "${this.guidePath}"`, { encoding: 'utf8' })
|
|
121
|
+
console.log(preview)
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.log(chalk.red('❌ Error al leer el archivo'))
|
|
124
|
+
}
|
|
125
|
+
console.log(chalk.gray('═'.repeat(60)))
|
|
126
|
+
console.log('')
|
|
127
|
+
console.log(chalk.gray('💡 Abre el archivo completo con: ') + chalk.yellow(`creta aws`))
|
|
128
|
+
console.log(chalk.gray(' O directamente: ') + chalk.yellow(`nvim "${this.guidePath}"`))
|
|
129
|
+
console.log('')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
showTableOfContents() {
|
|
133
|
+
console.log('\n' + chalk.cyan('📚 Tabla de Contenido'))
|
|
134
|
+
console.log(chalk.gray('═'.repeat(60)))
|
|
135
|
+
console.log('')
|
|
136
|
+
|
|
137
|
+
const toc = [
|
|
138
|
+
{ section: 'El Misterio', description: 'Por qué AWS cobra después de apagar todo' },
|
|
139
|
+
{ section: 'Parte 1', description: 'Obtener tus AWS Access Keys del console' },
|
|
140
|
+
{ section: 'Parte 2', description: 'Instalar y configurar AWS CLI' },
|
|
141
|
+
{ section: 'Parte 3', description: 'Auditoría completa de billing' },
|
|
142
|
+
{ section: '', description: ' • EC2 instances' },
|
|
143
|
+
{ section: '', description: ' • EBS volumes (el culpable #1)' },
|
|
144
|
+
{ section: '', description: ' • Elastic IPs ($3.60/mes cada una)' },
|
|
145
|
+
{ section: '', description: ' • NAT Gateways ($32/mes cada uno!)' },
|
|
146
|
+
{ section: '', description: ' • Load Balancers' },
|
|
147
|
+
{ section: '', description: ' • RDS databases' },
|
|
148
|
+
{ section: '', description: ' • Snapshots' },
|
|
149
|
+
{ section: '', description: ' • S3 buckets' },
|
|
150
|
+
{ section: '', description: ' • CloudWatch logs' },
|
|
151
|
+
{ section: 'Parte 4', description: 'Script de auditoría multi-región' },
|
|
152
|
+
{ section: 'Parte 5', description: 'Cost Explorer (interfaz web)' },
|
|
153
|
+
{ section: 'Parte 6', description: 'Alertas de billing (prevención)' },
|
|
154
|
+
{ section: 'Quick Reference', description: 'Comandos más comunes' },
|
|
155
|
+
{ section: 'Troubleshooting', description: 'Solución de problemas' },
|
|
156
|
+
{ section: 'Checklist', description: 'Lista de prevención' },
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
toc.forEach(({ section, description }) => {
|
|
160
|
+
if (section) {
|
|
161
|
+
console.log(chalk.bold(section.padEnd(15)) + chalk.gray(description))
|
|
162
|
+
} else {
|
|
163
|
+
console.log(chalk.gray(description))
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
console.log('')
|
|
168
|
+
console.log(chalk.gray('─'.repeat(60)))
|
|
169
|
+
console.log('')
|
|
170
|
+
console.log(chalk.yellow('💡 La guía completa tiene 588 líneas con:'))
|
|
171
|
+
console.log(' • Comandos copy-paste listos para usar')
|
|
172
|
+
console.log(' • Explicaciones de qué hace cada servicio')
|
|
173
|
+
console.log(' • Estimaciones de costo por servicio')
|
|
174
|
+
console.log(' • Scripts para automatizar la auditoría')
|
|
175
|
+
console.log('')
|
|
176
|
+
console.log(chalk.gray('Abre con: ') + chalk.yellow('nvim "' + this.guidePath + '"'))
|
|
177
|
+
console.log('')
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -33,6 +33,14 @@ export class ExercisesExecutor {
|
|
|
33
33
|
file: "railway-deployment.md",
|
|
34
34
|
lines: 500,
|
|
35
35
|
topics: ["Railway", "Deployment", "Node.js", "DevOps"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 4,
|
|
39
|
+
title: "AWS Billing Detective",
|
|
40
|
+
description: "Hunt down AWS charges and prevent surprises",
|
|
41
|
+
file: "aws-billing-detective.md",
|
|
42
|
+
lines: 588,
|
|
43
|
+
topics: ["AWS", "CLI", "Cost Management", "DevOps"]
|
|
36
44
|
}
|
|
37
45
|
]
|
|
38
46
|
}
|