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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/dist/cli.js +88 -31
  3. 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.35.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 ? { baseUrl } : void 0);
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 = readFlag23(args, "--region") || env.AWS_REGION || env.AWS_DEFAULT_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 VALUE_FLAGS3 = /* @__PURE__ */ new Set(["--api-key", "--base-url"]);
4331
- function readFlag23(args, flag) {
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 = readFlag23(args, "--api-key") || env.GROUNDMAIL_API_KEY;
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 = readFlag23(args, "--base-url");
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("--") && !VALUE_FLAGS3.has(args[i - 1] ?? ""));
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 = readFlag23(args, "--api-key") || env.GROUNDMAIL_API_KEY;
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 = readFlag23(args, "--key") || env[KEY_ENV[id]];
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 = readFlag23(args, "--account-token") || env.POSTMARK_ACCOUNT_TOKEN;
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 = readFlag23(args, "--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 = readFlag23(args, "--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 = readFlag23(args, "--base-url");
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 readFlag24(args, flag) {
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 VALUE_FLAGS4 = ["--api-key", "--base-url", "--domain", "--apply"];
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 (VALUE_FLAGS4.includes(arg)) i += 1;
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 = readFlag24(args, "--api-key") || env.GROUNDMAIL_API_KEY;
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 = readFlag24(args, "--domain") || readFirstPositional15(args);
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, readFlag24(args, "--base-url"));
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 = readFlag24(args, "--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 readFlag25(args, flag) {
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 = readFlag25(args, "--api-key") || env.GROUNDMAIL_API_KEY;
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, readFlag25(args, "--base-url"));
4716
+ const client2 = await createTestInboxClient(apiKey, readFlag26(args, "--base-url"));
4666
4717
  if (subcommand === "address") {
4667
- console.log(await client2.testInbox.address({ prefix: readFlag25(args, "--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: readFlag25(args, "--to"),
4673
- since: readFlag25(args, "--since"),
4674
- limit: readFlag25(args, "--limit") ? Number(readFlag25(args, "--limit")) : void 0
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 = readFlag25(args, "--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 = readFlag25(args, "--timeout");
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: readFlag25(args, "--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.35.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.37.0"
41
+ "groundmail": "0.38.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "tsup": "^8.5.0",