bindler 1.6.2 → 1.7.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/dist/cli.js +81 -18
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -741,14 +741,30 @@ function isProtectedPath(path) {
|
|
|
741
741
|
(protected_) => normalizedPath === protected_ || normalizedPath.startsWith(protected_ + "/")
|
|
742
742
|
);
|
|
743
743
|
}
|
|
744
|
-
function checkPortAvailable(port) {
|
|
744
|
+
function checkPortAvailable(port, projectName) {
|
|
745
745
|
const result = execCommandSafe(`lsof -i :${port} -P -n 2>/dev/null | grep LISTEN | head -1`);
|
|
746
746
|
if (!result.success || !result.output) {
|
|
747
747
|
return { available: true };
|
|
748
748
|
}
|
|
749
749
|
const parts = result.output.trim().split(/\s+/);
|
|
750
750
|
const processName = parts[0] || "unknown";
|
|
751
|
-
|
|
751
|
+
const pid = parts[1] || "";
|
|
752
|
+
if (projectName && pid) {
|
|
753
|
+
const pm2Check = execCommandSafe(`pm2 jlist 2>/dev/null`);
|
|
754
|
+
if (pm2Check.success && pm2Check.output) {
|
|
755
|
+
try {
|
|
756
|
+
const processes = JSON.parse(pm2Check.output);
|
|
757
|
+
const bindlerProcess = processes.find(
|
|
758
|
+
(p) => p.name === `bindler:${projectName}` && String(p.pid) === pid
|
|
759
|
+
);
|
|
760
|
+
if (bindlerProcess) {
|
|
761
|
+
return { available: false, usedBy: processName, isOwnProcess: true };
|
|
762
|
+
}
|
|
763
|
+
} catch {
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return { available: false, usedBy: processName, isOwnProcess: false };
|
|
752
768
|
}
|
|
753
769
|
function validateProject(project) {
|
|
754
770
|
const errors = [];
|
|
@@ -762,8 +778,8 @@ function validateProject(project) {
|
|
|
762
778
|
);
|
|
763
779
|
}
|
|
764
780
|
if (project.type === "npm" && project.port) {
|
|
765
|
-
const portCheck = checkPortAvailable(project.port);
|
|
766
|
-
if (!portCheck.available) {
|
|
781
|
+
const portCheck = checkPortAvailable(project.port, project.name);
|
|
782
|
+
if (!portCheck.available && !portCheck.isOwnProcess) {
|
|
767
783
|
warnings.push(`Port ${project.port} is in use by ${portCheck.usedBy}`);
|
|
768
784
|
}
|
|
769
785
|
}
|
|
@@ -1745,6 +1761,15 @@ ${succeeded}/${results.length} stopped successfully`));
|
|
|
1745
1761
|
console.log(chalk7.yellow(`Project "${name}" is a static site - no process to stop.`));
|
|
1746
1762
|
return;
|
|
1747
1763
|
}
|
|
1764
|
+
const processStatus = getProcessByName(name);
|
|
1765
|
+
if (!processStatus) {
|
|
1766
|
+
console.log(chalk7.yellow(`${name} is not managed by PM2 (never started)`));
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
if (processStatus.status === "stopped") {
|
|
1770
|
+
console.log(chalk7.yellow(`${name} is already stopped`));
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1748
1773
|
console.log(chalk7.blue(`Stopping ${name}...`));
|
|
1749
1774
|
const result = stopProject(name);
|
|
1750
1775
|
if (result.success) {
|
|
@@ -2369,7 +2394,7 @@ async function infoCommand() {
|
|
|
2369
2394
|
`));
|
|
2370
2395
|
console.log(chalk15.white(" Manage multiple projects behind Cloudflare Tunnel"));
|
|
2371
2396
|
console.log(chalk15.white(" with Nginx and PM2\n"));
|
|
2372
|
-
console.log(chalk15.dim(" Version: ") + chalk15.white("1.
|
|
2397
|
+
console.log(chalk15.dim(" Version: ") + chalk15.white("1.7.0"));
|
|
2373
2398
|
console.log(chalk15.dim(" Author: ") + chalk15.white("alfaoz"));
|
|
2374
2399
|
console.log(chalk15.dim(" License: ") + chalk15.white("MIT"));
|
|
2375
2400
|
console.log(chalk15.dim(" GitHub: ") + chalk15.cyan("https://github.com/alfaoz/bindler"));
|
|
@@ -2426,8 +2451,9 @@ async function checkDns(hostname) {
|
|
|
2426
2451
|
}
|
|
2427
2452
|
return result;
|
|
2428
2453
|
}
|
|
2429
|
-
async function checkHttp(hostname, path = "/") {
|
|
2430
|
-
const
|
|
2454
|
+
async function checkHttp(hostname, path = "/", useHttp = false) {
|
|
2455
|
+
const protocol = useHttp ? "http" : "https";
|
|
2456
|
+
const url = `${protocol}://${hostname}${path}`;
|
|
2431
2457
|
const startTime = Date.now();
|
|
2432
2458
|
try {
|
|
2433
2459
|
const controller = new AbortController();
|
|
@@ -2443,12 +2469,14 @@ async function checkHttp(hostname, path = "/") {
|
|
|
2443
2469
|
reachable: true,
|
|
2444
2470
|
statusCode: response.status,
|
|
2445
2471
|
redirectUrl: response.headers.get("location") || void 0,
|
|
2446
|
-
responseTime
|
|
2472
|
+
responseTime,
|
|
2473
|
+
protocol
|
|
2447
2474
|
};
|
|
2448
2475
|
} catch (error) {
|
|
2449
2476
|
return {
|
|
2450
2477
|
reachable: false,
|
|
2451
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2478
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2479
|
+
protocol
|
|
2452
2480
|
};
|
|
2453
2481
|
}
|
|
2454
2482
|
}
|
|
@@ -2488,8 +2516,9 @@ Checking ${hostname}...
|
|
|
2488
2516
|
console.log(chalk16.green(" \u2713 AAAA (IPv6): ") + dns.ipv6.join(", "));
|
|
2489
2517
|
}
|
|
2490
2518
|
console.log("");
|
|
2491
|
-
|
|
2492
|
-
|
|
2519
|
+
const useHttp = options.http ?? isLocal;
|
|
2520
|
+
console.log(chalk16.bold(`HTTP Check (${useHttp ? "HTTP" : "HTTPS"}):`));
|
|
2521
|
+
const http = await checkHttp(hostname, basePath, useHttp);
|
|
2493
2522
|
if (!http.reachable) {
|
|
2494
2523
|
console.log(chalk16.red(" \u2717 Not reachable"));
|
|
2495
2524
|
const err = http.error || "";
|
|
@@ -3800,14 +3829,48 @@ async function cloneCommand(source, newName, options) {
|
|
|
3800
3829
|
process.exit(1);
|
|
3801
3830
|
}
|
|
3802
3831
|
}
|
|
3832
|
+
let targetPath = options.path;
|
|
3833
|
+
if (!targetPath) {
|
|
3834
|
+
const answer = await inquirer5.prompt([
|
|
3835
|
+
{
|
|
3836
|
+
type: "input",
|
|
3837
|
+
name: "path",
|
|
3838
|
+
message: "Path for new project:",
|
|
3839
|
+
default: sourceProject.path.replace(source, targetName),
|
|
3840
|
+
validate: (input) => {
|
|
3841
|
+
if (!input || input.trim() === "") {
|
|
3842
|
+
return "Path is required";
|
|
3843
|
+
}
|
|
3844
|
+
return true;
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
]);
|
|
3848
|
+
targetPath = answer.path;
|
|
3849
|
+
}
|
|
3803
3850
|
const newProject = {
|
|
3804
3851
|
...sourceProject,
|
|
3805
3852
|
name: targetName,
|
|
3806
3853
|
hostname: targetHostname,
|
|
3807
|
-
path:
|
|
3854
|
+
path: targetPath
|
|
3808
3855
|
};
|
|
3809
3856
|
if (newProject.type === "npm") {
|
|
3810
|
-
|
|
3857
|
+
let port = options.port || findAvailablePort();
|
|
3858
|
+
const portCheck = checkPortInUse(port);
|
|
3859
|
+
if (portCheck.inUse) {
|
|
3860
|
+
const processInfo = portCheck.process ? ` by ${portCheck.process}` : "";
|
|
3861
|
+
console.log(chalk27.yellow(`Warning: Port ${port} is already in use${processInfo}`));
|
|
3862
|
+
if (!options.port) {
|
|
3863
|
+
for (let p = port + 1; p <= 9e3; p++) {
|
|
3864
|
+
const check = checkPortInUse(p);
|
|
3865
|
+
if (!check.inUse) {
|
|
3866
|
+
port = p;
|
|
3867
|
+
console.log(chalk27.dim(` Using port ${port} instead`));
|
|
3868
|
+
break;
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
newProject.port = port;
|
|
3811
3874
|
if (newProject.env?.PORT) {
|
|
3812
3875
|
newProject.env = { ...newProject.env, PORT: String(newProject.port) };
|
|
3813
3876
|
}
|
|
@@ -4111,28 +4174,28 @@ async function checkForUpdates() {
|
|
|
4111
4174
|
const cache = readCache();
|
|
4112
4175
|
const now = Date.now();
|
|
4113
4176
|
if (now - cache.lastCheck < CHECK_INTERVAL) {
|
|
4114
|
-
if (cache.latestVersion && compareVersions("1.
|
|
4177
|
+
if (cache.latestVersion && compareVersions("1.7.0", cache.latestVersion) > 0) {
|
|
4115
4178
|
showUpdateMessage(cache.latestVersion);
|
|
4116
4179
|
}
|
|
4117
4180
|
return;
|
|
4118
4181
|
}
|
|
4119
4182
|
fetchLatestVersion().then((latestVersion) => {
|
|
4120
4183
|
writeCache({ lastCheck: now, latestVersion });
|
|
4121
|
-
if (latestVersion && compareVersions("1.
|
|
4184
|
+
if (latestVersion && compareVersions("1.7.0", latestVersion) > 0) {
|
|
4122
4185
|
showUpdateMessage(latestVersion);
|
|
4123
4186
|
}
|
|
4124
4187
|
});
|
|
4125
4188
|
}
|
|
4126
4189
|
function showUpdateMessage(latestVersion) {
|
|
4127
4190
|
console.log("");
|
|
4128
|
-
console.log(chalk30.yellow(` Update available: ${"1.
|
|
4191
|
+
console.log(chalk30.yellow(` Update available: ${"1.7.0"} \u2192 ${latestVersion}`));
|
|
4129
4192
|
console.log(chalk30.dim(` Run: npm update -g bindler`));
|
|
4130
4193
|
console.log("");
|
|
4131
4194
|
}
|
|
4132
4195
|
|
|
4133
4196
|
// src/cli.ts
|
|
4134
4197
|
var program = new Command();
|
|
4135
|
-
program.name("bindler").description("Manage multiple projects behind Cloudflare Tunnel with Nginx and PM2").version("1.
|
|
4198
|
+
program.name("bindler").description("Manage multiple projects behind Cloudflare Tunnel with Nginx and PM2").version("1.7.0");
|
|
4136
4199
|
program.hook("preAction", async () => {
|
|
4137
4200
|
try {
|
|
4138
4201
|
initConfig();
|
|
@@ -4234,7 +4297,7 @@ program.command("ports").description("Show allocated ports").action(async () =>
|
|
|
4234
4297
|
program.command("info").description("Show bindler information and stats").action(async () => {
|
|
4235
4298
|
await infoCommand();
|
|
4236
4299
|
});
|
|
4237
|
-
program.command("check [hostname]").description("Check DNS propagation and HTTP accessibility for a hostname").option("-v, --verbose", "Show verbose output").action(async (hostname, options) => {
|
|
4300
|
+
program.command("check [hostname]").description("Check DNS propagation and HTTP accessibility for a hostname").option("-v, --verbose", "Show verbose output").option("--http", "Use HTTP instead of HTTPS (auto-enabled for .local hostnames)").action(async (hostname, options) => {
|
|
4238
4301
|
if (!hostname) {
|
|
4239
4302
|
console.log(chalk31.red("Usage: bindler check <hostname>"));
|
|
4240
4303
|
console.log(chalk31.dim("\nExamples:"));
|