@lead-routing/cli 0.1.6 → 0.1.8

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.
Files changed (2) hide show
  1. package/dist/index.js +41 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -676,11 +676,20 @@ async function checkRemotePort(ssh, port) {
676
676
  const { stdout: occupant } = await ssh.execSilent(
677
677
  `ss -tlnp 2>/dev/null | grep ':${port} ' | head -1 || echo "unknown process"`
678
678
  );
679
+ const occupantStr = occupant.trim();
680
+ if (occupantStr.includes("docker-proxy")) {
681
+ return {
682
+ ok: false,
683
+ warn: true,
684
+ label: `Port ${port} is held by an existing Docker container (docker-proxy).
685
+ Docker Compose will reclaim it when the new stack starts \u2014 continuing.`
686
+ };
687
+ }
679
688
  return {
680
689
  ok: false,
681
- // Hard error — Caddy cannot get TLS certs without these ports
690
+ // Hard error — non-Docker process; Caddy cannot get TLS certs without these ports
682
691
  label: `Port ${port} is occupied and could not be freed automatically.
683
- Occupant: ${occupant.trim()}
692
+ Occupant: ${occupantStr}
684
693
  Stop the conflicting process on the server, then re-run:
685
694
  lead-routing init`
686
695
  };
@@ -893,9 +902,9 @@ async function seedAdminUser(localDir, localPort, adminEmail, adminPassword) {
893
902
  const safeEmail = adminEmail.replace(/'/g, "''");
894
903
  const safeWebhookSecret = webhookSecret.replace(/'/g, "''");
895
904
  const sql = `
896
- -- Create initial organisation if none exists
897
- INSERT INTO organizations (id, "webhookSecret", "createdAt", "updatedAt")
898
- SELECT gen_random_uuid(), '${safeWebhookSecret}', NOW(), NOW()
905
+ -- Create initial organisation if none exists (self-hosted defaults: PAID plan, unlimited seats/quota)
906
+ INSERT INTO organizations (id, "webhookSecret", plan, "seatsPurchased", "routingQuota", "isActive", "createdAt", "updatedAt")
907
+ SELECT gen_random_uuid(), '${safeWebhookSecret}', 'PAID', 9999, 999999, true, NOW(), NOW()
899
908
  WHERE NOT EXISTS (SELECT 1 FROM organizations);
900
909
 
901
910
  -- Create admin AppUser under the first org (idempotent)
@@ -1496,7 +1505,7 @@ async function runInit(options = {}) {
1496
1505
  log9.step("Step 1/9 Checking local prerequisites");
1497
1506
  await checkPrerequisites();
1498
1507
  log9.step("Step 2/9 SSH connection");
1499
- const sshCfg = await collectSshConfig({
1508
+ let sshCfg = await collectSshConfig({
1500
1509
  sshPort: options.sshPort,
1501
1510
  sshUser: options.sshUser,
1502
1511
  sshKey: options.sshKey,
@@ -1507,9 +1516,31 @@ async function runInit(options = {}) {
1507
1516
  await ssh.connect(sshCfg);
1508
1517
  log9.success(`Connected to ${sshCfg.host}`);
1509
1518
  } catch (err) {
1510
- log9.error(`SSH connection failed: ${String(err)}`);
1511
- log9.info("Fix your SSH credentials and re-run `lead-routing init`.");
1512
- process.exit(1);
1519
+ if (sshCfg.privateKeyPath && !sshCfg.password) {
1520
+ log9.warn(`Key auth failed \u2014 the server rejected the SSH key`);
1521
+ log9.info("Falling back to password auth\u2026");
1522
+ const pw = await promptPassword({
1523
+ message: `SSH password for ${sshCfg.username}@${sshCfg.host}`,
1524
+ validate: (v) => !v ? "Required" : void 0
1525
+ });
1526
+ if (isCancel4(pw)) {
1527
+ cancel3("Setup cancelled.");
1528
+ process.exit(0);
1529
+ }
1530
+ sshCfg = { ...sshCfg, privateKeyPath: void 0, password: pw };
1531
+ try {
1532
+ await ssh.connect(sshCfg);
1533
+ log9.success(`Connected to ${sshCfg.host}`);
1534
+ } catch (err2) {
1535
+ log9.error(`SSH connection failed: ${String(err2)}`);
1536
+ log9.info("Fix your SSH credentials and re-run `lead-routing init`.");
1537
+ process.exit(1);
1538
+ }
1539
+ } else {
1540
+ log9.error(`SSH connection failed: ${String(err)}`);
1541
+ log9.info("Fix your SSH credentials and re-run `lead-routing init`.");
1542
+ process.exit(1);
1543
+ }
1513
1544
  }
1514
1545
  }
1515
1546
  log9.step("Step 3/9 Configuration");
@@ -1976,7 +2007,7 @@ async function runSfdcDeploy() {
1976
2007
 
1977
2008
  // src/index.ts
1978
2009
  var program = new Command();
1979
- program.name("lead-routing").description("Self-hosted Lead Routing \u2014 scaffold, deploy, and manage your installation").version("0.1.0");
2010
+ program.name("lead-routing").description("Self-hosted Lead Routing \u2014 scaffold, deploy, and manage your installation").version("0.1.8");
1980
2011
  program.command("init").description("Interactive setup wizard \u2014 configure and deploy the full Lead Routing stack").option("--dry-run", "Generate config files without connecting or deploying").option("--resume", "Skip to health check using existing lead-routing.json (post-timeout recovery)").option("--sandbox", "Use Salesforce sandbox (test.salesforce.com) instead of production").option("--ssh-port <port>", "SSH port (default: 22)", parseInt).option("--ssh-user <user>", "SSH username (default: root)").option("--ssh-key <path>", "Path to SSH private key (overrides auto-detection)").option("--remote-dir <path>", "Remote install directory (default: ~/lead-routing)").option("--external-db <url>", "Use external PostgreSQL URL instead of managed Docker container").option("--external-redis <url>", "Use external Redis URL instead of managed Docker container").action((opts) => runInit({
1981
2012
  dryRun: opts.dryRun,
1982
2013
  resume: opts.resume,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lead-routing/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Self-hosted deployment CLI for Lead Routing",
5
5
  "homepage": "https://github.com/lead-routing/lead-routing",
6
6
  "keywords": ["salesforce", "lead-routing", "self-hosted", "deployment", "cli"],