argorant 0.7.0 → 0.8.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 +77 -2
  2. package/package.json +1 -1
package/bin/argorant.js CHANGED
@@ -1568,6 +1568,47 @@ async function cmdInboxThreads(argv) {
1568
1568
  die(`unknown inbox subcommand: ${sub} (list, read, reply, classify)`);
1569
1569
  }
1570
1570
 
1571
+ async function campaignsAnalytics(argv) {
1572
+ const key = requireKey();
1573
+ const args = readFlags(argv, { "--days": "days" });
1574
+ const identifier = args._[0];
1575
+ if (!identifier) die("usage: argorant campaigns analytics <campaign> [--days 30]");
1576
+ const campaignId = await resolveCampaign(args.base, key, identifier);
1577
+ const r = need(await request("GET", args.base, `/api/v1/campaigns/${campaignId}/analytics`, { key, query: { days: args.days || 30 } }), "campaigns analytics");
1578
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1579
+ const t = r.totals || {};
1580
+ console.log(`${bold("last " + r.days + " days")} sent ${Number(t.sent || 0).toLocaleString()} · replies ${Number(t.replies || 0).toLocaleString()} (${t.reply_rate}%) · positive ${Number(t.positive || 0).toLocaleString()} · bounces ${Number(t.bounces || 0).toLocaleString()} (${t.bounce_rate}%) · unsubscribes ${Number(t.unsubscribes || 0).toLocaleString()}`);
1581
+ for (const d of r.series || []) console.log(dim(` ${d.day} sent ${d.sent} replies ${d.replies} positive ${d.positive} bounces ${d.bounces}`));
1582
+ }
1583
+
1584
+ async function cmdBlocklist(argv) {
1585
+ const sub = argv[0] || "list";
1586
+ const key = requireKey();
1587
+ if (sub === "list") {
1588
+ const args = readFlags(argv.slice(1));
1589
+ const r = need(await request("GET", args.base, "/api/v1/blocklist", { key }), "blocklist");
1590
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1591
+ if (!(r.entries || []).length) return console.log(dim("Blocklist is empty."));
1592
+ for (const e of r.entries) console.log(`${e.type === "domain" ? "@" : " "}${e.value} ${dim(e.reason || "")}${e.yours ? "" : dim(" (Argorant-wide)")} ${dim(e.id)}`);
1593
+ return;
1594
+ }
1595
+ if (sub === "add") {
1596
+ const args = readFlags(argv.slice(1), { "--reason": "reason" });
1597
+ const value = args._[0]; if (!value) die("usage: argorant blocklist add <email|domain> [--reason text]");
1598
+ const r = need(await request("POST", args.base, "/api/v1/blocklist", { key, body: { value, reason: args.reason || "manual" } }), "blocklist add");
1599
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1600
+ return console.log(green("✓") + ` Blocked ${r.type} ${r.value}` + (r.leads_suppressed ? dim(` (${r.leads_suppressed} enrolled lead(s) suppressed)`) : ""));
1601
+ }
1602
+ if (sub === "remove") {
1603
+ const args = readFlags(argv.slice(1));
1604
+ const id = args._[0]; if (!id) die("usage: argorant blocklist remove <entry-id>");
1605
+ const r = need(await request("DELETE", args.base, `/api/v1/blocklist/${encodeURIComponent(id)}`, { key }), "blocklist remove");
1606
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1607
+ return console.log(green("✓") + ` Removed ${r.removed}`);
1608
+ }
1609
+ die(`unknown blocklist subcommand: ${sub} (list, add, remove)`);
1610
+ }
1611
+
1571
1612
  function campaignsHelp() {
1572
1613
  const p = bold("argorant campaigns");
1573
1614
  console.log(`
@@ -1590,6 +1631,7 @@ ${bold("USAGE")}
1590
1631
  ${p} senders set <campaign> --emails a@x.com,b@y.com | --all
1591
1632
  ${p} senders remove <campaign> <mailbox>
1592
1633
  ${p} replies <campaign> [-n 50]
1634
+ ${p} analytics <campaign> [--days 30] ${dim("daily sent / replies / positive / bounces")}
1593
1635
  ${p} delete <campaign> [-y]
1594
1636
  ${p} launch <campaign> [-y] ${dim("asks for confirmation; sends real email")}
1595
1637
  ${p} pause <campaign>
@@ -1597,7 +1639,10 @@ ${bold("USAGE")}
1597
1639
  ${p} status <campaign>
1598
1640
 
1599
1641
  ${bold("argorant inboxes")} list
1642
+ ${bold("argorant inboxes")} set <mailbox> [--daily-limit 40] [--warmup|--no-warmup] [--active|--inactive] [--timezone <tz>]
1643
+ ${bold("argorant inboxes")} disconnect <mailbox>
1600
1644
  ${bold("argorant inboxes")} connect-google --admin <admin@yourdomain.com> [--emails a,b | --all]
1645
+ ${bold("argorant blocklist")} list | add <email|domain> | remove <id>
1601
1646
 
1602
1647
  ${bold("argorant inbox")} list [--folder replies|interested|not_interested|out_of_office|bounces|unsubscribes|all] [--campaign <c>] [--q text]
1603
1648
  ${bold("argorant inbox")} read <thread-id>
@@ -1634,6 +1679,7 @@ async function cmdCampaigns(argv) {
1634
1679
  set: campaignsUpdate,
1635
1680
  delete: campaignsDelete,
1636
1681
  replies: campaignsReplies,
1682
+ analytics: campaignsAnalytics,
1637
1683
  emails: campaignsEmails,
1638
1684
  steps: campaignsSteps,
1639
1685
  senders: campaignsSenders,
@@ -1666,6 +1712,33 @@ async function cmdInboxes(argv) {
1666
1712
  }
1667
1713
  return;
1668
1714
  }
1715
+ if (sub === "set") {
1716
+ const args = readFlags(argv.slice(1), { "--daily-limit": "dailyLimit", "--name": "name", "--timezone": "timezone" }, { "--warmup": "warmupOn", "--no-warmup": "warmupOff", "--active": "activeOn", "--inactive": "activeOff" });
1717
+ const email = args._[0]; if (!email) die("usage: argorant inboxes set <mailbox> [--daily-limit 40] [--warmup|--no-warmup] [--active|--inactive] [--name \"…\"] [--timezone Europe/Berlin]");
1718
+ const body = {};
1719
+ if (args.dailyLimit) body.daily_limit = parseLimit(args.dailyLimit, "--daily-limit");
1720
+ if (args.warmupOn) body.warmup = true; if (args.warmupOff) body.warmup = false;
1721
+ if (args.activeOn) body.active = true; if (args.activeOff) body.active = false;
1722
+ if (args.name !== undefined) body.display_name = args.name;
1723
+ if (args.timezone) body.timezone = args.timezone;
1724
+ if (!Object.keys(body).length) die("nothing to change");
1725
+ const r = need(await request("PATCH", args.base, `/api/v1/inboxes/${encodeURIComponent(email)}`, { key, body }), "inboxes set");
1726
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1727
+ const i = r.inbox || {};
1728
+ return console.log(green("✓") + ` ${i.email}: ${i.daily_limit} a day · warm-up ${i.warmup ? "on" : "off"} · ${i.active ? "active" : "inactive"} · ${i.timezone || ""}`);
1729
+ }
1730
+ if (sub === "disconnect") {
1731
+ const args = readFlags(argv.slice(1));
1732
+ const email = args._[0]; if (!email) die("usage: argorant inboxes disconnect <mailbox> [-y]");
1733
+ if (!args.yes) {
1734
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1735
+ const ok = await new Promise((r) => rl.question(`Disconnect ${email}? It is detached from every campaign. [y/N] `, (a) => { rl.close(); r(/^y/i.test(a)); }));
1736
+ if (!ok) return console.log(dim("aborted"));
1737
+ }
1738
+ const r = need(await request("DELETE", args.base, `/api/v1/inboxes/${encodeURIComponent(email)}`, { key }), "inboxes disconnect");
1739
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1740
+ return console.log(green("✓") + ` Disconnected ${r.disconnected}`);
1741
+ }
1669
1742
  if (sub === "connect-google") {
1670
1743
  const args = readFlags(argv.slice(1), { "--admin": "admin", "--emails": "emails" }, { "--all": "all" });
1671
1744
  if (!args.admin) die("usage: argorant inboxes connect-google --admin admin@yourdomain.com [--emails a@x.com,b@x.com | --all]");
@@ -1690,7 +1763,7 @@ async function cmdInboxes(argv) {
1690
1763
  for (const c of r.connected || []) console.log(dim(` ${c.email} · ${c.health || "ok"}`));
1691
1764
  return;
1692
1765
  }
1693
- die(`unknown inboxes subcommand: ${sub} (list, connect-google)`);
1766
+ die(`unknown inboxes subcommand: ${sub} (list, set, disconnect, connect-google)`);
1694
1767
  }
1695
1768
 
1696
1769
  function help() {
@@ -1723,6 +1796,7 @@ ${bold("COMMANDS")}
1723
1796
  ${cyan("campaigns")} ... Email outreach: create, write, enroll, launch ${dim("(argorant campaigns help)")}
1724
1797
  ${cyan("inboxes")} list | connect-google Connected mailboxes; connect a Google Workspace
1725
1798
  ${cyan("inbox")} list | read | reply | classify Replies across campaigns; answer in-thread
1799
+ ${cyan("blocklist")} list | add | remove Addresses and domains never contacted
1726
1800
 
1727
1801
  ${bold("FILTERS")}
1728
1802
  --keywords <k> Comma = OR. The widest, most reliable filter - prefer it
@@ -1780,9 +1854,10 @@ async function main() {
1780
1854
  // `campaigns` has its own flag vocabulary (--step, --count, --pool, --csv, ...)
1781
1855
  // handled by readFlags — it never goes through the generic filter parser
1782
1856
  // below, which would reject those flags as unknown.
1783
- if (cmd === "campaigns" || cmd === "campaign" || cmd === "inboxes" || cmd === "mailboxes" || cmd === "inbox" || cmd === "unibox") {
1857
+ if (cmd === "campaigns" || cmd === "campaign" || cmd === "inboxes" || cmd === "mailboxes" || cmd === "inbox" || cmd === "unibox" || cmd === "blocklist") {
1784
1858
  try {
1785
1859
  if (cmd.startsWith("campaign")) await cmdCampaigns(argv.slice(1));
1860
+ else if (cmd === "blocklist") await cmdBlocklist(argv.slice(1));
1786
1861
  else if (cmd === "inbox" || cmd === "unibox") await cmdInboxThreads(argv.slice(1));
1787
1862
  else await cmdInboxes(argv.slice(1));
1788
1863
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argorant",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
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"