argorant 0.9.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
@@ -153,18 +153,29 @@ argorant inboxes connect-google --admin admin@yourdomain.com --emails a@x.com,b@
153
153
  ```
154
154
 
155
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 and the total; the order returns a checkout link that you pay
157
- yourself. Nothing is bought before that payment, and setup starts once it is confirmed. Warm-up is included
158
- in every plan; switch it on per mailbox with `argorant inboxes set <mailbox> --warmup`.
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).
159
164
 
160
165
  ```
161
- argorant inboxes suggest acme.com # free domain names with prices
166
+ argorant inboxes suggest acme.com [--ending com] # free domain names with prices
162
167
  argorant inboxes quote --mailboxes 100 --company acme.com # or --domains getacme.com,tryacme.co
163
- argorant inboxes order <quote-id> --senders "Elena Vaughn" # asks first; prints the checkout link
164
- argorant inboxes status [<order-id>] # steps, domains, renewal prices, bundles
165
- argorant inboxes cancel-bundle <order-id> <bundle-id> [--keep-domains] | cancel-domain <order-id> <domain>
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]
166
174
  ```
167
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
+
168
179
  `<campaign>` accepts an id or an unambiguous case-insensitive name prefix. Every address is verified before
169
180
  it is enrolled; only valid ones send unless `--include-catch-all`. Variables: `{{first_name}}`, `{{last_name}}`,
170
181
  `{{company}}`, `{{title}}`; spintax `{a|b}` varies phrasing. `--query "..."` filter enrollment stays available
package/bin/argorant.js CHANGED
@@ -1654,11 +1654,14 @@ ${bold("USAGE")}
1654
1654
  ${bold("argorant inboxes")} set <mailbox> [--daily-limit 40] [--warmup|--no-warmup] [--active|--inactive] [--timezone <tz>]
1655
1655
  ${bold("argorant inboxes")} disconnect <mailbox>
1656
1656
  ${bold("argorant inboxes")} connect-google --admin <admin@yourdomain.com> [--emails a,b | --all]
1657
- ${bold("argorant inboxes")} suggest <website|brand> [-n 20] ${dim("free domain names with prices")}
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")}
1658
1659
  ${bold("argorant inboxes")} quote --mailboxes 100 [--company acme.com] [--domains a.com,b.com] [--currency usd|eur] [--billing month|quarter]
1659
- ${bold("argorant inboxes")} order <quote-id> --senders "Elena Vaughn[,Mark Hill]" [--website acme.com] [-y] ${dim("returns a checkout link; nothing is bought before you pay it")}
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")}
1660
1661
  ${bold("argorant inboxes")} status [<order-id>]
1661
- ${bold("argorant inboxes")} cancel-bundle <order-id> <bundle-id> [--keep-domains] [-y] | cancel-domain <order-id> <domain> [-y]
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")}
1662
1665
  ${bold("argorant blocklist")} list | add <email|domain> | remove <id>
1663
1666
 
1664
1667
  ${bold("argorant inbox")} list [--folder replies|interested|not_interested|out_of_office|bounces|unsubscribes|all] [--campaign <c>] [--q text]
@@ -1780,11 +1783,18 @@ async function cmdInboxes(argv) {
1780
1783
  for (const c of r.connected || []) console.log(dim(` ${c.email} · ${c.health || "ok"}`));
1781
1784
  return;
1782
1785
  }
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
+ }
1783
1792
  if (sub === "suggest") {
1784
- const args = readFlags(argv.slice(1), { "-n": "limit", "--limit": "limit" });
1793
+ const args = readFlags(argv.slice(1), { "-n": "limit", "--limit": "limit", "--ending": "ending", "--tld": "ending" });
1785
1794
  const company = args._.join(" ").trim();
1786
- if (!company) die("usage: argorant inboxes suggest <website|brand> [-n 20]");
1795
+ if (!company) die("usage: argorant inboxes suggest <website|brand> [--ending com] [-n 20]");
1787
1796
  const query = { company, limit: args.limit ? parseLimit(args.limit, "--limit") : 20 };
1797
+ if (args.ending) query.ending = String(args.ending).replace(/^\./, "").toLowerCase();
1788
1798
  const r = need(await request("GET", args.base, "/api/v1/inboxes/domains/suggest", { key, query }), "inboxes suggest");
1789
1799
  if (args.json) return console.log(JSON.stringify(r, null, 2));
1790
1800
  for (const d of r.results || []) {
@@ -1816,32 +1826,47 @@ async function cmdInboxes(argv) {
1816
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`);
1817
1827
  console.log(dim(q.currency_rule));
1818
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})` : ""}`));
1819
1832
  console.log(`\nQuote ${bold(q.quote_id)} ${dim(`(valid until ${String(q.expires_at).slice(0, 16).replace("T", " ")} UTC)`)}`);
1820
1833
  if (q.can_order) console.log(dim(`Order it: argorant inboxes order ${q.quote_id} --senders "First Last"`));
1821
1834
  else console.log(yellow(q.next_step));
1822
1835
  return;
1823
1836
  }
1824
1837
  if (sub === "order") {
1825
- const args = readFlags(argv.slice(1), { "--senders": "senders", "--sender": "senders", "--website": "website" });
1838
+ const args = readFlags(argv.slice(1), { "--senders": "senders", "--sender": "senders", "--website": "website", "--pay-with": "payWith" });
1826
1839
  const quoteId = args._[0];
1827
- if (!quoteId || !args.senders) die("usage: argorant inboxes order <quote-id> --senders \"Elena Vaughn[,Mark Hill]\" [--website acme.com] [-y]");
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");
1828
1843
  const senders = String(args.senders).split(",").map((x) => x.trim()).filter(Boolean).map((full) => {
1829
1844
  const parts = full.split(/\s+/);
1830
1845
  if (parts.length < 2) die(`sender "${full}" needs a first and a last name`);
1831
1846
  return { first_name: parts[0], last_name: parts.slice(1).join(" ") };
1832
1847
  });
1833
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.";
1834
1851
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1835
- const ok = await new Promise((r) => rl.question(`Order the inboxes in quote ${quoteId}? You get a checkout link to pay; nothing is bought before that. [y/N] `, (a) => { rl.close(); r(/^y/i.test(a)); }));
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)); }));
1836
1853
  if (!ok) return console.log(dim("aborted"));
1837
1854
  }
1838
1855
  const body = { quote_id: quoteId, senders, website: args.website || "", confirmed: true };
1839
- // Same command, same key: running it again returns the same order and link instead of a second order.
1856
+ if (payWith !== "auto") body.pay_with = payWith;
1857
+ // Same command, same key: running it again returns the same order (and never charges twice).
1840
1858
  const idem = "cli-" + crypto.createHash("sha256").update(JSON.stringify(body)).digest("hex").slice(0, 40);
1841
1859
  const r = need(await request("POST", args.base, "/api/v1/inboxes/orders", { key, body, headers: { "Idempotency-Key": idem } }), "inboxes order");
1842
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
+ }
1843
1867
  if (!r.checkout_url) return console.log(`Order ${r.order_id}: ${r.status}. ${dim(r.message || "")}`);
1844
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}`));
1845
1870
  console.log(`Pay here: ${cyan(r.checkout_url)}`);
1846
1871
  console.log(dim("Nothing is bought until you pay it; setup starts after the payment is confirmed."));
1847
1872
  console.log(dim(`Track it: argorant inboxes status ${r.order_id}`));
@@ -1857,7 +1882,7 @@ async function cmdInboxes(argv) {
1857
1882
  if (!rows.length) return console.log(dim("No inbox orders yet. Start with: argorant inboxes quote --mailboxes 100 --company <your website>"));
1858
1883
  for (const o of rows) {
1859
1884
  const mb = o.mailboxes || {};
1860
- 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}`) : ""}`);
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" : ""}`) : ""}`);
1861
1886
  for (const d of o.domains || []) {
1862
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" : ""}`));
1863
1888
  }
@@ -1867,26 +1892,47 @@ async function cmdInboxes(argv) {
1867
1892
  }
1868
1893
  return;
1869
1894
  }
1870
- if (sub === "cancel-bundle" || sub === "cancel-domain") {
1871
- const args = readFlags(argv.slice(1), {}, { "--keep-domains": "keepDomains" });
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" });
1872
1918
  const [orderId, target] = args._;
1873
- if (!orderId || !target) die(sub === "cancel-bundle" ? "usage: argorant inboxes cancel-bundle <order-id> <bundle-id> [--keep-domains] [-y]" : "usage: argorant inboxes cancel-domain <order-id> <domain> [-y]");
1874
- const question = sub === "cancel-bundle"
1875
- ? `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] `
1876
- : `Stop renewing ${target}? It works until its expiry date and is not renewed. [y/N] `;
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] `;
1877
1923
  if (!args.yes) {
1878
1924
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1879
1925
  const ok = await new Promise((r) => rl.question(question, (a) => { rl.close(); r(/^y/i.test(a)); }));
1880
1926
  if (!ok) return console.log(dim("aborted"));
1881
1927
  }
1882
- const base = `/api/v1/inboxes/orders/${encodeURIComponent(orderId)}`;
1883
- const r = sub === "cancel-bundle"
1884
- ? need(await request("POST", args.base, `${base}/bundles/${encodeURIComponent(target)}/cancel`, { key, body: { keep_domains: !!args.keepDomains } }), "inboxes cancel-bundle")
1885
- : need(await request("POST", args.base, `${base}/domains/${encodeURIComponent(target)}/cancel`, { key, body: {} }), "inboxes cancel-domain");
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");
1886
1932
  if (args.json) return console.log(JSON.stringify(r, null, 2));
1887
- return console.log(green("✓") + (sub === "cancel-bundle" ? ` Bundle ${target} cancelled${r.existing ? dim(" (already)") : ""}` : ` ${target} will not renew`));
1933
+ return console.log(green("✓") + ` Bundle ${target} cancelled${r.existing ? dim(" (already)") : ""}`);
1888
1934
  }
1889
- die(`unknown inboxes subcommand: ${sub} (list, set, disconnect, connect-google, suggest, quote, order, status, cancel-bundle, cancel-domain)`);
1935
+ die(`unknown inboxes subcommand: ${sub} (list, set, disconnect, connect-google, endings, suggest, quote, order, status, renewal, cancel-bundle)`);
1890
1936
  }
1891
1937
 
1892
1938
  function money(cents, currency) {
@@ -1924,7 +1970,7 @@ ${bold("COMMANDS")}
1924
1970
  ${cyan("verify")} --file emails.csv -o out.csv Bulk-verify your own list ${dim("(recent re-checks free)")}
1925
1971
  ${cyan("campaigns")} ... Email outreach: create, write, enroll, launch ${dim("(argorant campaigns help)")}
1926
1972
  ${cyan("inboxes")} list | connect-google Connected mailboxes; connect a Google Workspace
1927
- ${cyan("inboxes")} quote | order | status New mailboxes on new domains ${dim("(you pay each order at checkout)")}
1973
+ ${cyan("inboxes")} quote | order | status | renewal New mailboxes on new domains ${dim("(saved card or payment link)")}
1928
1974
  ${cyan("inbox")} list | read | reply | forward | classify Replies across campaigns; answer in-thread or forward
1929
1975
  ${cyan("blocklist")} list | add | remove Addresses and domains never contacted
1930
1976
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argorant",
3
- "version": "0.9.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"