entexto-cli 2.1.2 → 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) {
@@ -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.2",
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": {