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.
- package/bin/argorant.js +59 -8
- 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
|
-
|
|
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.
|
|
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?
|
|
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
|
|
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("(
|
|
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