opentunnel-cli 1.0.9 → 1.0.11
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 +60 -0
- package/dist/cli/index.js +57 -44
- package/dist/cli/index.js.map +1 -1
- package/dist/client/TunnelClient.d.ts +2 -1
- package/dist/client/TunnelClient.d.ts.map +1 -1
- package/dist/client/TunnelClient.js +23 -9
- package/dist/client/TunnelClient.js.map +1 -1
- package/dist/server/TunnelServer.d.ts +3 -0
- package/dist/server/TunnelServer.d.ts.map +1 -1
- package/dist/server/TunnelServer.js +64 -1
- package/dist/server/TunnelServer.js.map +1 -1
- package/dist/shared/types.d.ts +7 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/shared/utils.js +6 -2
- package/dist/shared/utils.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- [As a Client](#-as-a-client) - Expose your local ports
|
|
10
10
|
- [As a Server](#-as-a-server) - Host your own tunnel server
|
|
11
11
|
- [Authentication](#-authentication) - Secure your server
|
|
12
|
+
- [IP Access Control](#-ip-access-control) - Allow/deny IPs and CIDR ranges
|
|
12
13
|
- [Configuration File](#-configuration-file) - opentunnel.yml reference
|
|
13
14
|
- [Environment Variables](#environment-variables) - Docker-style ${VAR:-default} syntax
|
|
14
15
|
- [Commands Reference](#-commands-reference)
|
|
@@ -232,6 +233,11 @@ SSL/TLS:
|
|
|
232
233
|
--email <email> Email for Let's Encrypt
|
|
233
234
|
--production Use Let's Encrypt production (not staging)
|
|
234
235
|
--cloudflare-token <token> Cloudflare API token for DNS-01 challenge
|
|
236
|
+
|
|
237
|
+
IP Access Control:
|
|
238
|
+
--ip-mode <mode> Access mode: all, allowlist, denylist (default: all)
|
|
239
|
+
--ip-allow <ips> Comma-separated IPs/CIDRs to allow
|
|
240
|
+
--ip-deny <ips> Comma-separated IPs/CIDRs to deny
|
|
235
241
|
```
|
|
236
242
|
|
|
237
243
|
## Server Modes
|
|
@@ -311,6 +317,60 @@ server:
|
|
|
311
317
|
|
|
312
318
|
---
|
|
313
319
|
|
|
320
|
+
# 🛡️ IP Access Control
|
|
321
|
+
|
|
322
|
+
Control which IP addresses can connect to your server. By default, all IPs are allowed.
|
|
323
|
+
|
|
324
|
+
## Access Modes
|
|
325
|
+
|
|
326
|
+
| Mode | Description |
|
|
327
|
+
|------|-------------|
|
|
328
|
+
| `all` | Allow all IPs (default) |
|
|
329
|
+
| `allowlist` | Only allow IPs in the allow list |
|
|
330
|
+
| `denylist` | Deny IPs in the deny list, allow others |
|
|
331
|
+
|
|
332
|
+
## Command Line
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
# Only allow specific IPs/ranges
|
|
336
|
+
opentunnel server -d --domain example.com --ip-mode allowlist --ip-allow "192.168.1.0/24,10.0.0.1"
|
|
337
|
+
|
|
338
|
+
# Deny specific IPs
|
|
339
|
+
opentunnel server -d --domain example.com --ip-mode denylist --ip-deny "1.2.3.4,5.6.7.0/24"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Configuration File
|
|
343
|
+
|
|
344
|
+
```yaml
|
|
345
|
+
server:
|
|
346
|
+
domain: example.com
|
|
347
|
+
token: ${AUTH_TOKEN}
|
|
348
|
+
ipAccess:
|
|
349
|
+
mode: allowlist # all, allowlist, or denylist
|
|
350
|
+
allowList:
|
|
351
|
+
- 192.168.1.0/24 # Allow entire subnet
|
|
352
|
+
- 10.0.0.1 # Allow single IP
|
|
353
|
+
- 172.16.0.0/16 # Allow another range
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
```yaml
|
|
357
|
+
server:
|
|
358
|
+
domain: example.com
|
|
359
|
+
ipAccess:
|
|
360
|
+
mode: denylist
|
|
361
|
+
denyList:
|
|
362
|
+
- 1.2.3.4 # Block single IP
|
|
363
|
+
- 5.6.7.0/24 # Block entire subnet
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Supported Formats
|
|
367
|
+
|
|
368
|
+
- Single IP: `192.168.1.1`
|
|
369
|
+
- CIDR notation: `192.168.1.0/24` (256 addresses)
|
|
370
|
+
- IPv6: `::1`, `2001:db8::/32`
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
314
374
|
# 📄 Configuration File
|
|
315
375
|
|
|
316
376
|
Create `opentunnel.yml` in your project directory.
|
package/dist/cli/index.js
CHANGED
|
@@ -67,13 +67,13 @@ function loadEnvFile() {
|
|
|
67
67
|
value = value.slice(1, -1);
|
|
68
68
|
}
|
|
69
69
|
// Only set if not already defined in environment
|
|
70
|
-
if (process.env[key] === undefined)
|
|
70
|
+
if (process.env[key] === undefined)
|
|
71
71
|
process.env[key] = value;
|
|
72
|
-
}
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
}
|
|
76
|
+
;
|
|
77
77
|
// Docker-style environment variable substitution
|
|
78
78
|
// Supports: ${VAR}, ${VAR:-default}, ${VAR:=default}
|
|
79
79
|
function substituteEnvVars(content) {
|
|
@@ -81,19 +81,17 @@ function substituteEnvVars(content) {
|
|
|
81
81
|
const pattern = /\$\{([^}:]+)(?:(:[-=])([^}]*))?\}/g;
|
|
82
82
|
return content.replace(pattern, (match, varName, operator, defaultValue) => {
|
|
83
83
|
const envValue = process.env[varName];
|
|
84
|
-
if (operator === ":-" || operator === ":=")
|
|
85
|
-
// Use default if variable is unset or empty
|
|
84
|
+
if (operator === ":-" || operator === ":=")
|
|
86
85
|
return (envValue !== undefined && envValue !== "") ? envValue : (defaultValue || "");
|
|
87
|
-
}
|
|
88
86
|
// Just ${VAR} - return value or empty string
|
|
89
87
|
return envValue || "";
|
|
90
88
|
});
|
|
91
89
|
}
|
|
90
|
+
;
|
|
92
91
|
// Load and parse config file with environment variable substitution
|
|
93
92
|
function loadConfig(configPath) {
|
|
94
|
-
if (!fs.existsSync(configPath))
|
|
93
|
+
if (!fs.existsSync(configPath))
|
|
95
94
|
return null;
|
|
96
|
-
}
|
|
97
95
|
// Load .env file first
|
|
98
96
|
loadEnvFile();
|
|
99
97
|
// Read and substitute environment variables
|
|
@@ -101,12 +99,13 @@ function loadConfig(configPath) {
|
|
|
101
99
|
content = substituteEnvVars(content);
|
|
102
100
|
return (0, yaml_1.parse)(content);
|
|
103
101
|
}
|
|
102
|
+
;
|
|
104
103
|
const program = new commander_1.Command();
|
|
105
104
|
program
|
|
106
105
|
.name("opentunnel")
|
|
107
106
|
.alias("ot")
|
|
108
107
|
.description("Expose local ports to the internet via custom domains or ngrok")
|
|
109
|
-
.version("1.0.
|
|
108
|
+
.version("1.0.11");
|
|
110
109
|
// Helper function to build WebSocket URL from domain
|
|
111
110
|
// User only provides base domain (e.g., fjrg2007.com), system handles the rest
|
|
112
111
|
// Note: --insecure flag only affects certificate verification, not the protocol
|
|
@@ -166,7 +165,7 @@ program
|
|
|
166
165
|
token: options.token,
|
|
167
166
|
reconnect: true,
|
|
168
167
|
silent: true,
|
|
169
|
-
rejectUnauthorized: !insecure
|
|
168
|
+
rejectUnauthorized: !insecure
|
|
170
169
|
});
|
|
171
170
|
await client.connect();
|
|
172
171
|
spinner.text = "Creating tunnel...";
|
|
@@ -200,18 +199,16 @@ program
|
|
|
200
199
|
publicUrl = result.publicUrl;
|
|
201
200
|
usedInsecure = true;
|
|
202
201
|
}
|
|
203
|
-
else
|
|
202
|
+
else
|
|
204
203
|
throw firstErr;
|
|
205
|
-
}
|
|
206
204
|
}
|
|
207
205
|
spinner.succeed("Tunnel established!");
|
|
208
206
|
console.log("");
|
|
209
207
|
console.log(chalk_1.default.cyan(` OpenTunnel ${chalk_1.default.gray(`(via ${serverDisplayName})`)}`));
|
|
210
208
|
console.log(chalk_1.default.gray(" ─────────────────────────────────────────"));
|
|
211
209
|
console.log(` ${chalk_1.default.white("Status:")} ${chalk_1.default.green("● Online")}`);
|
|
212
|
-
if (usedInsecure && !options.insecure)
|
|
210
|
+
if (usedInsecure && !options.insecure)
|
|
213
211
|
console.log(` ${chalk_1.default.white("Security:")} ${chalk_1.default.yellow("⚠ Insecure (self-signed cert)")}`);
|
|
214
|
-
}
|
|
215
212
|
console.log(` ${chalk_1.default.white("Protocol:")} ${chalk_1.default.yellow(options.protocol.toUpperCase())}`);
|
|
216
213
|
console.log(` ${chalk_1.default.white("Local:")} ${chalk_1.default.gray(`${options.host}:${port}`)}`);
|
|
217
214
|
console.log(` ${chalk_1.default.white("Public:")} ${chalk_1.default.green(publicUrl)}`);
|
|
@@ -335,7 +332,7 @@ program
|
|
|
335
332
|
subdomain,
|
|
336
333
|
serverUrl,
|
|
337
334
|
token: options.token,
|
|
338
|
-
insecure: true
|
|
335
|
+
insecure: true
|
|
339
336
|
});
|
|
340
337
|
}
|
|
341
338
|
catch (error) {
|
|
@@ -365,7 +362,7 @@ program
|
|
|
365
362
|
localPort: parseInt(port),
|
|
366
363
|
remotePort: options.remotePort ? parseInt(options.remotePort) : undefined,
|
|
367
364
|
authtoken: options.token,
|
|
368
|
-
region: options.region
|
|
365
|
+
region: options.region
|
|
369
366
|
});
|
|
370
367
|
return;
|
|
371
368
|
}
|
|
@@ -379,7 +376,7 @@ program
|
|
|
379
376
|
remotePort: options.remotePort ? parseInt(options.remotePort) : undefined,
|
|
380
377
|
serverUrl,
|
|
381
378
|
token: options.token,
|
|
382
|
-
insecure: options.insecure
|
|
379
|
+
insecure: options.insecure
|
|
383
380
|
});
|
|
384
381
|
return;
|
|
385
382
|
}
|
|
@@ -402,7 +399,7 @@ program
|
|
|
402
399
|
domain,
|
|
403
400
|
basePath: "op",
|
|
404
401
|
tunnelPortRange: { min: 10000, max: 20000 },
|
|
405
|
-
selfSignedHttps: { enabled: true }
|
|
402
|
+
selfSignedHttps: { enabled: true }
|
|
406
403
|
});
|
|
407
404
|
try {
|
|
408
405
|
await server.start();
|
|
@@ -417,7 +414,7 @@ program
|
|
|
417
414
|
remotePort: options.remotePort ? parseInt(options.remotePort) : undefined,
|
|
418
415
|
serverUrl,
|
|
419
416
|
token: options.token,
|
|
420
|
-
insecure: true
|
|
417
|
+
insecure: true
|
|
421
418
|
});
|
|
422
419
|
}
|
|
423
420
|
catch (error) {
|
|
@@ -451,7 +448,7 @@ program
|
|
|
451
448
|
localHost: "localhost",
|
|
452
449
|
localPort: parseInt(port),
|
|
453
450
|
subdomain: options.subdomain,
|
|
454
|
-
authtoken: options.token
|
|
451
|
+
authtoken: options.token
|
|
455
452
|
});
|
|
456
453
|
}
|
|
457
454
|
else {
|
|
@@ -462,7 +459,7 @@ program
|
|
|
462
459
|
subdomain: options.subdomain,
|
|
463
460
|
serverUrl,
|
|
464
461
|
token: options.token,
|
|
465
|
-
insecure: options.insecure
|
|
462
|
+
insecure: options.insecure
|
|
466
463
|
});
|
|
467
464
|
}
|
|
468
465
|
});
|
|
@@ -486,6 +483,9 @@ program
|
|
|
486
483
|
.option("--production", "Use Let's Encrypt production (default: staging)")
|
|
487
484
|
.option("--cloudflare-token <token>", "Cloudflare API token for DNS-01 challenge")
|
|
488
485
|
.option("--duckdns-token <token>", "DuckDNS token for dynamic DNS updates")
|
|
486
|
+
.option("--ip-mode <mode>", "IP access mode: all, allowlist, denylist (default: all)")
|
|
487
|
+
.option("--ip-allow <ips>", "Comma-separated IPs/CIDRs to allow (e.g., 192.168.1.0/24,10.0.0.1)")
|
|
488
|
+
.option("--ip-deny <ips>", "Comma-separated IPs/CIDRs to deny")
|
|
489
489
|
.option("-d, --detach", "Run server in background (detached mode)")
|
|
490
490
|
.action(async (options) => {
|
|
491
491
|
// Load config from opentunnel.yml if exists (with env variable substitution)
|
|
@@ -493,9 +493,8 @@ program
|
|
|
493
493
|
let fileConfig = {};
|
|
494
494
|
try {
|
|
495
495
|
const parsed = loadConfig(configPath);
|
|
496
|
-
if (parsed?.server && parsed.server.domain)
|
|
496
|
+
if (parsed?.server && parsed.server.domain)
|
|
497
497
|
fileConfig = parsed.server;
|
|
498
|
-
}
|
|
499
498
|
}
|
|
500
499
|
catch (err) {
|
|
501
500
|
// Ignore parse errors, use CLI options
|
|
@@ -518,6 +517,9 @@ program
|
|
|
518
517
|
production: options.production,
|
|
519
518
|
cloudflareToken: options.cloudflareToken,
|
|
520
519
|
duckdnsToken: options.duckdnsToken,
|
|
520
|
+
ipMode: options.ipMode || fileConfig.ipAccess?.mode || "all",
|
|
521
|
+
ipAllow: options.ipAllow || fileConfig.ipAccess?.allowList?.join(","),
|
|
522
|
+
ipDeny: options.ipDeny || fileConfig.ipAccess?.denyList?.join(","),
|
|
521
523
|
detach: options.detach,
|
|
522
524
|
};
|
|
523
525
|
// Detached mode - run in background
|
|
@@ -562,6 +564,12 @@ program
|
|
|
562
564
|
args.push("--cloudflare-token", mergedOptions.cloudflareToken);
|
|
563
565
|
if (mergedOptions.duckdnsToken)
|
|
564
566
|
args.push("--duckdns-token", mergedOptions.duckdnsToken);
|
|
567
|
+
if (mergedOptions.ipMode && mergedOptions.ipMode !== "all")
|
|
568
|
+
args.push("--ip-mode", mergedOptions.ipMode);
|
|
569
|
+
if (mergedOptions.ipAllow)
|
|
570
|
+
args.push("--ip-allow", mergedOptions.ipAllow);
|
|
571
|
+
if (mergedOptions.ipDeny)
|
|
572
|
+
args.push("--ip-deny", mergedOptions.ipDeny);
|
|
565
573
|
const out = fsAsync.openSync(logFile, "a");
|
|
566
574
|
const err = fsAsync.openSync(logFile, "a");
|
|
567
575
|
const child = spawn(process.execPath, [process.argv[1], ...args], {
|
|
@@ -611,6 +619,12 @@ program
|
|
|
611
619
|
enabled: mergedOptions.https !== false,
|
|
612
620
|
};
|
|
613
621
|
}
|
|
622
|
+
// Build IP access config
|
|
623
|
+
const ipAccessConfig = mergedOptions.ipMode !== "all" ? {
|
|
624
|
+
mode: mergedOptions.ipMode,
|
|
625
|
+
allowList: mergedOptions.ipAllow ? mergedOptions.ipAllow.split(",").map((ip) => ip.trim()) : undefined,
|
|
626
|
+
denyList: mergedOptions.ipDeny ? mergedOptions.ipDeny.split(",").map((ip) => ip.trim()) : undefined,
|
|
627
|
+
} : undefined;
|
|
614
628
|
const server = new TunnelServer({
|
|
615
629
|
port: parseInt(mergedOptions.port),
|
|
616
630
|
publicPort: mergedOptions.publicPort ? parseInt(mergedOptions.publicPort) : undefined,
|
|
@@ -624,10 +638,11 @@ program
|
|
|
624
638
|
auth: mergedOptions.authTokens
|
|
625
639
|
? { required: true, tokens: mergedOptions.authTokens.split(",") }
|
|
626
640
|
: undefined,
|
|
641
|
+
ipAccess: ipAccessConfig,
|
|
627
642
|
https: httpsConfig,
|
|
628
643
|
selfSignedHttps: selfSignedHttpsConfig,
|
|
629
644
|
autoHttps: autoHttpsConfig,
|
|
630
|
-
autoDns: detectDnsConfig(mergedOptions)
|
|
645
|
+
autoDns: detectDnsConfig(mergedOptions)
|
|
631
646
|
});
|
|
632
647
|
// Helper function to auto-detect DNS provider
|
|
633
648
|
function detectDnsConfig(opts) {
|
|
@@ -642,7 +657,7 @@ program
|
|
|
642
657
|
cloudflareToken: opts.cloudflareToken,
|
|
643
658
|
createRecords: false,
|
|
644
659
|
deleteOnClose: false,
|
|
645
|
-
setupWildcard: true
|
|
660
|
+
setupWildcard: true
|
|
646
661
|
};
|
|
647
662
|
}
|
|
648
663
|
if (opts.duckdnsToken || isDuckDnsDomain) {
|
|
@@ -652,7 +667,7 @@ program
|
|
|
652
667
|
duckdnsToken: opts.duckdnsToken,
|
|
653
668
|
createRecords: false,
|
|
654
669
|
deleteOnClose: false,
|
|
655
|
-
setupWildcard: false
|
|
670
|
+
setupWildcard: false
|
|
656
671
|
};
|
|
657
672
|
}
|
|
658
673
|
return undefined;
|
|
@@ -700,9 +715,8 @@ program
|
|
|
700
715
|
fs.unlinkSync(pidFile);
|
|
701
716
|
console.log(chalk_1.default.yellow(`Server was not running (stale PID file removed)`));
|
|
702
717
|
}
|
|
703
|
-
else
|
|
718
|
+
else
|
|
704
719
|
console.log(chalk_1.default.red(`Failed to stop server: ${err.message}`));
|
|
705
|
-
}
|
|
706
720
|
}
|
|
707
721
|
});
|
|
708
722
|
// Logs command
|
|
@@ -732,9 +746,8 @@ program
|
|
|
732
746
|
console.log(fs.readFileSync(logFile, "utf-8"));
|
|
733
747
|
});
|
|
734
748
|
}
|
|
735
|
-
else
|
|
749
|
+
else
|
|
736
750
|
spawn("tail", ["-f", "-n", options.lines, logFile], { stdio: "inherit" });
|
|
737
|
-
}
|
|
738
751
|
}
|
|
739
752
|
else {
|
|
740
753
|
const content = fs.readFileSync(logFile, "utf-8");
|
|
@@ -1194,9 +1207,8 @@ program
|
|
|
1194
1207
|
.filter(f => f.startsWith(".opentunnel-") && f.endsWith(".pid"));
|
|
1195
1208
|
// Also include the server PID
|
|
1196
1209
|
const serverPidFile = ".opentunnel.pid";
|
|
1197
|
-
if (fs.existsSync(path.join(process.cwd(), serverPidFile)))
|
|
1210
|
+
if (fs.existsSync(path.join(process.cwd(), serverPidFile)))
|
|
1198
1211
|
pidFiles.push(serverPidFile);
|
|
1199
|
-
}
|
|
1200
1212
|
if (pidFiles.length === 0) {
|
|
1201
1213
|
console.log(chalk_1.default.yellow("No tunnels running"));
|
|
1202
1214
|
return;
|
|
@@ -1216,17 +1228,15 @@ program
|
|
|
1216
1228
|
fs.unlinkSync(pidPath);
|
|
1217
1229
|
console.log(chalk_1.default.yellow(` - ${name} was not running (cleaned up)`));
|
|
1218
1230
|
}
|
|
1219
|
-
else
|
|
1231
|
+
else
|
|
1220
1232
|
console.log(chalk_1.default.red(` ✗ Failed to stop ${name}: ${err.message}`));
|
|
1221
|
-
}
|
|
1222
1233
|
}
|
|
1223
1234
|
}
|
|
1224
1235
|
// Clean up log files
|
|
1225
1236
|
const logFiles = fs.readdirSync(process.cwd())
|
|
1226
1237
|
.filter(f => f.startsWith("opentunnel") && f.endsWith(".log"));
|
|
1227
|
-
if (logFiles.length > 0)
|
|
1238
|
+
if (logFiles.length > 0)
|
|
1228
1239
|
console.log(chalk_1.default.gray(`\nLog files preserved: ${logFiles.join(", ")}`));
|
|
1229
|
-
}
|
|
1230
1240
|
console.log(chalk_1.default.green("\nAll tunnels stopped"));
|
|
1231
1241
|
});
|
|
1232
1242
|
// PS command - list running tunnel processes
|
|
@@ -1236,8 +1246,7 @@ program
|
|
|
1236
1246
|
.action(async () => {
|
|
1237
1247
|
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
1238
1248
|
const path = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1239
|
-
const pidFiles = fs.readdirSync(process.cwd())
|
|
1240
|
-
.filter(f => f.startsWith(".opentunnel") && f.endsWith(".pid"));
|
|
1249
|
+
const pidFiles = fs.readdirSync(process.cwd()).filter(f => f.startsWith(".opentunnel") && f.endsWith(".pid"));
|
|
1241
1250
|
if (pidFiles.length === 0) {
|
|
1242
1251
|
console.log(chalk_1.default.yellow("No tunnels running"));
|
|
1243
1252
|
console.log(chalk_1.default.gray("Start tunnels with: opentunnel up -d"));
|
|
@@ -1327,13 +1336,13 @@ program
|
|
|
1327
1336
|
method,
|
|
1328
1337
|
url,
|
|
1329
1338
|
headers: req.headers,
|
|
1330
|
-
body: body || undefined
|
|
1339
|
+
body: body || undefined
|
|
1331
1340
|
},
|
|
1332
1341
|
server: {
|
|
1333
1342
|
port,
|
|
1334
1343
|
timestamp,
|
|
1335
|
-
uptime: process.uptime()
|
|
1336
|
-
}
|
|
1344
|
+
uptime: process.uptime()
|
|
1345
|
+
}
|
|
1337
1346
|
};
|
|
1338
1347
|
res.writeHead(200, {
|
|
1339
1348
|
"Content-Type": "application/json",
|
|
@@ -1377,10 +1386,8 @@ program
|
|
|
1377
1386
|
const specific = `.test-server-${options.port}.pid`;
|
|
1378
1387
|
pidFiles = fs.existsSync(path.join(process.cwd(), specific)) ? [specific] : [];
|
|
1379
1388
|
}
|
|
1380
|
-
else
|
|
1381
|
-
pidFiles = fs.readdirSync(process.cwd())
|
|
1382
|
-
.filter(f => f.startsWith(".test-server-") && f.endsWith(".pid"));
|
|
1383
|
-
}
|
|
1389
|
+
else
|
|
1390
|
+
pidFiles = fs.readdirSync(process.cwd()).filter(f => f.startsWith(".test-server-") && f.endsWith(".pid"));
|
|
1384
1391
|
if (pidFiles.length === 0) {
|
|
1385
1392
|
console.log(chalk_1.default.yellow("No test servers running"));
|
|
1386
1393
|
return;
|
|
@@ -1443,6 +1450,7 @@ async function runTunnelInBackgroundFromConfig(name, protocol, port, options) {
|
|
|
1443
1450
|
fs.writeFileSync(pidFile, String(child.pid));
|
|
1444
1451
|
console.log(chalk_1.default.green(` ✓ ${name}: started (PID: ${child.pid})`));
|
|
1445
1452
|
}
|
|
1453
|
+
;
|
|
1446
1454
|
async function startTunnelsFromConfig(tunnels, serverUrl, token, insecure) {
|
|
1447
1455
|
const spinner = (0, ora_1.default)("Connecting to server...").start();
|
|
1448
1456
|
const client = new TunnelClient_1.TunnelClient({
|
|
@@ -1517,6 +1525,7 @@ async function startTunnelsFromConfig(tunnels, serverUrl, token, insecure) {
|
|
|
1517
1525
|
process.exit(1);
|
|
1518
1526
|
}
|
|
1519
1527
|
}
|
|
1528
|
+
;
|
|
1520
1529
|
async function runTunnelInBackground(command, port, options) {
|
|
1521
1530
|
const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
|
|
1522
1531
|
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
@@ -1594,6 +1603,7 @@ async function runTunnelInBackground(command, port, options) {
|
|
|
1594
1603
|
console.log(chalk_1.default.gray(`Stop with: kill ${child.pid}`));
|
|
1595
1604
|
console.log(chalk_1.default.gray(`Check: tail -f ${logFile}`));
|
|
1596
1605
|
}
|
|
1606
|
+
;
|
|
1597
1607
|
async function createTunnel(options) {
|
|
1598
1608
|
const spinner = (0, ora_1.default)("Connecting to server...").start();
|
|
1599
1609
|
const client = new TunnelClient_1.TunnelClient({
|
|
@@ -1653,6 +1663,7 @@ async function createTunnel(options) {
|
|
|
1653
1663
|
process.exit(1);
|
|
1654
1664
|
}
|
|
1655
1665
|
}
|
|
1666
|
+
;
|
|
1656
1667
|
async function createNgrokTunnel(options) {
|
|
1657
1668
|
const spinner = (0, ora_1.default)("Starting ngrok...").start();
|
|
1658
1669
|
const client = new NgrokClient_1.NgrokClient({
|
|
@@ -1703,6 +1714,7 @@ async function createNgrokTunnel(options) {
|
|
|
1703
1714
|
process.exit(1);
|
|
1704
1715
|
}
|
|
1705
1716
|
}
|
|
1717
|
+
;
|
|
1706
1718
|
function printTunnelInfo(info) {
|
|
1707
1719
|
console.log("");
|
|
1708
1720
|
console.log(chalk_1.default.cyan(` OpenTunnel ${chalk_1.default.gray(`(via ${info.provider})`)}`));
|
|
@@ -1716,5 +1728,6 @@ function printTunnelInfo(info) {
|
|
|
1716
1728
|
console.log(chalk_1.default.gray(" Press Ctrl+C to close the tunnel"));
|
|
1717
1729
|
console.log("");
|
|
1718
1730
|
}
|
|
1731
|
+
;
|
|
1719
1732
|
program.parse();
|
|
1720
1733
|
//# sourceMappingURL=index.js.map
|