@lead-routing/cli 0.1.10 → 0.1.12
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 +41 -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
|
|
|
@@ -385,6 +369,7 @@ function renderEnvWeb(c) {
|
|
|
385
369
|
``,
|
|
386
370
|
`# Engine`,
|
|
387
371
|
`ENGINE_URL=${c.engineUrl}`,
|
|
372
|
+
`PUBLIC_ENGINE_URL=${c.publicEngineUrl}`,
|
|
388
373
|
`ENGINE_WEBHOOK_SECRET=${c.engineWebhookSecret}`,
|
|
389
374
|
``,
|
|
390
375
|
`# Database`,
|
|
@@ -489,9 +474,9 @@ function renderCaddyfile(appUrl, engineUrl) {
|
|
|
489
474
|
|
|
490
475
|
// src/utils/config.ts
|
|
491
476
|
import { readFileSync, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
492
|
-
import { join
|
|
477
|
+
import { join } from "path";
|
|
493
478
|
function getConfigPath(dir) {
|
|
494
|
-
return
|
|
479
|
+
return join(dir, "lead-routing.json");
|
|
495
480
|
}
|
|
496
481
|
function readConfig(dir) {
|
|
497
482
|
const path2 = getConfigPath(dir);
|
|
@@ -506,10 +491,10 @@ function writeConfig(dir, config2) {
|
|
|
506
491
|
writeFileSync(getConfigPath(dir), JSON.stringify(config2, null, 2), "utf8");
|
|
507
492
|
}
|
|
508
493
|
function findInstallDir(startDir = process.cwd()) {
|
|
509
|
-
const candidate =
|
|
494
|
+
const candidate = join(startDir, "lead-routing.json");
|
|
510
495
|
if (existsSync2(candidate)) return startDir;
|
|
511
|
-
const nested =
|
|
512
|
-
if (existsSync2(nested)) return
|
|
496
|
+
const nested = join(startDir, "lead-routing", "lead-routing.json");
|
|
497
|
+
if (existsSync2(nested)) return join(startDir, "lead-routing");
|
|
513
498
|
return null;
|
|
514
499
|
}
|
|
515
500
|
|
|
@@ -517,14 +502,14 @@ function findInstallDir(startDir = process.cwd()) {
|
|
|
517
502
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
518
503
|
function getCliVersion() {
|
|
519
504
|
try {
|
|
520
|
-
const pkg = JSON.parse(readFileSync2(
|
|
505
|
+
const pkg = JSON.parse(readFileSync2(join2(__dirname, "../../package.json"), "utf8"));
|
|
521
506
|
return pkg.version ?? "0.1.0";
|
|
522
507
|
} catch {
|
|
523
508
|
return "0.1.0";
|
|
524
509
|
}
|
|
525
510
|
}
|
|
526
511
|
function generateFiles(cfg, sshCfg) {
|
|
527
|
-
const dir =
|
|
512
|
+
const dir = join2(process.cwd(), "lead-routing");
|
|
528
513
|
mkdirSync(dir, { recursive: true });
|
|
529
514
|
const dockerEngineUrl = `http://engine:3001`;
|
|
530
515
|
const composeContent = renderDockerCompose({
|
|
@@ -532,15 +517,16 @@ function generateFiles(cfg, sshCfg) {
|
|
|
532
517
|
managedRedis: cfg.managedRedis,
|
|
533
518
|
dbPassword: cfg.dbPassword
|
|
534
519
|
});
|
|
535
|
-
const composeFile =
|
|
520
|
+
const composeFile = join2(dir, "docker-compose.yml");
|
|
536
521
|
writeFileSync2(composeFile, composeContent, "utf8");
|
|
537
522
|
log3.success("Generated docker-compose.yml");
|
|
538
523
|
const caddyfileContent = renderCaddyfile(cfg.appUrl, cfg.engineUrl);
|
|
539
|
-
writeFileSync2(
|
|
524
|
+
writeFileSync2(join2(dir, "Caddyfile"), caddyfileContent, "utf8");
|
|
540
525
|
log3.success("Generated Caddyfile");
|
|
541
526
|
const envWebContent = renderEnvWeb({
|
|
542
527
|
appUrl: cfg.appUrl,
|
|
543
528
|
engineUrl: dockerEngineUrl,
|
|
529
|
+
publicEngineUrl: cfg.engineUrl,
|
|
544
530
|
databaseUrl: cfg.databaseUrl,
|
|
545
531
|
redisUrl: cfg.redisUrl,
|
|
546
532
|
sfdcClientId: cfg.sfdcClientId,
|
|
@@ -552,7 +538,7 @@ function generateFiles(cfg, sshCfg) {
|
|
|
552
538
|
resendApiKey: cfg.resendApiKey || void 0,
|
|
553
539
|
feedbackToEmail: cfg.feedbackToEmail || void 0
|
|
554
540
|
});
|
|
555
|
-
const envWeb =
|
|
541
|
+
const envWeb = join2(dir, ".env.web");
|
|
556
542
|
writeFileSync2(envWeb, envWebContent, "utf8");
|
|
557
543
|
log3.success("Generated .env.web");
|
|
558
544
|
const envEngineContent = renderEnvEngine({
|
|
@@ -563,7 +549,7 @@ function generateFiles(cfg, sshCfg) {
|
|
|
563
549
|
sfdcLoginUrl: cfg.sfdcLoginUrl,
|
|
564
550
|
engineWebhookSecret: cfg.engineWebhookSecret
|
|
565
551
|
});
|
|
566
|
-
const envEngine =
|
|
552
|
+
const envEngine = join2(dir, ".env.engine");
|
|
567
553
|
writeFileSync2(envEngine, envEngineContent, "utf8");
|
|
568
554
|
log3.success("Generated .env.engine");
|
|
569
555
|
writeConfig(dir, {
|
|
@@ -732,7 +718,7 @@ async function checkRemotePort(ssh, port) {
|
|
|
732
718
|
}
|
|
733
719
|
|
|
734
720
|
// src/steps/upload-files.ts
|
|
735
|
-
import { join as
|
|
721
|
+
import { join as join3 } from "path";
|
|
736
722
|
import { spinner as spinner3 } from "@clack/prompts";
|
|
737
723
|
async function uploadFiles(ssh, localDir, remoteDir) {
|
|
738
724
|
const s = spinner3();
|
|
@@ -748,7 +734,7 @@ async function uploadFiles(ssh, localDir, remoteDir) {
|
|
|
748
734
|
];
|
|
749
735
|
await ssh.upload(
|
|
750
736
|
filenames.map((f) => ({
|
|
751
|
-
local:
|
|
737
|
+
local: join3(localDir, f),
|
|
752
738
|
remote: `${remoteDir}/${f}`
|
|
753
739
|
}))
|
|
754
740
|
);
|
|
@@ -1039,7 +1025,7 @@ function sleep2(ms) {
|
|
|
1039
1025
|
|
|
1040
1026
|
// src/steps/sfdc-deploy-inline.ts
|
|
1041
1027
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, cpSync, rmSync } from "fs";
|
|
1042
|
-
import { join as
|
|
1028
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
1043
1029
|
import { tmpdir } from "os";
|
|
1044
1030
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1045
1031
|
import { spinner as spinner7, log as log7 } from "@clack/prompts";
|
|
@@ -1070,10 +1056,10 @@ async function sfdcDeployInline(params) {
|
|
|
1070
1056
|
}
|
|
1071
1057
|
}
|
|
1072
1058
|
s.start("Copying Salesforce package\u2026");
|
|
1073
|
-
const inDist =
|
|
1074
|
-
const nextToDist =
|
|
1059
|
+
const inDist = join5(__dirname3, "sfdc-package");
|
|
1060
|
+
const nextToDist = join5(__dirname3, "..", "sfdc-package");
|
|
1075
1061
|
const bundledPkg = existsSync4(inDist) ? inDist : nextToDist;
|
|
1076
|
-
const destPkg =
|
|
1062
|
+
const destPkg = join5(installDir ?? tmpdir(), "lead-routing-sfdc-package");
|
|
1077
1063
|
if (!existsSync4(bundledPkg)) {
|
|
1078
1064
|
s.stop("sfdc-package not found in CLI bundle");
|
|
1079
1065
|
throw new Error(
|
|
@@ -1084,7 +1070,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1084
1070
|
if (existsSync4(destPkg)) rmSync(destPkg, { recursive: true, force: true });
|
|
1085
1071
|
cpSync(bundledPkg, destPkg, { recursive: true });
|
|
1086
1072
|
s.stop("Package copied");
|
|
1087
|
-
const ncPath =
|
|
1073
|
+
const ncPath = join5(
|
|
1088
1074
|
destPkg,
|
|
1089
1075
|
"force-app",
|
|
1090
1076
|
"main",
|
|
@@ -1096,7 +1082,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1096
1082
|
const nc = patchXml(readFileSync4(ncPath, "utf8"), "endpoint", engineUrl);
|
|
1097
1083
|
writeFileSync3(ncPath, nc, "utf8");
|
|
1098
1084
|
}
|
|
1099
|
-
const rssEnginePath =
|
|
1085
|
+
const rssEnginePath = join5(
|
|
1100
1086
|
destPkg,
|
|
1101
1087
|
"force-app",
|
|
1102
1088
|
"main",
|
|
@@ -1109,7 +1095,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1109
1095
|
rss = patchXml(rss, "description", "Lead Router Engine endpoint");
|
|
1110
1096
|
writeFileSync3(rssEnginePath, rss, "utf8");
|
|
1111
1097
|
}
|
|
1112
|
-
const rssAppPath =
|
|
1098
|
+
const rssAppPath = join5(
|
|
1113
1099
|
destPkg,
|
|
1114
1100
|
"force-app",
|
|
1115
1101
|
"main",
|
|
@@ -1541,7 +1527,7 @@ async function runInit(options = {}) {
|
|
|
1541
1527
|
log9.step("Step 1/9 Checking local prerequisites");
|
|
1542
1528
|
await checkPrerequisites();
|
|
1543
1529
|
log9.step("Step 2/9 SSH connection");
|
|
1544
|
-
|
|
1530
|
+
const sshCfg = await collectSshConfig({
|
|
1545
1531
|
sshPort: options.sshPort,
|
|
1546
1532
|
sshUser: options.sshUser,
|
|
1547
1533
|
sshKey: options.sshKey,
|
|
@@ -1552,31 +1538,9 @@ async function runInit(options = {}) {
|
|
|
1552
1538
|
await ssh.connect(sshCfg);
|
|
1553
1539
|
log9.success(`Connected to ${sshCfg.host}`);
|
|
1554
1540
|
} 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
|
-
}
|
|
1541
|
+
log9.error(`SSH connection failed: ${String(err)}`);
|
|
1542
|
+
log9.info("Check your password and re-run `lead-routing init`.");
|
|
1543
|
+
process.exit(1);
|
|
1580
1544
|
}
|
|
1581
1545
|
}
|
|
1582
1546
|
log9.step("Step 3/9 Configuration");
|
|
@@ -1650,7 +1614,7 @@ Files created: docker-compose.yml, Caddyfile, .env.web, .env.engine, lead-routin
|
|
|
1650
1614
|
|
|
1651
1615
|
// src/commands/deploy.ts
|
|
1652
1616
|
import { writeFileSync as writeFileSync4, unlinkSync } from "fs";
|
|
1653
|
-
import { join as
|
|
1617
|
+
import { join as join6 } from "path";
|
|
1654
1618
|
import { tmpdir as tmpdir2 } from "os";
|
|
1655
1619
|
import { intro as intro2, outro as outro2, log as log10, password as promptPassword2 } from "@clack/prompts";
|
|
1656
1620
|
import chalk3 from "chalk";
|
|
@@ -1692,7 +1656,7 @@ async function runDeploy() {
|
|
|
1692
1656
|
const remoteDir = await ssh.resolveHome(cfg.remoteDir);
|
|
1693
1657
|
log10.step("Syncing Caddyfile");
|
|
1694
1658
|
const caddyContent = renderCaddyfile(cfg.appUrl, cfg.engineUrl);
|
|
1695
|
-
const tmpCaddy =
|
|
1659
|
+
const tmpCaddy = join6(tmpdir2(), "lead-routing-Caddyfile");
|
|
1696
1660
|
writeFileSync4(tmpCaddy, caddyContent, "utf8");
|
|
1697
1661
|
await ssh.upload([{ local: tmpCaddy, remote: `${remoteDir}/Caddyfile` }]);
|
|
1698
1662
|
unlinkSync(tmpCaddy);
|
|
@@ -1858,7 +1822,7 @@ async function runStatus() {
|
|
|
1858
1822
|
|
|
1859
1823
|
// src/commands/config.ts
|
|
1860
1824
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
|
|
1861
|
-
import { join as
|
|
1825
|
+
import { join as join7 } from "path";
|
|
1862
1826
|
import { intro as intro4, outro as outro4, text as text3, password as password3, spinner as spinner8, log as log14 } from "@clack/prompts";
|
|
1863
1827
|
import chalk5 from "chalk";
|
|
1864
1828
|
import { execa as execa7 } from "execa";
|
|
@@ -1902,8 +1866,8 @@ async function runConfigSfdc() {
|
|
|
1902
1866
|
log14.info("Run `lead-routing init` first, or cd into your installation directory.");
|
|
1903
1867
|
process.exit(1);
|
|
1904
1868
|
}
|
|
1905
|
-
const envWeb =
|
|
1906
|
-
const envEngine =
|
|
1869
|
+
const envWeb = join7(dir, ".env.web");
|
|
1870
|
+
const envEngine = join7(dir, ".env.engine");
|
|
1907
1871
|
const currentWeb = parseEnv(envWeb);
|
|
1908
1872
|
const currentClientId = currentWeb.get("SFDC_CLIENT_ID") ?? "";
|
|
1909
1873
|
const currentLoginUrl = currentWeb.get("SFDC_LOGIN_URL") ?? "https://login.salesforce.com";
|
|
@@ -1956,7 +1920,7 @@ function runConfigShow() {
|
|
|
1956
1920
|
console.error("No lead-routing installation found in the current directory.");
|
|
1957
1921
|
process.exit(1);
|
|
1958
1922
|
}
|
|
1959
|
-
const envWeb =
|
|
1923
|
+
const envWeb = join7(dir, ".env.web");
|
|
1960
1924
|
const cfg = parseEnv(envWeb);
|
|
1961
1925
|
const adminSecret = cfg.get("ADMIN_SECRET") ?? "(not found)";
|
|
1962
1926
|
const appUrl = cfg.get("APP_URL") ?? "(not found)";
|
|
@@ -2043,7 +2007,7 @@ async function runSfdcDeploy() {
|
|
|
2043
2007
|
|
|
2044
2008
|
// src/index.ts
|
|
2045
2009
|
var program = new Command();
|
|
2046
|
-
program.name("lead-routing").description("Self-hosted Lead Routing \u2014 scaffold, deploy, and manage your installation").version("0.1.
|
|
2010
|
+
program.name("lead-routing").description("Self-hosted Lead Routing \u2014 scaffold, deploy, and manage your installation").version("0.1.12");
|
|
2047
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({
|
|
2048
2012
|
dryRun: opts.dryRun,
|
|
2049
2013
|
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.12",
|
|
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"],
|