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 +18 -7
- package/bin/argorant.js +69 -23
- package/package.json +1 -1
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
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
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;
|
|
164
|
-
argorant inboxes
|
|
165
|
-
argorant inboxes
|
|
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")}
|
|
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("
|
|
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")}
|
|
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}?
|
|
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
|
-
|
|
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 === "
|
|
1871
|
-
|
|
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(
|
|
1874
|
-
const
|
|
1875
|
-
|
|
1876
|
-
|
|
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
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
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("✓") +
|
|
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,
|
|
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("(
|
|
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