entexto-cli 2.1.1 → 2.2.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/bin/entexto.js CHANGED
@@ -81,10 +81,17 @@ program
81
81
  program
82
82
  .command('tunnel [target]')
83
83
  .description('Exponer un servidor local en una URL pública de entexto.com')
84
- .action((target) => {
85
- require('../lib/commands/tunnel')({ target: target || '3000' });
84
+ .option('-l, --link <name>', 'Link personalizado: /t/<name> (requiere login)')
85
+ .action((target, opts) => {
86
+ require('../lib/commands/tunnel')({ target: target || '3000', link: opts.link });
86
87
  });
87
88
 
89
+ // ─── entexto tunnels ─────────────────────────────────────────
90
+ program
91
+ .command('tunnels')
92
+ .description('Ver y gestionar tus túneles abiertos')
93
+ .action(require('../lib/commands/tunnels'));
94
+
88
95
  // ─── entexto create ──────────────────────────────────────────
89
96
  program
90
97
  .command('create')
@@ -30,6 +30,14 @@ module.exports = async function tunnel(opts) {
30
30
  const { getConfig } = require('../utils/config');
31
31
  const cfg = getConfig();
32
32
  const baseUrl = (cfg.baseUrl || 'https://entexto.com').replace(/\/$/, '');
33
+ const token = cfg.token || null;
34
+
35
+ // Validar link personalizado
36
+ const customLink = opts.link || null;
37
+ if (customLink && !token) {
38
+ console.error(chalk.red('Los links personalizados requieren iniciar sesión. Ejecuta: entexto login'));
39
+ process.exit(1);
40
+ }
33
41
 
34
42
  // Calcular URL del WebSocket
35
43
  const wsUrl = baseUrl.replace(/^http/, 'ws') + '/tunnel';
@@ -73,6 +81,8 @@ module.exports = async function tunnel(opts) {
73
81
  socket.on('connect', () => {
74
82
  const meta = { target: targetUrl };
75
83
  if (currentTunnelId) meta.reclaimId = currentTunnelId; // intentar recuperar el mismo ID
84
+ if (customLink) meta.link = customLink;
85
+ if (token) meta.token = token;
76
86
 
77
87
  socket.emit('open-tunnel', meta, (res) => {
78
88
  if (!res || !res.ok) {
@@ -230,7 +240,8 @@ module.exports = async function tunnel(opts) {
230
240
  }
231
241
  });
232
242
 
233
- localReq.setTimeout(useStreaming ? 120000 : 25000, () => {
243
+ // 10 min para streaming (modelos que piensan mucho), 25s clásico
244
+ localReq.setTimeout(useStreaming ? 600000 : 25000, () => {
234
245
  localReq.destroy();
235
246
  console.log(chalk.red('TIMEOUT'));
236
247
  if (useStreaming) {
@@ -0,0 +1,114 @@
1
+ 'use strict';
2
+
3
+ const chalk = require('chalk');
4
+ const ora = require('ora');
5
+
6
+ module.exports = async function tunnels() {
7
+ const { getConfig } = require('../utils/config');
8
+ const cfg = getConfig();
9
+ const token = cfg.token;
10
+
11
+ if (!token) {
12
+ console.error(chalk.red('Debes iniciar sesión para ver tus túneles. Ejecuta: entexto login'));
13
+ process.exit(1);
14
+ }
15
+
16
+ const baseUrl = (cfg.baseUrl || 'https://entexto.com').replace(/\/$/, '');
17
+ const wsUrl = baseUrl.replace(/^http/, 'ws') + '/tunnel';
18
+
19
+ const spinner = ora('Consultando túneles abiertos...').start();
20
+
21
+ let ioClient;
22
+ try {
23
+ ioClient = require('socket.io-client');
24
+ } catch {
25
+ spinner.fail('Falta socket.io-client. Ejecuta: npm install socket.io-client');
26
+ process.exit(1);
27
+ }
28
+
29
+ const socket = ioClient(wsUrl, {
30
+ transports: ['websocket'],
31
+ upgrade: false,
32
+ reconnection: false,
33
+ timeout: 10000,
34
+ });
35
+
36
+ socket.on('connect', () => {
37
+ socket.emit('list-tunnels', { token }, (res) => {
38
+ if (!res || !res.ok) {
39
+ spinner.fail('Error: ' + (res?.error || 'sin respuesta'));
40
+ socket.disconnect();
41
+ process.exit(1);
42
+ }
43
+
44
+ const list = res.tunnels || [];
45
+ spinner.stop();
46
+
47
+ if (list.length === 0) {
48
+ console.log(chalk.yellow('\n No tienes túneles abiertos.\n'));
49
+ socket.disconnect();
50
+ process.exit(0);
51
+ }
52
+
53
+ console.log(chalk.bold(`\n 🚇 Tus túneles abiertos (${list.length}):\n`));
54
+ list.forEach((t, i) => {
55
+ const status = t.connected
56
+ ? chalk.green('● conectado')
57
+ : chalk.yellow('○ reconectando');
58
+ const url = chalk.cyan.underline(`${baseUrl}/t/${t.id}`);
59
+ console.log(` ${i + 1}. ${url}`);
60
+ console.log(` → ${t.target} ${status}${t.queued ? chalk.gray(` (${t.queued} en cola)`) : ''}`);
61
+ });
62
+ console.log('');
63
+
64
+ // Preguntar si quiere cerrar alguno
65
+ askClose(socket, token, list, baseUrl);
66
+ });
67
+ });
68
+
69
+ socket.on('connect_error', (err) => {
70
+ spinner.fail('No se pudo conectar: ' + err.message);
71
+ process.exit(1);
72
+ });
73
+ };
74
+
75
+ async function askClose(socket, token, list, baseUrl) {
76
+ let inquirer;
77
+ try {
78
+ inquirer = require('inquirer');
79
+ } catch {
80
+ socket.disconnect();
81
+ process.exit(0);
82
+ }
83
+
84
+ const { action } = await inquirer.prompt([{
85
+ type: 'list',
86
+ name: 'action',
87
+ message: '¿Qué deseas hacer?',
88
+ choices: [
89
+ ...list.map((t, i) => ({
90
+ name: `Cerrar /t/${t.id} (→ ${t.target})`,
91
+ value: t.id,
92
+ })),
93
+ { name: 'Salir', value: null },
94
+ ],
95
+ }]);
96
+
97
+ if (!action) {
98
+ socket.disconnect();
99
+ process.exit(0);
100
+ }
101
+
102
+ const chalk = require('chalk');
103
+ const spinner = require('ora')('Cerrando túnel...').start();
104
+
105
+ socket.emit('close-tunnel', { token, tunnelId: action }, (res) => {
106
+ if (res?.ok) {
107
+ spinner.succeed(`Túnel /t/${action} cerrado`);
108
+ } else {
109
+ spinner.fail('Error: ' + (res?.error || 'sin respuesta'));
110
+ }
111
+ socket.disconnect();
112
+ process.exit(0);
113
+ });
114
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entexto-cli",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "CLI oficial de Entexto — Crea, deploya y gestiona proyectos, dominios, APIs y Live SDK desde tu terminal",
5
5
  "main": "lib/index.js",
6
6
  "bin": {