bindler 1.4.1 → 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 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 chalk30 from "chalk";
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
- const envFlags = envVars.length > 0 ? envVars.map((e) => `--env ${e}`).join(" ") : "";
584
- command = `pm2 start --name "${pm2Name}" --cwd "${project.path}" ${envFlags} -- bash -lc "${project.start}"`;
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
- const suggestedPort = yamlDefaults.port || findAvailablePort();
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
- project.port = options.port || findAvailablePort();
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
  }
@@ -1876,6 +1931,26 @@ Try running with sudo: ${chalk12.cyan("sudo bindler apply")}`));
1876
1931
  process.exit(1);
1877
1932
  }
1878
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
+ }
1879
1954
  } else {
1880
1955
  console.log(chalk12.yellow(" - Skipped nginx reload (--no-reload)"));
1881
1956
  }
@@ -2258,7 +2333,7 @@ async function infoCommand() {
2258
2333
  `));
2259
2334
  console.log(chalk15.white(" Manage multiple projects behind Cloudflare Tunnel"));
2260
2335
  console.log(chalk15.white(" with Nginx and PM2\n"));
2261
- console.log(chalk15.dim(" Version: ") + chalk15.white("1.4.1"));
2336
+ console.log(chalk15.dim(" Version: ") + chalk15.white("1.6.0"));
2262
2337
  console.log(chalk15.dim(" Author: ") + chalk15.white("alfaoz"));
2263
2338
  console.log(chalk15.dim(" License: ") + chalk15.white("MIT"));
2264
2339
  console.log(chalk15.dim(" GitHub: ") + chalk15.cyan("https://github.com/alfaoz/bindler"));
@@ -2723,12 +2798,18 @@ async function initCommand() {
2723
2798
  }
2724
2799
  }
2725
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
+ }
2726
2806
  const { mode } = await inquirer4.prompt([
2727
2807
  {
2728
2808
  type: "list",
2729
2809
  name: "mode",
2730
2810
  message: "How will you expose your projects?",
2731
- choices: [
2811
+ default: defaultMode,
2812
+ choices: hasCloudflared ? [
2732
2813
  {
2733
2814
  name: "Cloudflare Tunnel (recommended for home servers)",
2734
2815
  value: "tunnel"
@@ -2741,6 +2822,19 @@ async function initCommand() {
2741
2822
  name: "Local only (development, no internet access)",
2742
2823
  value: "local"
2743
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
+ }
2744
2838
  ]
2745
2839
  }
2746
2840
  ]);
@@ -3846,8 +3940,89 @@ Process exited with code ${code}`));
3846
3940
  });
3847
3941
  }
3848
3942
 
3849
- // src/lib/update-check.ts
3943
+ // src/commands/config.ts
3850
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";
3851
4026
  import { existsSync as existsSync14, readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5 } from "fs";
3852
4027
  import { join as join9 } from "path";
3853
4028
  import { homedir as homedir4 } from "os";
@@ -3900,28 +4075,28 @@ async function checkForUpdates() {
3900
4075
  const cache = readCache();
3901
4076
  const now = Date.now();
3902
4077
  if (now - cache.lastCheck < CHECK_INTERVAL) {
3903
- if (cache.latestVersion && compareVersions("1.4.1", cache.latestVersion) < 0) {
4078
+ if (cache.latestVersion && compareVersions("1.6.0", cache.latestVersion) < 0) {
3904
4079
  showUpdateMessage(cache.latestVersion);
3905
4080
  }
3906
4081
  return;
3907
4082
  }
3908
4083
  fetchLatestVersion().then((latestVersion) => {
3909
4084
  writeCache({ lastCheck: now, latestVersion });
3910
- if (latestVersion && compareVersions("1.4.1", latestVersion) < 0) {
4085
+ if (latestVersion && compareVersions("1.6.0", latestVersion) < 0) {
3911
4086
  showUpdateMessage(latestVersion);
3912
4087
  }
3913
4088
  });
3914
4089
  }
3915
4090
  function showUpdateMessage(latestVersion) {
3916
4091
  console.log("");
3917
- console.log(chalk29.yellow(` Update available: ${"1.4.1"} \u2192 ${latestVersion}`));
3918
- console.log(chalk29.dim(` Run: npm update -g bindler`));
4092
+ console.log(chalk30.yellow(` Update available: ${"1.6.0"} \u2192 ${latestVersion}`));
4093
+ console.log(chalk30.dim(` Run: npm update -g bindler`));
3919
4094
  console.log("");
3920
4095
  }
3921
4096
 
3922
4097
  // src/cli.ts
3923
4098
  var program = new Command();
3924
- program.name("bindler").description("Manage multiple projects behind Cloudflare Tunnel with Nginx and PM2").version("1.4.1");
4099
+ program.name("bindler").description("Manage multiple projects behind Cloudflare Tunnel with Nginx and PM2").version("1.6.0");
3925
4100
  program.hook("preAction", async () => {
3926
4101
  try {
3927
4102
  initConfig();
@@ -3940,73 +4115,73 @@ program.command("status").description("Show detailed status of all projects").ac
3940
4115
  });
3941
4116
  program.command("start [name]").description("Start an npm project with PM2").option("-a, --all", "Start all npm projects").action(async (name, options) => {
3942
4117
  if (!name && !options.all) {
3943
- console.log(chalk30.red("Usage: bindler start <name> or bindler start --all"));
3944
- console.log(chalk30.dim("\nExamples:"));
3945
- console.log(chalk30.dim(" bindler start myapp"));
3946
- console.log(chalk30.dim(" bindler start --all # start all npm projects"));
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"));
3947
4122
  process.exit(1);
3948
4123
  }
3949
4124
  await startCommand(name, options);
3950
4125
  });
3951
4126
  program.command("stop [name]").description("Stop an npm project").option("-a, --all", "Stop all npm projects").action(async (name, options) => {
3952
4127
  if (!name && !options.all) {
3953
- console.log(chalk30.red("Usage: bindler stop <name> or bindler stop --all"));
3954
- console.log(chalk30.dim("\nExamples:"));
3955
- console.log(chalk30.dim(" bindler stop myapp"));
3956
- console.log(chalk30.dim(" bindler stop --all # stop all npm projects"));
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"));
3957
4132
  process.exit(1);
3958
4133
  }
3959
4134
  await stopCommand(name, options);
3960
4135
  });
3961
4136
  program.command("restart [name]").description("Restart an npm project").option("-a, --all", "Restart all npm projects").action(async (name, options) => {
3962
4137
  if (!name && !options.all) {
3963
- console.log(chalk30.red("Usage: bindler restart <name> or bindler restart --all"));
3964
- console.log(chalk30.dim("\nExamples:"));
3965
- console.log(chalk30.dim(" bindler restart myapp"));
3966
- console.log(chalk30.dim(" bindler restart --all # restart all npm projects"));
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"));
3967
4142
  process.exit(1);
3968
4143
  }
3969
4144
  await restartCommand(name, options);
3970
4145
  });
3971
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) => {
3972
4147
  if (!name) {
3973
- console.log(chalk30.red("Usage: bindler logs <name>"));
3974
- console.log(chalk30.dim("\nExamples:"));
3975
- console.log(chalk30.dim(" bindler logs myapp"));
3976
- console.log(chalk30.dim(" bindler logs myapp --follow"));
3977
- console.log(chalk30.dim(" bindler logs myapp --lines 500"));
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"));
3978
4153
  process.exit(1);
3979
4154
  }
3980
4155
  await logsCommand(name, { ...options, lines: parseInt(options.lines, 10) });
3981
4156
  });
3982
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) => {
3983
4158
  if (!name) {
3984
- console.log(chalk30.red("Usage: bindler update <name> [options]"));
3985
- console.log(chalk30.dim("\nExamples:"));
3986
- console.log(chalk30.dim(" bindler update myapp --hostname newapp.example.com"));
3987
- console.log(chalk30.dim(" bindler update myapp --port 4000"));
3988
- console.log(chalk30.dim(" bindler update myapp --disable"));
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"));
3989
4164
  process.exit(1);
3990
4165
  }
3991
4166
  await updateCommand(name, options);
3992
4167
  });
3993
4168
  program.command("edit [name]").description("Edit project configuration in $EDITOR").action(async (name) => {
3994
4169
  if (!name) {
3995
- console.log(chalk30.red("Usage: bindler edit <name>"));
3996
- console.log(chalk30.dim("\nOpens the project config in your $EDITOR"));
3997
- console.log(chalk30.dim("\nExample:"));
3998
- console.log(chalk30.dim(" bindler edit myapp"));
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"));
3999
4174
  process.exit(1);
4000
4175
  }
4001
4176
  await editCommand(name);
4002
4177
  });
4003
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) => {
4004
4179
  if (!name) {
4005
- console.log(chalk30.red("Usage: bindler remove <name>"));
4006
- console.log(chalk30.dim("\nExamples:"));
4007
- console.log(chalk30.dim(" bindler remove myapp"));
4008
- console.log(chalk30.dim(" bindler remove myapp --force # skip confirmation"));
4009
- console.log(chalk30.dim(" bindler rm myapp # alias"));
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"));
4010
4185
  process.exit(1);
4011
4186
  }
4012
4187
  await removeCommand(name, options);
@@ -4025,10 +4200,10 @@ program.command("info").description("Show bindler information and stats").action
4025
4200
  });
4026
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) => {
4027
4202
  if (!hostname) {
4028
- console.log(chalk30.red("Usage: bindler check <hostname>"));
4029
- console.log(chalk30.dim("\nExamples:"));
4030
- console.log(chalk30.dim(" bindler check myapp.example.com"));
4031
- console.log(chalk30.dim(" bindler check myapp # uses project name"));
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"));
4032
4207
  process.exit(1);
4033
4208
  }
4034
4209
  await checkCommand(hostname, options);
@@ -4072,5 +4247,8 @@ program.command("clone [source] [new-name]").description("Clone a project config
4072
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) => {
4073
4248
  await devCommand(name, options);
4074
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
+ });
4075
4253
  program.parse();
4076
4254
  //# sourceMappingURL=cli.js.map