groundmail-cli 0.35.0 → 0.36.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 +2 -0
- package/dist/cli.js +88 -31
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -75,6 +75,8 @@ groundmail domains list
|
|
|
75
75
|
groundmail domains verify example.com
|
|
76
76
|
groundmail webhooks list
|
|
77
77
|
groundmail emails list
|
|
78
|
+
groundmail batches get batch_… # status counts for an emails batch
|
|
79
|
+
groundmail batches cancel batch_… # cancel the batch's emails that are still scheduled
|
|
78
80
|
groundmail brand set --logo-url https://cdn.acme.com/logo.png --color '#2f6feb' --company "Acme Inc" # logo, color and footer every template follows
|
|
79
81
|
groundmail senders list # saved senders; send with --sender billing instead of --from
|
|
80
82
|
SENDGRID_API_KEY=… groundmail migrate sendgrid --dry-run # import from SendGrid, Postmark, Mailgun, Resend or Amazon SES (beta)
|
package/dist/cli.js
CHANGED
|
@@ -2055,7 +2055,7 @@ function trimUrl(value) {
|
|
|
2055
2055
|
}
|
|
2056
2056
|
|
|
2057
2057
|
// package.json
|
|
2058
|
-
var version = "0.
|
|
2058
|
+
var version = "0.36.0";
|
|
2059
2059
|
|
|
2060
2060
|
// src/user-agent.ts
|
|
2061
2061
|
function buildUserAgent(own) {
|
|
@@ -2082,6 +2082,12 @@ function fetchWithUserAgent(userAgent) {
|
|
|
2082
2082
|
|
|
2083
2083
|
// src/client-info.ts
|
|
2084
2084
|
var CLI_PACKAGE = { name: "groundmail-cli", version };
|
|
2085
|
+
function sdkOptions(baseUrl) {
|
|
2086
|
+
return {
|
|
2087
|
+
...baseUrl ? { baseUrl } : {},
|
|
2088
|
+
fetch: fetchWithUserAgent(buildUserAgent(CLI_PACKAGE))
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2085
2091
|
|
|
2086
2092
|
// src/session/http.ts
|
|
2087
2093
|
var CliRequestError = class extends Error {
|
|
@@ -3133,7 +3139,7 @@ function resolveApiKey8(options, env) {
|
|
|
3133
3139
|
}
|
|
3134
3140
|
async function createTemplatesClient(apiKey, baseUrl) {
|
|
3135
3141
|
const { Groundmail } = await import("groundmail");
|
|
3136
|
-
return new Groundmail(apiKey, baseUrl
|
|
3142
|
+
return new Groundmail(apiKey, sdkOptions(baseUrl));
|
|
3137
3143
|
}
|
|
3138
3144
|
|
|
3139
3145
|
// src/commands/templates/delete.ts
|
|
@@ -4153,6 +4159,51 @@ async function runSetCommand(args, env = process.env) {
|
|
|
4153
4159
|
});
|
|
4154
4160
|
}
|
|
4155
4161
|
|
|
4162
|
+
// src/commands/batches/commands.ts
|
|
4163
|
+
var VALUE_FLAGS3 = ["--api-key", "--base-url", "--id"];
|
|
4164
|
+
var MISSING_KEY3 = "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key.";
|
|
4165
|
+
function readFlag23(args, flag) {
|
|
4166
|
+
const index = args.indexOf(flag);
|
|
4167
|
+
if (index === -1) return void 0;
|
|
4168
|
+
return args[index + 1];
|
|
4169
|
+
}
|
|
4170
|
+
function batchRef(args) {
|
|
4171
|
+
const id = readFlag23(args, "--id");
|
|
4172
|
+
if (id) return id;
|
|
4173
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
4174
|
+
const arg = args[i];
|
|
4175
|
+
if (arg.startsWith("--")) {
|
|
4176
|
+
if (VALUE_FLAGS3.includes(arg)) i += 1;
|
|
4177
|
+
continue;
|
|
4178
|
+
}
|
|
4179
|
+
return arg;
|
|
4180
|
+
}
|
|
4181
|
+
return void 0;
|
|
4182
|
+
}
|
|
4183
|
+
async function createBatchesClient(apiKey, baseUrl) {
|
|
4184
|
+
const { Groundmail } = await import("groundmail");
|
|
4185
|
+
return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
|
|
4186
|
+
}
|
|
4187
|
+
async function withBatch(args, env, usage, run) {
|
|
4188
|
+
const batchId = batchRef(args);
|
|
4189
|
+
if (!batchId) return { exitCode: 1, error: `Missing batch id (batch_\u2026). Usage: ${usage}` };
|
|
4190
|
+
const apiKey = readFlag23(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
4191
|
+
if (!apiKey) return { exitCode: 1, error: MISSING_KEY3 };
|
|
4192
|
+
try {
|
|
4193
|
+
const client2 = await createBatchesClient(apiKey, readFlag23(args, "--base-url"));
|
|
4194
|
+
console.log(JSON.stringify(await run(client2, batchId)));
|
|
4195
|
+
return { exitCode: 0 };
|
|
4196
|
+
} catch (error) {
|
|
4197
|
+
return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4200
|
+
async function runGetCommand10(args, env = process.env) {
|
|
4201
|
+
return withBatch(args, env, "groundmail batches get <batch_id>", (client2, id) => client2.batches.get(id));
|
|
4202
|
+
}
|
|
4203
|
+
async function runCancelCommand2(args, env = process.env) {
|
|
4204
|
+
return withBatch(args, env, "groundmail batches cancel <batch_id>", (client2, id) => client2.batches.cancel(id));
|
|
4205
|
+
}
|
|
4206
|
+
|
|
4156
4207
|
// src/commands/auth.ts
|
|
4157
4208
|
async function runAuthCommand(subcommand, argv, deps = {}) {
|
|
4158
4209
|
const env = deps.env ?? process.env;
|
|
@@ -4316,7 +4367,7 @@ function sesParams(args, env) {
|
|
|
4316
4367
|
"Use a key with the read-only import policy (https://docs.groundmail.dev/migrations#amazon-ses-iam-policy). It is used for this import only and never stored."
|
|
4317
4368
|
].join("\n");
|
|
4318
4369
|
}
|
|
4319
|
-
const region =
|
|
4370
|
+
const region = readFlag24(args, "--region") || env.AWS_REGION || env.AWS_DEFAULT_REGION;
|
|
4320
4371
|
if (!region) return "Set the SES region with --region (for example --region us-east-1) or AWS_REGION. One region per import.";
|
|
4321
4372
|
if (!AWS_REGION.test(region)) return `--region must be an AWS region such as us-east-1, not ${region}`;
|
|
4322
4373
|
return {
|
|
@@ -4327,8 +4378,8 @@ function sesParams(args, env) {
|
|
|
4327
4378
|
aws_region: region
|
|
4328
4379
|
};
|
|
4329
4380
|
}
|
|
4330
|
-
var
|
|
4331
|
-
function
|
|
4381
|
+
var VALUE_FLAGS4 = /* @__PURE__ */ new Set(["--api-key", "--base-url"]);
|
|
4382
|
+
function readFlag24(args, flag) {
|
|
4332
4383
|
const index = args.indexOf(flag);
|
|
4333
4384
|
if (index === -1) return void 0;
|
|
4334
4385
|
return args[index + 1];
|
|
@@ -4403,10 +4454,10 @@ function formatMigrationSummary(m) {
|
|
|
4403
4454
|
return lines.join("\n");
|
|
4404
4455
|
}
|
|
4405
4456
|
async function client(args, env) {
|
|
4406
|
-
const apiKey =
|
|
4457
|
+
const apiKey = readFlag24(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
4407
4458
|
if (!apiKey) return null;
|
|
4408
4459
|
const { Groundmail } = await import("groundmail");
|
|
4409
|
-
const baseUrl =
|
|
4460
|
+
const baseUrl = readFlag24(args, "--base-url");
|
|
4410
4461
|
return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
|
|
4411
4462
|
}
|
|
4412
4463
|
async function runStoredCommand(sub, args, env) {
|
|
@@ -4420,7 +4471,7 @@ async function runStoredCommand(sub, args, env) {
|
|
|
4420
4471
|
if (!data.length) console.error("No imports yet. Previews are not stored.");
|
|
4421
4472
|
return { exitCode: 0 };
|
|
4422
4473
|
}
|
|
4423
|
-
const id = args.find((arg, i) => !arg.startsWith("--") && !
|
|
4474
|
+
const id = args.find((arg, i) => !arg.startsWith("--") && !VALUE_FLAGS4.has(args[i - 1] ?? ""));
|
|
4424
4475
|
if (!id) return { exitCode: 1, error: "Usage: groundmail migrate report <import id> [--csv]" };
|
|
4425
4476
|
if (args.includes("--csv")) {
|
|
4426
4477
|
process.stdout.write(await gm.migrations.reportCsv(id));
|
|
@@ -4443,7 +4494,7 @@ async function runMigrateCommand(provider, args, env = process.env) {
|
|
|
4443
4494
|
};
|
|
4444
4495
|
}
|
|
4445
4496
|
const id = provider;
|
|
4446
|
-
const apiKey =
|
|
4497
|
+
const apiKey = readFlag24(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
4447
4498
|
if (!apiKey) {
|
|
4448
4499
|
return { exitCode: 1, error: "Not signed in. Run `groundmail login`, or set GROUNDMAIL_API_KEY." };
|
|
4449
4500
|
}
|
|
@@ -4453,7 +4504,7 @@ async function runMigrateCommand(provider, args, env = process.env) {
|
|
|
4453
4504
|
if (typeof ses === "string") return { exitCode: 1, error: ses };
|
|
4454
4505
|
params = ses;
|
|
4455
4506
|
} else {
|
|
4456
|
-
const providerKey =
|
|
4507
|
+
const providerKey = readFlag24(args, "--key") || env[KEY_ENV[id]];
|
|
4457
4508
|
if (!providerKey) {
|
|
4458
4509
|
const resend = id === "resend" ? " Use a key with Full access (Resend \u2192 API Keys); a Sending access key cannot read your account." : "";
|
|
4459
4510
|
return {
|
|
@@ -4463,15 +4514,15 @@ async function runMigrateCommand(provider, args, env = process.env) {
|
|
|
4463
4514
|
}
|
|
4464
4515
|
params = { provider: id, api_key: providerKey };
|
|
4465
4516
|
if (id === "postmark") {
|
|
4466
|
-
const accountToken =
|
|
4517
|
+
const accountToken = readFlag24(args, "--account-token") || env.POSTMARK_ACCOUNT_TOKEN;
|
|
4467
4518
|
if (accountToken) params.account_api_key = accountToken;
|
|
4468
4519
|
} else if (id === "sendgrid" || id === "mailgun") {
|
|
4469
|
-
const region =
|
|
4520
|
+
const region = readFlag24(args, "--region");
|
|
4470
4521
|
if (region === "us" || region === "eu") params.region = region;
|
|
4471
4522
|
else if (region) return { exitCode: 1, error: "--region must be us or eu" };
|
|
4472
4523
|
}
|
|
4473
4524
|
}
|
|
4474
|
-
const only =
|
|
4525
|
+
const only = readFlag24(args, "--only");
|
|
4475
4526
|
if (only) {
|
|
4476
4527
|
const list = only.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4477
4528
|
const bad = list.filter((r) => !RESOURCES.includes(r));
|
|
@@ -4483,7 +4534,7 @@ async function runMigrateCommand(provider, args, env = process.env) {
|
|
|
4483
4534
|
if (args.includes("--dry-run")) params.dry_run = true;
|
|
4484
4535
|
try {
|
|
4485
4536
|
const { Groundmail } = await import("groundmail");
|
|
4486
|
-
const baseUrl =
|
|
4537
|
+
const baseUrl = readFlag24(args, "--base-url");
|
|
4487
4538
|
const client2 = new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
|
|
4488
4539
|
console.error(`${params.dry_run ? "Previewing" : "Importing"} from ${LABELS[id]}${params.provider === "ses" ? ` (${params.aws_region})` : ""} (Beta)\u2026`);
|
|
4489
4540
|
const report = await client2.migrations.run(params, {
|
|
@@ -4527,17 +4578,17 @@ async function runValidateCommand(args, env = process.env) {
|
|
|
4527
4578
|
|
|
4528
4579
|
// src/commands/dmarc/dmarc.ts
|
|
4529
4580
|
var DMARC_APPLY_PROVIDERS = ["cloudflare", "vercel", "godaddy", "netlify", "digitalocean", "dnsimple"];
|
|
4530
|
-
function
|
|
4581
|
+
function readFlag25(args, flag) {
|
|
4531
4582
|
const index = args.indexOf(flag);
|
|
4532
4583
|
if (index === -1) return void 0;
|
|
4533
4584
|
return args[index + 1];
|
|
4534
4585
|
}
|
|
4535
|
-
var
|
|
4586
|
+
var VALUE_FLAGS5 = ["--api-key", "--base-url", "--domain", "--apply"];
|
|
4536
4587
|
function readFirstPositional15(args) {
|
|
4537
4588
|
for (let i = 0; i < args.length; i += 1) {
|
|
4538
4589
|
const arg = args[i];
|
|
4539
4590
|
if (arg.startsWith("--")) {
|
|
4540
|
-
if (
|
|
4591
|
+
if (VALUE_FLAGS5.includes(arg)) i += 1;
|
|
4541
4592
|
continue;
|
|
4542
4593
|
}
|
|
4543
4594
|
return arg;
|
|
@@ -4584,23 +4635,23 @@ function formatDmarcStatus(dmarc) {
|
|
|
4584
4635
|
return lines.join("\n");
|
|
4585
4636
|
}
|
|
4586
4637
|
async function runDmarcCommand(subcommand, args, env = process.env) {
|
|
4587
|
-
const apiKey =
|
|
4638
|
+
const apiKey = readFlag25(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
4588
4639
|
if (!apiKey) return { exitCode: 1, error: "Missing API key. Run groundmail login, set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
4589
|
-
const domain =
|
|
4640
|
+
const domain = readFlag25(args, "--domain") || readFirstPositional15(args);
|
|
4590
4641
|
if (!subcommand || !["status", "enable", "disable", "digest"].includes(subcommand)) {
|
|
4591
4642
|
return { exitCode: 1, error: "Usage: groundmail dmarc status|enable|disable|digest <domain>" };
|
|
4592
4643
|
}
|
|
4593
4644
|
if (!domain) return { exitCode: 1, error: `Missing domain. Usage: groundmail dmarc ${subcommand} example.com` };
|
|
4594
4645
|
const json = args.includes("--json");
|
|
4595
4646
|
try {
|
|
4596
|
-
const client2 = await createDmarcClient(apiKey,
|
|
4647
|
+
const client2 = await createDmarcClient(apiKey, readFlag25(args, "--base-url"));
|
|
4597
4648
|
if (subcommand === "status") {
|
|
4598
4649
|
const result2 = await client2.dmarc.get(domain);
|
|
4599
4650
|
console.log(json ? JSON.stringify(result2) : formatDmarcStatus(result2.dmarc));
|
|
4600
4651
|
return { exitCode: 0 };
|
|
4601
4652
|
}
|
|
4602
4653
|
if (subcommand === "enable") {
|
|
4603
|
-
const apply =
|
|
4654
|
+
const apply = readFlag25(args, "--apply");
|
|
4604
4655
|
if (apply && !DMARC_APPLY_PROVIDERS.includes(apply)) {
|
|
4605
4656
|
return { exitCode: 1, error: `--apply must be one of: ${DMARC_APPLY_PROVIDERS.join(", ")}` };
|
|
4606
4657
|
}
|
|
@@ -4630,7 +4681,7 @@ Updated the DMARC record with ${result2.applied.provider}.`);
|
|
|
4630
4681
|
}
|
|
4631
4682
|
|
|
4632
4683
|
// src/commands/test-inbox/test-inbox.ts
|
|
4633
|
-
function
|
|
4684
|
+
function readFlag26(args, flag) {
|
|
4634
4685
|
const index = args.indexOf(flag);
|
|
4635
4686
|
if (index === -1) return void 0;
|
|
4636
4687
|
return args[index + 1];
|
|
@@ -4658,20 +4709,20 @@ function formatTestEmail(email) {
|
|
|
4658
4709
|
var USAGE = "Usage: groundmail test-inbox address [--prefix signup] | wait --to <address> [--timeout 30] [--since <iso>] [--json] | list [--to <address>] [--json]";
|
|
4659
4710
|
async function runTestInboxCommand(subcommand, args, env = process.env) {
|
|
4660
4711
|
if (!subcommand || !["address", "wait", "list"].includes(subcommand)) return { exitCode: 1, error: USAGE };
|
|
4661
|
-
const apiKey =
|
|
4712
|
+
const apiKey = readFlag26(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
4662
4713
|
if (!apiKey) return { exitCode: 1, error: "Missing API key. Run groundmail login, set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
4663
4714
|
const json = args.includes("--json");
|
|
4664
4715
|
try {
|
|
4665
|
-
const client2 = await createTestInboxClient(apiKey,
|
|
4716
|
+
const client2 = await createTestInboxClient(apiKey, readFlag26(args, "--base-url"));
|
|
4666
4717
|
if (subcommand === "address") {
|
|
4667
|
-
console.log(await client2.testInbox.address({ prefix:
|
|
4718
|
+
console.log(await client2.testInbox.address({ prefix: readFlag26(args, "--prefix") }));
|
|
4668
4719
|
return { exitCode: 0 };
|
|
4669
4720
|
}
|
|
4670
4721
|
if (subcommand === "list") {
|
|
4671
4722
|
const result = await client2.testInbox.list({
|
|
4672
|
-
to:
|
|
4673
|
-
since:
|
|
4674
|
-
limit:
|
|
4723
|
+
to: readFlag26(args, "--to"),
|
|
4724
|
+
since: readFlag26(args, "--since"),
|
|
4725
|
+
limit: readFlag26(args, "--limit") ? Number(readFlag26(args, "--limit")) : void 0
|
|
4675
4726
|
});
|
|
4676
4727
|
if (json) console.log(JSON.stringify(result));
|
|
4677
4728
|
else if (!result.emails.length) console.log("No test emails yet.");
|
|
@@ -4682,13 +4733,13 @@ async function runTestInboxCommand(subcommand, args, env = process.env) {
|
|
|
4682
4733
|
}
|
|
4683
4734
|
return { exitCode: 0 };
|
|
4684
4735
|
}
|
|
4685
|
-
const to =
|
|
4736
|
+
const to = readFlag26(args, "--to");
|
|
4686
4737
|
if (!to) return { exitCode: 1, error: "Missing --to. Usage: groundmail test-inbox wait --to signup-k3m9x2pq@acme.test.groundmail.dev" };
|
|
4687
|
-
const timeoutRaw =
|
|
4738
|
+
const timeoutRaw = readFlag26(args, "--timeout");
|
|
4688
4739
|
const seconds = timeoutRaw === void 0 ? 30 : Number(timeoutRaw);
|
|
4689
4740
|
if (!Number.isFinite(seconds) || seconds < 0) return { exitCode: 1, error: "--timeout must be a number of seconds" };
|
|
4690
4741
|
try {
|
|
4691
|
-
const email = await client2.testInbox.waitFor({ to, timeout: seconds * 1e3, since:
|
|
4742
|
+
const email = await client2.testInbox.waitFor({ to, timeout: seconds * 1e3, since: readFlag26(args, "--since") });
|
|
4692
4743
|
console.log(json ? JSON.stringify({ email }) : formatTestEmail(email));
|
|
4693
4744
|
return { exitCode: 0 };
|
|
4694
4745
|
} catch (error) {
|
|
@@ -4736,6 +4787,8 @@ var USAGE_LINES = [
|
|
|
4736
4787
|
" groundmail emails insights [--window 7d|30d]",
|
|
4737
4788
|
" groundmail emails get --id msg_\u2026 [--watch [--timeout 60] [--json]] --watch: follow an email you already sent until it lands",
|
|
4738
4789
|
" groundmail emails cancel --id msg_\u2026",
|
|
4790
|
+
" groundmail batches get <batch_id> an emails batch's status counts (the batch_id `emails batch` returned)",
|
|
4791
|
+
" groundmail batches cancel <batch_id> cancel the batch's emails that are still scheduled; safe to repeat",
|
|
4739
4792
|
" groundmail emails resend --id msg_\u2026",
|
|
4740
4793
|
` groundmail events send --name user.created --email user@example.com --data '{"name":"Ada"}'`,
|
|
4741
4794
|
" groundmail automations list",
|
|
@@ -5176,6 +5229,10 @@ async function main() {
|
|
|
5176
5229
|
if (subcommand === "get") await finish(await runGetCommand9(rest));
|
|
5177
5230
|
if (subcommand === "set" || subcommand === "update") await finish(await runSetCommand(rest));
|
|
5178
5231
|
}
|
|
5232
|
+
if (command === "batches") {
|
|
5233
|
+
if (subcommand === "get") await finish(await runGetCommand10(rest));
|
|
5234
|
+
if (subcommand === "cancel") await finish(await runCancelCommand2(rest));
|
|
5235
|
+
}
|
|
5179
5236
|
if (command === "senders") {
|
|
5180
5237
|
if (subcommand === "list") await finish(await runListCommand9(rest));
|
|
5181
5238
|
if (subcommand === "create" || subcommand === "add") await finish(await runCreateCommand5(rest));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groundmail-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"description": "Groundmail CLI — send transactional email from the terminal",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"prepublishOnly": "npm run build"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"groundmail": "0.
|
|
41
|
+
"groundmail": "0.38.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"tsup": "^8.5.0",
|