bindler 1.3.0 → 1.6.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 +271 -69
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
8
|
|
|
9
9
|
// src/cli.ts
|
|
10
10
|
import { Command } from "commander";
|
|
11
|
-
import
|
|
11
|
+
import chalk31 from "chalk";
|
|
12
12
|
|
|
13
13
|
// src/commands/new.ts
|
|
14
14
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
@@ -284,6 +284,25 @@ function isPortAvailable(port) {
|
|
|
284
284
|
const usedPorts = new Set(getUsedPorts());
|
|
285
285
|
return !usedPorts.has(port);
|
|
286
286
|
}
|
|
287
|
+
function checkPortInUse(port) {
|
|
288
|
+
const lsofResult = execCommandSafe(`lsof -i :${port} -P -n 2>/dev/null | grep LISTEN`);
|
|
289
|
+
if (lsofResult.success && lsofResult.output) {
|
|
290
|
+
const match = lsofResult.output.match(/^(\S+)\s+(\d+)/);
|
|
291
|
+
if (match) {
|
|
292
|
+
return { inUse: true, process: `${match[1]} (PID ${match[2]})` };
|
|
293
|
+
}
|
|
294
|
+
return { inUse: true };
|
|
295
|
+
}
|
|
296
|
+
const ssResult = execCommandSafe(`ss -tlnp 2>/dev/null | grep ":${port} "`);
|
|
297
|
+
if (ssResult.success && ssResult.output) {
|
|
298
|
+
const match = ssResult.output.match(/users:\(\("([^"]+)",pid=(\d+)/);
|
|
299
|
+
if (match) {
|
|
300
|
+
return { inUse: true, process: `${match[1]} (PID ${match[2]})` };
|
|
301
|
+
}
|
|
302
|
+
return { inUse: true };
|
|
303
|
+
}
|
|
304
|
+
return { inUse: false };
|
|
305
|
+
}
|
|
287
306
|
function getPortsTable() {
|
|
288
307
|
const config = readConfig();
|
|
289
308
|
return config.projects.filter((p) => p.type === "npm" && p.port).map((p) => ({
|
|
@@ -570,18 +589,17 @@ function startProject(project) {
|
|
|
570
589
|
}
|
|
571
590
|
const pm2Name = getPm2ProcessName(project.name);
|
|
572
591
|
const existingProcess = getProcessByName(project.name);
|
|
573
|
-
const envVars = [];
|
|
574
|
-
if (project.env) {
|
|
575
|
-
for (const [key, value] of Object.entries(project.env)) {
|
|
576
|
-
envVars.push(`${key}=${value}`);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
592
|
let command;
|
|
580
593
|
if (existingProcess) {
|
|
581
594
|
command = `pm2 restart "${pm2Name}"`;
|
|
582
595
|
} else {
|
|
583
|
-
|
|
584
|
-
|
|
596
|
+
let startCmd = project.start;
|
|
597
|
+
if (project.env && Object.keys(project.env).length > 0) {
|
|
598
|
+
const envPrefix = Object.entries(project.env).map(([key, value]) => `${key}=${escapeShellArg(value)}`).join(" ");
|
|
599
|
+
startCmd = `${envPrefix} ${project.start}`;
|
|
600
|
+
}
|
|
601
|
+
const escapedCmd = startCmd.replace(/'/g, "'\\''");
|
|
602
|
+
command = `pm2 start 'bash -c "${escapedCmd}"' --name "${pm2Name}" --cwd "${project.path}"`;
|
|
585
603
|
}
|
|
586
604
|
const result = execCommandSafe(command);
|
|
587
605
|
if (!result.success) {
|
|
@@ -590,6 +608,12 @@ function startProject(project) {
|
|
|
590
608
|
execCommandSafe("pm2 save");
|
|
591
609
|
return { success: true };
|
|
592
610
|
}
|
|
611
|
+
function escapeShellArg(arg) {
|
|
612
|
+
if (/[^a-zA-Z0-9_\-=]/.test(arg)) {
|
|
613
|
+
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
614
|
+
}
|
|
615
|
+
return arg;
|
|
616
|
+
}
|
|
593
617
|
function stopProject(name) {
|
|
594
618
|
const pm2Name = getPm2ProcessName(name);
|
|
595
619
|
const result = execCommandSafe(`pm2 stop "${pm2Name}"`);
|
|
@@ -981,7 +1005,17 @@ async function newCommand(options) {
|
|
|
981
1005
|
if (yamlDefaults.environments) project.environments = yamlDefaults.environments;
|
|
982
1006
|
if (answers.type === "npm") {
|
|
983
1007
|
const scripts = existsSync6(answers.path) ? getPackageJsonScripts(answers.path) : [];
|
|
984
|
-
|
|
1008
|
+
let suggestedPort = yamlDefaults.port || findAvailablePort();
|
|
1009
|
+
const portCheck = checkPortInUse(suggestedPort);
|
|
1010
|
+
if (portCheck.inUse) {
|
|
1011
|
+
for (let p = suggestedPort + 1; p <= 9e3; p++) {
|
|
1012
|
+
const check = checkPortInUse(p);
|
|
1013
|
+
if (!check.inUse) {
|
|
1014
|
+
suggestedPort = p;
|
|
1015
|
+
break;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
985
1019
|
const npmAnswers = await inquirer.prompt([
|
|
986
1020
|
{
|
|
987
1021
|
type: "input",
|
|
@@ -993,6 +1027,11 @@ async function newCommand(options) {
|
|
|
993
1027
|
if (!validatePort(port)) {
|
|
994
1028
|
return "Invalid port. Use a number between 1024 and 65535.";
|
|
995
1029
|
}
|
|
1030
|
+
const inUseCheck = checkPortInUse(port);
|
|
1031
|
+
if (inUseCheck.inUse) {
|
|
1032
|
+
const processInfo = inUseCheck.process ? ` by ${inUseCheck.process}` : "";
|
|
1033
|
+
return `Port ${port} is already in use${processInfo}. Choose another port.`;
|
|
1034
|
+
}
|
|
996
1035
|
return true;
|
|
997
1036
|
},
|
|
998
1037
|
filter: (input) => parseInt(input, 10)
|
|
@@ -1048,7 +1087,23 @@ async function newCommand(options) {
|
|
|
1048
1087
|
project.local = true;
|
|
1049
1088
|
}
|
|
1050
1089
|
if (project.type === "npm") {
|
|
1051
|
-
|
|
1090
|
+
let port = options.port || findAvailablePort();
|
|
1091
|
+
const portCheck = checkPortInUse(port);
|
|
1092
|
+
if (portCheck.inUse) {
|
|
1093
|
+
const processInfo = portCheck.process ? ` by ${portCheck.process}` : "";
|
|
1094
|
+
console.log(chalk2.yellow(`Warning: Port ${port} is already in use${processInfo}`));
|
|
1095
|
+
if (!options.port) {
|
|
1096
|
+
for (let p = port + 1; p <= 9e3; p++) {
|
|
1097
|
+
const check = checkPortInUse(p);
|
|
1098
|
+
if (!check.inUse) {
|
|
1099
|
+
port = p;
|
|
1100
|
+
console.log(chalk2.dim(` Using port ${port} instead`));
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
project.port = port;
|
|
1052
1107
|
project.start = options.start || "npm start";
|
|
1053
1108
|
project.env = { PORT: String(project.port) };
|
|
1054
1109
|
}
|
|
@@ -1649,11 +1704,33 @@ async function removeCommand(name, options) {
|
|
|
1649
1704
|
try {
|
|
1650
1705
|
removeProject(name);
|
|
1651
1706
|
console.log(chalk11.green(`\u2713 Project "${name}" removed from registry`));
|
|
1652
|
-
|
|
1707
|
+
if (options.apply) {
|
|
1708
|
+
console.log(chalk11.dim("\nApplying nginx configuration..."));
|
|
1709
|
+
const config = readConfig();
|
|
1710
|
+
try {
|
|
1711
|
+
writeNginxConfig(config);
|
|
1712
|
+
const testResult = testNginxConfig();
|
|
1713
|
+
if (testResult.success) {
|
|
1714
|
+
reloadNginx();
|
|
1715
|
+
console.log(chalk11.green("\u2713 Nginx configuration updated"));
|
|
1716
|
+
} else {
|
|
1717
|
+
console.log(chalk11.yellow("! Nginx config test failed, reload skipped"));
|
|
1718
|
+
}
|
|
1719
|
+
} catch (err) {
|
|
1720
|
+
console.log(chalk11.yellow(`! Failed to update nginx: ${err}`));
|
|
1721
|
+
console.log(chalk11.dim(" Try running: sudo bindler apply"));
|
|
1722
|
+
}
|
|
1723
|
+
} else {
|
|
1724
|
+
console.log(chalk11.dim(`
|
|
1653
1725
|
Run ${chalk11.cyan("sudo bindler apply")} to update nginx configuration.`));
|
|
1654
|
-
|
|
1655
|
-
console.log(chalk11.
|
|
1656
|
-
console.log(chalk11.dim(`
|
|
1726
|
+
}
|
|
1727
|
+
console.log(chalk11.yellow("\nNote: Project files were not deleted."));
|
|
1728
|
+
console.log(chalk11.dim(` Path: ${project.path}`));
|
|
1729
|
+
if (!project.local) {
|
|
1730
|
+
console.log(chalk11.yellow("\nCloudflare DNS route was not removed."));
|
|
1731
|
+
console.log(chalk11.dim(" Remove it manually from the Cloudflare dashboard:"));
|
|
1732
|
+
console.log(chalk11.dim(" https://dash.cloudflare.com \u2192 DNS \u2192 Records \u2192 Delete the CNAME for " + project.hostname));
|
|
1733
|
+
}
|
|
1657
1734
|
} catch (error) {
|
|
1658
1735
|
console.error(chalk11.red(`Error: ${error instanceof Error ? error.message : error}`));
|
|
1659
1736
|
process.exit(1);
|
|
@@ -1784,12 +1861,9 @@ async function applyCommand(options) {
|
|
|
1784
1861
|
const envProjects = listProjectsForEnv(options.env);
|
|
1785
1862
|
config = { ...config, projects: envProjects };
|
|
1786
1863
|
}
|
|
1787
|
-
|
|
1788
|
-
console.log(chalk12.yellow("No projects registered. Nothing to apply."));
|
|
1789
|
-
return;
|
|
1790
|
-
}
|
|
1864
|
+
const hasProjects = config.projects.length > 0;
|
|
1791
1865
|
console.log(chalk12.blue("Applying configuration...\n"));
|
|
1792
|
-
if (!options.skipChecks) {
|
|
1866
|
+
if (hasProjects && !options.skipChecks) {
|
|
1793
1867
|
console.log(chalk12.dim("Running preflight checks..."));
|
|
1794
1868
|
const checkResult = runPreflightChecks(config);
|
|
1795
1869
|
if (!checkResult.valid) {
|
|
@@ -1857,11 +1931,32 @@ Try running with sudo: ${chalk12.cyan("sudo bindler apply")}`));
|
|
|
1857
1931
|
process.exit(1);
|
|
1858
1932
|
}
|
|
1859
1933
|
console.log(chalk12.green(" \u2713 Nginx reloaded successfully"));
|
|
1934
|
+
const listenPort = parseInt(defaults.nginxListen.split(":").pop() || "80", 10);
|
|
1935
|
+
const portCheck = execCommandSafe(`lsof -i :${listenPort} -P -n 2>/dev/null | grep LISTEN | grep nginx`);
|
|
1936
|
+
if (!portCheck.success || !portCheck.output) {
|
|
1937
|
+
const isPrivilegedPort = listenPort < 1024;
|
|
1938
|
+
console.log(chalk12.yellow(`
|
|
1939
|
+
\u26A0 Nginx is not listening on port ${listenPort}`));
|
|
1940
|
+
if (isPrivilegedPort) {
|
|
1941
|
+
console.log(chalk12.dim(` Port ${listenPort} requires root privileges.
|
|
1942
|
+
`));
|
|
1943
|
+
console.log(chalk12.cyan(" Solutions:\n"));
|
|
1944
|
+
console.log(chalk12.white(" Option 1: Restart nginx with sudo (recommended for port 80)"));
|
|
1945
|
+
console.log(chalk12.dim(" sudo pkill nginx && sudo nginx\n"));
|
|
1946
|
+
console.log(chalk12.white(" Option 2: Use a non-privileged port (no sudo needed)"));
|
|
1947
|
+
console.log(chalk12.dim(" bindler config set nginxListen 8080"));
|
|
1948
|
+
console.log(chalk12.dim(" bindler apply"));
|
|
1949
|
+
console.log(chalk12.dim(" # Then access via http://hostname:8080\n"));
|
|
1950
|
+
} else {
|
|
1951
|
+
console.log(chalk12.dim(" Try restarting nginx: brew services restart nginx"));
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1860
1954
|
} else {
|
|
1861
1955
|
console.log(chalk12.yellow(" - Skipped nginx reload (--no-reload)"));
|
|
1862
1956
|
}
|
|
1863
1957
|
const isDirectMode = defaults.mode === "direct";
|
|
1864
|
-
if (
|
|
1958
|
+
if (!hasProjects) {
|
|
1959
|
+
} else if (isDirectMode) {
|
|
1865
1960
|
console.log(chalk12.dim("\n - Direct mode: skipping Cloudflare DNS routes"));
|
|
1866
1961
|
} else if (!options.noCloudflare && defaults.applyCloudflareDnsRoutes) {
|
|
1867
1962
|
console.log(chalk12.dim("\nConfiguring Cloudflare DNS routes..."));
|
|
@@ -1887,7 +1982,7 @@ Try running with sudo: ${chalk12.cyan("sudo bindler apply")}`));
|
|
|
1887
1982
|
} else if (options.noCloudflare) {
|
|
1888
1983
|
console.log(chalk12.dim("\n - Skipped Cloudflare DNS routes (--no-cloudflare)"));
|
|
1889
1984
|
}
|
|
1890
|
-
if (isDirectMode && defaults.sslEnabled && options.ssl !== false) {
|
|
1985
|
+
if (hasProjects && isDirectMode && defaults.sslEnabled && options.ssl !== false) {
|
|
1891
1986
|
console.log(chalk12.dim("\nSetting up SSL certificates..."));
|
|
1892
1987
|
const hostnames = config.projects.filter((p) => p.enabled !== false && !p.local).map((p) => p.hostname);
|
|
1893
1988
|
if (hostnames.length === 0) {
|
|
@@ -1917,11 +2012,15 @@ Try running with sudo: ${chalk12.cyan("sudo bindler apply")}`));
|
|
|
1917
2012
|
}
|
|
1918
2013
|
}
|
|
1919
2014
|
console.log(chalk12.green("\n\u2713 Configuration applied successfully!"));
|
|
1920
|
-
|
|
2015
|
+
if (hasProjects) {
|
|
2016
|
+
console.log(chalk12.dim(`
|
|
1921
2017
|
${config.projects.length} project(s) configured:`));
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2018
|
+
for (const project of config.projects) {
|
|
2019
|
+
const status = project.enabled !== false ? chalk12.green("enabled") : chalk12.yellow("disabled");
|
|
2020
|
+
console.log(chalk12.dim(` - ${project.name} \u2192 ${project.hostname} (${status})`));
|
|
2021
|
+
}
|
|
2022
|
+
} else {
|
|
2023
|
+
console.log(chalk12.dim("\nNo projects configured. Nginx config cleared."));
|
|
1925
2024
|
}
|
|
1926
2025
|
}
|
|
1927
2026
|
|
|
@@ -2234,7 +2333,7 @@ async function infoCommand() {
|
|
|
2234
2333
|
`));
|
|
2235
2334
|
console.log(chalk15.white(" Manage multiple projects behind Cloudflare Tunnel"));
|
|
2236
2335
|
console.log(chalk15.white(" with Nginx and PM2\n"));
|
|
2237
|
-
console.log(chalk15.dim(" Version: ") + chalk15.white("1.
|
|
2336
|
+
console.log(chalk15.dim(" Version: ") + chalk15.white("1.6.0"));
|
|
2238
2337
|
console.log(chalk15.dim(" Author: ") + chalk15.white("alfaoz"));
|
|
2239
2338
|
console.log(chalk15.dim(" License: ") + chalk15.white("MIT"));
|
|
2240
2339
|
console.log(chalk15.dim(" GitHub: ") + chalk15.cyan("https://github.com/alfaoz/bindler"));
|
|
@@ -2699,12 +2798,18 @@ async function initCommand() {
|
|
|
2699
2798
|
}
|
|
2700
2799
|
}
|
|
2701
2800
|
console.log(chalk18.bold("\n1. Choose your setup:\n"));
|
|
2801
|
+
const hasCloudflared = isCloudflaredInstalled();
|
|
2802
|
+
const defaultMode = hasCloudflared ? "tunnel" : "direct";
|
|
2803
|
+
if (!hasCloudflared) {
|
|
2804
|
+
console.log(chalk18.dim(" cloudflared not detected - suggesting direct mode for VPS\n"));
|
|
2805
|
+
}
|
|
2702
2806
|
const { mode } = await inquirer4.prompt([
|
|
2703
2807
|
{
|
|
2704
2808
|
type: "list",
|
|
2705
2809
|
name: "mode",
|
|
2706
2810
|
message: "How will you expose your projects?",
|
|
2707
|
-
|
|
2811
|
+
default: defaultMode,
|
|
2812
|
+
choices: hasCloudflared ? [
|
|
2708
2813
|
{
|
|
2709
2814
|
name: "Cloudflare Tunnel (recommended for home servers)",
|
|
2710
2815
|
value: "tunnel"
|
|
@@ -2717,6 +2822,19 @@ async function initCommand() {
|
|
|
2717
2822
|
name: "Local only (development, no internet access)",
|
|
2718
2823
|
value: "local"
|
|
2719
2824
|
}
|
|
2825
|
+
] : [
|
|
2826
|
+
{
|
|
2827
|
+
name: "Direct (VPS with public IP, port 80/443) - recommended",
|
|
2828
|
+
value: "direct"
|
|
2829
|
+
},
|
|
2830
|
+
{
|
|
2831
|
+
name: "Cloudflare Tunnel (requires cloudflared)",
|
|
2832
|
+
value: "tunnel"
|
|
2833
|
+
},
|
|
2834
|
+
{
|
|
2835
|
+
name: "Local only (development, no internet access)",
|
|
2836
|
+
value: "local"
|
|
2837
|
+
}
|
|
2720
2838
|
]
|
|
2721
2839
|
}
|
|
2722
2840
|
]);
|
|
@@ -3822,8 +3940,89 @@ Process exited with code ${code}`));
|
|
|
3822
3940
|
});
|
|
3823
3941
|
}
|
|
3824
3942
|
|
|
3825
|
-
// src/
|
|
3943
|
+
// src/commands/config.ts
|
|
3826
3944
|
import chalk29 from "chalk";
|
|
3945
|
+
var VALID_KEYS = [
|
|
3946
|
+
"nginxListen",
|
|
3947
|
+
"projectsRoot",
|
|
3948
|
+
"tunnelName",
|
|
3949
|
+
"mode",
|
|
3950
|
+
"applyCloudflareDnsRoutes",
|
|
3951
|
+
"sslEnabled",
|
|
3952
|
+
"sslEmail"
|
|
3953
|
+
];
|
|
3954
|
+
async function configCommand(action, key, value) {
|
|
3955
|
+
if (!action || action === "list") {
|
|
3956
|
+
const defaults = getDefaults();
|
|
3957
|
+
console.log(chalk29.blue("Current configuration:\n"));
|
|
3958
|
+
for (const [k, v] of Object.entries(defaults)) {
|
|
3959
|
+
console.log(` ${chalk29.cyan(k)}: ${chalk29.white(String(v))}`);
|
|
3960
|
+
}
|
|
3961
|
+
console.log(chalk29.dim("\nUse `bindler config set <key> <value>` to change a setting."));
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3964
|
+
if (action === "get") {
|
|
3965
|
+
if (!key) {
|
|
3966
|
+
console.error(chalk29.red("Usage: bindler config get <key>"));
|
|
3967
|
+
console.log(chalk29.dim(`
|
|
3968
|
+
Available keys: ${VALID_KEYS.join(", ")}`));
|
|
3969
|
+
process.exit(1);
|
|
3970
|
+
}
|
|
3971
|
+
const defaults = getDefaults();
|
|
3972
|
+
const configKey = key;
|
|
3973
|
+
if (!(configKey in defaults)) {
|
|
3974
|
+
console.error(chalk29.red(`Unknown config key: ${key}`));
|
|
3975
|
+
console.log(chalk29.dim(`
|
|
3976
|
+
Available keys: ${VALID_KEYS.join(", ")}`));
|
|
3977
|
+
process.exit(1);
|
|
3978
|
+
}
|
|
3979
|
+
console.log(defaults[configKey]);
|
|
3980
|
+
return;
|
|
3981
|
+
}
|
|
3982
|
+
if (action === "set") {
|
|
3983
|
+
if (!key || value === void 0) {
|
|
3984
|
+
console.error(chalk29.red("Usage: bindler config set <key> <value>"));
|
|
3985
|
+
console.log(chalk29.dim(`
|
|
3986
|
+
Available keys: ${VALID_KEYS.join(", ")}`));
|
|
3987
|
+
console.log(chalk29.dim("\nExamples:"));
|
|
3988
|
+
console.log(chalk29.dim(" bindler config set nginxListen 8080"));
|
|
3989
|
+
console.log(chalk29.dim(" bindler config set mode tunnel"));
|
|
3990
|
+
process.exit(1);
|
|
3991
|
+
}
|
|
3992
|
+
const configKey = key;
|
|
3993
|
+
if (!VALID_KEYS.includes(configKey)) {
|
|
3994
|
+
console.error(chalk29.red(`Unknown config key: ${key}`));
|
|
3995
|
+
console.log(chalk29.dim(`
|
|
3996
|
+
Available keys: ${VALID_KEYS.join(", ")}`));
|
|
3997
|
+
process.exit(1);
|
|
3998
|
+
}
|
|
3999
|
+
const config = readConfig();
|
|
4000
|
+
let parsedValue = value;
|
|
4001
|
+
if (configKey === "applyCloudflareDnsRoutes" || configKey === "sslEnabled") {
|
|
4002
|
+
parsedValue = value === "true" || value === "1";
|
|
4003
|
+
} else if (configKey === "mode") {
|
|
4004
|
+
if (value !== "tunnel" && value !== "direct") {
|
|
4005
|
+
console.error(chalk29.red('Mode must be "tunnel" or "direct"'));
|
|
4006
|
+
process.exit(1);
|
|
4007
|
+
}
|
|
4008
|
+
parsedValue = value;
|
|
4009
|
+
}
|
|
4010
|
+
config.defaults[configKey] = parsedValue;
|
|
4011
|
+
writeConfig(config);
|
|
4012
|
+
console.log(chalk29.green(`\u2713 Set ${key} = ${parsedValue}`));
|
|
4013
|
+
console.log(chalk29.dim("\nRun `bindler apply` to apply changes."));
|
|
4014
|
+
return;
|
|
4015
|
+
}
|
|
4016
|
+
console.error(chalk29.red(`Unknown action: ${action}`));
|
|
4017
|
+
console.log(chalk29.dim("\nUsage:"));
|
|
4018
|
+
console.log(chalk29.dim(" bindler config # list all settings"));
|
|
4019
|
+
console.log(chalk29.dim(" bindler config get <key> # get a setting"));
|
|
4020
|
+
console.log(chalk29.dim(" bindler config set <key> <value> # set a setting"));
|
|
4021
|
+
process.exit(1);
|
|
4022
|
+
}
|
|
4023
|
+
|
|
4024
|
+
// src/lib/update-check.ts
|
|
4025
|
+
import chalk30 from "chalk";
|
|
3827
4026
|
import { existsSync as existsSync14, readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5 } from "fs";
|
|
3828
4027
|
import { join as join9 } from "path";
|
|
3829
4028
|
import { homedir as homedir4 } from "os";
|
|
@@ -3876,28 +4075,28 @@ async function checkForUpdates() {
|
|
|
3876
4075
|
const cache = readCache();
|
|
3877
4076
|
const now = Date.now();
|
|
3878
4077
|
if (now - cache.lastCheck < CHECK_INTERVAL) {
|
|
3879
|
-
if (cache.latestVersion && compareVersions("1.
|
|
4078
|
+
if (cache.latestVersion && compareVersions("1.6.0", cache.latestVersion) < 0) {
|
|
3880
4079
|
showUpdateMessage(cache.latestVersion);
|
|
3881
4080
|
}
|
|
3882
4081
|
return;
|
|
3883
4082
|
}
|
|
3884
4083
|
fetchLatestVersion().then((latestVersion) => {
|
|
3885
4084
|
writeCache({ lastCheck: now, latestVersion });
|
|
3886
|
-
if (latestVersion && compareVersions("1.
|
|
4085
|
+
if (latestVersion && compareVersions("1.6.0", latestVersion) < 0) {
|
|
3887
4086
|
showUpdateMessage(latestVersion);
|
|
3888
4087
|
}
|
|
3889
4088
|
});
|
|
3890
4089
|
}
|
|
3891
4090
|
function showUpdateMessage(latestVersion) {
|
|
3892
4091
|
console.log("");
|
|
3893
|
-
console.log(
|
|
3894
|
-
console.log(
|
|
4092
|
+
console.log(chalk30.yellow(` Update available: ${"1.6.0"} \u2192 ${latestVersion}`));
|
|
4093
|
+
console.log(chalk30.dim(` Run: npm update -g bindler`));
|
|
3895
4094
|
console.log("");
|
|
3896
4095
|
}
|
|
3897
4096
|
|
|
3898
4097
|
// src/cli.ts
|
|
3899
4098
|
var program = new Command();
|
|
3900
|
-
program.name("bindler").description("Manage multiple projects behind Cloudflare Tunnel with Nginx and PM2").version("1.
|
|
4099
|
+
program.name("bindler").description("Manage multiple projects behind Cloudflare Tunnel with Nginx and PM2").version("1.6.0");
|
|
3901
4100
|
program.hook("preAction", async () => {
|
|
3902
4101
|
try {
|
|
3903
4102
|
initConfig();
|
|
@@ -3916,73 +4115,73 @@ program.command("status").description("Show detailed status of all projects").ac
|
|
|
3916
4115
|
});
|
|
3917
4116
|
program.command("start [name]").description("Start an npm project with PM2").option("-a, --all", "Start all npm projects").action(async (name, options) => {
|
|
3918
4117
|
if (!name && !options.all) {
|
|
3919
|
-
console.log(
|
|
3920
|
-
console.log(
|
|
3921
|
-
console.log(
|
|
3922
|
-
console.log(
|
|
4118
|
+
console.log(chalk31.red("Usage: bindler start <name> or bindler start --all"));
|
|
4119
|
+
console.log(chalk31.dim("\nExamples:"));
|
|
4120
|
+
console.log(chalk31.dim(" bindler start myapp"));
|
|
4121
|
+
console.log(chalk31.dim(" bindler start --all # start all npm projects"));
|
|
3923
4122
|
process.exit(1);
|
|
3924
4123
|
}
|
|
3925
4124
|
await startCommand(name, options);
|
|
3926
4125
|
});
|
|
3927
4126
|
program.command("stop [name]").description("Stop an npm project").option("-a, --all", "Stop all npm projects").action(async (name, options) => {
|
|
3928
4127
|
if (!name && !options.all) {
|
|
3929
|
-
console.log(
|
|
3930
|
-
console.log(
|
|
3931
|
-
console.log(
|
|
3932
|
-
console.log(
|
|
4128
|
+
console.log(chalk31.red("Usage: bindler stop <name> or bindler stop --all"));
|
|
4129
|
+
console.log(chalk31.dim("\nExamples:"));
|
|
4130
|
+
console.log(chalk31.dim(" bindler stop myapp"));
|
|
4131
|
+
console.log(chalk31.dim(" bindler stop --all # stop all npm projects"));
|
|
3933
4132
|
process.exit(1);
|
|
3934
4133
|
}
|
|
3935
4134
|
await stopCommand(name, options);
|
|
3936
4135
|
});
|
|
3937
4136
|
program.command("restart [name]").description("Restart an npm project").option("-a, --all", "Restart all npm projects").action(async (name, options) => {
|
|
3938
4137
|
if (!name && !options.all) {
|
|
3939
|
-
console.log(
|
|
3940
|
-
console.log(
|
|
3941
|
-
console.log(
|
|
3942
|
-
console.log(
|
|
4138
|
+
console.log(chalk31.red("Usage: bindler restart <name> or bindler restart --all"));
|
|
4139
|
+
console.log(chalk31.dim("\nExamples:"));
|
|
4140
|
+
console.log(chalk31.dim(" bindler restart myapp"));
|
|
4141
|
+
console.log(chalk31.dim(" bindler restart --all # restart all npm projects"));
|
|
3943
4142
|
process.exit(1);
|
|
3944
4143
|
}
|
|
3945
4144
|
await restartCommand(name, options);
|
|
3946
4145
|
});
|
|
3947
4146
|
program.command("logs [name]").description("Show logs for an npm project").option("-f, --follow", "Follow log output").option("-l, --lines <n>", "Number of lines to show", "200").action(async (name, options) => {
|
|
3948
4147
|
if (!name) {
|
|
3949
|
-
console.log(
|
|
3950
|
-
console.log(
|
|
3951
|
-
console.log(
|
|
3952
|
-
console.log(
|
|
3953
|
-
console.log(
|
|
4148
|
+
console.log(chalk31.red("Usage: bindler logs <name>"));
|
|
4149
|
+
console.log(chalk31.dim("\nExamples:"));
|
|
4150
|
+
console.log(chalk31.dim(" bindler logs myapp"));
|
|
4151
|
+
console.log(chalk31.dim(" bindler logs myapp --follow"));
|
|
4152
|
+
console.log(chalk31.dim(" bindler logs myapp --lines 500"));
|
|
3954
4153
|
process.exit(1);
|
|
3955
4154
|
}
|
|
3956
4155
|
await logsCommand(name, { ...options, lines: parseInt(options.lines, 10) });
|
|
3957
4156
|
});
|
|
3958
4157
|
program.command("update [name]").description("Update project configuration").option("-h, --hostname <hostname>", "New hostname").option("--port <port>", "New port number").option("-s, --start <command>", "New start command").option("-p, --path <path>", "New project path").option("-e, --env <vars...>", "Environment variables (KEY=value)").option("--enable", "Enable the project").option("--disable", "Disable the project").action(async (name, options) => {
|
|
3959
4158
|
if (!name) {
|
|
3960
|
-
console.log(
|
|
3961
|
-
console.log(
|
|
3962
|
-
console.log(
|
|
3963
|
-
console.log(
|
|
3964
|
-
console.log(
|
|
4159
|
+
console.log(chalk31.red("Usage: bindler update <name> [options]"));
|
|
4160
|
+
console.log(chalk31.dim("\nExamples:"));
|
|
4161
|
+
console.log(chalk31.dim(" bindler update myapp --hostname newapp.example.com"));
|
|
4162
|
+
console.log(chalk31.dim(" bindler update myapp --port 4000"));
|
|
4163
|
+
console.log(chalk31.dim(" bindler update myapp --disable"));
|
|
3965
4164
|
process.exit(1);
|
|
3966
4165
|
}
|
|
3967
4166
|
await updateCommand(name, options);
|
|
3968
4167
|
});
|
|
3969
4168
|
program.command("edit [name]").description("Edit project configuration in $EDITOR").action(async (name) => {
|
|
3970
4169
|
if (!name) {
|
|
3971
|
-
console.log(
|
|
3972
|
-
console.log(
|
|
3973
|
-
console.log(
|
|
3974
|
-
console.log(
|
|
4170
|
+
console.log(chalk31.red("Usage: bindler edit <name>"));
|
|
4171
|
+
console.log(chalk31.dim("\nOpens the project config in your $EDITOR"));
|
|
4172
|
+
console.log(chalk31.dim("\nExample:"));
|
|
4173
|
+
console.log(chalk31.dim(" bindler edit myapp"));
|
|
3975
4174
|
process.exit(1);
|
|
3976
4175
|
}
|
|
3977
4176
|
await editCommand(name);
|
|
3978
4177
|
});
|
|
3979
4178
|
program.command("remove [name]").alias("rm").description("Remove a project from registry").option("-f, --force", "Skip confirmation").option("--apply", "Apply nginx config after removing").action(async (name, options) => {
|
|
3980
4179
|
if (!name) {
|
|
3981
|
-
console.log(
|
|
3982
|
-
console.log(
|
|
3983
|
-
console.log(
|
|
3984
|
-
console.log(
|
|
3985
|
-
console.log(
|
|
4180
|
+
console.log(chalk31.red("Usage: bindler remove <name>"));
|
|
4181
|
+
console.log(chalk31.dim("\nExamples:"));
|
|
4182
|
+
console.log(chalk31.dim(" bindler remove myapp"));
|
|
4183
|
+
console.log(chalk31.dim(" bindler remove myapp --force # skip confirmation"));
|
|
4184
|
+
console.log(chalk31.dim(" bindler rm myapp # alias"));
|
|
3986
4185
|
process.exit(1);
|
|
3987
4186
|
}
|
|
3988
4187
|
await removeCommand(name, options);
|
|
@@ -4001,10 +4200,10 @@ program.command("info").description("Show bindler information and stats").action
|
|
|
4001
4200
|
});
|
|
4002
4201
|
program.command("check [hostname]").description("Check DNS propagation and HTTP accessibility for a hostname").option("-v, --verbose", "Show verbose output").action(async (hostname, options) => {
|
|
4003
4202
|
if (!hostname) {
|
|
4004
|
-
console.log(
|
|
4005
|
-
console.log(
|
|
4006
|
-
console.log(
|
|
4007
|
-
console.log(
|
|
4203
|
+
console.log(chalk31.red("Usage: bindler check <hostname>"));
|
|
4204
|
+
console.log(chalk31.dim("\nExamples:"));
|
|
4205
|
+
console.log(chalk31.dim(" bindler check myapp.example.com"));
|
|
4206
|
+
console.log(chalk31.dim(" bindler check myapp # uses project name"));
|
|
4008
4207
|
process.exit(1);
|
|
4009
4208
|
}
|
|
4010
4209
|
await checkCommand(hostname, options);
|
|
@@ -4048,5 +4247,8 @@ program.command("clone [source] [new-name]").description("Clone a project config
|
|
|
4048
4247
|
program.command("dev [name]").description("Start a project in development mode with hot reload").option("-p, --port <port>", "Override port number").option("-h, --hostname <hostname>", "Override hostname").action(async (name, options) => {
|
|
4049
4248
|
await devCommand(name, options);
|
|
4050
4249
|
});
|
|
4250
|
+
program.command("config [action] [key] [value]").description("View or modify bindler configuration").action(async (action, key, value) => {
|
|
4251
|
+
await configCommand(action, key, value);
|
|
4252
|
+
});
|
|
4051
4253
|
program.parse();
|
|
4052
4254
|
//# sourceMappingURL=cli.js.map
|