argorant 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/argorant.js +101 -0
  2. package/package.json +1 -1
package/bin/argorant.js CHANGED
@@ -75,6 +75,9 @@ const VALUE_FLAGS = {
75
75
  "--company": "company_name",
76
76
  "--domain": "company_domain",
77
77
  "--website": "website",
78
+ // Used by `enrich` (email → full profile). Harmless on the other commands,
79
+ // which simply ignore an unknown query field.
80
+ "--email": "email",
78
81
  };
79
82
  // Boolean filter flags (presence => "true"). --verified-only is positive intent
80
83
  // only (deliverable contacts); there is deliberately NO flag to query invalid or
@@ -570,6 +573,96 @@ async function cmdReveal(args) {
570
573
  }
571
574
  }
572
575
 
576
+ // ---- enrich: one record in, one record out. Three modes, one endpoint:
577
+ // --email <addr> → the person behind an address (billed like a reveal)
578
+ // --name "<n>" --domain <d> → find that person and reveal a verified email
579
+ // --domain <d> → the company profile, free
580
+ // Exit code is part of the contract: a miss returns 0 rows and exits 1, so a
581
+ // script or agent can branch on it without parsing the payload.
582
+ function normalizeDomain(raw) {
583
+ return String(raw || "")
584
+ .trim()
585
+ .replace(/^https?:\/\//i, "")
586
+ .replace(/^www\./i, "")
587
+ .split(/[/?#]/, 1)[0]
588
+ .toLowerCase();
589
+ }
590
+
591
+ async function cmdEnrich(args) {
592
+ const key = requireKey();
593
+ const usage =
594
+ 'usage: argorant enrich --email <name@company.com>\n' +
595
+ ' argorant enrich --name "Jane Doe" --domain <company.com>\n' +
596
+ " argorant enrich --domain <company.com>";
597
+ const positional = (args._[0] || "").trim();
598
+ let email = String(args.filters.email || "").trim().toLowerCase();
599
+ let domain = String(args.filters.company_domain || "").trim();
600
+ const name = String(args.name || "").trim();
601
+ // Bare argument: an address is an email, anything else is a domain.
602
+ if (!email && !domain && positional) {
603
+ if (positional.includes("@")) email = positional.toLowerCase();
604
+ else domain = positional;
605
+ }
606
+ domain = domain ? normalizeDomain(domain) : "";
607
+ if (email && !email.includes("@")) die(`--email needs a full address (got "${email}").`);
608
+ if (!email && !domain) die(usage);
609
+ if (name && !domain) die(`--name needs --domain (the company the person works at).\n${usage}`);
610
+ const body = {};
611
+ if (email) body.email = email;
612
+ else if (name) { body.name = name; body.domain = domain; }
613
+ else body.domain = domain;
614
+ const personMode = Boolean(body.email || body.name);
615
+ // Company mode is free, so it never asks. Person mode is billed exactly like
616
+ // a reveal, so it gets the same interactive-only confirmation as `reveal`.
617
+ if (personMode && !args.yes && !args.json && process.stdin.isTTY) {
618
+ const ans = await prompt(
619
+ `Enrich this contact? A match costs 1 credit; a miss and a non-deliverable address are free. [y/N] `
620
+ );
621
+ if (!/^y(es)?$/i.test(ans)) return console.log(dim("aborted."));
622
+ }
623
+ const res = await request("POST", args.base, "/api/v1/enrich", { key, body });
624
+ const r = need(res, "enrich");
625
+ if (args.json) {
626
+ console.log(JSON.stringify(r, null, 2));
627
+ if (!r.found) process.exit(EXIT.ERROR);
628
+ return;
629
+ }
630
+ if (!r.found) {
631
+ console.log(dim(`no ${r.type || (personMode ? "person" : "company")} found · 0 credits charged`));
632
+ process.exit(EXIT.ERROR);
633
+ }
634
+ if (r.type === "company") {
635
+ const co = r.company || {};
636
+ console.log(`${bold(co.company || domain)} ${dim(co.company_domain || domain)}`);
637
+ const where = [co.city, co.state, co.country].filter(Boolean).join(", ");
638
+ const what = co.industry || (co.industries || [])[0];
639
+ if (what) console.log(` ${what}`);
640
+ if (where) console.log(` ${dim(where)}`);
641
+ console.log(dim(" company profile · 0 credits charged"));
642
+ return;
643
+ }
644
+ const p = r.person || {};
645
+ const who = [p.full_name || [p.first_name, p.last_name].filter(Boolean).join(" "), p.title].filter(Boolean).join(" · ");
646
+ console.log(` ${bold(who || "—")}`);
647
+ const bits = [
648
+ p.email && cyan(p.email),
649
+ p.phone,
650
+ p.linkedin_url,
651
+ [p.current_company_name, p.current_company_domain, p.country].filter(Boolean).join(", "),
652
+ ].filter(Boolean);
653
+ if (bits.length) console.log(" " + bits.join(dim(" · ")));
654
+ if (r.deliverable === false) {
655
+ console.log(" " + dim(r.message || "This address did not pass live verification, so nothing was charged."));
656
+ }
657
+ const charged = Number(r.charged || 0);
658
+ console.log(
659
+ dim(
660
+ ` ${charged === 0 ? "0 credits charged" : `${charged} credit${charged === 1 ? "" : "s"} charged`}` +
661
+ (r.already_revealed ? " · already in your workspace" : "")
662
+ )
663
+ );
664
+ }
665
+
573
666
  const EXPORT_TERMINAL_OK = ["completed", "done", "ready", "succeeded"];
574
667
  const EXPORT_TERMINAL_FAIL = ["failed", "error", "cancelled", "canceled"];
575
668
 
@@ -1351,6 +1444,9 @@ ${bold("COMMANDS")}
1351
1444
  ${cyan("search")} "<query>" -n 10 Preview matches, details redacted ${dim("(0 contact credits)")}
1352
1445
  ${cyan("sample")} <company.com> Build 25 distinct, live-valid company leads ${dim("(free sample)")}
1353
1446
  ${cyan("reveal")} "<query>" -n 25 Reveal full contact details ${dim("(uses credits; live-verified, pay only for deliverable)")}
1447
+ ${cyan("enrich")} --email <a@b.com> One address → the full person profile ${dim("(1 credit per match; miss = free)")}
1448
+ ${cyan("enrich")} --name "<n>" --domain <d> Find that person + reveal a verified email ${dim("(1 credit per match)")}
1449
+ ${cyan("enrich")} --domain <d> Company profile ${dim("(0 contact credits)")}
1354
1450
  ${cyan("export")} "<query>" -n 1000 -o leads.csv Verified CSV export ${dim("(uses credits)")}
1355
1451
  ${cyan("export status")} <job_id> Status of an existing export ${dim("(free; add --batch for >50k)")}
1356
1452
  ${cyan("export download")} <job_id> -o leads.csv Re-download a finished export ${dim("(free)")}
@@ -1390,6 +1486,7 @@ ${bold("NON-INTERACTIVE USE")} ${dim("(agents, CI, pipes)")}
1390
1486
  ${bold("EXIT CODES")}
1391
1487
  0 ok · 1 error · 2 not authenticated · 3 forbidden (missing scope)
1392
1488
  4 rate limit / daily quota · 5 plan upgrade required
1489
+ ${dim("`enrich` exits 1 when nothing matched, so scripts can branch without parsing the payload.")}
1393
1490
 
1394
1491
  ${bold("EXAMPLES")}
1395
1492
  ${p} count "fintech CFOs in germany"
@@ -1397,6 +1494,9 @@ ${bold("EXAMPLES")}
1397
1494
  ${p} search "heads of procurement" --country Germany -n 10
1398
1495
  ${p} sample recruitcrm.io -o sample.csv
1399
1496
  ${p} export --industry fintech --title CFO --country Germany -n 500 -o cfos.csv
1497
+ ${p} enrich --email patrick@stripe.com --json
1498
+ ${p} enrich --name "Patrick Collison" --domain stripe.com
1499
+ ${p} enrich --domain stripe.com
1400
1500
  ${p} verify ceo@stripe.com
1401
1501
  ${p} verify --file my-list.csv -o verified.csv
1402
1502
 
@@ -1430,6 +1530,7 @@ async function main() {
1430
1530
  search: cmdSearch,
1431
1531
  sample: cmdSample,
1432
1532
  reveal: cmdReveal,
1533
+ enrich: cmdEnrich,
1433
1534
  export: cmdExport,
1434
1535
  list: cmdList,
1435
1536
  verify: cmdVerify,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argorant",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Search, count, reveal, export, and verify B2B contacts from the Argorant database — from your terminal, scripts, or coding agent.",
5
5
  "bin": {
6
6
  "argorant": "bin/argorant.js"