@lead-routing/cli 0.4.0 → 0.4.2
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 +61 -168
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import { Command } from "commander";
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import { promises as dns } from "dns";
|
|
8
8
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
9
|
+
import { exec } from "child_process";
|
|
10
|
+
import { platform } from "os";
|
|
9
11
|
import { join as join4 } from "path";
|
|
10
12
|
import { intro, outro, note as note3, log as log7, confirm, cancel as cancel3, isCancel as isCancel3, password as promptPassword } from "@clack/prompts";
|
|
11
13
|
import chalk from "chalk";
|
|
@@ -111,7 +113,7 @@ function bail2(value) {
|
|
|
111
113
|
}
|
|
112
114
|
async function collectConfig(opts = {}) {
|
|
113
115
|
note2(
|
|
114
|
-
"You will need:\n \u2022
|
|
116
|
+
"You will need:\n \u2022 Public HTTPS URLs for the web app and routing engine",
|
|
115
117
|
"Before you begin"
|
|
116
118
|
);
|
|
117
119
|
const appUrl = await text2({
|
|
@@ -142,33 +144,6 @@ async function collectConfig(opts = {}) {
|
|
|
142
144
|
}
|
|
143
145
|
});
|
|
144
146
|
if (isCancel2(engineUrl)) bail2(engineUrl);
|
|
145
|
-
const callbackUrl = `${appUrl.trim().replace(/\/+$/, "")}/api/auth/sfdc/callback`;
|
|
146
|
-
note2(
|
|
147
|
-
`You need a Salesforce Connected App. If you haven't created one yet:
|
|
148
|
-
|
|
149
|
-
1. Go to Salesforce Setup \u2192 App Manager \u2192 New Connected App
|
|
150
|
-
2. Connected App Name: Lead Routing
|
|
151
|
-
3. Check "Enable OAuth Settings"
|
|
152
|
-
4. Callback URL (copy exactly \u2014 must match):
|
|
153
|
-
${callbackUrl}
|
|
154
|
-
5. Selected Scopes: api \u2022 refresh_token, offline_access
|
|
155
|
-
6. Check "Require Secret for Web Server Flow"
|
|
156
|
-
7. Save \u2014 wait ~2 min, then click "Manage Consumer Details"
|
|
157
|
-
8. Copy the Consumer Key (Client ID) and Consumer Secret below`,
|
|
158
|
-
"Salesforce Connected App setup"
|
|
159
|
-
);
|
|
160
|
-
const sfdcClientId = await text2({
|
|
161
|
-
message: 'Consumer Key (labelled "Client ID" in newer orgs)',
|
|
162
|
-
placeholder: "3MVG9...",
|
|
163
|
-
validate: (v) => !v ? "Required" : void 0
|
|
164
|
-
});
|
|
165
|
-
if (isCancel2(sfdcClientId)) bail2(sfdcClientId);
|
|
166
|
-
const sfdcClientSecret = await password2({
|
|
167
|
-
message: 'Consumer Secret (labelled "Client Secret" in newer orgs)',
|
|
168
|
-
validate: (v) => !v ? "Required" : void 0
|
|
169
|
-
});
|
|
170
|
-
if (isCancel2(sfdcClientSecret)) bail2(sfdcClientSecret);
|
|
171
|
-
const sfdcLoginUrl = opts.sandbox ? "https://test.salesforce.com" : "https://login.salesforce.com";
|
|
172
147
|
const dbPassword = generateSecret(16);
|
|
173
148
|
const managedDb = !opts.externalDb;
|
|
174
149
|
const databaseUrl = opts.externalDb ?? `postgresql://leadrouting:${dbPassword}@postgres:5432/leadrouting`;
|
|
@@ -200,9 +175,6 @@ async function collectConfig(opts = {}) {
|
|
|
200
175
|
return {
|
|
201
176
|
appUrl: appUrl.trim().replace(/\/+$/, ""),
|
|
202
177
|
engineUrl: engineUrl.trim().replace(/\/+$/, ""),
|
|
203
|
-
sfdcClientId: sfdcClientId.trim(),
|
|
204
|
-
sfdcClientSecret: sfdcClientSecret.trim(),
|
|
205
|
-
sfdcLoginUrl,
|
|
206
178
|
managedDb,
|
|
207
179
|
databaseUrl,
|
|
208
180
|
dbPassword: managedDb ? dbPassword : "",
|
|
@@ -360,12 +332,6 @@ function renderEnvWeb(c) {
|
|
|
360
332
|
`# Redis`,
|
|
361
333
|
`REDIS_URL=${c.redisUrl}`,
|
|
362
334
|
``,
|
|
363
|
-
`# Salesforce OAuth`,
|
|
364
|
-
`SFDC_CLIENT_ID=${c.sfdcClientId}`,
|
|
365
|
-
`SFDC_CLIENT_SECRET=${c.sfdcClientSecret}`,
|
|
366
|
-
`SFDC_LOGIN_URL=${c.sfdcLoginUrl}`,
|
|
367
|
-
`SFDC_REDIRECT_URI=${c.appUrl.replace(/\/+$/, "")}/api/auth/sfdc/callback`,
|
|
368
|
-
``,
|
|
369
335
|
`# Session`,
|
|
370
336
|
`SESSION_SECRET=${c.sessionSecret}`,
|
|
371
337
|
``,
|
|
@@ -400,11 +366,6 @@ function renderEnvEngine(c) {
|
|
|
400
366
|
`# Redis`,
|
|
401
367
|
`REDIS_URL=${c.redisUrl}`,
|
|
402
368
|
``,
|
|
403
|
-
`# Salesforce OAuth`,
|
|
404
|
-
`SFDC_CLIENT_ID=${c.sfdcClientId}`,
|
|
405
|
-
`SFDC_CLIENT_SECRET=${c.sfdcClientSecret}`,
|
|
406
|
-
`SFDC_LOGIN_URL=${c.sfdcLoginUrl}`,
|
|
407
|
-
``,
|
|
408
369
|
`# Webhook`,
|
|
409
370
|
`ENGINE_WEBHOOK_SECRET=${c.engineWebhookSecret}`,
|
|
410
371
|
``,
|
|
@@ -544,9 +505,6 @@ function generateFiles(cfg, sshCfg) {
|
|
|
544
505
|
publicEngineUrl: cfg.engineUrl,
|
|
545
506
|
databaseUrl: cfg.databaseUrl,
|
|
546
507
|
redisUrl: cfg.redisUrl,
|
|
547
|
-
sfdcClientId: cfg.sfdcClientId,
|
|
548
|
-
sfdcClientSecret: cfg.sfdcClientSecret,
|
|
549
|
-
sfdcLoginUrl: cfg.sfdcLoginUrl,
|
|
550
508
|
sessionSecret: cfg.sessionSecret,
|
|
551
509
|
engineWebhookSecret: cfg.engineWebhookSecret,
|
|
552
510
|
adminSecret: cfg.adminSecret,
|
|
@@ -562,9 +520,6 @@ function generateFiles(cfg, sshCfg) {
|
|
|
562
520
|
const envEngineContent = renderEnvEngine({
|
|
563
521
|
databaseUrl: cfg.databaseUrl,
|
|
564
522
|
redisUrl: cfg.redisUrl,
|
|
565
|
-
sfdcClientId: cfg.sfdcClientId,
|
|
566
|
-
sfdcClientSecret: cfg.sfdcClientSecret,
|
|
567
|
-
sfdcLoginUrl: cfg.sfdcLoginUrl,
|
|
568
523
|
engineWebhookSecret: cfg.engineWebhookSecret,
|
|
569
524
|
internalApiKey: cfg.internalApiKey
|
|
570
525
|
});
|
|
@@ -587,9 +542,6 @@ function generateFiles(cfg, sshCfg) {
|
|
|
587
542
|
db: cfg.managedDb,
|
|
588
543
|
redis: cfg.managedRedis
|
|
589
544
|
},
|
|
590
|
-
// Stored so `lead-routing sfdc deploy` can re-authenticate without re-prompting
|
|
591
|
-
sfdcClientId: cfg.sfdcClientId,
|
|
592
|
-
sfdcLoginUrl: cfg.sfdcLoginUrl,
|
|
593
545
|
engineWebhookSecret: cfg.engineWebhookSecret,
|
|
594
546
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
595
547
|
version: getCliVersion()
|
|
@@ -1050,6 +1002,11 @@ ${result.stderr || result.stdout}`
|
|
|
1050
1002
|
};
|
|
1051
1003
|
|
|
1052
1004
|
// src/commands/init.ts
|
|
1005
|
+
var MANAGED_PACKAGE_INSTALL_URL = "https://login.salesforce.com/packaging/installPackage.apexp?p0=04tgL000000CTnp";
|
|
1006
|
+
function openBrowser(url) {
|
|
1007
|
+
const cmd = platform() === "darwin" ? "open" : "xdg-open";
|
|
1008
|
+
exec(`${cmd} ${JSON.stringify(url)}`);
|
|
1009
|
+
}
|
|
1053
1010
|
async function checkDnsResolvable(appUrl, engineUrl) {
|
|
1054
1011
|
let hosts;
|
|
1055
1012
|
try {
|
|
@@ -1108,26 +1065,9 @@ async function runInit(options = {}) {
|
|
|
1108
1065
|
});
|
|
1109
1066
|
log7.success(`Connected to ${saved.ssh.host}`);
|
|
1110
1067
|
const remoteDir = await ssh.resolveHome(saved.remoteDir);
|
|
1111
|
-
log7.step("
|
|
1068
|
+
log7.step("Verifying health");
|
|
1112
1069
|
await verifyHealth(saved.appUrl, saved.engineUrl, ssh, remoteDir);
|
|
1113
|
-
|
|
1114
|
-
`Open ${saved.appUrl} \u2192 Integrations \u2192 Salesforce to connect your CRM and deploy the package.`,
|
|
1115
|
-
"Next: Connect Salesforce"
|
|
1116
|
-
);
|
|
1117
|
-
outro(
|
|
1118
|
-
chalk.green("\u2714 You're live!") + `
|
|
1119
|
-
|
|
1120
|
-
Dashboard: ${chalk.cyan(saved.appUrl)}
|
|
1121
|
-
Routing engine: ${chalk.cyan(saved.engineUrl)}
|
|
1122
|
-
|
|
1123
|
-
` + chalk.bold(" Next steps:\n") + ` ${chalk.cyan("1.")} Open ${chalk.cyan(saved.appUrl)} and log in
|
|
1124
|
-
${chalk.cyan("2.")} Go to Integrations \u2192 Salesforce to connect your org
|
|
1125
|
-
${chalk.cyan("3.")} Deploy the package and configure routing objects
|
|
1126
|
-
${chalk.cyan("4.")} Create your first routing rule
|
|
1127
|
-
|
|
1128
|
-
Run ${chalk.cyan("lead-routing doctor")} to check service health at any time.
|
|
1129
|
-
Run ${chalk.cyan("lead-routing deploy")} to update to a new version.`
|
|
1130
|
-
);
|
|
1070
|
+
outro(chalk.green("\u2714 Services are healthy!"));
|
|
1131
1071
|
} catch (err) {
|
|
1132
1072
|
const message = err instanceof Error ? err.message : String(err);
|
|
1133
1073
|
log7.error(`Resume failed: ${message}`);
|
|
@@ -1138,9 +1078,33 @@ async function runInit(options = {}) {
|
|
|
1138
1078
|
return;
|
|
1139
1079
|
}
|
|
1140
1080
|
try {
|
|
1141
|
-
log7.step("Step 1/
|
|
1081
|
+
log7.step("Step 1/8 Install Salesforce Package");
|
|
1082
|
+
note3(
|
|
1083
|
+
`The Lead Router managed package installs the required Connected App,
|
|
1084
|
+
triggers, and custom objects in your Salesforce org.
|
|
1085
|
+
|
|
1086
|
+
Install URL: ${chalk.cyan(MANAGED_PACKAGE_INSTALL_URL)}`,
|
|
1087
|
+
"Salesforce Package"
|
|
1088
|
+
);
|
|
1089
|
+
log7.info("Opening install URL in your browser...");
|
|
1090
|
+
openBrowser(MANAGED_PACKAGE_INSTALL_URL);
|
|
1091
|
+
log7.info(`${chalk.dim("If the browser didn't open, visit the URL above manually.")}`);
|
|
1092
|
+
const installed = await confirm({
|
|
1093
|
+
message: 'Have you installed the package? (Click "Install for All Users" in Salesforce)',
|
|
1094
|
+
initialValue: false
|
|
1095
|
+
});
|
|
1096
|
+
if (isCancel3(installed)) {
|
|
1097
|
+
cancel3("Setup cancelled.");
|
|
1098
|
+
process.exit(0);
|
|
1099
|
+
}
|
|
1100
|
+
if (!installed) {
|
|
1101
|
+
log7.warn("You can install the package later from Integrations \u2192 Salesforce in the web app.");
|
|
1102
|
+
} else {
|
|
1103
|
+
log7.success("Salesforce package installed");
|
|
1104
|
+
}
|
|
1105
|
+
log7.step("Step 2/8 Checking local prerequisites");
|
|
1142
1106
|
await checkPrerequisites();
|
|
1143
|
-
log7.step("Step
|
|
1107
|
+
log7.step("Step 3/8 SSH connection");
|
|
1144
1108
|
const sshCfg = await collectSshConfig({
|
|
1145
1109
|
sshPort: options.sshPort,
|
|
1146
1110
|
sshUser: options.sshUser,
|
|
@@ -1157,14 +1121,13 @@ async function runInit(options = {}) {
|
|
|
1157
1121
|
process.exit(1);
|
|
1158
1122
|
}
|
|
1159
1123
|
}
|
|
1160
|
-
log7.step("Step
|
|
1124
|
+
log7.step("Step 4/8 Configuration");
|
|
1161
1125
|
const cfg = await collectConfig({
|
|
1162
|
-
sandbox: options.sandbox,
|
|
1163
1126
|
externalDb: options.externalDb,
|
|
1164
1127
|
externalRedis: options.externalRedis
|
|
1165
1128
|
});
|
|
1166
1129
|
await checkDnsResolvable(cfg.appUrl, cfg.engineUrl);
|
|
1167
|
-
log7.step("Step
|
|
1130
|
+
log7.step("Step 5/8 Generating config files");
|
|
1168
1131
|
const { dir, adminSecret } = generateFiles(cfg, sshCfg);
|
|
1169
1132
|
note3(
|
|
1170
1133
|
`Local config directory: ${chalk.cyan(dir)}
|
|
@@ -1181,13 +1144,13 @@ Files created: docker-compose.yml, Caddyfile, .env.web, .env.engine, lead-routin
|
|
|
1181
1144
|
);
|
|
1182
1145
|
return;
|
|
1183
1146
|
}
|
|
1184
|
-
log7.step("Step
|
|
1147
|
+
log7.step("Step 6/8 Remote setup");
|
|
1185
1148
|
const remoteDir = await ssh.resolveHome(sshCfg.remoteDir);
|
|
1186
1149
|
await checkRemotePrerequisites(ssh);
|
|
1187
1150
|
await uploadFiles(ssh, dir, remoteDir);
|
|
1188
|
-
log7.step("Step
|
|
1151
|
+
log7.step("Step 7/8 Starting services");
|
|
1189
1152
|
await startServices(ssh, remoteDir);
|
|
1190
|
-
log7.step("Step
|
|
1153
|
+
log7.step("Step 8/8 Verifying health");
|
|
1191
1154
|
await verifyHealth(cfg.appUrl, cfg.engineUrl, ssh, remoteDir);
|
|
1192
1155
|
try {
|
|
1193
1156
|
const envWebPath = join4(dir, ".env.web");
|
|
@@ -1198,7 +1161,8 @@ Files created: docker-compose.yml, Caddyfile, .env.web, .env.engine, lead-routin
|
|
|
1198
1161
|
} catch {
|
|
1199
1162
|
}
|
|
1200
1163
|
note3(
|
|
1201
|
-
`Open ${cfg.appUrl} \u2192 Integrations \u2192 Salesforce to connect your
|
|
1164
|
+
`Open ${cfg.appUrl} \u2192 Integrations \u2192 Salesforce to connect your org.
|
|
1165
|
+
The managed package is already installed \u2014 just click "Connect Salesforce" to authorize.`,
|
|
1202
1166
|
"Next: Connect Salesforce"
|
|
1203
1167
|
);
|
|
1204
1168
|
outro(
|
|
@@ -1212,8 +1176,8 @@ Files created: docker-compose.yml, Caddyfile, .env.web, .env.engine, lead-routin
|
|
|
1212
1176
|
${chalk.dim("run `lead-routing config show` to retrieve later")}
|
|
1213
1177
|
|
|
1214
1178
|
` + chalk.bold(" Next steps:\n") + ` ${chalk.cyan("1.")} Open ${chalk.cyan(cfg.appUrl)} and log in
|
|
1215
|
-
${chalk.cyan("2.")} Go to Integrations \u2192 Salesforce
|
|
1216
|
-
${chalk.cyan("3.")}
|
|
1179
|
+
${chalk.cyan("2.")} Go to Integrations \u2192 Salesforce \u2192 Connect
|
|
1180
|
+
${chalk.cyan("3.")} Complete the onboarding wizard in Salesforce
|
|
1217
1181
|
${chalk.cyan("4.")} Create your first routing rule
|
|
1218
1182
|
|
|
1219
1183
|
Run ${chalk.cyan("lead-routing doctor")} to check service health at any time.
|
|
@@ -1437,9 +1401,8 @@ async function runStatus() {
|
|
|
1437
1401
|
// src/commands/config.ts
|
|
1438
1402
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync3 } from "fs";
|
|
1439
1403
|
import { join as join6 } from "path";
|
|
1440
|
-
import { intro as intro4, outro as outro4,
|
|
1404
|
+
import { intro as intro4, outro as outro4, log as log12 } from "@clack/prompts";
|
|
1441
1405
|
import chalk4 from "chalk";
|
|
1442
|
-
import { execa as execa4 } from "execa";
|
|
1443
1406
|
function parseEnv(filePath) {
|
|
1444
1407
|
const map = /* @__PURE__ */ new Map();
|
|
1445
1408
|
if (!existsSync3(filePath)) return map;
|
|
@@ -1452,81 +1415,18 @@ function parseEnv(filePath) {
|
|
|
1452
1415
|
}
|
|
1453
1416
|
return map;
|
|
1454
1417
|
}
|
|
1455
|
-
function writeEnv(filePath, updates) {
|
|
1456
|
-
const lines = existsSync3(filePath) ? readFileSync4(filePath, "utf8").split("\n") : [];
|
|
1457
|
-
const updated = /* @__PURE__ */ new Set();
|
|
1458
|
-
const result = lines.map((line) => {
|
|
1459
|
-
const trimmed = line.trim();
|
|
1460
|
-
if (!trimmed || trimmed.startsWith("#")) return line;
|
|
1461
|
-
const eq = trimmed.indexOf("=");
|
|
1462
|
-
if (eq === -1) return line;
|
|
1463
|
-
const key = trimmed.slice(0, eq);
|
|
1464
|
-
if (key in updates) {
|
|
1465
|
-
updated.add(key);
|
|
1466
|
-
return `${key}=${updates[key]}`;
|
|
1467
|
-
}
|
|
1468
|
-
return line;
|
|
1469
|
-
});
|
|
1470
|
-
for (const [key, val] of Object.entries(updates)) {
|
|
1471
|
-
if (!updated.has(key)) result.push(`${key}=${val}`);
|
|
1472
|
-
}
|
|
1473
|
-
writeFileSync5(filePath, result.join("\n"), "utf8");
|
|
1474
|
-
}
|
|
1475
1418
|
async function runConfigSfdc() {
|
|
1476
|
-
intro4("Lead Routing \u2014
|
|
1419
|
+
intro4("Lead Routing \u2014 Salesforce Configuration");
|
|
1477
1420
|
const dir = findInstallDir();
|
|
1478
1421
|
if (!dir) {
|
|
1479
1422
|
log12.error("No lead-routing installation found in the current directory.");
|
|
1480
1423
|
log12.info("Run `lead-routing init` first, or cd into your installation directory.");
|
|
1481
1424
|
process.exit(1);
|
|
1482
1425
|
}
|
|
1483
|
-
const envWeb = join6(dir, ".env.web");
|
|
1484
|
-
const envEngine = join6(dir, ".env.engine");
|
|
1485
|
-
const currentWeb = parseEnv(envWeb);
|
|
1486
|
-
const currentClientId = currentWeb.get("SFDC_CLIENT_ID") ?? "";
|
|
1487
|
-
const currentLoginUrl = currentWeb.get("SFDC_LOGIN_URL") ?? "https://login.salesforce.com";
|
|
1488
|
-
const currentAppUrl = currentWeb.get("APP_URL") ?? "";
|
|
1489
|
-
const callbackUrl = `${currentAppUrl}/api/auth/callback`;
|
|
1490
1426
|
log12.info(
|
|
1491
|
-
|
|
1492
|
-
Callback URL for your Connected App: ${callbackUrl}`
|
|
1493
|
-
);
|
|
1494
|
-
const clientId = await text3({
|
|
1495
|
-
message: "Consumer Key (Client ID)",
|
|
1496
|
-
initialValue: currentClientId,
|
|
1497
|
-
validate: (v) => !v ? "Required" : void 0
|
|
1498
|
-
});
|
|
1499
|
-
if (clientId === null || typeof clientId === "symbol") {
|
|
1500
|
-
process.exit(0);
|
|
1501
|
-
}
|
|
1502
|
-
const clientSecret = await password3({
|
|
1503
|
-
message: "Consumer Secret (Client Secret)",
|
|
1504
|
-
validate: (v) => !v ? "Required" : void 0
|
|
1505
|
-
});
|
|
1506
|
-
if (clientSecret === null || typeof clientSecret === "symbol") {
|
|
1507
|
-
process.exit(0);
|
|
1508
|
-
}
|
|
1509
|
-
const updates = {
|
|
1510
|
-
SFDC_CLIENT_ID: clientId,
|
|
1511
|
-
SFDC_CLIENT_SECRET: clientSecret
|
|
1512
|
-
};
|
|
1513
|
-
writeEnv(envWeb, updates);
|
|
1514
|
-
writeEnv(envEngine, updates);
|
|
1515
|
-
log12.success("Updated .env.web and .env.engine");
|
|
1516
|
-
const s = spinner5();
|
|
1517
|
-
s.start("Restarting web and engine containers\u2026");
|
|
1518
|
-
try {
|
|
1519
|
-
await execa4("docker", ["compose", "up", "-d", "--force-recreate", "web", "engine"], {
|
|
1520
|
-
cwd: dir
|
|
1521
|
-
});
|
|
1522
|
-
s.stop("Containers restarted");
|
|
1523
|
-
} catch (err) {
|
|
1524
|
-
s.stop("Restart failed \u2014 run `docker compose up -d --force-recreate web engine` manually");
|
|
1525
|
-
log12.warn(String(err));
|
|
1526
|
-
}
|
|
1527
|
-
outro4(
|
|
1528
|
-
"Salesforce credentials updated!\n\nNext: go to the web app \u2192 Settings \u2192 Connect Salesforce to refresh your OAuth tokens."
|
|
1427
|
+
"Salesforce credentials are now managed automatically via the managed package.\nNo manual configuration is needed.\n\nIf you need to reconnect Salesforce, go to the web app \u2192 Settings \u2192 Connect Salesforce."
|
|
1529
1428
|
);
|
|
1429
|
+
outro4("No changes needed.");
|
|
1530
1430
|
}
|
|
1531
1431
|
function runConfigShow() {
|
|
1532
1432
|
const dir = findInstallDir();
|
|
@@ -1538,19 +1438,16 @@ function runConfigShow() {
|
|
|
1538
1438
|
const cfg = parseEnv(envWeb);
|
|
1539
1439
|
const adminSecret = cfg.get("ADMIN_SECRET") ?? "(not found)";
|
|
1540
1440
|
const appUrl = cfg.get("APP_URL") ?? "(not found)";
|
|
1541
|
-
const sfdcClientId = cfg.get("SFDC_CLIENT_ID") ?? "(not found)";
|
|
1542
1441
|
console.log();
|
|
1543
1442
|
console.log(chalk4.bold("Lead Routing \u2014 Installation Config"));
|
|
1544
1443
|
console.log();
|
|
1545
1444
|
console.log(` Admin panel: ${chalk4.cyan(appUrl + "/admin")}`);
|
|
1546
1445
|
console.log(` Admin secret: ${chalk4.yellow(adminSecret)}`);
|
|
1547
1446
|
console.log();
|
|
1548
|
-
console.log(` SFDC Client ID: ${chalk4.white(sfdcClientId)}`);
|
|
1549
|
-
console.log();
|
|
1550
1447
|
}
|
|
1551
1448
|
|
|
1552
1449
|
// src/commands/sfdc.ts
|
|
1553
|
-
import { intro as intro5, outro as outro5, text as
|
|
1450
|
+
import { intro as intro5, outro as outro5, text as text3, log as log15 } from "@clack/prompts";
|
|
1554
1451
|
import chalk6 from "chalk";
|
|
1555
1452
|
|
|
1556
1453
|
// src/steps/sfdc-deploy-inline.ts
|
|
@@ -1559,7 +1456,7 @@ import { join as join8, dirname as dirname2 } from "path";
|
|
|
1559
1456
|
import { tmpdir as tmpdir2 } from "os";
|
|
1560
1457
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1561
1458
|
import { execSync } from "child_process";
|
|
1562
|
-
import { spinner as
|
|
1459
|
+
import { spinner as spinner5, log as log13 } from "@clack/prompts";
|
|
1563
1460
|
|
|
1564
1461
|
// src/utils/sfdc-api.ts
|
|
1565
1462
|
var API_VERSION = "v59.0";
|
|
@@ -1680,9 +1577,9 @@ Content-Type: application/zip\r
|
|
|
1680
1577
|
body
|
|
1681
1578
|
});
|
|
1682
1579
|
if (!res.ok) {
|
|
1683
|
-
const
|
|
1580
|
+
const text4 = await res.text();
|
|
1684
1581
|
throw new Error(
|
|
1685
|
-
`Metadata deploy request failed (${res.status}): ${
|
|
1582
|
+
`Metadata deploy request failed (${res.status}): ${text4}`
|
|
1686
1583
|
);
|
|
1687
1584
|
}
|
|
1688
1585
|
const result = await res.json();
|
|
@@ -1699,9 +1596,9 @@ Content-Type: application/zip\r
|
|
|
1699
1596
|
const url = `${this.baseUrl}/metadata/deployRequest/${deployId}?includeDetails=true`;
|
|
1700
1597
|
const res = await fetch(url, { headers: this.headers() });
|
|
1701
1598
|
if (!res.ok) {
|
|
1702
|
-
const
|
|
1599
|
+
const text4 = await res.text();
|
|
1703
1600
|
throw new Error(
|
|
1704
|
-
`Deploy status check failed (${res.status}): ${
|
|
1601
|
+
`Deploy status check failed (${res.status}): ${text4}`
|
|
1705
1602
|
);
|
|
1706
1603
|
}
|
|
1707
1604
|
const data = await res.json();
|
|
@@ -1842,7 +1739,7 @@ function patchXml(content, tag, value) {
|
|
|
1842
1739
|
}
|
|
1843
1740
|
async function sfdcDeployInline(params) {
|
|
1844
1741
|
const { appUrl, engineUrl, installDir } = params;
|
|
1845
|
-
const s =
|
|
1742
|
+
const s = spinner5();
|
|
1846
1743
|
const { accessToken, instanceUrl } = await loginViaAppBridge(appUrl);
|
|
1847
1744
|
const sf = new SalesforceApi(instanceUrl, accessToken);
|
|
1848
1745
|
s.start("Copying Salesforce package\u2026");
|
|
@@ -1985,7 +1882,7 @@ ${failureMsg || result.errorMessage || "Unknown error"}`
|
|
|
1985
1882
|
}
|
|
1986
1883
|
async function loginViaAppBridge(rawAppUrl) {
|
|
1987
1884
|
const appUrl = rawAppUrl.replace(/\/+$/, "");
|
|
1988
|
-
const s =
|
|
1885
|
+
const s = spinner5();
|
|
1989
1886
|
s.start("Starting Salesforce authentication via your Lead Router app\u2026");
|
|
1990
1887
|
let sessionId;
|
|
1991
1888
|
let authUrl;
|
|
@@ -2107,20 +2004,20 @@ async function runSfdcDeploy() {
|
|
|
2107
2004
|
log15.info(`Using config from ${dir}/lead-routing.json`);
|
|
2108
2005
|
} else {
|
|
2109
2006
|
log15.warn("No lead-routing.json found \u2014 enter the URLs from your installation.");
|
|
2110
|
-
const rawApp = await
|
|
2007
|
+
const rawApp = await text3({
|
|
2111
2008
|
message: "App URL (e.g. https://leads.acme.com)",
|
|
2112
2009
|
validate: (v) => !v ? "Required" : void 0
|
|
2113
2010
|
});
|
|
2114
2011
|
if (typeof rawApp === "symbol") process.exit(0);
|
|
2115
2012
|
appUrl = rawApp.trim();
|
|
2116
|
-
const rawEngine = await
|
|
2013
|
+
const rawEngine = await text3({
|
|
2117
2014
|
message: "Engine URL (e.g. https://engine.acme.com or https://acme.com:3001)",
|
|
2118
2015
|
validate: (v) => !v ? "Required" : void 0
|
|
2119
2016
|
});
|
|
2120
2017
|
if (typeof rawEngine === "symbol") process.exit(0);
|
|
2121
2018
|
engineUrl = rawEngine.trim();
|
|
2122
2019
|
}
|
|
2123
|
-
const alias = await
|
|
2020
|
+
const alias = await text3({
|
|
2124
2021
|
message: "Salesforce org alias (used to log in)",
|
|
2125
2022
|
placeholder: "lead-routing",
|
|
2126
2023
|
initialValue: "lead-routing",
|
|
@@ -2132,9 +2029,6 @@ async function runSfdcDeploy() {
|
|
|
2132
2029
|
appUrl,
|
|
2133
2030
|
engineUrl,
|
|
2134
2031
|
orgAlias: alias,
|
|
2135
|
-
// Read from config if available
|
|
2136
|
-
sfdcClientId: config2?.sfdcClientId ?? "",
|
|
2137
|
-
sfdcLoginUrl: config2?.sfdcLoginUrl ?? "https://login.salesforce.com",
|
|
2138
2032
|
installDir: dir ?? void 0,
|
|
2139
2033
|
webhookSecret: config2?.engineWebhookSecret
|
|
2140
2034
|
});
|
|
@@ -2241,10 +2135,9 @@ async function runUninstall() {
|
|
|
2241
2135
|
// src/index.ts
|
|
2242
2136
|
var program = new Command();
|
|
2243
2137
|
program.name("lead-routing").description("Self-hosted Lead Routing \u2014 scaffold, deploy, and manage your installation").version("0.1.13");
|
|
2244
|
-
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("--
|
|
2138
|
+
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("--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({
|
|
2245
2139
|
dryRun: opts.dryRun,
|
|
2246
2140
|
resume: opts.resume,
|
|
2247
|
-
sandbox: opts.sandbox,
|
|
2248
2141
|
sshPort: opts.sshPort,
|
|
2249
2142
|
sshUser: opts.sshUser,
|
|
2250
2143
|
sshKey: opts.sshKey,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lead-routing/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Self-hosted deployment CLI for Lead Routing",
|
|
5
5
|
"homepage": "https://github.com/lead-routing/lead-routing",
|
|
6
6
|
"keywords": [
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
],
|
|
13
13
|
"type": "module",
|
|
14
14
|
"bin": {
|
|
15
|
-
"lead-routing": "
|
|
15
|
+
"lead-routing": "dist/index.js"
|
|
16
16
|
},
|
|
17
17
|
"engines": {
|
|
18
18
|
"node": ">=20"
|