opentunnel-cli 1.0.19 → 1.0.20
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 +41 -0
- package/dist/cli/index.js +204 -65
- package/dist/cli/index.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 +186 -51
- package/dist/server/TunnelServer.js.map +1 -1
- package/dist/shared/types.d.ts +1 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -421,6 +421,47 @@ server:
|
|
|
421
421
|
basePath: op
|
|
422
422
|
```
|
|
423
423
|
|
|
424
|
+
## Domains Without Wildcard Support (DuckDNS)
|
|
425
|
+
|
|
426
|
+
Some DNS providers like **DuckDNS** don't support wildcard subdomains (`*.domain`). OpenTunnel automatically detects DuckDNS domains and uses **port-based routing** instead of subdomains.
|
|
427
|
+
|
|
428
|
+
**Auto-detection:** Domains ending in `.duckdns.org` automatically use port-based mode.
|
|
429
|
+
|
|
430
|
+
**Important:** DuckDNS domains cannot use `basePath` - it will throw an error:
|
|
431
|
+
|
|
432
|
+
```yaml
|
|
433
|
+
# ❌ WRONG - Will throw an error
|
|
434
|
+
server:
|
|
435
|
+
domains:
|
|
436
|
+
- domain: myapp.duckdns.org
|
|
437
|
+
basePath: op # Error! DuckDNS doesn't support subdomains
|
|
438
|
+
|
|
439
|
+
# ✅ CORRECT
|
|
440
|
+
server:
|
|
441
|
+
domains:
|
|
442
|
+
- domain: fjrg2007.com
|
|
443
|
+
basePath: op # Subdomain-based: *.op.fjrg2007.com
|
|
444
|
+
- domain: myapp.duckdns.org
|
|
445
|
+
# Port-based: myapp.duckdns.org:<port>
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Manual configuration:** Use `wildcard: false` for other domains without wildcard support:
|
|
449
|
+
|
|
450
|
+
```yaml
|
|
451
|
+
server:
|
|
452
|
+
domains:
|
|
453
|
+
- domain: fjrg2007.com
|
|
454
|
+
basePath: op # Subdomain-based: *.op.fjrg2007.com
|
|
455
|
+
- domain: other-no-wildcard.com
|
|
456
|
+
wildcard: false # Manual: port-based
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**How it works:**
|
|
460
|
+
- **Wildcard domains:** `https://myapp.op.fjrg2007.com`
|
|
461
|
+
- **Non-wildcard domains:** `https://myapp.duckdns.org:10001`
|
|
462
|
+
|
|
463
|
+
Clients connecting to non-wildcard domains receive port-based URLs automatically.
|
|
464
|
+
|
|
424
465
|
## SSL Certificates
|
|
425
466
|
|
|
426
467
|
When using self-signed certificates with multiple domains, OpenTunnel automatically generates a **SAN (Subject Alternative Name) certificate** that covers all configured domains and their wildcards.
|
package/dist/cli/index.js
CHANGED
|
@@ -53,6 +53,28 @@ function getRegistryPath() {
|
|
|
53
53
|
const registryDir = path.join(os.homedir(), ".opentunnel");
|
|
54
54
|
return path.join(registryDir, "registry.json");
|
|
55
55
|
}
|
|
56
|
+
function getLogsDir() {
|
|
57
|
+
const os = require("os");
|
|
58
|
+
const path = require("path");
|
|
59
|
+
const fs = require("fs");
|
|
60
|
+
const logsDir = path.join(os.homedir(), ".opentunnel", "logs");
|
|
61
|
+
// Ensure directory exists
|
|
62
|
+
if (!fs.existsSync(logsDir)) {
|
|
63
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
return logsDir;
|
|
66
|
+
}
|
|
67
|
+
function getLogFilePath(instanceName) {
|
|
68
|
+
const path = require("path");
|
|
69
|
+
// Sanitize instance name for filename
|
|
70
|
+
const safeName = instanceName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
71
|
+
return path.join(getLogsDir(), `${safeName}.log`);
|
|
72
|
+
}
|
|
73
|
+
function getPidFilePath(instanceName) {
|
|
74
|
+
const path = require("path");
|
|
75
|
+
const safeName = instanceName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
76
|
+
return path.join(getLogsDir(), `${safeName}.pid`);
|
|
77
|
+
}
|
|
56
78
|
function loadRegistry() {
|
|
57
79
|
const fs = require("fs");
|
|
58
80
|
const path = require("path");
|
|
@@ -156,7 +178,7 @@ program
|
|
|
156
178
|
.name("opentunnel")
|
|
157
179
|
.alias("ot")
|
|
158
180
|
.description("Expose local ports to the internet via custom domains or ngrok")
|
|
159
|
-
.version("1.0.
|
|
181
|
+
.version("1.0.20");
|
|
160
182
|
// Helper function to build WebSocket URL from domain
|
|
161
183
|
// User only provides base domain (e.g., fjrg2007.com), system handles the rest
|
|
162
184
|
// Note: --insecure flag only affects certificate verification, not the protocol
|
|
@@ -550,8 +572,8 @@ program
|
|
|
550
572
|
const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
|
|
551
573
|
const fsAsync = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
552
574
|
const pathAsync = await Promise.resolve().then(() => __importStar(require("path")));
|
|
553
|
-
const pidFile =
|
|
554
|
-
const logFile =
|
|
575
|
+
const pidFile = getPidFilePath("server");
|
|
576
|
+
const logFile = getLogFilePath("server");
|
|
555
577
|
// Check if already running
|
|
556
578
|
if (fsAsync.existsSync(pidFile)) {
|
|
557
579
|
const oldPid = fsAsync.readFileSync(pidFile, "utf-8").trim();
|
|
@@ -722,7 +744,7 @@ program
|
|
|
722
744
|
.action(async () => {
|
|
723
745
|
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
724
746
|
const path = await Promise.resolve().then(() => __importStar(require("path")));
|
|
725
|
-
const pidFile =
|
|
747
|
+
const pidFile = getPidFilePath("server");
|
|
726
748
|
if (!fs.existsSync(pidFile)) {
|
|
727
749
|
console.log(chalk_1.default.yellow("No server running (PID file not found)"));
|
|
728
750
|
return;
|
|
@@ -742,43 +764,6 @@ program
|
|
|
742
764
|
console.log(chalk_1.default.red(`Failed to stop server: ${err.message}`));
|
|
743
765
|
}
|
|
744
766
|
});
|
|
745
|
-
// Logs command
|
|
746
|
-
program
|
|
747
|
-
.command("logs")
|
|
748
|
-
.description("Show server logs")
|
|
749
|
-
.option("-f, --follow", "Follow log output")
|
|
750
|
-
.option("-n, --lines <n>", "Number of lines to show", "50")
|
|
751
|
-
.action(async (options) => {
|
|
752
|
-
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
753
|
-
const path = await Promise.resolve().then(() => __importStar(require("path")));
|
|
754
|
-
const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
|
|
755
|
-
const logFile = path.join(process.cwd(), "opentunnel.log");
|
|
756
|
-
if (!fs.existsSync(logFile)) {
|
|
757
|
-
console.log(chalk_1.default.yellow("No log file found"));
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
if (options.follow) {
|
|
761
|
-
// Use tail -f on Unix or PowerShell on Windows
|
|
762
|
-
const isWindows = process.platform === "win32";
|
|
763
|
-
if (isWindows) {
|
|
764
|
-
const child = spawn("powershell", ["-Command", `Get-Content -Path "${logFile}" -Tail ${options.lines} -Wait`], {
|
|
765
|
-
stdio: "inherit"
|
|
766
|
-
});
|
|
767
|
-
child.on("error", () => {
|
|
768
|
-
// Fallback: just read the file
|
|
769
|
-
console.log(fs.readFileSync(logFile, "utf-8"));
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
else
|
|
773
|
-
spawn("tail", ["-f", "-n", options.lines, logFile], { stdio: "inherit" });
|
|
774
|
-
}
|
|
775
|
-
else {
|
|
776
|
-
const content = fs.readFileSync(logFile, "utf-8");
|
|
777
|
-
const lines = content.split("\n");
|
|
778
|
-
const lastLines = lines.slice(-parseInt(options.lines));
|
|
779
|
-
console.log(lastLines.join("\n"));
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
767
|
// Status command
|
|
783
768
|
program
|
|
784
769
|
.command("status")
|
|
@@ -1158,8 +1143,8 @@ program
|
|
|
1158
1143
|
const shouldDetach = options.detach === true;
|
|
1159
1144
|
if (shouldDetach) {
|
|
1160
1145
|
const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
|
|
1161
|
-
const pidFile =
|
|
1162
|
-
const logFile =
|
|
1146
|
+
const pidFile = getPidFilePath(instanceName);
|
|
1147
|
+
const logFile = getLogFilePath(instanceName);
|
|
1163
1148
|
// Check if already running
|
|
1164
1149
|
if (fsModule.existsSync(pidFile)) {
|
|
1165
1150
|
const oldPid = fsModule.readFileSync(pidFile, "utf-8").trim();
|
|
@@ -1298,13 +1283,25 @@ program
|
|
|
1298
1283
|
const useHttps = config.server?.https !== false;
|
|
1299
1284
|
const hasTunnels = tunnelsToStart.length > 0;
|
|
1300
1285
|
// Parse multiple domains from config
|
|
1286
|
+
// Helper to check if domain is DuckDNS
|
|
1287
|
+
const isDuckDns = (domain) => domain.toLowerCase().endsWith(".duckdns.org");
|
|
1301
1288
|
let serverDomains;
|
|
1302
1289
|
if (config.server?.domains && config.server.domains.length > 0) {
|
|
1303
1290
|
serverDomains = config.server.domains.map(d => {
|
|
1304
1291
|
if (typeof d === "string") {
|
|
1305
|
-
|
|
1292
|
+
// DuckDNS domains don't use basePath
|
|
1293
|
+
return { domain: d, basePath: isDuckDns(d) ? "" : basePath };
|
|
1306
1294
|
}
|
|
1307
|
-
|
|
1295
|
+
// If basePath is explicitly set for DuckDNS, it will error in TunnelServer
|
|
1296
|
+
// If not set, default to empty for DuckDNS, or global basePath for others
|
|
1297
|
+
const domainBasePath = d.basePath !== undefined
|
|
1298
|
+
? d.basePath
|
|
1299
|
+
: (isDuckDns(d.domain) ? "" : basePath);
|
|
1300
|
+
return {
|
|
1301
|
+
domain: d.domain,
|
|
1302
|
+
basePath: domainBasePath,
|
|
1303
|
+
wildcard: d.wildcard,
|
|
1304
|
+
};
|
|
1308
1305
|
});
|
|
1309
1306
|
}
|
|
1310
1307
|
// Check if we have domain configuration (single or multiple)
|
|
@@ -1554,18 +1551,27 @@ program
|
|
|
1554
1551
|
const pidFile = instance.pidFile;
|
|
1555
1552
|
const instanceName = instance.name;
|
|
1556
1553
|
const cwd = instance.cwd;
|
|
1557
|
-
|
|
1554
|
+
// Open file descriptor for logging (required for detached processes)
|
|
1555
|
+
const logFd = fs.openSync(logFile, "a");
|
|
1558
1556
|
const child = spawn(process.execPath, [process.argv[1], "up", "-f", pathModule.basename(configPath)], {
|
|
1559
1557
|
cwd,
|
|
1560
1558
|
detached: true,
|
|
1561
|
-
stdio: ["ignore",
|
|
1559
|
+
stdio: ["ignore", logFd, logFd],
|
|
1562
1560
|
env: { ...process.env, OPENTUNNEL_INSTANCE_NAME: instanceName },
|
|
1563
1561
|
});
|
|
1564
1562
|
child.unref();
|
|
1565
|
-
|
|
1566
|
-
|
|
1563
|
+
fs.closeSync(logFd);
|
|
1564
|
+
// Wait and check if process started successfully
|
|
1565
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
1566
|
+
let processRunning = false;
|
|
1567
1567
|
try {
|
|
1568
1568
|
process.kill(child.pid, 0);
|
|
1569
|
+
processRunning = true;
|
|
1570
|
+
}
|
|
1571
|
+
catch {
|
|
1572
|
+
processRunning = false;
|
|
1573
|
+
}
|
|
1574
|
+
if (processRunning) {
|
|
1569
1575
|
// Process is running, register it
|
|
1570
1576
|
registerInstance({
|
|
1571
1577
|
name: instanceName,
|
|
@@ -1580,11 +1586,43 @@ program
|
|
|
1580
1586
|
fs.writeFileSync(pidFile, child.pid.toString());
|
|
1581
1587
|
console.log(chalk_1.default.green(` ↑ Started ${instanceName} (PID: ${child.pid})`));
|
|
1582
1588
|
}
|
|
1583
|
-
|
|
1589
|
+
else {
|
|
1590
|
+
// Process failed - check logs for error
|
|
1584
1591
|
console.log(chalk_1.default.red(` ✗ Failed to start ${instanceName}`));
|
|
1592
|
+
// Read last lines of log to show error
|
|
1593
|
+
if (fs.existsSync(logFile)) {
|
|
1594
|
+
const logContent = fs.readFileSync(logFile, "utf-8");
|
|
1595
|
+
const lines = logContent.split("\n");
|
|
1596
|
+
// Find error lines
|
|
1597
|
+
const errorLines = [];
|
|
1598
|
+
let capturing = false;
|
|
1599
|
+
for (let i = lines.length - 1; i >= 0 && errorLines.length < 10; i--) {
|
|
1600
|
+
const line = lines[i];
|
|
1601
|
+
if (line.includes("Error:") || line.includes("error:") || capturing) {
|
|
1602
|
+
errorLines.unshift(line);
|
|
1603
|
+
capturing = true;
|
|
1604
|
+
}
|
|
1605
|
+
if (line.includes("throw new Error") || line.includes("at new")) {
|
|
1606
|
+
capturing = true;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
if (errorLines.length > 0) {
|
|
1610
|
+
console.log(chalk_1.default.red("\n Error details:"));
|
|
1611
|
+
console.log(chalk_1.default.gray(" " + "─".repeat(60)));
|
|
1612
|
+
// Find the actual error message
|
|
1613
|
+
const errorMsg = errorLines.find(l => l.includes("Error:"));
|
|
1614
|
+
if (errorMsg) {
|
|
1615
|
+
const match = errorMsg.match(/Error:\s*(.+)/);
|
|
1616
|
+
if (match) {
|
|
1617
|
+
console.log(chalk_1.default.red(` ${match[1]}`));
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
console.log(chalk_1.default.gray(" " + "─".repeat(60)));
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1585
1623
|
}
|
|
1586
1624
|
}
|
|
1587
|
-
console.log(
|
|
1625
|
+
console.log();
|
|
1588
1626
|
});
|
|
1589
1627
|
// PS command - list running tunnel processes (global)
|
|
1590
1628
|
program
|
|
@@ -1725,6 +1763,104 @@ program
|
|
|
1725
1763
|
console.log(lines.join("\n"));
|
|
1726
1764
|
}
|
|
1727
1765
|
});
|
|
1766
|
+
// Logs clean command - remove log files
|
|
1767
|
+
program
|
|
1768
|
+
.command("logs-clean [name]")
|
|
1769
|
+
.description("Clean log files")
|
|
1770
|
+
.option("--all", "Clean all log files")
|
|
1771
|
+
.action(async (name, options) => {
|
|
1772
|
+
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
1773
|
+
const logsDir = getLogsDir();
|
|
1774
|
+
if (!fs.existsSync(logsDir)) {
|
|
1775
|
+
console.log(chalk_1.default.yellow("No logs directory found"));
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
let files;
|
|
1779
|
+
if (options.all) {
|
|
1780
|
+
// Clean all log files
|
|
1781
|
+
files = fs.readdirSync(logsDir).filter(f => f.endsWith(".log"));
|
|
1782
|
+
}
|
|
1783
|
+
else if (name) {
|
|
1784
|
+
// Clean specific instance logs
|
|
1785
|
+
const safeName = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1786
|
+
const logFile = `${safeName}.log`;
|
|
1787
|
+
files = fs.existsSync(path.join(logsDir, logFile)) ? [logFile] : [];
|
|
1788
|
+
}
|
|
1789
|
+
else {
|
|
1790
|
+
console.log(chalk_1.default.yellow("Specify instance name or use --all to clean all logs"));
|
|
1791
|
+
console.log(chalk_1.default.gray(` opentunnel logs-clean <name> Clean logs for specific instance`));
|
|
1792
|
+
console.log(chalk_1.default.gray(` opentunnel logs-clean --all Clean all log files`));
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
if (files.length === 0) {
|
|
1796
|
+
console.log(chalk_1.default.yellow(name ? `No logs found for "${name}"` : "No log files found"));
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
let totalSize = 0;
|
|
1800
|
+
for (const file of files) {
|
|
1801
|
+
const filePath = path.join(logsDir, file);
|
|
1802
|
+
try {
|
|
1803
|
+
const stat = fs.statSync(filePath);
|
|
1804
|
+
totalSize += stat.size;
|
|
1805
|
+
fs.unlinkSync(filePath);
|
|
1806
|
+
console.log(chalk_1.default.green(` ✓ Deleted ${file} (${(stat.size / 1024).toFixed(1)} KB)`));
|
|
1807
|
+
}
|
|
1808
|
+
catch (err) {
|
|
1809
|
+
console.log(chalk_1.default.red(` ✗ Failed to delete ${file}: ${err.message}`));
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
console.log(chalk_1.default.gray(`\nCleaned ${files.length} file(s), freed ${(totalSize / 1024).toFixed(1)} KB`));
|
|
1813
|
+
console.log(chalk_1.default.gray(`Logs directory: ${logsDir}`));
|
|
1814
|
+
});
|
|
1815
|
+
// Logs list command - show logs directory and list log files
|
|
1816
|
+
program
|
|
1817
|
+
.command("logs-list")
|
|
1818
|
+
.description("List all log files and show logs directory")
|
|
1819
|
+
.action(async () => {
|
|
1820
|
+
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
1821
|
+
const logsDir = getLogsDir();
|
|
1822
|
+
console.log(chalk_1.default.cyan(`Logs directory: ${logsDir}\n`));
|
|
1823
|
+
if (!fs.existsSync(logsDir)) {
|
|
1824
|
+
console.log(chalk_1.default.yellow("No logs directory found"));
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
const files = fs.readdirSync(logsDir).filter(f => f.endsWith(".log") || f.endsWith(".pid"));
|
|
1828
|
+
if (files.length === 0) {
|
|
1829
|
+
console.log(chalk_1.default.yellow("No log files found"));
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
const logFiles = files.filter(f => f.endsWith(".log"));
|
|
1833
|
+
const pidFiles = files.filter(f => f.endsWith(".pid"));
|
|
1834
|
+
if (logFiles.length > 0) {
|
|
1835
|
+
console.log(chalk_1.default.white("Log files:"));
|
|
1836
|
+
let totalSize = 0;
|
|
1837
|
+
for (const file of logFiles) {
|
|
1838
|
+
const filePath = path.join(logsDir, file);
|
|
1839
|
+
const stat = fs.statSync(filePath);
|
|
1840
|
+
const size = (stat.size / 1024).toFixed(1);
|
|
1841
|
+
const modified = stat.mtime.toLocaleString();
|
|
1842
|
+
totalSize += stat.size;
|
|
1843
|
+
console.log(chalk_1.default.gray(` ${file.padEnd(35)} ${size.padStart(8)} KB ${modified}`));
|
|
1844
|
+
}
|
|
1845
|
+
console.log(chalk_1.default.gray(`\n Total: ${logFiles.length} file(s), ${(totalSize / 1024).toFixed(1)} KB`));
|
|
1846
|
+
}
|
|
1847
|
+
if (pidFiles.length > 0) {
|
|
1848
|
+
console.log(chalk_1.default.white("\nPID files (active instances):"));
|
|
1849
|
+
for (const file of pidFiles) {
|
|
1850
|
+
const filePath = path.join(logsDir, file);
|
|
1851
|
+
const pid = fs.readFileSync(filePath, "utf-8").trim();
|
|
1852
|
+
const name = file.replace(".pid", "");
|
|
1853
|
+
let status = chalk_1.default.green("running");
|
|
1854
|
+
try {
|
|
1855
|
+
process.kill(parseInt(pid), 0);
|
|
1856
|
+
}
|
|
1857
|
+
catch {
|
|
1858
|
+
status = chalk_1.default.red("stale");
|
|
1859
|
+
}
|
|
1860
|
+
console.log(chalk_1.default.gray(` ${name.padEnd(35)} PID: ${pid.padStart(6)} ${status}`));
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
});
|
|
1728
1864
|
// Test server command - simple HTTP server for testing tunnels
|
|
1729
1865
|
program
|
|
1730
1866
|
.command("test-server")
|
|
@@ -1738,8 +1874,8 @@ program
|
|
|
1738
1874
|
const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
|
|
1739
1875
|
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
1740
1876
|
const path = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1741
|
-
const pidFile =
|
|
1742
|
-
const logFile =
|
|
1877
|
+
const pidFile = getPidFilePath(`test-server-${port}`);
|
|
1878
|
+
const logFile = getLogFilePath(`test-server-${port}`);
|
|
1743
1879
|
if (fs.existsSync(pidFile)) {
|
|
1744
1880
|
const oldPid = fs.readFileSync(pidFile, "utf-8").trim();
|
|
1745
1881
|
try {
|
|
@@ -1827,22 +1963,25 @@ program
|
|
|
1827
1963
|
.option("-p, --port <port>", "Stop specific port")
|
|
1828
1964
|
.action(async (options) => {
|
|
1829
1965
|
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
1830
|
-
const
|
|
1966
|
+
const logsDir = getLogsDir();
|
|
1831
1967
|
let pidFiles;
|
|
1832
1968
|
if (options.port) {
|
|
1833
|
-
const
|
|
1834
|
-
pidFiles = fs.existsSync(
|
|
1969
|
+
const pidPath = getPidFilePath(`test-server-${options.port}`);
|
|
1970
|
+
pidFiles = fs.existsSync(pidPath) ? [pidPath] : [];
|
|
1971
|
+
}
|
|
1972
|
+
else {
|
|
1973
|
+
pidFiles = fs.readdirSync(logsDir)
|
|
1974
|
+
.filter(f => f.startsWith("test-server-") && f.endsWith(".pid"))
|
|
1975
|
+
.map(f => path.join(logsDir, f));
|
|
1835
1976
|
}
|
|
1836
|
-
else
|
|
1837
|
-
pidFiles = fs.readdirSync(process.cwd()).filter(f => f.startsWith(".test-server-") && f.endsWith(".pid"));
|
|
1838
1977
|
if (pidFiles.length === 0) {
|
|
1839
1978
|
console.log(chalk_1.default.yellow("No test servers running"));
|
|
1840
1979
|
return;
|
|
1841
1980
|
}
|
|
1842
|
-
for (const
|
|
1843
|
-
const pidPath = path.join(process.cwd(), pidFile);
|
|
1981
|
+
for (const pidPath of pidFiles) {
|
|
1844
1982
|
const pid = parseInt(fs.readFileSync(pidPath, "utf-8").trim());
|
|
1845
|
-
const
|
|
1983
|
+
const fileName = path.basename(pidPath);
|
|
1984
|
+
const port = fileName.replace("test-server-", "").replace(".pid", "");
|
|
1846
1985
|
try {
|
|
1847
1986
|
process.kill(pid, "SIGTERM");
|
|
1848
1987
|
fs.unlinkSync(pidPath);
|
|
@@ -1860,8 +1999,8 @@ async function runTunnelInBackgroundFromConfig(name, protocol, port, options) {
|
|
|
1860
1999
|
const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
|
|
1861
2000
|
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
1862
2001
|
const path = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1863
|
-
const pidFile =
|
|
1864
|
-
const logFile =
|
|
2002
|
+
const pidFile = getPidFilePath(name);
|
|
2003
|
+
const logFile = getLogFilePath(name);
|
|
1865
2004
|
// Check if already running
|
|
1866
2005
|
if (fs.existsSync(pidFile)) {
|
|
1867
2006
|
const oldPid = fs.readFileSync(pidFile, "utf-8").trim();
|
|
@@ -1978,8 +2117,8 @@ async function runTunnelInBackground(command, port, options) {
|
|
|
1978
2117
|
const fs = await Promise.resolve().then(() => __importStar(require("fs")));
|
|
1979
2118
|
const path = await Promise.resolve().then(() => __importStar(require("path")));
|
|
1980
2119
|
const tunnelId = `tunnel-${port}-${Date.now()}`;
|
|
1981
|
-
const pidFile =
|
|
1982
|
-
const logFile =
|
|
2120
|
+
const pidFile = getPidFilePath(`tunnel-${port}`);
|
|
2121
|
+
const logFile = getLogFilePath(`tunnel-${port}`);
|
|
1983
2122
|
// Check if already running
|
|
1984
2123
|
if (fs.existsSync(pidFile)) {
|
|
1985
2124
|
const oldPid = fs.readFileSync(pidFile, "utf-8").trim();
|