private-connect 0.4.1 → 0.4.5
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 +88 -82
- package/dist/index.js +241 -1
- package/package.json +18 -6
package/README.md
CHANGED
|
@@ -1,82 +1,88 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
Zero-friction connectivity tools. No signup required.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Test connectivity to any service
|
|
9
|
-
npx private-connect test db.internal:5432
|
|
10
|
-
|
|
11
|
-
# Create a temporary public tunnel
|
|
12
|
-
npx private-connect tunnel 3000
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Commands
|
|
16
|
-
|
|
17
|
-
### `test` - Test connectivity
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npx private-connect test <target>
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Examples:**
|
|
24
|
-
```bash
|
|
25
|
-
npx private-connect test db.internal:5432 # Database
|
|
26
|
-
npx private-connect test redis:6379 # Redis
|
|
27
|
-
npx private-connect test https://api.internal # API
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
**What it checks:**
|
|
31
|
-
- TCP connection
|
|
32
|
-
- TLS/SSL (if applicable)
|
|
33
|
-
- HTTP response (for web services)
|
|
34
|
-
- Latency
|
|
35
|
-
|
|
36
|
-
### `tunnel` - Create a temporary tunnel
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npx private-connect tunnel <port>
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Instantly expose a local service to the internet. No signup required.
|
|
43
|
-
|
|
44
|
-
**Examples:**
|
|
45
|
-
```bash
|
|
46
|
-
npx private-connect tunnel 3000 # Expose localhost:3000
|
|
47
|
-
npx private-connect tunnel localhost:8080 # Specify host and port
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
**Output:**
|
|
51
|
-
```
|
|
52
|
-
Private Connect - Temporary Tunnel
|
|
53
|
-
────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
Local: localhost:3000
|
|
56
|
-
Public: https://
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1
|
+
# Private Connect
|
|
2
|
+
|
|
3
|
+
Zero-friction connectivity tools. No signup required.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Test connectivity to any service
|
|
9
|
+
npx private-connect test db.internal:5432
|
|
10
|
+
|
|
11
|
+
# Create a temporary public tunnel
|
|
12
|
+
npx private-connect tunnel 3000
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Commands
|
|
16
|
+
|
|
17
|
+
### `test` - Test connectivity
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx private-connect test <target>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Examples:**
|
|
24
|
+
```bash
|
|
25
|
+
npx private-connect test db.internal:5432 # Database
|
|
26
|
+
npx private-connect test redis:6379 # Redis
|
|
27
|
+
npx private-connect test https://api.internal # API
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**What it checks:**
|
|
31
|
+
- TCP connection
|
|
32
|
+
- TLS/SSL (if applicable)
|
|
33
|
+
- HTTP response (for web services)
|
|
34
|
+
- Latency
|
|
35
|
+
|
|
36
|
+
### `tunnel` - Create a temporary tunnel
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx private-connect tunnel <port>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Instantly expose a local service to the internet. No signup required.
|
|
43
|
+
|
|
44
|
+
**Examples:**
|
|
45
|
+
```bash
|
|
46
|
+
npx private-connect tunnel 3000 # Expose localhost:3000
|
|
47
|
+
npx private-connect tunnel localhost:8080 # Specify host and port
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Output:**
|
|
51
|
+
```
|
|
52
|
+
Private Connect - Temporary Tunnel
|
|
53
|
+
────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
Local: localhost:3000
|
|
56
|
+
Public: https://privateconnect.co/w/abc12345
|
|
57
|
+
Anyone can access this URL
|
|
58
|
+
Inspector: https://privateconnect.co/debug/s-xyz789
|
|
59
|
+
Live traffic monitoring & request replay
|
|
60
|
+
Expires: 120 minutes
|
|
61
|
+
|
|
62
|
+
────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
Press Ctrl+C to stop
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Sharing:**
|
|
68
|
+
- The public URL shows your actual website (like ngrok)
|
|
69
|
+
- Perfect for demos, testing, and sharing with teammates
|
|
70
|
+
- Works immediately - no landing page, just your app
|
|
71
|
+
|
|
72
|
+
**Features:**
|
|
73
|
+
- No signup or account required
|
|
74
|
+
- Auto-expires in 2 hours
|
|
75
|
+
- Real-time request logging
|
|
76
|
+
- Works with any HTTP service
|
|
77
|
+
- **Shareable URLs** - The public URL shows your actual website, perfect for demos and testing
|
|
78
|
+
|
|
79
|
+
## Need more?
|
|
80
|
+
|
|
81
|
+
For permanent tunnels, sharing with teammates, and AI agent integration:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
curl -fsSL https://privateconnect.co/install.sh | bash
|
|
85
|
+
connect up
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
→ [privateconnect.co](https://privateconnect.co)
|
package/dist/index.js
CHANGED
|
@@ -248,6 +248,9 @@ ${c.bold}Commands:${c.reset}
|
|
|
248
248
|
check <target> Test connectivity to any service
|
|
249
249
|
test <target> Alias for check
|
|
250
250
|
tunnel <port> Create a temporary public tunnel
|
|
251
|
+
list List all active tunnels
|
|
252
|
+
close <id> Close a tunnel by ID
|
|
253
|
+
close --all Close all active tunnels
|
|
251
254
|
|
|
252
255
|
${c.bold}Examples:${c.reset}
|
|
253
256
|
npx private-connect test vault.internal:8200
|
|
@@ -255,6 +258,8 @@ ${c.bold}Examples:${c.reset}
|
|
|
255
258
|
npx private-connect tunnel 3000
|
|
256
259
|
npx private-connect tunnel localhost:8080
|
|
257
260
|
npx private-connect tunnel 4096 --tcp
|
|
261
|
+
npx private-connect list
|
|
262
|
+
npx private-connect close abc123
|
|
258
263
|
|
|
259
264
|
${c.bold}Tunnel:${c.reset}
|
|
260
265
|
• No signup required
|
|
@@ -336,11 +341,41 @@ async function createTemporaryTunnel(options) {
|
|
|
336
341
|
if (HUB_URL.includes('localhost')) {
|
|
337
342
|
wsUrl = HUB_URL.replace('http', 'ws') + '/temp-tunnel';
|
|
338
343
|
}
|
|
344
|
+
// Auto-create debug session for HTTP tunnels
|
|
345
|
+
let debugSession = null;
|
|
346
|
+
if (data.tunnel.type === 'http') {
|
|
347
|
+
try {
|
|
348
|
+
const debugResponse = await httpRequest(`${HUB_URL}/v1/tunnels/temporary/${data.tunnel.tunnelId}/debug`, {
|
|
349
|
+
method: 'POST',
|
|
350
|
+
headers: { 'Content-Type': 'application/json' },
|
|
351
|
+
body: JSON.stringify({ aiEnabled: false }),
|
|
352
|
+
});
|
|
353
|
+
if (debugResponse.ok) {
|
|
354
|
+
const debugData = JSON.parse(debugResponse.body);
|
|
355
|
+
debugSession = { token: debugData.session.token, url: debugData.session.url };
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
// Silently fail - debug is optional
|
|
360
|
+
}
|
|
361
|
+
}
|
|
339
362
|
console.log();
|
|
340
363
|
console.log(`${c.gray}────────────────────────────────────${c.reset}`);
|
|
341
364
|
console.log();
|
|
342
365
|
console.log(` ${c.bold}Local:${c.reset} ${c.cyan}${host}:${port}${c.reset}`);
|
|
343
|
-
|
|
366
|
+
// Show public URL prominently
|
|
367
|
+
if (data.tunnel.subdomain) {
|
|
368
|
+
console.log(` ${c.bold}Public:${c.reset} ${c.green}${c.bold}${data.tunnel.publicUrl}${c.reset}`);
|
|
369
|
+
console.log(` ${c.gray} Anyone can access this URL${c.reset}`);
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
console.log(` ${c.bold}Public:${c.reset} ${c.green}${data.tunnel.publicUrl}${c.reset}`);
|
|
373
|
+
}
|
|
374
|
+
// Show debug inspector link
|
|
375
|
+
if (debugSession) {
|
|
376
|
+
console.log(` ${c.bold}Inspector:${c.reset} ${c.cyan}${debugSession.url}${c.reset}`);
|
|
377
|
+
console.log(` ${c.gray} Live traffic monitoring & request replay${c.reset}`);
|
|
378
|
+
}
|
|
344
379
|
if (data.tunnel.type === 'tcp' && data.tunnel.tcpHost && data.tunnel.tcpPort) {
|
|
345
380
|
console.log(` ${c.bold}Connect:${c.reset} ${c.cyan}${data.tunnel.tcpHost}:${data.tunnel.tcpPort}${c.reset}`);
|
|
346
381
|
}
|
|
@@ -440,6 +475,17 @@ async function runTunnelProxy(tunnelId, wsUrl, localHost, localPort) {
|
|
|
440
475
|
socket.disconnect();
|
|
441
476
|
resolve();
|
|
442
477
|
});
|
|
478
|
+
socket.on('server_shutdown', (data) => {
|
|
479
|
+
console.log();
|
|
480
|
+
console.log(` ${c.yellow}⚠ Server shutting down${c.reset}`);
|
|
481
|
+
if (data.message) {
|
|
482
|
+
console.log(` ${c.dim}${data.message}${c.reset}`);
|
|
483
|
+
}
|
|
484
|
+
if (data.reconnectIn) {
|
|
485
|
+
console.log(` ${c.dim}Reconnect in ${data.reconnectIn} seconds...${c.reset}`);
|
|
486
|
+
}
|
|
487
|
+
console.log();
|
|
488
|
+
});
|
|
443
489
|
socket.on('connect_error', (err) => {
|
|
444
490
|
console.log(` ${c.red}Connection error: ${err.message}${c.reset}`);
|
|
445
491
|
});
|
|
@@ -565,6 +611,22 @@ async function runTcpTunnelProxy(tunnelId, wsUrl, localHost, localPort) {
|
|
|
565
611
|
socket.disconnect();
|
|
566
612
|
resolve();
|
|
567
613
|
});
|
|
614
|
+
socket.on('server_shutdown', (data) => {
|
|
615
|
+
console.log();
|
|
616
|
+
console.log(` ${c.yellow}⚠ Server shutting down${c.reset}`);
|
|
617
|
+
if (data.message) {
|
|
618
|
+
console.log(` ${c.dim}${data.message}${c.reset}`);
|
|
619
|
+
}
|
|
620
|
+
if (data.reconnectIn) {
|
|
621
|
+
console.log(` ${c.dim}Reconnect in ${data.reconnectIn} seconds...${c.reset}`);
|
|
622
|
+
}
|
|
623
|
+
console.log();
|
|
624
|
+
// Close all TCP connections gracefully
|
|
625
|
+
for (const [, conn] of tcpConnections) {
|
|
626
|
+
conn.end();
|
|
627
|
+
}
|
|
628
|
+
tcpConnections.clear();
|
|
629
|
+
});
|
|
568
630
|
// Handle TCP dial request from hub
|
|
569
631
|
socket.on('tcp_dial', (data) => {
|
|
570
632
|
connectionCount++;
|
|
@@ -639,6 +701,167 @@ function parseTunnelTarget(target) {
|
|
|
639
701
|
// Default to localhost with provided port
|
|
640
702
|
return { host: 'localhost', port: parseInt(target, 10) || 3000 };
|
|
641
703
|
}
|
|
704
|
+
/**
|
|
705
|
+
* List all active tunnels
|
|
706
|
+
*/
|
|
707
|
+
async function listTunnels() {
|
|
708
|
+
console.log();
|
|
709
|
+
console.log(` ${c.cyan}${c.bold}Private Connect${c.reset} ${c.dim}Active Tunnels${c.reset}`);
|
|
710
|
+
console.log();
|
|
711
|
+
const hubUrl = process.env.CONNECT_HUB_URL || 'https://api.privateconnect.co';
|
|
712
|
+
try {
|
|
713
|
+
const response = await new Promise((resolve, reject) => {
|
|
714
|
+
const url = new url_1.URL(`${hubUrl}/v1/tunnels/temporary`);
|
|
715
|
+
const protocol = url.protocol === 'https:' ? https : http;
|
|
716
|
+
const req = protocol.get(url.href, (res) => {
|
|
717
|
+
let data = '';
|
|
718
|
+
res.on('data', chunk => data += chunk);
|
|
719
|
+
res.on('end', () => {
|
|
720
|
+
try {
|
|
721
|
+
resolve(JSON.parse(data));
|
|
722
|
+
}
|
|
723
|
+
catch {
|
|
724
|
+
reject(new Error('Invalid response'));
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
req.on('error', reject);
|
|
729
|
+
req.setTimeout(10000, () => {
|
|
730
|
+
req.destroy();
|
|
731
|
+
reject(new Error('Request timeout'));
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
if (response.count === 0) {
|
|
735
|
+
console.log(` ${c.dim}No active tunnels${c.reset}`);
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
console.log(` ${c.green}${response.count}${c.reset} active tunnel${response.count > 1 ? 's' : ''}`);
|
|
739
|
+
console.log();
|
|
740
|
+
for (const tunnel of response.tunnels) {
|
|
741
|
+
const status = tunnel.connected ? `${c.green}connected${c.reset}` : `${c.yellow}waiting${c.reset}`;
|
|
742
|
+
const expiresIn = Math.max(0, Math.floor((new Date(tunnel.expiresAt).getTime() - Date.now()) / 60000));
|
|
743
|
+
console.log(` ${c.cyan}${tunnel.tunnelId}${c.reset}`);
|
|
744
|
+
console.log(` Type: ${tunnel.type} Status: ${status} Requests: ${tunnel.requestCount} Expires: ${expiresIn}m`);
|
|
745
|
+
if (tunnel.subdomain) {
|
|
746
|
+
console.log(` Subdomain: ${c.dim}${tunnel.subdomain}${c.reset}`);
|
|
747
|
+
}
|
|
748
|
+
console.log();
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
catch (err) {
|
|
753
|
+
console.error(` ${fail} Failed to list tunnels: ${err.message}`);
|
|
754
|
+
}
|
|
755
|
+
console.log();
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Close a tunnel by ID
|
|
759
|
+
*/
|
|
760
|
+
async function closeTunnel(tunnelId) {
|
|
761
|
+
console.log();
|
|
762
|
+
console.log(` ${c.cyan}${c.bold}Private Connect${c.reset} ${c.dim}Close Tunnel${c.reset}`);
|
|
763
|
+
console.log();
|
|
764
|
+
const hubUrl = process.env.CONNECT_HUB_URL || 'https://api.privateconnect.co';
|
|
765
|
+
try {
|
|
766
|
+
await new Promise((resolve, reject) => {
|
|
767
|
+
const url = new url_1.URL(`${hubUrl}/v1/tunnels/temporary/${tunnelId}`);
|
|
768
|
+
const protocol = url.protocol === 'https:' ? https : http;
|
|
769
|
+
const req = protocol.request(url.href, { method: 'DELETE' }, (res) => {
|
|
770
|
+
let data = '';
|
|
771
|
+
res.on('data', chunk => data += chunk);
|
|
772
|
+
res.on('end', () => {
|
|
773
|
+
if (res.statusCode === 200) {
|
|
774
|
+
resolve();
|
|
775
|
+
}
|
|
776
|
+
else if (res.statusCode === 404) {
|
|
777
|
+
reject(new Error('Tunnel not found or already expired'));
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
reject(new Error(`Server error: ${res.statusCode}`));
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
req.on('error', reject);
|
|
785
|
+
req.setTimeout(10000, () => {
|
|
786
|
+
req.destroy();
|
|
787
|
+
reject(new Error('Request timeout'));
|
|
788
|
+
});
|
|
789
|
+
req.end();
|
|
790
|
+
});
|
|
791
|
+
console.log(` ${ok} Tunnel ${c.cyan}${tunnelId}${c.reset} closed`);
|
|
792
|
+
}
|
|
793
|
+
catch (err) {
|
|
794
|
+
console.error(` ${fail} ${err.message}`);
|
|
795
|
+
}
|
|
796
|
+
console.log();
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Close all active tunnels
|
|
800
|
+
*/
|
|
801
|
+
async function closeAllTunnels() {
|
|
802
|
+
console.log();
|
|
803
|
+
console.log(` ${c.cyan}${c.bold}Private Connect${c.reset} ${c.dim}Close All Tunnels${c.reset}`);
|
|
804
|
+
console.log();
|
|
805
|
+
const hubUrl = process.env.CONNECT_HUB_URL || 'https://api.privateconnect.co';
|
|
806
|
+
try {
|
|
807
|
+
// First list all tunnels
|
|
808
|
+
const response = await new Promise((resolve, reject) => {
|
|
809
|
+
const url = new url_1.URL(`${hubUrl}/v1/tunnels/temporary`);
|
|
810
|
+
const protocol = url.protocol === 'https:' ? https : http;
|
|
811
|
+
const req = protocol.get(url.href, (res) => {
|
|
812
|
+
let data = '';
|
|
813
|
+
res.on('data', chunk => data += chunk);
|
|
814
|
+
res.on('end', () => {
|
|
815
|
+
try {
|
|
816
|
+
resolve(JSON.parse(data));
|
|
817
|
+
}
|
|
818
|
+
catch {
|
|
819
|
+
reject(new Error('Invalid response'));
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
req.on('error', reject);
|
|
824
|
+
});
|
|
825
|
+
if (response.count === 0) {
|
|
826
|
+
console.log(` ${c.dim}No active tunnels to close${c.reset}`);
|
|
827
|
+
}
|
|
828
|
+
else {
|
|
829
|
+
let closed = 0;
|
|
830
|
+
for (const tunnel of response.tunnels) {
|
|
831
|
+
try {
|
|
832
|
+
await new Promise((resolve, reject) => {
|
|
833
|
+
const url = new url_1.URL(`${hubUrl}/v1/tunnels/temporary/${tunnel.tunnelId}`);
|
|
834
|
+
const protocol = url.protocol === 'https:' ? https : http;
|
|
835
|
+
const req = protocol.request(url.href, { method: 'DELETE' }, (res) => {
|
|
836
|
+
res.on('data', () => { });
|
|
837
|
+
res.on('end', () => {
|
|
838
|
+
if (res.statusCode === 200) {
|
|
839
|
+
resolve();
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
reject(new Error(`Failed: ${res.statusCode}`));
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
req.on('error', reject);
|
|
847
|
+
req.end();
|
|
848
|
+
});
|
|
849
|
+
console.log(` ${ok} Closed ${c.cyan}${tunnel.tunnelId}${c.reset}`);
|
|
850
|
+
closed++;
|
|
851
|
+
}
|
|
852
|
+
catch (err) {
|
|
853
|
+
console.log(` ${fail} Failed to close ${tunnel.tunnelId}: ${err.message}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
console.log();
|
|
857
|
+
console.log(` ${c.green}Closed ${closed}/${response.count} tunnels${c.reset}`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
catch (err) {
|
|
861
|
+
console.error(` ${fail} Failed to close tunnels: ${err.message}`);
|
|
862
|
+
}
|
|
863
|
+
console.log();
|
|
864
|
+
}
|
|
642
865
|
// Main
|
|
643
866
|
const args = process.argv.slice(2);
|
|
644
867
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
@@ -667,6 +890,23 @@ else if (args[0] === 'tunnel') {
|
|
|
667
890
|
const tcp = args.includes('--tcp') || args.includes('-t');
|
|
668
891
|
createTemporaryTunnel({ host, port, tcp }).catch(console.error);
|
|
669
892
|
}
|
|
893
|
+
else if (args[0] === 'list' || args[0] === 'ls') {
|
|
894
|
+
listTunnels().catch(console.error);
|
|
895
|
+
}
|
|
896
|
+
else if (args[0] === 'close' || args[0] === 'kill') {
|
|
897
|
+
if (!args[1]) {
|
|
898
|
+
console.error(`${c.red}Error: Tunnel ID required${c.reset}`);
|
|
899
|
+
console.error(`Usage: npx private-connect close <tunnelId>`);
|
|
900
|
+
console.error(` npx private-connect close --all`);
|
|
901
|
+
process.exit(1);
|
|
902
|
+
}
|
|
903
|
+
if (args[1] === '--all' || args[1] === '-a') {
|
|
904
|
+
closeAllTunnels().catch(console.error);
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
closeTunnel(args[1]).catch(console.error);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
670
910
|
else {
|
|
671
911
|
// Default to test if just a target is provided
|
|
672
912
|
runTest(args[0]).catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "private-connect",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.5",
|
|
4
|
+
"description": "Access private services by name from anywhere. No VPN setup, no firewall rules. Open source alternative to ngrok and Tailscale for service-level connectivity.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"private-connect": "./dist/index.js"
|
|
7
7
|
},
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "tsc",
|
|
14
15
|
"dev": "tsc -w"
|
|
15
16
|
},
|
|
16
17
|
"keywords": [
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"vpn-alternative",
|
|
19
|
+
"ngrok-alternative",
|
|
20
|
+
"tailscale-alternative",
|
|
21
|
+
"secure-tunneling",
|
|
22
|
+
"private-services",
|
|
23
|
+
"service-connectivity",
|
|
24
|
+
"zero-trust",
|
|
25
|
+
"networking",
|
|
26
|
+
"developer-tools",
|
|
27
|
+
"cli-tool",
|
|
20
28
|
"tunnel",
|
|
29
|
+
"connectivity",
|
|
30
|
+
"ssh-tunnel-alternative",
|
|
31
|
+
"firewall-bypass",
|
|
32
|
+
"service-mesh",
|
|
21
33
|
"private-connect"
|
|
22
34
|
],
|
|
23
35
|
"license": "FSL-1.1-MIT",
|