forgegit 1.3.0 → 1.4.1

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.
@@ -0,0 +1,78 @@
1
+ # ==== Sistema operativo ====
2
+ Thumbs.db
3
+ ehthumbs.db
4
+ Desktop.ini
5
+ $RECYCLE.BIN/
6
+ *.lnk
7
+ .DS_Store
8
+
9
+ # ==== Editor / IDE ====
10
+ .vscode/
11
+ .idea/
12
+ *.swp
13
+ *.swo
14
+ *~
15
+
16
+ # ==== Node.js ====
17
+ node_modules/
18
+ npm-debug.log*
19
+ yarn-debug.log*
20
+ yarn-error.log*
21
+ pnpm-debug.log*
22
+ package-lock.json
23
+ yarn.lock
24
+ pnpm-lock.yaml
25
+
26
+ # ==== Python ====
27
+ __pycache__/
28
+ *.py[cod]
29
+ .Python
30
+ venv/
31
+ .venv/
32
+ env/
33
+ .env
34
+
35
+ # ==== Rust ====
36
+ target/
37
+ Cargo.lock
38
+
39
+ # ==== Go ====
40
+ *.test
41
+ *.out
42
+
43
+ # ==== Build / dist ====
44
+ dist/
45
+ build/
46
+ out/
47
+ .cache/
48
+ .parcel-cache/
49
+ .vite/
50
+
51
+ # ==== Modelos / cache IA ====
52
+ *.onnx
53
+ *.bin
54
+ *.wasm
55
+ *.pb
56
+ models/
57
+ cache/
58
+ .huggingface/
59
+
60
+ # ==== Backups manuales ====
61
+ versiones/
62
+ old_versions/
63
+ backup/
64
+ backups/
65
+
66
+ # ==== Reportes de auditoria ====
67
+ audits/
68
+
69
+ # ==== Temporales ====
70
+ *.log
71
+ *.tmp
72
+ *.bak
73
+ *.orig
74
+
75
+ # ==== Capturas ====
76
+ screenshots/
77
+ capturas/
78
+ test-output/
@@ -0,0 +1,85 @@
1
+ @echo off
2
+ setlocal EnableDelayedExpansion
3
+ chcp 65001 >nul 2>nul
4
+
5
+ cd /d "%~dp0..\.."
6
+
7
+ REM --- Colores ANSI ---
8
+ for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
9
+ if "!ESC!"=="" (
10
+ set "C_CY=" & set "C_BC=" & set "C_GR=" & set "C_YE="
11
+ set "C_RE=" & set "C_GY=" & set "C_WH=" & set "C_RS="
12
+ ) else (
13
+ set "C_CY=!ESC![36m" & set "C_BC=!ESC![96m"
14
+ set "C_GR=!ESC![32m" & set "C_YE=!ESC![33m"
15
+ set "C_RE=!ESC![31m" & set "C_GY=!ESC![90m"
16
+ set "C_WH=!ESC![97m" & set "C_RS=!ESC![0m"
17
+ )
18
+
19
+ echo.
20
+ echo !C_CY!============================================!C_RS!
21
+ echo !C_BC! HISTORIAL!C_RS! !C_GY!- Vista rapida del proyecto!C_RS!
22
+ echo !C_CY!============================================!C_RS!
23
+ echo.
24
+
25
+ REM --- COMMITS ---
26
+ if not exist ".git" (
27
+ echo !C_RE![X]!C_RS! Sin repositorio Git.
28
+ goto :tags
29
+ )
30
+
31
+ echo !C_CY!-------------------------------------------------------------!C_RS!
32
+ echo !C_BC! ULTIMOS 20 COMMITS!C_RS!
33
+ echo !C_CY!-------------------------------------------------------------!C_RS!
34
+ echo !C_GY! IDX FECHA HASH MENSAJE!C_RS!
35
+ echo !C_CY!-------------------------------------------------------------!C_RS!
36
+
37
+ git log -n 20 --date=format:"%%Y-%%m-%%d %%H:%%M:%%S" --pretty=format:"%%h %%ad %%s" > "%TEMP%\fgh_log.txt" 2>nul
38
+
39
+ set "IDX=0"
40
+ for /f "delims=" %%L in ('type "%TEMP%\fgh_log.txt"') do (
41
+ set "LINE=%%L"
42
+ set "HASH=!LINE:~0,7!"
43
+ set "DT=!LINE:~8,19!"
44
+ set "MSG=!LINE:~28!"
45
+ echo !C_YE![!IDX!]!C_RS! !C_GY!!DT!!C_RS! !C_YE!!HASH!!C_RS! !C_WH!!MSG!!C_RS!
46
+ set /a IDX+=1
47
+ )
48
+ del "%TEMP%\fgh_log.txt" >nul 2>nul
49
+ echo !C_CY!-------------------------------------------------------------!C_RS!
50
+ echo.
51
+
52
+ :tags
53
+ echo !C_CY!-------------------------------------------------------------!C_RS!
54
+ echo !C_BC! TAGS!C_RS!
55
+ echo !C_CY!-------------------------------------------------------------!C_RS!
56
+ if exist ".git" git tag -l
57
+ echo.
58
+
59
+ echo !C_CY!-------------------------------------------------------------!C_RS!
60
+ echo !C_BC! SNAPSHOTS!C_RS!
61
+ echo !C_CY!-------------------------------------------------------------!C_RS!
62
+ if exist "versiones" goto :show_snaps
63
+ echo !C_GY!sin snapshots!C_RS!
64
+ goto :end_snaps
65
+
66
+ :show_snaps
67
+ dir /b /o-n "versiones" 2>nul
68
+
69
+ :end_snaps
70
+ echo !C_CY!-------------------------------------------------------------!C_RS!
71
+ echo.
72
+
73
+ call :shell_if_double_click
74
+ endlocal
75
+ exit /b 0
76
+
77
+ :shell_if_double_click
78
+ if "%NO_PAUSE%"=="1" goto :eof
79
+ echo %cmdcmdline% | find /i "%~nx0" >nul
80
+ if errorlevel 1 goto :eof
81
+ echo.
82
+ echo !C_GY!Sesion interactiva. Escribe 'exit' para cerrar.!C_RS!
83
+ echo.
84
+ cmd /k
85
+ goto :eof
@@ -0,0 +1,97 @@
1
+ @echo off
2
+ setlocal EnableDelayedExpansion
3
+ chcp 65001 >nul 2>nul
4
+
5
+ cd /d "%~dp0..\.."
6
+
7
+ REM --- Colores ANSI (con fallback) ---
8
+ for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
9
+ if "!ESC!"=="" (
10
+ set "C_CY=" & set "C_BC=" & set "C_GR=" & set "C_YE="
11
+ set "C_RE=" & set "C_GY=" & set "C_WH=" & set "C_RS="
12
+ ) else (
13
+ set "C_CY=!ESC![36m" & set "C_BC=!ESC![96m"
14
+ set "C_GR=!ESC![32m" & set "C_YE=!ESC![33m"
15
+ set "C_RE=!ESC![31m" & set "C_GY=!ESC![90m"
16
+ set "C_WH=!ESC![97m" & set "C_RS=!ESC![0m"
17
+ )
18
+
19
+ echo.
20
+ echo !C_CY!============================================!C_RS!
21
+ echo !C_BC! PUSH!C_RS! !C_GY!- Push a GitHub!C_RS!
22
+ echo !C_CY!============================================!C_RS!
23
+ echo.
24
+
25
+ if not exist ".git" (
26
+ echo !C_RE![X]!C_RS! No hay repositorio Git. Ejecuta: !C_WH!init.cmd!C_RS!
27
+ call :shell_if_double_click
28
+ exit /b 1
29
+ )
30
+
31
+ git diff --cached --quiet
32
+ set "STAGED=!ERRORLEVEL!"
33
+ git diff --quiet
34
+ set "UNSTAGED=!ERRORLEVEL!"
35
+
36
+ if !STAGED! NEQ 0 goto :has_changes
37
+ if !UNSTAGED! NEQ 0 goto :has_changes
38
+ goto :check_remote
39
+
40
+ :has_changes
41
+ echo !C_YE![!]!C_RS! Hay cambios sin commitear.
42
+ echo.
43
+ set "SAVE="
44
+ set /p "SAVE= !C_BC!Guardar cambios primero?!C_RS! !C_GY![S/n]!C_RS!: "
45
+ if /i "!SAVE!"=="n" goto :check_remote
46
+ echo.
47
+ set "SKIP_HISTORIAL=1"
48
+ set "NO_PAUSE=1"
49
+ call "guardar.cmd"
50
+ set "SKIP_HISTORIAL="
51
+ set "NO_PAUSE="
52
+ echo.
53
+
54
+ :check_remote
55
+ git remote get-url origin >nul 2>nul
56
+ if errorlevel 1 (
57
+ echo !C_RE![X]!C_RS! No hay remote 'origin' configurado.
58
+ echo.
59
+ echo !C_GY!Para vincular tu repo a GitHub:!C_RS!
60
+ echo.
61
+ echo !C_WH!git remote add origin https://github.com/TU-USUARIO/TU-REPO.git!C_RS!
62
+ echo.
63
+ call :shell_if_double_click
64
+ exit /b 1
65
+ )
66
+
67
+ echo !C_GY!Subiendo a GitHub...!C_RS!
68
+ echo.
69
+
70
+ git push -u origin main --follow-tags
71
+ set "RC=!ERRORLEVEL!"
72
+
73
+ echo.
74
+ if !RC! EQU 0 (
75
+ echo !C_GR![OK]!C_RS! Push completado
76
+ ) else (
77
+ echo !C_RE![ERROR]!C_RS! Push fallo con codigo !RC!
78
+ )
79
+ echo.
80
+
81
+ set "NO_PAUSE=1"
82
+ call "%~dp0historial.cmd"
83
+ set "NO_PAUSE="
84
+
85
+ call :shell_if_double_click
86
+ endlocal
87
+ exit /b 0
88
+
89
+ :shell_if_double_click
90
+ if "%NO_PAUSE%"=="1" goto :eof
91
+ echo %cmdcmdline% | find /i "%~nx0" >nul
92
+ if errorlevel 1 goto :eof
93
+ echo.
94
+ echo !C_GY!Sesion interactiva. Escribe 'exit' para cerrar.!C_RS!
95
+ echo.
96
+ cmd /k
97
+ goto :eof
@@ -0,0 +1,34 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ Proyecto inicializado con `forge.cmd`.
4
+
5
+ ## Estructura
6
+
7
+ - `forge.cmd` - Menu interactivo + CLI
8
+ - `.forge/` - Infraestructura (helpers, templates, lib)
9
+
10
+ ## Comandos
11
+
12
+ ### CLI directo
13
+
14
+ forge Menu interactivo
15
+ forge save Commit de cambios
16
+ forge push Push a GitHub
17
+ forge log Ver commits recientes
18
+ forge snap Crear/restaurar snapshots
19
+ forge audit Generar reporte diff
20
+ forge open Abrir en VS Code
21
+ forge help Ayuda completa
22
+
23
+ ### Helper scripts en .forge/commands/
24
+
25
+ - save.cmd - Commit
26
+ - push.cmd - Push a GitHub
27
+ - snapshots.cmd - Backups
28
+ - historial.cmd - Log
29
+ - auditar.cmd - Reportes
30
+ - abrir.cmd - VS Code
31
+
32
+ ---
33
+
34
+ Generado por forge-git v{{FG_VERSION}}
@@ -0,0 +1,73 @@
1
+ @echo off
2
+ setlocal EnableDelayedExpansion
3
+ chcp 65001 >nul 2>nul
4
+
5
+ cd /d "%~dp0..\.."
6
+
7
+ for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
8
+ if "!ESC!"=="" (
9
+ set "C_CY=" & set "C_BC=" & set "C_GR=" & set "C_YE="
10
+ set "C_RE=" & set "C_GY=" & set "C_WH=" & set "C_RS="
11
+ ) else (
12
+ set "C_CY=!ESC![36m" & set "C_BC=!ESC![96m"
13
+ set "C_GR=!ESC![32m" & set "C_YE=!ESC![33m"
14
+ set "C_RE=!ESC![31m" & set "C_GY=!ESC![90m"
15
+ set "C_WH=!ESC![97m" & set "C_RS=!ESC![0m"
16
+ )
17
+
18
+ if not exist ".git" (
19
+ echo !C_RE![X]!C_RS! No hay repositorio Git. Ejecuta: !C_WH!init.cmd!C_RS!
20
+ exit /b 1
21
+ )
22
+
23
+ git add -A >nul 2>nul
24
+ git diff --cached --quiet
25
+ if not errorlevel 1 (
26
+ echo !C_GY!Sin cambios para SAVE.!C_RS!
27
+ exit /b 0
28
+ )
29
+
30
+ echo.
31
+ echo !C_CY!------------------------------------------------------------!C_RS!
32
+ echo !C_BC! SAVE!C_RS! !C_GY!- Commit local!C_RS!
33
+ echo !C_CY!------------------------------------------------------------!C_RS!
34
+ echo.
35
+ echo !C_GY!Archivos a SAVE:!C_RS!
36
+ git diff --cached --name-only | findstr /n "^"
37
+ echo.
38
+ echo !C_GY!Evita usar: " # : + / , en el mensaje!C_RS!
39
+ echo.
40
+ echo !C_BC!Mensaje del commit!C_RS! !C_GY![Enter = Sin definir]!C_RS!
41
+ set "MSG="
42
+ set /p MSG=
43
+ if "!MSG!"=="" set "MSG=Sin definir"
44
+
45
+ git commit -q -m "!MSG!"
46
+ if errorlevel 1 (
47
+ echo.
48
+ echo !C_RE![X]!C_RS! Commit fallo. Revisa el mensaje.
49
+ exit /b 1
50
+ )
51
+ echo.
52
+ echo !C_GR![OK]!C_RS! Commit: !C_WH!!MSG!!C_RS!
53
+ echo.
54
+
55
+ if "%SKIP_HISTORIAL%"=="1" goto :skip_hist
56
+ set "NO_PAUSE=1"
57
+ call "%~dp0historial.cmd"
58
+ set "NO_PAUSE="
59
+ :skip_hist
60
+
61
+ call :shell_if_double_click
62
+ endlocal
63
+ exit /b 0
64
+
65
+ :shell_if_double_click
66
+ if "%NO_PAUSE%"=="1" goto :eof
67
+ echo %cmdcmdline% | find /i "%~nx0" >nul
68
+ if errorlevel 1 goto :eof
69
+ echo.
70
+ echo !C_GY!Sesion interactiva. Escribe 'exit' para cerrar.!C_RS!
71
+ echo.
72
+ cmd /k
73
+ goto :eof
@@ -0,0 +1,86 @@
1
+ // serve-static.js - Minimal static file server (no dependencies)
2
+ // Usage: node serve-static.js [port] [root]
3
+
4
+ const http = require('http');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const PORT = parseInt(process.argv[2] || '8080', 10);
9
+ const ROOT = path.resolve(process.argv[3] || process.cwd());
10
+
11
+ const MIME = {
12
+ '.html': 'text/html; charset=utf-8',
13
+ '.htm': 'text/html; charset=utf-8',
14
+ '.js': 'application/javascript; charset=utf-8',
15
+ '.mjs': 'application/javascript; charset=utf-8',
16
+ '.css': 'text/css; charset=utf-8',
17
+ '.json': 'application/json; charset=utf-8',
18
+ '.xml': 'application/xml; charset=utf-8',
19
+ '.txt': 'text/plain; charset=utf-8',
20
+ '.md': 'text/markdown; charset=utf-8',
21
+ '.png': 'image/png',
22
+ '.jpg': 'image/jpeg',
23
+ '.jpeg': 'image/jpeg',
24
+ '.gif': 'image/gif',
25
+ '.svg': 'image/svg+xml',
26
+ '.webp': 'image/webp',
27
+ '.ico': 'image/x-icon',
28
+ '.wasm': 'application/wasm',
29
+ '.woff': 'font/woff',
30
+ '.woff2': 'font/woff2',
31
+ '.ttf': 'font/ttf',
32
+ '.mp4': 'video/mp4',
33
+ '.webm': 'video/webm',
34
+ '.mp3': 'audio/mpeg',
35
+ '.wav': 'audio/wav'
36
+ };
37
+
38
+ const server = http.createServer((req, res) => {
39
+ let urlPath;
40
+ try {
41
+ urlPath = decodeURIComponent(req.url.split('?')[0]);
42
+ } catch (e) {
43
+ res.writeHead(400); res.end('400 Bad Request'); return;
44
+ }
45
+
46
+ if (urlPath === '/') urlPath = '/index.html';
47
+
48
+ const filePath = path.resolve(path.join(ROOT, urlPath));
49
+
50
+ // Bloqueo de path traversal
51
+ if (!filePath.startsWith(ROOT)) {
52
+ res.writeHead(403); res.end('403 Forbidden'); return;
53
+ }
54
+
55
+ fs.readFile(filePath, (err, data) => {
56
+ if (err) {
57
+ res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
58
+ res.end('404 Not Found: ' + urlPath);
59
+ return;
60
+ }
61
+ const ext = path.extname(filePath).toLowerCase();
62
+ res.writeHead(200, {
63
+ 'Content-Type': MIME[ext] || 'application/octet-stream',
64
+ 'Cache-Control': 'no-cache'
65
+ });
66
+ res.end(data);
67
+ });
68
+ });
69
+
70
+ server.on('error', (err) => {
71
+ if (err.code === 'EADDRINUSE') {
72
+ console.error('[ERROR] Port ' + PORT + ' is already in use.');
73
+ process.exit(1);
74
+ }
75
+ console.error('[ERROR] ' + err.message);
76
+ process.exit(1);
77
+ });
78
+
79
+ server.listen(PORT, '127.0.0.1', () => {
80
+ console.log('');
81
+ console.log(' Serving ' + ROOT);
82
+ console.log(' -> http://localhost:' + PORT + '/');
83
+ console.log('');
84
+ console.log(' Press Ctrl+C to stop');
85
+ console.log('');
86
+ });
@@ -0,0 +1,209 @@
1
+ @echo off
2
+ setlocal EnableDelayedExpansion
3
+ chcp 65001 >nul 2>nul
4
+
5
+ cd /d "%~dp0..\.."
6
+
7
+ REM --- Colores ANSI ---
8
+ for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
9
+ if "!ESC!"=="" (
10
+ set "C_CY=" & set "C_BC=" & set "C_GR=" & set "C_YE="
11
+ set "C_RE=" & set "C_GY=" & set "C_WH=" & set "C_RS="
12
+ ) else (
13
+ set "C_CY=!ESC![36m" & set "C_BC=!ESC![96m"
14
+ set "C_GR=!ESC![32m" & set "C_YE=!ESC![33m"
15
+ set "C_RE=!ESC![31m" & set "C_GY=!ESC![90m"
16
+ set "C_WH=!ESC![97m" & set "C_RS=!ESC![0m"
17
+ )
18
+
19
+ echo.
20
+ echo !C_CY!============================================!C_RS!
21
+ echo !C_BC! SERVE!C_RS! !C_GY!- Iniciar servidor de desarrollo!C_RS!
22
+ echo !C_CY!============================================!C_RS!
23
+ echo.
24
+
25
+ REM ============================================================
26
+ REM DETECCION DE STACK
27
+ REM ============================================================
28
+ set "STACK="
29
+ set "CMD="
30
+ set "NEEDS_PYTHON=0"
31
+ set "NEEDS_NODE=0"
32
+ set "PORT="
33
+
34
+ REM --- Node.js: package.json ---
35
+ if exist "package.json" (
36
+ findstr /C:"\"dev\"" package.json >nul 2>nul
37
+ if not errorlevel 1 (
38
+ set "STACK=Node.js (npm run dev)"
39
+ set "CMD=npm run dev"
40
+ set "NEEDS_NODE=1"
41
+ goto :detected
42
+ )
43
+ findstr /C:"\"start\"" package.json >nul 2>nul
44
+ if not errorlevel 1 (
45
+ set "STACK=Node.js (npm start)"
46
+ set "CMD=npm start"
47
+ set "NEEDS_NODE=1"
48
+ goto :detected
49
+ )
50
+ )
51
+
52
+ REM --- Django ---
53
+ if exist "manage.py" (
54
+ set "STACK=Django"
55
+ set "NEEDS_PYTHON=1"
56
+ call :find_free_port 8000
57
+ set "CMD=python manage.py runserver !PORT!"
58
+ goto :detected
59
+ )
60
+
61
+ REM --- Python simple ---
62
+ if exist "app.py" (
63
+ set "STACK=Python (app.py)"
64
+ set "NEEDS_PYTHON=1"
65
+ set "CMD=python app.py"
66
+ goto :detected
67
+ )
68
+ if exist "main.py" (
69
+ set "STACK=Python (main.py)"
70
+ set "NEEDS_PYTHON=1"
71
+ set "CMD=python main.py"
72
+ goto :detected
73
+ )
74
+
75
+ REM --- Node.js simple (server.js) ---
76
+ if exist "server.js" (
77
+ set "STACK=Node.js (server.js)"
78
+ set "NEEDS_NODE=1"
79
+ set "CMD=node server.js"
80
+ goto :detected
81
+ )
82
+
83
+ REM --- Rust ---
84
+ if exist "Cargo.toml" (
85
+ set "STACK=Rust (cargo)"
86
+ set "CMD=cargo run"
87
+ goto :detected
88
+ )
89
+
90
+ REM --- Go ---
91
+ if exist "go.mod" (
92
+ set "STACK=Go"
93
+ set "CMD=go run ."
94
+ goto :detected
95
+ )
96
+
97
+ REM --- Docker Compose ---
98
+ if exist "docker-compose.yml" (
99
+ set "STACK=Docker Compose"
100
+ set "CMD=docker-compose up"
101
+ goto :detected
102
+ )
103
+ if exist "docker-compose.yaml" (
104
+ set "STACK=Docker Compose"
105
+ set "CMD=docker-compose up"
106
+ goto :detected
107
+ )
108
+
109
+ REM --- HTML estatico: NODE primero, PYTHON fallback ---
110
+ if exist "index.html" (
111
+ call :find_free_port 8080
112
+ where node >nul 2>nul
113
+ if not errorlevel 1 (
114
+ set "STACK=HTML estatico (Node.js)"
115
+ set "NEEDS_NODE=1"
116
+ set "CMD=node .forge\serve-static.js !PORT!"
117
+ goto :detected
118
+ )
119
+ where python >nul 2>nul
120
+ if not errorlevel 1 (
121
+ set "STACK=HTML estatico (Python)"
122
+ set "NEEDS_PYTHON=1"
123
+ set "CMD=python -m http.server !PORT!"
124
+ goto :detected
125
+ )
126
+ echo !C_RE![X]!C_RS! No hay Node.js ni Python para servir HTML.
127
+ echo !C_GY!Instala Node.js: https://nodejs.org!C_RS!
128
+ echo !C_GY!O Python: https://python.org!C_RS!
129
+ pause
130
+ goto :end
131
+ )
132
+
133
+ REM --- No detectado ---
134
+ echo !C_YE![!]!C_RS! No se detecto stack automaticamente.
135
+ echo.
136
+ echo !C_GY!Archivos buscados:!C_RS!
137
+ echo - package.json Node.js
138
+ echo - manage.py Django
139
+ echo - app.py / main.py Flask / FastAPI
140
+ echo - server.js Node.js simple
141
+ echo - Cargo.toml Rust
142
+ echo - go.mod Go
143
+ echo - docker-compose.yml Docker
144
+ echo - index.html HTML estatico
145
+ echo.
146
+ pause
147
+ goto :end
148
+
149
+ :detected
150
+ REM --- Verificar dependencias ---
151
+ if "!NEEDS_PYTHON!"=="1" (
152
+ where python >nul 2>nul
153
+ if errorlevel 1 (
154
+ echo !C_RE![X]!C_RS! Python no esta instalado.
155
+ echo !C_GY!Descarga: https://python.org!C_RS!
156
+ pause
157
+ goto :end
158
+ )
159
+ )
160
+ if "!NEEDS_NODE!"=="1" (
161
+ where node >nul 2>nul
162
+ if errorlevel 1 (
163
+ echo !C_RE![X]!C_RS! Node.js no esta instalado.
164
+ echo !C_GY!Descarga: https://nodejs.org!C_RS!
165
+ pause
166
+ goto :end
167
+ )
168
+ )
169
+
170
+ REM --- Mostrar info ---
171
+ echo !C_CY!-!C_RS! !C_GY!Stack:!C_RS! !C_WH!!STACK!!C_RS!
172
+ if not "!PORT!"=="" echo !C_CY!-!C_RS! !C_GY!Puerto:!C_RS! !C_WH!!PORT!!C_RS!
173
+ echo !C_CY!-!C_RS! !C_GY!Comando:!C_RS! !C_WH!!CMD!!C_RS!
174
+ echo.
175
+
176
+ REM --- Confirmar ---
177
+ set "CONFIRM="
178
+ set /p "CONFIRM= !C_BC!Ejecutar servidor?!C_RS! !C_GY![S/n]!C_RS!: "
179
+ if /i "!CONFIRM!"=="n" goto :end
180
+
181
+ echo.
182
+ echo !C_GR!Iniciando servidor...!C_RS!
183
+ echo !C_GY!Ctrl+C para detener!C_RS!
184
+ echo.
185
+
186
+ REM --- Ejecutar ---
187
+ !CMD!
188
+
189
+ goto :end
190
+
191
+ :end
192
+ endlocal
193
+ exit /b 0
194
+
195
+ REM ============================================================
196
+ REM :find_free_port - Encuentra puerto libre desde %1
197
+ REM Prueba %1, %1+1, ... hasta %1+20
198
+ REM ============================================================
199
+ :find_free_port
200
+ set "PORT=%~1"
201
+ set /a "MAX=%~1 + 20"
202
+ :fp_loop
203
+ netstat -an 2>nul | findstr ":!PORT! " >nul 2>nul
204
+ if errorlevel 1 goto :fp_found
205
+ set /a PORT+=1
206
+ if !PORT! LEQ !MAX! goto :fp_loop
207
+ set "PORT=%~1"
208
+ :fp_found
209
+ goto :eof