@lead-routing/cli 0.1.10 → 0.1.11
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/index.js +39 -77
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69,16 +69,7 @@ async function checkSalesforceCLI() {
|
|
|
69
69
|
// src/steps/collect-ssh-config.ts
|
|
70
70
|
import { existsSync } from "fs";
|
|
71
71
|
import { homedir } from "os";
|
|
72
|
-
import { join } from "path";
|
|
73
72
|
import { text, password, note, log as log2, cancel, isCancel } from "@clack/prompts";
|
|
74
|
-
var DEFAULT_KEYS = [
|
|
75
|
-
join(homedir(), ".ssh", "id_ed25519"),
|
|
76
|
-
join(homedir(), ".ssh", "id_rsa"),
|
|
77
|
-
join(homedir(), ".ssh", "id_ecdsa")
|
|
78
|
-
];
|
|
79
|
-
function detectDefaultKey() {
|
|
80
|
-
return DEFAULT_KEYS.find(existsSync);
|
|
81
|
-
}
|
|
82
73
|
function bail(value) {
|
|
83
74
|
if (isCancel(value)) {
|
|
84
75
|
cancel("Setup cancelled.");
|
|
@@ -88,7 +79,7 @@ function bail(value) {
|
|
|
88
79
|
}
|
|
89
80
|
async function collectSshConfig(opts = {}) {
|
|
90
81
|
note(
|
|
91
|
-
"The CLI will SSH into your server to deploy the full stack.\nYou will need:\n \u2022 Server hostname or IP address\n \u2022 SSH
|
|
82
|
+
"The CLI will SSH into your server to deploy the full stack.\nYou will need:\n \u2022 Server hostname or IP address\n \u2022 SSH password for your server (root access)\n \u2022 A fresh Linux VPS (Ubuntu/Debian/CentOS) \u2014 Docker installed automatically",
|
|
92
83
|
"Server connection"
|
|
93
84
|
);
|
|
94
85
|
const host = await text({
|
|
@@ -108,19 +99,12 @@ async function collectSshConfig(opts = {}) {
|
|
|
108
99
|
privateKeyPath = resolved;
|
|
109
100
|
log2.info(`Using SSH key: ${opts.sshKey}`);
|
|
110
101
|
} else {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const p = await password({
|
|
118
|
-
message: `SSH password for ${opts.sshUser ?? "root"}@${host}`,
|
|
119
|
-
validate: (v) => !v ? "Required" : void 0
|
|
120
|
-
});
|
|
121
|
-
if (isCancel(p)) bail(p);
|
|
122
|
-
pwd = p;
|
|
123
|
-
}
|
|
102
|
+
const p = await password({
|
|
103
|
+
message: `SSH password for ${opts.sshUser ?? "root"}@${host}`,
|
|
104
|
+
validate: (v) => !v ? "Required" : void 0
|
|
105
|
+
});
|
|
106
|
+
if (isCancel(p)) bail(p);
|
|
107
|
+
pwd = p;
|
|
124
108
|
}
|
|
125
109
|
return {
|
|
126
110
|
host,
|
|
@@ -258,7 +242,7 @@ async function collectConfig(opts = {}) {
|
|
|
258
242
|
|
|
259
243
|
// src/steps/generate-files.ts
|
|
260
244
|
import { mkdirSync, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
|
|
261
|
-
import { join as
|
|
245
|
+
import { join as join2, dirname } from "path";
|
|
262
246
|
import { fileURLToPath } from "url";
|
|
263
247
|
import { log as log3 } from "@clack/prompts";
|
|
264
248
|
|
|
@@ -489,9 +473,9 @@ function renderCaddyfile(appUrl, engineUrl) {
|
|
|
489
473
|
|
|
490
474
|
// src/utils/config.ts
|
|
491
475
|
import { readFileSync, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
492
|
-
import { join
|
|
476
|
+
import { join } from "path";
|
|
493
477
|
function getConfigPath(dir) {
|
|
494
|
-
return
|
|
478
|
+
return join(dir, "lead-routing.json");
|
|
495
479
|
}
|
|
496
480
|
function readConfig(dir) {
|
|
497
481
|
const path2 = getConfigPath(dir);
|
|
@@ -506,10 +490,10 @@ function writeConfig(dir, config2) {
|
|
|
506
490
|
writeFileSync(getConfigPath(dir), JSON.stringify(config2, null, 2), "utf8");
|
|
507
491
|
}
|
|
508
492
|
function findInstallDir(startDir = process.cwd()) {
|
|
509
|
-
const candidate =
|
|
493
|
+
const candidate = join(startDir, "lead-routing.json");
|
|
510
494
|
if (existsSync2(candidate)) return startDir;
|
|
511
|
-
const nested =
|
|
512
|
-
if (existsSync2(nested)) return
|
|
495
|
+
const nested = join(startDir, "lead-routing", "lead-routing.json");
|
|
496
|
+
if (existsSync2(nested)) return join(startDir, "lead-routing");
|
|
513
497
|
return null;
|
|
514
498
|
}
|
|
515
499
|
|
|
@@ -517,14 +501,14 @@ function findInstallDir(startDir = process.cwd()) {
|
|
|
517
501
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
518
502
|
function getCliVersion() {
|
|
519
503
|
try {
|
|
520
|
-
const pkg = JSON.parse(readFileSync2(
|
|
504
|
+
const pkg = JSON.parse(readFileSync2(join2(__dirname, "../../package.json"), "utf8"));
|
|
521
505
|
return pkg.version ?? "0.1.0";
|
|
522
506
|
} catch {
|
|
523
507
|
return "0.1.0";
|
|
524
508
|
}
|
|
525
509
|
}
|
|
526
510
|
function generateFiles(cfg, sshCfg) {
|
|
527
|
-
const dir =
|
|
511
|
+
const dir = join2(process.cwd(), "lead-routing");
|
|
528
512
|
mkdirSync(dir, { recursive: true });
|
|
529
513
|
const dockerEngineUrl = `http://engine:3001`;
|
|
530
514
|
const composeContent = renderDockerCompose({
|
|
@@ -532,11 +516,11 @@ function generateFiles(cfg, sshCfg) {
|
|
|
532
516
|
managedRedis: cfg.managedRedis,
|
|
533
517
|
dbPassword: cfg.dbPassword
|
|
534
518
|
});
|
|
535
|
-
const composeFile =
|
|
519
|
+
const composeFile = join2(dir, "docker-compose.yml");
|
|
536
520
|
writeFileSync2(composeFile, composeContent, "utf8");
|
|
537
521
|
log3.success("Generated docker-compose.yml");
|
|
538
522
|
const caddyfileContent = renderCaddyfile(cfg.appUrl, cfg.engineUrl);
|
|
539
|
-
writeFileSync2(
|
|
523
|
+
writeFileSync2(join2(dir, "Caddyfile"), caddyfileContent, "utf8");
|
|
540
524
|
log3.success("Generated Caddyfile");
|
|
541
525
|
const envWebContent = renderEnvWeb({
|
|
542
526
|
appUrl: cfg.appUrl,
|
|
@@ -552,7 +536,7 @@ function generateFiles(cfg, sshCfg) {
|
|
|
552
536
|
resendApiKey: cfg.resendApiKey || void 0,
|
|
553
537
|
feedbackToEmail: cfg.feedbackToEmail || void 0
|
|
554
538
|
});
|
|
555
|
-
const envWeb =
|
|
539
|
+
const envWeb = join2(dir, ".env.web");
|
|
556
540
|
writeFileSync2(envWeb, envWebContent, "utf8");
|
|
557
541
|
log3.success("Generated .env.web");
|
|
558
542
|
const envEngineContent = renderEnvEngine({
|
|
@@ -563,7 +547,7 @@ function generateFiles(cfg, sshCfg) {
|
|
|
563
547
|
sfdcLoginUrl: cfg.sfdcLoginUrl,
|
|
564
548
|
engineWebhookSecret: cfg.engineWebhookSecret
|
|
565
549
|
});
|
|
566
|
-
const envEngine =
|
|
550
|
+
const envEngine = join2(dir, ".env.engine");
|
|
567
551
|
writeFileSync2(envEngine, envEngineContent, "utf8");
|
|
568
552
|
log3.success("Generated .env.engine");
|
|
569
553
|
writeConfig(dir, {
|
|
@@ -732,7 +716,7 @@ async function checkRemotePort(ssh, port) {
|
|
|
732
716
|
}
|
|
733
717
|
|
|
734
718
|
// src/steps/upload-files.ts
|
|
735
|
-
import { join as
|
|
719
|
+
import { join as join3 } from "path";
|
|
736
720
|
import { spinner as spinner3 } from "@clack/prompts";
|
|
737
721
|
async function uploadFiles(ssh, localDir, remoteDir) {
|
|
738
722
|
const s = spinner3();
|
|
@@ -748,7 +732,7 @@ async function uploadFiles(ssh, localDir, remoteDir) {
|
|
|
748
732
|
];
|
|
749
733
|
await ssh.upload(
|
|
750
734
|
filenames.map((f) => ({
|
|
751
|
-
local:
|
|
735
|
+
local: join3(localDir, f),
|
|
752
736
|
remote: `${remoteDir}/${f}`
|
|
753
737
|
}))
|
|
754
738
|
);
|
|
@@ -1039,7 +1023,7 @@ function sleep2(ms) {
|
|
|
1039
1023
|
|
|
1040
1024
|
// src/steps/sfdc-deploy-inline.ts
|
|
1041
1025
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, cpSync, rmSync } from "fs";
|
|
1042
|
-
import { join as
|
|
1026
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
1043
1027
|
import { tmpdir } from "os";
|
|
1044
1028
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1045
1029
|
import { spinner as spinner7, log as log7 } from "@clack/prompts";
|
|
@@ -1070,10 +1054,10 @@ async function sfdcDeployInline(params) {
|
|
|
1070
1054
|
}
|
|
1071
1055
|
}
|
|
1072
1056
|
s.start("Copying Salesforce package\u2026");
|
|
1073
|
-
const inDist =
|
|
1074
|
-
const nextToDist =
|
|
1057
|
+
const inDist = join5(__dirname3, "sfdc-package");
|
|
1058
|
+
const nextToDist = join5(__dirname3, "..", "sfdc-package");
|
|
1075
1059
|
const bundledPkg = existsSync4(inDist) ? inDist : nextToDist;
|
|
1076
|
-
const destPkg =
|
|
1060
|
+
const destPkg = join5(installDir ?? tmpdir(), "lead-routing-sfdc-package");
|
|
1077
1061
|
if (!existsSync4(bundledPkg)) {
|
|
1078
1062
|
s.stop("sfdc-package not found in CLI bundle");
|
|
1079
1063
|
throw new Error(
|
|
@@ -1084,7 +1068,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1084
1068
|
if (existsSync4(destPkg)) rmSync(destPkg, { recursive: true, force: true });
|
|
1085
1069
|
cpSync(bundledPkg, destPkg, { recursive: true });
|
|
1086
1070
|
s.stop("Package copied");
|
|
1087
|
-
const ncPath =
|
|
1071
|
+
const ncPath = join5(
|
|
1088
1072
|
destPkg,
|
|
1089
1073
|
"force-app",
|
|
1090
1074
|
"main",
|
|
@@ -1096,7 +1080,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1096
1080
|
const nc = patchXml(readFileSync4(ncPath, "utf8"), "endpoint", engineUrl);
|
|
1097
1081
|
writeFileSync3(ncPath, nc, "utf8");
|
|
1098
1082
|
}
|
|
1099
|
-
const rssEnginePath =
|
|
1083
|
+
const rssEnginePath = join5(
|
|
1100
1084
|
destPkg,
|
|
1101
1085
|
"force-app",
|
|
1102
1086
|
"main",
|
|
@@ -1109,7 +1093,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1109
1093
|
rss = patchXml(rss, "description", "Lead Router Engine endpoint");
|
|
1110
1094
|
writeFileSync3(rssEnginePath, rss, "utf8");
|
|
1111
1095
|
}
|
|
1112
|
-
const rssAppPath =
|
|
1096
|
+
const rssAppPath = join5(
|
|
1113
1097
|
destPkg,
|
|
1114
1098
|
"force-app",
|
|
1115
1099
|
"main",
|
|
@@ -1541,7 +1525,7 @@ async function runInit(options = {}) {
|
|
|
1541
1525
|
log9.step("Step 1/9 Checking local prerequisites");
|
|
1542
1526
|
await checkPrerequisites();
|
|
1543
1527
|
log9.step("Step 2/9 SSH connection");
|
|
1544
|
-
|
|
1528
|
+
const sshCfg = await collectSshConfig({
|
|
1545
1529
|
sshPort: options.sshPort,
|
|
1546
1530
|
sshUser: options.sshUser,
|
|
1547
1531
|
sshKey: options.sshKey,
|
|
@@ -1552,31 +1536,9 @@ async function runInit(options = {}) {
|
|
|
1552
1536
|
await ssh.connect(sshCfg);
|
|
1553
1537
|
log9.success(`Connected to ${sshCfg.host}`);
|
|
1554
1538
|
} catch (err) {
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
const pw = await promptPassword({
|
|
1559
|
-
message: `SSH password for ${sshCfg.username}@${sshCfg.host}`,
|
|
1560
|
-
validate: (v) => !v ? "Required" : void 0
|
|
1561
|
-
});
|
|
1562
|
-
if (isCancel4(pw)) {
|
|
1563
|
-
cancel3("Setup cancelled.");
|
|
1564
|
-
process.exit(0);
|
|
1565
|
-
}
|
|
1566
|
-
sshCfg = { ...sshCfg, privateKeyPath: void 0, password: pw };
|
|
1567
|
-
try {
|
|
1568
|
-
await ssh.connect(sshCfg);
|
|
1569
|
-
log9.success(`Connected to ${sshCfg.host}`);
|
|
1570
|
-
} catch (err2) {
|
|
1571
|
-
log9.error(`SSH connection failed: ${String(err2)}`);
|
|
1572
|
-
log9.info("Fix your SSH credentials and re-run `lead-routing init`.");
|
|
1573
|
-
process.exit(1);
|
|
1574
|
-
}
|
|
1575
|
-
} else {
|
|
1576
|
-
log9.error(`SSH connection failed: ${String(err)}`);
|
|
1577
|
-
log9.info("Fix your SSH credentials and re-run `lead-routing init`.");
|
|
1578
|
-
process.exit(1);
|
|
1579
|
-
}
|
|
1539
|
+
log9.error(`SSH connection failed: ${String(err)}`);
|
|
1540
|
+
log9.info("Check your password and re-run `lead-routing init`.");
|
|
1541
|
+
process.exit(1);
|
|
1580
1542
|
}
|
|
1581
1543
|
}
|
|
1582
1544
|
log9.step("Step 3/9 Configuration");
|
|
@@ -1650,7 +1612,7 @@ Files created: docker-compose.yml, Caddyfile, .env.web, .env.engine, lead-routin
|
|
|
1650
1612
|
|
|
1651
1613
|
// src/commands/deploy.ts
|
|
1652
1614
|
import { writeFileSync as writeFileSync4, unlinkSync } from "fs";
|
|
1653
|
-
import { join as
|
|
1615
|
+
import { join as join6 } from "path";
|
|
1654
1616
|
import { tmpdir as tmpdir2 } from "os";
|
|
1655
1617
|
import { intro as intro2, outro as outro2, log as log10, password as promptPassword2 } from "@clack/prompts";
|
|
1656
1618
|
import chalk3 from "chalk";
|
|
@@ -1692,7 +1654,7 @@ async function runDeploy() {
|
|
|
1692
1654
|
const remoteDir = await ssh.resolveHome(cfg.remoteDir);
|
|
1693
1655
|
log10.step("Syncing Caddyfile");
|
|
1694
1656
|
const caddyContent = renderCaddyfile(cfg.appUrl, cfg.engineUrl);
|
|
1695
|
-
const tmpCaddy =
|
|
1657
|
+
const tmpCaddy = join6(tmpdir2(), "lead-routing-Caddyfile");
|
|
1696
1658
|
writeFileSync4(tmpCaddy, caddyContent, "utf8");
|
|
1697
1659
|
await ssh.upload([{ local: tmpCaddy, remote: `${remoteDir}/Caddyfile` }]);
|
|
1698
1660
|
unlinkSync(tmpCaddy);
|
|
@@ -1858,7 +1820,7 @@ async function runStatus() {
|
|
|
1858
1820
|
|
|
1859
1821
|
// src/commands/config.ts
|
|
1860
1822
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
|
|
1861
|
-
import { join as
|
|
1823
|
+
import { join as join7 } from "path";
|
|
1862
1824
|
import { intro as intro4, outro as outro4, text as text3, password as password3, spinner as spinner8, log as log14 } from "@clack/prompts";
|
|
1863
1825
|
import chalk5 from "chalk";
|
|
1864
1826
|
import { execa as execa7 } from "execa";
|
|
@@ -1902,8 +1864,8 @@ async function runConfigSfdc() {
|
|
|
1902
1864
|
log14.info("Run `lead-routing init` first, or cd into your installation directory.");
|
|
1903
1865
|
process.exit(1);
|
|
1904
1866
|
}
|
|
1905
|
-
const envWeb =
|
|
1906
|
-
const envEngine =
|
|
1867
|
+
const envWeb = join7(dir, ".env.web");
|
|
1868
|
+
const envEngine = join7(dir, ".env.engine");
|
|
1907
1869
|
const currentWeb = parseEnv(envWeb);
|
|
1908
1870
|
const currentClientId = currentWeb.get("SFDC_CLIENT_ID") ?? "";
|
|
1909
1871
|
const currentLoginUrl = currentWeb.get("SFDC_LOGIN_URL") ?? "https://login.salesforce.com";
|
|
@@ -1956,7 +1918,7 @@ function runConfigShow() {
|
|
|
1956
1918
|
console.error("No lead-routing installation found in the current directory.");
|
|
1957
1919
|
process.exit(1);
|
|
1958
1920
|
}
|
|
1959
|
-
const envWeb =
|
|
1921
|
+
const envWeb = join7(dir, ".env.web");
|
|
1960
1922
|
const cfg = parseEnv(envWeb);
|
|
1961
1923
|
const adminSecret = cfg.get("ADMIN_SECRET") ?? "(not found)";
|
|
1962
1924
|
const appUrl = cfg.get("APP_URL") ?? "(not found)";
|
|
@@ -2043,7 +2005,7 @@ async function runSfdcDeploy() {
|
|
|
2043
2005
|
|
|
2044
2006
|
// src/index.ts
|
|
2045
2007
|
var program = new Command();
|
|
2046
|
-
program.name("lead-routing").description("Self-hosted Lead Routing \u2014 scaffold, deploy, and manage your installation").version("0.1.
|
|
2008
|
+
program.name("lead-routing").description("Self-hosted Lead Routing \u2014 scaffold, deploy, and manage your installation").version("0.1.11");
|
|
2047
2009
|
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({
|
|
2048
2010
|
dryRun: opts.dryRun,
|
|
2049
2011
|
resume: opts.resume,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lead-routing/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
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"],
|