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 +24 -0
- package/bin/argorant.js +185 -9
- package/package.json +1 -1
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?
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
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