@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.
Files changed (2) hide show
  1. package/dist/index.js +44 -31
  2. 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 \u2022 openid
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: "0.1.0"
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 = fileURLToPath(import.meta.url);
912
- var __dirname = path.dirname(__filename);
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(__dirname, "../../../.bin/prisma"),
932
- path.join(__dirname, "../../../prisma/bin/prisma.js"),
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(__dirname, "../node_modules/.bin/prisma"),
935
- path.join(__dirname, "../node_modules/prisma/bin/prisma.js"),
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
- await seedAdminUser(localDir, localPort, adminEmail, adminPassword);
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(__dirname, "prisma/schema.prisma");
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 readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync4, cpSync, rmSync } from "fs";
1090
- import { join as join5, dirname as dirname2 } from "path";
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 fileURLToPath2 } from "url";
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 __dirname2 = dirname2(fileURLToPath2(import.meta.url));
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(__dirname2, "sfdc-package");
1122
- const nextToDist = join5(__dirname2, "..", "sfdc-package");
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(readFileSync3(ncPath, "utf8"), "endpoint", engineUrl);
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(readFileSync3(rssEnginePath, "utf8"), "url", engineUrl);
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(readFileSync3(rssAppPath, "utf8"), "url", appUrl);
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 readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
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 readFileSync4(filePath, "utf8").split("\n")) {
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) ? readFileSync4(filePath, "utf8").split("\n") : [];
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",
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"],