@lead-routing/cli 0.1.3 → 0.1.4
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 +44 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -189,7 +189,8 @@ async function collectConfig() {
|
|
|
189
189
|
validate: (v) => {
|
|
190
190
|
if (!v) return "Required";
|
|
191
191
|
try {
|
|
192
|
-
new URL(v);
|
|
192
|
+
const u = new URL(v);
|
|
193
|
+
if (u.protocol !== "https:") return "Must be an HTTPS URL (required for Salesforce OAuth)";
|
|
193
194
|
} catch {
|
|
194
195
|
return "Must be a valid URL (e.g. https://routing.acme.com)";
|
|
195
196
|
}
|
|
@@ -211,16 +212,16 @@ async function collectConfig() {
|
|
|
211
212
|
}
|
|
212
213
|
});
|
|
213
214
|
if (isCancel2(engineUrl)) bail2(engineUrl);
|
|
214
|
-
const callbackUrl = `${appUrl}/api/auth/callback`;
|
|
215
|
+
const callbackUrl = `${appUrl.trim().replace(/\/+$/, "")}/api/auth/sfdc/callback`;
|
|
215
216
|
note2(
|
|
216
217
|
`You need a Salesforce Connected App. If you haven't created one yet:
|
|
217
218
|
|
|
218
219
|
1. Go to Salesforce Setup \u2192 App Manager \u2192 New Connected App
|
|
219
220
|
2. Connected App Name: Lead Routing
|
|
220
221
|
3. Check "Enable OAuth Settings"
|
|
221
|
-
4. Callback URL:
|
|
222
|
+
4. Callback URL (copy exactly \u2014 must match):
|
|
222
223
|
${callbackUrl}
|
|
223
|
-
5. Selected Scopes: api \u2022 refresh_token, offline_access
|
|
224
|
+
5. Selected Scopes: api \u2022 refresh_token, offline_access
|
|
224
225
|
6. Check "Require Secret for Web Server Flow"
|
|
225
226
|
7. Save \u2014 wait ~2 min, then click "Manage Consumer Details"
|
|
226
227
|
8. Copy the Consumer Key (Client ID) and Consumer Secret below`,
|
|
@@ -342,8 +343,8 @@ async function collectConfig() {
|
|
|
342
343
|
return {
|
|
343
344
|
appUrl: appUrl.trim().replace(/\/+$/, ""),
|
|
344
345
|
engineUrl: engineUrl.trim().replace(/\/+$/, ""),
|
|
345
|
-
sfdcClientId,
|
|
346
|
-
sfdcClientSecret,
|
|
346
|
+
sfdcClientId: sfdcClientId.trim(),
|
|
347
|
+
sfdcClientSecret: sfdcClientSecret.trim(),
|
|
347
348
|
sfdcLoginUrl,
|
|
348
349
|
orgAlias,
|
|
349
350
|
managedDb,
|
|
@@ -362,8 +363,9 @@ async function collectConfig() {
|
|
|
362
363
|
}
|
|
363
364
|
|
|
364
365
|
// src/steps/generate-files.ts
|
|
365
|
-
import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
366
|
-
import { join as join2 } from "path";
|
|
366
|
+
import { mkdirSync, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
|
|
367
|
+
import { join as join2, dirname } from "path";
|
|
368
|
+
import { fileURLToPath } from "url";
|
|
367
369
|
import { log as log2 } from "@clack/prompts";
|
|
368
370
|
|
|
369
371
|
// src/templates/docker-compose.ts
|
|
@@ -618,6 +620,15 @@ function findInstallDir(startDir = process.cwd()) {
|
|
|
618
620
|
}
|
|
619
621
|
|
|
620
622
|
// src/steps/generate-files.ts
|
|
623
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
624
|
+
function getCliVersion() {
|
|
625
|
+
try {
|
|
626
|
+
const pkg = JSON.parse(readFileSync2(join2(__dirname, "../../package.json"), "utf8"));
|
|
627
|
+
return pkg.version ?? "0.1.0";
|
|
628
|
+
} catch {
|
|
629
|
+
return "0.1.0";
|
|
630
|
+
}
|
|
631
|
+
}
|
|
621
632
|
function generateFiles(cfg, sshCfg) {
|
|
622
633
|
const dir = join2(process.cwd(), "lead-routing");
|
|
623
634
|
mkdirSync(dir, { recursive: true });
|
|
@@ -681,7 +692,7 @@ function generateFiles(cfg, sshCfg) {
|
|
|
681
692
|
sfdcClientId: cfg.sfdcClientId,
|
|
682
693
|
sfdcLoginUrl: cfg.sfdcLoginUrl,
|
|
683
694
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
684
|
-
version:
|
|
695
|
+
version: getCliVersion()
|
|
685
696
|
});
|
|
686
697
|
log2.success("Generated lead-routing.json");
|
|
687
698
|
return { dir, composeFile, envWeb, envEngine, adminSecret: cfg.adminSecret };
|
|
@@ -905,11 +916,11 @@ function sleep(ms) {
|
|
|
905
916
|
import * as fs from "fs";
|
|
906
917
|
import * as path from "path";
|
|
907
918
|
import * as crypto from "crypto";
|
|
908
|
-
import { fileURLToPath } from "url";
|
|
919
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
909
920
|
import { execa as execa2 } from "execa";
|
|
910
921
|
import { spinner as spinner4 } from "@clack/prompts";
|
|
911
|
-
var __filename =
|
|
912
|
-
var
|
|
922
|
+
var __filename = fileURLToPath2(import.meta.url);
|
|
923
|
+
var __dirname2 = path.dirname(__filename);
|
|
913
924
|
function readEnvVar(envFile, key) {
|
|
914
925
|
const content = fs.readFileSync(envFile, "utf8");
|
|
915
926
|
const match = content.match(new RegExp(`^${key}=(.+)$`, "m"));
|
|
@@ -928,11 +939,11 @@ function findPrismaBin() {
|
|
|
928
939
|
// npx / npm global install: @lead-routing/cli is nested under the scope dir,
|
|
929
940
|
// so prisma lands 3 levels above dist/ in node_modules/.bin/
|
|
930
941
|
// e.g. ~/.npm/_npx/HASH/node_modules/.bin/prisma
|
|
931
|
-
path.join(
|
|
932
|
-
path.join(
|
|
942
|
+
path.join(__dirname2, "../../../.bin/prisma"),
|
|
943
|
+
path.join(__dirname2, "../../../prisma/bin/prisma.js"),
|
|
933
944
|
// Fallback: prisma nested inside the package's own node_modules (hoisted install)
|
|
934
|
-
path.join(
|
|
935
|
-
path.join(
|
|
945
|
+
path.join(__dirname2, "../node_modules/.bin/prisma"),
|
|
946
|
+
path.join(__dirname2, "../node_modules/prisma/bin/prisma.js"),
|
|
936
947
|
// Monorepo dev paths
|
|
937
948
|
path.resolve("packages/db/node_modules/.bin/prisma"),
|
|
938
949
|
path.resolve("node_modules/.bin/prisma"),
|
|
@@ -951,7 +962,9 @@ async function runMigrations(ssh, localDir, adminEmail, adminPassword) {
|
|
|
951
962
|
tunnelClose = close;
|
|
952
963
|
s.stop(`Database tunnel open (local port ${localPort})`);
|
|
953
964
|
await applyMigrations(localDir, localPort);
|
|
954
|
-
|
|
965
|
+
if (adminEmail && adminPassword) {
|
|
966
|
+
await seedAdminUser(localDir, localPort, adminEmail, adminPassword);
|
|
967
|
+
}
|
|
955
968
|
} finally {
|
|
956
969
|
tunnelClose?.();
|
|
957
970
|
}
|
|
@@ -962,7 +975,7 @@ async function applyMigrations(localDir, localPort) {
|
|
|
962
975
|
try {
|
|
963
976
|
const DATABASE_URL = getTunneledDbUrl(localDir, localPort);
|
|
964
977
|
const prismaBin = findPrismaBin();
|
|
965
|
-
const bundledSchema = path.join(
|
|
978
|
+
const bundledSchema = path.join(__dirname2, "prisma/schema.prisma");
|
|
966
979
|
const monoSchema = path.resolve("packages/db/prisma/schema.prisma");
|
|
967
980
|
const schemaPath = fs.existsSync(bundledSchema) ? bundledSchema : monoSchema;
|
|
968
981
|
await execa2(prismaBin, ["migrate", "deploy", "--schema", schemaPath], {
|
|
@@ -1086,13 +1099,13 @@ function sleep2(ms) {
|
|
|
1086
1099
|
}
|
|
1087
1100
|
|
|
1088
1101
|
// src/steps/sfdc-deploy-inline.ts
|
|
1089
|
-
import { readFileSync as
|
|
1090
|
-
import { join as join5, dirname as
|
|
1102
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, cpSync, rmSync } from "fs";
|
|
1103
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
1091
1104
|
import { tmpdir } from "os";
|
|
1092
|
-
import { fileURLToPath as
|
|
1105
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1093
1106
|
import { spinner as spinner6, log as log6 } from "@clack/prompts";
|
|
1094
1107
|
import { execa as execa3 } from "execa";
|
|
1095
|
-
var
|
|
1108
|
+
var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
|
|
1096
1109
|
function patchXml(content, tag, value) {
|
|
1097
1110
|
const re = new RegExp(`(<${tag}>)[^<]*(</\\s*${tag}>)`, "g");
|
|
1098
1111
|
return content.replace(re, `$1${value}$2`);
|
|
@@ -1118,8 +1131,8 @@ async function sfdcDeployInline(params) {
|
|
|
1118
1131
|
}
|
|
1119
1132
|
}
|
|
1120
1133
|
s.start("Copying Salesforce package\u2026");
|
|
1121
|
-
const inDist = join5(
|
|
1122
|
-
const nextToDist = join5(
|
|
1134
|
+
const inDist = join5(__dirname3, "sfdc-package");
|
|
1135
|
+
const nextToDist = join5(__dirname3, "..", "sfdc-package");
|
|
1123
1136
|
const bundledPkg = existsSync4(inDist) ? inDist : nextToDist;
|
|
1124
1137
|
const destPkg = join5(installDir ?? tmpdir(), "lead-routing-sfdc-package");
|
|
1125
1138
|
if (!existsSync4(bundledPkg)) {
|
|
@@ -1141,7 +1154,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1141
1154
|
"RoutingEngine.namedCredential-meta.xml"
|
|
1142
1155
|
);
|
|
1143
1156
|
if (existsSync4(ncPath)) {
|
|
1144
|
-
const nc = patchXml(
|
|
1157
|
+
const nc = patchXml(readFileSync4(ncPath, "utf8"), "endpoint", engineUrl);
|
|
1145
1158
|
writeFileSync3(ncPath, nc, "utf8");
|
|
1146
1159
|
}
|
|
1147
1160
|
const rssEnginePath = join5(
|
|
@@ -1153,7 +1166,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1153
1166
|
"LeadRouterEngine.remoteSite-meta.xml"
|
|
1154
1167
|
);
|
|
1155
1168
|
if (existsSync4(rssEnginePath)) {
|
|
1156
|
-
let rss = patchXml(
|
|
1169
|
+
let rss = patchXml(readFileSync4(rssEnginePath, "utf8"), "url", engineUrl);
|
|
1157
1170
|
rss = patchXml(rss, "description", "Lead Router Engine endpoint");
|
|
1158
1171
|
writeFileSync3(rssEnginePath, rss, "utf8");
|
|
1159
1172
|
}
|
|
@@ -1166,7 +1179,7 @@ The CLI may need to be reinstalled: npm i -g @lead-routing/cli`
|
|
|
1166
1179
|
"LeadRouterApp.remoteSite-meta.xml"
|
|
1167
1180
|
);
|
|
1168
1181
|
if (existsSync4(rssAppPath)) {
|
|
1169
|
-
let rss = patchXml(
|
|
1182
|
+
let rss = patchXml(readFileSync4(rssAppPath, "utf8"), "url", appUrl);
|
|
1170
1183
|
rss = patchXml(rss, "description", "Lead Router App URL");
|
|
1171
1184
|
writeFileSync3(rssAppPath, rss, "utf8");
|
|
1172
1185
|
}
|
|
@@ -1628,7 +1641,7 @@ async function runDeploy() {
|
|
|
1628
1641
|
await ssh.exec("docker compose up -d --remove-orphans", remoteDir);
|
|
1629
1642
|
log9.success("Services restarted");
|
|
1630
1643
|
log9.step("Running database migrations");
|
|
1631
|
-
await runMigrations(ssh, dir
|
|
1644
|
+
await runMigrations(ssh, dir);
|
|
1632
1645
|
outro2(
|
|
1633
1646
|
chalk3.green("\u2714 Deployment complete!") + `
|
|
1634
1647
|
|
|
@@ -1780,7 +1793,7 @@ async function runStatus() {
|
|
|
1780
1793
|
}
|
|
1781
1794
|
|
|
1782
1795
|
// src/commands/config.ts
|
|
1783
|
-
import { readFileSync as
|
|
1796
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
|
|
1784
1797
|
import { join as join7 } from "path";
|
|
1785
1798
|
import { intro as intro4, outro as outro4, text as text3, password as password3, spinner as spinner7, log as log13 } from "@clack/prompts";
|
|
1786
1799
|
import chalk5 from "chalk";
|
|
@@ -1788,7 +1801,7 @@ import { execa as execa7 } from "execa";
|
|
|
1788
1801
|
function parseEnv(filePath) {
|
|
1789
1802
|
const map = /* @__PURE__ */ new Map();
|
|
1790
1803
|
if (!existsSync5(filePath)) return map;
|
|
1791
|
-
for (const line of
|
|
1804
|
+
for (const line of readFileSync5(filePath, "utf8").split("\n")) {
|
|
1792
1805
|
const trimmed = line.trim();
|
|
1793
1806
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1794
1807
|
const eq = trimmed.indexOf("=");
|
|
@@ -1798,7 +1811,7 @@ function parseEnv(filePath) {
|
|
|
1798
1811
|
return map;
|
|
1799
1812
|
}
|
|
1800
1813
|
function writeEnv(filePath, updates) {
|
|
1801
|
-
const lines = existsSync5(filePath) ?
|
|
1814
|
+
const lines = existsSync5(filePath) ? readFileSync5(filePath, "utf8").split("\n") : [];
|
|
1802
1815
|
const updated = /* @__PURE__ */ new Set();
|
|
1803
1816
|
const result = lines.map((line) => {
|
|
1804
1817
|
const trimmed = line.trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lead-routing/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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"],
|