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 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
- return { available: false, usedBy: processName };
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.6.2"));
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 url = `https://${hostname}${path}`;
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
- console.log(chalk16.bold("HTTP Check:"));
2492
- const http = await checkHttp(hostname, basePath);
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: options.path || sourceProject.path
3854
+ path: targetPath
3808
3855
  };
3809
3856
  if (newProject.type === "npm") {
3810
- newProject.port = options.port || findAvailablePort();
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.6.2", cache.latestVersion) > 0) {
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.6.2", latestVersion) > 0) {
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.6.2"} \u2192 ${latestVersion}`));
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.6.2");
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:"));