@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/index.js
CHANGED
|
@@ -64,10 +64,10 @@ module.exports = __toCommonJS(index_exports);
|
|
|
64
64
|
|
|
65
65
|
// src/commands/auth.ts
|
|
66
66
|
var os4 = __toESM(require("os"));
|
|
67
|
-
var
|
|
68
|
-
var
|
|
67
|
+
var import_commander3 = require("commander");
|
|
68
|
+
var import_inquirer3 = __toESM(require("inquirer"));
|
|
69
69
|
var import_ora2 = __toESM(require("ora"));
|
|
70
|
-
var
|
|
70
|
+
var import_sdk4 = require("@delegance/sdk");
|
|
71
71
|
|
|
72
72
|
// src/utils/config.ts
|
|
73
73
|
var import_conf = __toESM(require("conf"));
|
|
@@ -76,7 +76,7 @@ var path = __toESM(require("path"));
|
|
|
76
76
|
var os = __toESM(require("os"));
|
|
77
77
|
|
|
78
78
|
// src/version.ts
|
|
79
|
-
var CLI_VERSION = "0.
|
|
79
|
+
var CLI_VERSION = "0.3.0";
|
|
80
80
|
|
|
81
81
|
// src/utils/config.ts
|
|
82
82
|
var CONFIG_DIR = path.join(os.homedir(), ".delegance");
|
|
@@ -229,6 +229,7 @@ __export(output_exports, {
|
|
|
229
229
|
error: () => error,
|
|
230
230
|
formatCurrency: () => formatCurrency,
|
|
231
231
|
formatDate: () => formatDate,
|
|
232
|
+
formatDateOnly: () => formatDateOnly,
|
|
232
233
|
formatFileSize: () => formatFileSize,
|
|
233
234
|
formatStatus: () => formatStatus,
|
|
234
235
|
header: () => header,
|
|
@@ -236,6 +237,7 @@ __export(output_exports, {
|
|
|
236
237
|
json: () => json,
|
|
237
238
|
keyValue: () => keyValue,
|
|
238
239
|
output: () => output,
|
|
240
|
+
renderCompactTable: () => renderCompactTable,
|
|
239
241
|
success: () => success,
|
|
240
242
|
table: () => table,
|
|
241
243
|
warn: () => warn
|
|
@@ -298,12 +300,28 @@ function formatValue(value) {
|
|
|
298
300
|
}
|
|
299
301
|
return String(value);
|
|
300
302
|
}
|
|
303
|
+
function visibleWidth(s) {
|
|
304
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
305
|
+
}
|
|
306
|
+
function renderCompactTable(headers, rows) {
|
|
307
|
+
const cells = rows.map((r) => headers.map((_, i) => r[i] === null || r[i] === void 0 ? "-" : String(r[i])));
|
|
308
|
+
const widths = headers.map((h, i) => Math.max(visibleWidth(h), ...cells.map((r) => visibleWidth(r[i]))));
|
|
309
|
+
const line = (parts) => ` ${parts.map((p, i) => p + " ".repeat(widths[i] - visibleWidth(p))).join(" ")}`.replace(/\s+$/, "");
|
|
310
|
+
return [
|
|
311
|
+
line(headers.map((h) => import_chalk.default.bold(h))),
|
|
312
|
+
import_chalk.default.dim(line(widths.map((w) => "\u2500".repeat(w)))),
|
|
313
|
+
...cells.map(line)
|
|
314
|
+
].join("\n");
|
|
315
|
+
}
|
|
301
316
|
function table(headers, rows, options = {}) {
|
|
317
|
+
if (options.compact) {
|
|
318
|
+
console.log(renderCompactTable(headers, rows));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
302
321
|
const tbl = new import_cli_table3.default({
|
|
303
322
|
head: headers.map((h) => import_chalk.default.bold(h)),
|
|
304
323
|
style: {
|
|
305
|
-
head: ["cyan"]
|
|
306
|
-
compact: options.compact
|
|
324
|
+
head: ["cyan"]
|
|
307
325
|
}
|
|
308
326
|
});
|
|
309
327
|
for (const row of rows) {
|
|
@@ -340,6 +358,15 @@ function formatDate(dateStr) {
|
|
|
340
358
|
minute: "2-digit"
|
|
341
359
|
});
|
|
342
360
|
}
|
|
361
|
+
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
362
|
+
function formatDateOnly(iso) {
|
|
363
|
+
if (!iso) return "";
|
|
364
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
|
|
365
|
+
if (!m) return iso;
|
|
366
|
+
const month = MONTHS[Number(m[2]) - 1];
|
|
367
|
+
if (!month) return iso;
|
|
368
|
+
return `${month} ${Number(m[3])}, ${m[1]}`;
|
|
369
|
+
}
|
|
343
370
|
function formatFileSize(bytes) {
|
|
344
371
|
if (bytes === void 0) return import_chalk.default.gray("(unknown)");
|
|
345
372
|
const units = ["B", "KB", "MB", "GB"];
|
|
@@ -409,14 +436,26 @@ function noteTraceId(traceId) {
|
|
|
409
436
|
if (current && typeof traceId === "string" && TRACE_RE.test(traceId)) current.traceId = traceId;
|
|
410
437
|
}
|
|
411
438
|
|
|
439
|
+
// src/utils/invocation.ts
|
|
440
|
+
var NPX_INVOCATION = "npx -y @delegance/cli";
|
|
441
|
+
function cliInvocation(env = process.env, argv = process.argv) {
|
|
442
|
+
const entry = (argv[1] ?? "").replace(/\\/g, "/");
|
|
443
|
+
if (entry.includes("/_npx/")) return NPX_INVOCATION;
|
|
444
|
+
if (env.npm_command === "exec" && /(^|\/)delegance(\.[cm]?js)?$/.test(entry)) return NPX_INVOCATION;
|
|
445
|
+
return "delegance";
|
|
446
|
+
}
|
|
447
|
+
function cliCommand(rest) {
|
|
448
|
+
return `${cliInvocation()} ${rest}`;
|
|
449
|
+
}
|
|
450
|
+
|
|
412
451
|
// src/utils/client.ts
|
|
413
452
|
var PORTAL_URL = "https://portal.delegancebrokerage.com";
|
|
414
453
|
function getClient() {
|
|
415
454
|
const apiKey = getApiKey();
|
|
416
455
|
if (!apiKey) {
|
|
417
456
|
error("Not authenticated");
|
|
418
|
-
info(
|
|
419
|
-
info(
|
|
457
|
+
info(`New to Delegance? Run "${cliInvocation()} signup" to create an account.`);
|
|
458
|
+
info(`Have an API key? Run "${cliInvocation()} auth login", or set DELEGANCE_API_KEY.`);
|
|
420
459
|
process.exit(1);
|
|
421
460
|
}
|
|
422
461
|
return new import_sdk.DeleganceClient({
|
|
@@ -428,7 +467,7 @@ function getCompanyId(options) {
|
|
|
428
467
|
const companyId = options.company || getConfigValue("defaultCompanyId");
|
|
429
468
|
if (!companyId) {
|
|
430
469
|
error("No company specified");
|
|
431
|
-
info(
|
|
470
|
+
info(`Use --company <id> or set a default with "${cliInvocation()} company use <id>"`);
|
|
432
471
|
process.exit(1);
|
|
433
472
|
}
|
|
434
473
|
return companyId;
|
|
@@ -440,9 +479,9 @@ function describeApiError(err) {
|
|
|
440
479
|
}
|
|
441
480
|
if (err instanceof import_sdk.AuthenticationError) {
|
|
442
481
|
if (err.reason === "key_expired" || err.reason === "key_membership_revoked") {
|
|
443
|
-
return { message: `${err.message}${traceSuffix}`, hint:
|
|
482
|
+
return { message: `${err.message}${traceSuffix}`, hint: `Run "${cliInvocation()} auth login" to sign in again.` };
|
|
444
483
|
}
|
|
445
|
-
return { message: `${err.message}${traceSuffix}`, hint:
|
|
484
|
+
return { message: `${err.message}${traceSuffix}`, hint: `Check your key with "${cliInvocation()} auth status", or log in again.` };
|
|
446
485
|
}
|
|
447
486
|
if (err instanceof import_sdk.ForbiddenError) {
|
|
448
487
|
return { message: `${err.message}${traceSuffix}`, hint: "Your API key lacks the scope for this command. Create a key with the right scope in the portal." };
|
|
@@ -503,10 +542,10 @@ function openInBrowser(url, platform = process.platform) {
|
|
|
503
542
|
|
|
504
543
|
// src/commands/signup.ts
|
|
505
544
|
var import_crypto = require("crypto");
|
|
506
|
-
var
|
|
507
|
-
var
|
|
545
|
+
var import_commander2 = require("commander");
|
|
546
|
+
var import_inquirer2 = __toESM(require("inquirer"));
|
|
508
547
|
var import_ora = __toESM(require("ora"));
|
|
509
|
-
var
|
|
548
|
+
var import_sdk3 = require("@delegance/sdk");
|
|
510
549
|
|
|
511
550
|
// src/utils/pending-signup.ts
|
|
512
551
|
var fs3 = __toESM(require("fs"));
|
|
@@ -547,6 +586,82 @@ function clearPendingSignup(home) {
|
|
|
547
586
|
}
|
|
548
587
|
}
|
|
549
588
|
|
|
589
|
+
// src/commands/terms.ts
|
|
590
|
+
var import_commander = require("commander");
|
|
591
|
+
var import_inquirer = __toESM(require("inquirer"));
|
|
592
|
+
var import_sdk2 = require("@delegance/sdk");
|
|
593
|
+
function termsAcceptRefusal(prompted) {
|
|
594
|
+
if (!prompted) {
|
|
595
|
+
return { kind: "error", message: `No prompt to answer. Read the Terms, then run "${cliCommand("terms accept --yes")}".` };
|
|
596
|
+
}
|
|
597
|
+
return { kind: "warn", message: "Not accepted. You can still quote; binding needs the current Terms." };
|
|
598
|
+
}
|
|
599
|
+
function termsLines(t) {
|
|
600
|
+
return [
|
|
601
|
+
`Delegance Terms of Service (version ${t.version}): ${t.termsUrl}`,
|
|
602
|
+
`Privacy Policy: ${t.privacyUrl}`,
|
|
603
|
+
...t.summaries
|
|
604
|
+
];
|
|
605
|
+
}
|
|
606
|
+
async function confirmTerms(t, opts) {
|
|
607
|
+
termsLines(t).forEach((l) => (opts.print ?? info)(l));
|
|
608
|
+
if (opts.flag) return true;
|
|
609
|
+
if (!opts.interactive) return false;
|
|
610
|
+
const ask = opts.ask ?? (async () => {
|
|
611
|
+
const a = await import_inquirer.default.prompt([{
|
|
612
|
+
type: "confirm",
|
|
613
|
+
name: "ok",
|
|
614
|
+
message: "Do you accept the Terms of Service and Privacy Policy?",
|
|
615
|
+
default: false
|
|
616
|
+
}]);
|
|
617
|
+
return a.ok === true;
|
|
618
|
+
});
|
|
619
|
+
return ask();
|
|
620
|
+
}
|
|
621
|
+
function createTermsCommand() {
|
|
622
|
+
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) => {
|
|
623
|
+
try {
|
|
624
|
+
const t = await (0, import_sdk2.createSignup)({ baseUrl: getBaseUrl() }).getTerms();
|
|
625
|
+
if (options.json) {
|
|
626
|
+
json(t);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
termsLines(t).forEach((l) => info(l));
|
|
630
|
+
info(`Broker review before you bind: ${t.brokerReview.command}, or email ${t.brokerReview.email}`);
|
|
631
|
+
} catch (err) {
|
|
632
|
+
failWith("Could not load the Terms", err);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
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) => {
|
|
636
|
+
const client = getClient();
|
|
637
|
+
try {
|
|
638
|
+
const t = await client.terms.get();
|
|
639
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !options.json;
|
|
640
|
+
const ok = await confirmTerms(t, {
|
|
641
|
+
flag: options.yes === true,
|
|
642
|
+
interactive,
|
|
643
|
+
// --json keeps stdout to one JSON document; the Terms still print, on stderr.
|
|
644
|
+
...options.json ? { print: (l) => console.error(l) } : {}
|
|
645
|
+
});
|
|
646
|
+
if (!ok) {
|
|
647
|
+
const refusal = termsAcceptRefusal(interactive);
|
|
648
|
+
if (refusal.kind === "error") error(refusal.message);
|
|
649
|
+
else warn(refusal.message);
|
|
650
|
+
process.exit(1);
|
|
651
|
+
}
|
|
652
|
+
const r = await client.terms.accept(t.version);
|
|
653
|
+
if (options.json) {
|
|
654
|
+
json(r);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
success(`Accepted the Terms of Service, version ${r.version}.`);
|
|
658
|
+
} catch (err) {
|
|
659
|
+
failWith("Could not record your acceptance", err);
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
return cmd;
|
|
663
|
+
}
|
|
664
|
+
|
|
550
665
|
// src/commands/signup.ts
|
|
551
666
|
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
552
667
|
var COMPANY_SIZES = ["0", "1", "2-10", "11-25", "26-50", "51-100", "100-499", "500+"];
|
|
@@ -571,34 +686,72 @@ function addressProblem(a) {
|
|
|
571
686
|
if (!/^\d{5}(-\d{4})?$/.test(a.zip.trim())) return "ZIP must be 5 digits (or ZIP+4).";
|
|
572
687
|
return null;
|
|
573
688
|
}
|
|
689
|
+
var TERMS_REASONS = /* @__PURE__ */ new Set(["terms_not_accepted", "terms_version_outdated"]);
|
|
690
|
+
function isTermsRefusal(err) {
|
|
691
|
+
return err instanceof import_sdk3.DeleganceError && TERMS_REASONS.has(String(err.reason ?? ""));
|
|
692
|
+
}
|
|
693
|
+
async function loadSignupTerms(get) {
|
|
694
|
+
try {
|
|
695
|
+
return await get();
|
|
696
|
+
} catch (err) {
|
|
697
|
+
if (err instanceof import_sdk3.DeleganceError && err.statusCode === 404) return null;
|
|
698
|
+
throw err;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
async function loadSignupTermsOrExit(baseUrl) {
|
|
702
|
+
try {
|
|
703
|
+
return await loadSignupTerms(() => (0, import_sdk3.createSignup)({ baseUrl }).getTerms());
|
|
704
|
+
} catch (err) {
|
|
705
|
+
failWith("Could not load the Terms of Service", err);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function nonInteractiveMissing(options, terms) {
|
|
709
|
+
const missing = missingSignupFields(options);
|
|
710
|
+
if (terms && options.acceptTerms !== true) missing.push("--accept-terms");
|
|
711
|
+
return missing;
|
|
712
|
+
}
|
|
713
|
+
async function acceptTermsOrExit(baseUrl, flag, preloaded) {
|
|
714
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
715
|
+
const terms = preloaded === void 0 ? await loadSignupTermsOrExit(baseUrl) : preloaded;
|
|
716
|
+
if (!terms) return "";
|
|
717
|
+
const ok = await confirmTerms(terms, { flag, interactive });
|
|
718
|
+
if (!ok) {
|
|
719
|
+
if (interactive) warn("Not signed up. Delegance needs your acceptance of the Terms and Privacy Policy to open an account.");
|
|
720
|
+
else error("No terminal to ask in. Read the Terms and Privacy Policy above, then pass --accept-terms.");
|
|
721
|
+
process.exit(1);
|
|
722
|
+
}
|
|
723
|
+
return terms.version;
|
|
724
|
+
}
|
|
574
725
|
function exchangeRefusal(err) {
|
|
575
|
-
if (!(err instanceof
|
|
726
|
+
if (!(err instanceof import_sdk3.DeleganceError)) return null;
|
|
576
727
|
switch (err.reason) {
|
|
577
728
|
case "account_exists":
|
|
578
729
|
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`;
|
|
579
730
|
case "already_collected":
|
|
580
731
|
return `The key for this signup was already collected. If you lost it, create a new key at ${PORTAL_URL}/portal/settings/api-keys`;
|
|
581
732
|
case "expired":
|
|
582
|
-
return
|
|
733
|
+
return `This signup expired (24 hours). Run "${cliInvocation()} signup" again.`;
|
|
583
734
|
default:
|
|
584
735
|
return null;
|
|
585
736
|
}
|
|
586
737
|
}
|
|
587
738
|
async function ensureEnrolled(pending) {
|
|
588
739
|
if (pending.enrollmentId) return pending;
|
|
589
|
-
const signup = (0,
|
|
740
|
+
const signup = (0, import_sdk3.createSignup)({ baseUrl: pending.baseUrl });
|
|
590
741
|
const hash = (0, import_crypto.createHash)("sha256").update(pending.deviceSecret).digest("hex");
|
|
591
742
|
const enrollment = await signup.enroll(pending.bootstrapKey, {
|
|
592
743
|
deviceSecretHash: hash,
|
|
593
744
|
firstName: pending.firstName ?? "",
|
|
594
|
-
lastName: pending.lastName ?? ""
|
|
745
|
+
lastName: pending.lastName ?? "",
|
|
746
|
+
termsAccepted: true,
|
|
747
|
+
termsVersion: pending.termsVersion ?? ""
|
|
595
748
|
});
|
|
596
749
|
const next = { ...pending, enrollmentId: enrollment.enrollmentId, verificationSent: false };
|
|
597
750
|
savePendingSignup(next);
|
|
598
751
|
return sendVerification(next);
|
|
599
752
|
}
|
|
600
753
|
async function sendVerification(pending) {
|
|
601
|
-
await (0,
|
|
754
|
+
await (0, import_sdk3.createSignup)({ baseUrl: pending.baseUrl }).requestVerification(pending.bootstrapKey);
|
|
602
755
|
const next = { ...pending, verificationSent: true };
|
|
603
756
|
savePendingSignup(next);
|
|
604
757
|
return next;
|
|
@@ -609,14 +762,14 @@ function storeOrPrintKey(apiKey, store = storeApiKey) {
|
|
|
609
762
|
return true;
|
|
610
763
|
} catch (err) {
|
|
611
764
|
error(`Could not save the key to ~/.delegance/credentials: ${err.message}`);
|
|
612
|
-
warn(
|
|
765
|
+
warn(`Your key is below. It is shown once. Save it now, then run "${cliInvocation()} auth login --key <key>".`);
|
|
613
766
|
console.log(apiKey);
|
|
614
767
|
return false;
|
|
615
768
|
}
|
|
616
769
|
}
|
|
617
770
|
async function waitAndStore(pending, timeoutMs) {
|
|
618
|
-
const signup = (0,
|
|
619
|
-
const spinner = (0, import_ora.default)(`Waiting for you to click the link sent to ${pending.email} (Ctrl-C is safe: resume with "
|
|
771
|
+
const signup = (0, import_sdk3.createSignup)({ baseUrl: pending.baseUrl });
|
|
772
|
+
const spinner = (0, import_ora.default)(`Waiting for you to click the link sent to ${pending.email} (Ctrl-C is safe: resume with "${cliInvocation()} verify")`).start();
|
|
620
773
|
const started = Date.now();
|
|
621
774
|
try {
|
|
622
775
|
const result = await signup.waitForKey(pending.enrollmentId, pending.deviceSecret, {
|
|
@@ -628,7 +781,7 @@ async function waitAndStore(pending, timeoutMs) {
|
|
|
628
781
|
});
|
|
629
782
|
if (result.status !== "ready") {
|
|
630
783
|
spinner.warn("Still waiting on the email confirmation.");
|
|
631
|
-
info(
|
|
784
|
+
info(`Run "${cliInvocation()} verify" after you click the link.`);
|
|
632
785
|
return;
|
|
633
786
|
}
|
|
634
787
|
spinner.stop();
|
|
@@ -636,7 +789,7 @@ async function waitAndStore(pending, timeoutMs) {
|
|
|
636
789
|
setConfig("defaultCompanyId", result.companyId);
|
|
637
790
|
clearPendingSignup();
|
|
638
791
|
if (stored) success(`Verified. API key ${maskApiKey(result.apiKey)} saved to ~/.delegance/credentials`);
|
|
639
|
-
success(
|
|
792
|
+
success(`You can quote now: ${cliInvocation()} quote start --coverage gl`);
|
|
640
793
|
} catch (err) {
|
|
641
794
|
spinner.stop();
|
|
642
795
|
const why = exchangeRefusal(err);
|
|
@@ -666,21 +819,24 @@ function signupPrompts(options, interactive) {
|
|
|
666
819
|
return all.filter((q) => q.when !== false).map(({ when: _when, ...q }) => q);
|
|
667
820
|
}
|
|
668
821
|
function createSignupCommand() {
|
|
669
|
-
return new
|
|
822
|
+
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) => {
|
|
670
823
|
const existing = loadPendingSignup();
|
|
671
824
|
if (existing) {
|
|
672
825
|
warn(`A signup for ${existing.email} is already waiting for its email confirmation.`);
|
|
673
|
-
info(
|
|
826
|
+
info(`Run "${cliInvocation()} verify" to finish it, or "${cliInvocation()} verify --cancel" to start over.`);
|
|
674
827
|
process.exit(1);
|
|
675
828
|
}
|
|
829
|
+
const baseUrl = getBaseUrl();
|
|
830
|
+
let preloadedTerms;
|
|
676
831
|
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
677
|
-
|
|
832
|
+
preloadedTerms = await loadSignupTermsOrExit(baseUrl);
|
|
833
|
+
const missing = nonInteractiveMissing(options, preloadedTerms);
|
|
678
834
|
if (missing.length) {
|
|
679
835
|
error(`No terminal to ask in. Missing: ${missing.join(" ")}`);
|
|
680
836
|
process.exit(1);
|
|
681
837
|
}
|
|
682
838
|
}
|
|
683
|
-
const answers = await
|
|
839
|
+
const answers = await import_inquirer2.default.prompt(signupPrompts(options, Boolean(process.stdin.isTTY && process.stdout.isTTY)));
|
|
684
840
|
const address = {
|
|
685
841
|
street: String(options.street ?? answers.street).trim(),
|
|
686
842
|
city: String(options.city ?? answers.city).trim(),
|
|
@@ -703,8 +859,8 @@ function createSignupCommand() {
|
|
|
703
859
|
error(`--size must be one of: ${COMPANY_SIZES.join(", ")}`);
|
|
704
860
|
process.exit(1);
|
|
705
861
|
}
|
|
706
|
-
const
|
|
707
|
-
const signup = (0,
|
|
862
|
+
const termsVersion = await acceptTermsOrExit(baseUrl, options.acceptTerms === true, preloadedTerms);
|
|
863
|
+
const signup = (0, import_sdk3.createSignup)({ baseUrl });
|
|
708
864
|
const spinner = (0, import_ora.default)("Creating your account...").start();
|
|
709
865
|
try {
|
|
710
866
|
const account = await signup.createAccount({
|
|
@@ -716,7 +872,7 @@ function createSignupCommand() {
|
|
|
716
872
|
...description ? { description } : {},
|
|
717
873
|
...options.website ? { website: options.website } : {}
|
|
718
874
|
});
|
|
719
|
-
const device = (0,
|
|
875
|
+
const device = (0, import_sdk3.newDeviceSecret)();
|
|
720
876
|
let pending = {
|
|
721
877
|
enrollmentId: "",
|
|
722
878
|
deviceSecret: device.secret,
|
|
@@ -725,6 +881,7 @@ function createSignupCommand() {
|
|
|
725
881
|
email,
|
|
726
882
|
firstName: String(options.first ?? answers.first).trim(),
|
|
727
883
|
lastName: String(options.last ?? answers.last).trim(),
|
|
884
|
+
termsVersion,
|
|
728
885
|
baseUrl,
|
|
729
886
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
730
887
|
};
|
|
@@ -735,7 +892,7 @@ function createSignupCommand() {
|
|
|
735
892
|
warn("We could not save your business address. Add it in the portal before quoting, or carriers cannot price you.");
|
|
736
893
|
}
|
|
737
894
|
if (options.wait === false) {
|
|
738
|
-
info(
|
|
895
|
+
info(`After you click it, run "${cliInvocation()} verify" to collect your API key.`);
|
|
739
896
|
return;
|
|
740
897
|
}
|
|
741
898
|
await waitAndStore(pending, Number(options.timeout) * 6e4 || 15 * 6e4);
|
|
@@ -746,17 +903,17 @@ function createSignupCommand() {
|
|
|
746
903
|
});
|
|
747
904
|
}
|
|
748
905
|
function resendFailureMessage(err) {
|
|
749
|
-
if (err instanceof
|
|
906
|
+
if (err instanceof import_sdk3.DeleganceError && (err.statusCode === 401 || err.statusCode === 403)) {
|
|
750
907
|
return "Your email is already confirmed, so there is nothing to re-send. Collecting your key now.";
|
|
751
908
|
}
|
|
752
909
|
const why = err instanceof Error ? err.message : String(err);
|
|
753
910
|
return `Could not re-send the email (${why}). Still waiting for the link you already have.`;
|
|
754
911
|
}
|
|
755
912
|
function createVerifyCommand() {
|
|
756
|
-
return new
|
|
913
|
+
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) => {
|
|
757
914
|
const pending = loadPendingSignup();
|
|
758
915
|
if (!pending) {
|
|
759
|
-
info(
|
|
916
|
+
info(`No signup is waiting on this machine. Start one with "${cliInvocation()} signup".`);
|
|
760
917
|
return;
|
|
761
918
|
}
|
|
762
919
|
if (options.cancel) {
|
|
@@ -773,8 +930,21 @@ function createVerifyCommand() {
|
|
|
773
930
|
failWith("Could not send the confirm email", err);
|
|
774
931
|
}
|
|
775
932
|
} else if (!pending.enrollmentId) {
|
|
933
|
+
let toEnroll = pending;
|
|
934
|
+
if (!pending.termsVersion) {
|
|
935
|
+
toEnroll = { ...pending, termsVersion: await acceptTermsOrExit(pending.baseUrl, options.acceptTerms === true) };
|
|
936
|
+
savePendingSignup(toEnroll);
|
|
937
|
+
}
|
|
776
938
|
try {
|
|
777
|
-
|
|
939
|
+
try {
|
|
940
|
+
resumed = await ensureEnrolled(toEnroll);
|
|
941
|
+
} catch (err) {
|
|
942
|
+
if (!isTermsRefusal(err)) throw err;
|
|
943
|
+
warn("The Terms of Service changed since this signup started. Review them again.");
|
|
944
|
+
toEnroll = { ...toEnroll, termsVersion: await acceptTermsOrExit(pending.baseUrl, options.acceptTerms === true) };
|
|
945
|
+
savePendingSignup(toEnroll);
|
|
946
|
+
resumed = await ensureEnrolled(toEnroll);
|
|
947
|
+
}
|
|
778
948
|
success(`Finished setting up the signup. We emailed a confirm link to ${pending.email}.`);
|
|
779
949
|
} catch (err) {
|
|
780
950
|
failWith("Could not finish the signup", err);
|
|
@@ -782,7 +952,7 @@ function createVerifyCommand() {
|
|
|
782
952
|
}
|
|
783
953
|
if (options.resend && resumed === pending) {
|
|
784
954
|
try {
|
|
785
|
-
await (0,
|
|
955
|
+
await (0, import_sdk3.createSignup)({ baseUrl: pending.baseUrl }).requestVerification(pending.bootstrapKey);
|
|
786
956
|
success(`Sent the confirm link to ${pending.email} again.`);
|
|
787
957
|
} catch (err) {
|
|
788
958
|
info(resendFailureMessage(err));
|
|
@@ -813,27 +983,27 @@ function describeExpiry(expiresAt, now = /* @__PURE__ */ new Date()) {
|
|
|
813
983
|
return `${date} (in ${days} day${days === 1 ? "" : "s"})`;
|
|
814
984
|
}
|
|
815
985
|
function deviceLoginRefusal(err) {
|
|
816
|
-
if (!(err instanceof
|
|
986
|
+
if (!(err instanceof import_sdk4.DeleganceError)) return null;
|
|
817
987
|
switch (err.reason) {
|
|
818
988
|
case "access_denied":
|
|
819
989
|
return "The request was denied in the portal. Nothing was stored.";
|
|
820
990
|
case "expired":
|
|
821
|
-
return
|
|
991
|
+
return `The code expired before it was approved. Run "${cliInvocation()} auth login" again.`;
|
|
822
992
|
case "already_collected":
|
|
823
|
-
return
|
|
993
|
+
return `This login was already used. Run "${cliInvocation()} auth login" again.`;
|
|
824
994
|
default:
|
|
825
995
|
return null;
|
|
826
996
|
}
|
|
827
997
|
}
|
|
828
998
|
function notAvailable(err) {
|
|
829
|
-
return err instanceof
|
|
999
|
+
return err instanceof import_sdk4.DeleganceError && (err.statusCode === 404 || err.reason === "not_available");
|
|
830
1000
|
}
|
|
831
1001
|
function pasteFallbackHint() {
|
|
832
|
-
info(`Create a key at ${PORTAL_URL}/portal/settings/api-keys, then run "
|
|
1002
|
+
info(`Create a key at ${PORTAL_URL}/portal/settings/api-keys, then run "${cliInvocation()} auth login --key <key>".`);
|
|
833
1003
|
}
|
|
834
1004
|
async function readKeyInfo(apiKey, timeoutMs = 5e3) {
|
|
835
1005
|
try {
|
|
836
|
-
const client = new
|
|
1006
|
+
const client = new import_sdk4.DeleganceClient({ apiKey, baseUrl: getBaseUrl(), timeout: timeoutMs, retries: 0 });
|
|
837
1007
|
return await client.auth.keyInfo();
|
|
838
1008
|
} catch {
|
|
839
1009
|
return null;
|
|
@@ -854,7 +1024,7 @@ async function loginWithKey(apiKey, skipVerify) {
|
|
|
854
1024
|
}
|
|
855
1025
|
const spinner = (0, import_ora2.default)("Verifying API key...").start();
|
|
856
1026
|
try {
|
|
857
|
-
const client = new
|
|
1027
|
+
const client = new import_sdk4.DeleganceClient({ apiKey, baseUrl: getBaseUrl() });
|
|
858
1028
|
const isValid = await client.verifyApiKey();
|
|
859
1029
|
if (!isValid) {
|
|
860
1030
|
spinner.fail("API key verification failed");
|
|
@@ -875,8 +1045,8 @@ async function loginWithKey(apiKey, skipVerify) {
|
|
|
875
1045
|
}
|
|
876
1046
|
}
|
|
877
1047
|
async function loginWithBrowser(openBrowser) {
|
|
878
|
-
const login = (0,
|
|
879
|
-
const device = (0,
|
|
1048
|
+
const login = (0, import_sdk4.createDeviceLogin)({ baseUrl: getBaseUrl() });
|
|
1049
|
+
const device = (0, import_sdk4.newDeviceSecret)();
|
|
880
1050
|
let start;
|
|
881
1051
|
try {
|
|
882
1052
|
start = await login.start({ deviceSecretHash: device.hash, deviceName: defaultDeviceName() });
|
|
@@ -906,7 +1076,7 @@ async function loginWithBrowser(openBrowser) {
|
|
|
906
1076
|
});
|
|
907
1077
|
if (result.status !== "ready") {
|
|
908
1078
|
spinner.fail("The code expired before it was approved.");
|
|
909
|
-
info(
|
|
1079
|
+
info(`Run "${cliInvocation()} auth login" again.`);
|
|
910
1080
|
process.exit(1);
|
|
911
1081
|
}
|
|
912
1082
|
spinner.stop();
|
|
@@ -930,7 +1100,7 @@ async function loginWithBrowser(openBrowser) {
|
|
|
930
1100
|
}
|
|
931
1101
|
}
|
|
932
1102
|
function createAuthCommand() {
|
|
933
|
-
const auth = new
|
|
1103
|
+
const auth = new import_commander3.Command("auth").description("Manage authentication");
|
|
934
1104
|
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) => {
|
|
935
1105
|
try {
|
|
936
1106
|
if (options.key) {
|
|
@@ -938,7 +1108,7 @@ function createAuthCommand() {
|
|
|
938
1108
|
return;
|
|
939
1109
|
}
|
|
940
1110
|
if (options.paste) {
|
|
941
|
-
const answers = await
|
|
1111
|
+
const answers = await import_inquirer3.default.prompt([
|
|
942
1112
|
{
|
|
943
1113
|
type: "password",
|
|
944
1114
|
name: "apiKey",
|
|
@@ -963,7 +1133,7 @@ function createAuthCommand() {
|
|
|
963
1133
|
header("Authentication Status");
|
|
964
1134
|
if (!isAuthenticated()) {
|
|
965
1135
|
warn("Not authenticated");
|
|
966
|
-
info(
|
|
1136
|
+
info(`Run "${cliInvocation()} auth login" to sign in, or "${cliInvocation()} signup" to create an account`);
|
|
967
1137
|
return;
|
|
968
1138
|
}
|
|
969
1139
|
const apiKey = getApiKey() || "";
|
|
@@ -973,20 +1143,20 @@ function createAuthCommand() {
|
|
|
973
1143
|
let origin = null;
|
|
974
1144
|
let refused = null;
|
|
975
1145
|
try {
|
|
976
|
-
const client = new
|
|
1146
|
+
const client = new import_sdk4.DeleganceClient({ apiKey, baseUrl: getBaseUrl(), timeout: 5e3, retries: 0 });
|
|
977
1147
|
const keyInfo = await client.auth.keyInfo();
|
|
978
1148
|
origin = keyInfo.origin;
|
|
979
1149
|
expiryLine = describeExpiry(keyInfo.expiresAt);
|
|
980
1150
|
if (!fromEnv) setStoredKeyExpiry(keyInfo.expiresAt);
|
|
981
1151
|
} catch (err) {
|
|
982
|
-
if (err instanceof
|
|
1152
|
+
if (err instanceof import_sdk4.DeleganceError && err.statusCode === 401) {
|
|
983
1153
|
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";
|
|
984
1154
|
expiryLine = err.reason === "key_expired" ? "Expired" : "Not valid";
|
|
985
1155
|
}
|
|
986
1156
|
}
|
|
987
1157
|
if (refused) {
|
|
988
1158
|
warn(refused);
|
|
989
|
-
info(
|
|
1159
|
+
info(`Run "${cliInvocation()} auth login" to sign in again.`);
|
|
990
1160
|
} else {
|
|
991
1161
|
success("Authenticated");
|
|
992
1162
|
}
|
|
@@ -1004,18 +1174,18 @@ function createAuthCommand() {
|
|
|
1004
1174
|
const apiKey = getApiKey();
|
|
1005
1175
|
if (!apiKey) {
|
|
1006
1176
|
error("Not authenticated");
|
|
1007
|
-
info(
|
|
1177
|
+
info(`Run "${cliInvocation()} auth login" to sign in`);
|
|
1008
1178
|
process.exit(1);
|
|
1009
1179
|
}
|
|
1010
1180
|
const spinner = (0, import_ora2.default)("Refreshing your key...").start();
|
|
1011
1181
|
try {
|
|
1012
|
-
const client = new
|
|
1182
|
+
const client = new import_sdk4.DeleganceClient({ apiKey, baseUrl: getBaseUrl() });
|
|
1013
1183
|
const { expiresAt } = await client.auth.refresh();
|
|
1014
1184
|
if (!process.env.DELEGANCE_API_KEY) setStoredKeyExpiry(expiresAt);
|
|
1015
1185
|
spinner.succeed(`Key refreshed. Expires ${describeExpiry(expiresAt)}.`);
|
|
1016
1186
|
} catch (err) {
|
|
1017
1187
|
spinner.stop();
|
|
1018
|
-
if (err instanceof
|
|
1188
|
+
if (err instanceof import_sdk4.DeleganceError && err.reason === "not_refreshable") {
|
|
1019
1189
|
info("This key came from the portal and keeps the expiry it was created with. Nothing changed.");
|
|
1020
1190
|
return;
|
|
1021
1191
|
}
|
|
@@ -1032,7 +1202,7 @@ function createAuthCommand() {
|
|
|
1032
1202
|
return;
|
|
1033
1203
|
}
|
|
1034
1204
|
if (!options.force) {
|
|
1035
|
-
const { confirm } = await
|
|
1205
|
+
const { confirm } = await import_inquirer3.default.prompt([
|
|
1036
1206
|
{
|
|
1037
1207
|
type: "confirm",
|
|
1038
1208
|
name: "confirm",
|
|
@@ -1052,7 +1222,7 @@ function createAuthCommand() {
|
|
|
1052
1222
|
auth.command("whoami").description("Show current authenticated identity").action(async () => {
|
|
1053
1223
|
if (!isAuthenticated()) {
|
|
1054
1224
|
error("Not authenticated");
|
|
1055
|
-
info(
|
|
1225
|
+
info(`Run "${cliInvocation()} auth login" to authenticate`);
|
|
1056
1226
|
process.exit(1);
|
|
1057
1227
|
}
|
|
1058
1228
|
const apiKey = getApiKey();
|
|
@@ -1062,7 +1232,7 @@ function createAuthCommand() {
|
|
|
1062
1232
|
}
|
|
1063
1233
|
const spinner = (0, import_ora2.default)("Fetching account info...").start();
|
|
1064
1234
|
try {
|
|
1065
|
-
const client = new
|
|
1235
|
+
const client = new import_sdk4.DeleganceClient({
|
|
1066
1236
|
apiKey,
|
|
1067
1237
|
baseUrl: getBaseUrl()
|
|
1068
1238
|
});
|
|
@@ -1090,11 +1260,11 @@ function createAuthCommand() {
|
|
|
1090
1260
|
}
|
|
1091
1261
|
|
|
1092
1262
|
// src/commands/company.ts
|
|
1093
|
-
var
|
|
1094
|
-
var
|
|
1263
|
+
var import_commander4 = require("commander");
|
|
1264
|
+
var import_inquirer4 = __toESM(require("inquirer"));
|
|
1095
1265
|
var import_ora3 = __toESM(require("ora"));
|
|
1096
1266
|
function createCompanyCommand() {
|
|
1097
|
-
const company = new
|
|
1267
|
+
const company = new import_commander4.Command("company").alias("companies").description("Manage companies");
|
|
1098
1268
|
company.command("list").description("List companies").option("--include-created", "Include companies created via this API key").option("--json", "Output as JSON").action(async (options) => {
|
|
1099
1269
|
const client = getClient();
|
|
1100
1270
|
const spinner = (0, import_ora3.default)("Fetching companies...").start();
|
|
@@ -1130,7 +1300,7 @@ function createCompanyCommand() {
|
|
|
1130
1300
|
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) => {
|
|
1131
1301
|
let companyData;
|
|
1132
1302
|
if (options.interactive || !options.name && !options.industry) {
|
|
1133
|
-
const answers = await
|
|
1303
|
+
const answers = await import_inquirer4.default.prompt([
|
|
1134
1304
|
{
|
|
1135
1305
|
type: "input",
|
|
1136
1306
|
name: "name",
|
|
@@ -1248,7 +1418,7 @@ function createCompanyCommand() {
|
|
|
1248
1418
|
const companyId = getConfigValue("defaultCompanyId");
|
|
1249
1419
|
if (!companyId) {
|
|
1250
1420
|
info("No default company set");
|
|
1251
|
-
info(
|
|
1421
|
+
info(`Use "${cliInvocation()} company use <id>" to set one`);
|
|
1252
1422
|
return;
|
|
1253
1423
|
}
|
|
1254
1424
|
const client = getClient();
|
|
@@ -1276,7 +1446,7 @@ function createCompanyCommand() {
|
|
|
1276
1446
|
}
|
|
1277
1447
|
|
|
1278
1448
|
// src/commands/document.ts
|
|
1279
|
-
var
|
|
1449
|
+
var import_commander5 = require("commander");
|
|
1280
1450
|
var import_ora4 = __toESM(require("ora"));
|
|
1281
1451
|
var fs4 = __toESM(require("fs"));
|
|
1282
1452
|
var path4 = __toESM(require("path"));
|
|
@@ -1287,7 +1457,7 @@ function extractionOutcome(status) {
|
|
|
1287
1457
|
return "pending";
|
|
1288
1458
|
}
|
|
1289
1459
|
function createDocumentCommand() {
|
|
1290
|
-
const doc = new
|
|
1460
|
+
const doc = new import_commander5.Command("doc").alias("docs").alias("document").alias("documents").description("Manage documents");
|
|
1291
1461
|
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) => {
|
|
1292
1462
|
const companyId = getCompanyId(options);
|
|
1293
1463
|
const client = getClient();
|
|
@@ -1522,7 +1692,7 @@ function createDocumentCommand() {
|
|
|
1522
1692
|
process.exit(1);
|
|
1523
1693
|
} else {
|
|
1524
1694
|
waitSpinner.warn(`Extraction still ${finished.status}`);
|
|
1525
|
-
if (!options.json) info(`Run "
|
|
1695
|
+
if (!options.json) info(`Run "${cliInvocation()} doc wait ${documentId}" to keep following it`);
|
|
1526
1696
|
}
|
|
1527
1697
|
return;
|
|
1528
1698
|
}
|
|
@@ -1530,7 +1700,7 @@ function createDocumentCommand() {
|
|
|
1530
1700
|
json(result);
|
|
1531
1701
|
} else {
|
|
1532
1702
|
info(`${result.reprocessesRemaining} reprocess attempts left today`);
|
|
1533
|
-
info(`Run "
|
|
1703
|
+
info(`Run "${cliInvocation()} doc wait ${documentId}" to follow it`);
|
|
1534
1704
|
}
|
|
1535
1705
|
} catch (err) {
|
|
1536
1706
|
spinner.fail("Failed to reprocess document");
|
|
@@ -1565,11 +1735,11 @@ function createDocumentCommand() {
|
|
|
1565
1735
|
}
|
|
1566
1736
|
|
|
1567
1737
|
// src/commands/chat.ts
|
|
1568
|
-
var
|
|
1738
|
+
var import_commander6 = require("commander");
|
|
1569
1739
|
var import_ora5 = __toESM(require("ora"));
|
|
1570
|
-
var
|
|
1740
|
+
var import_inquirer5 = __toESM(require("inquirer"));
|
|
1571
1741
|
function createChatCommand() {
|
|
1572
|
-
const chat = new
|
|
1742
|
+
const chat = new import_commander6.Command("ask").alias("chat").description("Ask the Orin insurance assistant a question");
|
|
1573
1743
|
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) => {
|
|
1574
1744
|
const question = questionParts.join(" ");
|
|
1575
1745
|
const client = getClient();
|
|
@@ -1605,7 +1775,7 @@ function createChatCommand() {
|
|
|
1605
1775
|
console.log('Type your questions. Enter "exit" or "quit" to end the session.');
|
|
1606
1776
|
console.log();
|
|
1607
1777
|
while (true) {
|
|
1608
|
-
const { question } = await
|
|
1778
|
+
const { question } = await import_inquirer5.default.prompt([
|
|
1609
1779
|
{
|
|
1610
1780
|
type: "input",
|
|
1611
1781
|
name: "question",
|
|
@@ -1665,18 +1835,102 @@ function createChatCommand() {
|
|
|
1665
1835
|
}
|
|
1666
1836
|
|
|
1667
1837
|
// src/commands/quote.ts
|
|
1668
|
-
var
|
|
1838
|
+
var import_commander7 = require("commander");
|
|
1669
1839
|
var import_ora7 = __toESM(require("ora"));
|
|
1670
|
-
var
|
|
1671
|
-
var
|
|
1840
|
+
var import_inquirer7 = __toESM(require("inquirer"));
|
|
1841
|
+
var import_sdk6 = require("@delegance/sdk");
|
|
1672
1842
|
|
|
1673
1843
|
// src/commands/instant-quote.ts
|
|
1674
1844
|
var fs5 = __toESM(require("fs"));
|
|
1675
1845
|
var import_crypto2 = require("crypto");
|
|
1676
1846
|
var import_chalk2 = __toESM(require("chalk"));
|
|
1677
|
-
var
|
|
1847
|
+
var import_inquirer6 = __toESM(require("inquirer"));
|
|
1678
1848
|
var import_ora6 = __toESM(require("ora"));
|
|
1679
|
-
var
|
|
1849
|
+
var import_sdk5 = require("@delegance/sdk");
|
|
1850
|
+
|
|
1851
|
+
// src/utils/coverage-names.ts
|
|
1852
|
+
var NAMES = {
|
|
1853
|
+
// General liability
|
|
1854
|
+
cgl: "General Liability",
|
|
1855
|
+
gl: "General Liability",
|
|
1856
|
+
general_liability: "General Liability",
|
|
1857
|
+
// Workers' comp
|
|
1858
|
+
wc: "Workers' Comp",
|
|
1859
|
+
workers_comp: "Workers' Comp",
|
|
1860
|
+
workers_compensation: "Workers' Comp",
|
|
1861
|
+
// Business owner's policy
|
|
1862
|
+
bop: "Business Owner's Policy",
|
|
1863
|
+
business_owners: "Business Owner's Policy",
|
|
1864
|
+
business_owners_policy: "Business Owner's Policy",
|
|
1865
|
+
// Cyber
|
|
1866
|
+
cyber: "Cyber",
|
|
1867
|
+
cyber_liability: "Cyber",
|
|
1868
|
+
// Professional liability (E&O)
|
|
1869
|
+
mpl: "Professional Liability (E&O)",
|
|
1870
|
+
pl: "Professional Liability (E&O)",
|
|
1871
|
+
pro_liab: "Professional Liability (E&O)",
|
|
1872
|
+
professional_liability: "Professional Liability (E&O)",
|
|
1873
|
+
eo: "Professional Liability (E&O)",
|
|
1874
|
+
e_o: "Professional Liability (E&O)",
|
|
1875
|
+
tech_eo: "Tech E&O",
|
|
1876
|
+
errors_omissions: "Tech E&O",
|
|
1877
|
+
// Auto
|
|
1878
|
+
ca: "Commercial Auto",
|
|
1879
|
+
auto: "Commercial Auto",
|
|
1880
|
+
commercial_auto: "Commercial Auto",
|
|
1881
|
+
// Property
|
|
1882
|
+
cp: "Commercial Property",
|
|
1883
|
+
property: "Commercial Property",
|
|
1884
|
+
commercial_property: "Commercial Property",
|
|
1885
|
+
// Umbrella
|
|
1886
|
+
umbrella: "Umbrella",
|
|
1887
|
+
excess: "Umbrella",
|
|
1888
|
+
commercial_umbrella: "Umbrella",
|
|
1889
|
+
// Builders risk
|
|
1890
|
+
br: "Builders' Risk",
|
|
1891
|
+
builders_risk: "Builders' Risk",
|
|
1892
|
+
// Inland marine and marine
|
|
1893
|
+
im: "Inland Marine",
|
|
1894
|
+
inland_marine: "Inland Marine",
|
|
1895
|
+
marine: "Marine",
|
|
1896
|
+
// Management liability family
|
|
1897
|
+
ml: "Management Liability",
|
|
1898
|
+
management_liability: "Management Liability",
|
|
1899
|
+
do: "Directors and Officers (D&O)",
|
|
1900
|
+
d_o: "Directors and Officers (D&O)",
|
|
1901
|
+
directors_officers: "Directors and Officers (D&O)",
|
|
1902
|
+
epli: "Employment Practices Liability",
|
|
1903
|
+
employment_practices: "Employment Practices Liability",
|
|
1904
|
+
crime: "Crime",
|
|
1905
|
+
fiduciary: "Fiduciary Liability",
|
|
1906
|
+
fiduciary_liability: "Fiduciary Liability",
|
|
1907
|
+
// Everything else on the canonical list
|
|
1908
|
+
product_liability: "Product Liability",
|
|
1909
|
+
liquor_liability: "Liquor Liability",
|
|
1910
|
+
contractors_liability: "Contractors' Liability",
|
|
1911
|
+
equipment_breakdown: "Equipment Breakdown",
|
|
1912
|
+
environmental_liability: "Environmental Liability",
|
|
1913
|
+
healthcare_medical: "Healthcare Medical Liability",
|
|
1914
|
+
life_sciences: "Life Sciences",
|
|
1915
|
+
hospitality_programs: "Hospitality Program",
|
|
1916
|
+
commercial_flood: "Commercial Flood",
|
|
1917
|
+
home_based_business: "Home-Based Business",
|
|
1918
|
+
foreign_package: "Foreign Package",
|
|
1919
|
+
multinational_package: "Multinational Package",
|
|
1920
|
+
political_risk: "Political Risk",
|
|
1921
|
+
trade_credit: "Trade Credit",
|
|
1922
|
+
kidnap_ransom: "Kidnap and Ransom",
|
|
1923
|
+
product_recall: "Product Recall",
|
|
1924
|
+
railroad_liability: "Railroad Liability"
|
|
1925
|
+
};
|
|
1926
|
+
function coverageName(code) {
|
|
1927
|
+
if (code === null || code === void 0) return "";
|
|
1928
|
+
const key = code.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1929
|
+
return NAMES[key] ?? code;
|
|
1930
|
+
}
|
|
1931
|
+
function coverageNames(codes) {
|
|
1932
|
+
return codes.map(coverageName).filter(Boolean).join(", ");
|
|
1933
|
+
}
|
|
1680
1934
|
|
|
1681
1935
|
// src/commerce/questions.ts
|
|
1682
1936
|
var COVERAGE_ALIASES = {
|
|
@@ -1756,14 +2010,23 @@ function validateAnswers(questions, provided) {
|
|
|
1756
2010
|
}
|
|
1757
2011
|
return { answers, problems };
|
|
1758
2012
|
}
|
|
2013
|
+
function rowStatus(q) {
|
|
2014
|
+
if (String(q.state).toUpperCase() === "REFERRED") {
|
|
2015
|
+
return q.resultStatus === "needs_info" ? "needs_info" : "referred";
|
|
2016
|
+
}
|
|
2017
|
+
return q.marketStatus || q.state;
|
|
2018
|
+
}
|
|
1759
2019
|
function sortQuoteRows(quotes) {
|
|
1760
2020
|
return quotes.map((q) => ({
|
|
1761
2021
|
carrier: q.carrierName || q.carrierId,
|
|
1762
2022
|
coverage: q.coverage ?? "",
|
|
1763
2023
|
premium: q.premium,
|
|
1764
|
-
status: q
|
|
2024
|
+
status: rowStatus(q),
|
|
1765
2025
|
bindable: q.bindable === true && q.state === "QUOTED",
|
|
1766
|
-
opportunityId: q.opportunityId
|
|
2026
|
+
opportunityId: q.opportunityId,
|
|
2027
|
+
limits: typeof q.limits === "string" && q.limits.trim() ? q.limits : null,
|
|
2028
|
+
deductible: typeof q.deductible === "number" && Number.isFinite(q.deductible) ? q.deductible : null,
|
|
2029
|
+
message: typeof q.message === "string" && q.message.trim() ? q.message.trim() : null
|
|
1767
2030
|
})).sort((a, b) => {
|
|
1768
2031
|
if (a.bindable !== b.bindable) return a.bindable ? -1 : 1;
|
|
1769
2032
|
if (a.premium === null && b.premium === null) return 0;
|
|
@@ -1772,8 +2035,50 @@ function sortQuoteRows(quotes) {
|
|
|
1772
2035
|
return a.premium - b.premium;
|
|
1773
2036
|
});
|
|
1774
2037
|
}
|
|
1775
|
-
function
|
|
1776
|
-
|
|
2038
|
+
function bestPerCoverage(rows) {
|
|
2039
|
+
const best = /* @__PURE__ */ new Map();
|
|
2040
|
+
for (const r of rows) {
|
|
2041
|
+
if (!r.bindable || r.premium === null) continue;
|
|
2042
|
+
const key = coverageName(r.coverage);
|
|
2043
|
+
const cur = best.get(key);
|
|
2044
|
+
if (!cur || r.premium < cur.premium) best.set(key, r);
|
|
2045
|
+
}
|
|
2046
|
+
return [...best.values()].sort((a, b) => a.premium - b.premium);
|
|
2047
|
+
}
|
|
2048
|
+
function groupRowsByCoverage(rows) {
|
|
2049
|
+
const order = /* @__PURE__ */ new Map();
|
|
2050
|
+
rows.forEach((r) => {
|
|
2051
|
+
const key = coverageName(r.coverage);
|
|
2052
|
+
if (!order.has(key)) order.set(key, order.size);
|
|
2053
|
+
});
|
|
2054
|
+
const rank = (r) => order.get(coverageName(r.coverage));
|
|
2055
|
+
return rows.map((r, i) => ({ r, i })).sort((a, b) => rank(a.r) - rank(b.r) || a.i - b.i).map((x) => x.r);
|
|
2056
|
+
}
|
|
2057
|
+
var STATUS_WORDS = {
|
|
2058
|
+
quote: "Quoted",
|
|
2059
|
+
quoted: "Quoted",
|
|
2060
|
+
declined: "Declined",
|
|
2061
|
+
decline: "Declined",
|
|
2062
|
+
quoting: "Pricing",
|
|
2063
|
+
pending: "Pricing",
|
|
2064
|
+
submitted: "Pricing",
|
|
2065
|
+
in_progress: "Pricing",
|
|
2066
|
+
referred: "Referred to an underwriter",
|
|
2067
|
+
refer: "Referred to an underwriter",
|
|
2068
|
+
needs_info: "Needs more info",
|
|
2069
|
+
awaiting_broker_review: "With a broker",
|
|
2070
|
+
broker_review: "With a broker",
|
|
2071
|
+
bind_requested: "Bind requested",
|
|
2072
|
+
bound: "Bound",
|
|
2073
|
+
expired: "Expired",
|
|
2074
|
+
error: "Failed",
|
|
2075
|
+
failed: "Failed"
|
|
2076
|
+
};
|
|
2077
|
+
function quoteStatusWord(status) {
|
|
2078
|
+
const key = status.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
2079
|
+
if (STATUS_WORDS[key]) return STATUS_WORDS[key];
|
|
2080
|
+
const words = key.replace(/_/g, " ").trim();
|
|
2081
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : status;
|
|
1777
2082
|
}
|
|
1778
2083
|
function summarizeStatus(s) {
|
|
1779
2084
|
const quoted = s.carrierQuotes.filter((q) => q.premium !== null).length;
|
|
@@ -1823,7 +2128,14 @@ function formatKnown(q, value) {
|
|
|
1823
2128
|
}
|
|
1824
2129
|
|
|
1825
2130
|
// src/commerce/demo.ts
|
|
1826
|
-
var DEMO_BUSINESS = "Riverbend Bakery (demo), Austin TX
|
|
2131
|
+
var DEMO_BUSINESS = "Riverbend Bakery (demo), Austin TX";
|
|
2132
|
+
var DEMO_SHORT_LABELS = {
|
|
2133
|
+
annual_revenue: "Annual revenue",
|
|
2134
|
+
employees: "Full-time employees",
|
|
2135
|
+
delivery: "Delivers orders",
|
|
2136
|
+
delivery_vehicles: "Delivery vehicles",
|
|
2137
|
+
claims_5y: "Claims in last 5 years"
|
|
2138
|
+
};
|
|
1827
2139
|
var DEMO_QUESTIONS = [
|
|
1828
2140
|
{ id: "annual_revenue", label: "Annual revenue (USD)", type: "currency", required: true, prefill: 85e4 },
|
|
1829
2141
|
{ id: "employees", label: "Full-time employees", type: "number", required: true, prefill: 6 },
|
|
@@ -1857,6 +2169,7 @@ var DEMO_QUESTIONS = [
|
|
|
1857
2169
|
];
|
|
1858
2170
|
var DEMO_STATUS = {
|
|
1859
2171
|
submissionId: "demo-submission",
|
|
2172
|
+
effectiveDate: "2026-11-01",
|
|
1860
2173
|
coverages: ["general_liability", "workers_comp"],
|
|
1861
2174
|
expiresAt: null,
|
|
1862
2175
|
submitted: true,
|
|
@@ -1864,19 +2177,19 @@ var DEMO_STATUS = {
|
|
|
1864
2177
|
allCarriersResolved: true,
|
|
1865
2178
|
awaitingBrokerReviewCount: 0,
|
|
1866
2179
|
carrierQuotes: [
|
|
1867
|
-
{ opportunityId: "demo-1", carrierId: "CARRIER_A", carrierName: "Carrier A", coverage: "CGL", state: "QUOTED", marketStatus: "quote", premium: 612, quoteNumber: "DEMO-GL-1", bindable: true },
|
|
1868
|
-
{ opportunityId: "demo-2", carrierId: "CARRIER_B", carrierName: "Carrier B", coverage: "CGL", state: "QUOTED", marketStatus: "quote", premium: 748, quoteNumber: "DEMO-GL-2", bindable: true },
|
|
1869
|
-
{ opportunityId: "demo-3", carrierId: "CARRIER_C", carrierName: "Carrier C", coverage: "WC", state: "QUOTED", marketStatus: "quote", premium: 1184, quoteNumber: "DEMO-WC-1", bindable: true },
|
|
1870
|
-
{ opportunityId: "demo-4", carrierId: "CARRIER_D", carrierName: "Carrier D", coverage: "WC", state: "DECLINED", marketStatus: "declined", premium: null, quoteNumber: null, bindable: false }
|
|
2180
|
+
{ 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 },
|
|
2181
|
+
{ 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 },
|
|
2182
|
+
{ 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 },
|
|
2183
|
+
{ opportunityId: "demo-4", carrierId: "CARRIER_D", carrierName: "Carrier D", coverage: "WC", state: "DECLINED", marketStatus: "declined", premium: null, quoteNumber: null, bindable: false, limits: null, deductible: null }
|
|
1871
2184
|
]
|
|
1872
2185
|
};
|
|
1873
2186
|
|
|
1874
2187
|
// src/commands/instant-quote.ts
|
|
1875
2188
|
function activeRequestLines(err, coverageFlag) {
|
|
1876
|
-
if (!(err instanceof
|
|
2189
|
+
if (!(err instanceof import_sdk5.DeleganceError) || err.statusCode !== 409 || !err.body) return null;
|
|
1877
2190
|
const { body } = err;
|
|
1878
2191
|
const code = body.message;
|
|
1879
|
-
const requote =
|
|
2192
|
+
const requote = `${cliInvocation()} quote start --coverage ${coverageFlag} --replace`;
|
|
1880
2193
|
if (code === "active_request_exists") {
|
|
1881
2194
|
const active = Array.isArray(body.active) ? body.active : [];
|
|
1882
2195
|
const first = active[0];
|
|
@@ -1884,7 +2197,7 @@ function activeRequestLines(err, coverageFlag) {
|
|
|
1884
2197
|
const carriers = Array.isArray(first?.carriers) ? first.carriers.map((c) => `${c.carrierId}${c.state ? ` (${c.state.toLowerCase()})` : ""}`).join(", ") : "";
|
|
1885
2198
|
return [
|
|
1886
2199
|
`Already with carriers${id ? `: ${id}` : ""}${carriers ? `, ${carriers}` : ""}.`,
|
|
1887
|
-
`Check it: ${id ?
|
|
2200
|
+
`Check it: ${id ? `${cliInvocation()} quote status ${id}` : `${cliInvocation()} quote status <id>`} Replace it: ${requote}`
|
|
1888
2201
|
];
|
|
1889
2202
|
}
|
|
1890
2203
|
if (code === "replace_conflict") {
|
|
@@ -1936,7 +2249,7 @@ async function askOne(q, answers, fallback) {
|
|
|
1936
2249
|
}
|
|
1937
2250
|
};
|
|
1938
2251
|
}
|
|
1939
|
-
const { v } = await
|
|
2252
|
+
const { v } = await import_inquirer6.default.prompt([prompt]);
|
|
1940
2253
|
const c = coerceAnswer(q, v);
|
|
1941
2254
|
if (c.ok && c.value !== void 0) answers[q.id] = c.value;
|
|
1942
2255
|
}
|
|
@@ -1947,7 +2260,7 @@ async function askQuestions(questions, answers = {}) {
|
|
|
1947
2260
|
const round = planQuestionRound(questions, answers, handled);
|
|
1948
2261
|
if (round.confirm.length + round.checklist.length + round.ask.length === 0) break;
|
|
1949
2262
|
if (round.confirm.length) {
|
|
1950
|
-
const { keep } = await
|
|
2263
|
+
const { keep } = await import_inquirer6.default.prompt([
|
|
1951
2264
|
{
|
|
1952
2265
|
type: "checkbox",
|
|
1953
2266
|
name: "keep",
|
|
@@ -1976,7 +2289,7 @@ async function askQuestions(questions, answers = {}) {
|
|
|
1976
2289
|
}
|
|
1977
2290
|
}
|
|
1978
2291
|
if (round.checklist.length) {
|
|
1979
|
-
const { yes } = await
|
|
2292
|
+
const { yes } = await import_inquirer6.default.prompt([
|
|
1980
2293
|
{
|
|
1981
2294
|
type: "checkbox",
|
|
1982
2295
|
name: "yes",
|
|
@@ -1987,7 +2300,7 @@ async function askQuestions(questions, answers = {}) {
|
|
|
1987
2300
|
]);
|
|
1988
2301
|
const yn = checklistAnswers(round.checklist, yes);
|
|
1989
2302
|
const yesCount = Object.values(yn).filter((v) => v === "yes").length;
|
|
1990
|
-
const { ok } = await
|
|
2303
|
+
const { ok } = await import_inquirer6.default.prompt([
|
|
1991
2304
|
{ type: "confirm", name: "ok", message: `Yes to ${yesCount}, No to ${round.checklist.length - yesCount}. Correct?`, default: true }
|
|
1992
2305
|
]);
|
|
1993
2306
|
if (ok) {
|
|
@@ -2011,7 +2324,7 @@ function handoffLines(r) {
|
|
|
2011
2324
|
if (!r.alreadyHandedOff && r.unansweredRequired > 0) {
|
|
2012
2325
|
lines.push(`The broker will email you for the ${r.unansweredRequired} answer(s) still missing.`);
|
|
2013
2326
|
}
|
|
2014
|
-
lines.push(`Check it:
|
|
2327
|
+
lines.push(`Check it: ${cliInvocation()} quote status ${r.submissionId}`);
|
|
2015
2328
|
return lines;
|
|
2016
2329
|
}
|
|
2017
2330
|
async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
@@ -2021,7 +2334,7 @@ async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
|
2021
2334
|
try {
|
|
2022
2335
|
plan = await client.instantQuotes.brokerHandoffQuestions(submissionId, instantAnswers);
|
|
2023
2336
|
} catch (err) {
|
|
2024
|
-
if (err instanceof
|
|
2337
|
+
if (err instanceof import_sdk5.DeleganceError && err.statusCode === 503) {
|
|
2025
2338
|
say("A broker can still market it: reply to the confirmation email, or start in the portal.");
|
|
2026
2339
|
return;
|
|
2027
2340
|
}
|
|
@@ -2052,7 +2365,7 @@ async function runBrokerHandoff(submissionId, instantAnswers, opts) {
|
|
|
2052
2365
|
process.exit(1);
|
|
2053
2366
|
}
|
|
2054
2367
|
} else if (!opts.json && isInteractive() && plan.questions.length > 0) {
|
|
2055
|
-
info(`A broker can place ${plan.coverages
|
|
2368
|
+
info(`A broker can place ${coverageNames(plan.coverages)} with a market that quotes by hand. ${plan.questions.length} application question(s) left.`);
|
|
2056
2369
|
info("Press Ctrl-C to stop at any point; what you have answered goes to the broker.");
|
|
2057
2370
|
const onInterrupt = () => {
|
|
2058
2371
|
complete = false;
|
|
@@ -2084,7 +2397,7 @@ async function sendHandoff(submissionId, input, idempotencyKey, asJson) {
|
|
|
2084
2397
|
else handoffLines(result).forEach((l) => info(l));
|
|
2085
2398
|
} catch (err) {
|
|
2086
2399
|
spinner?.fail("Could not reach a broker");
|
|
2087
|
-
console.error(`Try again:
|
|
2400
|
+
console.error(`Try again: ${cliInvocation()} quote handoff ${submissionId}`);
|
|
2088
2401
|
failWith("API error", err);
|
|
2089
2402
|
}
|
|
2090
2403
|
}
|
|
@@ -2095,8 +2408,34 @@ function printFloorDrops(failures) {
|
|
|
2095
2408
|
floors.forEach((f) => console.log(` - ${f.carrierId}: ${f.missing}`));
|
|
2096
2409
|
info("Add it to your business profile in the portal, then run the quote again.");
|
|
2097
2410
|
}
|
|
2411
|
+
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.";
|
|
2412
|
+
var premiumText = (n) => n === null ? "-" : formatCurrency(n);
|
|
2413
|
+
var deductibleText = (n) => n === null ? "-" : n === 0 ? "None" : formatCurrency(n);
|
|
2414
|
+
function bestPriceLines(best) {
|
|
2415
|
+
if (best.length === 0) return [];
|
|
2416
|
+
const names = best.map((r) => coverageName(r.coverage));
|
|
2417
|
+
const nameW = Math.max(...names.map((n) => n.length));
|
|
2418
|
+
const carrierW = Math.max(...best.map((r) => r.carrier.length));
|
|
2419
|
+
return [
|
|
2420
|
+
"Best price per coverage:",
|
|
2421
|
+
...best.map((r, i) => ` ${names[i].padEnd(nameW)} ${r.carrier.padEnd(carrierW)} ${formatCurrency(r.premium)}/yr`)
|
|
2422
|
+
];
|
|
2423
|
+
}
|
|
2424
|
+
function bindHintLines(submissionId, best) {
|
|
2425
|
+
if (best.length === 1) return [`Bind it: ${cliCommand(`bind ${submissionId} ${best[0].opportunityId}`)}`];
|
|
2426
|
+
return best.map((r) => `Bind ${coverageName(r.coverage)}: ${cliCommand(`bind ${submissionId} ${r.opportunityId}`)}`);
|
|
2427
|
+
}
|
|
2428
|
+
function demoClosingLines(best) {
|
|
2429
|
+
const example = best[0] ? ` (e.g. bind ... ${best[0].opportunityId})` : "";
|
|
2430
|
+
return [
|
|
2431
|
+
`Next, on a real quote: ${cliCommand("bind <quote id> <offer>")}${example}`,
|
|
2432
|
+
"DEMO: sample premiums, nothing went to a carrier.",
|
|
2433
|
+
`Get real quotes: ${cliCommand("signup")}`,
|
|
2434
|
+
AGENT_TIP
|
|
2435
|
+
];
|
|
2436
|
+
}
|
|
2098
2437
|
function printQuoteTable(status, opts = {}) {
|
|
2099
|
-
const rows = sortQuoteRows(status.carrierQuotes);
|
|
2438
|
+
const rows = groupRowsByCoverage(sortQuoteRows(status.carrierQuotes));
|
|
2100
2439
|
if (rows.length === 0) {
|
|
2101
2440
|
if (status.brokerReview) {
|
|
2102
2441
|
info(`A broker has this request (${status.brokerReview.status.replace(/_/g, " ")}). They place it by hand and email you with quotes or questions.`);
|
|
@@ -2106,25 +2445,67 @@ function printQuoteTable(status, opts = {}) {
|
|
|
2106
2445
|
if (status.awaitingBrokerReviewCount > 0) info(`${status.awaitingBrokerReviewCount} quote(s) are with a broker for review.`);
|
|
2107
2446
|
return;
|
|
2108
2447
|
}
|
|
2109
|
-
const best =
|
|
2110
|
-
|
|
2111
|
-
|
|
2448
|
+
const best = bestPerCoverage(rows);
|
|
2449
|
+
const bestIds = new Set(best.map((r) => r.opportunityId));
|
|
2450
|
+
const effectiveDate = status.effectiveDate;
|
|
2451
|
+
console.log();
|
|
2452
|
+
if (effectiveDate && opts.showStartDate !== false) {
|
|
2453
|
+
console.log(`Coverage starts ${formatDateOnly(effectiveDate)}`);
|
|
2454
|
+
}
|
|
2455
|
+
const bestLines = bestPriceLines(best);
|
|
2456
|
+
if (bestLines.length) {
|
|
2457
|
+
console.log(import_chalk2.default.bold(bestLines[0]));
|
|
2458
|
+
bestLines.slice(1).forEach((l) => console.log(l));
|
|
2459
|
+
console.log();
|
|
2112
2460
|
}
|
|
2113
2461
|
table(
|
|
2114
|
-
["Carrier", "Coverage", "Premium/yr", "
|
|
2462
|
+
["Carrier", "Coverage", "Premium/yr", "Limits", "Deductible", "Status", "Offer"],
|
|
2115
2463
|
rows.map((r) => {
|
|
2116
|
-
const cells = [
|
|
2117
|
-
|
|
2118
|
-
|
|
2464
|
+
const cells = [
|
|
2465
|
+
r.carrier,
|
|
2466
|
+
coverageName(r.coverage),
|
|
2467
|
+
premiumText(r.premium),
|
|
2468
|
+
r.limits ?? "-",
|
|
2469
|
+
deductibleText(r.deductible),
|
|
2470
|
+
quoteStatusWord(r.status),
|
|
2471
|
+
r.opportunityId
|
|
2472
|
+
];
|
|
2473
|
+
return bestIds.has(r.opportunityId) ? cells.map((c) => import_chalk2.default.bold(c)) : cells;
|
|
2474
|
+
}),
|
|
2475
|
+
{ compact: true }
|
|
2119
2476
|
);
|
|
2120
|
-
|
|
2121
|
-
if (
|
|
2122
|
-
console.log(
|
|
2477
|
+
const reasons = rows.filter((r) => r.message);
|
|
2478
|
+
if (reasons.length) {
|
|
2479
|
+
console.log();
|
|
2480
|
+
reasons.forEach((r) => console.log(`${r.carrier} (${quoteStatusWord(r.status)}): ${r.message}`));
|
|
2123
2481
|
}
|
|
2124
|
-
|
|
2482
|
+
console.log();
|
|
2483
|
+
console.log(import_chalk2.default.dim(`${summarizeStatus(status)}.`));
|
|
2484
|
+
if (opts.demo) {
|
|
2485
|
+
const [next, notice, signup, tip] = demoClosingLines(best);
|
|
2486
|
+
console.log(import_chalk2.default.green(next));
|
|
2487
|
+
console.log(import_chalk2.default.yellow(notice));
|
|
2488
|
+
console.log(import_chalk2.default.yellow(signup));
|
|
2489
|
+
console.log(import_chalk2.default.dim(tip));
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
bindHintLines(status.submissionId, best).forEach((l) => console.log(import_chalk2.default.green(l)));
|
|
2493
|
+
}
|
|
2494
|
+
function demoAnswerLines(answers, effectiveDate) {
|
|
2495
|
+
const pairs = [];
|
|
2496
|
+
for (const q of DEMO_QUESTIONS) {
|
|
2497
|
+
if (answers[q.id] === void 0) continue;
|
|
2498
|
+
const v = answers[q.id];
|
|
2499
|
+
const shown = q.type === "currency" && typeof v === "number" ? formatCurrency(v) : formatKnown(q, v);
|
|
2500
|
+
pairs.push([DEMO_SHORT_LABELS[q.id] ?? q.label, shown]);
|
|
2501
|
+
}
|
|
2502
|
+
if (effectiveDate) pairs.push(["Coverage starts", formatDateOnly(effectiveDate)]);
|
|
2503
|
+
const w = Math.max(...pairs.map(([k]) => k.length));
|
|
2504
|
+
return ["Sample answers (a real quote asks you these):", ...pairs.map(([k, v]) => ` ${k.padEnd(w)} ${v}`)];
|
|
2125
2505
|
}
|
|
2126
2506
|
async function runDemo(asJson, answersFile) {
|
|
2127
2507
|
(asJson ? console.error : console.log)(import_chalk2.default.yellow(`DEMO quote for ${DEMO_BUSINESS}. Nothing leaves this machine.`));
|
|
2508
|
+
let printedStart = false;
|
|
2128
2509
|
if (answersFile) {
|
|
2129
2510
|
let provided;
|
|
2130
2511
|
try {
|
|
@@ -2142,15 +2523,19 @@ async function runDemo(asJson, answersFile) {
|
|
|
2142
2523
|
const mode = answerMode({ demo: true, hasAnswersFile: false, json: asJson, isTTY: isInteractive() });
|
|
2143
2524
|
if (mode === "prompt") await askQuestions(DEMO_QUESTIONS);
|
|
2144
2525
|
else {
|
|
2145
|
-
suggestedAnswers(DEMO_QUESTIONS);
|
|
2146
|
-
if (!asJson)
|
|
2526
|
+
const assumed = suggestedAnswers(DEMO_QUESTIONS);
|
|
2527
|
+
if (!asJson) {
|
|
2528
|
+
demoAnswerLines(assumed, DEMO_STATUS.effectiveDate).forEach((l) => console.log(l));
|
|
2529
|
+
printedStart = true;
|
|
2530
|
+
}
|
|
2147
2531
|
}
|
|
2148
2532
|
}
|
|
2149
|
-
const spinner = asJson ? null : (0, import_ora6.default)("Asking carriers (demo)...")
|
|
2533
|
+
const spinner = asJson ? null : (0, import_ora6.default)("Asking carriers (demo)...");
|
|
2534
|
+
if (spinner && process.stderr.isTTY) spinner.start();
|
|
2150
2535
|
await new Promise((resolve) => setTimeout(resolve, asJson ? 0 : 1200));
|
|
2151
2536
|
spinner?.succeed("4 carriers answered (demo)");
|
|
2152
2537
|
if (asJson) json({ demo: true, ...DEMO_STATUS });
|
|
2153
|
-
else printQuoteTable(DEMO_STATUS, { demo: true });
|
|
2538
|
+
else printQuoteTable(DEMO_STATUS, { demo: true, showStartDate: !printedStart });
|
|
2154
2539
|
}
|
|
2155
2540
|
function addInstantQuoteCommands(quote) {
|
|
2156
2541
|
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) => {
|
|
@@ -2222,7 +2607,7 @@ function addInstantQuoteCommands(quote) {
|
|
|
2222
2607
|
process.exit(1);
|
|
2223
2608
|
}
|
|
2224
2609
|
submitSpinner.fail("Submit failed");
|
|
2225
|
-
console.error(`Before trying again, check whether it went through:
|
|
2610
|
+
console.error(`Before trying again, check whether it went through: ${cliInvocation()} quote status ${session.submissionId}`);
|
|
2226
2611
|
failWith("API error", err);
|
|
2227
2612
|
}
|
|
2228
2613
|
if (submitted.status === "no_carriers") {
|
|
@@ -2238,14 +2623,14 @@ function addInstantQuoteCommands(quote) {
|
|
|
2238
2623
|
}
|
|
2239
2624
|
if (submitted.status === "acord_pending") {
|
|
2240
2625
|
submitSpinner.succeed("Sent to a broker: these coverages need a person, not an instant quote.");
|
|
2241
|
-
info(`Track it with:
|
|
2626
|
+
info(`Track it with: ${cliInvocation()} quote status ${session.submissionId}`);
|
|
2242
2627
|
return;
|
|
2243
2628
|
}
|
|
2244
2629
|
submitSpinner.succeed(`Filed with ${submitted.quotes.filter((q) => q.status === "submitted").length} carrier(s).`);
|
|
2245
2630
|
printFloorDrops(submitted.partialFailures);
|
|
2246
2631
|
if (submitted.replaceNotice && !options.json) info(submitted.replaceNotice);
|
|
2247
2632
|
if (options.wait === false) {
|
|
2248
|
-
info(`Check premiums with:
|
|
2633
|
+
info(`Check premiums with: ${cliInvocation()} quote status ${session.submissionId} --wait`);
|
|
2249
2634
|
return;
|
|
2250
2635
|
}
|
|
2251
2636
|
await waitAndPrint(session.submissionId, Number(options.timeout) * 6e4 || 6e5, !!options.json);
|
|
@@ -2274,11 +2659,11 @@ function addInstantQuoteCommands(quote) {
|
|
|
2274
2659
|
}
|
|
2275
2660
|
if (submitted.status !== "submitted") {
|
|
2276
2661
|
info("Sent to a broker for these coverages.");
|
|
2277
|
-
info(`Check it:
|
|
2662
|
+
info(`Check it: ${cliInvocation()} quote status ${submissionId}`);
|
|
2278
2663
|
return;
|
|
2279
2664
|
}
|
|
2280
2665
|
if (options.wait === false) {
|
|
2281
|
-
info(`Check premiums with:
|
|
2666
|
+
info(`Check premiums with: ${cliInvocation()} quote status ${submissionId} --wait`);
|
|
2282
2667
|
return;
|
|
2283
2668
|
}
|
|
2284
2669
|
await waitAndPrint(submissionId, Number(options.timeout) * 6e4 || 6e5, !!options.json);
|
|
@@ -2289,7 +2674,7 @@ function addInstantQuoteCommands(quote) {
|
|
|
2289
2674
|
guardLines.forEach((l) => options.json ? console.error(l) : info(l));
|
|
2290
2675
|
process.exit(1);
|
|
2291
2676
|
}
|
|
2292
|
-
console.error(`Before trying again, check whether it went through:
|
|
2677
|
+
console.error(`Before trying again, check whether it went through: ${cliInvocation()} quote status ${submissionId}`);
|
|
2293
2678
|
failWith("Submit failed", err);
|
|
2294
2679
|
}
|
|
2295
2680
|
});
|
|
@@ -2325,7 +2710,7 @@ async function waitAndPrint(submissionId, timeoutMs, asJson) {
|
|
|
2325
2710
|
spinner?.stop();
|
|
2326
2711
|
if (asJson) json(status);
|
|
2327
2712
|
else printQuoteTable(status);
|
|
2328
|
-
if (!status.allCarriersResolved && !asJson) info(`Some carriers are still working. Check again:
|
|
2713
|
+
if (!status.allCarriersResolved && !asJson) info(`Some carriers are still working. Check again: ${cliInvocation()} quote status ${submissionId} --wait`);
|
|
2329
2714
|
} catch (err) {
|
|
2330
2715
|
spinner?.fail("Could not read the quote");
|
|
2331
2716
|
failWith("API error", err);
|
|
@@ -2335,7 +2720,7 @@ async function waitAndPrint(submissionId, timeoutMs, asJson) {
|
|
|
2335
2720
|
// src/commands/quote.ts
|
|
2336
2721
|
var QUOTE_SUBMISSION_UNAVAILABLE = "Quote submission via API isn't available yet";
|
|
2337
2722
|
function createQuoteCommand() {
|
|
2338
|
-
const quote = new
|
|
2723
|
+
const quote = new import_commander7.Command("quote").alias("quotes").description("Request and manage insurance quotes");
|
|
2339
2724
|
quote.command("carriers").description("List available carriers for quoting").option("--json", "Output as JSON").action(async (options) => {
|
|
2340
2725
|
const client = getClient();
|
|
2341
2726
|
const spinner = (0, import_ora7.default)("Fetching carriers...").start();
|
|
@@ -2382,7 +2767,7 @@ function createQuoteCommand() {
|
|
|
2382
2767
|
process.exit(1);
|
|
2383
2768
|
}
|
|
2384
2769
|
if (options.interactive || carrierIds.length === 0 && !options.all) {
|
|
2385
|
-
const { selectedCarriers } = await
|
|
2770
|
+
const { selectedCarriers } = await import_inquirer7.default.prompt([
|
|
2386
2771
|
{
|
|
2387
2772
|
type: "checkbox",
|
|
2388
2773
|
name: "selectedCarriers",
|
|
@@ -2438,13 +2823,13 @@ function createQuoteCommand() {
|
|
|
2438
2823
|
}
|
|
2439
2824
|
console.log();
|
|
2440
2825
|
info(`Queue IDs: ${result.queueIds.join(", ")}`);
|
|
2441
|
-
info(
|
|
2826
|
+
info(`Use "${cliInvocation()} quote list" to check status`);
|
|
2442
2827
|
}
|
|
2443
2828
|
} catch (err) {
|
|
2444
2829
|
spinner.fail("Failed to submit quote request");
|
|
2445
|
-
if (err instanceof
|
|
2830
|
+
if (err instanceof import_sdk6.NotImplementedError) {
|
|
2446
2831
|
error(QUOTE_SUBMISSION_UNAVAILABLE);
|
|
2447
|
-
info(
|
|
2832
|
+
info(`For an instant quote from the terminal: ${cliInvocation()} quote start --coverage gl`);
|
|
2448
2833
|
info(`Or start a quote at ${PORTAL_URL}`);
|
|
2449
2834
|
process.exit(1);
|
|
2450
2835
|
}
|
|
@@ -2575,11 +2960,11 @@ function formatQuoteStatus(status) {
|
|
|
2575
2960
|
}
|
|
2576
2961
|
|
|
2577
2962
|
// src/commands/policy.ts
|
|
2578
|
-
var
|
|
2579
|
-
var
|
|
2963
|
+
var import_commander8 = require("commander");
|
|
2964
|
+
var import_inquirer8 = __toESM(require("inquirer"));
|
|
2580
2965
|
var import_ora8 = __toESM(require("ora"));
|
|
2581
2966
|
function createPolicyCommand() {
|
|
2582
|
-
const policy = new
|
|
2967
|
+
const policy = new import_commander8.Command("policy").alias("policies").description("Manage insurance policies");
|
|
2583
2968
|
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) => {
|
|
2584
2969
|
const client = getClient();
|
|
2585
2970
|
const spinner = (0, import_ora8.default)("Fetching policies...").start();
|
|
@@ -2602,7 +2987,7 @@ function createPolicyCommand() {
|
|
|
2602
2987
|
["Carrier", "Type", "Policy #", "Expiry", "Status", "Premium"],
|
|
2603
2988
|
policies.map((p) => [
|
|
2604
2989
|
p.carrier || "Unknown",
|
|
2605
|
-
p.policyType
|
|
2990
|
+
(p.policyType ? coverageName(p.policyType) : p.name) || "-",
|
|
2606
2991
|
p.policyNumber || "-",
|
|
2607
2992
|
p.expiryDate ? formatDate(p.expiryDate) : "-",
|
|
2608
2993
|
formatPolicyStatus(p.isActive, p.daysUntilExpiry),
|
|
@@ -2636,21 +3021,21 @@ function createPolicyCommand() {
|
|
|
2636
3021
|
if (urgent.length > 0) {
|
|
2637
3022
|
warn(`\u26A0\uFE0F Expiring within 7 days (${urgent.length}):`);
|
|
2638
3023
|
for (const p of urgent) {
|
|
2639
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
3024
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - expires ${formatDate(p.expiryDate)}`);
|
|
2640
3025
|
}
|
|
2641
3026
|
console.log();
|
|
2642
3027
|
}
|
|
2643
3028
|
if (soon.length > 0) {
|
|
2644
3029
|
info(`\u{1F4C5} Expiring within 30 days (${soon.length}):`);
|
|
2645
3030
|
for (const p of soon) {
|
|
2646
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
3031
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - expires ${formatDate(p.expiryDate)}`);
|
|
2647
3032
|
}
|
|
2648
3033
|
console.log();
|
|
2649
3034
|
}
|
|
2650
3035
|
if (later.length > 0) {
|
|
2651
3036
|
console.log(`\u{1F4C6} Expiring later (${later.length}):`);
|
|
2652
3037
|
for (const p of later) {
|
|
2653
|
-
console.log(` - ${p.carrier} ${p.policyType
|
|
3038
|
+
console.log(` - ${p.carrier} ${p.policyType ? coverageName(p.policyType) : p.name} - ${p.daysUntilExpiry} days`);
|
|
2654
3039
|
}
|
|
2655
3040
|
}
|
|
2656
3041
|
}
|
|
@@ -2674,11 +3059,11 @@ function createPolicyCommand() {
|
|
|
2674
3059
|
} else {
|
|
2675
3060
|
const { policy: p, coverages } = found;
|
|
2676
3061
|
console.log();
|
|
2677
|
-
header(`${p.carrier} - ${p.policyType
|
|
3062
|
+
header(`${p.carrier} - ${p.policyType ? coverageName(p.policyType) : p.name}`);
|
|
2678
3063
|
console.log();
|
|
2679
3064
|
console.log(` Policy #: ${p.policyNumber || "N/A"}`);
|
|
2680
3065
|
console.log(` Carrier: ${p.carrier}`);
|
|
2681
|
-
console.log(` Type: ${p.policyType
|
|
3066
|
+
console.log(` Type: ${p.policyType ? coverageName(p.policyType) : "N/A"}`);
|
|
2682
3067
|
console.log(` Effective: ${p.effectiveDate ? formatDate(p.effectiveDate) : "N/A"}`);
|
|
2683
3068
|
console.log(` Expiry: ${p.expiryDate ? formatDate(p.expiryDate) : "N/A"}`);
|
|
2684
3069
|
console.log(` Status: ${formatPolicyStatus(p.isActive, p.daysUntilExpiry)}`);
|
|
@@ -2729,14 +3114,14 @@ function createPolicyCommand() {
|
|
|
2729
3114
|
return;
|
|
2730
3115
|
}
|
|
2731
3116
|
if (docs.length === 0) {
|
|
2732
|
-
info(
|
|
3117
|
+
info(`No source documents are linked to this policy yet. Upload one with: ${cliInvocation()} document upload <file>`);
|
|
2733
3118
|
return;
|
|
2734
3119
|
}
|
|
2735
3120
|
table(
|
|
2736
3121
|
["Document", "File", "Type", "Status", "Added"],
|
|
2737
3122
|
docs.map((d) => [d.id, d.fileName ?? "-", d.documentType ?? "-", d.status, d.createdAt ? formatDate(d.createdAt) : "-"])
|
|
2738
3123
|
);
|
|
2739
|
-
info(
|
|
3124
|
+
info(`Download one with: ${cliInvocation()} document status <documentId>`);
|
|
2740
3125
|
} catch (err) {
|
|
2741
3126
|
failWith("API error", err);
|
|
2742
3127
|
}
|
|
@@ -2769,9 +3154,9 @@ function createPolicyCommand() {
|
|
|
2769
3154
|
await signWithStepUp(client, out.request.id, sig, void 0, void 0);
|
|
2770
3155
|
} else {
|
|
2771
3156
|
info(`Sign here: ${out.signatureUrl}`);
|
|
2772
|
-
info(`Or sign from here:
|
|
3157
|
+
info(`Or sign from here: ${cliInvocation()} policy sign ${out.request.id} --signer-name "..." --signer-title "..." --attest`);
|
|
2773
3158
|
}
|
|
2774
|
-
info(`Check progress with "
|
|
3159
|
+
info(`Check progress with "${cliInvocation()} policy cancellation ${out.request.id}"`);
|
|
2775
3160
|
} catch (err) {
|
|
2776
3161
|
spinner.fail("Could not request the cancellation");
|
|
2777
3162
|
failWith("API error", err);
|
|
@@ -2828,10 +3213,10 @@ async function signWithStepUp(client, requestId, sig, stepUpId, presetCode) {
|
|
|
2828
3213
|
let code = presetCode;
|
|
2829
3214
|
if (!code) {
|
|
2830
3215
|
if (!process.stdin.isTTY) {
|
|
2831
|
-
warn(`No terminal to type the code in. Finish with:
|
|
3216
|
+
warn(`No terminal to type the code in. Finish with: ${cliInvocation()} policy sign ${requestId} --signer-name "${sig.signerName}" --signer-title "${sig.signerTitle}" --attest --step-up ${id} --code <code>`);
|
|
2832
3217
|
return;
|
|
2833
3218
|
}
|
|
2834
|
-
const answer = await
|
|
3219
|
+
const answer = await import_inquirer8.default.prompt([{
|
|
2835
3220
|
type: "input",
|
|
2836
3221
|
name: "code",
|
|
2837
3222
|
message: "Code from your email:",
|
|
@@ -2858,11 +3243,11 @@ function formatPolicyStatus(isActive, daysUntilExpiry) {
|
|
|
2858
3243
|
}
|
|
2859
3244
|
|
|
2860
3245
|
// src/commands/coi.ts
|
|
2861
|
-
var
|
|
3246
|
+
var import_commander9 = require("commander");
|
|
2862
3247
|
var import_ora9 = __toESM(require("ora"));
|
|
2863
|
-
var
|
|
3248
|
+
var import_inquirer9 = __toESM(require("inquirer"));
|
|
2864
3249
|
function createCOICommand() {
|
|
2865
|
-
const coi = new
|
|
3250
|
+
const coi = new import_commander9.Command("coi").description("Generate and manage Certificates of Insurance");
|
|
2866
3251
|
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) => {
|
|
2867
3252
|
const client = getClient();
|
|
2868
3253
|
let policyIds = options.policies || [];
|
|
@@ -2878,16 +3263,16 @@ function createCOICommand() {
|
|
|
2878
3263
|
policiesSpinner.stop();
|
|
2879
3264
|
if (policies.length === 0) {
|
|
2880
3265
|
error("No active policies found");
|
|
2881
|
-
info(
|
|
3266
|
+
info(`Upload policy documents first with "${cliInvocation()} doc upload"`);
|
|
2882
3267
|
process.exit(1);
|
|
2883
3268
|
}
|
|
2884
|
-
const answers = await
|
|
3269
|
+
const answers = await import_inquirer9.default.prompt([
|
|
2885
3270
|
{
|
|
2886
3271
|
type: "checkbox",
|
|
2887
3272
|
name: "selectedPolicies",
|
|
2888
3273
|
message: "Select policies to include:",
|
|
2889
3274
|
choices: policies.map((p) => ({
|
|
2890
|
-
name: `${p.carrier} - ${p.policyType
|
|
3275
|
+
name: `${p.carrier} - ${p.policyType ? coverageName(p.policyType) : p.name} (${p.policyNumber || "No #"})`,
|
|
2891
3276
|
value: p.id,
|
|
2892
3277
|
checked: policyIds.includes(p.id)
|
|
2893
3278
|
})),
|
|
@@ -2974,7 +3359,7 @@ function createCOICommand() {
|
|
|
2974
3359
|
console.log();
|
|
2975
3360
|
console.log("Policies included:");
|
|
2976
3361
|
for (const p of result.policiesIncluded) {
|
|
2977
|
-
console.log(` - ${p.carrier} (${p.policyType})`);
|
|
3362
|
+
console.log(` - ${p.carrier} (${coverageName(p.policyType)})`);
|
|
2978
3363
|
}
|
|
2979
3364
|
}
|
|
2980
3365
|
} catch (err) {
|
|
@@ -2990,7 +3375,7 @@ function createCOICommand() {
|
|
|
2990
3375
|
spinner.succeed(`Found ${count} COIs`);
|
|
2991
3376
|
if (cois.length === 0) {
|
|
2992
3377
|
info("No COIs generated yet");
|
|
2993
|
-
info(
|
|
3378
|
+
info(`Generate one with "${cliInvocation()} coi generate --interactive"`);
|
|
2994
3379
|
return;
|
|
2995
3380
|
}
|
|
2996
3381
|
if (options.json) {
|
|
@@ -3017,12 +3402,12 @@ function createCOICommand() {
|
|
|
3017
3402
|
}
|
|
3018
3403
|
|
|
3019
3404
|
// src/commands/sov.ts
|
|
3020
|
-
var
|
|
3405
|
+
var import_commander10 = require("commander");
|
|
3021
3406
|
var import_ora10 = __toESM(require("ora"));
|
|
3022
3407
|
var import_fs = __toESM(require("fs"));
|
|
3023
3408
|
var import_path = __toESM(require("path"));
|
|
3024
3409
|
function createSOVCommand() {
|
|
3025
|
-
const sov = new
|
|
3410
|
+
const sov = new import_commander10.Command("sov").description("Manage Schedules of Values (property schedules)");
|
|
3026
3411
|
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) => {
|
|
3027
3412
|
const client = getClient();
|
|
3028
3413
|
const filePath = import_path.default.resolve(file);
|
|
@@ -3067,7 +3452,7 @@ function createSOVCommand() {
|
|
|
3067
3452
|
info(`SOV ID: ${result.sovId}`);
|
|
3068
3453
|
info(`Status: ${result.status}`);
|
|
3069
3454
|
if (!options.wait) {
|
|
3070
|
-
info(
|
|
3455
|
+
info(`Use "${cliInvocation()} sov status <id>" to check parsing progress`);
|
|
3071
3456
|
}
|
|
3072
3457
|
}
|
|
3073
3458
|
} catch (err) {
|
|
@@ -3086,7 +3471,7 @@ function createSOVCommand() {
|
|
|
3086
3471
|
spinner.succeed(`Found ${count} SOVs`);
|
|
3087
3472
|
if (sovs.length === 0) {
|
|
3088
3473
|
info("No SOVs found");
|
|
3089
|
-
info(
|
|
3474
|
+
info(`Upload one with "${cliInvocation()} sov upload schedule.xlsx"`);
|
|
3090
3475
|
return;
|
|
3091
3476
|
}
|
|
3092
3477
|
if (options.json) {
|
|
@@ -3180,7 +3565,7 @@ function showSOVDetails(s) {
|
|
|
3180
3565
|
}
|
|
3181
3566
|
|
|
3182
3567
|
// src/commands/appetite.ts
|
|
3183
|
-
var
|
|
3568
|
+
var import_commander11 = require("commander");
|
|
3184
3569
|
var import_ora11 = __toESM(require("ora"));
|
|
3185
3570
|
function num(value) {
|
|
3186
3571
|
if (value === void 0) return void 0;
|
|
@@ -3228,7 +3613,7 @@ function printResult(result) {
|
|
|
3228
3613
|
for (const w of result.warnings) warn(w);
|
|
3229
3614
|
}
|
|
3230
3615
|
function createAppetiteCommand() {
|
|
3231
|
-
const appetite = new
|
|
3616
|
+
const appetite = new import_commander11.Command("appetite").description("Check which carriers will write a risk");
|
|
3232
3617
|
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) => {
|
|
3233
3618
|
const input = buildMatchInput(options);
|
|
3234
3619
|
const timeoutMs = positiveInt(options.timeout, "--timeout");
|
|
@@ -3250,7 +3635,7 @@ function createAppetiteCommand() {
|
|
|
3250
3635
|
if (!options.wait) {
|
|
3251
3636
|
spinner.succeed(`Match queued: ${start.jobId}`);
|
|
3252
3637
|
if (options.json) json(start);
|
|
3253
|
-
else info(`Check it with "
|
|
3638
|
+
else info(`Check it with "${cliInvocation()} appetite status ${start.jobId}"`);
|
|
3254
3639
|
return;
|
|
3255
3640
|
}
|
|
3256
3641
|
spinner.text = `Matching appetite (about ${start.estimatedSeconds}s)...`;
|
|
@@ -3289,7 +3674,7 @@ function createAppetiteCommand() {
|
|
|
3289
3674
|
}
|
|
3290
3675
|
|
|
3291
3676
|
// src/commands/lead.ts
|
|
3292
|
-
var
|
|
3677
|
+
var import_commander12 = require("commander");
|
|
3293
3678
|
var import_ora12 = __toESM(require("ora"));
|
|
3294
3679
|
function buildLeadInput(options) {
|
|
3295
3680
|
const input = {
|
|
@@ -3320,7 +3705,7 @@ function buildLeadInput(options) {
|
|
|
3320
3705
|
return input;
|
|
3321
3706
|
}
|
|
3322
3707
|
function createLeadCommand() {
|
|
3323
|
-
const lead = new
|
|
3708
|
+
const lead = new import_commander12.Command("lead").alias("leads").description("Send prospect businesses to Delegance");
|
|
3324
3709
|
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) => {
|
|
3325
3710
|
const input = buildLeadInput(options);
|
|
3326
3711
|
if (input.address && (!input.address.street || !input.address.city || !input.address.state || !input.address.zip)) {
|
|
@@ -3343,11 +3728,11 @@ function createLeadCommand() {
|
|
|
3343
3728
|
}
|
|
3344
3729
|
|
|
3345
3730
|
// src/commands/knowledge.ts
|
|
3346
|
-
var
|
|
3731
|
+
var import_commander13 = require("commander");
|
|
3347
3732
|
var import_ora13 = __toESM(require("ora"));
|
|
3348
3733
|
var SEARCH_TYPES = ["notecard", "document_chunk"];
|
|
3349
3734
|
function createKnowledgeCommand() {
|
|
3350
|
-
const knowledge = new
|
|
3735
|
+
const knowledge = new import_commander13.Command("knowledge").alias("kb").description("Search notecards and document facts");
|
|
3351
3736
|
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) => {
|
|
3352
3737
|
const client = getClient();
|
|
3353
3738
|
const query = queryParts.join(" ");
|
|
@@ -3396,7 +3781,7 @@ function createKnowledgeCommand() {
|
|
|
3396
3781
|
);
|
|
3397
3782
|
}
|
|
3398
3783
|
if (pagination.hasMore && pagination.nextCursor) {
|
|
3399
|
-
info(`More:
|
|
3784
|
+
info(`More: ${cliInvocation()} knowledge notecards --cursor ${pagination.nextCursor}`);
|
|
3400
3785
|
}
|
|
3401
3786
|
} catch (err) {
|
|
3402
3787
|
spinner.fail("Failed to list notecards");
|
|
@@ -3449,7 +3834,7 @@ function createKnowledgeCommand() {
|
|
|
3449
3834
|
}
|
|
3450
3835
|
|
|
3451
3836
|
// src/commands/onboarding.ts
|
|
3452
|
-
var
|
|
3837
|
+
var import_commander14 = require("commander");
|
|
3453
3838
|
var import_ora14 = __toESM(require("ora"));
|
|
3454
3839
|
var ONBOARDING_STATES = [
|
|
3455
3840
|
"created",
|
|
@@ -3459,7 +3844,7 @@ var ONBOARDING_STATES = [
|
|
|
3459
3844
|
"ready"
|
|
3460
3845
|
];
|
|
3461
3846
|
function createOnboardingCommand() {
|
|
3462
|
-
const onboarding = new
|
|
3847
|
+
const onboarding = new import_commander14.Command("onboarding").alias("onboard").description("Check account setup and onboarding state");
|
|
3463
3848
|
onboarding.command("status").description("Show the setup checklist and the next step").option("--json", "Output as JSON").action(async (options) => {
|
|
3464
3849
|
const client = getClient();
|
|
3465
3850
|
const spinner = (0, import_ora14.default)("Fetching onboarding status...").start();
|
|
@@ -3520,7 +3905,7 @@ function createOnboardingCommand() {
|
|
|
3520
3905
|
const result = await client.onboarding.requestVerification();
|
|
3521
3906
|
spinner.succeed(`Verification sent to ${result.sentTo}`);
|
|
3522
3907
|
if (options.json) json(result);
|
|
3523
|
-
else info(
|
|
3908
|
+
else info(`Open the link in that email, then check progress with "${cliInvocation()} onboarding state"`);
|
|
3524
3909
|
} catch (err) {
|
|
3525
3910
|
spinner.fail("Failed to request verification");
|
|
3526
3911
|
failWith("API error", err);
|
|
@@ -3550,7 +3935,7 @@ function createOnboardingCommand() {
|
|
|
3550
3935
|
}
|
|
3551
3936
|
|
|
3552
3937
|
// src/commands/usage.ts
|
|
3553
|
-
var
|
|
3938
|
+
var import_commander15 = require("commander");
|
|
3554
3939
|
var import_ora15 = __toESM(require("ora"));
|
|
3555
3940
|
function isoDay(daysAgo, now = /* @__PURE__ */ new Date()) {
|
|
3556
3941
|
const d = new Date(now.getTime() - daysAgo * 24 * 60 * 60 * 1e3);
|
|
@@ -3560,7 +3945,7 @@ function cents(n) {
|
|
|
3560
3945
|
return `$${(n / 100).toFixed(2)}`;
|
|
3561
3946
|
}
|
|
3562
3947
|
function createUsageCommand() {
|
|
3563
|
-
const usage = new
|
|
3948
|
+
const usage = new import_commander15.Command("usage").description("Show API usage, billing, and rate limits");
|
|
3564
3949
|
usage.command("summary").description("Usage for the current billing period").option("--json", "Output as JSON").action(async (options) => {
|
|
3565
3950
|
const client = getClient();
|
|
3566
3951
|
const spinner = (0, import_ora15.default)("Fetching usage summary...").start();
|
|
@@ -3640,21 +4025,30 @@ function createUsageCommand() {
|
|
|
3640
4025
|
}
|
|
3641
4026
|
|
|
3642
4027
|
// src/commands/keys.ts
|
|
3643
|
-
var
|
|
4028
|
+
var import_commander16 = require("commander");
|
|
3644
4029
|
var import_ora16 = __toESM(require("ora"));
|
|
3645
4030
|
var import_crypto3 = require("crypto");
|
|
3646
|
-
var
|
|
4031
|
+
var import_sdk7 = require("@delegance/sdk");
|
|
3647
4032
|
var KEYS_PORTAL_URL = `${PORTAL_URL}/portal/settings/api-keys`;
|
|
4033
|
+
function parseExpiresInDays(raw) {
|
|
4034
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
4035
|
+
if (raw.trim().toLowerCase() === "never") return "never";
|
|
4036
|
+
return parseInt(raw, 10);
|
|
4037
|
+
}
|
|
3648
4038
|
function parseScopes(raw) {
|
|
3649
4039
|
const parts = (Array.isArray(raw) ? raw : [raw ?? "read,write"]).flatMap((p) => p.split(/[\s,]+/)).map((p) => p.trim()).filter(Boolean);
|
|
3650
|
-
const unknown = parts.filter((p) => !
|
|
4040
|
+
const unknown = parts.filter((p) => !import_sdk7.API_KEY_SCOPES.includes(p));
|
|
3651
4041
|
if (unknown.length > 0) {
|
|
3652
|
-
throw new Error(`Unknown scope(s): ${unknown.join(", ")}. Allowed: ${
|
|
4042
|
+
throw new Error(`Unknown scope(s): ${unknown.join(", ")}. Allowed: ${import_sdk7.API_KEY_SCOPES.join(", ")}`);
|
|
3653
4043
|
}
|
|
3654
4044
|
return Array.from(new Set(parts));
|
|
3655
4045
|
}
|
|
3656
|
-
|
|
3657
|
-
|
|
4046
|
+
var EXPIRING_SCOPES = /* @__PURE__ */ new Set(["write", "admin", "claims:write"]);
|
|
4047
|
+
function isReadOnlyScopeSet(scopes) {
|
|
4048
|
+
return !scopes.some((s) => EXPIRING_SCOPES.has(s));
|
|
4049
|
+
}
|
|
4050
|
+
function replacementExpiryDays(expiresAt, now = /* @__PURE__ */ new Date(), scopes = []) {
|
|
4051
|
+
if (!expiresAt) return isReadOnlyScopeSet(scopes) ? "never" : void 0;
|
|
3658
4052
|
const ms = new Date(expiresAt).getTime() - now.getTime();
|
|
3659
4053
|
if (!Number.isFinite(ms)) throw new Error(`Cannot read the old key's expiry "${expiresAt}"; not rotating`);
|
|
3660
4054
|
const days = Math.ceil(ms / (24 * 60 * 60 * 1e3));
|
|
@@ -3674,7 +4068,7 @@ function printCreatedKey(created) {
|
|
|
3674
4068
|
console.log(` ${created.key}`);
|
|
3675
4069
|
}
|
|
3676
4070
|
function createKeysCommand() {
|
|
3677
|
-
const keys = new
|
|
4071
|
+
const keys = new import_commander16.Command("keys").alias("api-keys").description("Manage API keys");
|
|
3678
4072
|
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) => {
|
|
3679
4073
|
const client = getClient();
|
|
3680
4074
|
const spinner = (0, import_ora16.default)("Fetching API keys...").start();
|
|
@@ -3703,14 +4097,14 @@ function createKeysCommand() {
|
|
|
3703
4097
|
])
|
|
3704
4098
|
);
|
|
3705
4099
|
if (pagination?.hasMore && pagination.nextCursor) {
|
|
3706
|
-
info(`More keys:
|
|
4100
|
+
info(`More keys: ${cliInvocation()} keys list --cursor ${pagination.nextCursor}`);
|
|
3707
4101
|
}
|
|
3708
4102
|
} catch (err) {
|
|
3709
4103
|
spinner.fail("Failed to list API keys");
|
|
3710
4104
|
failWith("API error", err);
|
|
3711
4105
|
}
|
|
3712
4106
|
});
|
|
3713
|
-
keys.command("create <name>").description("Create an API key (needs a key with the admin scope)").option("-s, --scopes <scopes>", `Comma-separated scopes (${
|
|
4107
|
+
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) => {
|
|
3714
4108
|
let scopes;
|
|
3715
4109
|
try {
|
|
3716
4110
|
scopes = parseScopes(options.scopes);
|
|
@@ -3725,7 +4119,7 @@ function createKeysCommand() {
|
|
|
3725
4119
|
{
|
|
3726
4120
|
name,
|
|
3727
4121
|
scopes,
|
|
3728
|
-
expiresInDays:
|
|
4122
|
+
expiresInDays: parseExpiresInDays(options.expiresInDays)
|
|
3729
4123
|
},
|
|
3730
4124
|
{ idempotencyKey: (0, import_crypto3.randomUUID)() }
|
|
3731
4125
|
);
|
|
@@ -3760,7 +4154,7 @@ function createKeysCommand() {
|
|
|
3760
4154
|
{
|
|
3761
4155
|
name: `${current2.name} (rotated)`,
|
|
3762
4156
|
scopes: current2.scopes,
|
|
3763
|
-
expiresInDays: replacementExpiryDays(current2.expiresAt)
|
|
4157
|
+
expiresInDays: replacementExpiryDays(current2.expiresAt, /* @__PURE__ */ new Date(), current2.scopes)
|
|
3764
4158
|
},
|
|
3765
4159
|
{ idempotencyKey: (0, import_crypto3.randomUUID)() }
|
|
3766
4160
|
);
|
|
@@ -3771,6 +4165,9 @@ function createKeysCommand() {
|
|
|
3771
4165
|
}
|
|
3772
4166
|
printCreatedKey(created);
|
|
3773
4167
|
console.log();
|
|
4168
|
+
if (!current2.expiresAt && !isReadOnlyScopeSet(current2.scopes)) {
|
|
4169
|
+
warn("The old key never expired. Keys that can write must expire now, so the new key lasts 90 days.");
|
|
4170
|
+
}
|
|
3774
4171
|
warn(`The old key ${current2.prefix} is still active.`);
|
|
3775
4172
|
info(`Switch your integrations to the new key, then revoke the old one at ${KEYS_PORTAL_URL}`);
|
|
3776
4173
|
} catch (err) {
|
|
@@ -3786,7 +4183,7 @@ function createKeysCommand() {
|
|
|
3786
4183
|
spinner.succeed("API key revoked");
|
|
3787
4184
|
} catch (err) {
|
|
3788
4185
|
spinner.fail("Failed to revoke API key");
|
|
3789
|
-
if (err instanceof
|
|
4186
|
+
if (err instanceof import_sdk7.AuthenticationError) {
|
|
3790
4187
|
error("Revoking a key needs a signed-in portal session today");
|
|
3791
4188
|
info(`Revoke it at ${KEYS_PORTAL_URL}`);
|
|
3792
4189
|
process.exit(1);
|
|
@@ -3798,10 +4195,10 @@ function createKeysCommand() {
|
|
|
3798
4195
|
}
|
|
3799
4196
|
|
|
3800
4197
|
// src/commands/config.ts
|
|
3801
|
-
var
|
|
3802
|
-
var
|
|
4198
|
+
var import_commander17 = require("commander");
|
|
4199
|
+
var import_inquirer10 = __toESM(require("inquirer"));
|
|
3803
4200
|
function createConfigCommand() {
|
|
3804
|
-
const config2 = new
|
|
4201
|
+
const config2 = new import_commander17.Command("config").description("Manage CLI configuration");
|
|
3805
4202
|
config2.command("show").alias("list").description("Show current configuration").option("--json", "Output as JSON").action((options) => {
|
|
3806
4203
|
const currentConfig = getConfig();
|
|
3807
4204
|
const apiKey = getApiKey();
|
|
@@ -3845,7 +4242,7 @@ function createConfigCommand() {
|
|
|
3845
4242
|
});
|
|
3846
4243
|
config2.command("reset").description("Reset all configuration to defaults").option("-f, --force", "Skip confirmation").action(async (options) => {
|
|
3847
4244
|
if (!options.force) {
|
|
3848
|
-
const { confirm } = await
|
|
4245
|
+
const { confirm } = await import_inquirer10.default.prompt([
|
|
3849
4246
|
{
|
|
3850
4247
|
type: "confirm",
|
|
3851
4248
|
name: "confirm",
|
|
@@ -3862,7 +4259,7 @@ function createConfigCommand() {
|
|
|
3862
4259
|
currentConfig.clear();
|
|
3863
4260
|
clearCredentials();
|
|
3864
4261
|
success("Configuration reset to defaults");
|
|
3865
|
-
info(
|
|
4262
|
+
info(`You will need to run "${cliInvocation()} auth login" again`);
|
|
3866
4263
|
});
|
|
3867
4264
|
config2.command("env").description("Show environment information").action(() => {
|
|
3868
4265
|
header("Environment");
|
|
@@ -3888,7 +4285,7 @@ function createConfigCommand() {
|
|
|
3888
4285
|
info("No profiles configured");
|
|
3889
4286
|
info("Profiles allow you to switch between different accounts/environments");
|
|
3890
4287
|
console.log();
|
|
3891
|
-
info(
|
|
4288
|
+
info(`Example: ${cliInvocation()} config set-profile production --url https://portal.delegancebrokerage.com`);
|
|
3892
4289
|
} else {
|
|
3893
4290
|
for (const [name, profile] of Object.entries(profiles)) {
|
|
3894
4291
|
const isActive = name === activeProfile;
|
|
@@ -3904,7 +4301,7 @@ function createConfigCommand() {
|
|
|
3904
4301
|
const profiles = currentConfig.get("profiles") || {};
|
|
3905
4302
|
if (!profiles[name] && name !== "default") {
|
|
3906
4303
|
error(`Profile "${name}" not found`);
|
|
3907
|
-
info(
|
|
4304
|
+
info(`Create it with: ${cliInvocation()} config set-profile <name> --url <base-url>`);
|
|
3908
4305
|
process.exit(1);
|
|
3909
4306
|
}
|
|
3910
4307
|
currentConfig.set("activeProfile", name);
|
|
@@ -3930,15 +4327,15 @@ function createConfigCommand() {
|
|
|
3930
4327
|
}
|
|
3931
4328
|
|
|
3932
4329
|
// src/program.ts
|
|
3933
|
-
var
|
|
4330
|
+
var import_commander20 = require("commander");
|
|
3934
4331
|
|
|
3935
4332
|
// src/commands/feedback.ts
|
|
3936
|
-
var
|
|
3937
|
-
var
|
|
3938
|
-
var
|
|
4333
|
+
var import_commander18 = require("commander");
|
|
4334
|
+
var import_inquirer11 = __toESM(require("inquirer"));
|
|
4335
|
+
var import_sdk8 = require("@delegance/sdk");
|
|
3939
4336
|
var TYPES = ["bug", "idea", "question"];
|
|
3940
4337
|
var MESSAGE_MAX = 500;
|
|
3941
|
-
var USAGE =
|
|
4338
|
+
var USAGE = `${cliInvocation()} feedback "what happened" [--type bug|idea|question] [--include-last]`;
|
|
3942
4339
|
function feedbackInputMode(o) {
|
|
3943
4340
|
if (o.message && o.message.trim()) return "given";
|
|
3944
4341
|
return o.isTTY ? "prompt" : "usage_error";
|
|
@@ -3952,7 +4349,7 @@ function buildFeedbackContext(last) {
|
|
|
3952
4349
|
ctx.at = last.at;
|
|
3953
4350
|
}
|
|
3954
4351
|
ctx.cliVersion = CLI_VERSION;
|
|
3955
|
-
ctx.sdkVersion =
|
|
4352
|
+
ctx.sdkVersion = import_sdk8.SDK_VERSION;
|
|
3956
4353
|
return ctx;
|
|
3957
4354
|
}
|
|
3958
4355
|
function usageError(message) {
|
|
@@ -3961,14 +4358,14 @@ function usageError(message) {
|
|
|
3961
4358
|
process.exit(2);
|
|
3962
4359
|
}
|
|
3963
4360
|
function createFeedbackCommand() {
|
|
3964
|
-
return new
|
|
4361
|
+
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) => {
|
|
3965
4362
|
const type = String(options.type ?? "bug").toLowerCase();
|
|
3966
4363
|
if (!TYPES.includes(type)) usageError(`--type must be bug, idea, or question, got "${options.type}"`);
|
|
3967
4364
|
let message = (words ?? []).join(" ").trim();
|
|
3968
4365
|
const mode = feedbackInputMode({ message, isTTY: Boolean(process.stdin.isTTY && process.stdout.isTTY) });
|
|
3969
4366
|
if (mode === "usage_error") usageError(`Say what happened: ${USAGE}`);
|
|
3970
4367
|
if (mode === "prompt") {
|
|
3971
|
-
const answer = await
|
|
4368
|
+
const answer = await import_inquirer11.default.prompt([
|
|
3972
4369
|
{ type: "input", name: "message", message: type === "bug" ? "What went wrong?" : "What would you like to tell us?" }
|
|
3973
4370
|
]);
|
|
3974
4371
|
message = String(answer.message ?? "").trim();
|
|
@@ -3989,12 +4386,42 @@ function createFeedbackCommand() {
|
|
|
3989
4386
|
});
|
|
3990
4387
|
}
|
|
3991
4388
|
|
|
4389
|
+
// src/utils/checkbox-prompt.ts
|
|
4390
|
+
var import_inquirer12 = __toESM(require("inquirer"));
|
|
4391
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
4392
|
+
var CHECKBOX_HINT = "(space to select, Enter to continue)";
|
|
4393
|
+
var registered = false;
|
|
4394
|
+
function minimalCheckboxPrompt(Base) {
|
|
4395
|
+
return class MinimalCheckboxPrompt extends Base {
|
|
4396
|
+
constructor(...args) {
|
|
4397
|
+
super(...args);
|
|
4398
|
+
this.dontShowHints = true;
|
|
4399
|
+
}
|
|
4400
|
+
getQuestion() {
|
|
4401
|
+
const question = super.getQuestion();
|
|
4402
|
+
return this.status === "answered" ? question : `${question}${import_chalk3.default.dim(CHECKBOX_HINT)} `;
|
|
4403
|
+
}
|
|
4404
|
+
onAllKey() {
|
|
4405
|
+
}
|
|
4406
|
+
onInverseKey() {
|
|
4407
|
+
}
|
|
4408
|
+
};
|
|
4409
|
+
}
|
|
4410
|
+
function registerMinimalCheckbox() {
|
|
4411
|
+
if (registered) return;
|
|
4412
|
+
const prompts = import_inquirer12.default.prompt.prompts;
|
|
4413
|
+
const Base = prompts?.checkbox;
|
|
4414
|
+
if (!Base) return;
|
|
4415
|
+
import_inquirer12.default.registerPrompt("checkbox", minimalCheckboxPrompt(Base));
|
|
4416
|
+
registered = true;
|
|
4417
|
+
}
|
|
4418
|
+
|
|
3992
4419
|
// src/commands/commerce.ts
|
|
3993
|
-
var
|
|
4420
|
+
var import_commander19 = require("commander");
|
|
3994
4421
|
var fs7 = __toESM(require("fs"));
|
|
3995
|
-
var
|
|
4422
|
+
var import_inquirer13 = __toESM(require("inquirer"));
|
|
3996
4423
|
var import_ora17 = __toESM(require("ora"));
|
|
3997
|
-
var
|
|
4424
|
+
var import_sdk9 = require("@delegance/sdk");
|
|
3998
4425
|
function formatCents(cents2, currency = "usd") {
|
|
3999
4426
|
return currency.toLowerCase() === "usd" ? formatCurrency(cents2 / 100) : `${(cents2 / 100).toFixed(2)} ${currency.toUpperCase()}`;
|
|
4000
4427
|
}
|
|
@@ -4020,9 +4447,58 @@ function describeBindResult(r) {
|
|
|
4020
4447
|
lines.push(r.next);
|
|
4021
4448
|
return lines;
|
|
4022
4449
|
}
|
|
4450
|
+
var BROKER_REVIEW_EMAIL = "support@delegancebrokerage.com";
|
|
4451
|
+
function selfDirectedAckLines(submissionId, offerId) {
|
|
4452
|
+
return [
|
|
4453
|
+
"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).",
|
|
4454
|
+
`Want a licensed broker to review it first? Run: ${cliInvocation()} bind ${submissionId} ${offerId} --broker-review`
|
|
4455
|
+
];
|
|
4456
|
+
}
|
|
4457
|
+
function brokerReviewMailto(submissionId, offerId) {
|
|
4458
|
+
const subject = encodeURIComponent(`Broker review before bind: submission ${submissionId}, offer ${offerId}`);
|
|
4459
|
+
return `mailto:${BROKER_REVIEW_EMAIL}?subject=${subject}`;
|
|
4460
|
+
}
|
|
4461
|
+
function brokerReviewLines(submissionId, offerId) {
|
|
4462
|
+
return [
|
|
4463
|
+
"Nothing was bound, and no review has been requested yet.",
|
|
4464
|
+
`To request a review by a licensed Delegance broker, send an email to ${BROKER_REVIEW_EMAIL}`,
|
|
4465
|
+
`naming submission ${submissionId} and offer ${offerId}: ${brokerReviewMailto(submissionId, offerId)}`,
|
|
4466
|
+
"Wait for the broker to reply before you bind. A reviewed placement is not self-directed."
|
|
4467
|
+
];
|
|
4468
|
+
}
|
|
4469
|
+
function brokerReviewResult(submissionId, offerId) {
|
|
4470
|
+
return {
|
|
4471
|
+
bound: false,
|
|
4472
|
+
reviewRequested: false,
|
|
4473
|
+
submissionId,
|
|
4474
|
+
offerId,
|
|
4475
|
+
brokerReview: { email: BROKER_REVIEW_EMAIL, mailto: brokerReviewMailto(submissionId, offerId) },
|
|
4476
|
+
message: brokerReviewLines(submissionId, offerId)
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
4479
|
+
function exactDollars(cents2) {
|
|
4480
|
+
return `$${(cents2 / 100).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
4481
|
+
}
|
|
4482
|
+
function describeFinancing(f) {
|
|
4483
|
+
if (!f || !f.available) return [];
|
|
4484
|
+
const lines = [];
|
|
4485
|
+
if (f.checkoutUrl) {
|
|
4486
|
+
lines.push(`Pay over time with ${f.lender}: ${f.checkoutUrl}`);
|
|
4487
|
+
} else {
|
|
4488
|
+
lines.push(`Financing with ${f.lender} opens once your broker confirms the final premium.`);
|
|
4489
|
+
}
|
|
4490
|
+
const t = f.terms;
|
|
4491
|
+
if (t && t.aprBps !== null && t.downPaymentCents !== null && t.installmentCents !== null && t.numberOfInstallments !== null) {
|
|
4492
|
+
lines.push(
|
|
4493
|
+
`Lender terms: ${(t.aprBps / 100).toFixed(2)}% APR, ${exactDollars(t.downPaymentCents)} down, ${t.numberOfInstallments} payments of ${exactDollars(t.installmentCents)}.`
|
|
4494
|
+
);
|
|
4495
|
+
}
|
|
4496
|
+
lines.push(f.disclosure);
|
|
4497
|
+
return lines;
|
|
4498
|
+
}
|
|
4023
4499
|
async function askCode() {
|
|
4024
4500
|
if (!process.stdin.isTTY) return null;
|
|
4025
|
-
const answer = await
|
|
4501
|
+
const answer = await import_inquirer13.default.prompt([{
|
|
4026
4502
|
type: "input",
|
|
4027
4503
|
name: "code",
|
|
4028
4504
|
message: "Code from your email:",
|
|
@@ -4031,11 +4507,19 @@ async function askCode() {
|
|
|
4031
4507
|
return String(answer.code).trim();
|
|
4032
4508
|
}
|
|
4033
4509
|
function createBindCommand() {
|
|
4034
|
-
return new
|
|
4510
|
+
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) => {
|
|
4511
|
+
if (options.brokerReview) {
|
|
4512
|
+
if (options.json) json(brokerReviewResult(submissionId, offerId));
|
|
4513
|
+
else brokerReviewLines(submissionId, offerId).forEach((l) => info(l));
|
|
4514
|
+
return;
|
|
4515
|
+
}
|
|
4035
4516
|
const client = getClient();
|
|
4517
|
+
const ack = selfDirectedAckLines(submissionId, offerId);
|
|
4518
|
+
if (options.json) ack.forEach((l) => console.error(l));
|
|
4519
|
+
else ack.forEach((l) => info(l));
|
|
4036
4520
|
let attest = options.attest === true;
|
|
4037
4521
|
if (!attest && process.stdin.isTTY && !options.json) {
|
|
4038
|
-
const a = await
|
|
4522
|
+
const a = await import_inquirer13.default.prompt([{
|
|
4039
4523
|
type: "confirm",
|
|
4040
4524
|
name: "ok",
|
|
4041
4525
|
message: "Are the answers on this application accurate? Binding relies on them.",
|
|
@@ -4043,7 +4527,7 @@ function createBindCommand() {
|
|
|
4043
4527
|
}]);
|
|
4044
4528
|
attest = a.ok === true;
|
|
4045
4529
|
if (!attest) {
|
|
4046
|
-
warn(
|
|
4530
|
+
warn(`Not bound. Fix the answers with "${cliInvocation()} quote start", then bind again.`);
|
|
4047
4531
|
return;
|
|
4048
4532
|
}
|
|
4049
4533
|
}
|
|
@@ -4059,7 +4543,7 @@ function createBindCommand() {
|
|
|
4059
4543
|
info(`We emailed a 6-digit code to ${r.sentTo}. It expires in 10 minutes.`);
|
|
4060
4544
|
const code = options.code ? String(options.code) : await askCode();
|
|
4061
4545
|
if (!code) {
|
|
4062
|
-
warn(`Finish with:
|
|
4546
|
+
warn(`Finish with: ${cliInvocation()} bind ${submissionId} ${offerId} --attest --step-up ${stepUpId} --code <code>`);
|
|
4063
4547
|
return;
|
|
4064
4548
|
}
|
|
4065
4549
|
r = await client.instantQuotes.bindWithStepUp(submissionId, offerId, { ...attest ? { attest: true } : {}, stepUpId, code });
|
|
@@ -4069,16 +4553,17 @@ function createBindCommand() {
|
|
|
4069
4553
|
}
|
|
4070
4554
|
}
|
|
4071
4555
|
if (options.json) {
|
|
4072
|
-
json(r);
|
|
4556
|
+
json({ ...r, selfDirected: true, disclosure: ack[0] });
|
|
4073
4557
|
return;
|
|
4074
4558
|
}
|
|
4075
4559
|
if (r.existing) warn(`A bind for this coverage is already open (${r.carrierName}). Showing it.`);
|
|
4076
|
-
else if (r.tier !== "A") success(`Bind requested: ${r.carrierName} ${r.coverage}${r.premium === null ? "" : ` at ${formatCurrency(r.premium)}/yr`}`);
|
|
4560
|
+
else if (r.tier !== "A") success(`Bind requested: ${r.carrierName} ${coverageName(r.coverage)}${r.premium === null ? "" : ` at ${formatCurrency(r.premium)}/yr`}`);
|
|
4077
4561
|
keyValue({ "Bind request": r.bindRequestId, Status: r.status });
|
|
4078
4562
|
describeBindResult(r).forEach((l) => info(l));
|
|
4563
|
+
describeFinancing(r.financing).forEach((l) => info(l));
|
|
4079
4564
|
} catch (err) {
|
|
4080
4565
|
spinner?.stop();
|
|
4081
|
-
if (err instanceof
|
|
4566
|
+
if (err instanceof import_sdk9.DeleganceError && [400, 403, 409].includes(err.statusCode ?? 0)) {
|
|
4082
4567
|
error(err.message);
|
|
4083
4568
|
process.exit(1);
|
|
4084
4569
|
}
|
|
@@ -4099,7 +4584,7 @@ function summarizeBilling(payments) {
|
|
|
4099
4584
|
return { openCents, paidCents, openCount };
|
|
4100
4585
|
}
|
|
4101
4586
|
function createBillingCommand() {
|
|
4102
|
-
return new
|
|
4587
|
+
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) => {
|
|
4103
4588
|
try {
|
|
4104
4589
|
const payments = await getClient().payments.list();
|
|
4105
4590
|
const sum = summarizeBilling(payments);
|
|
@@ -4116,14 +4601,14 @@ function createBillingCommand() {
|
|
|
4116
4601
|
payments.map((p) => [p.id, formatCents(p.amount, p.currency), p.status, p.dueDate ? formatDate(p.dueDate) : "-", p.description ?? "-"])
|
|
4117
4602
|
);
|
|
4118
4603
|
keyValue({ "Open": `${formatCents(sum.openCents)} (${sum.openCount})`, "Paid": formatCents(sum.paidCents) });
|
|
4119
|
-
if (sum.openCount > 0) info(
|
|
4604
|
+
if (sum.openCount > 0) info(`Pay one with: ${cliInvocation()} pay <paymentId>`);
|
|
4120
4605
|
} catch (err) {
|
|
4121
4606
|
failWith("API error", err);
|
|
4122
4607
|
}
|
|
4123
4608
|
});
|
|
4124
4609
|
}
|
|
4125
4610
|
function createPayCommand() {
|
|
4126
|
-
return new
|
|
4611
|
+
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) => {
|
|
4127
4612
|
const client = getClient();
|
|
4128
4613
|
try {
|
|
4129
4614
|
if (!paymentId) {
|
|
@@ -4141,7 +4626,7 @@ function createPayCommand() {
|
|
|
4141
4626
|
payments.map((p) => [p.id, formatCents(p.amount, p.currency), p.description ?? "", p.payable ? `${p.status} (payable)` : p.status, p.dueDate ? formatDate(p.dueDate) : "-"])
|
|
4142
4627
|
);
|
|
4143
4628
|
const next = payments.find((p) => p.payable);
|
|
4144
|
-
if (next) info(`Pay it:
|
|
4629
|
+
if (next) info(`Pay it: ${cliInvocation()} pay ${next.id}`);
|
|
4145
4630
|
return;
|
|
4146
4631
|
}
|
|
4147
4632
|
const { checkoutUrl } = await client.payments.checkout(paymentId);
|
|
@@ -4171,14 +4656,14 @@ function bondStartLines(r, ctx = {}) {
|
|
|
4171
4656
|
out.push(` ${str(q.key)}: ${str(q.label)}${q.required ? " (required)" : ""}${opts}`);
|
|
4172
4657
|
});
|
|
4173
4658
|
}
|
|
4174
|
-
out.push(`Answer with:
|
|
4659
|
+
out.push(`Answer with: ${cliInvocation()} bond answer ${str(r.session_id)} --answers answers.json`);
|
|
4175
4660
|
return out;
|
|
4176
4661
|
}
|
|
4177
4662
|
if (Array.isArray(r.candidates) && r.candidates.length) {
|
|
4178
4663
|
out.push("That name fits more than one bond. Rerun with the exact name:");
|
|
4179
4664
|
const rawState = (ctx.state ?? "").trim();
|
|
4180
4665
|
const state = !rawState ? null : /^[A-Za-z]{2}$/.test(rawState) ? rawState.toUpperCase() : shellQuote(rawState);
|
|
4181
|
-
r.candidates.forEach((name) => out.push(state ? `
|
|
4666
|
+
r.candidates.forEach((name) => out.push(state ? ` ${cliInvocation()} bond quote --state ${state} --bond ${shellQuote(str(name))}` : ` --bond ${shellQuote(str(name))}`));
|
|
4182
4667
|
return out;
|
|
4183
4668
|
}
|
|
4184
4669
|
if (r.needsAmount) {
|
|
@@ -4208,7 +4693,7 @@ function bondStartLines(r, ctx = {}) {
|
|
|
4208
4693
|
return out;
|
|
4209
4694
|
}
|
|
4210
4695
|
function createBondCommand() {
|
|
4211
|
-
const bond = new
|
|
4696
|
+
const bond = new import_commander19.Command("bond").alias("bonds").description("Surety bond quotes");
|
|
4212
4697
|
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) => {
|
|
4213
4698
|
try {
|
|
4214
4699
|
const r = await getClient().bonds.start({
|
|
@@ -4233,7 +4718,7 @@ function createBondCommand() {
|
|
|
4233
4718
|
if (options.json) json(r);
|
|
4234
4719
|
else {
|
|
4235
4720
|
console.log(JSON.stringify(r, null, 2));
|
|
4236
|
-
info(`Check it:
|
|
4721
|
+
info(`Check it: ${cliInvocation()} bond status ${sessionId}`);
|
|
4237
4722
|
}
|
|
4238
4723
|
} catch (err) {
|
|
4239
4724
|
failWith("Could not submit the answers", err);
|
|
@@ -4254,7 +4739,7 @@ function createBondCommand() {
|
|
|
4254
4739
|
return bond;
|
|
4255
4740
|
}
|
|
4256
4741
|
function createClaimCommand() {
|
|
4257
|
-
const claim = new
|
|
4742
|
+
const claim = new import_commander19.Command("claim").alias("claims").description("File and track claims");
|
|
4258
4743
|
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) => {
|
|
4259
4744
|
let description = options.description;
|
|
4260
4745
|
if (options.descriptionFile) {
|
|
@@ -4278,7 +4763,7 @@ function createClaimCommand() {
|
|
|
4278
4763
|
success(r.message);
|
|
4279
4764
|
keyValue({ Claim: r.claimId, Status: r.status, Portal: r.portalUrl });
|
|
4280
4765
|
} catch (err) {
|
|
4281
|
-
if (err instanceof
|
|
4766
|
+
if (err instanceof import_sdk9.DeleganceError && err.reason === "gate_not_acknowledged") {
|
|
4282
4767
|
error("Accept the claims disclaimer once in the portal, then file again. It cannot be accepted for you.");
|
|
4283
4768
|
info(String(err.body?.portalUrl ?? ""));
|
|
4284
4769
|
process.exit(1);
|
|
@@ -4310,7 +4795,8 @@ function createClaimCommand() {
|
|
|
4310
4795
|
|
|
4311
4796
|
// src/program.ts
|
|
4312
4797
|
function createProgram() {
|
|
4313
|
-
|
|
4798
|
+
registerMinimalCheckbox();
|
|
4799
|
+
const program = new import_commander20.Command();
|
|
4314
4800
|
program.name("delegance").description("CLI for the Delegance Brokerage platform").version(CLI_VERSION).enablePositionalOptions().option("-v, --verbose", "Enable verbose output").option("--json", "Output in JSON format").hook("preAction", () => {
|
|
4315
4801
|
migrateLegacyConfigKey();
|
|
4316
4802
|
});
|
|
@@ -4336,6 +4822,7 @@ function createProgram() {
|
|
|
4336
4822
|
program.addCommand(createBondCommand());
|
|
4337
4823
|
program.addCommand(createClaimCommand());
|
|
4338
4824
|
program.addCommand(createFeedbackCommand());
|
|
4825
|
+
program.addCommand(createTermsCommand());
|
|
4339
4826
|
program.addCommand(createConfigCommand());
|
|
4340
4827
|
return program;
|
|
4341
4828
|
}
|