1- require ( 'dotenv' ) . config ( ) ;
2- const express = require ( 'express' ) ;
3- const http = require ( 'http' ) ;
4- const { WebSocketServer } = require ( 'ws' ) ;
5- const { Client } = require ( 'ssh2' ) ;
1+ // ✅ Backend index.js actualizado (autocompletado y sugerencias con logs extendidos)
2+
3+ require ( "dotenv" ) . config ( ) ;
4+ const express = require ( "express" ) ;
5+ const http = require ( "http" ) ;
6+ const { WebSocketServer } = require ( "ws" ) ;
7+ const { Client } = require ( "ssh2" ) ;
68
79const PORT = process . env . PORT || 3001 ;
810const app = express ( ) ;
911const server = http . createServer ( app ) ;
1012const wss = new WebSocketServer ( { server } ) ;
1113
12- wss . on ( ' connection' , ( ws ) => {
13- console . log ( ' 📡 Cliente conectado' ) ;
14- ws . send ( JSON . stringify ( { status : ' 🟢 Consola lista para conectar' } ) ) ;
14+ wss . on ( " connection" , ( ws ) => {
15+ console . log ( " 📡 Cliente conectado" ) ;
16+ ws . send ( JSON . stringify ( { status : " 🟢 Consola lista para conectar" } ) ) ;
1517
1618 const ssh = new Client ( ) ;
1719 let shellStream ;
18- let contextBuffer = '' ;
20+ let contextBuffer = "" ;
1921 let collectingContext = false ;
2022
2123 const cleanContextPart = ( text ) => {
2224 return text
23- . replace ( / \x1B \[ [ 0 - 9 ; ? ] * [ a - z A - Z ] / g, '' )
24- . split ( '\n' )
25+ . replace ( / \x1B \[ [ 0 - 9 ; ? ] * [ a - z A - Z ] / g, "" )
26+ . split ( "\n" )
2527 . map ( ( line ) => line . trim ( ) )
26- . filter ( ( line ) => line && ! line . includes ( ' __CTX' ) && ! line . includes ( '$' ) )
27- . join ( '' )
28+ . filter ( ( line ) => line && ! line . includes ( " __CTX" ) && ! line . includes ( "$" ) )
29+ . join ( "" )
2830 . trim ( ) ;
2931 } ;
3032
3133 ssh
32- . on ( ' ready' , ( ) => {
33- console . log ( ' 🔐 Conexión SSH establecida' ) ;
34+ . on ( " ready" , ( ) => {
35+ console . log ( " 🔐 Conexión SSH establecida" ) ;
3436 ssh . shell ( ( err , stream ) => {
3537 if ( err ) {
36- ws . send ( JSON . stringify ( { error : ' Error iniciando shell: ' + err . message } ) ) ;
38+ ws . send ( JSON . stringify ( { error : " Error iniciando shell: " + err . message } ) ) ;
3739 return ;
3840 }
3941
4042 shellStream = stream ;
41- stream . write ( 'echo __CTX_START__ && whoami && echo __CTX__ && hostname && echo __CTX__ && pwd && echo __CTX_END__\n' ) ;
43+ stream . write (
44+ "echo __CTX_START__ && whoami && echo __CTX__ && hostname && echo __CTX__ && pwd && echo __CTX_END__\n"
45+ ) ;
4246
43- stream . on ( ' data' , ( data ) => {
47+ stream . on ( " data" , ( data ) => {
4448 const text = data . toString ( ) ;
45-
46- if ( text . includes ( '__AUTO_MARK_' ) && text . includes ( '__END' ) ) {
47- const matched = text . match ( / _ _ A U T O _ M A R K _ ( \d + ) _ _ / ) ;
48- const id = matched ?. [ 1 ] ;
49- if ( ! id ) return ;
50-
51- const [ _ , raw ] = text . split ( `__AUTO_MARK_${ id } __` ) ;
52- const [ content ] = raw . split ( `__AUTO_MARK_${ id } ___END` ) ;
53-
54- const lines = content
55- . split ( '\n' )
56- . map ( ( l ) => l . trim ( ) )
57- . filter ( Boolean ) ;
58-
59- const unique = [ ...new Set ( lines ) ] ;
60- if ( unique . length === 1 ) {
61- ws . send ( JSON . stringify ( { autocomplete : unique [ 0 ] } ) ) ;
62- } else if ( unique . length > 1 ) {
63- ws . send ( JSON . stringify ( { suggestions : unique } ) ) ;
64- }
65- return ;
49+ console . log ( "📥 SSH DATA:" , JSON . stringify ( text ) ) ;
50+
51+ if ( text . includes ( "__AUTO_MARK_" ) && text . includes ( "__END" ) ) {
52+ const matched = text . match ( / _ _ A U T O _ M A R K _ ( \d + ) _ _ / ) ;
53+ const id = matched ?. [ 1 ] ;
54+ if ( ! id ) return ;
55+
56+ const [ _ , raw ] = text . split ( `__AUTO_MARK_${ id } __` ) ;
57+ const [ content ] = raw . split ( `__AUTO_MARK_${ id } ___END` ) ;
58+
59+ const rawLines = content . split ( "\n" ) . map ( ( l ) => l . trim ( ) ) ;
60+
61+ const cleanLine = ( line ) =>
62+ line
63+ . replace ( / \x1B \[ [ 0 - 9 ; ? ] * [ a - z A - Z ] / g, "" ) // ANSI codes
64+ . replace ( / \x1B \] [ ^ \x07 ] * \x07 / g, "" ) // OSC sequences
65+ . replace ( / ^ \s * [ \x00 - \x1F \x7F - \x9F ] + / , "" ) // Control chars
66+ . trim ( ) ;
67+
68+ const lines = rawLines
69+ . map ( cleanLine )
70+ . filter (
71+ ( l ) =>
72+ l &&
73+ ! l . includes ( "__AUTO" ) &&
74+ ! l . includes ( "_END" ) &&
75+ ! / ^ \w + @ [ \w . - ] + : .* \$ / . test ( l ) &&
76+ ! l . startsWith ( "echo" ) &&
77+ ! l . includes ( "compgen" ) &&
78+ ! l . startsWith ( "bash:" ) &&
79+ ! / ^ \x1B / . test ( l )
80+ ) ;
81+
82+ const unique = [ ...new Set ( lines ) ] ;
83+
84+ console . log ( "🔍 Sugerencias/autocompletado recibidas:" , unique ) ;
85+
86+ if ( unique . length > 0 ) {
87+ ws . send ( JSON . stringify ( { suggestions : unique } ) ) ;
88+ // También opcionalmente las imprimes en la terminal
89+ // shellStream.write("echo " + unique.join(" ") + "\n");
90+ } else {
91+ ws . send ( JSON . stringify ( { suggestions : [ ] } ) ) ;
92+ }
93+ return ;
6694 }
6795
68- if ( text . includes ( ' __CTX_START__' ) ) {
96+ if ( text . includes ( " __CTX_START__" ) ) {
6997 collectingContext = true ;
70- contextBuffer = '' ;
98+ contextBuffer = "" ;
7199 return ;
72100 }
73101
74- if ( text . includes ( ' __CTX_END__' ) ) {
102+ if ( text . includes ( " __CTX_END__" ) ) {
75103 collectingContext = false ;
76104 contextBuffer += text ;
77- const parts = contextBuffer . split ( ' __CTX__' ) ;
105+ const parts = contextBuffer . split ( " __CTX__" ) ;
78106 const context = {
79- user : cleanContextPart ( parts [ 0 ] || '' ) ,
80- host : cleanContextPart ( parts [ 1 ] || '' ) ,
81- cwd : cleanContextPart ( parts [ 2 ] || '' ) ,
107+ user : cleanContextPart ( parts [ 0 ] || "" ) ,
108+ host : cleanContextPart ( parts [ 1 ] || "" ) ,
109+ cwd : cleanContextPart ( parts [ 2 ] || "" ) ,
82110 } ;
83111 ws . send ( JSON . stringify ( context ) ) ;
84112 return ;
@@ -89,33 +117,37 @@ wss.on('connection', (ws) => {
89117 return ;
90118 }
91119
92- if ( ! text . includes ( '__AUTO_MARK_' ) ) {
120+ if (
121+ ! text . includes ( "__AUTO_MARK_" ) &&
122+ ! / ^ e c h o \s + [ ^ \n ] + $ / m. test ( text . trim ( ) ) // evita imprimir líneas como echo AST_API
123+ ) {
93124 ws . send ( JSON . stringify ( { output : text } ) ) ;
94125 }
95126 } ) ;
96127
97- stream . stderr . on ( 'data' , ( data ) => {
128+ stream . stderr . on ( "data" , ( data ) => {
129+ console . error ( "❌ STDERR:" , data . toString ( ) ) ;
98130 ws . send ( JSON . stringify ( { error : data . toString ( ) } ) ) ;
99131 } ) ;
100132
101- stream . on ( ' close' , ( ) => {
102- console . log ( ' 🔒 Shell cerrada' ) ;
133+ stream . on ( " close" , ( ) => {
134+ console . log ( " 🔒 Shell cerrada" ) ;
103135 ssh . end ( ) ;
104136 } ) ;
105137 } ) ;
106138 } )
107- . on ( ' error' , ( err ) => {
108- console . error ( ' ❌ Error SSH:' , err . message ) ;
109- ws . send ( JSON . stringify ( { error : ' Fallo SSH: ' + err . message } ) ) ;
139+ . on ( " error" , ( err ) => {
140+ console . error ( " ❌ Error SSH:" , err . message ) ;
141+ ws . send ( JSON . stringify ( { error : " Fallo SSH: " + err . message } ) ) ;
110142 } )
111143 . connect ( {
112- host : ' 192.168.20.58' ,
144+ host : " 192.168.20.58" ,
113145 port : 22 ,
114- username : ' chechojgb' ,
115- password : ' 3209925728' ,
146+ username : " chechojgb" ,
147+ password : " 3209925728" ,
116148 } ) ;
117149
118- ws . on ( ' message' , ( msg ) => {
150+ ws . on ( " message" , ( msg ) => {
119151 let command ;
120152 try {
121153 const parsed = JSON . parse ( msg ) ;
@@ -126,48 +158,32 @@ wss.on('connection', (ws) => {
126158
127159 if ( ! shellStream || ! command ) return ;
128160
129- const isInternal = command . startsWith ( ' __AUTO_COMPLETE__' ) || command . startsWith ( ' __AUTO_SUGGEST__' ) ;
161+ const isInternal = command . startsWith ( " __AUTO_COMPLETE__" ) || command . startsWith ( " __AUTO_SUGGEST__" ) ;
130162
131163 if ( isInternal ) {
132- if ( command . startsWith ( '__AUTO_COMPLETE__' ) ) {
133- const prefix = command . replace ( '__AUTO_COMPLETE__' , '' ) . trim ( ) ;
134- const parts = prefix . split ( / \s + / ) ;
135- const lastWord = parts [ parts . length - 1 ] || '' ;
136- const autoId = Date . now ( ) ;
137-
138- const baseCommand = parts [ 0 ] ;
139-
140- let flags = '' ;
141- if ( baseCommand === 'cd' ) {
142- flags = '-d' ;
143- } else if ( baseCommand === 'cat' || baseCommand === 'less' || baseCommand === 'nano' ) {
144- flags = '-f' ;
145- } else {
146- flags = '-d -f' ;
147- }
164+ const parts = command . replace ( / _ _ A U T O _ ( C O M P L E T E | S U G G E S T ) _ _ / , "" ) . trim ( ) . split ( / \s + / ) ;
165+ const lastWord = parts [ parts . length - 1 ] || "" ;
166+ const baseCommand = parts [ 0 ] ;
167+ const autoId = Date . now ( ) ;
148168
149- const wrapped = `echo __AUTO_MARK_${ autoId } __ && compgen ${ flags } "${ lastWord } " && echo __AUTO_MARK_${ autoId } ___END` ;
150- console . log ( `🧠 Ejecutando autocompletado con flags ${ flags } para: ${ lastWord } ` ) ;
151- shellStream . write ( `${ wrapped } \n` ) ;
152- return ;
153- }
154-
155- if ( command . startsWith ( '__AUTO_SUGGEST__' ) ) {
156- shellStream . write ( 'ls -1\n' ) ;
157- shellStream . once ( 'data' , ( data ) => {
158- const lines = data . toString ( ) . split ( '\n' ) . map ( ( l ) => l . trim ( ) ) . filter ( Boolean ) ;
159- ws . send ( JSON . stringify ( { suggestions : lines } ) ) ;
160- } ) ;
161- return ;
162- }
169+ let flags = "-d -f" ;
170+ if ( baseCommand === "cd" ) flags = "-d" ;
171+ if ( [ "cat" , "nano" , "less" ] . includes ( baseCommand ) ) flags = "-f" ;
172+
173+ const wrapped = `echo __AUTO_MARK_${ autoId } __ && compgen ${ flags } \"${ lastWord } \" && echo __AUTO_MARK_${ autoId } ___END` ;
174+
175+ console . log ( `📤 Ejecutando ${ command . startsWith ( "__AUTO_COMPLETE__" ) ? "autocompletado" : "sugerencias" } con:` , wrapped ) ;
176+
177+ shellStream . write ( `${ wrapped } \n` ) ;
178+ return ;
163179 }
164180
165181 shellStream . write ( `${ command } \n` ) ;
166- shellStream . write ( ' echo __CTX_START__ && whoami && echo __CTX__ && hostname && echo __CTX__ && pwd && echo __CTX_END__\n' ) ;
182+ shellStream . write ( " echo __CTX_START__ && whoami && echo __CTX__ && hostname && echo __CTX__ && pwd && echo __CTX_END__\n" ) ;
167183 } ) ;
168184
169- ws . on ( ' close' , ( ) => {
170- console . log ( ' 🔌 Cliente desconectado' ) ;
185+ ws . on ( " close" , ( ) => {
186+ console . log ( " 🔌 Cliente desconectado" ) ;
171187 if ( shellStream ) shellStream . end ( ) ;
172188 ssh . end ( ) ;
173189 } ) ;
0 commit comments