@delegance/cli 0.2.1 → 0.3.0
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 +169 -64
- package/dist/bin/delegance.js +702 -217
- package/dist/index.d.ts +17 -3
- package/dist/index.js +701 -214
- package/package.json +12 -6
package/dist/bin/delegance.js
CHANGED
|
@@ -24,20 +24,20 @@ 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
|
-
var
|
|
30
|
+
var import_commander20 = require("commander");
|
|
31
31
|
|
|
32
32
|
// src/version.ts
|
|
33
|
-
var CLI_VERSION = "0.
|
|
33
|
+
var CLI_VERSION = "0.3.0";
|
|
34
34
|
|
|
35
35
|
// src/commands/auth.ts
|
|
36
36
|
var os4 = __toESM(require("os"));
|
|
37
|
-
var
|
|
38
|
-
var
|
|
37
|
+
var import_commander3 = require("commander");
|
|
38
|
+
var import_inquirer3 = __toESM(require("inquirer"));
|
|
39
39
|
var import_ora2 = __toESM(require("ora"));
|
|
40
|
-
var
|
|
40
|
+
var import_sdk4 = require("@delegance/sdk");
|
|
41
41
|
|
|
42
42
|
// src/utils/config.ts
|
|
43
43
|
var import_conf = __toESM(require("conf"));
|
|
@@ -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." };
|
|
@@ -465,10 +502,10 @@ function openInBrowser(url, platform = process.platform) {
|
|
|
465
502
|
|
|
466
503
|
// src/commands/signup.ts
|
|
467
504
|
var import_crypto = require("crypto");
|
|
468
|
-
var
|
|
469
|
-
var
|
|
505
|
+
var import_commander2 = require("commander");
|
|
506
|
+
var import_inquirer2 = __toESM(require("inquirer"));
|
|
470
507
|
var import_ora = __toESM(require("ora"));
|
|
471
|
-
var
|
|
508
|
+
var import_sdk3 = require("@delegance/sdk");
|
|
472
509
|
|
|
473
510
|
// src/utils/pending-signup.ts
|
|
474
511
|
var fs3 = __toESM(require("fs"));
|
|
@@ -509,6 +546,82 @@ function clearPendingSignup(home) {
|
|
|
509
546
|
}
|
|
510
547
|
}
|
|
511
548
|
|
|
549
|
+
// src/commands/terms.ts
|
|
550
|
+
var import_commander = require("commander");
|
|
551
|
+
var import_inquirer = __toESM(require("inquirer"));
|
|
552
|
+
var import_sdk2 = require("@delegance/sdk");
|
|
553
|
+
function termsAcceptRefusal(prompted) {
|
|
554
|
+
if (!prompted) {
|
|
555
|
+
return { kind: "error", message: `No prompt to answer. Read the Terms, then run "${cliCommand("terms accept --yes")}".` };
|
|
556
|
+
}
|
|
557
|
+
return { kind: "warn", message: "Not accepted. You can still quote; binding needs the current Terms." };
|
|
558
|
+
}
|
|
559
|
+
function termsLines(t) {
|
|
560
|
+
return [
|
|
561
|
+
`Delegance Terms of Service (version ${t.version}): ${t.termsUrl}`,
|
|
562
|
+
`Privacy Policy: ${t.privacyUrl}`,
|
|
563
|
+
...t.summaries
|
|
564
|
+
];
|
|
565
|
+
}
|
|
566
|
+
async function confirmTerms(t, opts) {
|
|
567
|
+
termsLines(t).forEach((l) => (opts.print ?? info)(l));
|
|
568
|
+
if (opts.flag) return true;
|
|
569
|
+
if (!opts.interactive) return false;
|
|
570
|
+
const ask = opts.ask ?? (async () => {
|
|
571
|
+
const a = await import_inquirer.default.prompt([{
|
|
572
|
+
type: "confirm",
|
|
573
|
+
name: "ok",
|
|
574
|
+
message: "Do you accept the Terms of Service and Privacy Policy?",
|
|
575
|
+
default: false
|
|
576
|
+
}]);
|
|
577
|
+
return a.ok === true;
|
|
578
|
+
});
|
|
579
|
+
return ask();
|
|
580
|
+
}
|
|
581
|
+
function createTermsCommand() {
|
|
582
|
+
const cmd = new import_commander.Command("terms").description("Show the Delegance Terms of Service, or accept the current version").option("--json", "Output as JSON").action(async (options) => {
|
|
583
|
+
try {
|
|
584
|
+
const t = await (0, import_sdk2.createSignup)({ baseUrl: getBaseUrl() }).getTerms();
|
|
585
|
+
if (options.json) {
|
|
586
|
+
json(t);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
termsLines(t).forEach((l) => info(l));
|
|
590
|
+
info(`Broker review before you bind: ${t.brokerReview.command}, or email ${t.brokerReview.email}`);
|
|
591
|
+
} catch (err) {
|
|
592
|
+
failWith("Could not load the Terms", err);
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
cmd.command("accept").description("Accept the current Terms of Service for your account (a bind can require it)").option("-y, --yes", "Accept without the prompt (you have read the Terms)").option("--json", "Output as JSON").action(async (options) => {
|
|
596
|
+
const client = getClient();
|
|
597
|
+
try {
|
|
598
|
+
const t = await client.terms.get();
|
|
599
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !options.json;
|
|
600
|
+
const ok = await confirmTerms(t, {
|
|
601
|
+
flag: options.yes === true,
|
|
602
|
+
interactive,
|
|
603
|
+
// --json keeps stdout to one JSON document; the Terms still print, on stderr.
|
|
604
|
+
...options.json ? { print: (l) => console.error(l) } : {}
|
|
605
|
+
});
|
|
606
|
+
if (!ok) {
|
|
607
|
+
const refusal = termsAcceptRefusal(interactive);
|
|
608
|
+
if (refusal.kind === "error") error(refusal.message);
|
|
609
|
+
else warn(refusal.message);
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
const r = await client.terms.accept(t.version);
|
|
613
|
+
if (options.json) {
|
|
614
|
+
json(r);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
success(`Accepted the Terms of Service, version ${r.version}.`);
|
|
618
|
+
} catch (err) {
|
|
619
|
+
failWith("Could not record your acceptance", err);
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
return cmd;
|
|
623
|
+
}
|
|
624
|
+
|
|
512
625
|
// src/commands/signup.ts
|
|
513
626
|
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
514
627
|
var COMPANY_SIZES = ["0", "1", "2-10", "11-25", "26-50", "51-100", "100-499", "500+"];
|
|
@@ -533,34 +646,72 @@ function addressProblem(a) {
|
|
|
533
646
|
if (!/^\d{5}(-\d{4})?$/.test(a.zip.trim())) return "ZIP must be 5 digits (or ZIP+4).";
|
|
534
647
|
return null;
|
|
535
648
|
}
|
|
649
|
+
var TERMS_REASONS = /* @__PURE__ */ new Set(["terms_not_accepted", "terms_version_outdated"]);
|
|
650
|
+
function isTermsRefusal(err) {
|
|
651
|
+
return err instanceof import_sdk3.DeleganceError && TERMS_REASONS.has(String(err.reason ?? ""));
|
|
652
|
+
}
|
|
653
|
+
async function loadSignupTerms(get) {
|
|
654
|
+
try {
|
|
655
|
+
return await get();
|
|
656
|
+
} catch (err) {
|
|
657
|
+
if (err instanceof import_sdk3.DeleganceError && err.statusCode === 404) return null;
|
|
658
|
+
throw err;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
async function loadSignupTermsOrExit(baseUrl) {
|
|
662
|
+
try {
|
|
663
|
+
return await loadSignupTerms(() => (0, import_sdk3.createSignup)({ baseUrl }).getTerms());
|
|
664
|
+
} catch (err) {
|
|
665
|
+
failWith("Could not load the Terms of Service", err);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function nonInteractiveMissing(options, terms) {
|
|
669
|
+
const missing = missingSignupFields(options);
|
|
670
|
+
if (terms && options.acceptTerms !== true) missing.push("--accept-terms");
|
|
671
|
+
return missing;
|
|
672
|
+
}
|
|
673
|
+
async function acceptTermsOrExit(baseUrl, flag, preloaded) {
|
|
674
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
675
|
+
const terms = preloaded === void 0 ? await loadSignupTermsOrExit(baseUrl) : preloaded;
|
|
676
|
+
if (!terms) return "";
|
|
677
|
+
const ok = await confirmTerms(terms, { flag, interactive });
|
|
678
|
+
if (!ok) {
|
|
679
|
+
if (interactive) warn("Not signed up. Delegance needs your acceptance of the Terms and Privacy Policy to open an account.");
|
|
680
|
+
else error("No terminal to ask in. Read the Terms and Privacy Policy above, then pass --accept-terms.");
|
|
681
|
+
process.exit(1);
|
|
682
|
+
}
|
|
683
|
+
return terms.version;
|
|
684
|
+
}
|
|
536
685
|
function exchangeRefusal(err) {
|
|
537
|
-
if (!(err instanceof
|
|
686
|
+
if (!(err instanceof import_sdk3.DeleganceError)) return null;
|
|
538
687
|
switch (err.reason) {
|
|
539
688
|
case "account_exists":
|
|
540
689
|
return `This email already has a Delegance account, so signup did not attach it to a new one. Sign in and create a key at ${PORTAL_URL}/portal/settings/api-keys`;
|
|
541
690
|
case "already_collected":
|
|
542
691
|
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
692
|
case "expired":
|
|
544
|
-
return
|
|
693
|
+
return `This signup expired (24 hours). Run "${cliInvocation()} signup" again.`;
|
|
545
694
|
default:
|
|
546
695
|
return null;
|
|
547
696
|
}
|
|
548
697
|
}
|
|
549
698
|
async function ensureEnrolled(pending) {
|
|
550
699
|
if (pending.enrollmentId) return pending;
|
|
551
|
-
const signup = (0,
|
|
700
|
+
const signup = (0, import_sdk3.createSignup)({ baseUrl: pending.baseUrl });
|
|
552
701
|
const hash = (0, import_crypto.createHash)("sha256").update(pending.deviceSecret).digest("hex");
|
|
553
702
|
const enrollment = await signup.enroll(pending.bootstrapKey, {
|
|
554
703
|
deviceSecretHash: hash,
|
|
555
704
|
firstName: pending.firstName ?? "",
|
|
556
|
-
lastName: pending.lastName ?? ""
|
|
705
|
+
lastName: pending.lastName ?? "",
|
|
706
|
+
termsAccepted: true,
|
|
707
|
+
termsVersion: pending.termsVersion ?? ""
|
|
557
708
|
});
|
|
558
709
|
const next = { ...pending, enrollmentId: enrollment.enrollmentId, verificationSent: false };
|
|
559
710
|
savePendingSignup(next);
|
|
560
711
|
return sendVerification(next);
|
|
561
712
|
}
|
|
562
713
|
async function sendVerification(pending) {
|
|
563
|
-
await (0,
|
|
714
|
+
await (0, import_sdk3.createSignup)({ baseUrl: pending.baseUrl }).requestVerification(pending.bootstrapKey);
|
|
564
715
|
const next = { ...pending, verificationSent: true };
|
|
565
716
|
savePendingSignup(next);
|
|
566
717
|
return next;
|
|
@@ -571,14 +722,14 @@ function storeOrPrintKey(apiKey, store = storeApiKey) {
|
|
|
571
722
|
return true;
|
|
572
723
|
} catch (err) {
|
|
573
724
|
error(`Could not save the key to ~/.delegance/credentials: ${err.message}`);
|
|
574
|
-
warn(
|
|
725
|
+
warn(`Your key is below. It is shown once. Save it now, then run "${cliInvocation()} auth login --key <key>".`);
|
|
575
726
|
console.log(apiKey);
|
|
576
727
|
return false;
|
|
577
728
|
}
|
|
578
729
|
}
|
|
579
730
|
async function waitAndStore(pending, timeoutMs) {
|
|
580
|
-
const signup = (0,
|
|
581
|
-
const spinner = (0, import_ora.default)(`Waiting for you to click the link sent to ${pending.email} (Ctrl-C is safe: resume with "
|
|
731
|
+
const signup = (0, import_sdk3.createSignup)({ baseUrl: pending.baseUrl });
|
|
732
|
+
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
733
|
const started = Date.now();
|
|
583
734
|
try {
|
|
584
735
|
const result = await signup.waitForKey(pending.enrollmentId, pending.deviceSecret, {
|
|
@@ -590,7 +741,7 @@ async function waitAndStore(pending, timeoutMs) {
|
|
|
590
741
|
});
|
|
591
742
|
if (result.status !== "ready") {
|
|
592
743
|
spinner.warn("Still waiting on the email confirmation.");
|
|
593
|
-
info(
|
|
744
|
+
info(`Run "${cliInvocation()} verify" after you click the link.`);
|
|
594
745
|
return;
|
|
595
746
|
}
|
|
596
747
|
spinner.stop();
|
|
@@ -598,7 +749,7 @@ async function waitAndStore(pending, timeoutMs) {
|
|
|
598
749
|
setConfig("defaultCompanyId", result.companyId);
|
|
599
750
|
clearPendingSignup();
|
|
600
751
|
if (stored) success(`Verified. API key ${maskApiKey(result.apiKey)} saved to ~/.delegance/credentials`);
|
|
601
|
-
success(
|
|
752
|
+
success(`You can quote now: ${cliInvocation()} quote start --coverage gl`);
|
|
602
753
|
} catch (err) {
|
|
603
754
|
spinner.stop();
|
|
604
755
|
const why = exchangeRefusal(err);
|
|
@@ -628,21 +779,24 @@ function signupPrompts(options, interactive) {
|
|
|
628
779
|
return all.filter((q) => q.when !== false).map(({ when: _when, ...q }) => q);
|
|
629
780
|
}
|
|
630
781
|
function createSignupCommand() {
|
|
631
|
-
return new
|
|
782
|
+
return new import_commander2.Command("signup").description("Create a Delegance account and get an API key, from the terminal").option("-e, --email <email>", "Your work email (the confirm link goes here)").option("-b, --business <name>", "Business name").option("--first <name>", "Your first name").option("--last <name>", "Your last name").option("-i, --industry <industry>", 'What the business does, e.g. "commercial bakery"').option("--size <size>", `Employees: ${COMPANY_SIZES.join(", ")}`).option("-w, --website <url>", "Website (full URL)").option("--street <street>", "Business street address").option("--city <city>", "City").option("--state <state>", "State, 2-letter code (carriers quote by state)").option("--zip <zip>", "ZIP code").option("-d, --description <text>", "A sentence on what the business does (helps carriers classify it)").option("--accept-terms", "Accept the Terms of Service and Privacy Policy (shown before the account is created)").option("--timeout <minutes>", "How long to wait for the email click", "15").option("--no-wait", 'Send the email and exit; collect later with "delegance verify"').action(async (options) => {
|
|
632
783
|
const existing = loadPendingSignup();
|
|
633
784
|
if (existing) {
|
|
634
785
|
warn(`A signup for ${existing.email} is already waiting for its email confirmation.`);
|
|
635
|
-
info(
|
|
786
|
+
info(`Run "${cliInvocation()} verify" to finish it, or "${cliInvocation()} verify --cancel" to start over.`);
|
|
636
787
|
process.exit(1);
|
|
637
788
|
}
|
|
789
|
+
const baseUrl = getBaseUrl();
|
|
790
|
+
let preloadedTerms;
|
|
638
791
|
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
639
|
-
|
|
792
|
+
preloadedTerms = await loadSignupTermsOrExit(baseUrl);
|
|
793
|
+
const missing = nonInteractiveMissing(options, preloadedTerms);
|
|
640
794
|
if (missing.length) {
|
|
641
795
|
error(`No terminal to ask in. Missing: ${missing.join(" ")}`);
|
|
642
796
|
process.exit(1);
|
|
643
797
|
}
|
|
644
798
|
}
|
|
645
|
-
const answers = await
|
|
799
|
+
const answers = await import_inquirer2.default.prompt(signupPrompts(options, Boolean(process.stdin.isTTY && process.stdout.isTTY)));
|
|
646
800
|
const address = {
|
|
647
801
|
street: String(options.street ?? answers.street).trim(),
|
|
648
802
|
city: String(options.city ?? answers.city).trim(),
|
|
@@ -665,8 +819,8 @@ function createSignupCommand() {
|
|
|
665
819
|
error(`--size must be one of: ${COMPANY_SIZES.join(", ")}`);
|
|
666
820
|
process.exit(1);
|
|
667
821
|
}
|
|
668
|
-
const
|
|
669
|
-
const signup = (0,
|
|
822
|
+
const termsVersion = await acceptTermsOrExit(baseUrl, options.acceptTerms === true, preloadedTerms);
|
|
823
|
+
const signup = (0, import_sdk3.createSignup)({ baseUrl });
|
|
670
824
|
const spinner = (0, import_ora.default)("Creating your account...").start();
|
|
671
825
|
try {
|
|
672
826
|
const account = await signup.createAccount({
|
|
@@ -678,7 +832,7 @@ function createSignupCommand() {
|
|
|
678
832
|
...description ? { description } : {},
|
|
679
833
|
...options.website ? { website: options.website } : {}
|
|
680
834
|
});
|
|
681
|
-
const device = (0,
|
|
835
|
+
const device = (0, import_sdk3.newDeviceSecret)();
|
|
682
836
|
let pending = {
|
|
683
837
|
enrollmentId: "",
|
|
684
838
|
deviceSecret: device.secret,
|
|
@@ -687,6 +841,7 @@ function createSignupCommand() {
|
|
|
687
841
|
email,
|
|
688
842
|
firstName: String(options.first ?? answers.first).trim(),
|
|
689
843
|
lastName: String(options.last ?? answers.last).trim(),
|
|
844
|
+
termsVersion,
|
|
690
845
|
baseUrl,
|
|
691
846
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
692
847
|
};
|
|
@@ -697,7 +852,7 @@ function createSignupCommand() {
|
|
|
697
852
|
warn("We could not save your business address. Add it in the portal before quoting, or carriers cannot price you.");
|
|
698
853
|
}
|
|
699
854
|
if (options.wait === false) {
|
|
700
|
-
info(
|
|
855
|
+
info(`After you click it, run "${cliInvocation()} verify" to collect your API key.`);
|
|
701
856
|
return;
|
|
702
857
|
}
|
|
703
858
|
await waitAndStore(pending, Number(options.timeout) * 6e4 || 15 * 6e4);
|
|
@@ -708,17 +863,17 @@ function createSignupCommand() {
|
|
|
708
863
|
});
|
|
709
864
|
}
|
|
710
865
|
function resendFailureMessage(err) {
|
|
711
|
-
if (err instanceof
|
|
866
|
+
if (err instanceof import_sdk3.DeleganceError && (err.statusCode === 401 || err.statusCode === 403)) {
|
|
712
867
|
return "Your email is already confirmed, so there is nothing to re-send. Collecting your key now.";
|
|
713
868
|
}
|
|
714
869
|
const why = err instanceof Error ? err.message : String(err);
|
|
715
870
|
return `Could not re-send the email (${why}). Still waiting for the link you already have.`;
|
|
716
871
|
}
|
|
717
872
|
function createVerifyCommand() {
|
|
718
|
-
return new
|
|
873
|
+
return new import_commander2.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("--accept-terms", "Accept the Terms of Service, if this signup has not recorded an acceptance yet").option("--timeout <minutes>", "How long to wait", "15").action(async (options) => {
|
|
719
874
|
const pending = loadPendingSignup();
|
|
720
875
|
if (!pending) {
|
|
721
|
-
info(
|
|
876
|
+
info(`No signup is waiting on this machine. Start one with "${cliInvocation()} signup".`);
|
|
722
877
|
return;
|
|
723
878
|
}
|
|
724
879
|
if (options.cancel) {
|
|
@@ -735,8 +890,21 @@ function createVerifyCommand() {
|
|
|
735
890
|
failWith("Could not send the confirm email", err);
|
|
736
891
|
}
|
|
737
892
|
} else if (!pending.enrollmentId) {
|
|
893
|
+
let toEnroll = pending;
|
|
894
|
+
if (!pending.termsVersion) {
|
|
895
|
+
toEnroll = { ...pending, termsVersion: await acceptTermsOrExit(pending.baseUrl, options.acceptTerms === true) };
|
|
896
|
+
savePendingSignup(toEnroll);
|
|
897
|
+
}
|
|
738
898
|
try {
|
|
739
|
-
|
|
899
|
+
try {
|
|
900
|
+
resumed = await ensureEnrolled(toEnroll);
|
|
901
|
+
} catch (err) {
|
|
902
|
+
if (!isTermsRefusal(err)) throw err;
|
|
903
|
+
warn("The Terms of Service changed since this signup started. Review them again.");
|
|
904
|
+
toEnroll = { ...toEnroll, termsVersion: await acceptTermsOrExit(pending.baseUrl, options.acceptTerms === true) };
|
|
905
|
+
savePendingSignup(toEnroll);
|
|
906
|
+
resumed = await ensureEnrolled(toEnroll);
|
|
907
|
+
}
|
|
740
908
|
success(`Finished setting up the signup. We emailed a confirm link to ${pending.email}.`);
|
|
741
909
|
} catch (err) {
|
|
742
910
|
failWith("Could not finish the signup", err);
|
|
@@ -744,7 +912,7 @@ function createVerifyCommand() {
|
|
|
744
912
|
}
|
|
745
913
|
if (options.resend && resumed === pending) {
|
|
746
914
|
try {
|
|
747
|
-
await (0,
|
|
915
|
+
await (0, import_sdk3.createSignup)({ baseUrl: pending.baseUrl }).requestVerification(pending.bootstrapKey);
|
|
748
916
|
success(`Sent the confirm link to ${pending.email} again.`);
|
|
749
917
|
} catch (err) {
|
|
750
918
|
info(resendFailureMessage(err));
|
|
@@ -775,27 +943,27 @@ function describeExpiry(expiresAt, now = /* @__PURE__ */ new Date()) {
|
|
|
775
943
|
return `${date} (in ${days} day${days === 1 ? "" : "s"})`;
|
|
776
944
|
}
|
|
777
945
|
function deviceLoginRefusal(err) {
|
|
778
|
-
if (!(err instanceof
|
|
946
|
+
if (!(err instanceof import_sdk4.DeleganceError)) return null;
|
|
779
947
|
switch (err.reason) {
|
|
780
948
|
case "access_denied":
|
|
781
949
|
return "The request was denied in the portal. Nothing was stored.";
|
|
782
950
|
case "expired":
|
|
783
|
-
return
|
|
951
|
+
return `The code expired before it was approved. Run "${cliInvocation()} auth login" again.`;
|
|
784
952
|
case "already_collected":
|
|
785
|
-
return
|
|
953
|
+
return `This login was already used. Run "${cliInvocation()} auth login" again.`;
|
|
786
954
|
default:
|
|
787
955
|
return null;
|
|
788
956
|
}
|
|
789
957
|
}
|
|
790
958
|
function notAvailable(err) {
|
|
791
|
-
return err instanceof
|
|
959
|
+
return err instanceof import_sdk4.DeleganceError && (err.statusCode === 404 || err.reason === "not_available");
|
|
792
960
|
}
|
|
793
961
|
function pasteFallbackHint() {
|
|
794
|
-
info(`Create a key at ${PORTAL_URL}/portal/settings/api-keys, then run "
|
|
962
|
+
info(`Create a key at ${PORTAL_URL}/portal/settings/api-keys, then run "${cliInvocation()} auth login --key <key>".`);
|
|
795
963
|
}
|
|
796
964
|
async function readKeyInfo(apiKey, timeoutMs = 5e3) {
|
|
797
965
|
try {
|
|
798
|
-
const client = new
|
|
966
|
+
const client = new import_sdk4.DeleganceClient({ apiKey, baseUrl: getBaseUrl(), timeout: timeoutMs, retries: 0 });
|
|
799
967
|
return await client.auth.keyInfo();
|
|
800
968
|
} catch {
|
|
801
969
|
return null;
|
|
@@ -816,7 +984,7 @@ async function loginWithKey(apiKey, skipVerify) {
|
|
|
816
984
|
}
|
|
817
985
|
const spinner = (0, import_ora2.default)("Verifying API key...").start();
|
|
818
986
|
try {
|
|
819
|
-
const client = new
|
|
987
|
+
const client = new import_sdk4.DeleganceClient({ apiKey, baseUrl: getBaseUrl() });
|
|
820
988
|
const isValid = await client.verifyApiKey();
|
|
821
989
|
if (!isValid) {
|
|
822
990
|
spinner.fail("API key verification failed");
|
|
@@ -837,8 +1005,8 @@ async function loginWithKey(apiKey, skipVerify) {
|
|
|
837
1005
|
}
|
|
838
1006
|
}
|
|
839
1007
|
async function loginWithBrowser(openBrowser) {
|
|
840
|
-
const login = (0,
|
|
841
|
-
const device = (0,
|
|
1008
|
+
const login = (0, import_sdk4.createDeviceLogin)({ baseUrl: getBaseUrl() });
|
|
1009
|
+
const device = (0, import_sdk4.newDeviceSecret)();
|
|
842
1010
|
let start;
|
|
843
1011
|
try {
|
|
844
1012
|
start = await login.start({ deviceSecretHash: device.hash, deviceName: defaultDeviceName() });
|
|
@@ -868,7 +1036,7 @@ async function loginWithBrowser(openBrowser) {
|
|
|
868
1036
|
});
|
|
869
1037
|
if (result.status !== "ready") {
|
|
870
1038
|
spinner.fail("The code expired before it was approved.");
|
|
871
|
-
info(
|
|
1039
|
+
info(`Run "${cliInvocation()} auth login" again.`);
|
|
872
1040
|
process.exit(1);
|
|
873
1041
|
}
|
|
874
1042
|
spinner.stop();
|
|
@@ -892,7 +1060,7 @@ async function loginWithBrowser(openBrowser) {
|
|
|
892
1060
|
}
|
|
893
1061
|
}
|
|
894
1062
|
function createAuthCommand() {
|
|
895
|
-
const auth = new
|
|
1063
|
+
const auth = new import_commander3.Command("auth").description("Manage authentication");
|
|
896
1064
|
auth.command("login").description("Sign in: approve this terminal in the portal, or store an API key").option("-k, --key <apiKey>", "Store this API key instead of signing in with the browser").option("--paste", "Prompt for an API key instead of signing in with the browser").option("--no-browser", "Print the approval link without opening a browser").option("--skip-verify", "With --key or --paste: store without verification (useful when API is not reachable)").action(async (options) => {
|
|
897
1065
|
try {
|
|
898
1066
|
if (options.key) {
|
|
@@ -900,7 +1068,7 @@ function createAuthCommand() {
|
|
|
900
1068
|
return;
|
|
901
1069
|
}
|
|
902
1070
|
if (options.paste) {
|
|
903
|
-
const answers = await
|
|
1071
|
+
const answers = await import_inquirer3.default.prompt([
|
|
904
1072
|
{
|
|
905
1073
|
type: "password",
|
|
906
1074
|
name: "apiKey",
|
|
@@ -925,7 +1093,7 @@ function createAuthCommand() {
|
|
|
925
1093
|
header("Authentication Status");
|
|
926
1094
|
if (!isAuthenticated()) {
|
|
927
1095
|
warn("Not authenticated");
|
|
928
|
-
info(
|
|
1096
|
+
info(`Run "${cliInvocation()} auth login" to sign in, or "${cliInvocation()} signup" to create an account`);
|
|
929
1097
|
return;
|
|
930
1098
|
}
|
|
931
1099
|
const apiKey = getApiKey() || "";
|
|
@@ -935,20 +1103,20 @@ function createAuthCommand() {
|
|
|
935
1103
|
let origin = null;
|
|
936
1104
|
let refused = null;
|
|
937
1105
|
try {
|
|
938
|
-
const client = new
|
|
1106
|
+
const client = new import_sdk4.DeleganceClient({ apiKey, baseUrl: getBaseUrl(), timeout: 5e3, retries: 0 });
|
|
939
1107
|
const keyInfo = await client.auth.keyInfo();
|
|
940
1108
|
origin = keyInfo.origin;
|
|
941
1109
|
expiryLine = describeExpiry(keyInfo.expiresAt);
|
|
942
1110
|
if (!fromEnv) setStoredKeyExpiry(keyInfo.expiresAt);
|
|
943
1111
|
} catch (err) {
|
|
944
|
-
if (err instanceof
|
|
1112
|
+
if (err instanceof import_sdk4.DeleganceError && err.statusCode === 401) {
|
|
945
1113
|
refused = err.reason === "key_expired" ? "Your API key expired" : err.reason === "key_membership_revoked" ? "Your API key was revoked: its user is no longer an active member of the company" : "Your API key was rejected by the server";
|
|
946
1114
|
expiryLine = err.reason === "key_expired" ? "Expired" : "Not valid";
|
|
947
1115
|
}
|
|
948
1116
|
}
|
|
949
1117
|
if (refused) {
|
|
950
1118
|
warn(refused);
|
|
951
|
-
info(
|
|
1119
|
+
info(`Run "${cliInvocation()} auth login" to sign in again.`);
|
|
952
1120
|
} else {
|
|
953
1121
|
success("Authenticated");
|
|
954
1122
|
}
|
|
@@ -966,18 +1134,18 @@ function createAuthCommand() {
|
|
|
966
1134
|
const apiKey = getApiKey();
|
|
967
1135
|
if (!apiKey) {
|
|
968
1136
|
error("Not authenticated");
|
|
969
|
-
info(
|
|
1137
|
+
info(`Run "${cliInvocation()} auth login" to sign in`);
|
|
970
1138
|
process.exit(1);
|
|
971
1139
|
}
|
|
972
1140
|
const spinner = (0, import_ora2.default)("Refreshing your key...").start();
|
|
973
1141
|
try {
|
|
974
|
-
const client = new
|
|
1142
|
+
const client = new import_sdk4.DeleganceClient({ apiKey, baseUrl: getBaseUrl() });
|
|
975
1143
|
const { expiresAt } = await client.auth.refresh();
|
|
976
1144
|
if (!process.env.DELEGANCE_API_KEY) setStoredKeyExpiry(expiresAt);
|
|
977
1145
|
spinner.succeed(`Key refreshed. Expires ${describeExpiry(expiresAt)}.`);
|
|
978
1146
|
} catch (err) {
|
|
979
1147
|
spinner.stop();
|
|
980
|
-
if (err instanceof
|
|
1148
|
+
if (err instanceof import_sdk4.DeleganceError && err.reason === "not_refreshable") {
|
|
981
1149
|
info("This key came from the portal and keeps the expiry it was created with. Nothing changed.");
|
|
982
1150
|
return;
|
|
983
1151
|
}
|
|
@@ -994,7 +1162,7 @@ function createAuthCommand() {
|
|
|
994
1162
|
return;
|
|
995
1163
|
}
|
|
996
1164
|
if (!options.force) {
|
|
997
|
-
const { confirm } = await
|
|
1165
|
+
const { confirm } = await import_inquirer3.default.prompt([
|
|
998
1166
|
{
|
|
999
1167
|
type: "confirm",
|
|
1000
1168
|
name: "confirm",
|
|
@@ -1014,7 +1182,7 @@ function createAuthCommand() {
|
|
|
1014
1182
|
auth.command("whoami").description("Show current authenticated identity").action(async () => {
|
|
1015
1183
|
if (!isAuthenticated()) {
|
|
1016
1184
|
error("Not authenticated");
|
|
1017
|
-
info(
|
|
1185
|
+
info(`Run "${cliInvocation()} auth login" to authenticate`);
|
|
1018
1186
|
process.exit(1);
|
|
1019
1187
|
}
|
|
1020
1188
|
const apiKey = getApiKey();
|
|
@@ -1024,7 +1192,7 @@ function createAuthCommand() {
|
|
|
1024
1192
|
}
|
|
1025
1193
|
const spinner = (0, import_ora2.default)("Fetching account info...").start();
|
|
1026
1194
|
try {
|
|
1027
|
-
const client = new
|
|
1195
|
+
const client = new import_sdk4.DeleganceClient({
|
|
1028
1196
|
apiKey,
|
|
1029
1197
|
baseUrl: getBaseUrl()
|
|
1030
1198
|
});
|
|
@@ -1052,11 +1220,11 @@ function createAuthCommand() {
|
|
|
1052
1220
|
}
|
|
1053
1221
|
|
|
1054
1222
|
// src/commands/company.ts
|
|
1055
|
-
var
|
|
1056
|
-
var
|
|
1223
|
+
var import_commander4 = require("commander");
|
|
1224
|
+
var import_inquirer4 = __toESM(require("inquirer"));
|
|
1057
1225
|
var import_ora3 = __toESM(require("ora"));
|
|
1058
1226
|
function createCompanyCommand() {
|
|
1059
|
-
const company = new
|
|
1227
|
+
const company = new import_commander4.Command("company").alias("companies").description("Manage companies");
|
|
1060
1228
|
company.command("list").description("List companies").option("--include-created", "Include companies created via this API key").option("--json", "Output as JSON").action(async (options) => {
|
|
1061
1229
|
const client = getClient();
|
|
1062
1230
|
const spinner = (0, import_ora3.default)("Fetching companies...").start();
|
|
@@ -1092,7 +1260,7 @@ function createCompanyCommand() {
|
|
|
1092
1260
|
company.command("create").description("Create a new company").option("-n, --name <name>", "Company name").option("-i, --industry <industry>", "Industry").option("-s, --size <size>", "Company size (1, 2-10, 11-25, 26-50, 51-100, 100-499, 500+)").option("-e, --email <email>", "Contact email").option("--fein <fein>", "FEIN (XX-XXXXXXX)").option("--website <url>", "Website URL").option("--external-id <id>", "External ID for deduplication").option("--interactive", "Interactive mode with prompts").option("--json", "Output as JSON").action(async (options) => {
|
|
1093
1261
|
let companyData;
|
|
1094
1262
|
if (options.interactive || !options.name && !options.industry) {
|
|
1095
|
-
const answers = await
|
|
1263
|
+
const answers = await import_inquirer4.default.prompt([
|
|
1096
1264
|
{
|
|
1097
1265
|
type: "input",
|
|
1098
1266
|
name: "name",
|
|
@@ -1210,7 +1378,7 @@ function createCompanyCommand() {
|
|
|
1210
1378
|
const companyId = getConfigValue("defaultCompanyId");
|
|
1211
1379
|
if (!companyId) {
|
|
1212
1380
|
info("No default company set");
|
|
1213
|
-
info(
|
|
1381
|
+
info(`Use "${cliInvocation()} company use <id>" to set one`);
|
|
1214
1382
|
return;
|
|
1215
1383
|
}
|
|
1216
1384
|
const client = getClient();
|
|
@@ -1238,7 +1406,7 @@ function createCompanyCommand() {
|
|
|
1238
1406
|
}
|
|
1239
1407
|
|
|
1240
1408
|
// src/commands/document.ts
|
|
1241
|
-
var
|
|
1409
|
+
var import_commander5 = require("commander");
|
|
1242
1410
|
var import_ora4 = __toESM(require("ora"));
|
|
1243
1411
|
var fs4 = __toESM(require("fs"));
|
|
1244
1412
|
var path4 = __toESM(require("path"));
|
|
@@ -1249,7 +1417,7 @@ function extractionOutcome(status) {
|
|
|
1249
1417
|
return "pending";
|
|
1250
1418
|
}
|
|
1251
1419
|
function createDocumentCommand() {
|
|
1252
|
-
const doc = new
|
|
1420
|
+
const doc = new import_commander5.Command("doc").alias("docs").alias("document").alias("documents").description("Manage documents");
|
|
1253
1421
|
doc.command("upload <files...>").description("Upload one or more documents").option("-c, --company <id>", "Company ID (or use default)").option("-t, --type <type>", "Document type (policy, loss_run, financial_statement, sov, application, certificate, endorsement, bor, other)").option("--external-id <id>", "External ID for reference").option("-w, --wait", "Wait for extraction to complete").option("--timeout <ms>", "Timeout for waiting (default: 120000)", "120000").option("--json", "Output as JSON").action(async (filePatterns, options) => {
|
|
1254
1422
|
const companyId = getCompanyId(options);
|
|
1255
1423
|
const client = getClient();
|
|
@@ -1484,7 +1652,7 @@ function createDocumentCommand() {
|
|
|
1484
1652
|
process.exit(1);
|
|
1485
1653
|
} else {
|
|
1486
1654
|
waitSpinner.warn(`Extraction still ${finished.status}`);
|
|
1487
|
-
if (!options.json) info(`Run "
|
|
1655
|
+
if (!options.json) info(`Run "${cliInvocation()} doc wait ${documentId}" to keep following it`);
|
|
1488
1656
|
}
|
|
1489
1657
|
return;
|
|
1490
1658
|
}
|
|
@@ -1492,7 +1660,7 @@ function createDocumentCommand() {
|
|
|
1492
1660
|
json(result);
|
|
1493
1661
|
} else {
|
|
1494
1662
|
info(`${result.reprocessesRemaining} reprocess attempts left today`);
|
|
1495
|
-
info(`Run "
|
|
1663
|
+
info(`Run "${cliInvocation()} doc wait ${documentId}" to follow it`);
|
|
1496
1664
|
}
|
|
1497
1665
|
} catch (err) {
|
|
1498
1666
|
spinner.fail("Failed to reprocess document");
|
|
@@ -1527,11 +1695,11 @@ function createDocumentCommand() {
|
|
|
1527
1695
|
}
|
|
1528
1696
|
|
|
1529
1697
|
// src/commands/chat.ts
|
|
1530
|
-
var
|
|
1698
|
+
var import_commander6 = require("commander");
|
|
1531
1699
|
var import_ora5 = __toESM(require("ora"));
|
|
1532
|
-
var
|
|
1700
|
+
var import_inquirer5 = __toESM(require("inquirer"));
|
|
1533
1701
|
function createChatCommand() {
|
|
1534
|
-
const chat = new
|
|
1702
|
+
const chat = new import_commander6.Command("ask").alias("chat").description("Ask the Orin insurance assistant a question");
|
|
1535
1703
|
chat.command("query <question...>").alias("q").description("Ask a single question").option("-c, --conversation <id>", "Continue a previous conversation").option("--json", "Output as JSON").action(async (questionParts, options) => {
|
|
1536
1704
|
const question = questionParts.join(" ");
|
|
1537
1705
|
const client = getClient();
|
|
@@ -1567,7 +1735,7 @@ function createChatCommand() {
|
|
|
1567
1735
|
console.log('Type your questions. Enter "exit" or "quit" to end the session.');
|
|
1568
1736
|
console.log();
|
|
1569
1737
|
while (true) {
|
|
1570
|
-
const { question } = await
|
|
1738
|
+
const { question } = await import_inquirer5.default.prompt([
|
|
1571
1739
|
{
|
|
1572
1740
|
type: "input",
|
|
1573
1741
|
name: "question",
|
|
@@ -1627,18 +1795,102 @@ function createChatCommand() {
|
|
|
1627
1795
|
}
|
|
1628
1796
|
|
|
1629
1797
|
// src/commands/quote.ts
|
|
1630
|
-
var
|
|
1798
|
+
var import_commander7 = require("commander");
|
|
1631
1799
|
var import_ora7 = __toESM(require("ora"));
|
|
1632
|
-
var
|
|
1633
|
-
var
|
|
1800
|
+
var import_inquirer7 = __toESM(require("inquirer"));
|
|
1801
|
+
var import_sdk6 = require("@delegance/sdk");
|
|
1634
1802
|
|
|
1635
1803
|
// src/commands/instant-quote.ts
|
|
1636
1804
|
var fs5 = __toESM(require("fs"));
|
|
1637
1805
|
var import_crypto2 = require("crypto");
|
|
1638
1806
|
var import_chalk2 = __toESM(require("chalk"));
|
|
1639
|
-
var
|
|
1807
|
+
var import_inquirer6 = __toESM(require("inquirer"));
|
|
1640
1808
|
var import_ora6 = __toESM(require("ora"));
|
|
1641
|
-
var
|
|
1809
|
+
var import_sdk5 = require("@delegance/sdk");
|
|
1810
|
+
|
|
1811
|
+
// src/utils/coverage-names.ts
|
|
1812
|
+
var NAMES = {
|
|
1813
|
+
// General liability
|
|
1814
|
+
cgl: "General Liability",
|
|
1815
|
+
gl: "General Liability",
|
|
1816
|
+
general_liability: "General Liability",
|
|
1817
|
+
// Workers' comp
|
|
1818
|
+
wc: "Workers' Comp",
|
|
1819
|
+
workers_comp: "Workers' Comp",
|
|
1820
|
+
workers_compensation: "Workers' Comp",
|
|
1821
|
+
// Business owner's policy
|
|
1822
|
+
bop: "Business Owner's Policy",
|
|
1823
|
+
business_owners: "Business Owner's Policy",
|
|
1824
|
+
business_owners_policy: "Business Owner's Policy",
|
|
1825
|
+
// Cyber
|
|
1826
|
+
cyber: "Cyber",
|
|
1827
|
+
cyber_liability: "Cyber",
|
|
1828
|
+
// Professional liability (E&O)
|
|
1829
|
+
mpl: "Professional Liability (E&O)",
|
|
1830
|
+
pl: "Professional Liability (E&O)",
|
|
1831
|
+
pro_liab: "Professional Liability (E&O)",
|
|
1832
|
+
professional_liability: "Professional Liability (E&O)",
|
|
1833
|
+
eo: "Professional Liability (E&O)",
|
|
1834
|
+
e_o: "Professional Liability (E&O)",
|
|
1835
|
+
tech_eo: "Tech E&O",
|
|
1836
|
+
errors_omissions: "Tech E&O",
|
|
1837
|
+
// Auto
|
|
1838
|
+
ca: "Commercial Auto",
|
|
1839
|
+
auto: "Commercial Auto",
|
|
1840
|
+
commercial_auto: "Commercial Auto",
|
|
1841
|
+
// Property
|
|
1842
|
+
cp: "Commercial Property",
|
|
1843
|
+
property: "Commercial Property",
|
|
1844
|
+
commercial_property: "Commercial Property",
|
|
1845
|
+
// Umbrella
|
|
1846
|
+
umbrella: "Umbrella",
|
|
1847
|
+
excess: "Umbrella",
|
|
1848
|
+
commercial_umbrella: "Umbrella",
|
|
1849
|
+
// Builders risk
|
|
1850
|
+
br: "Builders' Risk",
|
|
1851
|
+
builders_risk: "Builders' Risk",
|
|
1852
|
+
// Inland marine and marine
|
|
1853
|
+
im: "Inland Marine",
|
|
1854
|
+
inland_marine: "Inland Marine",
|
|
1855
|
+
marine: "Marine",
|
|
1856
|
+
// Management liability family
|
|
1857
|
+
ml: "Management Liability",
|
|
1858
|
+
management_liability: "Management Liability",
|
|
1859
|
+
do: "Directors and Officers (D&O)",
|
|
1860
|
+
d_o: "Directors and Officers (D&O)",
|
|
1861
|
+
directors_officers: "Directors and Officers (D&O)",
|
|
1862
|
+
epli: "Employment Practices Liability",
|
|
1863
|
+
employment_practices: "Employment Practices Liability",
|
|
1864
|
+
crime: "Crime",
|
|
1865
|
+
fiduciary: "Fiduciary Liability",
|
|
1866
|
+
fiduciary_liability: "Fiduciary Liability",
|
|
1867
|
+
// Everything else on the canonical list
|
|
1868
|
+
product_liability: "Product Liability",
|
|
1869
|
+
liquor_liability: "Liquor Liability",
|
|
1870
|
+
contractors_liability: "Contractors' Liability",
|
|
1871
|
+
equipment_breakdown: "Equipment Breakdown",
|
|
1872
|
+
environmental_liability: "Environmental Liability",
|
|
1873
|
+
healthcare_medical: "Healthcare Medical Liability",
|
|
1874
|
+
life_sciences: "Life Sciences",
|
|
1875
|
+
hospitality_programs: "Hospitality Program",
|
|
1876
|
+
commercial_flood: "Commercial Flood",
|
|
1877
|
+
home_based_business: "Home-Based Business",
|
|
1878
|
+
foreign_package: "Foreign Package",
|
|
1879
|
+
multinational_package: "Multinational Package",
|
|
1880
|
+
political_risk: "Political Risk",
|
|
1881
|
+
trade_credit: "Trade Credit",
|
|
1882
|
+
kidnap_ransom: "Kidnap and Ransom",
|
|
1883
|
+
product_recall: "Product Recall",
|
|
1884
|
+
railroad_liability: "Railroad Liability"
|
|
1885
|
+
};
|
|
1886
|
+
function coverageName(code) {
|
|
1887
|
+
if (code === null || code === void 0) return "";
|
|
1888
|
+
const key = code.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1889
|
+
return NAMES[key] ?? code;
|
|
1890
|
+
}
|
|
1891
|
+
function coverageNames(codes) {
|
|
1892
|
+
return codes.map(coverageName).filter(Boolean).join(", ");
|
|
1893
|
+
}
|
|
1642
1894
|
|
|
1643
1895
|
// src/commerce/questions.ts
|
|
1644
1896
|
var COVERAGE_ALIASES = {
|
|
@@ -1718,14 +1970,23 @@ function validateAnswers(questions, provided) {
|
|
|
1718
1970
|
}
|
|
1719
1971
|
return { answers, problems };
|
|
1720
1972
|
}
|
|
1973
|
+
function rowStatus(q) {
|
|
1974
|
+
if (String(q.state).toUpperCase() === "REFERRED") {
|
|
1975
|
+
return q.resultStatus === "needs_info" ? "needs_info" : "referred";
|
|
1976
|
+
}
|
|
1977
|
+
return q.marketStatus || q.state;
|
|
1978
|
+
}
|
|
1721
1979
|
function sortQuoteRows(quotes) {
|
|
1722
1980
|
return quotes.map((q) => ({
|
|
1723
1981
|
carrier: q.carrierName || q.carrierId,
|
|
1724
1982
|
coverage: q.coverage ?? "",
|
|
1725
1983
|
premium: q.premium,
|
|
1726
|
-
status: q
|
|
1984
|
+
status: rowStatus(q),
|
|
1727
1985
|
bindable: q.bindable === true && q.state === "QUOTED",
|
|
1728
|
-
opportunityId: q.opportunityId
|
|
1986
|
+
opportunityId: q.opportunityId,
|
|
1987
|
+
limits: typeof q.limits === "string" && q.limits.trim() ? q.limits : null,
|
|
1988
|
+
deductible: typeof q.deductible === "number" && Number.isFinite(q.deductible) ? q.deductible : null,
|
|
1989
|
+
message: typeof q.message === "string" && q.message.trim() ? q.message.trim() : null
|
|
1729
1990
|
})).sort((a, b) => {
|
|
1730
1991
|
if (a.bindable !== b.bindable) return a.bindable ? -1 : 1;
|
|
1731
1992
|
if (a.premium === null && b.premium === null) return 0;
|
|
@@ -1734,8 +1995,50 @@ function sortQuoteRows(quotes) {
|
|
|
1734
1995
|
return a.premium - b.premium;
|
|
1735
1996
|
});
|
|
1736
1997
|
}
|
|
1737
|
-
function
|
|
1738
|
-
|
|
1998
|
+
function bestPerCoverage(rows) {
|
|
1999
|
+
const best = /* @__PURE__ */ new Map();
|
|
2000
|
+
for (const r of rows) {
|
|
2001
|
+
if (!r.bindable || r.premium === null) continue;
|
|
2002
|
+
const key = coverageName(r.coverage);
|
|
2003
|
+
const cur = best.get(key);
|
|
2004
|
+
if (!cur || r.premium < cur.premium) best.set(key, r);
|
|
2005
|
+
}
|
|
2006
|
+
return [...best.values()].sort((a, b) => a.premium - b.premium);
|
|
2007
|
+
}
|
|
2008
|
+
function groupRowsByCoverage(rows) {
|
|
2009
|
+
const order = /* @__PURE__ */ new Map();
|
|
2010
|
+
rows.forEach((r) => {
|
|
2011
|
+
const key = coverageName(r.coverage);
|
|
2012
|
+
if (!order.has(key)) order.set(key, order.size);
|
|
2013
|
+
});
|
|
2014
|
+
const rank = (r) => order.get(coverageName(r.coverage));
|
|
2015
|
+
return rows.map((r, i) => ({ r, i })).sort((a, b) => rank(a.r) - rank(b.r) || a.i - b.i).map((x) => x.r);
|
|
2016
|
+
}
|
|
2017
|
+
var STATUS_WORDS = {
|
|
2018
|
+
quote: "Quoted",
|
|
2019
|
+
quoted: "Quoted",
|
|
2020
|
+
declined: "Declined",
|
|
2021
|
+
decline: "Declined",
|
|
2022
|
+
quoting: "Pricing",
|
|
2023
|
+
pending: "Pricing",
|
|
2024
|
+
submitted: "Pricing",
|
|
2025
|
+
in_progress: "Pricing",
|
|
2026
|
+
referred: "Referred to an underwriter",
|
|
2027
|
+
refer: "Referred to an underwriter",
|
|
2028
|
+
needs_info: "Needs more info",
|
|
2029
|
+
awaiting_broker_review: "With a broker",
|
|
2030
|
+
broker_review: "With a broker",
|
|
2031
|
+
bind_requested: "Bind requested",
|
|
2032
|
+
bound: "Bound",
|
|
2033
|
+
expired: "Expired",
|
|
2034
|
+
error: "Failed",
|
|
2035
|
+
failed: "Failed"
|
|
2036
|
+
};
|
|
2037
|
+
function quoteStatusWord(status) {
|
|
2038
|
+
const key = status.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
2039
|
+
if (STATUS_WORDS[key]) return STATUS_WORDS[key];
|
|
2040
|
+
const words = key.replace(/_/g, " ").trim();
|
|
2041
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : status;
|
|
1739
2042
|
}
|
|
1740
2043
|
function summarizeStatus(s) {
|
|
1741
2044
|
const quoted = s.carrierQuotes.filter((q) => q.premium !== null).length;
|
|
@@ -1785,7 +2088,14 @@ function formatKnown(q, value) {
|
|
|
1785
2088
|
}
|
|
1786
2089
|
|
|
1787
2090
|
// src/commerce/demo.ts
|
|
1788
|
-
var DEMO_BUSINESS = "Riverbend Bakery (demo), Austin TX
|
|
2091
|
+
var DEMO_BUSINESS = "Riverbend Bakery (demo), Austin TX";
|
|
2092
|
+
var DEMO_SHORT_LABELS = {
|
|
2093
|
+
annual_revenue: "Annual revenue",
|
|
2094
|
+
employees: "Full-time employees",
|
|
2095
|
+
delivery: "Delivers orders",
|
|
2096
|
+
delivery_vehicles: "Delivery vehicles",
|
|
2097
|
+
claims_5y: "Claims in last 5 years"
|
|
2098
|
+
};
|
|
1789
2099
|
var DEMO_QUESTIONS = [
|
|
1790
2100
|
{ id: "annual_revenue", label: "Annual revenue (USD)", type: "currency", required: true, prefill: 85e4 },
|
|
1791
2101
|
{ id: "employees", label: "Full-time employees", type: "number", required: true, prefill: 6 },
|
|
@@ -1819,6 +2129,7 @@ var DEMO_QUESTIONS = [
|
|
|
1819
2129
|
];
|
|
1820
2130
|
var DEMO_STATUS = {
|
|
1821
2131
|
submissionId: "demo-submission",
|
|
2132
|
+
effectiveDate: "2026-11-01",
|
|
1822
2133
|
coverages: ["general_liability", "workers_comp"],
|
|
1823
2134
|
expiresAt: null,
|
|
1824
2135
|
submitted: true,
|
|
@@ -1826,19 +2137,19 @@ var DEMO_STATUS = {
|
|
|
1826
2137
|
allCarriersResolved: true,
|
|
1827
2138
|
awaitingBrokerReviewCount: 0,
|
|
1828
2139
|
carrierQuotes: [
|
|
1829
|
-
{ opportunityId: "demo-1", carrierId: "CARRIER_A", carrierName: "Carrier A", coverage: "CGL", state: "QUOTED", marketStatus: "quote", premium: 612, quoteNumber: "DEMO-GL-1", bindable: true },
|
|
1830
|
-
{ opportunityId: "demo-2", carrierId: "CARRIER_B", carrierName: "Carrier B", coverage: "CGL", state: "QUOTED", marketStatus: "quote", premium: 748, quoteNumber: "DEMO-GL-2", bindable: true },
|
|
1831
|
-
{ opportunityId: "demo-3", carrierId: "CARRIER_C", carrierName: "Carrier C", coverage: "WC", state: "QUOTED", marketStatus: "quote", premium: 1184, quoteNumber: "DEMO-WC-1", bindable: true },
|
|
1832
|
-
{ opportunityId: "demo-4", carrierId: "CARRIER_D", carrierName: "Carrier D", coverage: "WC", state: "DECLINED", marketStatus: "declined", premium: null, quoteNumber: null, bindable: false }
|
|
2140
|
+
{ 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 },
|
|
2141
|
+
{ 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 },
|
|
2142
|
+
{ 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 },
|
|
2143
|
+
{ 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
2144
|
]
|
|
1834
2145
|
};
|
|
1835
2146
|
|
|
1836
2147
|
// src/commands/instant-quote.ts
|
|
1837
2148
|
function activeRequestLines(err, coverageFlag) {
|
|
1838
|
-
if (!(err instanceof
|
|
2149
|
+
if (!(err instanceof import_sdk5.DeleganceError) || err.statusCode !== 409 || !err.body) return null;
|
|
1839
2150
|
const { body } = err;
|
|
1840
2151
|
const code = body.message;
|
|
1841
|
-
const requote =
|
|
2152
|
+
const requote = `${cliInvocation()} quote start --coverage ${coverageFlag} --replace`;
|
|
1842
2153
|
if (code === "active_request_exists") {
|
|
1843
2154
|
const active = Array.isArray(body.active) ? body.active : [];
|
|
1844
2155
|
const first = active[0];
|
|
@@ -1846,7 +2157,7 @@ function activeRequestLines(err, coverageFlag) {
|
|
|
1846
2157
|
const carriers = Array.isArray(first?.carriers) ? first.carriers.map((c) => `${c.carrierId}${c.state ? ` (${c.state.toLowerCase()})` : ""}`).join(", ") : "";
|
|
1847
2158
|
return [
|
|
1848
2159
|
`Already with carriers${id ? `: ${id}` : ""}${carriers ? `, ${carriers}` : ""}.`,
|
|
1849
|
-
`Check it: ${id ?
|
|
2160
|
+
`Check it: ${id ? `${cliInvocation()} quote status ${id}` : `${cliInvocation()} quote status <id>`} Replace it: ${requote}`
|
|
1850
2161
|
];
|
|
1851
2162
|
}
|
|
1852
2163
|
if (code === "replace_conflict") {
|
|
@@ -1898,7 +2209,7 @@ async function askOne(q, answers, fallback) {
|
|
|
1898
2209
|
}
|
|
1899
2210
|
};
|
|
1900
2211
|
}
|
|
1901
|
-
const { v } = await
|
|
2212
|
+
const { v } = await import_inquirer6.default.prompt([prompt]);
|
|
1902
2213
|
const c = coerceAnswer(q, v);
|
|
1903
2214
|
if (c.ok && c.value !== void 0) answers[q.id] = c.value;
|
|
1904
2215
|
}
|
|
@@ -1909,7 +2220,7 @@ async function askQuestions(questions, answers = {}) {
|
|
|
1909
2220
|
const round = planQuestionRound(questions, answers, handled);
|
|
1910
2221
|
if (round.confirm.length + round.checklist.length + round.ask.length === 0) break;
|
|
1911
2222
|
if (round.confirm.length) {
|
|
1912
|
-
const { keep } = await
|
|
2223
|
+
const { keep } = await import_inquirer6.default.prompt([
|
|
1913
2224
|
{
|
|
1914
2225
|
type: "checkbox",
|
|
1915
2226
|
name: "keep",
|
|
@@ -1938,7 +2249,7 @@ async function askQuestions(questions, answers = {}) {
|
|
|
1938
2249
|
}
|
|
1939
2250
|
}
|
|
1940
2251
|
if (round.checklist.length) {
|
|
1941
|
-
const { yes } = await
|
|
2252
|
+
const { yes } = await import_inquirer6.default.prompt([
|
|
1942
2253
|
{
|
|
1943
2254
|
type: "checkbox",
|
|
1944
2255
|
name: "yes",
|
|
@@ -1949,7 +2260,7 @@ async function askQuestions(questions, answers = {}) {
|
|
|
1949
2260
|
]);
|
|
1950
2261
|
const yn = checklistAnswers(round.checklist, yes);
|
|
1951
2262
|
const yesCount = Object.values(yn).filter((v) => v === "yes").length;
|
|
1952
|
-
const { ok } = await
|
|
2263
|
+
const { ok } = await import_inquirer6.default.prompt([
|
|
1953
2264
|
{ type: "confirm", name: "ok", message: `Yes to ${yesCount}, No to ${round.checklist.length - yesCount}. Correct?`, default: true }
|
|
1954
2265
|
]);
|
|
1955
2266
|
if (ok) {
|
|
@@ -1973,7 +2284,7 @@ function handoffLines(r) {
|
|
|
1973
2284
|
if (!r.alreadyHandedOff && r.unansweredRequired > 0) {
|
|
1974
2285
|
lines.push(`The broker will email you for the ${r.unansweredRequired} answer(s) still missing.`);
|
|
1975
2286
|
}
|
|
1976
|
-
lines.push(`Check it:
|
|
2287
|
+
lines.push(`Check it: ${cliInvocation()} quote status ${r.submissionId}`);
|
|
1977
2288
|
return lines;
|
|
1978
2289
|
}
|
|
1979
2290
|
async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
@@ -1983,7 +2294,7 @@ async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
|
1983
2294
|
try {
|
|
1984
2295
|
plan = await client.instantQuotes.brokerHandoffQuestions(submissionId, instantAnswers);
|
|
1985
2296
|
} catch (err) {
|
|
1986
|
-
if (err instanceof
|
|
2297
|
+
if (err instanceof import_sdk5.DeleganceError && err.statusCode === 503) {
|
|
1987
2298
|
say("A broker can still market it: reply to the confirmation email, or start in the portal.");
|
|
1988
2299
|
return;
|
|
1989
2300
|
}
|
|
@@ -2014,7 +2325,7 @@ async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
|
2014
2325
|
process.exit(1);
|
|
2015
2326
|
}
|
|
2016
2327
|
} else if (!opts.json && isInteractive() && plan.questions.length > 0) {
|
|
2017
|
-
info(`A broker can place ${plan.coverages
|
|
2328
|
+
info(`A broker can place ${coverageNames(plan.coverages)} with a market that quotes by hand. ${plan.questions.length} application question(s) left.`);
|
|
2018
2329
|
info("Press Ctrl-C to stop at any point; what you have answered goes to the broker.");
|
|
2019
2330
|
const onInterrupt = () => {
|
|
2020
2331
|
complete = false;
|
|
@@ -2046,7 +2357,7 @@ async function sendHandoff(submissionId, input, idempotencyKey, asJson) {
|
|
|
2046
2357
|
else handoffLines(result).forEach((l) => info(l));
|
|
2047
2358
|
} catch (err) {
|
|
2048
2359
|
spinner?.fail("Could not reach a broker");
|
|
2049
|
-
console.error(`Try again:
|
|
2360
|
+
console.error(`Try again: ${cliInvocation()} quote handoff ${submissionId}`);
|
|
2050
2361
|
failWith("API error", err);
|
|
2051
2362
|
}
|
|
2052
2363
|
}
|
|
@@ -2057,8 +2368,34 @@ function printFloorDrops(failures) {
|
|
|
2057
2368
|
floors.forEach((f) => console.log(` - ${f.carrierId}: ${f.missing}`));
|
|
2058
2369
|
info("Add it to your business profile in the portal, then run the quote again.");
|
|
2059
2370
|
}
|
|
2371
|
+
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.";
|
|
2372
|
+
var premiumText = (n) => n === null ? "-" : formatCurrency(n);
|
|
2373
|
+
var deductibleText = (n) => n === null ? "-" : n === 0 ? "None" : formatCurrency(n);
|
|
2374
|
+
function bestPriceLines(best) {
|
|
2375
|
+
if (best.length === 0) return [];
|
|
2376
|
+
const names = best.map((r) => coverageName(r.coverage));
|
|
2377
|
+
const nameW = Math.max(...names.map((n) => n.length));
|
|
2378
|
+
const carrierW = Math.max(...best.map((r) => r.carrier.length));
|
|
2379
|
+
return [
|
|
2380
|
+
"Best price per coverage:",
|
|
2381
|
+
...best.map((r, i) => ` ${names[i].padEnd(nameW)} ${r.carrier.padEnd(carrierW)} ${formatCurrency(r.premium)}/yr`)
|
|
2382
|
+
];
|
|
2383
|
+
}
|
|
2384
|
+
function bindHintLines(submissionId, best) {
|
|
2385
|
+
if (best.length === 1) return [`Bind it: ${cliCommand(`bind ${submissionId} ${best[0].opportunityId}`)}`];
|
|
2386
|
+
return best.map((r) => `Bind ${coverageName(r.coverage)}: ${cliCommand(`bind ${submissionId} ${r.opportunityId}`)}`);
|
|
2387
|
+
}
|
|
2388
|
+
function demoClosingLines(best) {
|
|
2389
|
+
const example = best[0] ? ` (e.g. bind ... ${best[0].opportunityId})` : "";
|
|
2390
|
+
return [
|
|
2391
|
+
`Next, on a real quote: ${cliCommand("bind <quote id> <offer>")}${example}`,
|
|
2392
|
+
"DEMO: sample premiums, nothing went to a carrier.",
|
|
2393
|
+
`Get real quotes: ${cliCommand("signup")}`,
|
|
2394
|
+
AGENT_TIP
|
|
2395
|
+
];
|
|
2396
|
+
}
|
|
2060
2397
|
function printQuoteTable(status, opts = {}) {
|
|
2061
|
-
const rows = sortQuoteRows(status.carrierQuotes);
|
|
2398
|
+
const rows = groupRowsByCoverage(sortQuoteRows(status.carrierQuotes));
|
|
2062
2399
|
if (rows.length === 0) {
|
|
2063
2400
|
if (status.brokerReview) {
|
|
2064
2401
|
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 +2405,67 @@ function printQuoteTable(status, opts = {}) {
|
|
|
2068
2405
|
if (status.awaitingBrokerReviewCount > 0) info(`${status.awaitingBrokerReviewCount} quote(s) are with a broker for review.`);
|
|
2069
2406
|
return;
|
|
2070
2407
|
}
|
|
2071
|
-
const best =
|
|
2072
|
-
|
|
2073
|
-
|
|
2408
|
+
const best = bestPerCoverage(rows);
|
|
2409
|
+
const bestIds = new Set(best.map((r) => r.opportunityId));
|
|
2410
|
+
const effectiveDate = status.effectiveDate;
|
|
2411
|
+
console.log();
|
|
2412
|
+
if (effectiveDate && opts.showStartDate !== false) {
|
|
2413
|
+
console.log(`Coverage starts ${formatDateOnly(effectiveDate)}`);
|
|
2414
|
+
}
|
|
2415
|
+
const bestLines = bestPriceLines(best);
|
|
2416
|
+
if (bestLines.length) {
|
|
2417
|
+
console.log(import_chalk2.default.bold(bestLines[0]));
|
|
2418
|
+
bestLines.slice(1).forEach((l) => console.log(l));
|
|
2419
|
+
console.log();
|
|
2074
2420
|
}
|
|
2075
2421
|
table(
|
|
2076
|
-
["Carrier", "Coverage", "Premium/yr", "
|
|
2422
|
+
["Carrier", "Coverage", "Premium/yr", "Limits", "Deductible", "Status", "Offer"],
|
|
2077
2423
|
rows.map((r) => {
|
|
2078
|
-
const cells = [
|
|
2079
|
-
|
|
2080
|
-
|
|
2424
|
+
const cells = [
|
|
2425
|
+
r.carrier,
|
|
2426
|
+
coverageName(r.coverage),
|
|
2427
|
+
premiumText(r.premium),
|
|
2428
|
+
r.limits ?? "-",
|
|
2429
|
+
deductibleText(r.deductible),
|
|
2430
|
+
quoteStatusWord(r.status),
|
|
2431
|
+
r.opportunityId
|
|
2432
|
+
];
|
|
2433
|
+
return bestIds.has(r.opportunityId) ? cells.map((c) => import_chalk2.default.bold(c)) : cells;
|
|
2434
|
+
}),
|
|
2435
|
+
{ compact: true }
|
|
2081
2436
|
);
|
|
2082
|
-
|
|
2083
|
-
if (
|
|
2084
|
-
console.log(
|
|
2437
|
+
const reasons = rows.filter((r) => r.message);
|
|
2438
|
+
if (reasons.length) {
|
|
2439
|
+
console.log();
|
|
2440
|
+
reasons.forEach((r) => console.log(`${r.carrier} (${quoteStatusWord(r.status)}): ${r.message}`));
|
|
2085
2441
|
}
|
|
2086
|
-
|
|
2442
|
+
console.log();
|
|
2443
|
+
console.log(import_chalk2.default.dim(`${summarizeStatus(status)}.`));
|
|
2444
|
+
if (opts.demo) {
|
|
2445
|
+
const [next, notice, signup, tip] = demoClosingLines(best);
|
|
2446
|
+
console.log(import_chalk2.default.green(next));
|
|
2447
|
+
console.log(import_chalk2.default.yellow(notice));
|
|
2448
|
+
console.log(import_chalk2.default.yellow(signup));
|
|
2449
|
+
console.log(import_chalk2.default.dim(tip));
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
bindHintLines(status.submissionId, best).forEach((l) => console.log(import_chalk2.default.green(l)));
|
|
2453
|
+
}
|
|
2454
|
+
function demoAnswerLines(answers, effectiveDate) {
|
|
2455
|
+
const pairs = [];
|
|
2456
|
+
for (const q of DEMO_QUESTIONS) {
|
|
2457
|
+
if (answers[q.id] === void 0) continue;
|
|
2458
|
+
const v = answers[q.id];
|
|
2459
|
+
const shown = q.type === "currency" && typeof v === "number" ? formatCurrency(v) : formatKnown(q, v);
|
|
2460
|
+
pairs.push([DEMO_SHORT_LABELS[q.id] ?? q.label, shown]);
|
|
2461
|
+
}
|
|
2462
|
+
if (effectiveDate) pairs.push(["Coverage starts", formatDateOnly(effectiveDate)]);
|
|
2463
|
+
const w = Math.max(...pairs.map(([k]) => k.length));
|
|
2464
|
+
return ["Sample answers (a real quote asks you these):", ...pairs.map(([k, v]) => ` ${k.padEnd(w)} ${v}`)];
|
|
2087
2465
|
}
|
|
2088
2466
|
async function runDemo(asJson, answersFile) {
|
|
2089
2467
|
(asJson ? console.error : console.log)(import_chalk2.default.yellow(`DEMO quote for ${DEMO_BUSINESS}. Nothing leaves this machine.`));
|
|
2468
|
+
let printedStart = false;
|
|
2090
2469
|
if (answersFile) {
|
|
2091
2470
|
let provided;
|
|
2092
2471
|
try {
|
|
@@ -2104,15 +2483,19 @@ async function runDemo(asJson, answersFile) {
|
|
|
2104
2483
|
const mode = answerMode({ demo: true, hasAnswersFile: false, json: asJson, isTTY: isInteractive() });
|
|
2105
2484
|
if (mode === "prompt") await askQuestions(DEMO_QUESTIONS);
|
|
2106
2485
|
else {
|
|
2107
|
-
suggestedAnswers(DEMO_QUESTIONS);
|
|
2108
|
-
if (!asJson)
|
|
2486
|
+
const assumed = suggestedAnswers(DEMO_QUESTIONS);
|
|
2487
|
+
if (!asJson) {
|
|
2488
|
+
demoAnswerLines(assumed, DEMO_STATUS.effectiveDate).forEach((l) => console.log(l));
|
|
2489
|
+
printedStart = true;
|
|
2490
|
+
}
|
|
2109
2491
|
}
|
|
2110
2492
|
}
|
|
2111
|
-
const spinner = asJson ? null : (0, import_ora6.default)("Asking carriers (demo)...")
|
|
2493
|
+
const spinner = asJson ? null : (0, import_ora6.default)("Asking carriers (demo)...");
|
|
2494
|
+
if (spinner && process.stderr.isTTY) spinner.start();
|
|
2112
2495
|
await new Promise((resolve) => setTimeout(resolve, asJson ? 0 : 1200));
|
|
2113
2496
|
spinner?.succeed("4 carriers answered (demo)");
|
|
2114
2497
|
if (asJson) json({ demo: true, ...DEMO_STATUS });
|
|
2115
|
-
else printQuoteTable(DEMO_STATUS, { demo: true });
|
|
2498
|
+
else printQuoteTable(DEMO_STATUS, { demo: true, showStartDate: !printedStart });
|
|
2116
2499
|
}
|
|
2117
2500
|
function addInstantQuoteCommands(quote) {
|
|
2118
2501
|
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) => {
|
|
@@ -2184,7 +2567,7 @@ function addInstantQuoteCommands(quote) {
|
|
|
2184
2567
|
process.exit(1);
|
|
2185
2568
|
}
|
|
2186
2569
|
submitSpinner.fail("Submit failed");
|
|
2187
|
-
console.error(`Before trying again, check whether it went through:
|
|
2570
|
+
console.error(`Before trying again, check whether it went through: ${cliInvocation()} quote status ${session.submissionId}`);
|
|
2188
2571
|
failWith("API error", err);
|
|
2189
2572
|
}
|
|
2190
2573
|
if (submitted.status === "no_carriers") {
|
|
@@ -2200,14 +2583,14 @@ function addInstantQuoteCommands(quote) {
|
|
|
2200
2583
|
}
|
|
2201
2584
|
if (submitted.status === "acord_pending") {
|
|
2202
2585
|
submitSpinner.succeed("Sent to a broker: these coverages need a person, not an instant quote.");
|
|
2203
|
-
info(`Track it with:
|
|
2586
|
+
info(`Track it with: ${cliInvocation()} quote status ${session.submissionId}`);
|
|
2204
2587
|
return;
|
|
2205
2588
|
}
|
|
2206
2589
|
submitSpinner.succeed(`Filed with ${submitted.quotes.filter((q) => q.status === "submitted").length} carrier(s).`);
|
|
2207
2590
|
printFloorDrops(submitted.partialFailures);
|
|
2208
2591
|
if (submitted.replaceNotice && !options.json) info(submitted.replaceNotice);
|
|
2209
2592
|
if (options.wait === false) {
|
|
2210
|
-
info(`Check premiums with:
|
|
2593
|
+
info(`Check premiums with: ${cliInvocation()} quote status ${session.submissionId} --wait`);
|
|
2211
2594
|
return;
|
|
2212
2595
|
}
|
|
2213
2596
|
await waitAndPrint(session.submissionId, Number(options.timeout) * 6e4 || 6e5, !!options.json);
|
|
@@ -2236,11 +2619,11 @@ function addInstantQuoteCommands(quote) {
|
|
|
2236
2619
|
}
|
|
2237
2620
|
if (submitted.status !== "submitted") {
|
|
2238
2621
|
info("Sent to a broker for these coverages.");
|
|
2239
|
-
info(`Check it:
|
|
2622
|
+
info(`Check it: ${cliInvocation()} quote status ${submissionId}`);
|
|
2240
2623
|
return;
|
|
2241
2624
|
}
|
|
2242
2625
|
if (options.wait === false) {
|
|
2243
|
-
info(`Check premiums with:
|
|
2626
|
+
info(`Check premiums with: ${cliInvocation()} quote status ${submissionId} --wait`);
|
|
2244
2627
|
return;
|
|
2245
2628
|
}
|
|
2246
2629
|
await waitAndPrint(submissionId, Number(options.timeout) * 6e4 || 6e5, !!options.json);
|
|
@@ -2251,7 +2634,7 @@ function addInstantQuoteCommands(quote) {
|
|
|
2251
2634
|
guardLines.forEach((l) => options.json ? console.error(l) : info(l));
|
|
2252
2635
|
process.exit(1);
|
|
2253
2636
|
}
|
|
2254
|
-
console.error(`Before trying again, check whether it went through:
|
|
2637
|
+
console.error(`Before trying again, check whether it went through: ${cliInvocation()} quote status ${submissionId}`);
|
|
2255
2638
|
failWith("Submit failed", err);
|
|
2256
2639
|
}
|
|
2257
2640
|
});
|
|
@@ -2287,7 +2670,7 @@ async function waitAndPrint(submissionId, timeoutMs, asJson) {
|
|
|
2287
2670
|
spinner?.stop();
|
|
2288
2671
|
if (asJson) json(status);
|
|
2289
2672
|
else printQuoteTable(status);
|
|
2290
|
-
if (!status.allCarriersResolved && !asJson) info(`Some carriers are still working. Check again:
|
|
2673
|
+
if (!status.allCarriersResolved && !asJson) info(`Some carriers are still working. Check again: ${cliInvocation()} quote status ${submissionId} --wait`);
|
|
2291
2674
|
} catch (err) {
|
|
2292
2675
|
spinner?.fail("Could not read the quote");
|
|
2293
2676
|
failWith("API error", err);
|
|
@@ -2297,7 +2680,7 @@ async function waitAndPrint(submissionId, timeoutMs, asJson) {
|
|
|
2297
2680
|
// src/commands/quote.ts
|
|
2298
2681
|
var QUOTE_SUBMISSION_UNAVAILABLE = "Quote submission via API isn't available yet";
|
|
2299
2682
|
function createQuoteCommand() {
|
|
2300
|
-
const quote = new
|
|
2683
|
+
const quote = new import_commander7.Command("quote").alias("quotes").description("Request and manage insurance quotes");
|
|
2301
2684
|
quote.command("carriers").description("List available carriers for quoting").option("--json", "Output as JSON").action(async (options) => {
|
|
2302
2685
|
const client = getClient();
|
|
2303
2686
|
const spinner = (0, import_ora7.default)("Fetching carriers...").start();
|
|
@@ -2344,7 +2727,7 @@ function createQuoteCommand() {
|
|
|
2344
2727
|
process.exit(1);
|
|
2345
2728
|
}
|
|
2346
2729
|
if (options.interactive || carrierIds.length === 0 && !options.all) {
|
|
2347
|
-
const { selectedCarriers } = await
|
|
2730
|
+
const { selectedCarriers } = await import_inquirer7.default.prompt([
|
|
2348
2731
|
{
|
|
2349
2732
|
type: "checkbox",
|
|
2350
2733
|
name: "selectedCarriers",
|
|
@@ -2400,13 +2783,13 @@ function createQuoteCommand() {
|
|
|
2400
2783
|
}
|
|
2401
2784
|
console.log();
|
|
2402
2785
|
info(`Queue IDs: ${result.queueIds.join(", ")}`);
|
|
2403
|
-
info(
|
|
2786
|
+
info(`Use "${cliInvocation()} quote list" to check status`);
|
|
2404
2787
|
}
|
|
2405
2788
|
} catch (err) {
|
|
2406
2789
|
spinner.fail("Failed to submit quote request");
|
|
2407
|
-
if (err instanceof
|
|
2790
|
+
if (err instanceof import_sdk6.NotImplementedError) {
|
|
2408
2791
|
error(QUOTE_SUBMISSION_UNAVAILABLE);
|
|
2409
|
-
info(
|
|
2792
|
+
info(`For an instant quote from the terminal: ${cliInvocation()} quote start --coverage gl`);
|
|
2410
2793
|
info(`Or start a quote at ${PORTAL_URL}`);
|
|
2411
2794
|
process.exit(1);
|
|
2412
2795
|
}
|
|
@@ -2537,11 +2920,11 @@ function formatQuoteStatus(status) {
|
|
|
2537
2920
|
}
|
|
2538
2921
|
|
|
2539
2922
|
// src/commands/policy.ts
|
|
2540
|
-
var
|
|
2541
|
-
var
|
|
2923
|
+
var import_commander8 = require("commander");
|
|
2924
|
+
var import_inquirer8 = __toESM(require("inquirer"));
|
|
2542
2925
|
var import_ora8 = __toESM(require("ora"));
|
|
2543
2926
|
function createPolicyCommand() {
|
|
2544
|
-
const policy = new
|
|
2927
|
+
const policy = new import_commander8.Command("policy").alias("policies").description("Manage insurance policies");
|
|
2545
2928
|
policy.command("list").alias("ls").description("List all policies").option("-s, --status <status>", "Filter by status (active, expired, all)", "active").option("-t, --type <type>", "Filter by policy type").option("-l, --limit <n>", "Maximum results", "50").option("--json", "Output as JSON").action(async (options) => {
|
|
2546
2929
|
const client = getClient();
|
|
2547
2930
|
const spinner = (0, import_ora8.default)("Fetching policies...").start();
|
|
@@ -2564,7 +2947,7 @@ function createPolicyCommand() {
|
|
|
2564
2947
|
["Carrier", "Type", "Policy #", "Expiry", "Status", "Premium"],
|
|
2565
2948
|
policies.map((p) => [
|
|
2566
2949
|
p.carrier || "Unknown",
|
|
2567
|
-
p.policyType
|
|
2950
|
+
(p.policyType ? coverageName(p.policyType) : p.name) || "-",
|
|
2568
2951
|
p.policyNumber || "-",
|
|
2569
2952
|
p.expiryDate ? formatDate(p.expiryDate) : "-",
|
|
2570
2953
|
formatPolicyStatus(p.isActive, p.daysUntilExpiry),
|
|
@@ -2598,21 +2981,21 @@ function createPolicyCommand() {
|
|
|
2598
2981
|
if (urgent.length > 0) {
|
|
2599
2982
|
warn(`\u26A0\uFE0F Expiring within 7 days (${urgent.length}):`);
|
|
2600
2983
|
for (const p of urgent) {
|
|
2601
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2984
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - expires ${formatDate(p.expiryDate)}`);
|
|
2602
2985
|
}
|
|
2603
2986
|
console.log();
|
|
2604
2987
|
}
|
|
2605
2988
|
if (soon.length > 0) {
|
|
2606
2989
|
info(`\u{1F4C5} Expiring within 30 days (${soon.length}):`);
|
|
2607
2990
|
for (const p of soon) {
|
|
2608
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2991
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - expires ${formatDate(p.expiryDate)}`);
|
|
2609
2992
|
}
|
|
2610
2993
|
console.log();
|
|
2611
2994
|
}
|
|
2612
2995
|
if (later.length > 0) {
|
|
2613
2996
|
console.log(`\u{1F4C6} Expiring later (${later.length}):`);
|
|
2614
2997
|
for (const p of later) {
|
|
2615
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
2998
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - ${p.daysUntilExpiry} days`);
|
|
2616
2999
|
}
|
|
2617
3000
|
}
|
|
2618
3001
|
}
|
|
@@ -2636,11 +3019,11 @@ function createPolicyCommand() {
|
|
|
2636
3019
|
} else {
|
|
2637
3020
|
const { policy: p, coverages } = found;
|
|
2638
3021
|
console.log();
|
|
2639
|
-
header(`${p.carrier} - ${p.policyType
|
|
3022
|
+
header(`${p.carrier} - ${p.policyType ? coverageName(p.policyType) : p.name}`);
|
|
2640
3023
|
console.log();
|
|
2641
3024
|
console.log(` Policy #: ${p.policyNumber || "N/A"}`);
|
|
2642
3025
|
console.log(` Carrier: ${p.carrier}`);
|
|
2643
|
-
console.log(` Type: ${p.policyType
|
|
3026
|
+
console.log(` Type: ${p.policyType ? coverageName(p.policyType) : "N/A"}`);
|
|
2644
3027
|
console.log(` Effective: ${p.effectiveDate ? formatDate(p.effectiveDate) : "N/A"}`);
|
|
2645
3028
|
console.log(` Expiry: ${p.expiryDate ? formatDate(p.expiryDate) : "N/A"}`);
|
|
2646
3029
|
console.log(` Status: ${formatPolicyStatus(p.isActive, p.daysUntilExpiry)}`);
|
|
@@ -2691,14 +3074,14 @@ function createPolicyCommand() {
|
|
|
2691
3074
|
return;
|
|
2692
3075
|
}
|
|
2693
3076
|
if (docs.length === 0) {
|
|
2694
|
-
info(
|
|
3077
|
+
info(`No source documents are linked to this policy yet. Upload one with: ${cliInvocation()} document upload <file>`);
|
|
2695
3078
|
return;
|
|
2696
3079
|
}
|
|
2697
3080
|
table(
|
|
2698
3081
|
["Document", "File", "Type", "Status", "Added"],
|
|
2699
3082
|
docs.map((d) => [d.id, d.fileName ?? "-", d.documentType ?? "-", d.status, d.createdAt ? formatDate(d.createdAt) : "-"])
|
|
2700
3083
|
);
|
|
2701
|
-
info(
|
|
3084
|
+
info(`Download one with: ${cliInvocation()} document status <documentId>`);
|
|
2702
3085
|
} catch (err) {
|
|
2703
3086
|
failWith("API error", err);
|
|
2704
3087
|
}
|
|
@@ -2731,9 +3114,9 @@ function createPolicyCommand() {
|
|
|
2731
3114
|
await signWithStepUp(client, out.request.id, sig, void 0, void 0);
|
|
2732
3115
|
} else {
|
|
2733
3116
|
info(`Sign here: ${out.signatureUrl}`);
|
|
2734
|
-
info(`Or sign from here:
|
|
3117
|
+
info(`Or sign from here: ${cliInvocation()} policy sign ${out.request.id} --signer-name "..." --signer-title "..." --attest`);
|
|
2735
3118
|
}
|
|
2736
|
-
info(`Check progress with "
|
|
3119
|
+
info(`Check progress with "${cliInvocation()} policy cancellation ${out.request.id}"`);
|
|
2737
3120
|
} catch (err) {
|
|
2738
3121
|
spinner.fail("Could not request the cancellation");
|
|
2739
3122
|
failWith("API error", err);
|
|
@@ -2790,10 +3173,10 @@ async function signWithStepUp(client, requestId, sig, stepUpId, presetCode) {
|
|
|
2790
3173
|
let code = presetCode;
|
|
2791
3174
|
if (!code) {
|
|
2792
3175
|
if (!process.stdin.isTTY) {
|
|
2793
|
-
warn(`No terminal to type the code in. Finish with:
|
|
3176
|
+
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
3177
|
return;
|
|
2795
3178
|
}
|
|
2796
|
-
const answer = await
|
|
3179
|
+
const answer = await import_inquirer8.default.prompt([{
|
|
2797
3180
|
type: "input",
|
|
2798
3181
|
name: "code",
|
|
2799
3182
|
message: "Code from your email:",
|
|
@@ -2820,11 +3203,11 @@ function formatPolicyStatus(isActive, daysUntilExpiry) {
|
|
|
2820
3203
|
}
|
|
2821
3204
|
|
|
2822
3205
|
// src/commands/coi.ts
|
|
2823
|
-
var
|
|
3206
|
+
var import_commander9 = require("commander");
|
|
2824
3207
|
var import_ora9 = __toESM(require("ora"));
|
|
2825
|
-
var
|
|
3208
|
+
var import_inquirer9 = __toESM(require("inquirer"));
|
|
2826
3209
|
function createCOICommand() {
|
|
2827
|
-
const coi = new
|
|
3210
|
+
const coi = new import_commander9.Command("coi").description("Generate and manage Certificates of Insurance");
|
|
2828
3211
|
coi.command("generate").alias("create").description("Generate a Certificate of Insurance").option("-p, --policies <ids...>", "Policy IDs to include").option("-n, --holder-name <name>", "Certificate holder name").option("-a, --holder-address <address>", "Certificate holder address").option("--city <city>", "Certificate holder city").option("--state <state>", "Certificate holder state (2-letter)").option("--zip <zip>", "Certificate holder ZIP").option("-t, --type <type>", "Certificate type (additional_insured, certificate_holder)", "certificate_holder").option("-i, --interactive", "Interactive mode").option("--json", "Output as JSON").action(async (options) => {
|
|
2829
3212
|
const client = getClient();
|
|
2830
3213
|
let policyIds = options.policies || [];
|
|
@@ -2840,16 +3223,16 @@ function createCOICommand() {
|
|
|
2840
3223
|
policiesSpinner.stop();
|
|
2841
3224
|
if (policies.length === 0) {
|
|
2842
3225
|
error("No active policies found");
|
|
2843
|
-
info(
|
|
3226
|
+
info(`Upload policy documents first with "${cliInvocation()} doc upload"`);
|
|
2844
3227
|
process.exit(1);
|
|
2845
3228
|
}
|
|
2846
|
-
const answers = await
|
|
3229
|
+
const answers = await import_inquirer9.default.prompt([
|
|
2847
3230
|
{
|
|
2848
3231
|
type: "checkbox",
|
|
2849
3232
|
name: "selectedPolicies",
|
|
2850
3233
|
message: "Select policies to include:",
|
|
2851
3234
|
choices: policies.map((p) => ({
|
|
2852
|
-
name: `${p.carrier} - ${p.policyType
|
|
3235
|
+
name: `${p.carrier} - ${p.policyType ? coverageName(p.policyType) : p.name} (${p.policyNumber || "No #"})`,
|
|
2853
3236
|
value: p.id,
|
|
2854
3237
|
checked: policyIds.includes(p.id)
|
|
2855
3238
|
})),
|
|
@@ -2936,7 +3319,7 @@ function createCOICommand() {
|
|
|
2936
3319
|
console.log();
|
|
2937
3320
|
console.log("Policies included:");
|
|
2938
3321
|
for (const p of result.policiesIncluded) {
|
|
2939
|
-
console.log(` - ${p.carrier} (${p.policyType})`);
|
|
3322
|
+
console.log(` - ${p.carrier} (${coverageName(p.policyType)})`);
|
|
2940
3323
|
}
|
|
2941
3324
|
}
|
|
2942
3325
|
} catch (err) {
|
|
@@ -2952,7 +3335,7 @@ function createCOICommand() {
|
|
|
2952
3335
|
spinner.succeed(`Found ${count} COIs`);
|
|
2953
3336
|
if (cois.length === 0) {
|
|
2954
3337
|
info("No COIs generated yet");
|
|
2955
|
-
info(
|
|
3338
|
+
info(`Generate one with "${cliInvocation()} coi generate --interactive"`);
|
|
2956
3339
|
return;
|
|
2957
3340
|
}
|
|
2958
3341
|
if (options.json) {
|
|
@@ -2979,12 +3362,12 @@ function createCOICommand() {
|
|
|
2979
3362
|
}
|
|
2980
3363
|
|
|
2981
3364
|
// src/commands/sov.ts
|
|
2982
|
-
var
|
|
3365
|
+
var import_commander10 = require("commander");
|
|
2983
3366
|
var import_ora10 = __toESM(require("ora"));
|
|
2984
3367
|
var import_fs = __toESM(require("fs"));
|
|
2985
3368
|
var import_path = __toESM(require("path"));
|
|
2986
3369
|
function createSOVCommand() {
|
|
2987
|
-
const sov = new
|
|
3370
|
+
const sov = new import_commander10.Command("sov").description("Manage Schedules of Values (property schedules)");
|
|
2988
3371
|
sov.command("upload <file>").description("Upload a Schedule of Values file").option("-l, --line <line>", "Line of business (property, inland_marine, etc)", "property").option("--effective <date>", "Effective date (YYYY-MM-DD)").option("--expiration <date>", "Expiration date (YYYY-MM-DD)").option("-w, --wait", "Wait for parsing to complete").option("--json", "Output as JSON").action(async (file, options) => {
|
|
2989
3372
|
const client = getClient();
|
|
2990
3373
|
const filePath = import_path.default.resolve(file);
|
|
@@ -3029,7 +3412,7 @@ function createSOVCommand() {
|
|
|
3029
3412
|
info(`SOV ID: ${result.sovId}`);
|
|
3030
3413
|
info(`Status: ${result.status}`);
|
|
3031
3414
|
if (!options.wait) {
|
|
3032
|
-
info(
|
|
3415
|
+
info(`Use "${cliInvocation()} sov status <id>" to check parsing progress`);
|
|
3033
3416
|
}
|
|
3034
3417
|
}
|
|
3035
3418
|
} catch (err) {
|
|
@@ -3048,7 +3431,7 @@ function createSOVCommand() {
|
|
|
3048
3431
|
spinner.succeed(`Found ${count} SOVs`);
|
|
3049
3432
|
if (sovs.length === 0) {
|
|
3050
3433
|
info("No SOVs found");
|
|
3051
|
-
info(
|
|
3434
|
+
info(`Upload one with "${cliInvocation()} sov upload schedule.xlsx"`);
|
|
3052
3435
|
return;
|
|
3053
3436
|
}
|
|
3054
3437
|
if (options.json) {
|
|
@@ -3142,7 +3525,7 @@ function showSOVDetails(s) {
|
|
|
3142
3525
|
}
|
|
3143
3526
|
|
|
3144
3527
|
// src/commands/appetite.ts
|
|
3145
|
-
var
|
|
3528
|
+
var import_commander11 = require("commander");
|
|
3146
3529
|
var import_ora11 = __toESM(require("ora"));
|
|
3147
3530
|
function num(value) {
|
|
3148
3531
|
if (value === void 0) return void 0;
|
|
@@ -3190,7 +3573,7 @@ function printResult(result) {
|
|
|
3190
3573
|
for (const w of result.warnings) warn(w);
|
|
3191
3574
|
}
|
|
3192
3575
|
function createAppetiteCommand() {
|
|
3193
|
-
const appetite = new
|
|
3576
|
+
const appetite = new import_commander11.Command("appetite").description("Check which carriers will write a risk");
|
|
3194
3577
|
appetite.command("match").description("Match a business to carrier appetite").requiredOption("-s, --state <state>", "Two-letter state code").option("-i, --industry <industry>", 'Industry, e.g. "bakery"').option("-d, --description <text>", "What the business does").option("--naics <code>", "NAICS code").option("--sic <code>", "SIC code").option("-c, --coverage <types>", "Comma-separated coverage types, e.g. general_liability,workers_comp").option("--carriers <names>", "Comma-separated carriers to limit the match to").option("--market-size <size>", '"Small Business" or "Middle Market"').option("--revenue <usd>", "Annual revenue").option("--payroll <usd>", "Annual payroll").option("--property-value <usd>", "Total property value").option("--min-fit <fit>", "Raise the quality floor: possible or strong").option("--top <n>", "Maximum matches (1-50)").option("--no-wait", "Return the job id instead of waiting for a cold match").option("--timeout <ms>", "How long to wait for a cold match", "120000").option("--json", "Output as JSON").action(async (options) => {
|
|
3195
3578
|
const input = buildMatchInput(options);
|
|
3196
3579
|
const timeoutMs = positiveInt(options.timeout, "--timeout");
|
|
@@ -3212,7 +3595,7 @@ function createAppetiteCommand() {
|
|
|
3212
3595
|
if (!options.wait) {
|
|
3213
3596
|
spinner.succeed(`Match queued: ${start.jobId}`);
|
|
3214
3597
|
if (options.json) json(start);
|
|
3215
|
-
else info(`Check it with "
|
|
3598
|
+
else info(`Check it with "${cliInvocation()} appetite status ${start.jobId}"`);
|
|
3216
3599
|
return;
|
|
3217
3600
|
}
|
|
3218
3601
|
spinner.text = `Matching appetite (about ${start.estimatedSeconds}s)...`;
|
|
@@ -3251,7 +3634,7 @@ function createAppetiteCommand() {
|
|
|
3251
3634
|
}
|
|
3252
3635
|
|
|
3253
3636
|
// src/commands/lead.ts
|
|
3254
|
-
var
|
|
3637
|
+
var import_commander12 = require("commander");
|
|
3255
3638
|
var import_ora12 = __toESM(require("ora"));
|
|
3256
3639
|
function buildLeadInput(options) {
|
|
3257
3640
|
const input = {
|
|
@@ -3282,7 +3665,7 @@ function buildLeadInput(options) {
|
|
|
3282
3665
|
return input;
|
|
3283
3666
|
}
|
|
3284
3667
|
function createLeadCommand() {
|
|
3285
|
-
const lead = new
|
|
3668
|
+
const lead = new import_commander12.Command("lead").alias("leads").description("Send prospect businesses to Delegance");
|
|
3286
3669
|
lead.command("create").description("Create a lead (idempotent on --external-id and contact email)").requiredOption("-n, --name <name>", "Business name").requiredOption("-e, --email <email>", "Contact email").option("--legal-name <name>", "Legal name, if different").option("-i, --industry <industry>", "Industry").option("-p, --phone <phone>", "Contact phone").option("-w, --website <url>", "Website (full URL)").option("--size <size>", "Company size").option("-d, --description <text>", "What the business does").option("--naics <code>", "NAICS code").option("--sic <code>", "SIC code").option("-s, --state <state>", "Two-letter state code").option("--street <street>", "Street address").option("--suite <suite>", "Suite").option("--city <city>", "City").option("--address-state <state>", "Address state (defaults to --state)").option("--zip <zip>", "ZIP code").option("--external-id <id>", "Your id for this lead").option("--source <source>", "Lead source label").option("--notes <notes>", "Notes for the broker").option("--json", "Output as JSON").action(async (options) => {
|
|
3287
3670
|
const input = buildLeadInput(options);
|
|
3288
3671
|
if (input.address && (!input.address.street || !input.address.city || !input.address.state || !input.address.zip)) {
|
|
@@ -3305,11 +3688,11 @@ function createLeadCommand() {
|
|
|
3305
3688
|
}
|
|
3306
3689
|
|
|
3307
3690
|
// src/commands/knowledge.ts
|
|
3308
|
-
var
|
|
3691
|
+
var import_commander13 = require("commander");
|
|
3309
3692
|
var import_ora13 = __toESM(require("ora"));
|
|
3310
3693
|
var SEARCH_TYPES = ["notecard", "document_chunk"];
|
|
3311
3694
|
function createKnowledgeCommand() {
|
|
3312
|
-
const knowledge = new
|
|
3695
|
+
const knowledge = new import_commander13.Command("knowledge").alias("kb").description("Search notecards and document facts");
|
|
3313
3696
|
knowledge.command("search <query...>").description("Search notecards and document text").option("-t, --type <type>", `Limit to one result type (${SEARCH_TYPES.join(", ")})`).option("-l, --limit <n>", "Maximum results", "10").option("--json", "Output as JSON").action(async (queryParts, options) => {
|
|
3314
3697
|
const client = getClient();
|
|
3315
3698
|
const query = queryParts.join(" ");
|
|
@@ -3358,7 +3741,7 @@ function createKnowledgeCommand() {
|
|
|
3358
3741
|
);
|
|
3359
3742
|
}
|
|
3360
3743
|
if (pagination.hasMore && pagination.nextCursor) {
|
|
3361
|
-
info(`More:
|
|
3744
|
+
info(`More: ${cliInvocation()} knowledge notecards --cursor ${pagination.nextCursor}`);
|
|
3362
3745
|
}
|
|
3363
3746
|
} catch (err) {
|
|
3364
3747
|
spinner.fail("Failed to list notecards");
|
|
@@ -3411,7 +3794,7 @@ function createKnowledgeCommand() {
|
|
|
3411
3794
|
}
|
|
3412
3795
|
|
|
3413
3796
|
// src/commands/onboarding.ts
|
|
3414
|
-
var
|
|
3797
|
+
var import_commander14 = require("commander");
|
|
3415
3798
|
var import_ora14 = __toESM(require("ora"));
|
|
3416
3799
|
var ONBOARDING_STATES = [
|
|
3417
3800
|
"created",
|
|
@@ -3421,7 +3804,7 @@ var ONBOARDING_STATES = [
|
|
|
3421
3804
|
"ready"
|
|
3422
3805
|
];
|
|
3423
3806
|
function createOnboardingCommand() {
|
|
3424
|
-
const onboarding = new
|
|
3807
|
+
const onboarding = new import_commander14.Command("onboarding").alias("onboard").description("Check account setup and onboarding state");
|
|
3425
3808
|
onboarding.command("status").description("Show the setup checklist and the next step").option("--json", "Output as JSON").action(async (options) => {
|
|
3426
3809
|
const client = getClient();
|
|
3427
3810
|
const spinner = (0, import_ora14.default)("Fetching onboarding status...").start();
|
|
@@ -3482,7 +3865,7 @@ function createOnboardingCommand() {
|
|
|
3482
3865
|
const result = await client.onboarding.requestVerification();
|
|
3483
3866
|
spinner.succeed(`Verification sent to ${result.sentTo}`);
|
|
3484
3867
|
if (options.json) json(result);
|
|
3485
|
-
else info(
|
|
3868
|
+
else info(`Open the link in that email, then check progress with "${cliInvocation()} onboarding state"`);
|
|
3486
3869
|
} catch (err) {
|
|
3487
3870
|
spinner.fail("Failed to request verification");
|
|
3488
3871
|
failWith("API error", err);
|
|
@@ -3512,7 +3895,7 @@ function createOnboardingCommand() {
|
|
|
3512
3895
|
}
|
|
3513
3896
|
|
|
3514
3897
|
// src/commands/usage.ts
|
|
3515
|
-
var
|
|
3898
|
+
var import_commander15 = require("commander");
|
|
3516
3899
|
var import_ora15 = __toESM(require("ora"));
|
|
3517
3900
|
function isoDay(daysAgo, now = /* @__PURE__ */ new Date()) {
|
|
3518
3901
|
const d = new Date(now.getTime() - daysAgo * 24 * 60 * 60 * 1e3);
|
|
@@ -3522,7 +3905,7 @@ function cents(n) {
|
|
|
3522
3905
|
return `$${(n / 100).toFixed(2)}`;
|
|
3523
3906
|
}
|
|
3524
3907
|
function createUsageCommand() {
|
|
3525
|
-
const usage = new
|
|
3908
|
+
const usage = new import_commander15.Command("usage").description("Show API usage, billing, and rate limits");
|
|
3526
3909
|
usage.command("summary").description("Usage for the current billing period").option("--json", "Output as JSON").action(async (options) => {
|
|
3527
3910
|
const client = getClient();
|
|
3528
3911
|
const spinner = (0, import_ora15.default)("Fetching usage summary...").start();
|
|
@@ -3602,21 +3985,30 @@ function createUsageCommand() {
|
|
|
3602
3985
|
}
|
|
3603
3986
|
|
|
3604
3987
|
// src/commands/keys.ts
|
|
3605
|
-
var
|
|
3988
|
+
var import_commander16 = require("commander");
|
|
3606
3989
|
var import_ora16 = __toESM(require("ora"));
|
|
3607
3990
|
var import_crypto3 = require("crypto");
|
|
3608
|
-
var
|
|
3991
|
+
var import_sdk7 = require("@delegance/sdk");
|
|
3609
3992
|
var KEYS_PORTAL_URL = `${PORTAL_URL}/portal/settings/api-keys`;
|
|
3993
|
+
function parseExpiresInDays(raw) {
|
|
3994
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
3995
|
+
if (raw.trim().toLowerCase() === "never") return "never";
|
|
3996
|
+
return parseInt(raw, 10);
|
|
3997
|
+
}
|
|
3610
3998
|
function parseScopes(raw) {
|
|
3611
3999
|
const parts = (Array.isArray(raw) ? raw : [raw ?? "read,write"]).flatMap((p) => p.split(/[\s,]+/)).map((p) => p.trim()).filter(Boolean);
|
|
3612
|
-
const unknown = parts.filter((p) => !
|
|
4000
|
+
const unknown = parts.filter((p) => !import_sdk7.API_KEY_SCOPES.includes(p));
|
|
3613
4001
|
if (unknown.length > 0) {
|
|
3614
|
-
throw new Error(`Unknown scope(s): ${unknown.join(", ")}. Allowed: ${
|
|
4002
|
+
throw new Error(`Unknown scope(s): ${unknown.join(", ")}. Allowed: ${import_sdk7.API_KEY_SCOPES.join(", ")}`);
|
|
3615
4003
|
}
|
|
3616
4004
|
return Array.from(new Set(parts));
|
|
3617
4005
|
}
|
|
3618
|
-
|
|
3619
|
-
|
|
4006
|
+
var EXPIRING_SCOPES = /* @__PURE__ */ new Set(["write", "admin", "claims:write"]);
|
|
4007
|
+
function isReadOnlyScopeSet(scopes) {
|
|
4008
|
+
return !scopes.some((s) => EXPIRING_SCOPES.has(s));
|
|
4009
|
+
}
|
|
4010
|
+
function replacementExpiryDays(expiresAt, now = /* @__PURE__ */ new Date(), scopes = []) {
|
|
4011
|
+
if (!expiresAt) return isReadOnlyScopeSet(scopes) ? "never" : void 0;
|
|
3620
4012
|
const ms = new Date(expiresAt).getTime() - now.getTime();
|
|
3621
4013
|
if (!Number.isFinite(ms)) throw new Error(`Cannot read the old key's expiry "${expiresAt}"; not rotating`);
|
|
3622
4014
|
const days = Math.ceil(ms / (24 * 60 * 60 * 1e3));
|
|
@@ -3636,7 +4028,7 @@ function printCreatedKey(created) {
|
|
|
3636
4028
|
console.log(` ${created.key}`);
|
|
3637
4029
|
}
|
|
3638
4030
|
function createKeysCommand() {
|
|
3639
|
-
const keys = new
|
|
4031
|
+
const keys = new import_commander16.Command("keys").alias("api-keys").description("Manage API keys");
|
|
3640
4032
|
keys.command("list").alias("ls").description("List API keys for your company").option("-l, --limit <n>", "Maximum results", "50").option("--cursor <cursor>", "Page cursor from a previous list").option("--json", "Output as JSON").action(async (options) => {
|
|
3641
4033
|
const client = getClient();
|
|
3642
4034
|
const spinner = (0, import_ora16.default)("Fetching API keys...").start();
|
|
@@ -3665,14 +4057,14 @@ function createKeysCommand() {
|
|
|
3665
4057
|
])
|
|
3666
4058
|
);
|
|
3667
4059
|
if (pagination?.hasMore && pagination.nextCursor) {
|
|
3668
|
-
info(`More keys:
|
|
4060
|
+
info(`More keys: ${cliInvocation()} keys list --cursor ${pagination.nextCursor}`);
|
|
3669
4061
|
}
|
|
3670
4062
|
} catch (err) {
|
|
3671
4063
|
spinner.fail("Failed to list API keys");
|
|
3672
4064
|
failWith("API error", err);
|
|
3673
4065
|
}
|
|
3674
4066
|
});
|
|
3675
|
-
keys.command("create <name>").description("Create an API key (needs a key with the admin scope)").option("-s, --scopes <scopes>", `Comma-separated scopes (${
|
|
4067
|
+
keys.command("create <name>").description("Create an API key (needs a key with the admin scope)").option("-s, --scopes <scopes>", `Comma-separated scopes (${import_sdk7.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
4068
|
let scopes;
|
|
3677
4069
|
try {
|
|
3678
4070
|
scopes = parseScopes(options.scopes);
|
|
@@ -3687,7 +4079,7 @@ function createKeysCommand() {
|
|
|
3687
4079
|
{
|
|
3688
4080
|
name,
|
|
3689
4081
|
scopes,
|
|
3690
|
-
expiresInDays:
|
|
4082
|
+
expiresInDays: parseExpiresInDays(options.expiresInDays)
|
|
3691
4083
|
},
|
|
3692
4084
|
{ idempotencyKey: (0, import_crypto3.randomUUID)() }
|
|
3693
4085
|
);
|
|
@@ -3722,7 +4114,7 @@ function createKeysCommand() {
|
|
|
3722
4114
|
{
|
|
3723
4115
|
name: `${current2.name} (rotated)`,
|
|
3724
4116
|
scopes: current2.scopes,
|
|
3725
|
-
expiresInDays: replacementExpiryDays(current2.expiresAt)
|
|
4117
|
+
expiresInDays: replacementExpiryDays(current2.expiresAt, /* @__PURE__ */ new Date(), current2.scopes)
|
|
3726
4118
|
},
|
|
3727
4119
|
{ idempotencyKey: (0, import_crypto3.randomUUID)() }
|
|
3728
4120
|
);
|
|
@@ -3733,6 +4125,9 @@ function createKeysCommand() {
|
|
|
3733
4125
|
}
|
|
3734
4126
|
printCreatedKey(created);
|
|
3735
4127
|
console.log();
|
|
4128
|
+
if (!current2.expiresAt && !isReadOnlyScopeSet(current2.scopes)) {
|
|
4129
|
+
warn("The old key never expired. Keys that can write must expire now, so the new key lasts 90 days.");
|
|
4130
|
+
}
|
|
3736
4131
|
warn(`The old key ${current2.prefix} is still active.`);
|
|
3737
4132
|
info(`Switch your integrations to the new key, then revoke the old one at ${KEYS_PORTAL_URL}`);
|
|
3738
4133
|
} catch (err) {
|
|
@@ -3748,7 +4143,7 @@ function createKeysCommand() {
|
|
|
3748
4143
|
spinner.succeed("API key revoked");
|
|
3749
4144
|
} catch (err) {
|
|
3750
4145
|
spinner.fail("Failed to revoke API key");
|
|
3751
|
-
if (err instanceof
|
|
4146
|
+
if (err instanceof import_sdk7.AuthenticationError) {
|
|
3752
4147
|
error("Revoking a key needs a signed-in portal session today");
|
|
3753
4148
|
info(`Revoke it at ${KEYS_PORTAL_URL}`);
|
|
3754
4149
|
process.exit(1);
|
|
@@ -3760,10 +4155,10 @@ function createKeysCommand() {
|
|
|
3760
4155
|
}
|
|
3761
4156
|
|
|
3762
4157
|
// src/commands/config.ts
|
|
3763
|
-
var
|
|
3764
|
-
var
|
|
4158
|
+
var import_commander17 = require("commander");
|
|
4159
|
+
var import_inquirer10 = __toESM(require("inquirer"));
|
|
3765
4160
|
function createConfigCommand() {
|
|
3766
|
-
const config2 = new
|
|
4161
|
+
const config2 = new import_commander17.Command("config").description("Manage CLI configuration");
|
|
3767
4162
|
config2.command("show").alias("list").description("Show current configuration").option("--json", "Output as JSON").action((options) => {
|
|
3768
4163
|
const currentConfig = getConfig();
|
|
3769
4164
|
const apiKey = getApiKey();
|
|
@@ -3807,7 +4202,7 @@ function createConfigCommand() {
|
|
|
3807
4202
|
});
|
|
3808
4203
|
config2.command("reset").description("Reset all configuration to defaults").option("-f, --force", "Skip confirmation").action(async (options) => {
|
|
3809
4204
|
if (!options.force) {
|
|
3810
|
-
const { confirm } = await
|
|
4205
|
+
const { confirm } = await import_inquirer10.default.prompt([
|
|
3811
4206
|
{
|
|
3812
4207
|
type: "confirm",
|
|
3813
4208
|
name: "confirm",
|
|
@@ -3824,7 +4219,7 @@ function createConfigCommand() {
|
|
|
3824
4219
|
currentConfig.clear();
|
|
3825
4220
|
clearCredentials();
|
|
3826
4221
|
success("Configuration reset to defaults");
|
|
3827
|
-
info(
|
|
4222
|
+
info(`You will need to run "${cliInvocation()} auth login" again`);
|
|
3828
4223
|
});
|
|
3829
4224
|
config2.command("env").description("Show environment information").action(() => {
|
|
3830
4225
|
header("Environment");
|
|
@@ -3850,7 +4245,7 @@ function createConfigCommand() {
|
|
|
3850
4245
|
info("No profiles configured");
|
|
3851
4246
|
info("Profiles allow you to switch between different accounts/environments");
|
|
3852
4247
|
console.log();
|
|
3853
|
-
info(
|
|
4248
|
+
info(`Example: ${cliInvocation()} config set-profile production --url https://portal.delegancebrokerage.com`);
|
|
3854
4249
|
} else {
|
|
3855
4250
|
for (const [name, profile] of Object.entries(profiles)) {
|
|
3856
4251
|
const isActive = name === activeProfile;
|
|
@@ -3866,7 +4261,7 @@ function createConfigCommand() {
|
|
|
3866
4261
|
const profiles = currentConfig.get("profiles") || {};
|
|
3867
4262
|
if (!profiles[name] && name !== "default") {
|
|
3868
4263
|
error(`Profile "${name}" not found`);
|
|
3869
|
-
info(
|
|
4264
|
+
info(`Create it with: ${cliInvocation()} config set-profile <name> --url <base-url>`);
|
|
3870
4265
|
process.exit(1);
|
|
3871
4266
|
}
|
|
3872
4267
|
currentConfig.set("activeProfile", name);
|
|
@@ -3892,12 +4287,12 @@ function createConfigCommand() {
|
|
|
3892
4287
|
}
|
|
3893
4288
|
|
|
3894
4289
|
// src/commands/feedback.ts
|
|
3895
|
-
var
|
|
3896
|
-
var
|
|
3897
|
-
var
|
|
4290
|
+
var import_commander18 = require("commander");
|
|
4291
|
+
var import_inquirer11 = __toESM(require("inquirer"));
|
|
4292
|
+
var import_sdk8 = require("@delegance/sdk");
|
|
3898
4293
|
var TYPES = ["bug", "idea", "question"];
|
|
3899
4294
|
var MESSAGE_MAX = 500;
|
|
3900
|
-
var USAGE =
|
|
4295
|
+
var USAGE = `${cliInvocation()} feedback "what happened" [--type bug|idea|question] [--include-last]`;
|
|
3901
4296
|
function feedbackInputMode(o) {
|
|
3902
4297
|
if (o.message && o.message.trim()) return "given";
|
|
3903
4298
|
return o.isTTY ? "prompt" : "usage_error";
|
|
@@ -3911,7 +4306,7 @@ function buildFeedbackContext(last) {
|
|
|
3911
4306
|
ctx.at = last.at;
|
|
3912
4307
|
}
|
|
3913
4308
|
ctx.cliVersion = CLI_VERSION;
|
|
3914
|
-
ctx.sdkVersion =
|
|
4309
|
+
ctx.sdkVersion = import_sdk8.SDK_VERSION;
|
|
3915
4310
|
return ctx;
|
|
3916
4311
|
}
|
|
3917
4312
|
function usageError(message) {
|
|
@@ -3920,14 +4315,14 @@ function usageError(message) {
|
|
|
3920
4315
|
process.exit(2);
|
|
3921
4316
|
}
|
|
3922
4317
|
function createFeedbackCommand() {
|
|
3923
|
-
return new
|
|
4318
|
+
return new import_commander18.Command("feedback").alias("bug").description("Report a bug, an idea, or a question to Delegance").argument("[message...]", "What happened (asked for when left out on a terminal)").option("-t, --type <type>", "bug, idea, or question", "bug").option("--include-last", "Attach the last command name, exit code, trace id, and CLI/SDK versions (never its arguments)").option("--json", "Output as JSON").action(async (words, options) => {
|
|
3924
4319
|
const type = String(options.type ?? "bug").toLowerCase();
|
|
3925
4320
|
if (!TYPES.includes(type)) usageError(`--type must be bug, idea, or question, got "${options.type}"`);
|
|
3926
4321
|
let message = (words ?? []).join(" ").trim();
|
|
3927
4322
|
const mode = feedbackInputMode({ message, isTTY: Boolean(process.stdin.isTTY && process.stdout.isTTY) });
|
|
3928
4323
|
if (mode === "usage_error") usageError(`Say what happened: ${USAGE}`);
|
|
3929
4324
|
if (mode === "prompt") {
|
|
3930
|
-
const answer = await
|
|
4325
|
+
const answer = await import_inquirer11.default.prompt([
|
|
3931
4326
|
{ type: "input", name: "message", message: type === "bug" ? "What went wrong?" : "What would you like to tell us?" }
|
|
3932
4327
|
]);
|
|
3933
4328
|
message = String(answer.message ?? "").trim();
|
|
@@ -3948,12 +4343,42 @@ function createFeedbackCommand() {
|
|
|
3948
4343
|
});
|
|
3949
4344
|
}
|
|
3950
4345
|
|
|
4346
|
+
// src/utils/checkbox-prompt.ts
|
|
4347
|
+
var import_inquirer12 = __toESM(require("inquirer"));
|
|
4348
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
4349
|
+
var CHECKBOX_HINT = "(space to select, Enter to continue)";
|
|
4350
|
+
var registered = false;
|
|
4351
|
+
function minimalCheckboxPrompt(Base) {
|
|
4352
|
+
return class MinimalCheckboxPrompt extends Base {
|
|
4353
|
+
constructor(...args) {
|
|
4354
|
+
super(...args);
|
|
4355
|
+
this.dontShowHints = true;
|
|
4356
|
+
}
|
|
4357
|
+
getQuestion() {
|
|
4358
|
+
const question = super.getQuestion();
|
|
4359
|
+
return this.status === "answered" ? question : `${question}${import_chalk3.default.dim(CHECKBOX_HINT)} `;
|
|
4360
|
+
}
|
|
4361
|
+
onAllKey() {
|
|
4362
|
+
}
|
|
4363
|
+
onInverseKey() {
|
|
4364
|
+
}
|
|
4365
|
+
};
|
|
4366
|
+
}
|
|
4367
|
+
function registerMinimalCheckbox() {
|
|
4368
|
+
if (registered) return;
|
|
4369
|
+
const prompts = import_inquirer12.default.prompt.prompts;
|
|
4370
|
+
const Base = prompts?.checkbox;
|
|
4371
|
+
if (!Base) return;
|
|
4372
|
+
import_inquirer12.default.registerPrompt("checkbox", minimalCheckboxPrompt(Base));
|
|
4373
|
+
registered = true;
|
|
4374
|
+
}
|
|
4375
|
+
|
|
3951
4376
|
// src/commands/commerce.ts
|
|
3952
|
-
var
|
|
4377
|
+
var import_commander19 = require("commander");
|
|
3953
4378
|
var fs7 = __toESM(require("fs"));
|
|
3954
|
-
var
|
|
4379
|
+
var import_inquirer13 = __toESM(require("inquirer"));
|
|
3955
4380
|
var import_ora17 = __toESM(require("ora"));
|
|
3956
|
-
var
|
|
4381
|
+
var import_sdk9 = require("@delegance/sdk");
|
|
3957
4382
|
function formatCents(cents2, currency = "usd") {
|
|
3958
4383
|
return currency.toLowerCase() === "usd" ? formatCurrency(cents2 / 100) : `${(cents2 / 100).toFixed(2)} ${currency.toUpperCase()}`;
|
|
3959
4384
|
}
|
|
@@ -3979,9 +4404,58 @@ function describeBindResult(r) {
|
|
|
3979
4404
|
lines.push(r.next);
|
|
3980
4405
|
return lines;
|
|
3981
4406
|
}
|
|
4407
|
+
var BROKER_REVIEW_EMAIL = "support@delegancebrokerage.com";
|
|
4408
|
+
function selfDirectedAckLines(submissionId, offerId) {
|
|
4409
|
+
return [
|
|
4410
|
+
"This is a self-directed placement: Delegance binds the coverage you chose and does not advise on whether it fits your needs (Terms Section 9A).",
|
|
4411
|
+
`Want a licensed broker to review it first? Run: ${cliInvocation()} bind ${submissionId} ${offerId} --broker-review`
|
|
4412
|
+
];
|
|
4413
|
+
}
|
|
4414
|
+
function brokerReviewMailto(submissionId, offerId) {
|
|
4415
|
+
const subject = encodeURIComponent(`Broker review before bind: submission ${submissionId}, offer ${offerId}`);
|
|
4416
|
+
return `mailto:${BROKER_REVIEW_EMAIL}?subject=${subject}`;
|
|
4417
|
+
}
|
|
4418
|
+
function brokerReviewLines(submissionId, offerId) {
|
|
4419
|
+
return [
|
|
4420
|
+
"Nothing was bound, and no review has been requested yet.",
|
|
4421
|
+
`To request a review by a licensed Delegance broker, send an email to ${BROKER_REVIEW_EMAIL}`,
|
|
4422
|
+
`naming submission ${submissionId} and offer ${offerId}: ${brokerReviewMailto(submissionId, offerId)}`,
|
|
4423
|
+
"Wait for the broker to reply before you bind. A reviewed placement is not self-directed."
|
|
4424
|
+
];
|
|
4425
|
+
}
|
|
4426
|
+
function brokerReviewResult(submissionId, offerId) {
|
|
4427
|
+
return {
|
|
4428
|
+
bound: false,
|
|
4429
|
+
reviewRequested: false,
|
|
4430
|
+
submissionId,
|
|
4431
|
+
offerId,
|
|
4432
|
+
brokerReview: { email: BROKER_REVIEW_EMAIL, mailto: brokerReviewMailto(submissionId, offerId) },
|
|
4433
|
+
message: brokerReviewLines(submissionId, offerId)
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
function exactDollars(cents2) {
|
|
4437
|
+
return `$${(cents2 / 100).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
4438
|
+
}
|
|
4439
|
+
function describeFinancing(f) {
|
|
4440
|
+
if (!f || !f.available) return [];
|
|
4441
|
+
const lines = [];
|
|
4442
|
+
if (f.checkoutUrl) {
|
|
4443
|
+
lines.push(`Pay over time with ${f.lender}: ${f.checkoutUrl}`);
|
|
4444
|
+
} else {
|
|
4445
|
+
lines.push(`Financing with ${f.lender} opens once your broker confirms the final premium.`);
|
|
4446
|
+
}
|
|
4447
|
+
const t = f.terms;
|
|
4448
|
+
if (t && t.aprBps !== null && t.downPaymentCents !== null && t.installmentCents !== null && t.numberOfInstallments !== null) {
|
|
4449
|
+
lines.push(
|
|
4450
|
+
`Lender terms: ${(t.aprBps / 100).toFixed(2)}% APR, ${exactDollars(t.downPaymentCents)} down, ${t.numberOfInstallments} payments of ${exactDollars(t.installmentCents)}.`
|
|
4451
|
+
);
|
|
4452
|
+
}
|
|
4453
|
+
lines.push(f.disclosure);
|
|
4454
|
+
return lines;
|
|
4455
|
+
}
|
|
3982
4456
|
async function askCode() {
|
|
3983
4457
|
if (!process.stdin.isTTY) return null;
|
|
3984
|
-
const answer = await
|
|
4458
|
+
const answer = await import_inquirer13.default.prompt([{
|
|
3985
4459
|
type: "input",
|
|
3986
4460
|
name: "code",
|
|
3987
4461
|
message: "Code from your email:",
|
|
@@ -3990,11 +4464,19 @@ async function askCode() {
|
|
|
3990
4464
|
return String(answer.code).trim();
|
|
3991
4465
|
}
|
|
3992
4466
|
function createBindCommand() {
|
|
3993
|
-
return new
|
|
4467
|
+
return new import_commander19.Command("bind").description('Bind one offer from "quote status": the carrier checkout when the carrier offers it, otherwise a broker review').argument("<submissionId>", "The quote submission id").argument("<offerId>", "The offer id from the quote table").option("--attest", "Confirm the application answers are accurate (asked interactively when omitted)").option("--broker-review", "Do not bind; ask a licensed broker to review this coverage first").option("--step-up <id>", "Finish an earlier attempt: the stepUpId the code email belongs to").option("--code <code>", "The 6-digit code from the email").option("--json", "Output as JSON").action(async (submissionId, offerId, options) => {
|
|
4468
|
+
if (options.brokerReview) {
|
|
4469
|
+
if (options.json) json(brokerReviewResult(submissionId, offerId));
|
|
4470
|
+
else brokerReviewLines(submissionId, offerId).forEach((l) => info(l));
|
|
4471
|
+
return;
|
|
4472
|
+
}
|
|
3994
4473
|
const client = getClient();
|
|
4474
|
+
const ack = selfDirectedAckLines(submissionId, offerId);
|
|
4475
|
+
if (options.json) ack.forEach((l) => console.error(l));
|
|
4476
|
+
else ack.forEach((l) => info(l));
|
|
3995
4477
|
let attest = options.attest === true;
|
|
3996
4478
|
if (!attest && process.stdin.isTTY && !options.json) {
|
|
3997
|
-
const a = await
|
|
4479
|
+
const a = await import_inquirer13.default.prompt([{
|
|
3998
4480
|
type: "confirm",
|
|
3999
4481
|
name: "ok",
|
|
4000
4482
|
message: "Are the answers on this application accurate? Binding relies on them.",
|
|
@@ -4002,7 +4484,7 @@ function createBindCommand() {
|
|
|
4002
4484
|
}]);
|
|
4003
4485
|
attest = a.ok === true;
|
|
4004
4486
|
if (!attest) {
|
|
4005
|
-
warn(
|
|
4487
|
+
warn(`Not bound. Fix the answers with "${cliInvocation()} quote start", then bind again.`);
|
|
4006
4488
|
return;
|
|
4007
4489
|
}
|
|
4008
4490
|
}
|
|
@@ -4018,7 +4500,7 @@ function createBindCommand() {
|
|
|
4018
4500
|
info(`We emailed a 6-digit code to ${r.sentTo}. It expires in 10 minutes.`);
|
|
4019
4501
|
const code = options.code ? String(options.code) : await askCode();
|
|
4020
4502
|
if (!code) {
|
|
4021
|
-
warn(`Finish with:
|
|
4503
|
+
warn(`Finish with: ${cliInvocation()} bind ${submissionId} ${offerId} --attest --step-up ${stepUpId} --code <code>`);
|
|
4022
4504
|
return;
|
|
4023
4505
|
}
|
|
4024
4506
|
r = await client.instantQuotes.bindWithStepUp(submissionId, offerId, { ...attest ? { attest: true } : {}, stepUpId, code });
|
|
@@ -4028,16 +4510,17 @@ function createBindCommand() {
|
|
|
4028
4510
|
}
|
|
4029
4511
|
}
|
|
4030
4512
|
if (options.json) {
|
|
4031
|
-
json(r);
|
|
4513
|
+
json({ ...r, selfDirected: true, disclosure: ack[0] });
|
|
4032
4514
|
return;
|
|
4033
4515
|
}
|
|
4034
4516
|
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`}`);
|
|
4517
|
+
else if (r.tier !== "A") success(`Bind requested: ${r.carrierName} ${coverageName(r.coverage)}${r.premium === null ? "" : ` at ${formatCurrency(r.premium)}/yr`}`);
|
|
4036
4518
|
keyValue({ "Bind request": r.bindRequestId, Status: r.status });
|
|
4037
4519
|
describeBindResult(r).forEach((l) => info(l));
|
|
4520
|
+
describeFinancing(r.financing).forEach((l) => info(l));
|
|
4038
4521
|
} catch (err) {
|
|
4039
4522
|
spinner?.stop();
|
|
4040
|
-
if (err instanceof
|
|
4523
|
+
if (err instanceof import_sdk9.DeleganceError && [400, 403, 409].includes(err.statusCode ?? 0)) {
|
|
4041
4524
|
error(err.message);
|
|
4042
4525
|
process.exit(1);
|
|
4043
4526
|
}
|
|
@@ -4058,7 +4541,7 @@ function summarizeBilling(payments) {
|
|
|
4058
4541
|
return { openCents, paidCents, openCount };
|
|
4059
4542
|
}
|
|
4060
4543
|
function createBillingCommand() {
|
|
4061
|
-
return new
|
|
4544
|
+
return new import_commander19.Command("billing").description("What you owe and what you paid: every premium payment request on the account").option("--json", "Output as JSON").action(async (options) => {
|
|
4062
4545
|
try {
|
|
4063
4546
|
const payments = await getClient().payments.list();
|
|
4064
4547
|
const sum = summarizeBilling(payments);
|
|
@@ -4075,14 +4558,14 @@ function createBillingCommand() {
|
|
|
4075
4558
|
payments.map((p) => [p.id, formatCents(p.amount, p.currency), p.status, p.dueDate ? formatDate(p.dueDate) : "-", p.description ?? "-"])
|
|
4076
4559
|
);
|
|
4077
4560
|
keyValue({ "Open": `${formatCents(sum.openCents)} (${sum.openCount})`, "Paid": formatCents(sum.paidCents) });
|
|
4078
|
-
if (sum.openCount > 0) info(
|
|
4561
|
+
if (sum.openCount > 0) info(`Pay one with: ${cliInvocation()} pay <paymentId>`);
|
|
4079
4562
|
} catch (err) {
|
|
4080
4563
|
failWith("API error", err);
|
|
4081
4564
|
}
|
|
4082
4565
|
});
|
|
4083
4566
|
}
|
|
4084
4567
|
function createPayCommand() {
|
|
4085
|
-
return new
|
|
4568
|
+
return new import_commander19.Command("pay").description("Pay a premium: list payment requests, or get a checkout link for one").argument("[paymentId]", "Payment request to pay (omit to list them)").option("--json", "Output as JSON").action(async (paymentId, options) => {
|
|
4086
4569
|
const client = getClient();
|
|
4087
4570
|
try {
|
|
4088
4571
|
if (!paymentId) {
|
|
@@ -4100,7 +4583,7 @@ function createPayCommand() {
|
|
|
4100
4583
|
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
4584
|
);
|
|
4102
4585
|
const next = payments.find((p) => p.payable);
|
|
4103
|
-
if (next) info(`Pay it:
|
|
4586
|
+
if (next) info(`Pay it: ${cliInvocation()} pay ${next.id}`);
|
|
4104
4587
|
return;
|
|
4105
4588
|
}
|
|
4106
4589
|
const { checkoutUrl } = await client.payments.checkout(paymentId);
|
|
@@ -4130,14 +4613,14 @@ function bondStartLines(r, ctx = {}) {
|
|
|
4130
4613
|
out.push(` ${str(q.key)}: ${str(q.label)}${q.required ? " (required)" : ""}${opts}`);
|
|
4131
4614
|
});
|
|
4132
4615
|
}
|
|
4133
|
-
out.push(`Answer with:
|
|
4616
|
+
out.push(`Answer with: ${cliInvocation()} bond answer ${str(r.session_id)} --answers answers.json`);
|
|
4134
4617
|
return out;
|
|
4135
4618
|
}
|
|
4136
4619
|
if (Array.isArray(r.candidates) && r.candidates.length) {
|
|
4137
4620
|
out.push("That name fits more than one bond. Rerun with the exact name:");
|
|
4138
4621
|
const rawState = (ctx.state ?? "").trim();
|
|
4139
4622
|
const state = !rawState ? null : /^[A-Za-z]{2}$/.test(rawState) ? rawState.toUpperCase() : shellQuote(rawState);
|
|
4140
|
-
r.candidates.forEach((name) => out.push(state ? `
|
|
4623
|
+
r.candidates.forEach((name) => out.push(state ? ` ${cliInvocation()} bond quote --state ${state} --bond ${shellQuote(str(name))}` : ` --bond ${shellQuote(str(name))}`));
|
|
4141
4624
|
return out;
|
|
4142
4625
|
}
|
|
4143
4626
|
if (r.needsAmount) {
|
|
@@ -4167,7 +4650,7 @@ function bondStartLines(r, ctx = {}) {
|
|
|
4167
4650
|
return out;
|
|
4168
4651
|
}
|
|
4169
4652
|
function createBondCommand() {
|
|
4170
|
-
const bond = new
|
|
4653
|
+
const bond = new import_commander19.Command("bond").alias("bonds").description("Surety bond quotes");
|
|
4171
4654
|
bond.command("quote").description("Start a bond quote and list its questions").requiredOption("-s, --state <state>", "Two-letter state, e.g. TX").option("-b, --bond <name>", 'Bond name or keyword, e.g. "notary", "contractor license"').option("--entity-id <id>", "Exact bond id, when you have it").option("--contract-amount <amount>", "Contract value, for contract bonds").option("--bond-amount <amount>", "Bond penal sum").option("--json", "Output as JSON").action(async (options) => {
|
|
4172
4655
|
try {
|
|
4173
4656
|
const r = await getClient().bonds.start({
|
|
@@ -4192,7 +4675,7 @@ function createBondCommand() {
|
|
|
4192
4675
|
if (options.json) json(r);
|
|
4193
4676
|
else {
|
|
4194
4677
|
console.log(JSON.stringify(r, null, 2));
|
|
4195
|
-
info(`Check it:
|
|
4678
|
+
info(`Check it: ${cliInvocation()} bond status ${sessionId}`);
|
|
4196
4679
|
}
|
|
4197
4680
|
} catch (err) {
|
|
4198
4681
|
failWith("Could not submit the answers", err);
|
|
@@ -4213,7 +4696,7 @@ function createBondCommand() {
|
|
|
4213
4696
|
return bond;
|
|
4214
4697
|
}
|
|
4215
4698
|
function createClaimCommand() {
|
|
4216
|
-
const claim = new
|
|
4699
|
+
const claim = new import_commander19.Command("claim").alias("claims").description("File and track claims");
|
|
4217
4700
|
claim.command("file").description("File a claim. The first time, accept the claims disclaimer in the portal").option("-d, --description <text>", "What happened (visible in shell history; prefer --description-file)").option("--description-file <file>", "Read what happened from a file, or - for stdin").option("--date <yyyy-mm-dd>", "When it happened").option("--policy <id>", "Policy id, if you know it").option("--json", "Output as JSON").action(async (options) => {
|
|
4218
4701
|
let description = options.description;
|
|
4219
4702
|
if (options.descriptionFile) {
|
|
@@ -4237,7 +4720,7 @@ function createClaimCommand() {
|
|
|
4237
4720
|
success(r.message);
|
|
4238
4721
|
keyValue({ Claim: r.claimId, Status: r.status, Portal: r.portalUrl });
|
|
4239
4722
|
} catch (err) {
|
|
4240
|
-
if (err instanceof
|
|
4723
|
+
if (err instanceof import_sdk9.DeleganceError && err.reason === "gate_not_acknowledged") {
|
|
4241
4724
|
error("Accept the claims disclaimer once in the portal, then file again. It cannot be accepted for you.");
|
|
4242
4725
|
info(String(err.body?.portalUrl ?? ""));
|
|
4243
4726
|
process.exit(1);
|
|
@@ -4269,7 +4752,8 @@ function createClaimCommand() {
|
|
|
4269
4752
|
|
|
4270
4753
|
// src/program.ts
|
|
4271
4754
|
function createProgram() {
|
|
4272
|
-
|
|
4755
|
+
registerMinimalCheckbox();
|
|
4756
|
+
const program2 = new import_commander20.Command();
|
|
4273
4757
|
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
4758
|
migrateLegacyConfigKey();
|
|
4275
4759
|
});
|
|
@@ -4295,6 +4779,7 @@ function createProgram() {
|
|
|
4295
4779
|
program2.addCommand(createBondCommand());
|
|
4296
4780
|
program2.addCommand(createClaimCommand());
|
|
4297
4781
|
program2.addCommand(createFeedbackCommand());
|
|
4782
|
+
program2.addCommand(createTermsCommand());
|
|
4298
4783
|
program2.addCommand(createConfigCommand());
|
|
4299
4784
|
return program2;
|
|
4300
4785
|
}
|
|
@@ -4303,9 +4788,9 @@ function createProgram() {
|
|
|
4303
4788
|
var program = createProgram();
|
|
4304
4789
|
installLastCommandRecorder(program);
|
|
4305
4790
|
program.on("command:*", () => {
|
|
4306
|
-
console.error(
|
|
4791
|
+
console.error(import_chalk4.default.red(`Unknown command: ${program.args.join(" ")}`));
|
|
4307
4792
|
console.log();
|
|
4308
|
-
console.log(
|
|
4793
|
+
console.log(`Run "${cliInvocation()} --help" for a list of available commands.`);
|
|
4309
4794
|
process.exit(1);
|
|
4310
4795
|
});
|
|
4311
4796
|
if (process.argv.length === 2) {
|