devportal-tunnel 1.0.1 → 1.0.2
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/README.md +13 -4
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +13 -13
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/list.js +3 -3
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/port.d.ts +2 -0
- package/dist/commands/port.d.ts.map +1 -0
- package/dist/commands/port.js +192 -0
- package/dist/commands/port.js.map +1 -0
- package/dist/commands/start.d.ts +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +132 -67
- package/dist/commands/start.js.map +1 -1
- package/dist/index.js +56 -44
- package/dist/index.js.map +1 -1
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +9 -8
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/logger.d.ts +18 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +56 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/tunnelClient.d.ts +34 -3
- package/dist/utils/tunnelClient.d.ts.map +1 -1
- package/dist/utils/tunnelClient.js +355 -104
- package/dist/utils/tunnelClient.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,11 +5,13 @@ Expose your localhost to the internet with a single command.
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
### From npm (after publishing)
|
|
8
|
+
|
|
8
9
|
```bash
|
|
9
10
|
npm install -g devportal
|
|
10
11
|
```
|
|
11
12
|
|
|
12
13
|
### From source
|
|
14
|
+
|
|
13
15
|
```bash
|
|
14
16
|
git clone <repo>
|
|
15
17
|
cd cli
|
|
@@ -30,9 +32,6 @@ devportal 3000 --subdomain my-api
|
|
|
30
32
|
# Password protected
|
|
31
33
|
devportal 3000 --password secret123
|
|
32
34
|
|
|
33
|
-
# Show QR code for mobile testing
|
|
34
|
-
devportal 3000 --qr
|
|
35
|
-
|
|
36
35
|
# Temporary demo link (expires in 2 hours)
|
|
37
36
|
devportal 3000 --demo
|
|
38
37
|
|
|
@@ -43,6 +42,7 @@ devportal 3000 --local
|
|
|
43
42
|
## Commands
|
|
44
43
|
|
|
45
44
|
### Start a tunnel
|
|
45
|
+
|
|
46
46
|
```bash
|
|
47
47
|
devportal <port> [options]
|
|
48
48
|
|
|
@@ -50,17 +50,18 @@ Options:
|
|
|
50
50
|
-s, --subdomain <name> Custom subdomain for your URL
|
|
51
51
|
-p, --password <pass> Password protect the tunnel
|
|
52
52
|
--demo Create a temporary link (2 hours)
|
|
53
|
-
--qr Display QR code in terminal
|
|
54
53
|
--auth-header <header> Add authorization header to forwarded requests
|
|
55
54
|
--local Run in simulation mode (no server required)
|
|
56
55
|
```
|
|
57
56
|
|
|
58
57
|
### List active tunnels
|
|
58
|
+
|
|
59
59
|
```bash
|
|
60
60
|
devportal ls
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
### Stop a tunnel
|
|
64
|
+
|
|
64
65
|
```bash
|
|
65
66
|
# Stop specific tunnel
|
|
66
67
|
devportal stop <tunnel-id>
|
|
@@ -70,6 +71,7 @@ devportal stop --all
|
|
|
70
71
|
```
|
|
71
72
|
|
|
72
73
|
### Replay requests
|
|
74
|
+
|
|
73
75
|
```bash
|
|
74
76
|
# Replay a captured request
|
|
75
77
|
devportal replay <request-id>
|
|
@@ -82,11 +84,13 @@ devportal replay <request-id> --body '{"test": true}'
|
|
|
82
84
|
```
|
|
83
85
|
|
|
84
86
|
### Stream logs
|
|
87
|
+
|
|
85
88
|
```bash
|
|
86
89
|
devportal logs <tunnel-id>
|
|
87
90
|
```
|
|
88
91
|
|
|
89
92
|
### Configuration
|
|
93
|
+
|
|
90
94
|
```bash
|
|
91
95
|
# View current config
|
|
92
96
|
devportal config
|
|
@@ -99,6 +103,7 @@ devportal config reset
|
|
|
99
103
|
```
|
|
100
104
|
|
|
101
105
|
### Device info
|
|
106
|
+
|
|
102
107
|
```bash
|
|
103
108
|
devportal whoami
|
|
104
109
|
```
|
|
@@ -147,6 +152,7 @@ Internet Request
|
|
|
147
152
|
## Publishing to npm
|
|
148
153
|
|
|
149
154
|
1. **Update package.json** with your details:
|
|
155
|
+
|
|
150
156
|
```json
|
|
151
157
|
{
|
|
152
158
|
"name": "devportal",
|
|
@@ -159,17 +165,20 @@ Internet Request
|
|
|
159
165
|
```
|
|
160
166
|
|
|
161
167
|
2. **Login and publish**:
|
|
168
|
+
|
|
162
169
|
```bash
|
|
163
170
|
npm login
|
|
164
171
|
npm publish
|
|
165
172
|
```
|
|
166
173
|
|
|
167
174
|
3. **For scoped package** (if name is taken):
|
|
175
|
+
|
|
168
176
|
```json
|
|
169
177
|
{
|
|
170
178
|
"name": "@yourusername/devportal"
|
|
171
179
|
}
|
|
172
180
|
```
|
|
181
|
+
|
|
173
182
|
```bash
|
|
174
183
|
npm publish --access public
|
|
175
184
|
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,CACjC,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAkCf"}
|
package/dist/commands/config.js
CHANGED
|
@@ -10,32 +10,32 @@ async function configCommand(action, value) {
|
|
|
10
10
|
if (!action) {
|
|
11
11
|
// Show current config
|
|
12
12
|
const config = (0, config_1.getConfig)();
|
|
13
|
-
console.log(chalk_1.default.bold(
|
|
13
|
+
console.log(chalk_1.default.bold("Current Configuration"));
|
|
14
14
|
console.log();
|
|
15
|
-
console.log(chalk_1.default.gray(
|
|
16
|
-
console.log(chalk_1.default.gray(
|
|
15
|
+
console.log(chalk_1.default.gray("Server URL: "), chalk_1.default.white(config.serverUrl));
|
|
16
|
+
console.log(chalk_1.default.gray("WebSocket URL: "), chalk_1.default.white(config.wsUrl));
|
|
17
17
|
console.log();
|
|
18
|
-
console.log(chalk_1.default.gray(
|
|
18
|
+
console.log(chalk_1.default.gray("Config file: ~/.devportal/config.json"));
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
switch (action) {
|
|
22
|
-
case
|
|
22
|
+
case "server":
|
|
23
23
|
if (!value) {
|
|
24
|
-
console.log(chalk_1.default.red(
|
|
25
|
-
console.log(chalk_1.default.gray(
|
|
24
|
+
console.log(chalk_1.default.red("Please provide a server URL"));
|
|
25
|
+
console.log(chalk_1.default.gray("Usage: devportal config server <url>"));
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
(0, config_1.setServerUrl)(value);
|
|
29
|
-
console.log(chalk_1.default.green(
|
|
30
|
-
console.log(chalk_1.default.gray(
|
|
29
|
+
console.log(chalk_1.default.green("Server URL updated"));
|
|
30
|
+
console.log(chalk_1.default.gray("Server: "), chalk_1.default.white(value));
|
|
31
31
|
break;
|
|
32
|
-
case
|
|
33
|
-
(0, config_1.setServerUrl)(
|
|
34
|
-
console.log(chalk_1.default.green(
|
|
32
|
+
case "reset":
|
|
33
|
+
(0, config_1.setServerUrl)("https://devportal.stylnode.in");
|
|
34
|
+
console.log(chalk_1.default.green("Config reset to defaults"));
|
|
35
35
|
break;
|
|
36
36
|
default:
|
|
37
37
|
console.log(chalk_1.default.red(`Unknown config action: ${action}`));
|
|
38
|
-
console.log(chalk_1.default.gray(
|
|
38
|
+
console.log(chalk_1.default.gray("Available actions: server, reset"));
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
//# sourceMappingURL=config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":";;;;;AAGA,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":";;;;;AAGA,sCAqCC;AAxCD,kDAA0B;AAC1B,4CAA0D;AAEnD,KAAK,UAAU,aAAa,CACjC,MAAe,EACf,KAAc;IAEd,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,sBAAsB;QACtB,MAAM,MAAM,GAAG,IAAA,kBAAS,GAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,eAAK,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,eAAK,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC,CAAC;QACjE,OAAO;IACT,CAAC;IAED,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC,CAAC;gBACtD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC,CAAC;gBAChE,OAAO;YACT,CAAC;YACD,IAAA,qBAAY,EAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,eAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3D,MAAM;QAER,KAAK,OAAO;YACV,IAAA,qBAAY,EAAC,+BAA+B,CAAC,CAAC;YAC9C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC;YACrD,MAAM;QAER;YACE,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC,CAAC;YAC3D,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;IAChE,CAAC;AACH,CAAC"}
|
package/dist/commands/list.js
CHANGED
|
@@ -19,17 +19,17 @@ async function listTunnels() {
|
|
|
19
19
|
const header = [
|
|
20
20
|
chalk_1.default.gray('ID'.padEnd(15)),
|
|
21
21
|
chalk_1.default.gray('Name'.padEnd(22)),
|
|
22
|
-
chalk_1.default.gray('Port'.padEnd(
|
|
22
|
+
chalk_1.default.gray('Local Port'.padEnd(12)),
|
|
23
23
|
chalk_1.default.gray('URL'),
|
|
24
24
|
].join(' ');
|
|
25
25
|
console.log(header);
|
|
26
|
-
console.log(chalk_1.default.gray('─'.repeat(
|
|
26
|
+
console.log(chalk_1.default.gray('─'.repeat(85)));
|
|
27
27
|
// Table rows
|
|
28
28
|
for (const tunnel of tunnels) {
|
|
29
29
|
const row = [
|
|
30
30
|
chalk_1.default.white(tunnel.id.padEnd(15)),
|
|
31
31
|
chalk_1.default.cyan(tunnel.name.padEnd(22)),
|
|
32
|
-
chalk_1.default.white(tunnel.localPort.toString().padEnd(
|
|
32
|
+
chalk_1.default.white(tunnel.localPort.toString().padEnd(12)),
|
|
33
33
|
chalk_1.default.underline(tunnel.url),
|
|
34
34
|
].join(' ');
|
|
35
35
|
console.log(row);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":";;;;;AAGA,kCAmCC;AAtCD,kDAA0B;AAC1B,8CAA8C;AAEvC,KAAK,UAAU,WAAW;IAC/B,MAAM,OAAO,GAAG,IAAA,oBAAU,GAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IAEhE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,eAAe;IACf,MAAM,MAAM,GAAG;QACb,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC3B,eAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7B,eAAK,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":";;;;;AAGA,kCAmCC;AAtCD,kDAA0B;AAC1B,8CAA8C;AAEvC,KAAK,UAAU,WAAW;IAC/B,MAAM,OAAO,GAAG,IAAA,oBAAU,GAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IAEhE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,eAAe;IACf,MAAM,MAAM,GAAG;QACb,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC3B,eAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7B,eAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnC,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC;KAClB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAExC,aAAa;IACb,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG;YACV,eAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACjC,eAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAClC,eAAK,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACnD,eAAK,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC;SAC5B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,mBAAmB,CAAC,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../src/commands/port.ts"],"names":[],"mappings":"AAWA,wBAAsB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B/E"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.portCommand = portCommand;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
+
const tunnels_1 = require("../utils/tunnels");
|
|
42
|
+
const http = __importStar(require("http"));
|
|
43
|
+
async function portCommand(action, port) {
|
|
44
|
+
if (!action) {
|
|
45
|
+
await showPortUsage();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
switch (action.toLowerCase()) {
|
|
49
|
+
case 'check':
|
|
50
|
+
if (!port) {
|
|
51
|
+
console.log(chalk_1.default.red('Please specify a port to check'));
|
|
52
|
+
console.log(chalk_1.default.gray('Usage: devportal-tunnel port check <port>'));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await checkPort(port);
|
|
56
|
+
break;
|
|
57
|
+
case 'scan':
|
|
58
|
+
const startPort = port || 3000;
|
|
59
|
+
await scanPorts(startPort, startPort + 9);
|
|
60
|
+
break;
|
|
61
|
+
case 'list':
|
|
62
|
+
await showPortUsage();
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
console.log(chalk_1.default.red(`Unknown port action: ${action}`));
|
|
66
|
+
console.log(chalk_1.default.gray('Available actions: check, scan, list'));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function showPortUsage() {
|
|
70
|
+
const tunnels = (0, tunnels_1.getTunnels)().filter(t => t.status === 'live');
|
|
71
|
+
console.log(chalk_1.default.bold('Port Usage'));
|
|
72
|
+
console.log();
|
|
73
|
+
if (tunnels.length === 0) {
|
|
74
|
+
console.log(chalk_1.default.yellow('No ports currently tunneled'));
|
|
75
|
+
console.log(chalk_1.default.gray('Start a tunnel to see port usage'));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Header
|
|
79
|
+
const header = [
|
|
80
|
+
chalk_1.default.gray('Port'.padEnd(8)),
|
|
81
|
+
chalk_1.default.gray('Status'.padEnd(12)),
|
|
82
|
+
chalk_1.default.gray('Tunnel ID'.padEnd(15)),
|
|
83
|
+
chalk_1.default.gray('Tunnel Name'),
|
|
84
|
+
].join(' ');
|
|
85
|
+
console.log(header);
|
|
86
|
+
console.log(chalk_1.default.gray('─'.repeat(70)));
|
|
87
|
+
// Show tunneled ports
|
|
88
|
+
for (const tunnel of tunnels) {
|
|
89
|
+
const row = [
|
|
90
|
+
chalk_1.default.white(tunnel.localPort.toString().padEnd(8)),
|
|
91
|
+
chalk_1.default.green('TUNNELED'.padEnd(12)),
|
|
92
|
+
chalk_1.default.cyan(tunnel.id.padEnd(15)),
|
|
93
|
+
chalk_1.default.white(tunnel.name),
|
|
94
|
+
].join(' ');
|
|
95
|
+
console.log(row);
|
|
96
|
+
}
|
|
97
|
+
console.log();
|
|
98
|
+
console.log(chalk_1.default.gray(`${tunnels.length} port(s) currently tunneled`));
|
|
99
|
+
console.log();
|
|
100
|
+
console.log(chalk_1.default.gray('Commands:'));
|
|
101
|
+
console.log(chalk_1.default.gray(' devportal-tunnel port check <port> - Check if a port is available'));
|
|
102
|
+
console.log(chalk_1.default.gray(' devportal-tunnel port scan [start] - Scan for available ports'));
|
|
103
|
+
}
|
|
104
|
+
async function checkPort(port) {
|
|
105
|
+
console.log(chalk_1.default.gray(`Checking port ${port}...`));
|
|
106
|
+
// Check if port is tunneled
|
|
107
|
+
const tunnels = (0, tunnels_1.getTunnels)().filter(t => t.status === 'live' && t.localPort === port);
|
|
108
|
+
if (tunnels.length > 0) {
|
|
109
|
+
const tunnel = tunnels[0];
|
|
110
|
+
console.log(chalk_1.default.yellow(`Port ${port} is currently tunneled`));
|
|
111
|
+
console.log(chalk_1.default.gray(` Tunnel ID: ${tunnel.id}`));
|
|
112
|
+
console.log(chalk_1.default.gray(` Tunnel Name: ${tunnel.name}`));
|
|
113
|
+
console.log(chalk_1.default.gray(` Public URL: ${tunnel.url}`));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Check if port is occupied
|
|
117
|
+
const isOccupied = await isPortOccupied(port);
|
|
118
|
+
if (isOccupied) {
|
|
119
|
+
console.log(chalk_1.default.green(`✓ Port ${port} is available (server running)`));
|
|
120
|
+
console.log(chalk_1.default.gray(' Ready to be tunneled'));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log(chalk_1.default.red(`✗ Port ${port} is not available (no server running)`));
|
|
124
|
+
console.log(chalk_1.default.gray(' Start your local server first'));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function scanPorts(startPort, endPort) {
|
|
128
|
+
console.log(chalk_1.default.gray(`Scanning ports ${startPort}-${endPort}...`));
|
|
129
|
+
console.log();
|
|
130
|
+
const results = [];
|
|
131
|
+
const tunnels = (0, tunnels_1.getTunnels)().filter(t => t.status === 'live');
|
|
132
|
+
for (let port = startPort; port <= endPort; port++) {
|
|
133
|
+
const tunnel = tunnels.find(t => t.localPort === port);
|
|
134
|
+
if (tunnel) {
|
|
135
|
+
results.push({
|
|
136
|
+
port,
|
|
137
|
+
status: 'tunneled',
|
|
138
|
+
tunnelId: tunnel.id,
|
|
139
|
+
tunnelName: tunnel.name,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const isOccupied = await isPortOccupied(port);
|
|
144
|
+
results.push({
|
|
145
|
+
port,
|
|
146
|
+
status: isOccupied ? 'occupied' : 'available',
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Header
|
|
151
|
+
const header = [
|
|
152
|
+
chalk_1.default.gray('Port'.padEnd(8)),
|
|
153
|
+
chalk_1.default.gray('Status'.padEnd(12)),
|
|
154
|
+
chalk_1.default.gray('Details'),
|
|
155
|
+
].join(' ');
|
|
156
|
+
console.log(header);
|
|
157
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
158
|
+
// Results
|
|
159
|
+
for (const result of results) {
|
|
160
|
+
const statusColor = result.status === 'available' ? chalk_1.default.red('AVAILABLE') :
|
|
161
|
+
result.status === 'occupied' ? chalk_1.default.green('OCCUPIED') :
|
|
162
|
+
chalk_1.default.yellow('TUNNELED');
|
|
163
|
+
const details = result.status === 'tunneled' ? `Tunnel: ${result.tunnelName}` :
|
|
164
|
+
result.status === 'occupied' ? 'Server running' :
|
|
165
|
+
'No server';
|
|
166
|
+
const row = [
|
|
167
|
+
chalk_1.default.white(result.port.toString().padEnd(8)),
|
|
168
|
+
statusColor.padEnd(12),
|
|
169
|
+
chalk_1.default.gray(details),
|
|
170
|
+
].join(' ');
|
|
171
|
+
console.log(row);
|
|
172
|
+
}
|
|
173
|
+
console.log();
|
|
174
|
+
const availableCount = results.filter(r => r.status === 'occupied').length;
|
|
175
|
+
const tunneledCount = results.filter(r => r.status === 'tunneled').length;
|
|
176
|
+
console.log(chalk_1.default.gray('Summary:'));
|
|
177
|
+
console.log(chalk_1.default.gray(` ${availableCount} port(s) available for tunneling`));
|
|
178
|
+
if (tunneledCount > 0) {
|
|
179
|
+
console.log(chalk_1.default.gray(` ${tunneledCount} port(s) already tunneled`));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function isPortOccupied(port) {
|
|
183
|
+
return new Promise((resolve) => {
|
|
184
|
+
const req = http.request({ host: 'localhost', port, timeout: 1000 }, () => {
|
|
185
|
+
resolve(true);
|
|
186
|
+
});
|
|
187
|
+
req.on('error', () => resolve(false));
|
|
188
|
+
req.on('timeout', () => resolve(false));
|
|
189
|
+
req.end();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/commands/port.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/start.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ interface StartOptions {
|
|
|
2
2
|
subdomain?: string;
|
|
3
3
|
password?: string;
|
|
4
4
|
demo?: boolean;
|
|
5
|
-
qr?: boolean;
|
|
6
5
|
authHeader?: string;
|
|
7
6
|
local?: boolean;
|
|
7
|
+
forward?: string;
|
|
8
8
|
}
|
|
9
9
|
export declare function startTunnel(port: number, options: StartOptions): Promise<void>;
|
|
10
10
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAYA,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAuNf"}
|