@delegance/cli 0.2.0 → 0.2.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/README.md +170 -64
- package/dist/bin/delegance.js +437 -113
- package/dist/index.d.ts +17 -3
- package/dist/index.js +436 -110
- package/package.json +13 -7
package/dist/bin/delegance.js
CHANGED
|
@@ -24,13 +24,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/bin/delegance.ts
|
|
27
|
-
var
|
|
27
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
28
28
|
|
|
29
29
|
// src/program.ts
|
|
30
30
|
var import_commander19 = require("commander");
|
|
31
31
|
|
|
32
32
|
// src/version.ts
|
|
33
|
-
var CLI_VERSION = "0.2.
|
|
33
|
+
var CLI_VERSION = "0.2.2";
|
|
34
34
|
|
|
35
35
|
// src/commands/auth.ts
|
|
36
36
|
var os4 = __toESM(require("os"));
|
|
@@ -235,12 +235,28 @@ function formatValue(value) {
|
|
|
235
235
|
}
|
|
236
236
|
return String(value);
|
|
237
237
|
}
|
|
238
|
+
function visibleWidth(s) {
|
|
239
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
240
|
+
}
|
|
241
|
+
function renderCompactTable(headers, rows) {
|
|
242
|
+
const cells = rows.map((r) => headers.map((_, i) => r[i] === null || r[i] === void 0 ? "-" : String(r[i])));
|
|
243
|
+
const widths = headers.map((h, i) => Math.max(visibleWidth(h), ...cells.map((r) => visibleWidth(r[i]))));
|
|
244
|
+
const line = (parts) => ` ${parts.map((p, i) => p + " ".repeat(widths[i] - visibleWidth(p))).join(" ")}`.replace(/\s+$/, "");
|
|
245
|
+
return [
|
|
246
|
+
line(headers.map((h) => import_chalk.default.bold(h))),
|
|
247
|
+
import_chalk.default.dim(line(widths.map((w) => "\u2500".repeat(w)))),
|
|
248
|
+
...cells.map(line)
|
|
249
|
+
].join("\n");
|
|
250
|
+
}
|
|
238
251
|
function table(headers, rows, options = {}) {
|
|
252
|
+
if (options.compact) {
|
|
253
|
+
console.log(renderCompactTable(headers, rows));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
239
256
|
const tbl = new import_cli_table3.default({
|
|
240
257
|
head: headers.map((h) => import_chalk.default.bold(h)),
|
|
241
258
|
style: {
|
|
242
|
-
head: ["cyan"]
|
|
243
|
-
compact: options.compact
|
|
259
|
+
head: ["cyan"]
|
|
244
260
|
}
|
|
245
261
|
});
|
|
246
262
|
for (const row of rows) {
|
|
@@ -262,6 +278,15 @@ function formatDate(dateStr) {
|
|
|
262
278
|
minute: "2-digit"
|
|
263
279
|
});
|
|
264
280
|
}
|
|
281
|
+
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
282
|
+
function formatDateOnly(iso) {
|
|
283
|
+
if (!iso) return "";
|
|
284
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
|
|
285
|
+
if (!m) return iso;
|
|
286
|
+
const month = MONTHS[Number(m[2]) - 1];
|
|
287
|
+
if (!month) return iso;
|
|
288
|
+
return `${month} ${Number(m[3])}, ${m[1]}`;
|
|
289
|
+
}
|
|
265
290
|
function formatFileSize(bytes) {
|
|
266
291
|
if (bytes === void 0) return import_chalk.default.gray("(unknown)");
|
|
267
292
|
const units = ["B", "KB", "MB", "GB"];
|
|
@@ -371,14 +396,26 @@ function installLastCommandRecorder(program2) {
|
|
|
371
396
|
});
|
|
372
397
|
}
|
|
373
398
|
|
|
399
|
+
// src/utils/invocation.ts
|
|
400
|
+
var NPX_INVOCATION = "npx -y @delegance/cli";
|
|
401
|
+
function cliInvocation(env = process.env, argv = process.argv) {
|
|
402
|
+
const entry = (argv[1] ?? "").replace(/\\/g, "/");
|
|
403
|
+
if (entry.includes("/_npx/")) return NPX_INVOCATION;
|
|
404
|
+
if (env.npm_command === "exec" && /(^|\/)delegance(\.[cm]?js)?$/.test(entry)) return NPX_INVOCATION;
|
|
405
|
+
return "delegance";
|
|
406
|
+
}
|
|
407
|
+
function cliCommand(rest) {
|
|
408
|
+
return `${cliInvocation()} ${rest}`;
|
|
409
|
+
}
|
|
410
|
+
|
|
374
411
|
// src/utils/client.ts
|
|
375
412
|
var PORTAL_URL = "https://portal.delegancebrokerage.com";
|
|
376
413
|
function getClient() {
|
|
377
414
|
const apiKey = getApiKey();
|
|
378
415
|
if (!apiKey) {
|
|
379
416
|
error("Not authenticated");
|
|
380
|
-
info(
|
|
381
|
-
info(
|
|
417
|
+
info(`New to Delegance? Run "${cliInvocation()} signup" to create an account.`);
|
|
418
|
+
info(`Have an API key? Run "${cliInvocation()} auth login", or set DELEGANCE_API_KEY.`);
|
|
382
419
|
process.exit(1);
|
|
383
420
|
}
|
|
384
421
|
return new import_sdk.DeleganceClient({
|
|
@@ -390,7 +427,7 @@ function getCompanyId(options) {
|
|
|
390
427
|
const companyId = options.company || getConfigValue("defaultCompanyId");
|
|
391
428
|
if (!companyId) {
|
|
392
429
|
error("No company specified");
|
|
393
|
-
info(
|
|
430
|
+
info(`Use --company <id> or set a default with "${cliInvocation()} company use <id>"`);
|
|
394
431
|
process.exit(1);
|
|
395
432
|
}
|
|
396
433
|
return companyId;
|
|
@@ -402,9 +439,9 @@ function describeApiError(err) {
|
|
|
402
439
|
}
|
|
403
440
|
if (err instanceof import_sdk.AuthenticationError) {
|
|
404
441
|
if (err.reason === "key_expired" || err.reason === "key_membership_revoked") {
|
|
405
|
-
return { message: `${err.message}${traceSuffix}`, hint:
|
|
442
|
+
return { message: `${err.message}${traceSuffix}`, hint: `Run "${cliInvocation()} auth login" to sign in again.` };
|
|
406
443
|
}
|
|
407
|
-
return { message: `${err.message}${traceSuffix}`, hint:
|
|
444
|
+
return { message: `${err.message}${traceSuffix}`, hint: `Check your key with "${cliInvocation()} auth status", or log in again.` };
|
|
408
445
|
}
|
|
409
446
|
if (err instanceof import_sdk.ForbiddenError) {
|
|
410
447
|
return { message: `${err.message}${traceSuffix}`, hint: "Your API key lacks the scope for this command. Create a key with the right scope in the portal." };
|
|
@@ -541,7 +578,7 @@ function exchangeRefusal(err) {
|
|
|
541
578
|
case "already_collected":
|
|
542
579
|
return `The key for this signup was already collected. If you lost it, create a new key at ${PORTAL_URL}/portal/settings/api-keys`;
|
|
543
580
|
case "expired":
|
|
544
|
-
return
|
|
581
|
+
return `This signup expired (24 hours). Run "${cliInvocation()} signup" again.`;
|
|
545
582
|
default:
|
|
546
583
|
return null;
|
|
547
584
|
}
|
|
@@ -571,14 +608,14 @@ function storeOrPrintKey(apiKey, store = storeApiKey) {
|
|
|
571
608
|
return true;
|
|
572
609
|
} catch (err) {
|
|
573
610
|
error(`Could not save the key to ~/.delegance/credentials: ${err.message}`);
|
|
574
|
-
warn(
|
|
611
|
+
warn(`Your key is below. It is shown once. Save it now, then run "${cliInvocation()} auth login --key <key>".`);
|
|
575
612
|
console.log(apiKey);
|
|
576
613
|
return false;
|
|
577
614
|
}
|
|
578
615
|
}
|
|
579
616
|
async function waitAndStore(pending, timeoutMs) {
|
|
580
617
|
const signup = (0, import_sdk2.createSignup)({ baseUrl: pending.baseUrl });
|
|
581
|
-
const spinner = (0, import_ora.default)(`Waiting for you to click the link sent to ${pending.email} (Ctrl-C is safe: resume with "
|
|
618
|
+
const spinner = (0, import_ora.default)(`Waiting for you to click the link sent to ${pending.email} (Ctrl-C is safe: resume with "${cliInvocation()} verify")`).start();
|
|
582
619
|
const started = Date.now();
|
|
583
620
|
try {
|
|
584
621
|
const result = await signup.waitForKey(pending.enrollmentId, pending.deviceSecret, {
|
|
@@ -590,7 +627,7 @@ async function waitAndStore(pending, timeoutMs) {
|
|
|
590
627
|
});
|
|
591
628
|
if (result.status !== "ready") {
|
|
592
629
|
spinner.warn("Still waiting on the email confirmation.");
|
|
593
|
-
info(
|
|
630
|
+
info(`Run "${cliInvocation()} verify" after you click the link.`);
|
|
594
631
|
return;
|
|
595
632
|
}
|
|
596
633
|
spinner.stop();
|
|
@@ -598,7 +635,7 @@ async function waitAndStore(pending, timeoutMs) {
|
|
|
598
635
|
setConfig("defaultCompanyId", result.companyId);
|
|
599
636
|
clearPendingSignup();
|
|
600
637
|
if (stored) success(`Verified. API key ${maskApiKey(result.apiKey)} saved to ~/.delegance/credentials`);
|
|
601
|
-
success(
|
|
638
|
+
success(`You can quote now: ${cliInvocation()} quote start --coverage gl`);
|
|
602
639
|
} catch (err) {
|
|
603
640
|
spinner.stop();
|
|
604
641
|
const why = exchangeRefusal(err);
|
|
@@ -632,7 +669,7 @@ function createSignupCommand() {
|
|
|
632
669
|
const existing = loadPendingSignup();
|
|
633
670
|
if (existing) {
|
|
634
671
|
warn(`A signup for ${existing.email} is already waiting for its email confirmation.`);
|
|
635
|
-
info(
|
|
672
|
+
info(`Run "${cliInvocation()} verify" to finish it, or "${cliInvocation()} verify --cancel" to start over.`);
|
|
636
673
|
process.exit(1);
|
|
637
674
|
}
|
|
638
675
|
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -697,7 +734,7 @@ function createSignupCommand() {
|
|
|
697
734
|
warn("We could not save your business address. Add it in the portal before quoting, or carriers cannot price you.");
|
|
698
735
|
}
|
|
699
736
|
if (options.wait === false) {
|
|
700
|
-
info(
|
|
737
|
+
info(`After you click it, run "${cliInvocation()} verify" to collect your API key.`);
|
|
701
738
|
return;
|
|
702
739
|
}
|
|
703
740
|
await waitAndStore(pending, Number(options.timeout) * 6e4 || 15 * 6e4);
|
|
@@ -718,7 +755,7 @@ function createVerifyCommand() {
|
|
|
718
755
|
return new import_commander.Command("verify").description("Finish a signup: wait for the email click and store your API key").option("--resend", "Send the confirm email again").option("--cancel", "Forget the in-progress signup on this machine").option("--timeout <minutes>", "How long to wait", "15").action(async (options) => {
|
|
719
756
|
const pending = loadPendingSignup();
|
|
720
757
|
if (!pending) {
|
|
721
|
-
info(
|
|
758
|
+
info(`No signup is waiting on this machine. Start one with "${cliInvocation()} signup".`);
|
|
722
759
|
return;
|
|
723
760
|
}
|
|
724
761
|
if (options.cancel) {
|
|
@@ -780,9 +817,9 @@ function deviceLoginRefusal(err) {
|
|
|
780
817
|
case "access_denied":
|
|
781
818
|
return "The request was denied in the portal. Nothing was stored.";
|
|
782
819
|
case "expired":
|
|
783
|
-
return
|
|
820
|
+
return `The code expired before it was approved. Run "${cliInvocation()} auth login" again.`;
|
|
784
821
|
case "already_collected":
|
|
785
|
-
return
|
|
822
|
+
return `This login was already used. Run "${cliInvocation()} auth login" again.`;
|
|
786
823
|
default:
|
|
787
824
|
return null;
|
|
788
825
|
}
|
|
@@ -791,7 +828,7 @@ function notAvailable(err) {
|
|
|
791
828
|
return err instanceof import_sdk3.DeleganceError && (err.statusCode === 404 || err.reason === "not_available");
|
|
792
829
|
}
|
|
793
830
|
function pasteFallbackHint() {
|
|
794
|
-
info(`Create a key at ${PORTAL_URL}/portal/settings/api-keys, then run "
|
|
831
|
+
info(`Create a key at ${PORTAL_URL}/portal/settings/api-keys, then run "${cliInvocation()} auth login --key <key>".`);
|
|
795
832
|
}
|
|
796
833
|
async function readKeyInfo(apiKey, timeoutMs = 5e3) {
|
|
797
834
|
try {
|
|
@@ -868,7 +905,7 @@ async function loginWithBrowser(openBrowser) {
|
|
|
868
905
|
});
|
|
869
906
|
if (result.status !== "ready") {
|
|
870
907
|
spinner.fail("The code expired before it was approved.");
|
|
871
|
-
info(
|
|
908
|
+
info(`Run "${cliInvocation()} auth login" again.`);
|
|
872
909
|
process.exit(1);
|
|
873
910
|
}
|
|
874
911
|
spinner.stop();
|
|
@@ -925,7 +962,7 @@ function createAuthCommand() {
|
|
|
925
962
|
header("Authentication Status");
|
|
926
963
|
if (!isAuthenticated()) {
|
|
927
964
|
warn("Not authenticated");
|
|
928
|
-
info(
|
|
965
|
+
info(`Run "${cliInvocation()} auth login" to sign in, or "${cliInvocation()} signup" to create an account`);
|
|
929
966
|
return;
|
|
930
967
|
}
|
|
931
968
|
const apiKey = getApiKey() || "";
|
|
@@ -948,7 +985,7 @@ function createAuthCommand() {
|
|
|
948
985
|
}
|
|
949
986
|
if (refused) {
|
|
950
987
|
warn(refused);
|
|
951
|
-
info(
|
|
988
|
+
info(`Run "${cliInvocation()} auth login" to sign in again.`);
|
|
952
989
|
} else {
|
|
953
990
|
success("Authenticated");
|
|
954
991
|
}
|
|
@@ -966,7 +1003,7 @@ function createAuthCommand() {
|
|
|
966
1003
|
const apiKey = getApiKey();
|
|
967
1004
|
if (!apiKey) {
|
|
968
1005
|
error("Not authenticated");
|
|
969
|
-
info(
|
|
1006
|
+
info(`Run "${cliInvocation()} auth login" to sign in`);
|
|
970
1007
|
process.exit(1);
|
|
971
1008
|
}
|
|
972
1009
|
const spinner = (0, import_ora2.default)("Refreshing your key...").start();
|
|
@@ -1014,7 +1051,7 @@ function createAuthCommand() {
|
|
|
1014
1051
|
auth.command("whoami").description("Show current authenticated identity").action(async () => {
|
|
1015
1052
|
if (!isAuthenticated()) {
|
|
1016
1053
|
error("Not authenticated");
|
|
1017
|
-
info(
|
|
1054
|
+
info(`Run "${cliInvocation()} auth login" to authenticate`);
|
|
1018
1055
|
process.exit(1);
|
|
1019
1056
|
}
|
|
1020
1057
|
const apiKey = getApiKey();
|
|
@@ -1210,7 +1247,7 @@ function createCompanyCommand() {
|
|
|
1210
1247
|
const companyId = getConfigValue("defaultCompanyId");
|
|
1211
1248
|
if (!companyId) {
|
|
1212
1249
|
info("No default company set");
|
|
1213
|
-
info(
|
|
1250
|
+
info(`Use "${cliInvocation()} company use <id>" to set one`);
|
|
1214
1251
|
return;
|
|
1215
1252
|
}
|
|
1216
1253
|
const client = getClient();
|
|
@@ -1484,7 +1521,7 @@ function createDocumentCommand() {
|
|
|
1484
1521
|
process.exit(1);
|
|
1485
1522
|
} else {
|
|
1486
1523
|
waitSpinner.warn(`Extraction still ${finished.status}`);
|
|
1487
|
-
if (!options.json) info(`Run "
|
|
1524
|
+
if (!options.json) info(`Run "${cliInvocation()} doc wait ${documentId}" to keep following it`);
|
|
1488
1525
|
}
|
|
1489
1526
|
return;
|
|
1490
1527
|
}
|
|
@@ -1492,7 +1529,7 @@ function createDocumentCommand() {
|
|
|
1492
1529
|
json(result);
|
|
1493
1530
|
} else {
|
|
1494
1531
|
info(`${result.reprocessesRemaining} reprocess attempts left today`);
|
|
1495
|
-
info(`Run "
|
|
1532
|
+
info(`Run "${cliInvocation()} doc wait ${documentId}" to follow it`);
|
|
1496
1533
|
}
|
|
1497
1534
|
} catch (err) {
|
|
1498
1535
|
spinner.fail("Failed to reprocess document");
|
|
@@ -1640,6 +1677,90 @@ var import_inquirer5 = __toESM(require("inquirer"));
|
|
|
1640
1677
|
var import_ora6 = __toESM(require("ora"));
|
|
1641
1678
|
var import_sdk4 = require("@delegance/sdk");
|
|
1642
1679
|
|
|
1680
|
+
// src/utils/coverage-names.ts
|
|
1681
|
+
var NAMES = {
|
|
1682
|
+
// General liability
|
|
1683
|
+
cgl: "General Liability",
|
|
1684
|
+
gl: "General Liability",
|
|
1685
|
+
general_liability: "General Liability",
|
|
1686
|
+
// Workers' comp
|
|
1687
|
+
wc: "Workers' Comp",
|
|
1688
|
+
workers_comp: "Workers' Comp",
|
|
1689
|
+
workers_compensation: "Workers' Comp",
|
|
1690
|
+
// Business owner's policy
|
|
1691
|
+
bop: "Business Owner's Policy",
|
|
1692
|
+
business_owners: "Business Owner's Policy",
|
|
1693
|
+
business_owners_policy: "Business Owner's Policy",
|
|
1694
|
+
// Cyber
|
|
1695
|
+
cyber: "Cyber",
|
|
1696
|
+
cyber_liability: "Cyber",
|
|
1697
|
+
// Professional liability (E&O)
|
|
1698
|
+
mpl: "Professional Liability (E&O)",
|
|
1699
|
+
pl: "Professional Liability (E&O)",
|
|
1700
|
+
pro_liab: "Professional Liability (E&O)",
|
|
1701
|
+
professional_liability: "Professional Liability (E&O)",
|
|
1702
|
+
eo: "Professional Liability (E&O)",
|
|
1703
|
+
e_o: "Professional Liability (E&O)",
|
|
1704
|
+
tech_eo: "Tech E&O",
|
|
1705
|
+
errors_omissions: "Tech E&O",
|
|
1706
|
+
// Auto
|
|
1707
|
+
ca: "Commercial Auto",
|
|
1708
|
+
auto: "Commercial Auto",
|
|
1709
|
+
commercial_auto: "Commercial Auto",
|
|
1710
|
+
// Property
|
|
1711
|
+
cp: "Commercial Property",
|
|
1712
|
+
property: "Commercial Property",
|
|
1713
|
+
commercial_property: "Commercial Property",
|
|
1714
|
+
// Umbrella
|
|
1715
|
+
umbrella: "Umbrella",
|
|
1716
|
+
excess: "Umbrella",
|
|
1717
|
+
commercial_umbrella: "Umbrella",
|
|
1718
|
+
// Builders risk
|
|
1719
|
+
br: "Builders' Risk",
|
|
1720
|
+
builders_risk: "Builders' Risk",
|
|
1721
|
+
// Inland marine and marine
|
|
1722
|
+
im: "Inland Marine",
|
|
1723
|
+
inland_marine: "Inland Marine",
|
|
1724
|
+
marine: "Marine",
|
|
1725
|
+
// Management liability family
|
|
1726
|
+
ml: "Management Liability",
|
|
1727
|
+
management_liability: "Management Liability",
|
|
1728
|
+
do: "Directors and Officers (D&O)",
|
|
1729
|
+
d_o: "Directors and Officers (D&O)",
|
|
1730
|
+
directors_officers: "Directors and Officers (D&O)",
|
|
1731
|
+
epli: "Employment Practices Liability",
|
|
1732
|
+
employment_practices: "Employment Practices Liability",
|
|
1733
|
+
crime: "Crime",
|
|
1734
|
+
fiduciary: "Fiduciary Liability",
|
|
1735
|
+
fiduciary_liability: "Fiduciary Liability",
|
|
1736
|
+
// Everything else on the canonical list
|
|
1737
|
+
product_liability: "Product Liability",
|
|
1738
|
+
liquor_liability: "Liquor Liability",
|
|
1739
|
+
contractors_liability: "Contractors' Liability",
|
|
1740
|
+
equipment_breakdown: "Equipment Breakdown",
|
|
1741
|
+
environmental_liability: "Environmental Liability",
|
|
1742
|
+
healthcare_medical: "Healthcare Medical Liability",
|
|
1743
|
+
life_sciences: "Life Sciences",
|
|
1744
|
+
hospitality_programs: "Hospitality Program",
|
|
1745
|
+
commercial_flood: "Commercial Flood",
|
|
1746
|
+
home_based_business: "Home-Based Business",
|
|
1747
|
+
foreign_package: "Foreign Package",
|
|
1748
|
+
multinational_package: "Multinational Package",
|
|
1749
|
+
political_risk: "Political Risk",
|
|
1750
|
+
trade_credit: "Trade Credit",
|
|
1751
|
+
kidnap_ransom: "Kidnap and Ransom",
|
|
1752
|
+
product_recall: "Product Recall",
|
|
1753
|
+
railroad_liability: "Railroad Liability"
|
|
1754
|
+
};
|
|
1755
|
+
function coverageName(code) {
|
|
1756
|
+
if (code === null || code === void 0) return "";
|
|
1757
|
+
const key = code.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1758
|
+
return NAMES[key] ?? code;
|
|
1759
|
+
}
|
|
1760
|
+
function coverageNames(codes) {
|
|
1761
|
+
return codes.map(coverageName).filter(Boolean).join(", ");
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1643
1764
|
// src/commerce/questions.ts
|
|
1644
1765
|
var COVERAGE_ALIASES = {
|
|
1645
1766
|
gl: "general_liability",
|
|
@@ -1718,14 +1839,23 @@ function validateAnswers(questions, provided) {
|
|
|
1718
1839
|
}
|
|
1719
1840
|
return { answers, problems };
|
|
1720
1841
|
}
|
|
1842
|
+
function rowStatus(q) {
|
|
1843
|
+
if (String(q.state).toUpperCase() === "REFERRED") {
|
|
1844
|
+
return q.resultStatus === "needs_info" ? "needs_info" : "referred";
|
|
1845
|
+
}
|
|
1846
|
+
return q.marketStatus || q.state;
|
|
1847
|
+
}
|
|
1721
1848
|
function sortQuoteRows(quotes) {
|
|
1722
1849
|
return quotes.map((q) => ({
|
|
1723
1850
|
carrier: q.carrierName || q.carrierId,
|
|
1724
1851
|
coverage: q.coverage ?? "",
|
|
1725
1852
|
premium: q.premium,
|
|
1726
|
-
status: q
|
|
1853
|
+
status: rowStatus(q),
|
|
1727
1854
|
bindable: q.bindable === true && q.state === "QUOTED",
|
|
1728
|
-
opportunityId: q.opportunityId
|
|
1855
|
+
opportunityId: q.opportunityId,
|
|
1856
|
+
limits: typeof q.limits === "string" && q.limits.trim() ? q.limits : null,
|
|
1857
|
+
deductible: typeof q.deductible === "number" && Number.isFinite(q.deductible) ? q.deductible : null,
|
|
1858
|
+
message: typeof q.message === "string" && q.message.trim() ? q.message.trim() : null
|
|
1729
1859
|
})).sort((a, b) => {
|
|
1730
1860
|
if (a.bindable !== b.bindable) return a.bindable ? -1 : 1;
|
|
1731
1861
|
if (a.premium === null && b.premium === null) return 0;
|
|
@@ -1734,8 +1864,50 @@ function sortQuoteRows(quotes) {
|
|
|
1734
1864
|
return a.premium - b.premium;
|
|
1735
1865
|
});
|
|
1736
1866
|
}
|
|
1737
|
-
function
|
|
1738
|
-
|
|
1867
|
+
function bestPerCoverage(rows) {
|
|
1868
|
+
const best = /* @__PURE__ */ new Map();
|
|
1869
|
+
for (const r of rows) {
|
|
1870
|
+
if (!r.bindable || r.premium === null) continue;
|
|
1871
|
+
const key = coverageName(r.coverage);
|
|
1872
|
+
const cur = best.get(key);
|
|
1873
|
+
if (!cur || r.premium < cur.premium) best.set(key, r);
|
|
1874
|
+
}
|
|
1875
|
+
return [...best.values()].sort((a, b) => a.premium - b.premium);
|
|
1876
|
+
}
|
|
1877
|
+
function groupRowsByCoverage(rows) {
|
|
1878
|
+
const order = /* @__PURE__ */ new Map();
|
|
1879
|
+
rows.forEach((r) => {
|
|
1880
|
+
const key = coverageName(r.coverage);
|
|
1881
|
+
if (!order.has(key)) order.set(key, order.size);
|
|
1882
|
+
});
|
|
1883
|
+
const rank = (r) => order.get(coverageName(r.coverage));
|
|
1884
|
+
return rows.map((r, i) => ({ r, i })).sort((a, b) => rank(a.r) - rank(b.r) || a.i - b.i).map((x) => x.r);
|
|
1885
|
+
}
|
|
1886
|
+
var STATUS_WORDS = {
|
|
1887
|
+
quote: "Quoted",
|
|
1888
|
+
quoted: "Quoted",
|
|
1889
|
+
declined: "Declined",
|
|
1890
|
+
decline: "Declined",
|
|
1891
|
+
quoting: "Pricing",
|
|
1892
|
+
pending: "Pricing",
|
|
1893
|
+
submitted: "Pricing",
|
|
1894
|
+
in_progress: "Pricing",
|
|
1895
|
+
referred: "Referred to an underwriter",
|
|
1896
|
+
refer: "Referred to an underwriter",
|
|
1897
|
+
needs_info: "Needs more info",
|
|
1898
|
+
awaiting_broker_review: "With a broker",
|
|
1899
|
+
broker_review: "With a broker",
|
|
1900
|
+
bind_requested: "Bind requested",
|
|
1901
|
+
bound: "Bound",
|
|
1902
|
+
expired: "Expired",
|
|
1903
|
+
error: "Failed",
|
|
1904
|
+
failed: "Failed"
|
|
1905
|
+
};
|
|
1906
|
+
function quoteStatusWord(status) {
|
|
1907
|
+
const key = status.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
1908
|
+
if (STATUS_WORDS[key]) return STATUS_WORDS[key];
|
|
1909
|
+
const words = key.replace(/_/g, " ").trim();
|
|
1910
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : status;
|
|
1739
1911
|
}
|
|
1740
1912
|
function summarizeStatus(s) {
|
|
1741
1913
|
const quoted = s.carrierQuotes.filter((q) => q.premium !== null).length;
|
|
@@ -1785,7 +1957,14 @@ function formatKnown(q, value) {
|
|
|
1785
1957
|
}
|
|
1786
1958
|
|
|
1787
1959
|
// src/commerce/demo.ts
|
|
1788
|
-
var DEMO_BUSINESS = "Riverbend Bakery (demo), Austin TX
|
|
1960
|
+
var DEMO_BUSINESS = "Riverbend Bakery (demo), Austin TX";
|
|
1961
|
+
var DEMO_SHORT_LABELS = {
|
|
1962
|
+
annual_revenue: "Annual revenue",
|
|
1963
|
+
employees: "Full-time employees",
|
|
1964
|
+
delivery: "Delivers orders",
|
|
1965
|
+
delivery_vehicles: "Delivery vehicles",
|
|
1966
|
+
claims_5y: "Claims in last 5 years"
|
|
1967
|
+
};
|
|
1789
1968
|
var DEMO_QUESTIONS = [
|
|
1790
1969
|
{ id: "annual_revenue", label: "Annual revenue (USD)", type: "currency", required: true, prefill: 85e4 },
|
|
1791
1970
|
{ id: "employees", label: "Full-time employees", type: "number", required: true, prefill: 6 },
|
|
@@ -1819,6 +1998,7 @@ var DEMO_QUESTIONS = [
|
|
|
1819
1998
|
];
|
|
1820
1999
|
var DEMO_STATUS = {
|
|
1821
2000
|
submissionId: "demo-submission",
|
|
2001
|
+
effectiveDate: "2026-11-01",
|
|
1822
2002
|
coverages: ["general_liability", "workers_comp"],
|
|
1823
2003
|
expiresAt: null,
|
|
1824
2004
|
submitted: true,
|
|
@@ -1826,19 +2006,24 @@ var DEMO_STATUS = {
|
|
|
1826
2006
|
allCarriersResolved: true,
|
|
1827
2007
|
awaitingBrokerReviewCount: 0,
|
|
1828
2008
|
carrierQuotes: [
|
|
1829
|
-
{ opportunityId: "demo-1", carrierId: "
|
|
1830
|
-
{ opportunityId: "demo-2", carrierId: "
|
|
1831
|
-
{ opportunityId: "demo-3", carrierId: "
|
|
1832
|
-
{ opportunityId: "demo-4", carrierId: "
|
|
2009
|
+
{ opportunityId: "demo-1", carrierId: "CARRIER_A", carrierName: "Carrier A", coverage: "CGL", state: "QUOTED", marketStatus: "quote", premium: 612, quoteNumber: "DEMO-GL-1", bindable: true, limits: "$1M per occurrence / $2M aggregate", deductible: 500 },
|
|
2010
|
+
{ opportunityId: "demo-2", carrierId: "CARRIER_B", carrierName: "Carrier B", coverage: "CGL", state: "QUOTED", marketStatus: "quote", premium: 748, quoteNumber: "DEMO-GL-2", bindable: true, limits: "$1M per occurrence / $2M aggregate", deductible: 1e3 },
|
|
2011
|
+
{ opportunityId: "demo-3", carrierId: "CARRIER_C", carrierName: "Carrier C", coverage: "WC", state: "QUOTED", marketStatus: "quote", premium: 1184, quoteNumber: "DEMO-WC-1", bindable: true, limits: "Statutory / $1M employers liability", deductible: 0 },
|
|
2012
|
+
{ opportunityId: "demo-4", carrierId: "CARRIER_D", carrierName: "Carrier D", coverage: "WC", state: "DECLINED", marketStatus: "declined", premium: null, quoteNumber: null, bindable: false, limits: null, deductible: null }
|
|
1833
2013
|
]
|
|
1834
2014
|
};
|
|
1835
2015
|
|
|
1836
2016
|
// src/commands/instant-quote.ts
|
|
1837
|
-
function
|
|
2017
|
+
function shellArg(value) {
|
|
2018
|
+
return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
2019
|
+
}
|
|
2020
|
+
function activeRequestLines(err, coverageFlag, ctx = {}) {
|
|
1838
2021
|
if (!(err instanceof import_sdk4.DeleganceError) || err.statusCode !== 409 || !err.body) return null;
|
|
1839
2022
|
const { body } = err;
|
|
1840
2023
|
const code = body.message;
|
|
1841
|
-
const
|
|
2024
|
+
const answersArg = ctx.answersFile ? ` --answers ${shellArg(ctx.answersFile)}` : "";
|
|
2025
|
+
const requote = `${cliInvocation()} quote start --coverage ${coverageFlag}${answersArg} --replace`;
|
|
2026
|
+
const replaceCmd = ctx.submit ? `${cliInvocation()} quote submit ${shellArg(ctx.submit.submissionId)} --session-version ${shellArg(ctx.submit.sessionVersion)} --answers ${ctx.answersFile ? shellArg(ctx.answersFile) : "<file>"} --replace` : requote;
|
|
1842
2027
|
if (code === "active_request_exists") {
|
|
1843
2028
|
const active = Array.isArray(body.active) ? body.active : [];
|
|
1844
2029
|
const first = active[0];
|
|
@@ -1846,7 +2031,7 @@ function activeRequestLines(err, coverageFlag) {
|
|
|
1846
2031
|
const carriers = Array.isArray(first?.carriers) ? first.carriers.map((c) => `${c.carrierId}${c.state ? ` (${c.state.toLowerCase()})` : ""}`).join(", ") : "";
|
|
1847
2032
|
return [
|
|
1848
2033
|
`Already with carriers${id ? `: ${id}` : ""}${carriers ? `, ${carriers}` : ""}.`,
|
|
1849
|
-
`Check it: ${id ?
|
|
2034
|
+
`Check it: ${id ? `${cliInvocation()} quote status ${id}` : `${cliInvocation()} quote status <id>`} Replace it: ${replaceCmd}`
|
|
1850
2035
|
];
|
|
1851
2036
|
}
|
|
1852
2037
|
if (code === "replace_conflict") {
|
|
@@ -1973,7 +2158,7 @@ function handoffLines(r) {
|
|
|
1973
2158
|
if (!r.alreadyHandedOff && r.unansweredRequired > 0) {
|
|
1974
2159
|
lines.push(`The broker will email you for the ${r.unansweredRequired} answer(s) still missing.`);
|
|
1975
2160
|
}
|
|
1976
|
-
lines.push(`Check it:
|
|
2161
|
+
lines.push(`Check it: ${cliInvocation()} quote status ${r.submissionId}`);
|
|
1977
2162
|
return lines;
|
|
1978
2163
|
}
|
|
1979
2164
|
async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
@@ -2014,7 +2199,7 @@ async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
|
2014
2199
|
process.exit(1);
|
|
2015
2200
|
}
|
|
2016
2201
|
} else if (!opts.json && isInteractive() && plan.questions.length > 0) {
|
|
2017
|
-
info(`A broker can place ${plan.coverages
|
|
2202
|
+
info(`A broker can place ${coverageNames(plan.coverages)} with a market that quotes by hand. ${plan.questions.length} application question(s) left.`);
|
|
2018
2203
|
info("Press Ctrl-C to stop at any point; what you have answered goes to the broker.");
|
|
2019
2204
|
const onInterrupt = () => {
|
|
2020
2205
|
complete = false;
|
|
@@ -2046,7 +2231,7 @@ async function sendHandoff(submissionId, input, idempotencyKey, asJson) {
|
|
|
2046
2231
|
else handoffLines(result).forEach((l) => info(l));
|
|
2047
2232
|
} catch (err) {
|
|
2048
2233
|
spinner?.fail("Could not reach a broker");
|
|
2049
|
-
console.error(`Try again:
|
|
2234
|
+
console.error(`Try again: ${cliInvocation()} quote handoff ${submissionId}`);
|
|
2050
2235
|
failWith("API error", err);
|
|
2051
2236
|
}
|
|
2052
2237
|
}
|
|
@@ -2057,8 +2242,34 @@ function printFloorDrops(failures) {
|
|
|
2057
2242
|
floors.forEach((f) => console.log(` - ${f.carrierId}: ${f.missing}`));
|
|
2058
2243
|
info("Add it to your business profile in the portal, then run the quote again.");
|
|
2059
2244
|
}
|
|
2245
|
+
var AGENT_TIP = "Tip: your AI agent can do this for you. Open Claude Code, Codex, Gemini CLI or any coding agent in a folder with your insurance documents and ask it to get you a quote with npx -y @delegance/cli.";
|
|
2246
|
+
var premiumText = (n) => n === null ? "-" : formatCurrency(n);
|
|
2247
|
+
var deductibleText = (n) => n === null ? "-" : n === 0 ? "None" : formatCurrency(n);
|
|
2248
|
+
function bestPriceLines(best) {
|
|
2249
|
+
if (best.length === 0) return [];
|
|
2250
|
+
const names = best.map((r) => coverageName(r.coverage));
|
|
2251
|
+
const nameW = Math.max(...names.map((n) => n.length));
|
|
2252
|
+
const carrierW = Math.max(...best.map((r) => r.carrier.length));
|
|
2253
|
+
return [
|
|
2254
|
+
"Best price per coverage:",
|
|
2255
|
+
...best.map((r, i) => ` ${names[i].padEnd(nameW)} ${r.carrier.padEnd(carrierW)} ${formatCurrency(r.premium)}/yr`)
|
|
2256
|
+
];
|
|
2257
|
+
}
|
|
2258
|
+
function bindHintLines(submissionId, best) {
|
|
2259
|
+
if (best.length === 1) return [`Bind it: ${cliCommand(`bind ${submissionId} ${best[0].opportunityId}`)}`];
|
|
2260
|
+
return best.map((r) => `Bind ${coverageName(r.coverage)}: ${cliCommand(`bind ${submissionId} ${r.opportunityId}`)}`);
|
|
2261
|
+
}
|
|
2262
|
+
function demoClosingLines(best) {
|
|
2263
|
+
const example = best[0] ? ` (e.g. bind ... ${best[0].opportunityId})` : "";
|
|
2264
|
+
return [
|
|
2265
|
+
`Next, on a real quote: ${cliCommand("bind <quote id> <offer>")}${example}`,
|
|
2266
|
+
"DEMO: sample premiums, nothing went to a carrier.",
|
|
2267
|
+
`Get real quotes: ${cliCommand("signup")}`,
|
|
2268
|
+
AGENT_TIP
|
|
2269
|
+
];
|
|
2270
|
+
}
|
|
2060
2271
|
function printQuoteTable(status, opts = {}) {
|
|
2061
|
-
const rows = sortQuoteRows(status.carrierQuotes);
|
|
2272
|
+
const rows = groupRowsByCoverage(sortQuoteRows(status.carrierQuotes));
|
|
2062
2273
|
if (rows.length === 0) {
|
|
2063
2274
|
if (status.brokerReview) {
|
|
2064
2275
|
info(`A broker has this request (${status.brokerReview.status.replace(/_/g, " ")}). They place it by hand and email you with quotes or questions.`);
|
|
@@ -2068,25 +2279,67 @@ function printQuoteTable(status, opts = {}) {
|
|
|
2068
2279
|
if (status.awaitingBrokerReviewCount > 0) info(`${status.awaitingBrokerReviewCount} quote(s) are with a broker for review.`);
|
|
2069
2280
|
return;
|
|
2070
2281
|
}
|
|
2071
|
-
const best =
|
|
2072
|
-
|
|
2073
|
-
|
|
2282
|
+
const best = bestPerCoverage(rows);
|
|
2283
|
+
const bestIds = new Set(best.map((r) => r.opportunityId));
|
|
2284
|
+
const effectiveDate = status.effectiveDate;
|
|
2285
|
+
console.log();
|
|
2286
|
+
if (effectiveDate && opts.showStartDate !== false) {
|
|
2287
|
+
console.log(`Coverage starts ${formatDateOnly(effectiveDate)}`);
|
|
2288
|
+
}
|
|
2289
|
+
const bestLines = bestPriceLines(best);
|
|
2290
|
+
if (bestLines.length) {
|
|
2291
|
+
console.log(import_chalk2.default.bold(bestLines[0]));
|
|
2292
|
+
bestLines.slice(1).forEach((l) => console.log(l));
|
|
2293
|
+
console.log();
|
|
2074
2294
|
}
|
|
2075
2295
|
table(
|
|
2076
|
-
["Carrier", "Coverage", "Premium/yr", "
|
|
2296
|
+
["Carrier", "Coverage", "Premium/yr", "Limits", "Deductible", "Status", "Offer"],
|
|
2077
2297
|
rows.map((r) => {
|
|
2078
|
-
const cells = [
|
|
2079
|
-
|
|
2080
|
-
|
|
2298
|
+
const cells = [
|
|
2299
|
+
r.carrier,
|
|
2300
|
+
coverageName(r.coverage),
|
|
2301
|
+
premiumText(r.premium),
|
|
2302
|
+
r.limits ?? "-",
|
|
2303
|
+
deductibleText(r.deductible),
|
|
2304
|
+
quoteStatusWord(r.status),
|
|
2305
|
+
r.opportunityId
|
|
2306
|
+
];
|
|
2307
|
+
return bestIds.has(r.opportunityId) ? cells.map((c) => import_chalk2.default.bold(c)) : cells;
|
|
2308
|
+
}),
|
|
2309
|
+
{ compact: true }
|
|
2081
2310
|
);
|
|
2082
|
-
|
|
2083
|
-
if (
|
|
2084
|
-
console.log(
|
|
2311
|
+
const reasons = rows.filter((r) => r.message);
|
|
2312
|
+
if (reasons.length) {
|
|
2313
|
+
console.log();
|
|
2314
|
+
reasons.forEach((r) => console.log(`${r.carrier} (${quoteStatusWord(r.status)}): ${r.message}`));
|
|
2085
2315
|
}
|
|
2086
|
-
|
|
2316
|
+
console.log();
|
|
2317
|
+
console.log(import_chalk2.default.dim(`${summarizeStatus(status)}.`));
|
|
2318
|
+
if (opts.demo) {
|
|
2319
|
+
const [next, notice, signup, tip] = demoClosingLines(best);
|
|
2320
|
+
console.log(import_chalk2.default.green(next));
|
|
2321
|
+
console.log(import_chalk2.default.yellow(notice));
|
|
2322
|
+
console.log(import_chalk2.default.yellow(signup));
|
|
2323
|
+
console.log(import_chalk2.default.dim(tip));
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
bindHintLines(status.submissionId, best).forEach((l) => console.log(import_chalk2.default.green(l)));
|
|
2327
|
+
}
|
|
2328
|
+
function demoAnswerLines(answers, effectiveDate) {
|
|
2329
|
+
const pairs = [];
|
|
2330
|
+
for (const q of DEMO_QUESTIONS) {
|
|
2331
|
+
if (answers[q.id] === void 0) continue;
|
|
2332
|
+
const v = answers[q.id];
|
|
2333
|
+
const shown = q.type === "currency" && typeof v === "number" ? formatCurrency(v) : formatKnown(q, v);
|
|
2334
|
+
pairs.push([DEMO_SHORT_LABELS[q.id] ?? q.label, shown]);
|
|
2335
|
+
}
|
|
2336
|
+
if (effectiveDate) pairs.push(["Coverage starts", formatDateOnly(effectiveDate)]);
|
|
2337
|
+
const w = Math.max(...pairs.map(([k]) => k.length));
|
|
2338
|
+
return ["Sample answers (a real quote asks you these):", ...pairs.map(([k, v]) => ` ${k.padEnd(w)} ${v}`)];
|
|
2087
2339
|
}
|
|
2088
2340
|
async function runDemo(asJson, answersFile) {
|
|
2089
2341
|
(asJson ? console.error : console.log)(import_chalk2.default.yellow(`DEMO quote for ${DEMO_BUSINESS}. Nothing leaves this machine.`));
|
|
2342
|
+
let printedStart = false;
|
|
2090
2343
|
if (answersFile) {
|
|
2091
2344
|
let provided;
|
|
2092
2345
|
try {
|
|
@@ -2104,15 +2357,19 @@ async function runDemo(asJson, answersFile) {
|
|
|
2104
2357
|
const mode = answerMode({ demo: true, hasAnswersFile: false, json: asJson, isTTY: isInteractive() });
|
|
2105
2358
|
if (mode === "prompt") await askQuestions(DEMO_QUESTIONS);
|
|
2106
2359
|
else {
|
|
2107
|
-
suggestedAnswers(DEMO_QUESTIONS);
|
|
2108
|
-
if (!asJson)
|
|
2360
|
+
const assumed = suggestedAnswers(DEMO_QUESTIONS);
|
|
2361
|
+
if (!asJson) {
|
|
2362
|
+
demoAnswerLines(assumed, DEMO_STATUS.effectiveDate).forEach((l) => console.log(l));
|
|
2363
|
+
printedStart = true;
|
|
2364
|
+
}
|
|
2109
2365
|
}
|
|
2110
2366
|
}
|
|
2111
|
-
const spinner = asJson ? null : (0, import_ora6.default)("Asking carriers (demo)...")
|
|
2367
|
+
const spinner = asJson ? null : (0, import_ora6.default)("Asking carriers (demo)...");
|
|
2368
|
+
if (spinner && process.stderr.isTTY) spinner.start();
|
|
2112
2369
|
await new Promise((resolve) => setTimeout(resolve, asJson ? 0 : 1200));
|
|
2113
2370
|
spinner?.succeed("4 carriers answered (demo)");
|
|
2114
2371
|
if (asJson) json({ demo: true, ...DEMO_STATUS });
|
|
2115
|
-
else printQuoteTable(DEMO_STATUS, { demo: true });
|
|
2372
|
+
else printQuoteTable(DEMO_STATUS, { demo: true, showStartDate: !printedStart });
|
|
2116
2373
|
}
|
|
2117
2374
|
function addInstantQuoteCommands(quote) {
|
|
2118
2375
|
quote.command("start").description("Get instant quotes from real carriers: answers the questions, files them, waits for premiums").option("-c, --coverage <list>", "Coverages, comma-separated: gl, wc, bop, cyber, pl (E&O), auto, property, umbrella", "gl").option("-a, --answers <file>", "JSON file of answers keyed by question id (skips the prompts)").option("--demo", "Walk the flow on sample data; nothing is sent anywhere").option("--no-wait", 'File the quote and exit; check later with "quote status"').option("--timeout <minutes>", "How long to wait for premiums", "10").option("--replace", "Close the request already with carriers for this coverage and re-quote").option("--json", "Output as JSON").action(async (options) => {
|
|
@@ -2177,14 +2434,14 @@ function addInstantQuoteCommands(quote) {
|
|
|
2177
2434
|
...options.replace ? { replace: true } : {}
|
|
2178
2435
|
});
|
|
2179
2436
|
} catch (err) {
|
|
2180
|
-
const guardLines = activeRequestLines(err, String(options.coverage));
|
|
2437
|
+
const guardLines = activeRequestLines(err, String(options.coverage), { answersFile: options.answers });
|
|
2181
2438
|
if (guardLines) {
|
|
2182
2439
|
submitSpinner.fail("Nothing was sent.");
|
|
2183
2440
|
guardLines.forEach((l) => options.json ? console.error(l) : info(l));
|
|
2184
2441
|
process.exit(1);
|
|
2185
2442
|
}
|
|
2186
2443
|
submitSpinner.fail("Submit failed");
|
|
2187
|
-
console.error(`Before trying again, check whether it went through:
|
|
2444
|
+
console.error(`Before trying again, check whether it went through: ${cliInvocation()} quote status ${session.submissionId}`);
|
|
2188
2445
|
failWith("API error", err);
|
|
2189
2446
|
}
|
|
2190
2447
|
if (submitted.status === "no_carriers") {
|
|
@@ -2200,14 +2457,14 @@ function addInstantQuoteCommands(quote) {
|
|
|
2200
2457
|
}
|
|
2201
2458
|
if (submitted.status === "acord_pending") {
|
|
2202
2459
|
submitSpinner.succeed("Sent to a broker: these coverages need a person, not an instant quote.");
|
|
2203
|
-
info(`Track it with:
|
|
2460
|
+
info(`Track it with: ${cliInvocation()} quote status ${session.submissionId}`);
|
|
2204
2461
|
return;
|
|
2205
2462
|
}
|
|
2206
2463
|
submitSpinner.succeed(`Filed with ${submitted.quotes.filter((q) => q.status === "submitted").length} carrier(s).`);
|
|
2207
2464
|
printFloorDrops(submitted.partialFailures);
|
|
2208
2465
|
if (submitted.replaceNotice && !options.json) info(submitted.replaceNotice);
|
|
2209
2466
|
if (options.wait === false) {
|
|
2210
|
-
info(`Check premiums with:
|
|
2467
|
+
info(`Check premiums with: ${cliInvocation()} quote status ${session.submissionId} --wait`);
|
|
2211
2468
|
return;
|
|
2212
2469
|
}
|
|
2213
2470
|
await waitAndPrint(session.submissionId, Number(options.timeout) * 6e4 || 6e5, !!options.json);
|
|
@@ -2236,22 +2493,25 @@ function addInstantQuoteCommands(quote) {
|
|
|
2236
2493
|
}
|
|
2237
2494
|
if (submitted.status !== "submitted") {
|
|
2238
2495
|
info("Sent to a broker for these coverages.");
|
|
2239
|
-
info(`Check it:
|
|
2496
|
+
info(`Check it: ${cliInvocation()} quote status ${submissionId}`);
|
|
2240
2497
|
return;
|
|
2241
2498
|
}
|
|
2242
2499
|
if (options.wait === false) {
|
|
2243
|
-
info(`Check premiums with:
|
|
2500
|
+
info(`Check premiums with: ${cliInvocation()} quote status ${submissionId} --wait`);
|
|
2244
2501
|
return;
|
|
2245
2502
|
}
|
|
2246
2503
|
await waitAndPrint(submissionId, Number(options.timeout) * 6e4 || 6e5, !!options.json);
|
|
2247
2504
|
} catch (err) {
|
|
2248
|
-
const guardLines = activeRequestLines(err, "<list>"
|
|
2505
|
+
const guardLines = activeRequestLines(err, "<list>", {
|
|
2506
|
+
answersFile: options.answers,
|
|
2507
|
+
submit: { submissionId, sessionVersion: String(options.sessionVersion) }
|
|
2508
|
+
});
|
|
2249
2509
|
if (guardLines) {
|
|
2250
2510
|
error("Nothing was sent.");
|
|
2251
2511
|
guardLines.forEach((l) => options.json ? console.error(l) : info(l));
|
|
2252
2512
|
process.exit(1);
|
|
2253
2513
|
}
|
|
2254
|
-
console.error(`Before trying again, check whether it went through:
|
|
2514
|
+
console.error(`Before trying again, check whether it went through: ${cliInvocation()} quote status ${submissionId}`);
|
|
2255
2515
|
failWith("Submit failed", err);
|
|
2256
2516
|
}
|
|
2257
2517
|
});
|
|
@@ -2287,7 +2547,7 @@ async function waitAndPrint(submissionId, timeoutMs, asJson) {
|
|
|
2287
2547
|
spinner?.stop();
|
|
2288
2548
|
if (asJson) json(status);
|
|
2289
2549
|
else printQuoteTable(status);
|
|
2290
|
-
if (!status.allCarriersResolved && !asJson) info(`Some carriers are still working. Check again:
|
|
2550
|
+
if (!status.allCarriersResolved && !asJson) info(`Some carriers are still working. Check again: ${cliInvocation()} quote status ${submissionId} --wait`);
|
|
2291
2551
|
} catch (err) {
|
|
2292
2552
|
spinner?.fail("Could not read the quote");
|
|
2293
2553
|
failWith("API error", err);
|
|
@@ -2400,13 +2660,13 @@ function createQuoteCommand() {
|
|
|
2400
2660
|
}
|
|
2401
2661
|
console.log();
|
|
2402
2662
|
info(`Queue IDs: ${result.queueIds.join(", ")}`);
|
|
2403
|
-
info(
|
|
2663
|
+
info(`Use "${cliInvocation()} quote list" to check status`);
|
|
2404
2664
|
}
|
|
2405
2665
|
} catch (err) {
|
|
2406
2666
|
spinner.fail("Failed to submit quote request");
|
|
2407
2667
|
if (err instanceof import_sdk5.NotImplementedError) {
|
|
2408
2668
|
error(QUOTE_SUBMISSION_UNAVAILABLE);
|
|
2409
|
-
info(
|
|
2669
|
+
info(`For an instant quote from the terminal: ${cliInvocation()} quote start --coverage gl`);
|
|
2410
2670
|
info(`Or start a quote at ${PORTAL_URL}`);
|
|
2411
2671
|
process.exit(1);
|
|
2412
2672
|
}
|
|
@@ -2564,7 +2824,7 @@ function createPolicyCommand() {
|
|
|
2564
2824
|
["Carrier", "Type", "Policy #", "Expiry", "Status", "Premium"],
|
|
2565
2825
|
policies.map((p) => [
|
|
2566
2826
|
p.carrier || "Unknown",
|
|
2567
|
-
p.policyType
|
|
2827
|
+
(p.policyType ? coverageName(p.policyType) : p.name) || "-",
|
|
2568
2828
|
p.policyNumber || "-",
|
|
2569
2829
|
p.expiryDate ? formatDate(p.expiryDate) : "-",
|
|
2570
2830
|
formatPolicyStatus(p.isActive, p.daysUntilExpiry),
|
|
@@ -2598,21 +2858,21 @@ function createPolicyCommand() {
|
|
|
2598
2858
|
if (urgent.length > 0) {
|
|
2599
2859
|
warn(`\u26A0\uFE0F Expiring within 7 days (${urgent.length}):`);
|
|
2600
2860
|
for (const p of urgent) {
|
|
2601
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2861
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - expires ${formatDate(p.expiryDate)}`);
|
|
2602
2862
|
}
|
|
2603
2863
|
console.log();
|
|
2604
2864
|
}
|
|
2605
2865
|
if (soon.length > 0) {
|
|
2606
2866
|
info(`\u{1F4C5} Expiring within 30 days (${soon.length}):`);
|
|
2607
2867
|
for (const p of soon) {
|
|
2608
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2868
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - expires ${formatDate(p.expiryDate)}`);
|
|
2609
2869
|
}
|
|
2610
2870
|
console.log();
|
|
2611
2871
|
}
|
|
2612
2872
|
if (later.length > 0) {
|
|
2613
2873
|
console.log(`\u{1F4C6} Expiring later (${later.length}):`);
|
|
2614
2874
|
for (const p of later) {
|
|
2615
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2875
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - ${p.daysUntilExpiry} days`);
|
|
2616
2876
|
}
|
|
2617
2877
|
}
|
|
2618
2878
|
}
|
|
@@ -2636,11 +2896,11 @@ function createPolicyCommand() {
|
|
|
2636
2896
|
} else {
|
|
2637
2897
|
const { policy: p, coverages } = found;
|
|
2638
2898
|
console.log();
|
|
2639
|
-
header(`${p.carrier} - ${p.policyType
|
|
2899
|
+
header(`${p.carrier} - ${p.policyType ? coverageName(p.policyType) : p.name}`);
|
|
2640
2900
|
console.log();
|
|
2641
2901
|
console.log(` Policy #: ${p.policyNumber || "N/A"}`);
|
|
2642
2902
|
console.log(` Carrier: ${p.carrier}`);
|
|
2643
|
-
console.log(` Type: ${p.policyType
|
|
2903
|
+
console.log(` Type: ${p.policyType ? coverageName(p.policyType) : "N/A"}`);
|
|
2644
2904
|
console.log(` Effective: ${p.effectiveDate ? formatDate(p.effectiveDate) : "N/A"}`);
|
|
2645
2905
|
console.log(` Expiry: ${p.expiryDate ? formatDate(p.expiryDate) : "N/A"}`);
|
|
2646
2906
|
console.log(` Status: ${formatPolicyStatus(p.isActive, p.daysUntilExpiry)}`);
|
|
@@ -2691,14 +2951,14 @@ function createPolicyCommand() {
|
|
|
2691
2951
|
return;
|
|
2692
2952
|
}
|
|
2693
2953
|
if (docs.length === 0) {
|
|
2694
|
-
info(
|
|
2954
|
+
info(`No source documents are linked to this policy yet. Upload one with: ${cliInvocation()} document upload <file>`);
|
|
2695
2955
|
return;
|
|
2696
2956
|
}
|
|
2697
2957
|
table(
|
|
2698
2958
|
["Document", "File", "Type", "Status", "Added"],
|
|
2699
2959
|
docs.map((d) => [d.id, d.fileName ?? "-", d.documentType ?? "-", d.status, d.createdAt ? formatDate(d.createdAt) : "-"])
|
|
2700
2960
|
);
|
|
2701
|
-
info(
|
|
2961
|
+
info(`Download one with: ${cliInvocation()} document status <documentId>`);
|
|
2702
2962
|
} catch (err) {
|
|
2703
2963
|
failWith("API error", err);
|
|
2704
2964
|
}
|
|
@@ -2731,9 +2991,9 @@ function createPolicyCommand() {
|
|
|
2731
2991
|
await signWithStepUp(client, out.request.id, sig, void 0, void 0);
|
|
2732
2992
|
} else {
|
|
2733
2993
|
info(`Sign here: ${out.signatureUrl}`);
|
|
2734
|
-
info(`Or sign from here:
|
|
2994
|
+
info(`Or sign from here: ${cliInvocation()} policy sign ${out.request.id} --signer-name "..." --signer-title "..." --attest`);
|
|
2735
2995
|
}
|
|
2736
|
-
info(`Check progress with "
|
|
2996
|
+
info(`Check progress with "${cliInvocation()} policy cancellation ${out.request.id}"`);
|
|
2737
2997
|
} catch (err) {
|
|
2738
2998
|
spinner.fail("Could not request the cancellation");
|
|
2739
2999
|
failWith("API error", err);
|
|
@@ -2790,7 +3050,7 @@ async function signWithStepUp(client, requestId, sig, stepUpId, presetCode) {
|
|
|
2790
3050
|
let code = presetCode;
|
|
2791
3051
|
if (!code) {
|
|
2792
3052
|
if (!process.stdin.isTTY) {
|
|
2793
|
-
warn(`No terminal to type the code in. Finish with:
|
|
3053
|
+
warn(`No terminal to type the code in. Finish with: ${cliInvocation()} policy sign ${requestId} --signer-name "${sig.signerName}" --signer-title "${sig.signerTitle}" --attest --step-up ${id} --code <code>`);
|
|
2794
3054
|
return;
|
|
2795
3055
|
}
|
|
2796
3056
|
const answer = await import_inquirer7.default.prompt([{
|
|
@@ -2840,7 +3100,7 @@ function createCOICommand() {
|
|
|
2840
3100
|
policiesSpinner.stop();
|
|
2841
3101
|
if (policies.length === 0) {
|
|
2842
3102
|
error("No active policies found");
|
|
2843
|
-
info(
|
|
3103
|
+
info(`Upload policy documents first with "${cliInvocation()} doc upload"`);
|
|
2844
3104
|
process.exit(1);
|
|
2845
3105
|
}
|
|
2846
3106
|
const answers = await import_inquirer8.default.prompt([
|
|
@@ -2849,7 +3109,7 @@ function createCOICommand() {
|
|
|
2849
3109
|
name: "selectedPolicies",
|
|
2850
3110
|
message: "Select policies to include:",
|
|
2851
3111
|
choices: policies.map((p) => ({
|
|
2852
|
-
name: `${p.carrier} - ${p.policyType
|
|
3112
|
+
name: `${p.carrier} - ${p.policyType ? coverageName(p.policyType) : p.name} (${p.policyNumber || "No #"})`,
|
|
2853
3113
|
value: p.id,
|
|
2854
3114
|
checked: policyIds.includes(p.id)
|
|
2855
3115
|
})),
|
|
@@ -2936,7 +3196,7 @@ function createCOICommand() {
|
|
|
2936
3196
|
console.log();
|
|
2937
3197
|
console.log("Policies included:");
|
|
2938
3198
|
for (const p of result.policiesIncluded) {
|
|
2939
|
-
console.log(` - ${p.carrier} (${p.policyType})`);
|
|
3199
|
+
console.log(` - ${p.carrier} (${coverageName(p.policyType)})`);
|
|
2940
3200
|
}
|
|
2941
3201
|
}
|
|
2942
3202
|
} catch (err) {
|
|
@@ -2952,7 +3212,7 @@ function createCOICommand() {
|
|
|
2952
3212
|
spinner.succeed(`Found ${count} COIs`);
|
|
2953
3213
|
if (cois.length === 0) {
|
|
2954
3214
|
info("No COIs generated yet");
|
|
2955
|
-
info(
|
|
3215
|
+
info(`Generate one with "${cliInvocation()} coi generate --interactive"`);
|
|
2956
3216
|
return;
|
|
2957
3217
|
}
|
|
2958
3218
|
if (options.json) {
|
|
@@ -3029,7 +3289,7 @@ function createSOVCommand() {
|
|
|
3029
3289
|
info(`SOV ID: ${result.sovId}`);
|
|
3030
3290
|
info(`Status: ${result.status}`);
|
|
3031
3291
|
if (!options.wait) {
|
|
3032
|
-
info(
|
|
3292
|
+
info(`Use "${cliInvocation()} sov status <id>" to check parsing progress`);
|
|
3033
3293
|
}
|
|
3034
3294
|
}
|
|
3035
3295
|
} catch (err) {
|
|
@@ -3048,7 +3308,7 @@ function createSOVCommand() {
|
|
|
3048
3308
|
spinner.succeed(`Found ${count} SOVs`);
|
|
3049
3309
|
if (sovs.length === 0) {
|
|
3050
3310
|
info("No SOVs found");
|
|
3051
|
-
info(
|
|
3311
|
+
info(`Upload one with "${cliInvocation()} sov upload schedule.xlsx"`);
|
|
3052
3312
|
return;
|
|
3053
3313
|
}
|
|
3054
3314
|
if (options.json) {
|
|
@@ -3212,7 +3472,7 @@ function createAppetiteCommand() {
|
|
|
3212
3472
|
if (!options.wait) {
|
|
3213
3473
|
spinner.succeed(`Match queued: ${start.jobId}`);
|
|
3214
3474
|
if (options.json) json(start);
|
|
3215
|
-
else info(`Check it with "
|
|
3475
|
+
else info(`Check it with "${cliInvocation()} appetite status ${start.jobId}"`);
|
|
3216
3476
|
return;
|
|
3217
3477
|
}
|
|
3218
3478
|
spinner.text = `Matching appetite (about ${start.estimatedSeconds}s)...`;
|
|
@@ -3358,7 +3618,7 @@ function createKnowledgeCommand() {
|
|
|
3358
3618
|
);
|
|
3359
3619
|
}
|
|
3360
3620
|
if (pagination.hasMore && pagination.nextCursor) {
|
|
3361
|
-
info(`More:
|
|
3621
|
+
info(`More: ${cliInvocation()} knowledge notecards --cursor ${pagination.nextCursor}`);
|
|
3362
3622
|
}
|
|
3363
3623
|
} catch (err) {
|
|
3364
3624
|
spinner.fail("Failed to list notecards");
|
|
@@ -3482,7 +3742,7 @@ function createOnboardingCommand() {
|
|
|
3482
3742
|
const result = await client.onboarding.requestVerification();
|
|
3483
3743
|
spinner.succeed(`Verification sent to ${result.sentTo}`);
|
|
3484
3744
|
if (options.json) json(result);
|
|
3485
|
-
else info(
|
|
3745
|
+
else info(`Open the link in that email, then check progress with "${cliInvocation()} onboarding state"`);
|
|
3486
3746
|
} catch (err) {
|
|
3487
3747
|
spinner.fail("Failed to request verification");
|
|
3488
3748
|
failWith("API error", err);
|
|
@@ -3607,6 +3867,11 @@ var import_ora16 = __toESM(require("ora"));
|
|
|
3607
3867
|
var import_crypto3 = require("crypto");
|
|
3608
3868
|
var import_sdk6 = require("@delegance/sdk");
|
|
3609
3869
|
var KEYS_PORTAL_URL = `${PORTAL_URL}/portal/settings/api-keys`;
|
|
3870
|
+
function parseExpiresInDays(raw) {
|
|
3871
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
3872
|
+
if (raw.trim().toLowerCase() === "never") return "never";
|
|
3873
|
+
return parseInt(raw, 10);
|
|
3874
|
+
}
|
|
3610
3875
|
function parseScopes(raw) {
|
|
3611
3876
|
const parts = (Array.isArray(raw) ? raw : [raw ?? "read,write"]).flatMap((p) => p.split(/[\s,]+/)).map((p) => p.trim()).filter(Boolean);
|
|
3612
3877
|
const unknown = parts.filter((p) => !import_sdk6.API_KEY_SCOPES.includes(p));
|
|
@@ -3615,8 +3880,12 @@ function parseScopes(raw) {
|
|
|
3615
3880
|
}
|
|
3616
3881
|
return Array.from(new Set(parts));
|
|
3617
3882
|
}
|
|
3618
|
-
|
|
3619
|
-
|
|
3883
|
+
var EXPIRING_SCOPES = /* @__PURE__ */ new Set(["write", "admin", "claims:write"]);
|
|
3884
|
+
function isReadOnlyScopeSet(scopes) {
|
|
3885
|
+
return !scopes.some((s) => EXPIRING_SCOPES.has(s));
|
|
3886
|
+
}
|
|
3887
|
+
function replacementExpiryDays(expiresAt, now = /* @__PURE__ */ new Date(), scopes = []) {
|
|
3888
|
+
if (!expiresAt) return isReadOnlyScopeSet(scopes) ? "never" : void 0;
|
|
3620
3889
|
const ms = new Date(expiresAt).getTime() - now.getTime();
|
|
3621
3890
|
if (!Number.isFinite(ms)) throw new Error(`Cannot read the old key's expiry "${expiresAt}"; not rotating`);
|
|
3622
3891
|
const days = Math.ceil(ms / (24 * 60 * 60 * 1e3));
|
|
@@ -3665,14 +3934,14 @@ function createKeysCommand() {
|
|
|
3665
3934
|
])
|
|
3666
3935
|
);
|
|
3667
3936
|
if (pagination?.hasMore && pagination.nextCursor) {
|
|
3668
|
-
info(`More keys:
|
|
3937
|
+
info(`More keys: ${cliInvocation()} keys list --cursor ${pagination.nextCursor}`);
|
|
3669
3938
|
}
|
|
3670
3939
|
} catch (err) {
|
|
3671
3940
|
spinner.fail("Failed to list API keys");
|
|
3672
3941
|
failWith("API error", err);
|
|
3673
3942
|
}
|
|
3674
3943
|
});
|
|
3675
|
-
keys.command("create <name>").description("Create an API key (needs a key with the admin scope)").option("-s, --scopes <scopes>", `Comma-separated scopes (${import_sdk6.API_KEY_SCOPES.join(", ")})`, "read,write").option("-e, --expires-in-days <days>",
|
|
3944
|
+
keys.command("create <name>").description("Create an API key (needs a key with the admin scope)").option("-s, --scopes <scopes>", `Comma-separated scopes (${import_sdk6.API_KEY_SCOPES.join(", ")})`, "read,write").option("-e, --expires-in-days <days>", 'Expire the key after N days (1-365; default 90), or "never" for a read-only key').option("--json", "Output as JSON").action(async (name, options) => {
|
|
3676
3945
|
let scopes;
|
|
3677
3946
|
try {
|
|
3678
3947
|
scopes = parseScopes(options.scopes);
|
|
@@ -3687,7 +3956,7 @@ function createKeysCommand() {
|
|
|
3687
3956
|
{
|
|
3688
3957
|
name,
|
|
3689
3958
|
scopes,
|
|
3690
|
-
expiresInDays:
|
|
3959
|
+
expiresInDays: parseExpiresInDays(options.expiresInDays)
|
|
3691
3960
|
},
|
|
3692
3961
|
{ idempotencyKey: (0, import_crypto3.randomUUID)() }
|
|
3693
3962
|
);
|
|
@@ -3722,7 +3991,7 @@ function createKeysCommand() {
|
|
|
3722
3991
|
{
|
|
3723
3992
|
name: `${current2.name} (rotated)`,
|
|
3724
3993
|
scopes: current2.scopes,
|
|
3725
|
-
expiresInDays: replacementExpiryDays(current2.expiresAt)
|
|
3994
|
+
expiresInDays: replacementExpiryDays(current2.expiresAt, /* @__PURE__ */ new Date(), current2.scopes)
|
|
3726
3995
|
},
|
|
3727
3996
|
{ idempotencyKey: (0, import_crypto3.randomUUID)() }
|
|
3728
3997
|
);
|
|
@@ -3733,6 +4002,9 @@ function createKeysCommand() {
|
|
|
3733
4002
|
}
|
|
3734
4003
|
printCreatedKey(created);
|
|
3735
4004
|
console.log();
|
|
4005
|
+
if (!current2.expiresAt && !isReadOnlyScopeSet(current2.scopes)) {
|
|
4006
|
+
warn("The old key never expired. Keys that can write must expire now, so the new key lasts 90 days.");
|
|
4007
|
+
}
|
|
3736
4008
|
warn(`The old key ${current2.prefix} is still active.`);
|
|
3737
4009
|
info(`Switch your integrations to the new key, then revoke the old one at ${KEYS_PORTAL_URL}`);
|
|
3738
4010
|
} catch (err) {
|
|
@@ -3824,7 +4096,7 @@ function createConfigCommand() {
|
|
|
3824
4096
|
currentConfig.clear();
|
|
3825
4097
|
clearCredentials();
|
|
3826
4098
|
success("Configuration reset to defaults");
|
|
3827
|
-
info(
|
|
4099
|
+
info(`You will need to run "${cliInvocation()} auth login" again`);
|
|
3828
4100
|
});
|
|
3829
4101
|
config2.command("env").description("Show environment information").action(() => {
|
|
3830
4102
|
header("Environment");
|
|
@@ -3850,7 +4122,7 @@ function createConfigCommand() {
|
|
|
3850
4122
|
info("No profiles configured");
|
|
3851
4123
|
info("Profiles allow you to switch between different accounts/environments");
|
|
3852
4124
|
console.log();
|
|
3853
|
-
info(
|
|
4125
|
+
info(`Example: ${cliInvocation()} config set-profile production --url https://portal.delegancebrokerage.com`);
|
|
3854
4126
|
} else {
|
|
3855
4127
|
for (const [name, profile] of Object.entries(profiles)) {
|
|
3856
4128
|
const isActive = name === activeProfile;
|
|
@@ -3866,7 +4138,7 @@ function createConfigCommand() {
|
|
|
3866
4138
|
const profiles = currentConfig.get("profiles") || {};
|
|
3867
4139
|
if (!profiles[name] && name !== "default") {
|
|
3868
4140
|
error(`Profile "${name}" not found`);
|
|
3869
|
-
info(
|
|
4141
|
+
info(`Create it with: ${cliInvocation()} config set-profile <name> --url <base-url>`);
|
|
3870
4142
|
process.exit(1);
|
|
3871
4143
|
}
|
|
3872
4144
|
currentConfig.set("activeProfile", name);
|
|
@@ -3897,7 +4169,7 @@ var import_inquirer10 = __toESM(require("inquirer"));
|
|
|
3897
4169
|
var import_sdk7 = require("@delegance/sdk");
|
|
3898
4170
|
var TYPES = ["bug", "idea", "question"];
|
|
3899
4171
|
var MESSAGE_MAX = 500;
|
|
3900
|
-
var USAGE =
|
|
4172
|
+
var USAGE = `${cliInvocation()} feedback "what happened" [--type bug|idea|question] [--include-last]`;
|
|
3901
4173
|
function feedbackInputMode(o) {
|
|
3902
4174
|
if (o.message && o.message.trim()) return "given";
|
|
3903
4175
|
return o.isTTY ? "prompt" : "usage_error";
|
|
@@ -3948,10 +4220,40 @@ function createFeedbackCommand() {
|
|
|
3948
4220
|
});
|
|
3949
4221
|
}
|
|
3950
4222
|
|
|
4223
|
+
// src/utils/checkbox-prompt.ts
|
|
4224
|
+
var import_inquirer11 = __toESM(require("inquirer"));
|
|
4225
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
4226
|
+
var CHECKBOX_HINT = "(space to select, Enter to continue)";
|
|
4227
|
+
var registered = false;
|
|
4228
|
+
function minimalCheckboxPrompt(Base) {
|
|
4229
|
+
return class MinimalCheckboxPrompt extends Base {
|
|
4230
|
+
constructor(...args) {
|
|
4231
|
+
super(...args);
|
|
4232
|
+
this.dontShowHints = true;
|
|
4233
|
+
}
|
|
4234
|
+
getQuestion() {
|
|
4235
|
+
const question = super.getQuestion();
|
|
4236
|
+
return this.status === "answered" ? question : `${question}${import_chalk3.default.dim(CHECKBOX_HINT)} `;
|
|
4237
|
+
}
|
|
4238
|
+
onAllKey() {
|
|
4239
|
+
}
|
|
4240
|
+
onInverseKey() {
|
|
4241
|
+
}
|
|
4242
|
+
};
|
|
4243
|
+
}
|
|
4244
|
+
function registerMinimalCheckbox() {
|
|
4245
|
+
if (registered) return;
|
|
4246
|
+
const prompts = import_inquirer11.default.prompt.prompts;
|
|
4247
|
+
const Base = prompts?.checkbox;
|
|
4248
|
+
if (!Base) return;
|
|
4249
|
+
import_inquirer11.default.registerPrompt("checkbox", minimalCheckboxPrompt(Base));
|
|
4250
|
+
registered = true;
|
|
4251
|
+
}
|
|
4252
|
+
|
|
3951
4253
|
// src/commands/commerce.ts
|
|
3952
4254
|
var import_commander18 = require("commander");
|
|
3953
4255
|
var fs7 = __toESM(require("fs"));
|
|
3954
|
-
var
|
|
4256
|
+
var import_inquirer12 = __toESM(require("inquirer"));
|
|
3955
4257
|
var import_ora17 = __toESM(require("ora"));
|
|
3956
4258
|
var import_sdk8 = require("@delegance/sdk");
|
|
3957
4259
|
function formatCents(cents2, currency = "usd") {
|
|
@@ -3979,9 +4281,29 @@ function describeBindResult(r) {
|
|
|
3979
4281
|
lines.push(r.next);
|
|
3980
4282
|
return lines;
|
|
3981
4283
|
}
|
|
4284
|
+
function exactDollars(cents2) {
|
|
4285
|
+
return `$${(cents2 / 100).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
4286
|
+
}
|
|
4287
|
+
function describeFinancing(f) {
|
|
4288
|
+
if (!f || !f.available) return [];
|
|
4289
|
+
const lines = [];
|
|
4290
|
+
if (f.checkoutUrl) {
|
|
4291
|
+
lines.push(`Pay over time with ${f.lender}: ${f.checkoutUrl}`);
|
|
4292
|
+
} else {
|
|
4293
|
+
lines.push(`Financing with ${f.lender} opens once your broker confirms the final premium.`);
|
|
4294
|
+
}
|
|
4295
|
+
const t = f.terms;
|
|
4296
|
+
if (t && t.aprBps !== null && t.downPaymentCents !== null && t.installmentCents !== null && t.numberOfInstallments !== null) {
|
|
4297
|
+
lines.push(
|
|
4298
|
+
`Lender terms: ${(t.aprBps / 100).toFixed(2)}% APR, ${exactDollars(t.downPaymentCents)} down, ${t.numberOfInstallments} payments of ${exactDollars(t.installmentCents)}.`
|
|
4299
|
+
);
|
|
4300
|
+
}
|
|
4301
|
+
lines.push(f.disclosure);
|
|
4302
|
+
return lines;
|
|
4303
|
+
}
|
|
3982
4304
|
async function askCode() {
|
|
3983
4305
|
if (!process.stdin.isTTY) return null;
|
|
3984
|
-
const answer = await
|
|
4306
|
+
const answer = await import_inquirer12.default.prompt([{
|
|
3985
4307
|
type: "input",
|
|
3986
4308
|
name: "code",
|
|
3987
4309
|
message: "Code from your email:",
|
|
@@ -3994,7 +4316,7 @@ function createBindCommand() {
|
|
|
3994
4316
|
const client = getClient();
|
|
3995
4317
|
let attest = options.attest === true;
|
|
3996
4318
|
if (!attest && process.stdin.isTTY && !options.json) {
|
|
3997
|
-
const a = await
|
|
4319
|
+
const a = await import_inquirer12.default.prompt([{
|
|
3998
4320
|
type: "confirm",
|
|
3999
4321
|
name: "ok",
|
|
4000
4322
|
message: "Are the answers on this application accurate? Binding relies on them.",
|
|
@@ -4002,7 +4324,7 @@ function createBindCommand() {
|
|
|
4002
4324
|
}]);
|
|
4003
4325
|
attest = a.ok === true;
|
|
4004
4326
|
if (!attest) {
|
|
4005
|
-
warn(
|
|
4327
|
+
warn(`Not bound. Fix the answers with "${cliInvocation()} quote start", then bind again.`);
|
|
4006
4328
|
return;
|
|
4007
4329
|
}
|
|
4008
4330
|
}
|
|
@@ -4018,7 +4340,7 @@ function createBindCommand() {
|
|
|
4018
4340
|
info(`We emailed a 6-digit code to ${r.sentTo}. It expires in 10 minutes.`);
|
|
4019
4341
|
const code = options.code ? String(options.code) : await askCode();
|
|
4020
4342
|
if (!code) {
|
|
4021
|
-
warn(`Finish with:
|
|
4343
|
+
warn(`Finish with: ${cliInvocation()} bind ${submissionId} ${offerId} --attest --step-up ${stepUpId} --code <code>`);
|
|
4022
4344
|
return;
|
|
4023
4345
|
}
|
|
4024
4346
|
r = await client.instantQuotes.bindWithStepUp(submissionId, offerId, { ...attest ? { attest: true } : {}, stepUpId, code });
|
|
@@ -4032,9 +4354,10 @@ function createBindCommand() {
|
|
|
4032
4354
|
return;
|
|
4033
4355
|
}
|
|
4034
4356
|
if (r.existing) warn(`A bind for this coverage is already open (${r.carrierName}). Showing it.`);
|
|
4035
|
-
else if (r.tier !== "A") success(`Bind requested: ${r.carrierName} ${r.coverage}${r.premium === null ? "" : ` at ${formatCurrency(r.premium)}/yr`}`);
|
|
4357
|
+
else if (r.tier !== "A") success(`Bind requested: ${r.carrierName} ${coverageName(r.coverage)}${r.premium === null ? "" : ` at ${formatCurrency(r.premium)}/yr`}`);
|
|
4036
4358
|
keyValue({ "Bind request": r.bindRequestId, Status: r.status });
|
|
4037
4359
|
describeBindResult(r).forEach((l) => info(l));
|
|
4360
|
+
describeFinancing(r.financing).forEach((l) => info(l));
|
|
4038
4361
|
} catch (err) {
|
|
4039
4362
|
spinner?.stop();
|
|
4040
4363
|
if (err instanceof import_sdk8.DeleganceError && [400, 403, 409].includes(err.statusCode ?? 0)) {
|
|
@@ -4075,7 +4398,7 @@ function createBillingCommand() {
|
|
|
4075
4398
|
payments.map((p) => [p.id, formatCents(p.amount, p.currency), p.status, p.dueDate ? formatDate(p.dueDate) : "-", p.description ?? "-"])
|
|
4076
4399
|
);
|
|
4077
4400
|
keyValue({ "Open": `${formatCents(sum.openCents)} (${sum.openCount})`, "Paid": formatCents(sum.paidCents) });
|
|
4078
|
-
if (sum.openCount > 0) info(
|
|
4401
|
+
if (sum.openCount > 0) info(`Pay one with: ${cliInvocation()} pay <paymentId>`);
|
|
4079
4402
|
} catch (err) {
|
|
4080
4403
|
failWith("API error", err);
|
|
4081
4404
|
}
|
|
@@ -4100,7 +4423,7 @@ function createPayCommand() {
|
|
|
4100
4423
|
payments.map((p) => [p.id, formatCents(p.amount, p.currency), p.description ?? "", p.payable ? `${p.status} (payable)` : p.status, p.dueDate ? formatDate(p.dueDate) : "-"])
|
|
4101
4424
|
);
|
|
4102
4425
|
const next = payments.find((p) => p.payable);
|
|
4103
|
-
if (next) info(`Pay it:
|
|
4426
|
+
if (next) info(`Pay it: ${cliInvocation()} pay ${next.id}`);
|
|
4104
4427
|
return;
|
|
4105
4428
|
}
|
|
4106
4429
|
const { checkoutUrl } = await client.payments.checkout(paymentId);
|
|
@@ -4130,14 +4453,14 @@ function bondStartLines(r, ctx = {}) {
|
|
|
4130
4453
|
out.push(` ${str(q.key)}: ${str(q.label)}${q.required ? " (required)" : ""}${opts}`);
|
|
4131
4454
|
});
|
|
4132
4455
|
}
|
|
4133
|
-
out.push(`Answer with:
|
|
4456
|
+
out.push(`Answer with: ${cliInvocation()} bond answer ${str(r.session_id)} --answers answers.json`);
|
|
4134
4457
|
return out;
|
|
4135
4458
|
}
|
|
4136
4459
|
if (Array.isArray(r.candidates) && r.candidates.length) {
|
|
4137
4460
|
out.push("That name fits more than one bond. Rerun with the exact name:");
|
|
4138
4461
|
const rawState = (ctx.state ?? "").trim();
|
|
4139
4462
|
const state = !rawState ? null : /^[A-Za-z]{2}$/.test(rawState) ? rawState.toUpperCase() : shellQuote(rawState);
|
|
4140
|
-
r.candidates.forEach((name) => out.push(state ? `
|
|
4463
|
+
r.candidates.forEach((name) => out.push(state ? ` ${cliInvocation()} bond quote --state ${state} --bond ${shellQuote(str(name))}` : ` --bond ${shellQuote(str(name))}`));
|
|
4141
4464
|
return out;
|
|
4142
4465
|
}
|
|
4143
4466
|
if (r.needsAmount) {
|
|
@@ -4192,7 +4515,7 @@ function createBondCommand() {
|
|
|
4192
4515
|
if (options.json) json(r);
|
|
4193
4516
|
else {
|
|
4194
4517
|
console.log(JSON.stringify(r, null, 2));
|
|
4195
|
-
info(`Check it:
|
|
4518
|
+
info(`Check it: ${cliInvocation()} bond status ${sessionId}`);
|
|
4196
4519
|
}
|
|
4197
4520
|
} catch (err) {
|
|
4198
4521
|
failWith("Could not submit the answers", err);
|
|
@@ -4269,6 +4592,7 @@ function createClaimCommand() {
|
|
|
4269
4592
|
|
|
4270
4593
|
// src/program.ts
|
|
4271
4594
|
function createProgram() {
|
|
4595
|
+
registerMinimalCheckbox();
|
|
4272
4596
|
const program2 = new import_commander19.Command();
|
|
4273
4597
|
program2.name("delegance").description("CLI for the Delegance Brokerage platform").version(CLI_VERSION).enablePositionalOptions().option("-v, --verbose", "Enable verbose output").option("--json", "Output in JSON format").hook("preAction", () => {
|
|
4274
4598
|
migrateLegacyConfigKey();
|
|
@@ -4303,9 +4627,9 @@ function createProgram() {
|
|
|
4303
4627
|
var program = createProgram();
|
|
4304
4628
|
installLastCommandRecorder(program);
|
|
4305
4629
|
program.on("command:*", () => {
|
|
4306
|
-
console.error(
|
|
4630
|
+
console.error(import_chalk4.default.red(`Unknown command: ${program.args.join(" ")}`));
|
|
4307
4631
|
console.log();
|
|
4308
|
-
console.log(
|
|
4632
|
+
console.log(`Run "${cliInvocation()} --help" for a list of available commands.`);
|
|
4309
4633
|
process.exit(1);
|
|
4310
4634
|
});
|
|
4311
4635
|
if (process.argv.length === 2) {
|