@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/index.js
CHANGED
|
@@ -76,7 +76,7 @@ var path = __toESM(require("path"));
|
|
|
76
76
|
var os = __toESM(require("os"));
|
|
77
77
|
|
|
78
78
|
// src/version.ts
|
|
79
|
-
var CLI_VERSION = "0.2.
|
|
79
|
+
var CLI_VERSION = "0.2.2";
|
|
80
80
|
|
|
81
81
|
// src/utils/config.ts
|
|
82
82
|
var CONFIG_DIR = path.join(os.homedir(), ".delegance");
|
|
@@ -229,6 +229,7 @@ __export(output_exports, {
|
|
|
229
229
|
error: () => error,
|
|
230
230
|
formatCurrency: () => formatCurrency,
|
|
231
231
|
formatDate: () => formatDate,
|
|
232
|
+
formatDateOnly: () => formatDateOnly,
|
|
232
233
|
formatFileSize: () => formatFileSize,
|
|
233
234
|
formatStatus: () => formatStatus,
|
|
234
235
|
header: () => header,
|
|
@@ -236,6 +237,7 @@ __export(output_exports, {
|
|
|
236
237
|
json: () => json,
|
|
237
238
|
keyValue: () => keyValue,
|
|
238
239
|
output: () => output,
|
|
240
|
+
renderCompactTable: () => renderCompactTable,
|
|
239
241
|
success: () => success,
|
|
240
242
|
table: () => table,
|
|
241
243
|
warn: () => warn
|
|
@@ -298,12 +300,28 @@ function formatValue(value) {
|
|
|
298
300
|
}
|
|
299
301
|
return String(value);
|
|
300
302
|
}
|
|
303
|
+
function visibleWidth(s) {
|
|
304
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
305
|
+
}
|
|
306
|
+
function renderCompactTable(headers, rows) {
|
|
307
|
+
const cells = rows.map((r) => headers.map((_, i) => r[i] === null || r[i] === void 0 ? "-" : String(r[i])));
|
|
308
|
+
const widths = headers.map((h, i) => Math.max(visibleWidth(h), ...cells.map((r) => visibleWidth(r[i]))));
|
|
309
|
+
const line = (parts) => ` ${parts.map((p, i) => p + " ".repeat(widths[i] - visibleWidth(p))).join(" ")}`.replace(/\s+$/, "");
|
|
310
|
+
return [
|
|
311
|
+
line(headers.map((h) => import_chalk.default.bold(h))),
|
|
312
|
+
import_chalk.default.dim(line(widths.map((w) => "\u2500".repeat(w)))),
|
|
313
|
+
...cells.map(line)
|
|
314
|
+
].join("\n");
|
|
315
|
+
}
|
|
301
316
|
function table(headers, rows, options = {}) {
|
|
317
|
+
if (options.compact) {
|
|
318
|
+
console.log(renderCompactTable(headers, rows));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
302
321
|
const tbl = new import_cli_table3.default({
|
|
303
322
|
head: headers.map((h) => import_chalk.default.bold(h)),
|
|
304
323
|
style: {
|
|
305
|
-
head: ["cyan"]
|
|
306
|
-
compact: options.compact
|
|
324
|
+
head: ["cyan"]
|
|
307
325
|
}
|
|
308
326
|
});
|
|
309
327
|
for (const row of rows) {
|
|
@@ -340,6 +358,15 @@ function formatDate(dateStr) {
|
|
|
340
358
|
minute: "2-digit"
|
|
341
359
|
});
|
|
342
360
|
}
|
|
361
|
+
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
362
|
+
function formatDateOnly(iso) {
|
|
363
|
+
if (!iso) return "";
|
|
364
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
|
|
365
|
+
if (!m) return iso;
|
|
366
|
+
const month = MONTHS[Number(m[2]) - 1];
|
|
367
|
+
if (!month) return iso;
|
|
368
|
+
return `${month} ${Number(m[3])}, ${m[1]}`;
|
|
369
|
+
}
|
|
343
370
|
function formatFileSize(bytes) {
|
|
344
371
|
if (bytes === void 0) return import_chalk.default.gray("(unknown)");
|
|
345
372
|
const units = ["B", "KB", "MB", "GB"];
|
|
@@ -409,14 +436,26 @@ function noteTraceId(traceId) {
|
|
|
409
436
|
if (current && typeof traceId === "string" && TRACE_RE.test(traceId)) current.traceId = traceId;
|
|
410
437
|
}
|
|
411
438
|
|
|
439
|
+
// src/utils/invocation.ts
|
|
440
|
+
var NPX_INVOCATION = "npx -y @delegance/cli";
|
|
441
|
+
function cliInvocation(env = process.env, argv = process.argv) {
|
|
442
|
+
const entry = (argv[1] ?? "").replace(/\\/g, "/");
|
|
443
|
+
if (entry.includes("/_npx/")) return NPX_INVOCATION;
|
|
444
|
+
if (env.npm_command === "exec" && /(^|\/)delegance(\.[cm]?js)?$/.test(entry)) return NPX_INVOCATION;
|
|
445
|
+
return "delegance";
|
|
446
|
+
}
|
|
447
|
+
function cliCommand(rest) {
|
|
448
|
+
return `${cliInvocation()} ${rest}`;
|
|
449
|
+
}
|
|
450
|
+
|
|
412
451
|
// src/utils/client.ts
|
|
413
452
|
var PORTAL_URL = "https://portal.delegancebrokerage.com";
|
|
414
453
|
function getClient() {
|
|
415
454
|
const apiKey = getApiKey();
|
|
416
455
|
if (!apiKey) {
|
|
417
456
|
error("Not authenticated");
|
|
418
|
-
info(
|
|
419
|
-
info(
|
|
457
|
+
info(`New to Delegance? Run "${cliInvocation()} signup" to create an account.`);
|
|
458
|
+
info(`Have an API key? Run "${cliInvocation()} auth login", or set DELEGANCE_API_KEY.`);
|
|
420
459
|
process.exit(1);
|
|
421
460
|
}
|
|
422
461
|
return new import_sdk.DeleganceClient({
|
|
@@ -428,7 +467,7 @@ function getCompanyId(options) {
|
|
|
428
467
|
const companyId = options.company || getConfigValue("defaultCompanyId");
|
|
429
468
|
if (!companyId) {
|
|
430
469
|
error("No company specified");
|
|
431
|
-
info(
|
|
470
|
+
info(`Use --company <id> or set a default with "${cliInvocation()} company use <id>"`);
|
|
432
471
|
process.exit(1);
|
|
433
472
|
}
|
|
434
473
|
return companyId;
|
|
@@ -440,9 +479,9 @@ function describeApiError(err) {
|
|
|
440
479
|
}
|
|
441
480
|
if (err instanceof import_sdk.AuthenticationError) {
|
|
442
481
|
if (err.reason === "key_expired" || err.reason === "key_membership_revoked") {
|
|
443
|
-
return { message: `${err.message}${traceSuffix}`, hint:
|
|
482
|
+
return { message: `${err.message}${traceSuffix}`, hint: `Run "${cliInvocation()} auth login" to sign in again.` };
|
|
444
483
|
}
|
|
445
|
-
return { message: `${err.message}${traceSuffix}`, hint:
|
|
484
|
+
return { message: `${err.message}${traceSuffix}`, hint: `Check your key with "${cliInvocation()} auth status", or log in again.` };
|
|
446
485
|
}
|
|
447
486
|
if (err instanceof import_sdk.ForbiddenError) {
|
|
448
487
|
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." };
|
|
@@ -579,7 +618,7 @@ function exchangeRefusal(err) {
|
|
|
579
618
|
case "already_collected":
|
|
580
619
|
return `The key for this signup was already collected. If you lost it, create a new key at ${PORTAL_URL}/portal/settings/api-keys`;
|
|
581
620
|
case "expired":
|
|
582
|
-
return
|
|
621
|
+
return `This signup expired (24 hours). Run "${cliInvocation()} signup" again.`;
|
|
583
622
|
default:
|
|
584
623
|
return null;
|
|
585
624
|
}
|
|
@@ -609,14 +648,14 @@ function storeOrPrintKey(apiKey, store = storeApiKey) {
|
|
|
609
648
|
return true;
|
|
610
649
|
} catch (err) {
|
|
611
650
|
error(`Could not save the key to ~/.delegance/credentials: ${err.message}`);
|
|
612
|
-
warn(
|
|
651
|
+
warn(`Your key is below. It is shown once. Save it now, then run "${cliInvocation()} auth login --key <key>".`);
|
|
613
652
|
console.log(apiKey);
|
|
614
653
|
return false;
|
|
615
654
|
}
|
|
616
655
|
}
|
|
617
656
|
async function waitAndStore(pending, timeoutMs) {
|
|
618
657
|
const signup = (0, import_sdk2.createSignup)({ baseUrl: pending.baseUrl });
|
|
619
|
-
const spinner = (0, import_ora.default)(`Waiting for you to click the link sent to ${pending.email} (Ctrl-C is safe: resume with "
|
|
658
|
+
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();
|
|
620
659
|
const started = Date.now();
|
|
621
660
|
try {
|
|
622
661
|
const result = await signup.waitForKey(pending.enrollmentId, pending.deviceSecret, {
|
|
@@ -628,7 +667,7 @@ async function waitAndStore(pending, timeoutMs) {
|
|
|
628
667
|
});
|
|
629
668
|
if (result.status !== "ready") {
|
|
630
669
|
spinner.warn("Still waiting on the email confirmation.");
|
|
631
|
-
info(
|
|
670
|
+
info(`Run "${cliInvocation()} verify" after you click the link.`);
|
|
632
671
|
return;
|
|
633
672
|
}
|
|
634
673
|
spinner.stop();
|
|
@@ -636,7 +675,7 @@ async function waitAndStore(pending, timeoutMs) {
|
|
|
636
675
|
setConfig("defaultCompanyId", result.companyId);
|
|
637
676
|
clearPendingSignup();
|
|
638
677
|
if (stored) success(`Verified. API key ${maskApiKey(result.apiKey)} saved to ~/.delegance/credentials`);
|
|
639
|
-
success(
|
|
678
|
+
success(`You can quote now: ${cliInvocation()} quote start --coverage gl`);
|
|
640
679
|
} catch (err) {
|
|
641
680
|
spinner.stop();
|
|
642
681
|
const why = exchangeRefusal(err);
|
|
@@ -670,7 +709,7 @@ function createSignupCommand() {
|
|
|
670
709
|
const existing = loadPendingSignup();
|
|
671
710
|
if (existing) {
|
|
672
711
|
warn(`A signup for ${existing.email} is already waiting for its email confirmation.`);
|
|
673
|
-
info(
|
|
712
|
+
info(`Run "${cliInvocation()} verify" to finish it, or "${cliInvocation()} verify --cancel" to start over.`);
|
|
674
713
|
process.exit(1);
|
|
675
714
|
}
|
|
676
715
|
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -735,7 +774,7 @@ function createSignupCommand() {
|
|
|
735
774
|
warn("We could not save your business address. Add it in the portal before quoting, or carriers cannot price you.");
|
|
736
775
|
}
|
|
737
776
|
if (options.wait === false) {
|
|
738
|
-
info(
|
|
777
|
+
info(`After you click it, run "${cliInvocation()} verify" to collect your API key.`);
|
|
739
778
|
return;
|
|
740
779
|
}
|
|
741
780
|
await waitAndStore(pending, Number(options.timeout) * 6e4 || 15 * 6e4);
|
|
@@ -756,7 +795,7 @@ function createVerifyCommand() {
|
|
|
756
795
|
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) => {
|
|
757
796
|
const pending = loadPendingSignup();
|
|
758
797
|
if (!pending) {
|
|
759
|
-
info(
|
|
798
|
+
info(`No signup is waiting on this machine. Start one with "${cliInvocation()} signup".`);
|
|
760
799
|
return;
|
|
761
800
|
}
|
|
762
801
|
if (options.cancel) {
|
|
@@ -818,9 +857,9 @@ function deviceLoginRefusal(err) {
|
|
|
818
857
|
case "access_denied":
|
|
819
858
|
return "The request was denied in the portal. Nothing was stored.";
|
|
820
859
|
case "expired":
|
|
821
|
-
return
|
|
860
|
+
return `The code expired before it was approved. Run "${cliInvocation()} auth login" again.`;
|
|
822
861
|
case "already_collected":
|
|
823
|
-
return
|
|
862
|
+
return `This login was already used. Run "${cliInvocation()} auth login" again.`;
|
|
824
863
|
default:
|
|
825
864
|
return null;
|
|
826
865
|
}
|
|
@@ -829,7 +868,7 @@ function notAvailable(err) {
|
|
|
829
868
|
return err instanceof import_sdk3.DeleganceError && (err.statusCode === 404 || err.reason === "not_available");
|
|
830
869
|
}
|
|
831
870
|
function pasteFallbackHint() {
|
|
832
|
-
info(`Create a key at ${PORTAL_URL}/portal/settings/api-keys, then run "
|
|
871
|
+
info(`Create a key at ${PORTAL_URL}/portal/settings/api-keys, then run "${cliInvocation()} auth login --key <key>".`);
|
|
833
872
|
}
|
|
834
873
|
async function readKeyInfo(apiKey, timeoutMs = 5e3) {
|
|
835
874
|
try {
|
|
@@ -906,7 +945,7 @@ async function loginWithBrowser(openBrowser) {
|
|
|
906
945
|
});
|
|
907
946
|
if (result.status !== "ready") {
|
|
908
947
|
spinner.fail("The code expired before it was approved.");
|
|
909
|
-
info(
|
|
948
|
+
info(`Run "${cliInvocation()} auth login" again.`);
|
|
910
949
|
process.exit(1);
|
|
911
950
|
}
|
|
912
951
|
spinner.stop();
|
|
@@ -963,7 +1002,7 @@ function createAuthCommand() {
|
|
|
963
1002
|
header("Authentication Status");
|
|
964
1003
|
if (!isAuthenticated()) {
|
|
965
1004
|
warn("Not authenticated");
|
|
966
|
-
info(
|
|
1005
|
+
info(`Run "${cliInvocation()} auth login" to sign in, or "${cliInvocation()} signup" to create an account`);
|
|
967
1006
|
return;
|
|
968
1007
|
}
|
|
969
1008
|
const apiKey = getApiKey() || "";
|
|
@@ -986,7 +1025,7 @@ function createAuthCommand() {
|
|
|
986
1025
|
}
|
|
987
1026
|
if (refused) {
|
|
988
1027
|
warn(refused);
|
|
989
|
-
info(
|
|
1028
|
+
info(`Run "${cliInvocation()} auth login" to sign in again.`);
|
|
990
1029
|
} else {
|
|
991
1030
|
success("Authenticated");
|
|
992
1031
|
}
|
|
@@ -1004,7 +1043,7 @@ function createAuthCommand() {
|
|
|
1004
1043
|
const apiKey = getApiKey();
|
|
1005
1044
|
if (!apiKey) {
|
|
1006
1045
|
error("Not authenticated");
|
|
1007
|
-
info(
|
|
1046
|
+
info(`Run "${cliInvocation()} auth login" to sign in`);
|
|
1008
1047
|
process.exit(1);
|
|
1009
1048
|
}
|
|
1010
1049
|
const spinner = (0, import_ora2.default)("Refreshing your key...").start();
|
|
@@ -1052,7 +1091,7 @@ function createAuthCommand() {
|
|
|
1052
1091
|
auth.command("whoami").description("Show current authenticated identity").action(async () => {
|
|
1053
1092
|
if (!isAuthenticated()) {
|
|
1054
1093
|
error("Not authenticated");
|
|
1055
|
-
info(
|
|
1094
|
+
info(`Run "${cliInvocation()} auth login" to authenticate`);
|
|
1056
1095
|
process.exit(1);
|
|
1057
1096
|
}
|
|
1058
1097
|
const apiKey = getApiKey();
|
|
@@ -1248,7 +1287,7 @@ function createCompanyCommand() {
|
|
|
1248
1287
|
const companyId = getConfigValue("defaultCompanyId");
|
|
1249
1288
|
if (!companyId) {
|
|
1250
1289
|
info("No default company set");
|
|
1251
|
-
info(
|
|
1290
|
+
info(`Use "${cliInvocation()} company use <id>" to set one`);
|
|
1252
1291
|
return;
|
|
1253
1292
|
}
|
|
1254
1293
|
const client = getClient();
|
|
@@ -1522,7 +1561,7 @@ function createDocumentCommand() {
|
|
|
1522
1561
|
process.exit(1);
|
|
1523
1562
|
} else {
|
|
1524
1563
|
waitSpinner.warn(`Extraction still ${finished.status}`);
|
|
1525
|
-
if (!options.json) info(`Run "
|
|
1564
|
+
if (!options.json) info(`Run "${cliInvocation()} doc wait ${documentId}" to keep following it`);
|
|
1526
1565
|
}
|
|
1527
1566
|
return;
|
|
1528
1567
|
}
|
|
@@ -1530,7 +1569,7 @@ function createDocumentCommand() {
|
|
|
1530
1569
|
json(result);
|
|
1531
1570
|
} else {
|
|
1532
1571
|
info(`${result.reprocessesRemaining} reprocess attempts left today`);
|
|
1533
|
-
info(`Run "
|
|
1572
|
+
info(`Run "${cliInvocation()} doc wait ${documentId}" to follow it`);
|
|
1534
1573
|
}
|
|
1535
1574
|
} catch (err) {
|
|
1536
1575
|
spinner.fail("Failed to reprocess document");
|
|
@@ -1678,6 +1717,90 @@ var import_inquirer5 = __toESM(require("inquirer"));
|
|
|
1678
1717
|
var import_ora6 = __toESM(require("ora"));
|
|
1679
1718
|
var import_sdk4 = require("@delegance/sdk");
|
|
1680
1719
|
|
|
1720
|
+
// src/utils/coverage-names.ts
|
|
1721
|
+
var NAMES = {
|
|
1722
|
+
// General liability
|
|
1723
|
+
cgl: "General Liability",
|
|
1724
|
+
gl: "General Liability",
|
|
1725
|
+
general_liability: "General Liability",
|
|
1726
|
+
// Workers' comp
|
|
1727
|
+
wc: "Workers' Comp",
|
|
1728
|
+
workers_comp: "Workers' Comp",
|
|
1729
|
+
workers_compensation: "Workers' Comp",
|
|
1730
|
+
// Business owner's policy
|
|
1731
|
+
bop: "Business Owner's Policy",
|
|
1732
|
+
business_owners: "Business Owner's Policy",
|
|
1733
|
+
business_owners_policy: "Business Owner's Policy",
|
|
1734
|
+
// Cyber
|
|
1735
|
+
cyber: "Cyber",
|
|
1736
|
+
cyber_liability: "Cyber",
|
|
1737
|
+
// Professional liability (E&O)
|
|
1738
|
+
mpl: "Professional Liability (E&O)",
|
|
1739
|
+
pl: "Professional Liability (E&O)",
|
|
1740
|
+
pro_liab: "Professional Liability (E&O)",
|
|
1741
|
+
professional_liability: "Professional Liability (E&O)",
|
|
1742
|
+
eo: "Professional Liability (E&O)",
|
|
1743
|
+
e_o: "Professional Liability (E&O)",
|
|
1744
|
+
tech_eo: "Tech E&O",
|
|
1745
|
+
errors_omissions: "Tech E&O",
|
|
1746
|
+
// Auto
|
|
1747
|
+
ca: "Commercial Auto",
|
|
1748
|
+
auto: "Commercial Auto",
|
|
1749
|
+
commercial_auto: "Commercial Auto",
|
|
1750
|
+
// Property
|
|
1751
|
+
cp: "Commercial Property",
|
|
1752
|
+
property: "Commercial Property",
|
|
1753
|
+
commercial_property: "Commercial Property",
|
|
1754
|
+
// Umbrella
|
|
1755
|
+
umbrella: "Umbrella",
|
|
1756
|
+
excess: "Umbrella",
|
|
1757
|
+
commercial_umbrella: "Umbrella",
|
|
1758
|
+
// Builders risk
|
|
1759
|
+
br: "Builders' Risk",
|
|
1760
|
+
builders_risk: "Builders' Risk",
|
|
1761
|
+
// Inland marine and marine
|
|
1762
|
+
im: "Inland Marine",
|
|
1763
|
+
inland_marine: "Inland Marine",
|
|
1764
|
+
marine: "Marine",
|
|
1765
|
+
// Management liability family
|
|
1766
|
+
ml: "Management Liability",
|
|
1767
|
+
management_liability: "Management Liability",
|
|
1768
|
+
do: "Directors and Officers (D&O)",
|
|
1769
|
+
d_o: "Directors and Officers (D&O)",
|
|
1770
|
+
directors_officers: "Directors and Officers (D&O)",
|
|
1771
|
+
epli: "Employment Practices Liability",
|
|
1772
|
+
employment_practices: "Employment Practices Liability",
|
|
1773
|
+
crime: "Crime",
|
|
1774
|
+
fiduciary: "Fiduciary Liability",
|
|
1775
|
+
fiduciary_liability: "Fiduciary Liability",
|
|
1776
|
+
// Everything else on the canonical list
|
|
1777
|
+
product_liability: "Product Liability",
|
|
1778
|
+
liquor_liability: "Liquor Liability",
|
|
1779
|
+
contractors_liability: "Contractors' Liability",
|
|
1780
|
+
equipment_breakdown: "Equipment Breakdown",
|
|
1781
|
+
environmental_liability: "Environmental Liability",
|
|
1782
|
+
healthcare_medical: "Healthcare Medical Liability",
|
|
1783
|
+
life_sciences: "Life Sciences",
|
|
1784
|
+
hospitality_programs: "Hospitality Program",
|
|
1785
|
+
commercial_flood: "Commercial Flood",
|
|
1786
|
+
home_based_business: "Home-Based Business",
|
|
1787
|
+
foreign_package: "Foreign Package",
|
|
1788
|
+
multinational_package: "Multinational Package",
|
|
1789
|
+
political_risk: "Political Risk",
|
|
1790
|
+
trade_credit: "Trade Credit",
|
|
1791
|
+
kidnap_ransom: "Kidnap and Ransom",
|
|
1792
|
+
product_recall: "Product Recall",
|
|
1793
|
+
railroad_liability: "Railroad Liability"
|
|
1794
|
+
};
|
|
1795
|
+
function coverageName(code) {
|
|
1796
|
+
if (code === null || code === void 0) return "";
|
|
1797
|
+
const key = code.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1798
|
+
return NAMES[key] ?? code;
|
|
1799
|
+
}
|
|
1800
|
+
function coverageNames(codes) {
|
|
1801
|
+
return codes.map(coverageName).filter(Boolean).join(", ");
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1681
1804
|
// src/commerce/questions.ts
|
|
1682
1805
|
var COVERAGE_ALIASES = {
|
|
1683
1806
|
gl: "general_liability",
|
|
@@ -1756,14 +1879,23 @@ function validateAnswers(questions, provided) {
|
|
|
1756
1879
|
}
|
|
1757
1880
|
return { answers, problems };
|
|
1758
1881
|
}
|
|
1882
|
+
function rowStatus(q) {
|
|
1883
|
+
if (String(q.state).toUpperCase() === "REFERRED") {
|
|
1884
|
+
return q.resultStatus === "needs_info" ? "needs_info" : "referred";
|
|
1885
|
+
}
|
|
1886
|
+
return q.marketStatus || q.state;
|
|
1887
|
+
}
|
|
1759
1888
|
function sortQuoteRows(quotes) {
|
|
1760
1889
|
return quotes.map((q) => ({
|
|
1761
1890
|
carrier: q.carrierName || q.carrierId,
|
|
1762
1891
|
coverage: q.coverage ?? "",
|
|
1763
1892
|
premium: q.premium,
|
|
1764
|
-
status: q
|
|
1893
|
+
status: rowStatus(q),
|
|
1765
1894
|
bindable: q.bindable === true && q.state === "QUOTED",
|
|
1766
|
-
opportunityId: q.opportunityId
|
|
1895
|
+
opportunityId: q.opportunityId,
|
|
1896
|
+
limits: typeof q.limits === "string" && q.limits.trim() ? q.limits : null,
|
|
1897
|
+
deductible: typeof q.deductible === "number" && Number.isFinite(q.deductible) ? q.deductible : null,
|
|
1898
|
+
message: typeof q.message === "string" && q.message.trim() ? q.message.trim() : null
|
|
1767
1899
|
})).sort((a, b) => {
|
|
1768
1900
|
if (a.bindable !== b.bindable) return a.bindable ? -1 : 1;
|
|
1769
1901
|
if (a.premium === null && b.premium === null) return 0;
|
|
@@ -1772,8 +1904,50 @@ function sortQuoteRows(quotes) {
|
|
|
1772
1904
|
return a.premium - b.premium;
|
|
1773
1905
|
});
|
|
1774
1906
|
}
|
|
1775
|
-
function
|
|
1776
|
-
|
|
1907
|
+
function bestPerCoverage(rows) {
|
|
1908
|
+
const best = /* @__PURE__ */ new Map();
|
|
1909
|
+
for (const r of rows) {
|
|
1910
|
+
if (!r.bindable || r.premium === null) continue;
|
|
1911
|
+
const key = coverageName(r.coverage);
|
|
1912
|
+
const cur = best.get(key);
|
|
1913
|
+
if (!cur || r.premium < cur.premium) best.set(key, r);
|
|
1914
|
+
}
|
|
1915
|
+
return [...best.values()].sort((a, b) => a.premium - b.premium);
|
|
1916
|
+
}
|
|
1917
|
+
function groupRowsByCoverage(rows) {
|
|
1918
|
+
const order = /* @__PURE__ */ new Map();
|
|
1919
|
+
rows.forEach((r) => {
|
|
1920
|
+
const key = coverageName(r.coverage);
|
|
1921
|
+
if (!order.has(key)) order.set(key, order.size);
|
|
1922
|
+
});
|
|
1923
|
+
const rank = (r) => order.get(coverageName(r.coverage));
|
|
1924
|
+
return rows.map((r, i) => ({ r, i })).sort((a, b) => rank(a.r) - rank(b.r) || a.i - b.i).map((x) => x.r);
|
|
1925
|
+
}
|
|
1926
|
+
var STATUS_WORDS = {
|
|
1927
|
+
quote: "Quoted",
|
|
1928
|
+
quoted: "Quoted",
|
|
1929
|
+
declined: "Declined",
|
|
1930
|
+
decline: "Declined",
|
|
1931
|
+
quoting: "Pricing",
|
|
1932
|
+
pending: "Pricing",
|
|
1933
|
+
submitted: "Pricing",
|
|
1934
|
+
in_progress: "Pricing",
|
|
1935
|
+
referred: "Referred to an underwriter",
|
|
1936
|
+
refer: "Referred to an underwriter",
|
|
1937
|
+
needs_info: "Needs more info",
|
|
1938
|
+
awaiting_broker_review: "With a broker",
|
|
1939
|
+
broker_review: "With a broker",
|
|
1940
|
+
bind_requested: "Bind requested",
|
|
1941
|
+
bound: "Bound",
|
|
1942
|
+
expired: "Expired",
|
|
1943
|
+
error: "Failed",
|
|
1944
|
+
failed: "Failed"
|
|
1945
|
+
};
|
|
1946
|
+
function quoteStatusWord(status) {
|
|
1947
|
+
const key = status.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
1948
|
+
if (STATUS_WORDS[key]) return STATUS_WORDS[key];
|
|
1949
|
+
const words = key.replace(/_/g, " ").trim();
|
|
1950
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : status;
|
|
1777
1951
|
}
|
|
1778
1952
|
function summarizeStatus(s) {
|
|
1779
1953
|
const quoted = s.carrierQuotes.filter((q) => q.premium !== null).length;
|
|
@@ -1823,7 +1997,14 @@ function formatKnown(q, value) {
|
|
|
1823
1997
|
}
|
|
1824
1998
|
|
|
1825
1999
|
// src/commerce/demo.ts
|
|
1826
|
-
var DEMO_BUSINESS = "Riverbend Bakery (demo), Austin TX
|
|
2000
|
+
var DEMO_BUSINESS = "Riverbend Bakery (demo), Austin TX";
|
|
2001
|
+
var DEMO_SHORT_LABELS = {
|
|
2002
|
+
annual_revenue: "Annual revenue",
|
|
2003
|
+
employees: "Full-time employees",
|
|
2004
|
+
delivery: "Delivers orders",
|
|
2005
|
+
delivery_vehicles: "Delivery vehicles",
|
|
2006
|
+
claims_5y: "Claims in last 5 years"
|
|
2007
|
+
};
|
|
1827
2008
|
var DEMO_QUESTIONS = [
|
|
1828
2009
|
{ id: "annual_revenue", label: "Annual revenue (USD)", type: "currency", required: true, prefill: 85e4 },
|
|
1829
2010
|
{ id: "employees", label: "Full-time employees", type: "number", required: true, prefill: 6 },
|
|
@@ -1857,6 +2038,7 @@ var DEMO_QUESTIONS = [
|
|
|
1857
2038
|
];
|
|
1858
2039
|
var DEMO_STATUS = {
|
|
1859
2040
|
submissionId: "demo-submission",
|
|
2041
|
+
effectiveDate: "2026-11-01",
|
|
1860
2042
|
coverages: ["general_liability", "workers_comp"],
|
|
1861
2043
|
expiresAt: null,
|
|
1862
2044
|
submitted: true,
|
|
@@ -1864,19 +2046,24 @@ var DEMO_STATUS = {
|
|
|
1864
2046
|
allCarriersResolved: true,
|
|
1865
2047
|
awaitingBrokerReviewCount: 0,
|
|
1866
2048
|
carrierQuotes: [
|
|
1867
|
-
{ opportunityId: "demo-1", carrierId: "
|
|
1868
|
-
{ opportunityId: "demo-2", carrierId: "
|
|
1869
|
-
{ opportunityId: "demo-3", carrierId: "
|
|
1870
|
-
{ opportunityId: "demo-4", carrierId: "
|
|
2049
|
+
{ 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 },
|
|
2050
|
+
{ 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 },
|
|
2051
|
+
{ 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 },
|
|
2052
|
+
{ 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 }
|
|
1871
2053
|
]
|
|
1872
2054
|
};
|
|
1873
2055
|
|
|
1874
2056
|
// src/commands/instant-quote.ts
|
|
1875
|
-
function
|
|
2057
|
+
function shellArg(value) {
|
|
2058
|
+
return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
2059
|
+
}
|
|
2060
|
+
function activeRequestLines(err, coverageFlag, ctx = {}) {
|
|
1876
2061
|
if (!(err instanceof import_sdk4.DeleganceError) || err.statusCode !== 409 || !err.body) return null;
|
|
1877
2062
|
const { body } = err;
|
|
1878
2063
|
const code = body.message;
|
|
1879
|
-
const
|
|
2064
|
+
const answersArg = ctx.answersFile ? ` --answers ${shellArg(ctx.answersFile)}` : "";
|
|
2065
|
+
const requote = `${cliInvocation()} quote start --coverage ${coverageFlag}${answersArg} --replace`;
|
|
2066
|
+
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;
|
|
1880
2067
|
if (code === "active_request_exists") {
|
|
1881
2068
|
const active = Array.isArray(body.active) ? body.active : [];
|
|
1882
2069
|
const first = active[0];
|
|
@@ -1884,7 +2071,7 @@ function activeRequestLines(err, coverageFlag) {
|
|
|
1884
2071
|
const carriers = Array.isArray(first?.carriers) ? first.carriers.map((c) => `${c.carrierId}${c.state ? ` (${c.state.toLowerCase()})` : ""}`).join(", ") : "";
|
|
1885
2072
|
return [
|
|
1886
2073
|
`Already with carriers${id ? `: ${id}` : ""}${carriers ? `, ${carriers}` : ""}.`,
|
|
1887
|
-
`Check it: ${id ?
|
|
2074
|
+
`Check it: ${id ? `${cliInvocation()} quote status ${id}` : `${cliInvocation()} quote status <id>`} Replace it: ${replaceCmd}`
|
|
1888
2075
|
];
|
|
1889
2076
|
}
|
|
1890
2077
|
if (code === "replace_conflict") {
|
|
@@ -2011,7 +2198,7 @@ function handoffLines(r) {
|
|
|
2011
2198
|
if (!r.alreadyHandedOff && r.unansweredRequired > 0) {
|
|
2012
2199
|
lines.push(`The broker will email you for the ${r.unansweredRequired} answer(s) still missing.`);
|
|
2013
2200
|
}
|
|
2014
|
-
lines.push(`Check it:
|
|
2201
|
+
lines.push(`Check it: ${cliInvocation()} quote status ${r.submissionId}`);
|
|
2015
2202
|
return lines;
|
|
2016
2203
|
}
|
|
2017
2204
|
async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
@@ -2052,7 +2239,7 @@ async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
|
2052
2239
|
process.exit(1);
|
|
2053
2240
|
}
|
|
2054
2241
|
} else if (!opts.json && isInteractive() && plan.questions.length > 0) {
|
|
2055
|
-
info(`A broker can place ${plan.coverages
|
|
2242
|
+
info(`A broker can place ${coverageNames(plan.coverages)} with a market that quotes by hand. ${plan.questions.length} application question(s) left.`);
|
|
2056
2243
|
info("Press Ctrl-C to stop at any point; what you have answered goes to the broker.");
|
|
2057
2244
|
const onInterrupt = () => {
|
|
2058
2245
|
complete = false;
|
|
@@ -2084,7 +2271,7 @@ async function sendHandoff(submissionId, input, idempotencyKey, asJson) {
|
|
|
2084
2271
|
else handoffLines(result).forEach((l) => info(l));
|
|
2085
2272
|
} catch (err) {
|
|
2086
2273
|
spinner?.fail("Could not reach a broker");
|
|
2087
|
-
console.error(`Try again:
|
|
2274
|
+
console.error(`Try again: ${cliInvocation()} quote handoff ${submissionId}`);
|
|
2088
2275
|
failWith("API error", err);
|
|
2089
2276
|
}
|
|
2090
2277
|
}
|
|
@@ -2095,8 +2282,34 @@ function printFloorDrops(failures) {
|
|
|
2095
2282
|
floors.forEach((f) => console.log(` - ${f.carrierId}: ${f.missing}`));
|
|
2096
2283
|
info("Add it to your business profile in the portal, then run the quote again.");
|
|
2097
2284
|
}
|
|
2285
|
+
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.";
|
|
2286
|
+
var premiumText = (n) => n === null ? "-" : formatCurrency(n);
|
|
2287
|
+
var deductibleText = (n) => n === null ? "-" : n === 0 ? "None" : formatCurrency(n);
|
|
2288
|
+
function bestPriceLines(best) {
|
|
2289
|
+
if (best.length === 0) return [];
|
|
2290
|
+
const names = best.map((r) => coverageName(r.coverage));
|
|
2291
|
+
const nameW = Math.max(...names.map((n) => n.length));
|
|
2292
|
+
const carrierW = Math.max(...best.map((r) => r.carrier.length));
|
|
2293
|
+
return [
|
|
2294
|
+
"Best price per coverage:",
|
|
2295
|
+
...best.map((r, i) => ` ${names[i].padEnd(nameW)} ${r.carrier.padEnd(carrierW)} ${formatCurrency(r.premium)}/yr`)
|
|
2296
|
+
];
|
|
2297
|
+
}
|
|
2298
|
+
function bindHintLines(submissionId, best) {
|
|
2299
|
+
if (best.length === 1) return [`Bind it: ${cliCommand(`bind ${submissionId} ${best[0].opportunityId}`)}`];
|
|
2300
|
+
return best.map((r) => `Bind ${coverageName(r.coverage)}: ${cliCommand(`bind ${submissionId} ${r.opportunityId}`)}`);
|
|
2301
|
+
}
|
|
2302
|
+
function demoClosingLines(best) {
|
|
2303
|
+
const example = best[0] ? ` (e.g. bind ... ${best[0].opportunityId})` : "";
|
|
2304
|
+
return [
|
|
2305
|
+
`Next, on a real quote: ${cliCommand("bind <quote id> <offer>")}${example}`,
|
|
2306
|
+
"DEMO: sample premiums, nothing went to a carrier.",
|
|
2307
|
+
`Get real quotes: ${cliCommand("signup")}`,
|
|
2308
|
+
AGENT_TIP
|
|
2309
|
+
];
|
|
2310
|
+
}
|
|
2098
2311
|
function printQuoteTable(status, opts = {}) {
|
|
2099
|
-
const rows = sortQuoteRows(status.carrierQuotes);
|
|
2312
|
+
const rows = groupRowsByCoverage(sortQuoteRows(status.carrierQuotes));
|
|
2100
2313
|
if (rows.length === 0) {
|
|
2101
2314
|
if (status.brokerReview) {
|
|
2102
2315
|
info(`A broker has this request (${status.brokerReview.status.replace(/_/g, " ")}). They place it by hand and email you with quotes or questions.`);
|
|
@@ -2106,25 +2319,67 @@ function printQuoteTable(status, opts = {}) {
|
|
|
2106
2319
|
if (status.awaitingBrokerReviewCount > 0) info(`${status.awaitingBrokerReviewCount} quote(s) are with a broker for review.`);
|
|
2107
2320
|
return;
|
|
2108
2321
|
}
|
|
2109
|
-
const best =
|
|
2110
|
-
|
|
2111
|
-
|
|
2322
|
+
const best = bestPerCoverage(rows);
|
|
2323
|
+
const bestIds = new Set(best.map((r) => r.opportunityId));
|
|
2324
|
+
const effectiveDate = status.effectiveDate;
|
|
2325
|
+
console.log();
|
|
2326
|
+
if (effectiveDate && opts.showStartDate !== false) {
|
|
2327
|
+
console.log(`Coverage starts ${formatDateOnly(effectiveDate)}`);
|
|
2328
|
+
}
|
|
2329
|
+
const bestLines = bestPriceLines(best);
|
|
2330
|
+
if (bestLines.length) {
|
|
2331
|
+
console.log(import_chalk2.default.bold(bestLines[0]));
|
|
2332
|
+
bestLines.slice(1).forEach((l) => console.log(l));
|
|
2333
|
+
console.log();
|
|
2112
2334
|
}
|
|
2113
2335
|
table(
|
|
2114
|
-
["Carrier", "Coverage", "Premium/yr", "
|
|
2336
|
+
["Carrier", "Coverage", "Premium/yr", "Limits", "Deductible", "Status", "Offer"],
|
|
2115
2337
|
rows.map((r) => {
|
|
2116
|
-
const cells = [
|
|
2117
|
-
|
|
2118
|
-
|
|
2338
|
+
const cells = [
|
|
2339
|
+
r.carrier,
|
|
2340
|
+
coverageName(r.coverage),
|
|
2341
|
+
premiumText(r.premium),
|
|
2342
|
+
r.limits ?? "-",
|
|
2343
|
+
deductibleText(r.deductible),
|
|
2344
|
+
quoteStatusWord(r.status),
|
|
2345
|
+
r.opportunityId
|
|
2346
|
+
];
|
|
2347
|
+
return bestIds.has(r.opportunityId) ? cells.map((c) => import_chalk2.default.bold(c)) : cells;
|
|
2348
|
+
}),
|
|
2349
|
+
{ compact: true }
|
|
2119
2350
|
);
|
|
2120
|
-
|
|
2121
|
-
if (
|
|
2122
|
-
console.log(
|
|
2351
|
+
const reasons = rows.filter((r) => r.message);
|
|
2352
|
+
if (reasons.length) {
|
|
2353
|
+
console.log();
|
|
2354
|
+
reasons.forEach((r) => console.log(`${r.carrier} (${quoteStatusWord(r.status)}): ${r.message}`));
|
|
2123
2355
|
}
|
|
2124
|
-
|
|
2356
|
+
console.log();
|
|
2357
|
+
console.log(import_chalk2.default.dim(`${summarizeStatus(status)}.`));
|
|
2358
|
+
if (opts.demo) {
|
|
2359
|
+
const [next, notice, signup, tip] = demoClosingLines(best);
|
|
2360
|
+
console.log(import_chalk2.default.green(next));
|
|
2361
|
+
console.log(import_chalk2.default.yellow(notice));
|
|
2362
|
+
console.log(import_chalk2.default.yellow(signup));
|
|
2363
|
+
console.log(import_chalk2.default.dim(tip));
|
|
2364
|
+
return;
|
|
2365
|
+
}
|
|
2366
|
+
bindHintLines(status.submissionId, best).forEach((l) => console.log(import_chalk2.default.green(l)));
|
|
2367
|
+
}
|
|
2368
|
+
function demoAnswerLines(answers, effectiveDate) {
|
|
2369
|
+
const pairs = [];
|
|
2370
|
+
for (const q of DEMO_QUESTIONS) {
|
|
2371
|
+
if (answers[q.id] === void 0) continue;
|
|
2372
|
+
const v = answers[q.id];
|
|
2373
|
+
const shown = q.type === "currency" && typeof v === "number" ? formatCurrency(v) : formatKnown(q, v);
|
|
2374
|
+
pairs.push([DEMO_SHORT_LABELS[q.id] ?? q.label, shown]);
|
|
2375
|
+
}
|
|
2376
|
+
if (effectiveDate) pairs.push(["Coverage starts", formatDateOnly(effectiveDate)]);
|
|
2377
|
+
const w = Math.max(...pairs.map(([k]) => k.length));
|
|
2378
|
+
return ["Sample answers (a real quote asks you these):", ...pairs.map(([k, v]) => ` ${k.padEnd(w)} ${v}`)];
|
|
2125
2379
|
}
|
|
2126
2380
|
async function runDemo(asJson, answersFile) {
|
|
2127
2381
|
(asJson ? console.error : console.log)(import_chalk2.default.yellow(`DEMO quote for ${DEMO_BUSINESS}. Nothing leaves this machine.`));
|
|
2382
|
+
let printedStart = false;
|
|
2128
2383
|
if (answersFile) {
|
|
2129
2384
|
let provided;
|
|
2130
2385
|
try {
|
|
@@ -2142,15 +2397,19 @@ async function runDemo(asJson, answersFile) {
|
|
|
2142
2397
|
const mode = answerMode({ demo: true, hasAnswersFile: false, json: asJson, isTTY: isInteractive() });
|
|
2143
2398
|
if (mode === "prompt") await askQuestions(DEMO_QUESTIONS);
|
|
2144
2399
|
else {
|
|
2145
|
-
suggestedAnswers(DEMO_QUESTIONS);
|
|
2146
|
-
if (!asJson)
|
|
2400
|
+
const assumed = suggestedAnswers(DEMO_QUESTIONS);
|
|
2401
|
+
if (!asJson) {
|
|
2402
|
+
demoAnswerLines(assumed, DEMO_STATUS.effectiveDate).forEach((l) => console.log(l));
|
|
2403
|
+
printedStart = true;
|
|
2404
|
+
}
|
|
2147
2405
|
}
|
|
2148
2406
|
}
|
|
2149
|
-
const spinner = asJson ? null : (0, import_ora6.default)("Asking carriers (demo)...")
|
|
2407
|
+
const spinner = asJson ? null : (0, import_ora6.default)("Asking carriers (demo)...");
|
|
2408
|
+
if (spinner && process.stderr.isTTY) spinner.start();
|
|
2150
2409
|
await new Promise((resolve) => setTimeout(resolve, asJson ? 0 : 1200));
|
|
2151
2410
|
spinner?.succeed("4 carriers answered (demo)");
|
|
2152
2411
|
if (asJson) json({ demo: true, ...DEMO_STATUS });
|
|
2153
|
-
else printQuoteTable(DEMO_STATUS, { demo: true });
|
|
2412
|
+
else printQuoteTable(DEMO_STATUS, { demo: true, showStartDate: !printedStart });
|
|
2154
2413
|
}
|
|
2155
2414
|
function addInstantQuoteCommands(quote) {
|
|
2156
2415
|
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) => {
|
|
@@ -2215,14 +2474,14 @@ function addInstantQuoteCommands(quote) {
|
|
|
2215
2474
|
...options.replace ? { replace: true } : {}
|
|
2216
2475
|
});
|
|
2217
2476
|
} catch (err) {
|
|
2218
|
-
const guardLines = activeRequestLines(err, String(options.coverage));
|
|
2477
|
+
const guardLines = activeRequestLines(err, String(options.coverage), { answersFile: options.answers });
|
|
2219
2478
|
if (guardLines) {
|
|
2220
2479
|
submitSpinner.fail("Nothing was sent.");
|
|
2221
2480
|
guardLines.forEach((l) => options.json ? console.error(l) : info(l));
|
|
2222
2481
|
process.exit(1);
|
|
2223
2482
|
}
|
|
2224
2483
|
submitSpinner.fail("Submit failed");
|
|
2225
|
-
console.error(`Before trying again, check whether it went through:
|
|
2484
|
+
console.error(`Before trying again, check whether it went through: ${cliInvocation()} quote status ${session.submissionId}`);
|
|
2226
2485
|
failWith("API error", err);
|
|
2227
2486
|
}
|
|
2228
2487
|
if (submitted.status === "no_carriers") {
|
|
@@ -2238,14 +2497,14 @@ function addInstantQuoteCommands(quote) {
|
|
|
2238
2497
|
}
|
|
2239
2498
|
if (submitted.status === "acord_pending") {
|
|
2240
2499
|
submitSpinner.succeed("Sent to a broker: these coverages need a person, not an instant quote.");
|
|
2241
|
-
info(`Track it with:
|
|
2500
|
+
info(`Track it with: ${cliInvocation()} quote status ${session.submissionId}`);
|
|
2242
2501
|
return;
|
|
2243
2502
|
}
|
|
2244
2503
|
submitSpinner.succeed(`Filed with ${submitted.quotes.filter((q) => q.status === "submitted").length} carrier(s).`);
|
|
2245
2504
|
printFloorDrops(submitted.partialFailures);
|
|
2246
2505
|
if (submitted.replaceNotice && !options.json) info(submitted.replaceNotice);
|
|
2247
2506
|
if (options.wait === false) {
|
|
2248
|
-
info(`Check premiums with:
|
|
2507
|
+
info(`Check premiums with: ${cliInvocation()} quote status ${session.submissionId} --wait`);
|
|
2249
2508
|
return;
|
|
2250
2509
|
}
|
|
2251
2510
|
await waitAndPrint(session.submissionId, Number(options.timeout) * 6e4 || 6e5, !!options.json);
|
|
@@ -2274,22 +2533,25 @@ function addInstantQuoteCommands(quote) {
|
|
|
2274
2533
|
}
|
|
2275
2534
|
if (submitted.status !== "submitted") {
|
|
2276
2535
|
info("Sent to a broker for these coverages.");
|
|
2277
|
-
info(`Check it:
|
|
2536
|
+
info(`Check it: ${cliInvocation()} quote status ${submissionId}`);
|
|
2278
2537
|
return;
|
|
2279
2538
|
}
|
|
2280
2539
|
if (options.wait === false) {
|
|
2281
|
-
info(`Check premiums with:
|
|
2540
|
+
info(`Check premiums with: ${cliInvocation()} quote status ${submissionId} --wait`);
|
|
2282
2541
|
return;
|
|
2283
2542
|
}
|
|
2284
2543
|
await waitAndPrint(submissionId, Number(options.timeout) * 6e4 || 6e5, !!options.json);
|
|
2285
2544
|
} catch (err) {
|
|
2286
|
-
const guardLines = activeRequestLines(err, "<list>"
|
|
2545
|
+
const guardLines = activeRequestLines(err, "<list>", {
|
|
2546
|
+
answersFile: options.answers,
|
|
2547
|
+
submit: { submissionId, sessionVersion: String(options.sessionVersion) }
|
|
2548
|
+
});
|
|
2287
2549
|
if (guardLines) {
|
|
2288
2550
|
error("Nothing was sent.");
|
|
2289
2551
|
guardLines.forEach((l) => options.json ? console.error(l) : info(l));
|
|
2290
2552
|
process.exit(1);
|
|
2291
2553
|
}
|
|
2292
|
-
console.error(`Before trying again, check whether it went through:
|
|
2554
|
+
console.error(`Before trying again, check whether it went through: ${cliInvocation()} quote status ${submissionId}`);
|
|
2293
2555
|
failWith("Submit failed", err);
|
|
2294
2556
|
}
|
|
2295
2557
|
});
|
|
@@ -2325,7 +2587,7 @@ async function waitAndPrint(submissionId, timeoutMs, asJson) {
|
|
|
2325
2587
|
spinner?.stop();
|
|
2326
2588
|
if (asJson) json(status);
|
|
2327
2589
|
else printQuoteTable(status);
|
|
2328
|
-
if (!status.allCarriersResolved && !asJson) info(`Some carriers are still working. Check again:
|
|
2590
|
+
if (!status.allCarriersResolved && !asJson) info(`Some carriers are still working. Check again: ${cliInvocation()} quote status ${submissionId} --wait`);
|
|
2329
2591
|
} catch (err) {
|
|
2330
2592
|
spinner?.fail("Could not read the quote");
|
|
2331
2593
|
failWith("API error", err);
|
|
@@ -2438,13 +2700,13 @@ function createQuoteCommand() {
|
|
|
2438
2700
|
}
|
|
2439
2701
|
console.log();
|
|
2440
2702
|
info(`Queue IDs: ${result.queueIds.join(", ")}`);
|
|
2441
|
-
info(
|
|
2703
|
+
info(`Use "${cliInvocation()} quote list" to check status`);
|
|
2442
2704
|
}
|
|
2443
2705
|
} catch (err) {
|
|
2444
2706
|
spinner.fail("Failed to submit quote request");
|
|
2445
2707
|
if (err instanceof import_sdk5.NotImplementedError) {
|
|
2446
2708
|
error(QUOTE_SUBMISSION_UNAVAILABLE);
|
|
2447
|
-
info(
|
|
2709
|
+
info(`For an instant quote from the terminal: ${cliInvocation()} quote start --coverage gl`);
|
|
2448
2710
|
info(`Or start a quote at ${PORTAL_URL}`);
|
|
2449
2711
|
process.exit(1);
|
|
2450
2712
|
}
|
|
@@ -2602,7 +2864,7 @@ function createPolicyCommand() {
|
|
|
2602
2864
|
["Carrier", "Type", "Policy #", "Expiry", "Status", "Premium"],
|
|
2603
2865
|
policies.map((p) => [
|
|
2604
2866
|
p.carrier || "Unknown",
|
|
2605
|
-
p.policyType
|
|
2867
|
+
(p.policyType ? coverageName(p.policyType) : p.name) || "-",
|
|
2606
2868
|
p.policyNumber || "-",
|
|
2607
2869
|
p.expiryDate ? formatDate(p.expiryDate) : "-",
|
|
2608
2870
|
formatPolicyStatus(p.isActive, p.daysUntilExpiry),
|
|
@@ -2636,21 +2898,21 @@ function createPolicyCommand() {
|
|
|
2636
2898
|
if (urgent.length > 0) {
|
|
2637
2899
|
warn(`\u26A0\uFE0F Expiring within 7 days (${urgent.length}):`);
|
|
2638
2900
|
for (const p of urgent) {
|
|
2639
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2901
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - expires ${formatDate(p.expiryDate)}`);
|
|
2640
2902
|
}
|
|
2641
2903
|
console.log();
|
|
2642
2904
|
}
|
|
2643
2905
|
if (soon.length > 0) {
|
|
2644
2906
|
info(`\u{1F4C5} Expiring within 30 days (${soon.length}):`);
|
|
2645
2907
|
for (const p of soon) {
|
|
2646
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2908
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - expires ${formatDate(p.expiryDate)}`);
|
|
2647
2909
|
}
|
|
2648
2910
|
console.log();
|
|
2649
2911
|
}
|
|
2650
2912
|
if (later.length > 0) {
|
|
2651
2913
|
console.log(`\u{1F4C6} Expiring later (${later.length}):`);
|
|
2652
2914
|
for (const p of later) {
|
|
2653
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2915
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - ${p.daysUntilExpiry} days`);
|
|
2654
2916
|
}
|
|
2655
2917
|
}
|
|
2656
2918
|
}
|
|
@@ -2674,11 +2936,11 @@ function createPolicyCommand() {
|
|
|
2674
2936
|
} else {
|
|
2675
2937
|
const { policy: p, coverages } = found;
|
|
2676
2938
|
console.log();
|
|
2677
|
-
header(`${p.carrier} - ${p.policyType
|
|
2939
|
+
header(`${p.carrier} - ${p.policyType ? coverageName(p.policyType) : p.name}`);
|
|
2678
2940
|
console.log();
|
|
2679
2941
|
console.log(` Policy #: ${p.policyNumber || "N/A"}`);
|
|
2680
2942
|
console.log(` Carrier: ${p.carrier}`);
|
|
2681
|
-
console.log(` Type: ${p.policyType
|
|
2943
|
+
console.log(` Type: ${p.policyType ? coverageName(p.policyType) : "N/A"}`);
|
|
2682
2944
|
console.log(` Effective: ${p.effectiveDate ? formatDate(p.effectiveDate) : "N/A"}`);
|
|
2683
2945
|
console.log(` Expiry: ${p.expiryDate ? formatDate(p.expiryDate) : "N/A"}`);
|
|
2684
2946
|
console.log(` Status: ${formatPolicyStatus(p.isActive, p.daysUntilExpiry)}`);
|
|
@@ -2729,14 +2991,14 @@ function createPolicyCommand() {
|
|
|
2729
2991
|
return;
|
|
2730
2992
|
}
|
|
2731
2993
|
if (docs.length === 0) {
|
|
2732
|
-
info(
|
|
2994
|
+
info(`No source documents are linked to this policy yet. Upload one with: ${cliInvocation()} document upload <file>`);
|
|
2733
2995
|
return;
|
|
2734
2996
|
}
|
|
2735
2997
|
table(
|
|
2736
2998
|
["Document", "File", "Type", "Status", "Added"],
|
|
2737
2999
|
docs.map((d) => [d.id, d.fileName ?? "-", d.documentType ?? "-", d.status, d.createdAt ? formatDate(d.createdAt) : "-"])
|
|
2738
3000
|
);
|
|
2739
|
-
info(
|
|
3001
|
+
info(`Download one with: ${cliInvocation()} document status <documentId>`);
|
|
2740
3002
|
} catch (err) {
|
|
2741
3003
|
failWith("API error", err);
|
|
2742
3004
|
}
|
|
@@ -2769,9 +3031,9 @@ function createPolicyCommand() {
|
|
|
2769
3031
|
await signWithStepUp(client, out.request.id, sig, void 0, void 0);
|
|
2770
3032
|
} else {
|
|
2771
3033
|
info(`Sign here: ${out.signatureUrl}`);
|
|
2772
|
-
info(`Or sign from here:
|
|
3034
|
+
info(`Or sign from here: ${cliInvocation()} policy sign ${out.request.id} --signer-name "..." --signer-title "..." --attest`);
|
|
2773
3035
|
}
|
|
2774
|
-
info(`Check progress with "
|
|
3036
|
+
info(`Check progress with "${cliInvocation()} policy cancellation ${out.request.id}"`);
|
|
2775
3037
|
} catch (err) {
|
|
2776
3038
|
spinner.fail("Could not request the cancellation");
|
|
2777
3039
|
failWith("API error", err);
|
|
@@ -2828,7 +3090,7 @@ async function signWithStepUp(client, requestId, sig, stepUpId, presetCode) {
|
|
|
2828
3090
|
let code = presetCode;
|
|
2829
3091
|
if (!code) {
|
|
2830
3092
|
if (!process.stdin.isTTY) {
|
|
2831
|
-
warn(`No terminal to type the code in. Finish with:
|
|
3093
|
+
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>`);
|
|
2832
3094
|
return;
|
|
2833
3095
|
}
|
|
2834
3096
|
const answer = await import_inquirer7.default.prompt([{
|
|
@@ -2878,7 +3140,7 @@ function createCOICommand() {
|
|
|
2878
3140
|
policiesSpinner.stop();
|
|
2879
3141
|
if (policies.length === 0) {
|
|
2880
3142
|
error("No active policies found");
|
|
2881
|
-
info(
|
|
3143
|
+
info(`Upload policy documents first with "${cliInvocation()} doc upload"`);
|
|
2882
3144
|
process.exit(1);
|
|
2883
3145
|
}
|
|
2884
3146
|
const answers = await import_inquirer8.default.prompt([
|
|
@@ -2887,7 +3149,7 @@ function createCOICommand() {
|
|
|
2887
3149
|
name: "selectedPolicies",
|
|
2888
3150
|
message: "Select policies to include:",
|
|
2889
3151
|
choices: policies.map((p) => ({
|
|
2890
|
-
name: `${p.carrier} - ${p.policyType
|
|
3152
|
+
name: `${p.carrier} - ${p.policyType ? coverageName(p.policyType) : p.name} (${p.policyNumber || "No #"})`,
|
|
2891
3153
|
value: p.id,
|
|
2892
3154
|
checked: policyIds.includes(p.id)
|
|
2893
3155
|
})),
|
|
@@ -2974,7 +3236,7 @@ function createCOICommand() {
|
|
|
2974
3236
|
console.log();
|
|
2975
3237
|
console.log("Policies included:");
|
|
2976
3238
|
for (const p of result.policiesIncluded) {
|
|
2977
|
-
console.log(` - ${p.carrier} (${p.policyType})`);
|
|
3239
|
+
console.log(` - ${p.carrier} (${coverageName(p.policyType)})`);
|
|
2978
3240
|
}
|
|
2979
3241
|
}
|
|
2980
3242
|
} catch (err) {
|
|
@@ -2990,7 +3252,7 @@ function createCOICommand() {
|
|
|
2990
3252
|
spinner.succeed(`Found ${count} COIs`);
|
|
2991
3253
|
if (cois.length === 0) {
|
|
2992
3254
|
info("No COIs generated yet");
|
|
2993
|
-
info(
|
|
3255
|
+
info(`Generate one with "${cliInvocation()} coi generate --interactive"`);
|
|
2994
3256
|
return;
|
|
2995
3257
|
}
|
|
2996
3258
|
if (options.json) {
|
|
@@ -3067,7 +3329,7 @@ function createSOVCommand() {
|
|
|
3067
3329
|
info(`SOV ID: ${result.sovId}`);
|
|
3068
3330
|
info(`Status: ${result.status}`);
|
|
3069
3331
|
if (!options.wait) {
|
|
3070
|
-
info(
|
|
3332
|
+
info(`Use "${cliInvocation()} sov status <id>" to check parsing progress`);
|
|
3071
3333
|
}
|
|
3072
3334
|
}
|
|
3073
3335
|
} catch (err) {
|
|
@@ -3086,7 +3348,7 @@ function createSOVCommand() {
|
|
|
3086
3348
|
spinner.succeed(`Found ${count} SOVs`);
|
|
3087
3349
|
if (sovs.length === 0) {
|
|
3088
3350
|
info("No SOVs found");
|
|
3089
|
-
info(
|
|
3351
|
+
info(`Upload one with "${cliInvocation()} sov upload schedule.xlsx"`);
|
|
3090
3352
|
return;
|
|
3091
3353
|
}
|
|
3092
3354
|
if (options.json) {
|
|
@@ -3250,7 +3512,7 @@ function createAppetiteCommand() {
|
|
|
3250
3512
|
if (!options.wait) {
|
|
3251
3513
|
spinner.succeed(`Match queued: ${start.jobId}`);
|
|
3252
3514
|
if (options.json) json(start);
|
|
3253
|
-
else info(`Check it with "
|
|
3515
|
+
else info(`Check it with "${cliInvocation()} appetite status ${start.jobId}"`);
|
|
3254
3516
|
return;
|
|
3255
3517
|
}
|
|
3256
3518
|
spinner.text = `Matching appetite (about ${start.estimatedSeconds}s)...`;
|
|
@@ -3396,7 +3658,7 @@ function createKnowledgeCommand() {
|
|
|
3396
3658
|
);
|
|
3397
3659
|
}
|
|
3398
3660
|
if (pagination.hasMore && pagination.nextCursor) {
|
|
3399
|
-
info(`More:
|
|
3661
|
+
info(`More: ${cliInvocation()} knowledge notecards --cursor ${pagination.nextCursor}`);
|
|
3400
3662
|
}
|
|
3401
3663
|
} catch (err) {
|
|
3402
3664
|
spinner.fail("Failed to list notecards");
|
|
@@ -3520,7 +3782,7 @@ function createOnboardingCommand() {
|
|
|
3520
3782
|
const result = await client.onboarding.requestVerification();
|
|
3521
3783
|
spinner.succeed(`Verification sent to ${result.sentTo}`);
|
|
3522
3784
|
if (options.json) json(result);
|
|
3523
|
-
else info(
|
|
3785
|
+
else info(`Open the link in that email, then check progress with "${cliInvocation()} onboarding state"`);
|
|
3524
3786
|
} catch (err) {
|
|
3525
3787
|
spinner.fail("Failed to request verification");
|
|
3526
3788
|
failWith("API error", err);
|
|
@@ -3645,6 +3907,11 @@ var import_ora16 = __toESM(require("ora"));
|
|
|
3645
3907
|
var import_crypto3 = require("crypto");
|
|
3646
3908
|
var import_sdk6 = require("@delegance/sdk");
|
|
3647
3909
|
var KEYS_PORTAL_URL = `${PORTAL_URL}/portal/settings/api-keys`;
|
|
3910
|
+
function parseExpiresInDays(raw) {
|
|
3911
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
3912
|
+
if (raw.trim().toLowerCase() === "never") return "never";
|
|
3913
|
+
return parseInt(raw, 10);
|
|
3914
|
+
}
|
|
3648
3915
|
function parseScopes(raw) {
|
|
3649
3916
|
const parts = (Array.isArray(raw) ? raw : [raw ?? "read,write"]).flatMap((p) => p.split(/[\s,]+/)).map((p) => p.trim()).filter(Boolean);
|
|
3650
3917
|
const unknown = parts.filter((p) => !import_sdk6.API_KEY_SCOPES.includes(p));
|
|
@@ -3653,8 +3920,12 @@ function parseScopes(raw) {
|
|
|
3653
3920
|
}
|
|
3654
3921
|
return Array.from(new Set(parts));
|
|
3655
3922
|
}
|
|
3656
|
-
|
|
3657
|
-
|
|
3923
|
+
var EXPIRING_SCOPES = /* @__PURE__ */ new Set(["write", "admin", "claims:write"]);
|
|
3924
|
+
function isReadOnlyScopeSet(scopes) {
|
|
3925
|
+
return !scopes.some((s) => EXPIRING_SCOPES.has(s));
|
|
3926
|
+
}
|
|
3927
|
+
function replacementExpiryDays(expiresAt, now = /* @__PURE__ */ new Date(), scopes = []) {
|
|
3928
|
+
if (!expiresAt) return isReadOnlyScopeSet(scopes) ? "never" : void 0;
|
|
3658
3929
|
const ms = new Date(expiresAt).getTime() - now.getTime();
|
|
3659
3930
|
if (!Number.isFinite(ms)) throw new Error(`Cannot read the old key's expiry "${expiresAt}"; not rotating`);
|
|
3660
3931
|
const days = Math.ceil(ms / (24 * 60 * 60 * 1e3));
|
|
@@ -3703,14 +3974,14 @@ function createKeysCommand() {
|
|
|
3703
3974
|
])
|
|
3704
3975
|
);
|
|
3705
3976
|
if (pagination?.hasMore && pagination.nextCursor) {
|
|
3706
|
-
info(`More keys:
|
|
3977
|
+
info(`More keys: ${cliInvocation()} keys list --cursor ${pagination.nextCursor}`);
|
|
3707
3978
|
}
|
|
3708
3979
|
} catch (err) {
|
|
3709
3980
|
spinner.fail("Failed to list API keys");
|
|
3710
3981
|
failWith("API error", err);
|
|
3711
3982
|
}
|
|
3712
3983
|
});
|
|
3713
|
-
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>",
|
|
3984
|
+
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) => {
|
|
3714
3985
|
let scopes;
|
|
3715
3986
|
try {
|
|
3716
3987
|
scopes = parseScopes(options.scopes);
|
|
@@ -3725,7 +3996,7 @@ function createKeysCommand() {
|
|
|
3725
3996
|
{
|
|
3726
3997
|
name,
|
|
3727
3998
|
scopes,
|
|
3728
|
-
expiresInDays:
|
|
3999
|
+
expiresInDays: parseExpiresInDays(options.expiresInDays)
|
|
3729
4000
|
},
|
|
3730
4001
|
{ idempotencyKey: (0, import_crypto3.randomUUID)() }
|
|
3731
4002
|
);
|
|
@@ -3760,7 +4031,7 @@ function createKeysCommand() {
|
|
|
3760
4031
|
{
|
|
3761
4032
|
name: `${current2.name} (rotated)`,
|
|
3762
4033
|
scopes: current2.scopes,
|
|
3763
|
-
expiresInDays: replacementExpiryDays(current2.expiresAt)
|
|
4034
|
+
expiresInDays: replacementExpiryDays(current2.expiresAt, /* @__PURE__ */ new Date(), current2.scopes)
|
|
3764
4035
|
},
|
|
3765
4036
|
{ idempotencyKey: (0, import_crypto3.randomUUID)() }
|
|
3766
4037
|
);
|
|
@@ -3771,6 +4042,9 @@ function createKeysCommand() {
|
|
|
3771
4042
|
}
|
|
3772
4043
|
printCreatedKey(created);
|
|
3773
4044
|
console.log();
|
|
4045
|
+
if (!current2.expiresAt && !isReadOnlyScopeSet(current2.scopes)) {
|
|
4046
|
+
warn("The old key never expired. Keys that can write must expire now, so the new key lasts 90 days.");
|
|
4047
|
+
}
|
|
3774
4048
|
warn(`The old key ${current2.prefix} is still active.`);
|
|
3775
4049
|
info(`Switch your integrations to the new key, then revoke the old one at ${KEYS_PORTAL_URL}`);
|
|
3776
4050
|
} catch (err) {
|
|
@@ -3862,7 +4136,7 @@ function createConfigCommand() {
|
|
|
3862
4136
|
currentConfig.clear();
|
|
3863
4137
|
clearCredentials();
|
|
3864
4138
|
success("Configuration reset to defaults");
|
|
3865
|
-
info(
|
|
4139
|
+
info(`You will need to run "${cliInvocation()} auth login" again`);
|
|
3866
4140
|
});
|
|
3867
4141
|
config2.command("env").description("Show environment information").action(() => {
|
|
3868
4142
|
header("Environment");
|
|
@@ -3888,7 +4162,7 @@ function createConfigCommand() {
|
|
|
3888
4162
|
info("No profiles configured");
|
|
3889
4163
|
info("Profiles allow you to switch between different accounts/environments");
|
|
3890
4164
|
console.log();
|
|
3891
|
-
info(
|
|
4165
|
+
info(`Example: ${cliInvocation()} config set-profile production --url https://portal.delegancebrokerage.com`);
|
|
3892
4166
|
} else {
|
|
3893
4167
|
for (const [name, profile] of Object.entries(profiles)) {
|
|
3894
4168
|
const isActive = name === activeProfile;
|
|
@@ -3904,7 +4178,7 @@ function createConfigCommand() {
|
|
|
3904
4178
|
const profiles = currentConfig.get("profiles") || {};
|
|
3905
4179
|
if (!profiles[name] && name !== "default") {
|
|
3906
4180
|
error(`Profile "${name}" not found`);
|
|
3907
|
-
info(
|
|
4181
|
+
info(`Create it with: ${cliInvocation()} config set-profile <name> --url <base-url>`);
|
|
3908
4182
|
process.exit(1);
|
|
3909
4183
|
}
|
|
3910
4184
|
currentConfig.set("activeProfile", name);
|
|
@@ -3938,7 +4212,7 @@ var import_inquirer10 = __toESM(require("inquirer"));
|
|
|
3938
4212
|
var import_sdk7 = require("@delegance/sdk");
|
|
3939
4213
|
var TYPES = ["bug", "idea", "question"];
|
|
3940
4214
|
var MESSAGE_MAX = 500;
|
|
3941
|
-
var USAGE =
|
|
4215
|
+
var USAGE = `${cliInvocation()} feedback "what happened" [--type bug|idea|question] [--include-last]`;
|
|
3942
4216
|
function feedbackInputMode(o) {
|
|
3943
4217
|
if (o.message && o.message.trim()) return "given";
|
|
3944
4218
|
return o.isTTY ? "prompt" : "usage_error";
|
|
@@ -3989,10 +4263,40 @@ function createFeedbackCommand() {
|
|
|
3989
4263
|
});
|
|
3990
4264
|
}
|
|
3991
4265
|
|
|
4266
|
+
// src/utils/checkbox-prompt.ts
|
|
4267
|
+
var import_inquirer11 = __toESM(require("inquirer"));
|
|
4268
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
4269
|
+
var CHECKBOX_HINT = "(space to select, Enter to continue)";
|
|
4270
|
+
var registered = false;
|
|
4271
|
+
function minimalCheckboxPrompt(Base) {
|
|
4272
|
+
return class MinimalCheckboxPrompt extends Base {
|
|
4273
|
+
constructor(...args) {
|
|
4274
|
+
super(...args);
|
|
4275
|
+
this.dontShowHints = true;
|
|
4276
|
+
}
|
|
4277
|
+
getQuestion() {
|
|
4278
|
+
const question = super.getQuestion();
|
|
4279
|
+
return this.status === "answered" ? question : `${question}${import_chalk3.default.dim(CHECKBOX_HINT)} `;
|
|
4280
|
+
}
|
|
4281
|
+
onAllKey() {
|
|
4282
|
+
}
|
|
4283
|
+
onInverseKey() {
|
|
4284
|
+
}
|
|
4285
|
+
};
|
|
4286
|
+
}
|
|
4287
|
+
function registerMinimalCheckbox() {
|
|
4288
|
+
if (registered) return;
|
|
4289
|
+
const prompts = import_inquirer11.default.prompt.prompts;
|
|
4290
|
+
const Base = prompts?.checkbox;
|
|
4291
|
+
if (!Base) return;
|
|
4292
|
+
import_inquirer11.default.registerPrompt("checkbox", minimalCheckboxPrompt(Base));
|
|
4293
|
+
registered = true;
|
|
4294
|
+
}
|
|
4295
|
+
|
|
3992
4296
|
// src/commands/commerce.ts
|
|
3993
4297
|
var import_commander18 = require("commander");
|
|
3994
4298
|
var fs7 = __toESM(require("fs"));
|
|
3995
|
-
var
|
|
4299
|
+
var import_inquirer12 = __toESM(require("inquirer"));
|
|
3996
4300
|
var import_ora17 = __toESM(require("ora"));
|
|
3997
4301
|
var import_sdk8 = require("@delegance/sdk");
|
|
3998
4302
|
function formatCents(cents2, currency = "usd") {
|
|
@@ -4020,9 +4324,29 @@ function describeBindResult(r) {
|
|
|
4020
4324
|
lines.push(r.next);
|
|
4021
4325
|
return lines;
|
|
4022
4326
|
}
|
|
4327
|
+
function exactDollars(cents2) {
|
|
4328
|
+
return `$${(cents2 / 100).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
4329
|
+
}
|
|
4330
|
+
function describeFinancing(f) {
|
|
4331
|
+
if (!f || !f.available) return [];
|
|
4332
|
+
const lines = [];
|
|
4333
|
+
if (f.checkoutUrl) {
|
|
4334
|
+
lines.push(`Pay over time with ${f.lender}: ${f.checkoutUrl}`);
|
|
4335
|
+
} else {
|
|
4336
|
+
lines.push(`Financing with ${f.lender} opens once your broker confirms the final premium.`);
|
|
4337
|
+
}
|
|
4338
|
+
const t = f.terms;
|
|
4339
|
+
if (t && t.aprBps !== null && t.downPaymentCents !== null && t.installmentCents !== null && t.numberOfInstallments !== null) {
|
|
4340
|
+
lines.push(
|
|
4341
|
+
`Lender terms: ${(t.aprBps / 100).toFixed(2)}% APR, ${exactDollars(t.downPaymentCents)} down, ${t.numberOfInstallments} payments of ${exactDollars(t.installmentCents)}.`
|
|
4342
|
+
);
|
|
4343
|
+
}
|
|
4344
|
+
lines.push(f.disclosure);
|
|
4345
|
+
return lines;
|
|
4346
|
+
}
|
|
4023
4347
|
async function askCode() {
|
|
4024
4348
|
if (!process.stdin.isTTY) return null;
|
|
4025
|
-
const answer = await
|
|
4349
|
+
const answer = await import_inquirer12.default.prompt([{
|
|
4026
4350
|
type: "input",
|
|
4027
4351
|
name: "code",
|
|
4028
4352
|
message: "Code from your email:",
|
|
@@ -4035,7 +4359,7 @@ function createBindCommand() {
|
|
|
4035
4359
|
const client = getClient();
|
|
4036
4360
|
let attest = options.attest === true;
|
|
4037
4361
|
if (!attest && process.stdin.isTTY && !options.json) {
|
|
4038
|
-
const a = await
|
|
4362
|
+
const a = await import_inquirer12.default.prompt([{
|
|
4039
4363
|
type: "confirm",
|
|
4040
4364
|
name: "ok",
|
|
4041
4365
|
message: "Are the answers on this application accurate? Binding relies on them.",
|
|
@@ -4043,7 +4367,7 @@ function createBindCommand() {
|
|
|
4043
4367
|
}]);
|
|
4044
4368
|
attest = a.ok === true;
|
|
4045
4369
|
if (!attest) {
|
|
4046
|
-
warn(
|
|
4370
|
+
warn(`Not bound. Fix the answers with "${cliInvocation()} quote start", then bind again.`);
|
|
4047
4371
|
return;
|
|
4048
4372
|
}
|
|
4049
4373
|
}
|
|
@@ -4059,7 +4383,7 @@ function createBindCommand() {
|
|
|
4059
4383
|
info(`We emailed a 6-digit code to ${r.sentTo}. It expires in 10 minutes.`);
|
|
4060
4384
|
const code = options.code ? String(options.code) : await askCode();
|
|
4061
4385
|
if (!code) {
|
|
4062
|
-
warn(`Finish with:
|
|
4386
|
+
warn(`Finish with: ${cliInvocation()} bind ${submissionId} ${offerId} --attest --step-up ${stepUpId} --code <code>`);
|
|
4063
4387
|
return;
|
|
4064
4388
|
}
|
|
4065
4389
|
r = await client.instantQuotes.bindWithStepUp(submissionId, offerId, { ...attest ? { attest: true } : {}, stepUpId, code });
|
|
@@ -4073,9 +4397,10 @@ function createBindCommand() {
|
|
|
4073
4397
|
return;
|
|
4074
4398
|
}
|
|
4075
4399
|
if (r.existing) warn(`A bind for this coverage is already open (${r.carrierName}). Showing it.`);
|
|
4076
|
-
else if (r.tier !== "A") success(`Bind requested: ${r.carrierName} ${r.coverage}${r.premium === null ? "" : ` at ${formatCurrency(r.premium)}/yr`}`);
|
|
4400
|
+
else if (r.tier !== "A") success(`Bind requested: ${r.carrierName} ${coverageName(r.coverage)}${r.premium === null ? "" : ` at ${formatCurrency(r.premium)}/yr`}`);
|
|
4077
4401
|
keyValue({ "Bind request": r.bindRequestId, Status: r.status });
|
|
4078
4402
|
describeBindResult(r).forEach((l) => info(l));
|
|
4403
|
+
describeFinancing(r.financing).forEach((l) => info(l));
|
|
4079
4404
|
} catch (err) {
|
|
4080
4405
|
spinner?.stop();
|
|
4081
4406
|
if (err instanceof import_sdk8.DeleganceError && [400, 403, 409].includes(err.statusCode ?? 0)) {
|
|
@@ -4116,7 +4441,7 @@ function createBillingCommand() {
|
|
|
4116
4441
|
payments.map((p) => [p.id, formatCents(p.amount, p.currency), p.status, p.dueDate ? formatDate(p.dueDate) : "-", p.description ?? "-"])
|
|
4117
4442
|
);
|
|
4118
4443
|
keyValue({ "Open": `${formatCents(sum.openCents)} (${sum.openCount})`, "Paid": formatCents(sum.paidCents) });
|
|
4119
|
-
if (sum.openCount > 0) info(
|
|
4444
|
+
if (sum.openCount > 0) info(`Pay one with: ${cliInvocation()} pay <paymentId>`);
|
|
4120
4445
|
} catch (err) {
|
|
4121
4446
|
failWith("API error", err);
|
|
4122
4447
|
}
|
|
@@ -4141,7 +4466,7 @@ function createPayCommand() {
|
|
|
4141
4466
|
payments.map((p) => [p.id, formatCents(p.amount, p.currency), p.description ?? "", p.payable ? `${p.status} (payable)` : p.status, p.dueDate ? formatDate(p.dueDate) : "-"])
|
|
4142
4467
|
);
|
|
4143
4468
|
const next = payments.find((p) => p.payable);
|
|
4144
|
-
if (next) info(`Pay it:
|
|
4469
|
+
if (next) info(`Pay it: ${cliInvocation()} pay ${next.id}`);
|
|
4145
4470
|
return;
|
|
4146
4471
|
}
|
|
4147
4472
|
const { checkoutUrl } = await client.payments.checkout(paymentId);
|
|
@@ -4171,14 +4496,14 @@ function bondStartLines(r, ctx = {}) {
|
|
|
4171
4496
|
out.push(` ${str(q.key)}: ${str(q.label)}${q.required ? " (required)" : ""}${opts}`);
|
|
4172
4497
|
});
|
|
4173
4498
|
}
|
|
4174
|
-
out.push(`Answer with:
|
|
4499
|
+
out.push(`Answer with: ${cliInvocation()} bond answer ${str(r.session_id)} --answers answers.json`);
|
|
4175
4500
|
return out;
|
|
4176
4501
|
}
|
|
4177
4502
|
if (Array.isArray(r.candidates) && r.candidates.length) {
|
|
4178
4503
|
out.push("That name fits more than one bond. Rerun with the exact name:");
|
|
4179
4504
|
const rawState = (ctx.state ?? "").trim();
|
|
4180
4505
|
const state = !rawState ? null : /^[A-Za-z]{2}$/.test(rawState) ? rawState.toUpperCase() : shellQuote(rawState);
|
|
4181
|
-
r.candidates.forEach((name) => out.push(state ? `
|
|
4506
|
+
r.candidates.forEach((name) => out.push(state ? ` ${cliInvocation()} bond quote --state ${state} --bond ${shellQuote(str(name))}` : ` --bond ${shellQuote(str(name))}`));
|
|
4182
4507
|
return out;
|
|
4183
4508
|
}
|
|
4184
4509
|
if (r.needsAmount) {
|
|
@@ -4233,7 +4558,7 @@ function createBondCommand() {
|
|
|
4233
4558
|
if (options.json) json(r);
|
|
4234
4559
|
else {
|
|
4235
4560
|
console.log(JSON.stringify(r, null, 2));
|
|
4236
|
-
info(`Check it:
|
|
4561
|
+
info(`Check it: ${cliInvocation()} bond status ${sessionId}`);
|
|
4237
4562
|
}
|
|
4238
4563
|
} catch (err) {
|
|
4239
4564
|
failWith("Could not submit the answers", err);
|
|
@@ -4310,6 +4635,7 @@ function createClaimCommand() {
|
|
|
4310
4635
|
|
|
4311
4636
|
// src/program.ts
|
|
4312
4637
|
function createProgram() {
|
|
4638
|
+
registerMinimalCheckbox();
|
|
4313
4639
|
const program = new import_commander19.Command();
|
|
4314
4640
|
program.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", () => {
|
|
4315
4641
|
migrateLegacyConfigKey();
|