argorant 0.3.0 → 0.4.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 +59 -8
  2. package/package.json +1 -1
package/bin/argorant.js CHANGED
@@ -67,15 +67,18 @@ const VALUE_FLAGS = {
67
67
  "--company": "company_name",
68
68
  "--domain": "company_domain",
69
69
  };
70
- // Boolean filter flags (presence => "true").
70
+ // Boolean filter flags (presence => "true"). --verified-only is positive intent
71
+ // only (deliverable contacts); there is deliberately NO flag to query invalid or
72
+ // any raw verification status — that is never exposed on any surface.
71
73
  const BOOL_FLAGS = {
72
74
  "--has-phone": "has_phone",
73
75
  "--has-linkedin": "has_linkedin",
74
76
  "--has-email": "has_email",
77
+ "--verified-only": "verified_only",
75
78
  };
76
79
 
77
80
  function parseArgs(argv) {
78
- const out = { _: [], filters: {}, limit: null, output: null, file: null, column: null, json: false, yes: false, base: DEFAULT_BASE };
81
+ const out = { _: [], filters: {}, limit: null, output: null, file: null, column: null, json: false, yes: false, base: DEFAULT_BASE, name: null, includeExported: false };
79
82
  for (let i = 0; i < argv.length; i++) {
80
83
  const a = argv[i];
81
84
  if (a === "--json") out.json = true;
@@ -85,6 +88,8 @@ function parseArgs(argv) {
85
88
  else if (a === "-f" || a === "--file") out.file = argv[++i];
86
89
  else if (a === "--column") out.column = argv[++i];
87
90
  else if (a === "--base") out.base = argv[++i];
91
+ else if (a === "--name") out.name = argv[++i];
92
+ else if (a === "--include-exported") out.includeExported = true;
88
93
  else if (a in VALUE_FLAGS) out.filters[VALUE_FLAGS[a]] = argv[++i];
89
94
  else if (a in BOOL_FLAGS) out.filters[BOOL_FLAGS[a]] = "true";
90
95
  else if (a.startsWith("--") && a.includes("=")) {
@@ -302,7 +307,16 @@ async function cmdExport(args) {
302
307
  const ans = await prompt(`Export up to ${bold(limit)} verified contacts to ${bold(dest)}? Uses quota/credits. [y/N] `);
303
308
  if (!/^y(es)?$/i.test(ans)) return console.log(dim("aborted."));
304
309
  }
305
- const create = await request("POST", args.base, "/api/mcp/exports/create", { key, body: { limit, filters: args.filters } });
310
+ // Match the MCP/app defaults so the CLI yields the same rows: business email
311
+ // present by default, and skip rows already exported (override with
312
+ // --include-exported). Verification stays live at export time — only deliverable
313
+ // rows are billed; no verification-status filter is exposed.
314
+ const exportFilters = { ...args.filters };
315
+ if (exportFilters.has_email === undefined) exportFilters.has_email = "true";
316
+ const create = await request("POST", args.base, "/api/mcp/exports/create", {
317
+ key,
318
+ body: { limit, filters: exportFilters, exclude_previously_exported: !args.includeExported },
319
+ });
306
320
  const job = need(create, "export");
307
321
  const statusPath = job.status_api_path || (job.job_id ? `/api/mcp/exports/${job.job_id}` : null);
308
322
  if (!statusPath) {
@@ -331,7 +345,7 @@ async function cmdExport(args) {
331
345
  if (!args.json) process.stdout.write("\n");
332
346
  if (!downloadPath) {
333
347
  if (args.json) return console.log(JSON.stringify(job, null, 2));
334
- return console.log("Export ready but no download path returned. Check `argorant export-list`.");
348
+ return console.log("Export ready but no download path returned yet. Re-run with --json to inspect the job, or check the Exports page in the app.");
335
349
  }
336
350
  await downloadTo(args.base, downloadPath, key, dest);
337
351
  if (args.json) return console.log(JSON.stringify({ ok: true, file: dest, job_id: job.job_id }, null, 2));
@@ -387,7 +401,7 @@ async function cmdVerifyFile(args, key) {
387
401
  if (!emails.length) die("no email addresses found in file (try --column <name>)");
388
402
  const out = args.output || "argorant-verified.csv";
389
403
  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] `);
404
+ const ans = await prompt(`Verify ${bold(emails.length.toLocaleString())} emails? You're billed only for fresh checks from your verification-check pool; recent re-checks are free. [y/N] `);
391
405
  if (!/^y(es)?$/i.test(ans)) return console.log(dim("aborted."));
392
406
  }
393
407
  const all = [];
@@ -406,7 +420,40 @@ async function cmdVerifyFile(args, key) {
406
420
  const csv = ["email,status,deliverable", ...all.map((r) => `${r.email},${r.status},${r.deliverable}`)].join("\n") + "\n";
407
421
  fs.writeFileSync(out, csv);
408
422
  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)`)}`);
423
+ console.log(green("✓") + ` ${bold(all.length.toLocaleString())} verified → ${bold(out)} ${dim(`(${deliverable.toLocaleString()} deliverable · ${charged.toLocaleString()} checks billed · ${cached.toLocaleString()} free)`)}`);
424
+ }
425
+
426
+ // ---- list: save & inspect reusable lead lists (parity with MCP/app). Creating a
427
+ // filtered list is free and does NOT reveal contacts — the server counts the
428
+ // matches itself, so the list reports its real size right away. ----
429
+ async function cmdList(args) {
430
+ const key = requireKey();
431
+ const sub = (args._[0] || "").toLowerCase();
432
+ if (sub === "create") {
433
+ const name = (args.name || "").trim();
434
+ if (!name) die('usage: argorant list create --name "My list" [filters] (e.g. --title CEO --country Germany)');
435
+ delete args.filters.q; // the "create" subcommand word leaks into q via parseArgs
436
+ const body = { name, filters: args.filters, record_type: "person", selection_mode: "filtered" };
437
+ const res = await request("POST", args.base, "/api/mcp/lists/create", { key, body });
438
+ const r = need(res, "list create");
439
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
440
+ const total = Number(r.snapshot_total || 0);
441
+ console.log(green("✓") + ` Created list ${bold("#" + r.list_id)} ${dim("“" + r.name + "”")} — ${bold(total.toLocaleString())} matching contacts`);
442
+ console.log(dim(`Export it with: argorant export ${Object.entries(args.filters).filter(([, v]) => v).map(([k, v]) => `--${k.replace(/_/g, "-")} ${/\s/.test(String(v)) ? `"${v}"` : v}`).join(" ")} -o leads.csv`));
443
+ return;
444
+ }
445
+ if (sub === "status" || sub === "show" || sub === "get") {
446
+ const id = args._[1] || args.name;
447
+ if (!id) die("usage: argorant list status <list_id>");
448
+ const res = await request("GET", args.base, `/api/mcp/lists/${encodeURIComponent(id)}`, { key });
449
+ const r = need(res, "list status");
450
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
451
+ const total = Number(r.snapshot_total ?? r.item_count ?? 0);
452
+ console.log(`${bold("List #" + (r.list_id ?? id))} ${dim("“" + (r.name || "—") + "”")}`);
453
+ console.log(` ${bold(total.toLocaleString())} contacts · ${dim((r.selection_mode || "filtered") + " · " + (r.record_type || "person"))}`);
454
+ return;
455
+ }
456
+ die("usage: argorant list create --name \"…\" [filters] | argorant list status <id>");
410
457
  }
411
458
 
412
459
  function help() {
@@ -424,15 +471,18 @@ ${bold("COMMANDS")}
424
471
  ${cyan("search")} "<query>" -n 10 Preview matches, details redacted ${dim("(free)")}
425
472
  ${cyan("reveal")} "<query>" -n 25 Reveal full contact details ${dim("(uses credits; live-verified, pay only for deliverable)")}
426
473
  ${cyan("export")} "<query>" -n 1000 -o leads.csv Verified CSV export ${dim("(uses credits)")}
474
+ ${cyan("list create")} --name "<n>" [filters] Save a reusable list ${dim("(free)")}
475
+ ${cyan("list status")} <id> Show a saved list's size ${dim("(free)")}
427
476
  ${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)")}
477
+ ${cyan("verify")} --file emails.csv -o out.csv Bulk-verify your own list ${dim("(recent re-checks free)")}
429
478
 
430
479
  ${bold("FILTERS")}
431
480
  --title <t> --exclude-title <t> --seniority <s> --department <d>
432
481
  --industry <i> --country <c> --geography <r> --state <s>
433
482
  --city <c> --company <name> --domain <domain>
434
- --has-phone --has-linkedin --has-email
483
+ --has-phone --has-linkedin --has-email --verified-only
435
484
  ${dim("--title is abbreviation-aware (CFO ↔ Chief Financial Officer).")}
485
+ ${dim("--verified-only keeps deliverable contacts; export verifies live & bills only valid.")}
436
486
  ${dim("--country / --geography accept regions: Europe, EMEA, DACH, Nordics, APAC, LATAM, GCC…")}
437
487
 
438
488
  ${bold("OPTIONS")}
@@ -469,6 +519,7 @@ async function main() {
469
519
  search: cmdSearch,
470
520
  reveal: cmdReveal,
471
521
  export: cmdExport,
522
+ list: cmdList,
472
523
  verify: cmdVerify,
473
524
  };
474
525
  const fn = table[cmd];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argorant",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
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"