argorant 0.8.0 → 0.10.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/README.md CHANGED
@@ -152,6 +152,30 @@ argorant inboxes connect-google --admin admin@yourdomain.com # lists mailb
152
152
  argorant inboxes connect-google --admin admin@yourdomain.com --emails a@x.com,b@x.com # or --all
153
153
  ```
154
154
 
155
+ New mailboxes on their own new domains, ordered from the terminal. The quote shows every domain with its
156
+ first-year and renewal price, the mailboxes, the total and how it is paid. When your workspace saved its card
157
+ for API and agent purchases (you ticked that box when you paid an earlier order, and the setting in
158
+ Settings > Billing is on), the order is charged to that card right away, within any limit you set there, and a
159
+ receipt is emailed to the workspace. Otherwise the order returns a payment link that you pay yourself.
160
+ Nothing is bought before the payment, and setup starts once it went through. Warm-up is included in every
161
+ plan; switch it on per mailbox with `argorant inboxes set <mailbox> --warmup`.
162
+
163
+ Domains can end in .com, .net, .org, .co, .sbs, .biz or .info (`argorant inboxes endings` prints the live list).
164
+
165
+ ```
166
+ argorant inboxes suggest acme.com [--ending com] # free domain names with prices
167
+ argorant inboxes quote --mailboxes 100 --company acme.com # or --domains getacme.com,tryacme.co
168
+ argorant inboxes order <quote-id> --senders "Elena Vaughn" # asks first; saved card or a payment link
169
+ argorant inboxes order <quote-id> --senders "Elena Vaughn" --pay-with link # always a payment link
170
+ argorant inboxes status [<order-id>] # steps, domains, renewal, bundles
171
+ argorant inboxes renewal <order-id> <domain> --off # Stop renewal (nothing is charged now)
172
+ argorant inboxes renewal <order-id> <domain> --on # Turn renewal back on (billed shortly before it renews)
173
+ argorant inboxes cancel-bundle <order-id> <bundle-id> --reason too_expensive [--note "..."] [--keep-domains]
174
+ ```
175
+
176
+ Cancel reasons: `too_expensive`, `not_sending_enough`, `deliverability`, `switching_provider`,
177
+ `campaign_finished`, `other`. `cancel-domain` still works as the old name of `renewal --off`.
178
+
155
179
  `<campaign>` accepts an id or an unambiguous case-insensitive name prefix. Every address is verified before
156
180
  it is enrolled; only valid ones send unless `--include-catch-all`. Variables: `{{first_name}}`, `{{last_name}}`,
157
181
  `{{company}}`, `{{title}}`; spintax `{a|b}` varies phrasing. `--query "..."` filter enrollment stays available
package/bin/argorant.js CHANGED
@@ -5,6 +5,7 @@
5
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
+ const crypto = require("crypto");
8
9
  const https = require("https");
9
10
  const http = require("http");
10
11
  const fs = require("fs");
@@ -178,7 +179,7 @@ function parseArgs(argv) {
178
179
  }
179
180
 
180
181
  // ---- HTTP ----
181
- function request(method, base, urlPath, { key, query, body } = {}) {
182
+ function request(method, base, urlPath, { key, query, body, headers: extraHeaders } = {}) {
182
183
  return new Promise((resolve, reject) => {
183
184
  let u;
184
185
  try {
@@ -192,7 +193,7 @@ function request(method, base, urlPath, { key, query, body } = {}) {
192
193
  }
193
194
  }
194
195
  const payload = body ? Buffer.from(JSON.stringify(body)) : null;
195
- const headers = { Accept: "application/json", "User-Agent": `argorant-cli/${VERSION}` };
196
+ const headers = { Accept: "application/json", "User-Agent": `argorant-cli/${VERSION}`, ...(extraHeaders || {}) };
196
197
  if (key) headers["Authorization"] = `Bearer ${key}`;
197
198
  if (payload) {
198
199
  headers["Content-Type"] = "application/json";
@@ -912,27 +913,28 @@ async function cmdVerifyFile(args, key) {
912
913
  const out = ensureWritable(args.output || "argorant-verified.csv");
913
914
  // Interactive-only by design — see reveal/export.
914
915
  if (!args.yes && !args.json && process.stdin.isTTY) {
915
- 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] `);
916
+ const ans = await prompt(`Verify ${bold(emails.length.toLocaleString())} emails? Fresh checks cost half a credit each; recent re-checks are free. [y/N] `);
916
917
  if (!/^y(es)?$/i.test(ans)) return console.log(dim("aborted."));
917
918
  }
918
919
  const all = [];
919
- let charged = 0, cached = 0;
920
+ let charged = 0, credits = 0, cached = 0;
920
921
  for (let i = 0; i < emails.length; i += 500) {
921
922
  const chunk = emails.slice(i, i + 500);
922
923
  const res = await request("POST", args.base, "/api/mcp/email/verify/batch", { key, body: { emails: chunk } });
923
924
  const r = need(res, "verify");
924
925
  charged += r.checks_charged || 0;
926
+ credits += r.credits_charged || 0;
925
927
  cached += r.cached || 0;
926
928
  for (const row of r.results || []) all.push(row);
927
929
  if (!args.json) process.stdout.write(`\r${dim(`verified ${Math.min(i + 500, emails.length).toLocaleString()}/${emails.length.toLocaleString()}`)}`);
928
930
  }
929
931
  if (!args.json) process.stdout.write("\n");
930
- if (args.json) return console.log(JSON.stringify({ ok: true, total: all.length, checks_charged: charged, cached, results: all }, null, 2));
932
+ if (args.json) return console.log(JSON.stringify({ ok: true, total: all.length, checks_charged: charged, credits_charged: credits, cached, results: all }, null, 2));
931
933
  const q = (v) => `"${String(v == null ? "" : v).replace(/"/g, '""')}"`;
932
934
  const csv = ["email,status,deliverable", ...all.map((r) => [r.email, r.status, r.deliverable].map(q).join(","))].join("\n") + "\n";
933
935
  fs.writeFileSync(out, csv);
934
936
  const deliverable = all.filter((r) => r.deliverable).length;
935
- console.log(green("✓") + ` ${bold(all.length.toLocaleString())} verified → ${bold(out)} ${dim(`(${deliverable.toLocaleString()} deliverable · ${charged.toLocaleString()} checks billed · ${cached.toLocaleString()} free)`)}`);
937
+ console.log(green("✓") + ` ${bold(all.length.toLocaleString())} verified → ${bold(out)} ${dim(`(${deliverable.toLocaleString()} deliverable · ${charged.toLocaleString()} fresh checks, ${credits.toLocaleString()} credits · ${cached.toLocaleString()} free)`)}`);
936
938
  }
937
939
 
938
940
  // ---- list: save & inspect reusable lead lists (parity with MCP/app). Creating a
@@ -1565,7 +1567,17 @@ async function cmdInboxThreads(argv) {
1565
1567
  console.log(green("✓") + ` ${r.classification}`);
1566
1568
  return;
1567
1569
  }
1568
- die(`unknown inbox subcommand: ${sub} (list, read, reply, classify)`);
1570
+ if (sub === "forward") {
1571
+ const args = readFlags(argv.slice(1), { "--to": "to", "--note": "note" });
1572
+ const id = args._[0]; if (!id) die("usage: argorant inbox forward <thread-id> --to someone@company.com[,other@company.com] [--note \"...\"]");
1573
+ const to = String(args.to || "").split(",").map((x) => x.trim()).filter(Boolean);
1574
+ if (!to.length) die("give at least one address with --to");
1575
+ const r = need(await request("POST", args.base, `/api/v1/inbox/${encodeURIComponent(id)}/forward`, { key, body: { to, note: args.note || "" } }), "inbox forward");
1576
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1577
+ console.log(green("✓") + ` Forwarded to ${to.join(", ")} from ${r.from || "the mailbox"}`);
1578
+ return;
1579
+ }
1580
+ die(`unknown inbox subcommand: ${sub} (list, read, reply, forward, classify)`);
1569
1581
  }
1570
1582
 
1571
1583
  async function campaignsAnalytics(argv) {
@@ -1642,6 +1654,14 @@ ${bold("USAGE")}
1642
1654
  ${bold("argorant inboxes")} set <mailbox> [--daily-limit 40] [--warmup|--no-warmup] [--active|--inactive] [--timezone <tz>]
1643
1655
  ${bold("argorant inboxes")} disconnect <mailbox>
1644
1656
  ${bold("argorant inboxes")} connect-google --admin <admin@yourdomain.com> [--emails a,b | --all]
1657
+ ${bold("argorant inboxes")} endings ${dim("the domain endings on offer (com, net, org, co, sbs, biz, info)")}
1658
+ ${bold("argorant inboxes")} suggest <website|brand> [--ending com] [-n 20] ${dim("free domain names with prices")}
1659
+ ${bold("argorant inboxes")} quote --mailboxes 100 [--company acme.com] [--domains a.com,b.com] [--currency usd|eur] [--billing month|quarter]
1660
+ ${bold("argorant inboxes")} order <quote-id> --senders "Elena Vaughn[,Mark Hill]" [--website acme.com] [--pay-with link] [-y] ${dim("the saved card when your workspace allows it, else a payment link")}
1661
+ ${bold("argorant inboxes")} status [<order-id>]
1662
+ ${bold("argorant inboxes")} renewal <order-id> <domain> --off|--on [-y] ${dim("Stop renewal / Turn renewal back on; nothing is charged now")}
1663
+ ${bold("argorant inboxes")} cancel-bundle <order-id> <bundle-id> --reason <reason> [--note "..."] [--keep-domains] [-y]
1664
+ ${dim(" reasons: too_expensive, not_sending_enough, deliverability, switching_provider, campaign_finished, other")}
1645
1665
  ${bold("argorant blocklist")} list | add <email|domain> | remove <id>
1646
1666
 
1647
1667
  ${bold("argorant inbox")} list [--folder replies|interested|not_interested|out_of_office|bounces|unsubscribes|all] [--campaign <c>] [--q text]
@@ -1763,7 +1783,162 @@ async function cmdInboxes(argv) {
1763
1783
  for (const c of r.connected || []) console.log(dim(` ${c.email} · ${c.health || "ok"}`));
1764
1784
  return;
1765
1785
  }
1766
- die(`unknown inboxes subcommand: ${sub} (list, set, disconnect, connect-google)`);
1786
+ if (sub === "endings") {
1787
+ const args = readFlags(argv.slice(1));
1788
+ const r = need(await request("GET", args.base, "/api/v1/inboxes/endings", { key }), "inboxes endings");
1789
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1790
+ return console.log((r.endings || []).map((e) => "." + e).join(" "));
1791
+ }
1792
+ if (sub === "suggest") {
1793
+ const args = readFlags(argv.slice(1), { "-n": "limit", "--limit": "limit", "--ending": "ending", "--tld": "ending" });
1794
+ const company = args._.join(" ").trim();
1795
+ if (!company) die("usage: argorant inboxes suggest <website|brand> [--ending com] [-n 20]");
1796
+ const query = { company, limit: args.limit ? parseLimit(args.limit, "--limit") : 20 };
1797
+ if (args.ending) query.ending = String(args.ending).replace(/^\./, "").toLowerCase();
1798
+ const r = need(await request("GET", args.base, "/api/v1/inboxes/domains/suggest", { key, query }), "inboxes suggest");
1799
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1800
+ for (const d of r.results || []) {
1801
+ const price = d.first_year ? `${money(d.first_year.usd, "usd")} first year, ${money(d.renewal.usd, "usd")}/year after` : "";
1802
+ console.log(`${d.available ? green("✓") : dim("x")} ${d.available ? bold(d.name) : dim(d.name)} ${dim(d.available ? price : "taken")}`);
1803
+ }
1804
+ return;
1805
+ }
1806
+ if (sub === "quote") {
1807
+ const args = readFlags(argv.slice(1), { "--mailboxes": "mailboxes", "--daily-volume": "dailyVolume", "--domains": "domains",
1808
+ "--company": "company", "--currency": "currency", "--billing": "billing" });
1809
+ if (!args.mailboxes && !args.dailyVolume) die("usage: argorant inboxes quote --mailboxes 100 [--company acme.com] [--domains a.com,b.com] [--currency usd|eur] [--billing month|quarter] (or --daily-volume 500)");
1810
+ const body = {
1811
+ mailboxes: args.mailboxes ? parseLimit(args.mailboxes, "--mailboxes") : null,
1812
+ daily_volume: args.dailyVolume ? parseLimit(args.dailyVolume, "--daily-volume") : null,
1813
+ domains: args.domains ? String(args.domains).split(",").map((x) => x.trim()).filter(Boolean) : [],
1814
+ company: args.company || "",
1815
+ currency: (args.currency || "usd").toLowerCase(),
1816
+ billing: (args.billing || "month").toLowerCase(),
1817
+ };
1818
+ const q = need(await request("POST", args.base, "/api/v1/inboxes/quote", { key, body }), "inboxes quote");
1819
+ if (args.json) return console.log(JSON.stringify(q, null, 2));
1820
+ const cur = q.currency;
1821
+ console.log(bold(`${q.plan.mailboxes} mailboxes on ${q.plan.domains} new domains`) + dim(` (${q.plan.daily_capacity} cold emails a day)`));
1822
+ for (const d of q.domains || []) {
1823
+ console.log(` ${d.available ? green("✓") : red("x")} ${d.name} ${money(d.first_year_cents, cur)} first year, renews at ${money(d.renewal_per_year_cents, cur)}/year${d.available ? "" : red(" not available")}${d.suggested ? dim(" suggested") : ""}`);
1824
+ }
1825
+ console.log(` Mailboxes: ${money(q.mailboxes.price_cents, cur)} per ${q.mailboxes.billing}`);
1826
+ console.log(bold(`Due today: ${money(q.due_today_cents, cur)}`) + ` then ${money(q.recurring_cents, cur)} per ${q.mailboxes.billing}; domains renew at ${money(q.renewal_per_year_cents, cur)} a year in total`);
1827
+ console.log(dim(q.currency_rule));
1828
+ for (const line of q.included || []) console.log(dim(` · ${line}`));
1829
+ const card = q.payment && q.payment.saved_card;
1830
+ if (card && card.usable) console.log(bold(`Paid with: the saved ${card.card}, charged right away when you order (within 15 minutes of this quote)`));
1831
+ else if (card) console.log(dim(`Paid with: a payment link${card.reason ? ` (saved card: ${card.reason})` : ""}`));
1832
+ console.log(`\nQuote ${bold(q.quote_id)} ${dim(`(valid until ${String(q.expires_at).slice(0, 16).replace("T", " ")} UTC)`)}`);
1833
+ if (q.can_order) console.log(dim(`Order it: argorant inboxes order ${q.quote_id} --senders "First Last"`));
1834
+ else console.log(yellow(q.next_step));
1835
+ return;
1836
+ }
1837
+ if (sub === "order") {
1838
+ const args = readFlags(argv.slice(1), { "--senders": "senders", "--sender": "senders", "--website": "website", "--pay-with": "payWith" });
1839
+ const quoteId = args._[0];
1840
+ if (!quoteId || !args.senders) die("usage: argorant inboxes order <quote-id> --senders \"Elena Vaughn[,Mark Hill]\" [--website acme.com] [--pay-with link] [-y]");
1841
+ const payWith = String(args.payWith || "auto").toLowerCase();
1842
+ if (!["auto", "link"].includes(payWith)) die("--pay-with is link (always a payment link); leave it out for the saved card when allowed");
1843
+ const senders = String(args.senders).split(",").map((x) => x.trim()).filter(Boolean).map((full) => {
1844
+ const parts = full.split(/\s+/);
1845
+ if (parts.length < 2) die(`sender "${full}" needs a first and a last name`);
1846
+ return { first_name: parts[0], last_name: parts.slice(1).join(" ") };
1847
+ });
1848
+ if (!args.yes) {
1849
+ const how = payWith === "link" ? "You get a payment link; nothing is bought before you pay it."
1850
+ : "If your workspace saved its card for API and agent purchases (the quote says so), that card is charged now; otherwise you get a payment link.";
1851
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1852
+ const ok = await new Promise((r) => rl.question(`Order the inboxes in quote ${quoteId}? ${how} [y/N] `, (a) => { rl.close(); r(/^y/i.test(a)); }));
1853
+ if (!ok) return console.log(dim("aborted"));
1854
+ }
1855
+ const body = { quote_id: quoteId, senders, website: args.website || "", confirmed: true };
1856
+ if (payWith !== "auto") body.pay_with = payWith;
1857
+ // Same command, same key: running it again returns the same order (and never charges twice).
1858
+ const idem = "cli-" + crypto.createHash("sha256").update(JSON.stringify(body)).digest("hex").slice(0, 40);
1859
+ const r = need(await request("POST", args.base, "/api/v1/inboxes/orders", { key, body, headers: { "Idempotency-Key": idem } }), "inboxes order");
1860
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1861
+ if (r.status === "paid") {
1862
+ console.log(green("✓") + ` Order ${bold(r.order_id)} paid with the saved ${r.card || "card"}${r.replayed ? dim(" (already placed)") : ""}`);
1863
+ console.log(dim(r.message || ""));
1864
+ console.log(dim(`Track it: argorant inboxes status ${r.order_id}`));
1865
+ return;
1866
+ }
1867
+ if (!r.checkout_url) return console.log(`Order ${r.order_id}: ${r.status}. ${dim(r.message || "")}`);
1868
+ console.log(green("✓") + ` Order ${bold(r.order_id)}${r.existing || r.replayed ? dim(" (already placed)") : ""}`);
1869
+ if (r.saved_card && r.saved_card.reason) console.log(yellow(`Not charged to the saved card: ${r.saved_card.reason}`));
1870
+ console.log(`Pay here: ${cyan(r.checkout_url)}`);
1871
+ console.log(dim("Nothing is bought until you pay it; setup starts after the payment is confirmed."));
1872
+ console.log(dim(`Track it: argorant inboxes status ${r.order_id}`));
1873
+ return;
1874
+ }
1875
+ if (sub === "status" || sub === "orders") {
1876
+ const args = readFlags(argv.slice(1));
1877
+ const orderId = sub === "status" ? args._[0] : undefined;
1878
+ const path_ = orderId ? `/api/v1/inboxes/orders/${encodeURIComponent(orderId)}` : "/api/v1/inboxes/orders";
1879
+ const r = need(await request("GET", args.base, path_, { key }), "inboxes status");
1880
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1881
+ const rows = orderId ? [r] : r.orders || [];
1882
+ if (!rows.length) return console.log(dim("No inbox orders yet. Start with: argorant inboxes quote --mailboxes 100 --company <your website>"));
1883
+ for (const o of rows) {
1884
+ const mb = o.mailboxes || {};
1885
+ console.log(`${bold(o.order_id)} ${o.status}${o.step ? dim(` · ${o.step}`) : ""} ${dim(`${mb.created || 0}/${mb.total || 0} mailboxes`)}${o.aging_ends_at ? dim(` · aging ends ${o.aging_ends_at}`) : ""}${o.ordered_via ? dim(` · ordered via API/agent${o.paid_with === "saved_card" ? ", saved card" : ""}`) : ""}`);
1886
+ for (const d of o.domains || []) {
1887
+ console.log(dim(` ${d.name}${d.expires_at ? ` · expires ${String(d.expires_at).slice(0, 10)}` : ""} · renews at ${money(d.renewal_per_year_cents, o.currency)}/year${d.auto_renew === false ? " · renewal off" : ""}`));
1888
+ }
1889
+ for (const b of (o.billing && o.billing.bundles) || []) {
1890
+ console.log(dim(` bundle ${b.bundle_id}: ${b.mailboxes} mailboxes on ${(b.domains || []).join(", ")} · ${b.status}${b.ends_at ? ` · ends ${String(b.ends_at).slice(0, 10)}` : ""}`));
1891
+ }
1892
+ }
1893
+ return;
1894
+ }
1895
+ if (sub === "renewal" || sub === "cancel-domain") {
1896
+ // cancel-domain is the old name of `renewal --off` (kept working, deprecated).
1897
+ const args = readFlags(argv.slice(1), {}, { "--on": "on", "--off": "off" });
1898
+ const [orderId, domain] = args._;
1899
+ const on = sub === "renewal" ? (args.on ? true : args.off ? false : null) : false;
1900
+ if (!orderId || !domain || on === null || (args.on && args.off)) die("usage: argorant inboxes renewal <order-id> <domain> --off|--on [-y]");
1901
+ if (sub === "cancel-domain") console.error(dim("cancel-domain is deprecated: use `argorant inboxes renewal <order-id> <domain> --off`."));
1902
+ const question = on
1903
+ ? `Turn renewal back on for ${domain}? Nothing is charged now; it renews at its expiry date, billed shortly before. [y/N] `
1904
+ : `Stop renewal for ${domain}? Nothing is charged now; it keeps working until its expiry date and is not renewed. [y/N] `;
1905
+ if (!args.yes) {
1906
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1907
+ const ok = await new Promise((r) => rl.question(question, (a) => { rl.close(); r(/^y/i.test(a)); }));
1908
+ if (!ok) return console.log(dim("aborted"));
1909
+ }
1910
+ const r = need(await request("POST", args.base, `/api/v1/inboxes/orders/${encodeURIComponent(orderId)}/domains/${encodeURIComponent(domain)}/renewal`,
1911
+ { key, body: { on } }), "inboxes renewal");
1912
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1913
+ return console.log(green("✓") + ` ${r.message || (on ? `${domain} renews again` : `${domain} will not renew`)}`);
1914
+ }
1915
+ if (sub === "cancel-bundle") {
1916
+ const REASONS = ["too_expensive", "not_sending_enough", "deliverability", "switching_provider", "campaign_finished", "other"];
1917
+ const args = readFlags(argv.slice(1), { "--reason": "reason", "--note": "note" }, { "--keep-domains": "keepDomains" });
1918
+ const [orderId, target] = args._;
1919
+ if (!orderId || !target || !args.reason) die("usage: argorant inboxes cancel-bundle <order-id> <bundle-id> --reason <reason> [--note \"...\"] [--keep-domains] [-y]\n reasons: " + REASONS.join(", "));
1920
+ const reason = String(args.reason).toLowerCase();
1921
+ if (!REASONS.includes(reason)) die(`--reason is one of: ${REASONS.join(", ")}`);
1922
+ const question = `Cancel bundle ${target}? Billing for its mailboxes stops now (no refund); they work until the paid period ends, then they are deleted${args.keepDomains ? "" : " and its domains stop renewing"}. [y/N] `;
1923
+ if (!args.yes) {
1924
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1925
+ const ok = await new Promise((r) => rl.question(question, (a) => { rl.close(); r(/^y/i.test(a)); }));
1926
+ if (!ok) return console.log(dim("aborted"));
1927
+ }
1928
+ const body = { keep_domains: !!args.keepDomains, reason };
1929
+ if (args.note) body.note = String(args.note);
1930
+ const r = need(await request("POST", args.base, `/api/v1/inboxes/orders/${encodeURIComponent(orderId)}/bundles/${encodeURIComponent(target)}/cancel`,
1931
+ { key, body }), "inboxes cancel-bundle");
1932
+ if (args.json) return console.log(JSON.stringify(r, null, 2));
1933
+ return console.log(green("✓") + ` Bundle ${target} cancelled${r.existing ? dim(" (already)") : ""}`);
1934
+ }
1935
+ die(`unknown inboxes subcommand: ${sub} (list, set, disconnect, connect-google, endings, suggest, quote, order, status, renewal, cancel-bundle)`);
1936
+ }
1937
+
1938
+ function money(cents, currency) {
1939
+ if (cents === null || cents === undefined) return "?";
1940
+ const sign = String(currency || "usd").toLowerCase() === "eur" ? "€" : "$";
1941
+ return sign + (Number(cents) / 100).toFixed(2);
1767
1942
  }
1768
1943
 
1769
1944
  function help() {
@@ -1795,7 +1970,8 @@ ${bold("COMMANDS")}
1795
1970
  ${cyan("verify")} --file emails.csv -o out.csv Bulk-verify your own list ${dim("(recent re-checks free)")}
1796
1971
  ${cyan("campaigns")} ... Email outreach: create, write, enroll, launch ${dim("(argorant campaigns help)")}
1797
1972
  ${cyan("inboxes")} list | connect-google Connected mailboxes; connect a Google Workspace
1798
- ${cyan("inbox")} list | read | reply | classify Replies across campaigns; answer in-thread
1973
+ ${cyan("inboxes")} quote | order | status | renewal New mailboxes on new domains ${dim("(saved card or payment link)")}
1974
+ ${cyan("inbox")} list | read | reply | forward | classify Replies across campaigns; answer in-thread or forward
1799
1975
  ${cyan("blocklist")} list | add | remove Addresses and domains never contacted
1800
1976
 
1801
1977
  ${bold("FILTERS")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argorant",
3
- "version": "0.8.0",
3
+ "version": "0.10.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"