argorant 0.2.0 → 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.
Files changed (2) hide show
  1. package/bin/argorant.js +76 -6
  2. package/package.json +2 -2
package/bin/argorant.js CHANGED
@@ -2,7 +2,7 @@
2
2
  "use strict";
3
3
 
4
4
  // Argorant CLI — a thin, dependency-free wrapper over the Argorant REST API.
5
- // Search, count, reveal, and export verified B2B contacts from the terminal.
5
+ // Search, count, reveal, export, and verify B2B contacts from the terminal.
6
6
  // Auth: an Argorant API key (ag_live_*) via `argorant login`, or ARGORANT_API_KEY.
7
7
 
8
8
  const https = require("https");
@@ -66,7 +66,6 @@ const VALUE_FLAGS = {
66
66
  "--city": "city",
67
67
  "--company": "company_name",
68
68
  "--domain": "company_domain",
69
- "--verify-status": "verify_status",
70
69
  };
71
70
  // Boolean filter flags (presence => "true").
72
71
  const BOOL_FLAGS = {
@@ -76,13 +75,15 @@ const BOOL_FLAGS = {
76
75
  };
77
76
 
78
77
  function parseArgs(argv) {
79
- const out = { _: [], filters: {}, limit: null, output: null, json: false, yes: false, base: DEFAULT_BASE };
78
+ const out = { _: [], filters: {}, limit: null, output: null, file: null, column: null, json: false, yes: false, base: DEFAULT_BASE };
80
79
  for (let i = 0; i < argv.length; i++) {
81
80
  const a = argv[i];
82
81
  if (a === "--json") out.json = true;
83
82
  else if (a === "--yes" || a === "-y") out.yes = true;
84
83
  else if (a === "-n" || a === "--limit") out.limit = parseInt(argv[++i], 10);
85
84
  else if (a === "-o" || a === "--output") out.output = argv[++i];
85
+ else if (a === "-f" || a === "--file") out.file = argv[++i];
86
+ else if (a === "--column") out.column = argv[++i];
86
87
  else if (a === "--base") out.base = argv[++i];
87
88
  else if (a in VALUE_FLAGS) out.filters[VALUE_FLAGS[a]] = argv[++i];
88
89
  else if (a in BOOL_FLAGS) out.filters[BOOL_FLAGS[a]] = "true";
@@ -344,6 +345,70 @@ async function cmdExport(args) {
344
345
  console.log(green("✓") + ` Saved ${rows != null ? bold(rows.toLocaleString()) + " rows → " : ""}${bold(dest)}`);
345
346
  }
346
347
 
348
+ // ---- verify: external email verification (own lists) — the verification pool,
349
+ // separate from contact credits. 60-day re-checks are free. ----
350
+ const EMAIL_RE = /[^\s,;"']+@[^\s,;"']+\.[^\s,;"']+/;
351
+
352
+ async function cmdVerify(args) {
353
+ const key = requireKey();
354
+ if (args.file) return cmdVerifyFile(args, key);
355
+ const email = (args._[0] || args.filters.q || "").trim().toLowerCase();
356
+ if (!email || !email.includes("@")) {
357
+ die('usage: argorant verify <email> | argorant verify --file emails.csv [-o out.csv]');
358
+ }
359
+ const res = await request("POST", args.base, "/api/mcp/email/verify", { key, body: { email } });
360
+ const r = need(res, "verify");
361
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
362
+ const tag = r.deliverable ? green(r.status) : dim(r.status);
363
+ console.log(` ${bold(email)} → ${tag}${r.deliverable ? " " + green("✓ deliverable") : ""}`);
364
+ }
365
+
366
+ async function cmdVerifyFile(args, key) {
367
+ let text;
368
+ try { text = fs.readFileSync(args.file, "utf8"); } catch { die(`cannot read file: ${args.file}`); }
369
+ const lines = text.split(/\r?\n/).filter((l) => l.trim());
370
+ if (!lines.length) die("file is empty");
371
+ // Use the named/auto-detected email column if the file looks like a CSV with a
372
+ // header; otherwise scan every line for an address.
373
+ const header = lines[0].split(",").map((h) => h.trim().toLowerCase().replace(/^["']|["']$/g, ""));
374
+ const colIdx = args.column
375
+ ? header.indexOf(args.column.toLowerCase())
376
+ : header.findIndex((h) => h === "email" || h.includes("email"));
377
+ let emails = [];
378
+ if (colIdx >= 0) {
379
+ for (let i = 1; i < lines.length; i++) {
380
+ const m = (lines[i].split(",")[colIdx] || "").match(EMAIL_RE);
381
+ if (m) emails.push(m[0].toLowerCase());
382
+ }
383
+ } else {
384
+ for (const l of lines) { const m = l.match(EMAIL_RE); if (m) emails.push(m[0].toLowerCase()); }
385
+ }
386
+ emails = [...new Set(emails)];
387
+ if (!emails.length) die("no email addresses found in file (try --column <name>)");
388
+ const out = args.output || "argorant-verified.csv";
389
+ if (!args.yes && !args.json && process.stdin.isTTY) {
390
+ const ans = await prompt(`Verify ${bold(emails.length.toLocaleString())} emails? Fresh checks use your verification-check pool; addresses checked in the last 60 days are free. [y/N] `);
391
+ if (!/^y(es)?$/i.test(ans)) return console.log(dim("aborted."));
392
+ }
393
+ const all = [];
394
+ let charged = 0, cached = 0;
395
+ for (let i = 0; i < emails.length; i += 500) {
396
+ const chunk = emails.slice(i, i + 500);
397
+ const res = await request("POST", args.base, "/api/mcp/email/verify/batch", { key, body: { emails: chunk } });
398
+ const r = need(res, "verify");
399
+ charged += r.checks_charged || 0;
400
+ cached += r.cached || 0;
401
+ for (const row of r.results || []) all.push(row);
402
+ if (!args.json) process.stdout.write(`\r${dim(`verified ${Math.min(i + 500, emails.length).toLocaleString()}/${emails.length.toLocaleString()}`)}`);
403
+ }
404
+ if (!args.json) process.stdout.write("\n");
405
+ if (args.json) return console.log(JSON.stringify({ ok: true, total: all.length, checks_charged: charged, cached, results: all }, null, 2));
406
+ const csv = ["email,status,deliverable", ...all.map((r) => `${r.email},${r.status},${r.deliverable}`)].join("\n") + "\n";
407
+ fs.writeFileSync(out, csv);
408
+ const deliverable = all.filter((r) => r.deliverable).length;
409
+ console.log(green("✓") + ` ${bold(all.length.toLocaleString())} verified → ${bold(out)} ${dim(`(${deliverable.toLocaleString()} deliverable · ${charged.toLocaleString()} checks billed · ${cached.toLocaleString()} free cache hits)`)}`);
410
+ }
411
+
347
412
  function help() {
348
413
  const p = bold("argorant");
349
414
  console.log(`
@@ -357,13 +422,15 @@ ${bold("COMMANDS")}
357
422
  ${cyan("whoami")} Account, scopes, and daily quota
358
423
  ${cyan("count")} "<query>" Count matching contacts ${dim("(free)")}
359
424
  ${cyan("search")} "<query>" -n 10 Preview matches, details redacted ${dim("(free)")}
360
- ${cyan("reveal")} "<query>" -n 25 Reveal full contact details ${dim("(uses quota)")}
361
- ${cyan("export")} "<query>" -n 1000 -o leads.csv Verified CSV export ${dim("(uses quota)")}
425
+ ${cyan("reveal")} "<query>" -n 25 Reveal full contact details ${dim("(uses credits; live-verified, pay only for deliverable)")}
426
+ ${cyan("export")} "<query>" -n 1000 -o leads.csv Verified CSV export ${dim("(uses credits)")}
427
+ ${cyan("verify")} <email> Verify one of your own emails ${dim("(verification pool)")}
428
+ ${cyan("verify")} --file emails.csv -o out.csv Bulk-verify your own list ${dim("(60-day re-checks free)")}
362
429
 
363
430
  ${bold("FILTERS")}
364
431
  --title <t> --exclude-title <t> --seniority <s> --department <d>
365
432
  --industry <i> --country <c> --geography <r> --state <s>
366
- --city <c> --company <name> --domain <domain> --verify-status <v>
433
+ --city <c> --company <name> --domain <domain>
367
434
  --has-phone --has-linkedin --has-email
368
435
  ${dim("--title is abbreviation-aware (CFO ↔ Chief Financial Officer).")}
369
436
  ${dim("--country / --geography accept regions: Europe, EMEA, DACH, Nordics, APAC, LATAM, GCC…")}
@@ -377,6 +444,8 @@ ${bold("EXAMPLES")}
377
444
  ${p} count "fintech CFOs in germany"
378
445
  ${p} search "heads of procurement" --country Germany -n 10
379
446
  ${p} export --industry fintech --title CFO --country Germany -n 500 -o cfos.csv
447
+ ${p} verify ceo@stripe.com
448
+ ${p} verify --file my-list.csv -o verified.csv
380
449
 
381
450
  Docs: ${cyan("https://argorant.com/docs/cli")}
382
451
  `);
@@ -400,6 +469,7 @@ async function main() {
400
469
  search: cmdSearch,
401
470
  reveal: cmdReveal,
402
471
  export: cmdExport,
472
+ verify: cmdVerify,
403
473
  };
404
474
  const fn = table[cmd];
405
475
  if (!fn) die(`unknown command: ${cmd}\nRun \`argorant help\` for usage.`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "argorant",
3
- "version": "0.2.0",
4
- "description": "Search, count, reveal, and export verified B2B contacts from the Argorant database \u2014 from your terminal, scripts, or coding agent.",
3
+ "version": "0.3.0",
4
+ "description": "Search, count, reveal, export, and verify B2B contacts from the Argorant database \u2014 from your terminal, scripts, or coding agent.",
5
5
  "bin": {
6
6
  "argorant": "bin/argorant.js"
7
7
  },