groundmail-cli 0.19.0 → 0.22.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 +18 -0
  2. package/dist/cli.js +554 -156
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -43,6 +43,7 @@ function parseSendCommandArgs(args) {
43
43
  scheduledAt: readFlag(args, "--schedule"),
44
44
  category,
45
45
  unsubscribe: args.includes("--unsubscribe") ? true : void 0,
46
+ tenant: readFlag(args, "--tenant"),
46
47
  apiKey: readFlag(args, "--api-key"),
47
48
  baseUrl: readFlag(args, "--base-url")
48
49
  };
@@ -66,9 +67,13 @@ async function runSendCommand(args, deps) {
66
67
  tags: options.tags,
67
68
  scheduledAt: options.scheduledAt,
68
69
  category: options.category,
69
- unsubscribe: options.unsubscribe
70
+ unsubscribe: options.unsubscribe,
71
+ ...options.tenant ? { tenant: options.tenant } : {}
70
72
  });
71
73
  stdout(JSON.stringify(result));
74
+ if (result.test) {
75
+ stderr("Test send: simulated, nothing was delivered. Its status and webhooks are marked test.");
76
+ }
72
77
  return 0;
73
78
  } catch (error) {
74
79
  stderr(error instanceof Error ? error.message : String(error));
@@ -101,6 +106,7 @@ function parseSendCommandOptions(args, env) {
101
106
  scheduledAt: parsed.scheduledAt,
102
107
  category: parsed.category,
103
108
  unsubscribe: parsed.unsubscribe,
109
+ tenant: parsed.tenant,
104
110
  apiKey,
105
111
  baseUrl: parsed.baseUrl
106
112
  };
@@ -135,7 +141,7 @@ function readFirstPositional(args) {
135
141
  for (let index = 0; index < args.length; index += 1) {
136
142
  const arg = args[index];
137
143
  if (arg.startsWith("--")) {
138
- if (arg === "--api-key" || arg === "--base-url" || arg === "--file") index += 1;
144
+ if (arg === "--api-key" || arg === "--base-url" || arg === "--file" || arg === "--tenant") index += 1;
139
145
  continue;
140
146
  }
141
147
  return arg;
@@ -164,7 +170,9 @@ async function runBatchCommand(args, deps) {
164
170
  stderr(payload.error);
165
171
  return { exitCode: 1, error: payload.error };
166
172
  }
167
- const result = await deps.sendBatch(payload.emails);
173
+ const tenant = readFlag2(args, "--tenant");
174
+ const emails = tenant ? payload.emails.map((email) => email.tenant ? email : { ...email, tenant }) : payload.emails;
175
+ const result = await deps.sendBatch(emails);
168
176
  stdout(JSON.stringify(result));
169
177
  return { exitCode: 0 };
170
178
  } catch (error) {
@@ -389,10 +397,18 @@ async function runListCommand(args, deps) {
389
397
  const limitRaw = readFlag7(args, "--limit");
390
398
  const cursor = readFlag7(args, "--cursor");
391
399
  const limit = limitRaw ? Number(limitRaw) : void 0;
400
+ if (args.includes("--test") && args.includes("--live")) {
401
+ stderr("Pass --test or --live, not both.");
402
+ return { exitCode: 1, error: "conflicting flags" };
403
+ }
404
+ const test = args.includes("--test") ? true : args.includes("--live") ? false : void 0;
405
+ const tenant = readFlag7(args, "--tenant");
392
406
  try {
393
407
  const result = await deps.listEmails({
394
408
  ...limit != null && Number.isFinite(limit) ? { limit } : {},
395
- ...cursor ? { cursor } : {}
409
+ ...cursor ? { cursor } : {},
410
+ ...test !== void 0 ? { test } : {},
411
+ ...tenant ? { tenant } : {}
396
412
  });
397
413
  stdout(JSON.stringify(result));
398
414
  return { exitCode: 0 };
@@ -699,7 +715,8 @@ function parseDomainsCommonArgs(args) {
699
715
  apiKey: readFlag10(args, "--api-key"),
700
716
  baseUrl: readFlag10(args, "--base-url"),
701
717
  name: readFlag10(args, "--name") || readFlag10(args, "--domain"),
702
- id: readFlag10(args, "--id")
718
+ id: readFlag10(args, "--id"),
719
+ tenant: readFlag10(args, "--tenant")
703
720
  };
704
721
  }
705
722
  function resolveApiKey3(options, env) {
@@ -732,7 +749,7 @@ async function runAddCommand(args, env = process.env) {
732
749
  if (!apiKey) {
733
750
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
734
751
  }
735
- const name = options.name || args.find((arg) => !arg.startsWith("--") && arg.includes("."));
752
+ const name = options.name || args.find((arg, i) => !arg.startsWith("--") && arg.includes(".") && args[i - 1] !== "--tenant");
736
753
  if (!name) {
737
754
  return {
738
755
  exitCode: 1,
@@ -744,11 +761,14 @@ async function runAddCommand(args, env = process.env) {
744
761
  const inbound = readFlag10(args, "--inbound");
745
762
  const result = await client2.domains.create({
746
763
  name,
747
- ...inbound === "true" || inbound === "1" ? { inbound_enabled: true } : {}
764
+ ...inbound === "true" || inbound === "1" ? { inbound_enabled: true } : {},
765
+ ...options.tenant ? { tenant: options.tenant } : {}
748
766
  });
749
767
  console.log(JSON.stringify(result));
750
768
  console.error("");
751
- console.error(`Domain ${result.domain.name} added (${result.domain.status}).`);
769
+ console.error(
770
+ `Domain ${result.domain.name} added (${result.domain.status})${options.tenant ? ` for tenant ${options.tenant}` : ""}.`
771
+ );
752
772
  console.error(formatDnsRecords(result.records));
753
773
  console.error("");
754
774
  console.error(`Then: groundmail domains verify ${result.domain.name}`);
@@ -883,7 +903,7 @@ async function runListCommand3(args, env = process.env) {
883
903
  }
884
904
  try {
885
905
  const client2 = await createDomainsClient(apiKey, options.baseUrl);
886
- const result = await client2.domains.list();
906
+ const result = await client2.domains.list(options.tenant ? { tenant: options.tenant } : {});
887
907
  console.log(JSON.stringify(result));
888
908
  return { exitCode: 0 };
889
909
  } catch (error) {
@@ -1066,13 +1086,46 @@ async function runListCommand4(args, env = process.env) {
1066
1086
  }
1067
1087
  }
1068
1088
 
1069
- // src/commands/webhooks/shared.ts
1089
+ // src/commands/tenants/shared.ts
1090
+ var TENANT_VALUE_FLAGS = [
1091
+ "--api-key",
1092
+ "--base-url",
1093
+ "--tenant",
1094
+ "--id",
1095
+ "--external-id",
1096
+ "--name",
1097
+ "--default-from-domain",
1098
+ "--stream-policy",
1099
+ "--daily-limit",
1100
+ "--metadata",
1101
+ "--status",
1102
+ "--reason",
1103
+ "--limit",
1104
+ "--cursor",
1105
+ "--days",
1106
+ "--domain"
1107
+ ];
1070
1108
  function readFlag12(args, flag) {
1071
1109
  const index = args.indexOf(flag);
1072
1110
  if (index === -1) return void 0;
1073
1111
  return args[index + 1];
1074
1112
  }
1075
- function parseWebhooksCommonArgs(args) {
1113
+ function hasFlag(args, flag) {
1114
+ return args.includes(flag);
1115
+ }
1116
+ function positionals(args, valueFlags = TENANT_VALUE_FLAGS) {
1117
+ const out = [];
1118
+ for (let i = 0; i < args.length; i += 1) {
1119
+ const arg = args[i];
1120
+ if (arg.startsWith("--")) {
1121
+ if (valueFlags.includes(arg)) i += 1;
1122
+ continue;
1123
+ }
1124
+ out.push(arg);
1125
+ }
1126
+ return out;
1127
+ }
1128
+ function parseTenantsCommonArgs(args) {
1076
1129
  return {
1077
1130
  apiKey: readFlag12(args, "--api-key"),
1078
1131
  baseUrl: readFlag12(args, "--base-url")
@@ -1081,6 +1134,82 @@ function parseWebhooksCommonArgs(args) {
1081
1134
  function resolveApiKey5(options, env) {
1082
1135
  return options.apiKey || env.GROUNDMAIL_API_KEY || null;
1083
1136
  }
1137
+ async function createTenantsClient(apiKey, baseUrl) {
1138
+ const { Groundmail } = await import("groundmail");
1139
+ return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
1140
+ }
1141
+ var MISSING_KEY = "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key.";
1142
+ function failure(error) {
1143
+ return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
1144
+ }
1145
+ function parseDailyLimit(value) {
1146
+ if (value === void 0) return {};
1147
+ if (value === "none" || value === "null") return { value: null };
1148
+ const n2 = Number(value);
1149
+ if (!Number.isInteger(n2) || n2 < 0) return { error: "--daily-limit must be a whole number, or none" };
1150
+ return { value: n2 };
1151
+ }
1152
+ function parseMetadata(value) {
1153
+ if (value === void 0) return {};
1154
+ if (value === "null") return { value: null };
1155
+ try {
1156
+ const parsed = JSON.parse(value);
1157
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1158
+ return { error: "--metadata must be a JSON object" };
1159
+ }
1160
+ return { value: parsed };
1161
+ } catch {
1162
+ return { error: "--metadata must be valid JSON" };
1163
+ }
1164
+ }
1165
+ function parseStreamPolicy(value) {
1166
+ if (value === void 0) return {};
1167
+ if (value === "all" || value === "transactional_only") return { value };
1168
+ return { error: "--stream-policy must be all or transactional_only" };
1169
+ }
1170
+ function parseTenantSettings(args) {
1171
+ const dailyLimit = parseDailyLimit(readFlag12(args, "--daily-limit"));
1172
+ if ("error" in dailyLimit) return dailyLimit;
1173
+ const metadata = parseMetadata(readFlag12(args, "--metadata"));
1174
+ if ("error" in metadata) return metadata;
1175
+ const streamPolicy = parseStreamPolicy(readFlag12(args, "--stream-policy"));
1176
+ if ("error" in streamPolicy) return streamPolicy;
1177
+ const name = readFlag12(args, "--name");
1178
+ const fromDomain = readFlag12(args, "--default-from-domain");
1179
+ return {
1180
+ value: {
1181
+ ...name !== void 0 ? { name } : {},
1182
+ ...fromDomain !== void 0 ? { default_from_domain: fromDomain === "none" ? null : fromDomain } : {},
1183
+ ...streamPolicy.value !== void 0 ? { stream_policy: streamPolicy.value } : {},
1184
+ ..."value" in dailyLimit ? { daily_limit: dailyLimit.value } : {},
1185
+ ..."value" in metadata ? { metadata: metadata.value } : {}
1186
+ }
1187
+ };
1188
+ }
1189
+ function tenantRef(args) {
1190
+ return readFlag12(args, "--tenant") || readFlag12(args, "--id") || positionals(args)[0];
1191
+ }
1192
+ function parseTenantsList(value) {
1193
+ if (value === void 0) return void 0;
1194
+ if (value === "all" || value === "none" || value === "") return null;
1195
+ return value.split(/[\s,]+/).map((v) => v.trim()).filter(Boolean);
1196
+ }
1197
+
1198
+ // src/commands/webhooks/shared.ts
1199
+ function readFlag13(args, flag) {
1200
+ const index = args.indexOf(flag);
1201
+ if (index === -1) return void 0;
1202
+ return args[index + 1];
1203
+ }
1204
+ function parseWebhooksCommonArgs(args) {
1205
+ return {
1206
+ apiKey: readFlag13(args, "--api-key"),
1207
+ baseUrl: readFlag13(args, "--base-url")
1208
+ };
1209
+ }
1210
+ function resolveApiKey6(options, env) {
1211
+ return options.apiKey || env.GROUNDMAIL_API_KEY || null;
1212
+ }
1084
1213
  async function createWebhooksClient(apiKey, baseUrl) {
1085
1214
  const { Groundmail } = await import("groundmail");
1086
1215
  return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
@@ -1089,17 +1218,18 @@ async function createWebhooksClient(apiKey, baseUrl) {
1089
1218
  // src/commands/webhooks/create.ts
1090
1219
  async function runCreateCommand2(args, env = process.env) {
1091
1220
  const options = parseWebhooksCommonArgs(args);
1092
- const apiKey = resolveApiKey5(options, env);
1221
+ const apiKey = resolveApiKey6(options, env);
1093
1222
  if (!apiKey) {
1094
1223
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1095
1224
  }
1096
- const url = readFlag12(args, "--url");
1225
+ const url = readFlag13(args, "--url");
1097
1226
  if (!url) {
1098
1227
  return { exitCode: 1, error: "Required flag: --url" };
1099
1228
  }
1100
1229
  try {
1101
1230
  const client2 = await createWebhooksClient(apiKey, options.baseUrl);
1102
- const result = await client2.webhooks.create({ url });
1231
+ const tenants = parseTenantsList(readFlag13(args, "--tenants"));
1232
+ const result = await client2.webhooks.create({ url, ...tenants?.length ? { tenants } : {} });
1103
1233
  console.log(JSON.stringify(result));
1104
1234
  return { exitCode: 0 };
1105
1235
  } catch (error) {
@@ -1111,9 +1241,9 @@ async function runCreateCommand2(args, env = process.env) {
1111
1241
  var POSITIONAL_FLAGS5 = ["--api-key", "--base-url", "--id"];
1112
1242
  async function runGetCommand5(args, env = process.env) {
1113
1243
  const options = parseWebhooksCommonArgs(args);
1114
- const apiKey = resolveApiKey5(options, env);
1244
+ const apiKey = resolveApiKey6(options, env);
1115
1245
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1116
- const id = readFlag13(args, "--id") || readFirstPositional6(args);
1246
+ const id = readFlag14(args, "--id") || readFirstPositional6(args);
1117
1247
  if (!id) return { exitCode: 1, error: "Missing webhook id. Pass --id wh_\u2026" };
1118
1248
  try {
1119
1249
  const client2 = await createWebhooksClient(apiKey, options.baseUrl);
@@ -1123,7 +1253,7 @@ async function runGetCommand5(args, env = process.env) {
1123
1253
  return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
1124
1254
  }
1125
1255
  }
1126
- function readFlag13(args, flag) {
1256
+ function readFlag14(args, flag) {
1127
1257
  const index = args.indexOf(flag);
1128
1258
  return index === -1 ? void 0 : args[index + 1];
1129
1259
  }
@@ -1142,11 +1272,11 @@ function readFirstPositional6(args) {
1142
1272
  // src/commands/webhooks/delete.ts
1143
1273
  async function runDeleteCommand3(args, env = process.env) {
1144
1274
  const options = parseWebhooksCommonArgs(args);
1145
- const apiKey = resolveApiKey5(options, env);
1275
+ const apiKey = resolveApiKey6(options, env);
1146
1276
  if (!apiKey) {
1147
1277
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1148
1278
  }
1149
- const id = readFlag12(args, "--id");
1279
+ const id = readFlag13(args, "--id");
1150
1280
  if (!id) {
1151
1281
  return { exitCode: 1, error: "Required flag: --id" };
1152
1282
  }
@@ -1163,7 +1293,7 @@ async function runDeleteCommand3(args, env = process.env) {
1163
1293
  // src/commands/webhooks/list.ts
1164
1294
  async function runListCommand5(args, env = process.env) {
1165
1295
  const options = parseWebhooksCommonArgs(args);
1166
- const apiKey = resolveApiKey5(options, env);
1296
+ const apiKey = resolveApiKey6(options, env);
1167
1297
  if (!apiKey) {
1168
1298
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1169
1299
  }
@@ -1178,24 +1308,26 @@ async function runListCommand5(args, env = process.env) {
1178
1308
  }
1179
1309
 
1180
1310
  // src/commands/webhooks/update.ts
1181
- var POSITIONAL_FLAGS6 = ["--api-key", "--base-url", "--id", "--url", "--event", "--enabled"];
1311
+ var POSITIONAL_FLAGS6 = ["--api-key", "--base-url", "--id", "--url", "--event", "--enabled", "--tenants"];
1182
1312
  async function runUpdateCommand3(args, env = process.env) {
1183
1313
  const options = parseWebhooksCommonArgs(args);
1184
- const apiKey = resolveApiKey5(options, env);
1314
+ const apiKey = resolveApiKey6(options, env);
1185
1315
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1186
- const id = readFlag14(args, "--id") || readFirstPositional7(args);
1316
+ const id = readFlag15(args, "--id") || readFirstPositional7(args);
1187
1317
  if (!id) return { exitCode: 1, error: "Missing webhook id. Pass --id wh_\u2026" };
1188
- const url = readFlag14(args, "--url");
1318
+ const url = readFlag15(args, "--url");
1189
1319
  const events = readFlags2(args, "--event");
1190
- const enabled = parseEnabledFlag(readFlag14(args, "--enabled"));
1320
+ const enabled = parseEnabledFlag(readFlag15(args, "--enabled"));
1191
1321
  if ("error" in enabled) return { exitCode: 1, error: enabled.error };
1192
- if (!url && events.length === 0 && enabled.value === void 0) {
1193
- return { exitCode: 1, error: "Nothing to update. Pass --url, --event, or --enabled true|false." };
1322
+ const tenants = parseTenantsList(readFlag15(args, "--tenants"));
1323
+ if (!url && events.length === 0 && enabled.value === void 0 && tenants === void 0) {
1324
+ return { exitCode: 1, error: "Nothing to update. Pass --url, --event, --enabled true|false, or --tenants." };
1194
1325
  }
1195
1326
  const payload = {
1196
1327
  ...url ? { url } : {},
1197
1328
  ...events.length > 0 ? { events } : {},
1198
- ...enabled.value !== void 0 ? { enabled: enabled.value } : {}
1329
+ ...enabled.value !== void 0 ? { enabled: enabled.value } : {},
1330
+ ...tenants !== void 0 ? { tenants } : {}
1199
1331
  };
1200
1332
  try {
1201
1333
  const client2 = await createWebhooksClient(apiKey, options.baseUrl);
@@ -1205,7 +1337,7 @@ async function runUpdateCommand3(args, env = process.env) {
1205
1337
  return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
1206
1338
  }
1207
1339
  }
1208
- function readFlag14(args, flag) {
1340
+ function readFlag15(args, flag) {
1209
1341
  const index = args.indexOf(flag);
1210
1342
  return index === -1 ? void 0 : args[index + 1];
1211
1343
  }
@@ -1235,22 +1367,22 @@ function parseEnabledFlag(value) {
1235
1367
  }
1236
1368
 
1237
1369
  // src/commands/keys/list.ts
1238
- function readFlag15(args, flag) {
1370
+ function readFlag16(args, flag) {
1239
1371
  const index = args.indexOf(flag);
1240
1372
  if (index === -1) return void 0;
1241
1373
  return args[index + 1];
1242
1374
  }
1243
1375
  async function runListKeysCommandWithClient(args, env = process.env) {
1244
- const apiKey = readFlag15(args, "--api-key") || env.GROUNDMAIL_API_KEY;
1245
- const baseUrl = readFlag15(args, "--base-url");
1376
+ const apiKey = readFlag16(args, "--api-key") || env.GROUNDMAIL_API_KEY;
1377
+ const baseUrl = readFlag16(args, "--base-url");
1246
1378
  if (!apiKey) {
1247
1379
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1248
1380
  }
1249
1381
  const { Groundmail } = await import("groundmail");
1250
1382
  const client2 = new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
1251
1383
  try {
1252
- const limitRaw = readFlag15(args, "--limit");
1253
- const cursor = readFlag15(args, "--cursor");
1384
+ const limitRaw = readFlag16(args, "--limit");
1385
+ const cursor = readFlag16(args, "--cursor");
1254
1386
  const result = await client2.apiKeys.list({
1255
1387
  ...limitRaw ? { limit: Number(limitRaw) } : {},
1256
1388
  ...cursor ? { cursor } : {}
@@ -1288,7 +1420,7 @@ function trimUrl(value) {
1288
1420
  }
1289
1421
 
1290
1422
  // package.json
1291
- var version = "0.19.0";
1423
+ var version = "0.22.0";
1292
1424
 
1293
1425
  // src/user-agent.ts
1294
1426
  function buildUserAgent(own) {
@@ -1372,15 +1504,15 @@ function parseArgs(argv, spec) {
1372
1504
  const booleanFlags = new Set(spec.booleans ?? []);
1373
1505
  const values = {};
1374
1506
  const booleans = {};
1375
- const positionals = [];
1507
+ const positionals2 = [];
1376
1508
  for (let i = 0; i < argv.length; i++) {
1377
1509
  const arg = argv[i];
1378
1510
  if (arg === "--") {
1379
- positionals.push(...argv.slice(i + 1));
1511
+ positionals2.push(...argv.slice(i + 1));
1380
1512
  break;
1381
1513
  }
1382
1514
  if (!arg.startsWith("--") || arg === "-") {
1383
- positionals.push(arg);
1515
+ positionals2.push(arg);
1384
1516
  continue;
1385
1517
  }
1386
1518
  const eq = arg.indexOf("=");
@@ -1406,7 +1538,7 @@ function parseArgs(argv, spec) {
1406
1538
  const known = [...valueFlags, ...booleanFlags].sort().join(", ");
1407
1539
  return { ok: false, error: `Unknown flag ${name}.${known ? ` This command takes: ${known}.` : ""}` };
1408
1540
  }
1409
- return { ok: true, args: { values, booleans, positionals } };
1541
+ return { ok: true, args: { values, booleans, positionals: positionals2 } };
1410
1542
  }
1411
1543
  function wantsJson(args, stdout = process.stdout) {
1412
1544
  if (args.booleans["--json"] !== void 0) return args.booleans["--json"];
@@ -1714,10 +1846,10 @@ async function resolveAuth(flags, env = process.env, config = loadConfig(env)) {
1714
1846
  return fromSaved(current.workspace);
1715
1847
  }
1716
1848
  async function applySavedSession(argv, env = process.env, config) {
1717
- const hasFlag = (flag) => argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
1849
+ const hasFlag2 = (flag) => argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
1718
1850
  let workspace = null;
1719
1851
  let notice;
1720
- if (!hasFlag("--api-key") && !envApiKey(env)) {
1852
+ if (!hasFlag2("--api-key") && !envApiKey(env)) {
1721
1853
  const current = currentSavedWorkspace(config ?? loadConfig(env), env);
1722
1854
  if (current.ok) {
1723
1855
  workspace = current.workspace;
@@ -1728,7 +1860,7 @@ async function applySavedSession(argv, env = process.env, config) {
1728
1860
  notice = current.error;
1729
1861
  }
1730
1862
  }
1731
- if (hasFlag("--base-url")) return { argv, notice };
1863
+ if (hasFlag2("--base-url")) return { argv, notice };
1732
1864
  const url = explicitApiUrl(env) || trimUrl(workspace?.apiUrl) || trimUrl(env.GROUNDMAIL_DEFAULT_API_URL);
1733
1865
  return { argv: url ? [...argv, "--base-url", url] : argv, notice };
1734
1866
  }
@@ -2125,27 +2257,29 @@ Usage: ${brand.bin} whoami [--workspace <name>] [--json]` };
2125
2257
  }
2126
2258
 
2127
2259
  // src/commands/keys/create.ts
2128
- function readFlag16(args, flag) {
2260
+ function readFlag17(args, flag) {
2129
2261
  const index = args.indexOf(flag);
2130
2262
  if (index === -1) return void 0;
2131
2263
  return args[index + 1];
2132
2264
  }
2133
2265
  async function runCreateKeyCommandWithClient(args, env = process.env) {
2134
- const apiKey = readFlag16(args, "--api-key") || env.GROUNDMAIL_API_KEY;
2135
- const baseUrl = readFlag16(args, "--base-url") || defaultApiUrl(env);
2266
+ const apiKey = readFlag17(args, "--api-key") || env.GROUNDMAIL_API_KEY;
2267
+ const baseUrl = readFlag17(args, "--base-url") || defaultApiUrl(env);
2136
2268
  if (!apiKey) {
2137
2269
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2138
2270
  }
2139
- const name = readFlag16(args, "--name");
2140
- const scope = readFlag16(args, "--scope");
2271
+ const name = readFlag17(args, "--name");
2272
+ const scope = readFlag17(args, "--scope");
2141
2273
  if (scope && scope !== "full" && scope !== "sending") {
2142
2274
  return { exitCode: 1, error: "--scope must be full or sending (or use --scopes for fine-grained scopes)" };
2143
2275
  }
2144
- const scopesFlag = readFlag16(args, "--scopes");
2276
+ const scopesFlag = readFlag17(args, "--scopes");
2145
2277
  const scopes = scopesFlag !== void 0 ? parseScopeFlag(scopesFlag) : null;
2146
2278
  if (scopes && !scopes.ok) return { exitCode: 1, error: scopes.error };
2147
- const domainsFlag = readFlag16(args, "--allowed-domains");
2279
+ const domainsFlag = readFlag17(args, "--allowed-domains");
2148
2280
  const allowedDomains = domainsFlag?.split(/[\s,]+/).map((domain) => domain.trim()).filter(Boolean);
2281
+ const test = args.includes("--test");
2282
+ const tenant = readFlag17(args, "--tenant");
2149
2283
  try {
2150
2284
  const response = await apiRequest(baseUrl, "/api-keys", {
2151
2285
  apiKey,
@@ -2154,13 +2288,18 @@ async function runCreateKeyCommandWithClient(args, env = process.env) {
2154
2288
  ...name ? { name } : {},
2155
2289
  ...scope && !scopes ? { scope } : {},
2156
2290
  ...scopes?.ok ? { scopes: scopes.scope.split(" ") } : {},
2157
- ...allowedDomains?.length ? { allowed_domains: allowedDomains } : {}
2291
+ ...allowedDomains?.length ? { allowed_domains: allowedDomains } : {},
2292
+ ...test ? { test: true } : {},
2293
+ ...tenant ? { tenant } : {}
2158
2294
  }
2159
2295
  });
2160
2296
  if (!response.ok) {
2161
2297
  return { exitCode: 1, error: errorMessage(response, "Could not create the key") };
2162
2298
  }
2163
2299
  console.log(JSON.stringify(response.body));
2300
+ if (test) {
2301
+ console.error("Test key: sends with it run every check and fire webhooks, but nothing is delivered. https://docs.groundmail.dev/testing");
2302
+ }
2164
2303
  return { exitCode: 0 };
2165
2304
  } catch (error) {
2166
2305
  return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
@@ -2168,15 +2307,15 @@ async function runCreateKeyCommandWithClient(args, env = process.env) {
2168
2307
  }
2169
2308
 
2170
2309
  // src/commands/keys/delete.ts
2171
- function readFlag17(args, flag) {
2310
+ function readFlag18(args, flag) {
2172
2311
  const index = args.indexOf(flag);
2173
2312
  if (index === -1) return void 0;
2174
2313
  return args[index + 1];
2175
2314
  }
2176
2315
  async function runDeleteKeyCommandWithClient(args, env = process.env) {
2177
- const apiKey = readFlag17(args, "--api-key") || env.GROUNDMAIL_API_KEY;
2178
- const baseUrl = readFlag17(args, "--base-url");
2179
- const id = readFlag17(args, "--id");
2316
+ const apiKey = readFlag18(args, "--api-key") || env.GROUNDMAIL_API_KEY;
2317
+ const baseUrl = readFlag18(args, "--base-url");
2318
+ const id = readFlag18(args, "--id");
2180
2319
  if (!apiKey) {
2181
2320
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2182
2321
  }
@@ -2197,18 +2336,18 @@ async function runDeleteKeyCommandWithClient(args, env = process.env) {
2197
2336
  }
2198
2337
 
2199
2338
  // src/commands/suppressions/shared.ts
2200
- function readFlag18(args, flag) {
2339
+ function readFlag19(args, flag) {
2201
2340
  const index = args.indexOf(flag);
2202
2341
  if (index === -1) return void 0;
2203
2342
  return args[index + 1];
2204
2343
  }
2205
2344
  function parseSuppressionsCommonArgs(args) {
2206
2345
  return {
2207
- apiKey: readFlag18(args, "--api-key"),
2208
- baseUrl: readFlag18(args, "--base-url")
2346
+ apiKey: readFlag19(args, "--api-key"),
2347
+ baseUrl: readFlag19(args, "--base-url")
2209
2348
  };
2210
2349
  }
2211
- function resolveApiKey6(options, env) {
2350
+ function resolveApiKey7(options, env) {
2212
2351
  return options.apiKey || env.GROUNDMAIL_API_KEY || null;
2213
2352
  }
2214
2353
  async function createSuppressionsClient(apiKey, baseUrl) {
@@ -2219,19 +2358,21 @@ async function createSuppressionsClient(apiKey, baseUrl) {
2219
2358
  // src/commands/suppressions/list.ts
2220
2359
  async function runListCommand6(args, env = process.env) {
2221
2360
  const options = parseSuppressionsCommonArgs(args);
2222
- const apiKey = resolveApiKey6(options, env);
2361
+ const apiKey = resolveApiKey7(options, env);
2223
2362
  if (!apiKey) {
2224
2363
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2225
2364
  }
2226
2365
  try {
2227
2366
  const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
2228
- const limitRaw = readFlag18(args, "--limit");
2229
- const cursor = readFlag18(args, "--cursor");
2230
- const email = readFlag18(args, "--email");
2367
+ const limitRaw = readFlag19(args, "--limit");
2368
+ const cursor = readFlag19(args, "--cursor");
2369
+ const email = readFlag19(args, "--email");
2370
+ const tenant = readFlag19(args, "--tenant");
2231
2371
  const result = await client2.suppressions.list({
2232
2372
  ...limitRaw ? { limit: Number(limitRaw) } : {},
2233
2373
  ...cursor ? { cursor } : {},
2234
- ...email ? { email } : {}
2374
+ ...email ? { email } : {},
2375
+ ...tenant ? { tenant } : {}
2235
2376
  });
2236
2377
  console.log(JSON.stringify(result));
2237
2378
  return { exitCode: 0 };
@@ -2243,20 +2384,22 @@ async function runListCommand6(args, env = process.env) {
2243
2384
  // src/commands/suppressions/add.ts
2244
2385
  async function runAddCommand2(args, env = process.env) {
2245
2386
  const options = parseSuppressionsCommonArgs(args);
2246
- const apiKey = resolveApiKey6(options, env);
2387
+ const apiKey = resolveApiKey7(options, env);
2247
2388
  if (!apiKey) {
2248
2389
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2249
2390
  }
2250
- const email = readFlag18(args, "--email") || args.find((a) => a.includes("@") && !a.startsWith("-"));
2391
+ const tenant = readFlag19(args, "--tenant");
2392
+ const email = readFlag19(args, "--email") || args.find((a, i) => a.includes("@") && !a.startsWith("-") && args[i - 1] !== "--tenant");
2251
2393
  if (!email) {
2252
2394
  return { exitCode: 1, error: "Missing email. Pass --email user@example.com" };
2253
2395
  }
2254
2396
  try {
2255
2397
  const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
2256
- const reason = readFlag18(args, "--reason");
2398
+ const reason = readFlag19(args, "--reason");
2257
2399
  const result = await client2.suppressions.create({
2258
2400
  email,
2259
- ...reason ? { reason } : {}
2401
+ ...reason ? { reason } : {},
2402
+ ...tenant ? { tenant } : {}
2260
2403
  });
2261
2404
  console.log(JSON.stringify(result));
2262
2405
  return { exitCode: 0 };
@@ -2268,17 +2411,20 @@ async function runAddCommand2(args, env = process.env) {
2268
2411
  // src/commands/suppressions/remove.ts
2269
2412
  async function runRemoveCommand(args, env = process.env) {
2270
2413
  const options = parseSuppressionsCommonArgs(args);
2271
- const apiKey = resolveApiKey6(options, env);
2414
+ const apiKey = resolveApiKey7(options, env);
2272
2415
  if (!apiKey) {
2273
2416
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2274
2417
  }
2275
- const idOrEmail = readFlag18(args, "--id") || readFlag18(args, "--email") || args.find((a) => !a.startsWith("-") && a !== "remove" && a !== "delete");
2418
+ const tenant = readFlag19(args, "--tenant");
2419
+ const idOrEmail = readFlag19(args, "--id") || readFlag19(args, "--email") || args.find(
2420
+ (a, i) => !a.startsWith("-") && a !== "remove" && a !== "delete" && !["--tenant", "--api-key", "--base-url"].includes(args[i - 1] ?? "")
2421
+ );
2276
2422
  if (!idOrEmail) {
2277
2423
  return { exitCode: 1, error: "Missing id or email. Pass --id <uuid> or --email user@example.com" };
2278
2424
  }
2279
2425
  try {
2280
2426
  const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
2281
- const result = await client2.suppressions.remove(idOrEmail);
2427
+ const result = tenant ? await client2.suppressions.remove(idOrEmail, { tenant }) : await client2.suppressions.remove(idOrEmail);
2282
2428
  console.log(JSON.stringify(result));
2283
2429
  return { exitCode: 0 };
2284
2430
  } catch (error) {
@@ -2287,18 +2433,18 @@ async function runRemoveCommand(args, env = process.env) {
2287
2433
  }
2288
2434
 
2289
2435
  // src/commands/templates/shared.ts
2290
- function readFlag19(args, flag) {
2436
+ function readFlag20(args, flag) {
2291
2437
  const index = args.indexOf(flag);
2292
2438
  if (index === -1) return void 0;
2293
2439
  return args[index + 1];
2294
2440
  }
2295
2441
  function parseTemplatesCommonArgs(args) {
2296
2442
  return {
2297
- apiKey: readFlag19(args, "--api-key"),
2298
- baseUrl: readFlag19(args, "--base-url")
2443
+ apiKey: readFlag20(args, "--api-key"),
2444
+ baseUrl: readFlag20(args, "--base-url")
2299
2445
  };
2300
2446
  }
2301
- function resolveApiKey7(options, env) {
2447
+ function resolveApiKey8(options, env) {
2302
2448
  return options.apiKey || env.GROUNDMAIL_API_KEY || null;
2303
2449
  }
2304
2450
  async function createTemplatesClient(apiKey, baseUrl) {
@@ -2310,9 +2456,9 @@ async function createTemplatesClient(apiKey, baseUrl) {
2310
2456
  var POSITIONAL_FLAGS7 = ["--api-key", "--base-url", "--id", "--alias"];
2311
2457
  async function runDeleteCommand4(args, env = process.env) {
2312
2458
  const options = parseTemplatesCommonArgs(args);
2313
- const apiKey = resolveApiKey7(options, env);
2459
+ const apiKey = resolveApiKey8(options, env);
2314
2460
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2315
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional8(args);
2461
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional8(args);
2316
2462
  if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2317
2463
  try {
2318
2464
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
@@ -2338,11 +2484,11 @@ function readFirstPositional8(args) {
2338
2484
  var POSITIONAL_FLAGS8 = ["--api-key", "--base-url", "--id", "--alias", "--name"];
2339
2485
  async function runDuplicateCommand(args, env = process.env) {
2340
2486
  const options = parseTemplatesCommonArgs(args);
2341
- const apiKey = resolveApiKey7(options, env);
2487
+ const apiKey = resolveApiKey8(options, env);
2342
2488
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2343
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional9(args);
2489
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional9(args);
2344
2490
  if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2345
- const name = readFlag19(args, "--name");
2491
+ const name = readFlag20(args, "--name");
2346
2492
  try {
2347
2493
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
2348
2494
  console.log(JSON.stringify(await client2.templates.duplicate(idOrAlias, name ? { name } : {})));
@@ -2367,9 +2513,9 @@ function readFirstPositional9(args) {
2367
2513
  var POSITIONAL_FLAGS9 = ["--api-key", "--base-url", "--id", "--alias"];
2368
2514
  async function runGetCommand6(args, env = process.env) {
2369
2515
  const options = parseTemplatesCommonArgs(args);
2370
- const apiKey = resolveApiKey7(options, env);
2516
+ const apiKey = resolveApiKey8(options, env);
2371
2517
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2372
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional10(args);
2518
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional10(args);
2373
2519
  if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2374
2520
  try {
2375
2521
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
@@ -2394,15 +2540,15 @@ function readFirstPositional10(args) {
2394
2540
  // src/commands/templates/list.ts
2395
2541
  async function runListCommand7(args, env = process.env) {
2396
2542
  const options = parseTemplatesCommonArgs(args);
2397
- const apiKey = resolveApiKey7(options, env);
2543
+ const apiKey = resolveApiKey8(options, env);
2398
2544
  if (!apiKey) {
2399
2545
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2400
2546
  }
2401
2547
  try {
2402
2548
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
2403
- const limitRaw = readFlag19(args, "--limit");
2404
- const cursor = readFlag19(args, "--cursor");
2405
- const status = readFlag19(args, "--status");
2549
+ const limitRaw = readFlag20(args, "--limit");
2550
+ const cursor = readFlag20(args, "--cursor");
2551
+ const status = readFlag20(args, "--status");
2406
2552
  const result = await client2.templates.list({
2407
2553
  ...limitRaw ? { limit: Number(limitRaw) } : {},
2408
2554
  ...cursor ? { cursor } : {},
@@ -2419,9 +2565,9 @@ async function runListCommand7(args, env = process.env) {
2419
2565
  var POSITIONAL_FLAGS10 = ["--api-key", "--base-url", "--id", "--alias"];
2420
2566
  async function runPublishCommand(args, env = process.env) {
2421
2567
  const options = parseTemplatesCommonArgs(args);
2422
- const apiKey = resolveApiKey7(options, env);
2568
+ const apiKey = resolveApiKey8(options, env);
2423
2569
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2424
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional11(args);
2570
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional11(args);
2425
2571
  if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2426
2572
  try {
2427
2573
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
@@ -2560,23 +2706,23 @@ function statusOf(error) {
2560
2706
  }
2561
2707
  async function runPushCommand(args, env = process.env, deps = {}) {
2562
2708
  const options = parseTemplatesCommonArgs(args);
2563
- const apiKey = resolveApiKey7(options, env);
2709
+ const apiKey = resolveApiKey8(options, env);
2564
2710
  if (!apiKey) {
2565
2711
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2566
2712
  }
2567
- const file = readFlag19(args, "--file") ?? readPositional(args);
2713
+ const file = readFlag20(args, "--file") ?? readPositional(args);
2568
2714
  if (!file) {
2569
2715
  return { exitCode: 1, error: "Missing template file. Usage: templates push <file> --alias <alias>" };
2570
2716
  }
2571
- const alias = readFlag19(args, "--alias");
2717
+ const alias = readFlag20(args, "--alias");
2572
2718
  if (!alias) {
2573
2719
  return { exitCode: 1, error: "Missing --alias. The alias identifies which template to update." };
2574
2720
  }
2575
- const subject = readFlag19(args, "--subject");
2576
- const name = readFlag19(args, "--name");
2577
- const preheader = readFlag19(args, "--preheader");
2578
- const textFile = readFlag19(args, "--text");
2579
- const propsRaw = readFlag19(args, "--props");
2721
+ const subject = readFlag20(args, "--subject");
2722
+ const name = readFlag20(args, "--name");
2723
+ const preheader = readFlag20(args, "--preheader");
2724
+ const textFile = readFlag20(args, "--text");
2725
+ const propsRaw = readFlag20(args, "--props");
2580
2726
  const shouldPublish = args.includes("--publish");
2581
2727
  let props = {};
2582
2728
  if (propsRaw) {
@@ -2646,16 +2792,16 @@ async function runPushCommand(args, env = process.env, deps = {}) {
2646
2792
  // src/commands/templates/create.ts
2647
2793
  async function runCreateCommand3(args, env = process.env) {
2648
2794
  const options = parseTemplatesCommonArgs(args);
2649
- const apiKey = resolveApiKey7(options, env);
2795
+ const apiKey = resolveApiKey8(options, env);
2650
2796
  if (!apiKey) {
2651
2797
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2652
2798
  }
2653
- const name = readFlag19(args, "--name");
2654
- const subject = readFlag19(args, "--subject");
2655
- const html = readFlag19(args, "--html");
2656
- const text = readFlag19(args, "--text");
2657
- const alias = readFlag19(args, "--alias");
2658
- const category = readFlag19(args, "--category");
2799
+ const name = readFlag20(args, "--name");
2800
+ const subject = readFlag20(args, "--subject");
2801
+ const html = readFlag20(args, "--html");
2802
+ const text = readFlag20(args, "--text");
2803
+ const alias = readFlag20(args, "--alias");
2804
+ const category = readFlag20(args, "--category");
2659
2805
  const unsubscribeEnabled = args.includes("--unsubscribe-enabled");
2660
2806
  if (!name) {
2661
2807
  return { exitCode: 1, error: 'Missing name. Pass --name "Welcome"' };
@@ -2699,21 +2845,21 @@ var POSITIONAL_FLAGS11 = [
2699
2845
  ];
2700
2846
  async function runUpdateCommand4(args, env = process.env) {
2701
2847
  const options = parseTemplatesCommonArgs(args);
2702
- const apiKey = resolveApiKey7(options, env);
2848
+ const apiKey = resolveApiKey8(options, env);
2703
2849
  if (!apiKey) {
2704
2850
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2705
2851
  }
2706
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional12(args);
2852
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional12(args);
2707
2853
  if (!idOrAlias) {
2708
2854
  return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2709
2855
  }
2710
- const name = readFlag19(args, "--name");
2711
- const subject = readFlag19(args, "--subject");
2712
- const html = readFlag19(args, "--html");
2713
- const text = readFlag19(args, "--text");
2714
- const alias = readFlag19(args, "--set-alias");
2856
+ const name = readFlag20(args, "--name");
2857
+ const subject = readFlag20(args, "--subject");
2858
+ const html = readFlag20(args, "--html");
2859
+ const text = readFlag20(args, "--text");
2860
+ const alias = readFlag20(args, "--set-alias");
2715
2861
  const variables = readFlags3(args, "--variable");
2716
- const category = readFlag19(args, "--category");
2862
+ const category = readFlag20(args, "--category");
2717
2863
  const unsubscribeEnabled = args.includes("--unsubscribe-enabled");
2718
2864
  const unsubscribeDisabled = args.includes("--no-unsubscribe-enabled");
2719
2865
  if (!name && !subject && !html && !text && !alias && variables.length === 0 && !category && !unsubscribeEnabled && !unsubscribeDisabled) {
@@ -2775,11 +2921,11 @@ function readFirstPositional13(args) {
2775
2921
  }
2776
2922
  async function runFromStarterCommand(args, env = process.env) {
2777
2923
  const options = parseTemplatesCommonArgs(args);
2778
- const apiKey = resolveApiKey7(options, env);
2924
+ const apiKey = resolveApiKey8(options, env);
2779
2925
  if (!apiKey) {
2780
2926
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2781
2927
  }
2782
- const alias = readFlag19(args, "--alias") || readFirstPositional13(args);
2928
+ const alias = readFlag20(args, "--alias") || readFirstPositional13(args);
2783
2929
  if (!alias) {
2784
2930
  return {
2785
2931
  exitCode: 1,
@@ -2796,7 +2942,7 @@ async function runFromStarterCommand(args, env = process.env) {
2796
2942
  }
2797
2943
  async function runListStartersCommand(args, env = process.env) {
2798
2944
  const options = parseTemplatesCommonArgs(args);
2799
- const apiKey = resolveApiKey7(options, env);
2945
+ const apiKey = resolveApiKey8(options, env);
2800
2946
  if (!apiKey) {
2801
2947
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2802
2948
  }
@@ -2824,11 +2970,11 @@ function readFirstPositional14(args) {
2824
2970
  }
2825
2971
  async function runVersionsCommand(args, env = process.env) {
2826
2972
  const options = parseTemplatesCommonArgs(args);
2827
- const apiKey = resolveApiKey7(options, env);
2973
+ const apiKey = resolveApiKey8(options, env);
2828
2974
  if (!apiKey) {
2829
2975
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2830
2976
  }
2831
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional14(args);
2977
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional14(args);
2832
2978
  if (!idOrAlias) {
2833
2979
  return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2834
2980
  }
@@ -2842,12 +2988,12 @@ async function runVersionsCommand(args, env = process.env) {
2842
2988
  }
2843
2989
  async function runRestoreCommand(args, env = process.env) {
2844
2990
  const options = parseTemplatesCommonArgs(args);
2845
- const apiKey = resolveApiKey7(options, env);
2991
+ const apiKey = resolveApiKey8(options, env);
2846
2992
  if (!apiKey) {
2847
2993
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2848
2994
  }
2849
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional14(args);
2850
- const versionRaw = readFlag19(args, "--version");
2995
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional14(args);
2996
+ const versionRaw = readFlag20(args, "--version");
2851
2997
  const version2 = versionRaw ? Number.parseInt(versionRaw, 10) : NaN;
2852
2998
  if (!idOrAlias || !Number.isFinite(version2) || version2 < 1) {
2853
2999
  return {
@@ -2865,13 +3011,13 @@ async function runRestoreCommand(args, env = process.env) {
2865
3011
  }
2866
3012
  async function runTestSendCommand(args, env = process.env) {
2867
3013
  const options = parseTemplatesCommonArgs(args);
2868
- const apiKey = resolveApiKey7(options, env);
3014
+ const apiKey = resolveApiKey8(options, env);
2869
3015
  if (!apiKey) {
2870
3016
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2871
3017
  }
2872
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional14(args);
2873
- const from = readFlag19(args, "--from");
2874
- const to = readFlag19(args, "--to");
3018
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional14(args);
3019
+ const from = readFlag20(args, "--from");
3020
+ const to = readFlag20(args, "--to");
2875
3021
  if (!idOrAlias || !from || !to) {
2876
3022
  return {
2877
3023
  exitCode: 1,
@@ -2888,18 +3034,18 @@ async function runTestSendCommand(args, env = process.env) {
2888
3034
  }
2889
3035
 
2890
3036
  // src/commands/integrations/shared.ts
2891
- function readFlag20(args, flag) {
3037
+ function readFlag21(args, flag) {
2892
3038
  const index = args.indexOf(flag);
2893
3039
  if (index === -1) return void 0;
2894
3040
  return args[index + 1];
2895
3041
  }
2896
3042
  function parseIntegrationsCommonArgs(args) {
2897
3043
  return {
2898
- apiKey: readFlag20(args, "--api-key"),
2899
- baseUrl: readFlag20(args, "--base-url")
3044
+ apiKey: readFlag21(args, "--api-key"),
3045
+ baseUrl: readFlag21(args, "--base-url")
2900
3046
  };
2901
3047
  }
2902
- function resolveApiKey8(options, env) {
3048
+ function resolveApiKey9(options, env) {
2903
3049
  return options.apiKey || env.GROUNDMAIL_API_KEY || null;
2904
3050
  }
2905
3051
  async function createIntegrationsClient(apiKey, baseUrl) {
@@ -2910,7 +3056,7 @@ async function createIntegrationsClient(apiKey, baseUrl) {
2910
3056
  // src/commands/integrations/twilio.ts
2911
3057
  async function runTwilioStatusCommand(args, env = process.env) {
2912
3058
  const options = parseIntegrationsCommonArgs(args);
2913
- const apiKey = resolveApiKey8(options, env);
3059
+ const apiKey = resolveApiKey9(options, env);
2914
3060
  if (!apiKey) {
2915
3061
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2916
3062
  }
@@ -2925,14 +3071,14 @@ async function runTwilioStatusCommand(args, env = process.env) {
2925
3071
  }
2926
3072
  async function runTwilioConnectCommand(args, env = process.env) {
2927
3073
  const options = parseIntegrationsCommonArgs(args);
2928
- const apiKey = resolveApiKey8(options, env);
3074
+ const apiKey = resolveApiKey9(options, env);
2929
3075
  if (!apiKey) {
2930
3076
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2931
3077
  }
2932
- const accountSid = readFlag20(args, "--account-sid") || env.TWILIO_ACCOUNT_SID || env.TWILIO_SID || "";
2933
- const authToken = readFlag20(args, "--auth-token") || env.TWILIO_AUTH_TOKEN || env.TWILIO_TOKEN || "";
2934
- const defaultFrom = readFlag20(args, "--from") || readFlag20(args, "--default-from") || env.TWILIO_DEFAULT_FROM || "";
2935
- const messagingServiceSid = readFlag20(args, "--messaging-service-sid") || readFlag20(args, "--messaging-sid") || env.TWILIO_MESSAGING_SERVICE_SID || "";
3078
+ const accountSid = readFlag21(args, "--account-sid") || env.TWILIO_ACCOUNT_SID || env.TWILIO_SID || "";
3079
+ const authToken = readFlag21(args, "--auth-token") || env.TWILIO_AUTH_TOKEN || env.TWILIO_TOKEN || "";
3080
+ const defaultFrom = readFlag21(args, "--from") || readFlag21(args, "--default-from") || env.TWILIO_DEFAULT_FROM || "";
3081
+ const messagingServiceSid = readFlag21(args, "--messaging-service-sid") || readFlag21(args, "--messaging-sid") || env.TWILIO_MESSAGING_SERVICE_SID || "";
2936
3082
  if (!accountSid.trim() || !authToken.trim()) {
2937
3083
  return {
2938
3084
  exitCode: 1,
@@ -2961,7 +3107,7 @@ async function runTwilioConnectCommand(args, env = process.env) {
2961
3107
  }
2962
3108
  async function runTwilioDisconnectCommand(args, env = process.env) {
2963
3109
  const options = parseIntegrationsCommonArgs(args);
2964
- const apiKey = resolveApiKey8(options, env);
3110
+ const apiKey = resolveApiKey9(options, env);
2965
3111
  if (!apiKey) {
2966
3112
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2967
3113
  }
@@ -2975,6 +3121,125 @@ async function runTwilioDisconnectCommand(args, env = process.env) {
2975
3121
  }
2976
3122
  }
2977
3123
 
3124
+ // src/commands/tenants/commands.ts
3125
+ async function withClient(args, env, run) {
3126
+ const options = parseTenantsCommonArgs(args);
3127
+ const apiKey = resolveApiKey5(options, env);
3128
+ if (!apiKey) return { exitCode: 1, error: MISSING_KEY };
3129
+ try {
3130
+ const client2 = await createTenantsClient(apiKey, options.baseUrl);
3131
+ console.log(JSON.stringify(await run(client2)));
3132
+ return { exitCode: 0 };
3133
+ } catch (error) {
3134
+ return failure(error);
3135
+ }
3136
+ }
3137
+ function missingTenant(usage) {
3138
+ return { exitCode: 1, error: `Missing tenant. Usage: ${usage}` };
3139
+ }
3140
+ async function runListCommand8(args, env = process.env) {
3141
+ const status = readFlag12(args, "--status");
3142
+ if (status !== void 0 && status !== "active" && status !== "paused") {
3143
+ return { exitCode: 1, error: "--status must be active or paused" };
3144
+ }
3145
+ const limitRaw = readFlag12(args, "--limit");
3146
+ const cursor = readFlag12(args, "--cursor");
3147
+ return withClient(
3148
+ args,
3149
+ env,
3150
+ (client2) => client2.tenants.list({
3151
+ ...limitRaw ? { limit: Number(limitRaw) } : {},
3152
+ ...cursor ? { cursor } : {},
3153
+ ...status ? { status } : {}
3154
+ })
3155
+ );
3156
+ }
3157
+ async function runCreateCommand4(args, env = process.env) {
3158
+ const externalId = readFlag12(args, "--external-id") || positionals(args)[0];
3159
+ if (!externalId) {
3160
+ return { exitCode: 1, error: "Missing external id. Usage: groundmail tenants create <external-id> [--name \u2026]" };
3161
+ }
3162
+ const settings = parseTenantSettings(args);
3163
+ if ("error" in settings) return { exitCode: 1, error: settings.error };
3164
+ return withClient(args, env, (client2) => client2.tenants.create({ external_id: externalId, ...settings.value }));
3165
+ }
3166
+ async function runGetCommand7(args, env = process.env) {
3167
+ const tenant = tenantRef(args);
3168
+ if (!tenant) return missingTenant("groundmail tenants get <tenant>");
3169
+ return withClient(args, env, (client2) => client2.tenants.get(tenant));
3170
+ }
3171
+ async function runUpdateCommand5(args, env = process.env) {
3172
+ const tenant = tenantRef(args);
3173
+ if (!tenant) return missingTenant("groundmail tenants update <tenant> [--name \u2026] [--daily-limit N|none]");
3174
+ const settings = parseTenantSettings(args);
3175
+ if ("error" in settings) return { exitCode: 1, error: settings.error };
3176
+ if (Object.keys(settings.value).length === 0) {
3177
+ return {
3178
+ exitCode: 1,
3179
+ error: "Nothing to update. Pass --name, --default-from-domain, --stream-policy, --daily-limit or --metadata (or use tenants pause / resume)."
3180
+ };
3181
+ }
3182
+ return withClient(args, env, (client2) => client2.tenants.update(tenant, settings.value));
3183
+ }
3184
+ async function runPauseCommand2(args, env = process.env) {
3185
+ const tenant = tenantRef(args);
3186
+ if (!tenant) return missingTenant("groundmail tenants pause <tenant> [--reason \u2026]");
3187
+ const reason = readFlag12(args, "--reason");
3188
+ return withClient(args, env, (client2) => client2.tenants.pause(tenant, reason));
3189
+ }
3190
+ async function runResumeCommand(args, env = process.env) {
3191
+ const tenant = tenantRef(args);
3192
+ if (!tenant) return missingTenant("groundmail tenants resume <tenant>");
3193
+ return withClient(args, env, (client2) => client2.tenants.resume(tenant));
3194
+ }
3195
+ async function runDeleteCommand5(args, env = process.env) {
3196
+ const tenant = tenantRef(args);
3197
+ if (!tenant) return missingTenant("groundmail tenants delete <tenant> --yes");
3198
+ if (!hasFlag(args, "--yes")) {
3199
+ return {
3200
+ exitCode: 1,
3201
+ error: `This deletes tenant ${tenant} and its suppression list; its domains go back to the account and keys limited to it stop working. Run again with --yes to confirm.`
3202
+ };
3203
+ }
3204
+ return withClient(args, env, (client2) => client2.tenants.delete(tenant));
3205
+ }
3206
+ async function runStatsCommand(args, env = process.env) {
3207
+ const tenant = tenantRef(args);
3208
+ if (!tenant) return missingTenant("groundmail tenants stats <tenant> [--days 7]");
3209
+ const daysRaw = readFlag12(args, "--days");
3210
+ const days = daysRaw !== void 0 ? Number(daysRaw) : void 0;
3211
+ if (days !== void 0 && (!Number.isInteger(days) || days < 1 || days > 90)) {
3212
+ return { exitCode: 1, error: "--days must be a whole number from 1 to 90" };
3213
+ }
3214
+ return withClient(args, env, (client2) => client2.tenants.stats(tenant, days !== void 0 ? { days } : {}));
3215
+ }
3216
+ async function runDomainsCommand(args, env = process.env) {
3217
+ const [action, ...rest] = args;
3218
+ const pos = positionals(rest);
3219
+ const tenant = readFlag12(rest, "--tenant") || readFlag12(rest, "--id") || pos.shift();
3220
+ const domain = readFlag12(rest, "--domain") || pos.shift();
3221
+ if (action === "list") {
3222
+ if (!tenant) return missingTenant("groundmail tenants domains list <tenant>");
3223
+ return withClient(rest, env, (client2) => client2.tenants.domains.list(tenant));
3224
+ }
3225
+ if (action === "add" || action === "assign") {
3226
+ if (!tenant || !domain) {
3227
+ return { exitCode: 1, error: "Usage: groundmail tenants domains add <tenant> <domain>" };
3228
+ }
3229
+ return withClient(rest, env, (client2) => client2.tenants.domains.assign(tenant, domain));
3230
+ }
3231
+ if (action === "remove" || action === "unassign" || action === "delete") {
3232
+ if (!tenant || !domain) {
3233
+ return { exitCode: 1, error: "Usage: groundmail tenants domains remove <tenant> <domain>" };
3234
+ }
3235
+ return withClient(rest, env, (client2) => client2.tenants.domains.unassign(tenant, domain));
3236
+ }
3237
+ return {
3238
+ exitCode: 1,
3239
+ error: "Usage: groundmail tenants domains list|add|remove <tenant> [<domain>]"
3240
+ };
3241
+ }
3242
+
2978
3243
  // src/commands/auth.ts
2979
3244
  async function runAuthCommand(subcommand, argv, deps = {}) {
2980
3245
  const env = deps.env ?? process.env;
@@ -3121,7 +3386,7 @@ var KEY_ENV = {
3121
3386
  };
3122
3387
  var LABELS = { sendgrid: "SendGrid", postmark: "Postmark", mailgun: "Mailgun" };
3123
3388
  var VALUE_FLAGS2 = /* @__PURE__ */ new Set(["--api-key", "--base-url"]);
3124
- function readFlag21(args, flag) {
3389
+ function readFlag22(args, flag) {
3125
3390
  const index = args.indexOf(flag);
3126
3391
  if (index === -1) return void 0;
3127
3392
  return args[index + 1];
@@ -3190,10 +3455,10 @@ function formatMigrationSummary(m) {
3190
3455
  return lines.join("\n");
3191
3456
  }
3192
3457
  async function client(args, env) {
3193
- const apiKey = readFlag21(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3458
+ const apiKey = readFlag22(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3194
3459
  if (!apiKey) return null;
3195
3460
  const { Groundmail } = await import("groundmail");
3196
- const baseUrl = readFlag21(args, "--base-url");
3461
+ const baseUrl = readFlag22(args, "--base-url");
3197
3462
  return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
3198
3463
  }
3199
3464
  async function runStoredCommand(sub, args, env) {
@@ -3230,11 +3495,11 @@ async function runMigrateCommand(provider, args, env = process.env) {
3230
3495
  };
3231
3496
  }
3232
3497
  const id = provider;
3233
- const apiKey = readFlag21(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3498
+ const apiKey = readFlag22(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3234
3499
  if (!apiKey) {
3235
3500
  return { exitCode: 1, error: "Not signed in. Run `groundmail login`, or set GROUNDMAIL_API_KEY." };
3236
3501
  }
3237
- const providerKey = readFlag21(args, "--key") || env[KEY_ENV[id]];
3502
+ const providerKey = readFlag22(args, "--key") || env[KEY_ENV[id]];
3238
3503
  if (!providerKey) {
3239
3504
  return {
3240
3505
  exitCode: 1,
@@ -3243,14 +3508,14 @@ async function runMigrateCommand(provider, args, env = process.env) {
3243
3508
  }
3244
3509
  const params = { provider: id, api_key: providerKey };
3245
3510
  if (id === "postmark") {
3246
- const accountToken = readFlag21(args, "--account-token") || env.POSTMARK_ACCOUNT_TOKEN;
3511
+ const accountToken = readFlag22(args, "--account-token") || env.POSTMARK_ACCOUNT_TOKEN;
3247
3512
  if (accountToken) params.account_api_key = accountToken;
3248
3513
  } else {
3249
- const region = readFlag21(args, "--region");
3514
+ const region = readFlag22(args, "--region");
3250
3515
  if (region === "us" || region === "eu") params.region = region;
3251
3516
  else if (region) return { exitCode: 1, error: "--region must be us or eu" };
3252
3517
  }
3253
- const only = readFlag21(args, "--only");
3518
+ const only = readFlag22(args, "--only");
3254
3519
  if (only) {
3255
3520
  const list = only.split(",").map((s) => s.trim()).filter(Boolean);
3256
3521
  const bad = list.filter((r) => !RESOURCES.includes(r));
@@ -3262,7 +3527,7 @@ async function runMigrateCommand(provider, args, env = process.env) {
3262
3527
  if (args.includes("--dry-run")) params.dry_run = true;
3263
3528
  try {
3264
3529
  const { Groundmail } = await import("groundmail");
3265
- const baseUrl = readFlag21(args, "--base-url");
3530
+ const baseUrl = readFlag22(args, "--base-url");
3266
3531
  const client2 = new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
3267
3532
  console.error(`${params.dry_run ? "Previewing" : "Importing"} from ${LABELS[id]} (Beta)\u2026`);
3268
3533
  const report = await client2.migrations.run(params, {
@@ -3285,12 +3550,12 @@ async function runMigrateCommand(provider, args, env = process.env) {
3285
3550
  // src/commands/validate.ts
3286
3551
  async function runValidateCommand(args, env = process.env) {
3287
3552
  const options = parseSuppressionsCommonArgs(args);
3288
- const apiKey = resolveApiKey6(options, env);
3553
+ const apiKey = resolveApiKey7(options, env);
3289
3554
  if (!apiKey) {
3290
3555
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY, pass --api-key, or run `groundmail login`." };
3291
3556
  }
3292
3557
  const flagValues = new Set([options.apiKey, options.baseUrl].filter(Boolean));
3293
- const email = readFlag18(args, "--email") || args.find((arg) => arg.includes("@") && !arg.startsWith("-") && !flagValues.has(arg));
3558
+ const email = readFlag19(args, "--email") || args.find((arg) => arg.includes("@") && !arg.startsWith("-") && !flagValues.has(arg));
3294
3559
  if (!email) {
3295
3560
  return { exitCode: 1, error: "Missing email. Usage: groundmail validate user@example.com" };
3296
3561
  }
@@ -3304,6 +3569,109 @@ async function runValidateCommand(args, env = process.env) {
3304
3569
  }
3305
3570
  }
3306
3571
 
3572
+ // src/commands/dmarc/dmarc.ts
3573
+ function readFlag23(args, flag) {
3574
+ const index = args.indexOf(flag);
3575
+ if (index === -1) return void 0;
3576
+ return args[index + 1];
3577
+ }
3578
+ var VALUE_FLAGS3 = ["--api-key", "--base-url", "--domain", "--apply"];
3579
+ function readFirstPositional15(args) {
3580
+ for (let i = 0; i < args.length; i += 1) {
3581
+ const arg = args[i];
3582
+ if (arg.startsWith("--")) {
3583
+ if (VALUE_FLAGS3.includes(arg)) i += 1;
3584
+ continue;
3585
+ }
3586
+ return arg;
3587
+ }
3588
+ return void 0;
3589
+ }
3590
+ async function createDmarcClient(apiKey, baseUrl) {
3591
+ const { Groundmail } = await import("groundmail");
3592
+ return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
3593
+ }
3594
+ function pct(rate) {
3595
+ const value = rate * 100;
3596
+ return value === 100 ? "100%" : `${value.toFixed(1).replace(/\.0$/, "")}%`;
3597
+ }
3598
+ function formatDmarcStatus(dmarc) {
3599
+ if (!dmarc.enabled) {
3600
+ return [
3601
+ `DMARC monitoring for ${dmarc.domain}: off`,
3602
+ ` Current record (${dmarc.dns.host}): ${dmarc.dns.record ?? "none"}`,
3603
+ "",
3604
+ `Turn it on: groundmail dmarc enable ${dmarc.domain}`
3605
+ ].join("\n");
3606
+ }
3607
+ const lines = [
3608
+ `DMARC monitoring for ${dmarc.domain}: ${dmarc.status === "waiting" ? "waiting for the first report" : "receiving reports"}`,
3609
+ ` Report address: ${dmarc.report_address}`
3610
+ ];
3611
+ if (!dmarc.dns.rua_present) {
3612
+ lines.push("", ` Add the address to your DMARC record. TXT ${dmarc.dns.host}:`, ` ${dmarc.dns.suggested_record}`);
3613
+ }
3614
+ lines.push(
3615
+ "",
3616
+ ` Last ${dmarc.window_days} days: ${dmarc.summary.messages.toLocaleString("en-US")} messages, ${pct(dmarc.summary.pass_rate)} passed DMARC, ${dmarc.summary.reports} reports`,
3617
+ ` Readiness: ${dmarc.readiness.label}. ${dmarc.readiness.detail}`
3618
+ );
3619
+ if (dmarc.sources.length) {
3620
+ lines.push("", " Sources:");
3621
+ for (const source of dmarc.sources.slice(0, 15)) {
3622
+ lines.push(
3623
+ ` ${source.name.padEnd(26)} ${source.status.padEnd(8)} ${String(source.messages).padStart(9)} msgs ${pct(source.pass_rate).padStart(6)} pass`
3624
+ );
3625
+ }
3626
+ }
3627
+ return lines.join("\n");
3628
+ }
3629
+ async function runDmarcCommand(subcommand, args, env = process.env) {
3630
+ const apiKey = readFlag23(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3631
+ if (!apiKey) return { exitCode: 1, error: "Missing API key. Run groundmail login, set GROUNDMAIL_API_KEY or pass --api-key." };
3632
+ const domain = readFlag23(args, "--domain") || readFirstPositional15(args);
3633
+ if (!subcommand || !["status", "enable", "disable", "digest"].includes(subcommand)) {
3634
+ return { exitCode: 1, error: "Usage: groundmail dmarc status|enable|disable|digest <domain>" };
3635
+ }
3636
+ if (!domain) return { exitCode: 1, error: `Missing domain. Usage: groundmail dmarc ${subcommand} example.com` };
3637
+ const json = args.includes("--json");
3638
+ try {
3639
+ const client2 = await createDmarcClient(apiKey, readFlag23(args, "--base-url"));
3640
+ if (subcommand === "status") {
3641
+ const result2 = await client2.dmarc.get(domain);
3642
+ console.log(json ? JSON.stringify(result2) : formatDmarcStatus(result2.dmarc));
3643
+ return { exitCode: 0 };
3644
+ }
3645
+ if (subcommand === "enable") {
3646
+ const apply = readFlag23(args, "--apply");
3647
+ if (apply && apply !== "cloudflare" && apply !== "vercel" && apply !== "godaddy") {
3648
+ return { exitCode: 1, error: "--apply must be cloudflare, vercel or godaddy" };
3649
+ }
3650
+ const result2 = await client2.dmarc.enable(domain, apply ? { apply } : {});
3651
+ if (json) console.log(JSON.stringify(result2));
3652
+ else {
3653
+ console.log(formatDmarcStatus(result2.dmarc));
3654
+ if (result2.applied) console.log(`
3655
+ Updated the DMARC record with ${result2.applied.provider}.`);
3656
+ }
3657
+ if (result2.apply_error) return { exitCode: 1, error: result2.apply_error };
3658
+ return { exitCode: 0 };
3659
+ }
3660
+ if (subcommand === "disable") {
3661
+ const result2 = await client2.dmarc.disable(domain);
3662
+ console.log(json ? JSON.stringify(result2) : `DMARC monitoring for ${domain} is off. Remove the report address from your DMARC record's rua= tag too.`);
3663
+ return { exitCode: 0 };
3664
+ }
3665
+ const value = args.includes("--off") ? false : args.includes("--on") ? true : void 0;
3666
+ if (value === void 0) return { exitCode: 1, error: "Usage: groundmail dmarc digest example.com --on|--off" };
3667
+ const result = await client2.dmarc.update(domain, { digest_enabled: value });
3668
+ console.log(json ? JSON.stringify(result) : `Weekly DMARC digest for ${domain} is ${value ? "on" : "off"}.`);
3669
+ return { exitCode: 0 };
3670
+ } catch (error) {
3671
+ return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
3672
+ }
3673
+ }
3674
+
3307
3675
  // src/help.ts
3308
3676
  var USAGE_LINES = [
3309
3677
  " groundmail login [--scope <scopes>] [--workspace <name>] sign in through the browser (OAuth; no key to copy)",
@@ -3312,20 +3680,24 @@ var USAGE_LINES = [
3312
3680
  " groundmail auth token [--scope <scopes>] print a short-lived access token (for agents and scripts)",
3313
3681
  " groundmail workspaces list",
3314
3682
  " groundmail workspaces use <name|id>",
3315
- " groundmail domains add example.com",
3683
+ " groundmail domains add example.com [--tenant <tenant>]",
3316
3684
  " groundmail domains apply example.com --provider cloudflare",
3317
3685
  " groundmail domains apply example.com --provider vercel",
3318
3686
  " groundmail domains apply example.com --provider godaddy",
3319
- " groundmail domains list",
3687
+ " groundmail domains list [--tenant <tenant>]",
3320
3688
  " groundmail domains get example.com",
3321
3689
  " groundmail domains update example.com --inbound true",
3322
3690
  " groundmail domains verify example.com",
3323
3691
  " groundmail domains delete example.com",
3692
+ " groundmail dmarc status example.com [--json] DMARC monitoring: sources, pass rates, readiness",
3693
+ " groundmail dmarc enable example.com [--apply cloudflare|vercel|godaddy] report address and the record to publish",
3694
+ " groundmail dmarc digest example.com --on|--off weekly digest email",
3695
+ " groundmail dmarc disable example.com",
3324
3696
  " groundmail received-emails list [--domain inbound.example.com]",
3325
3697
  " groundmail received-emails get inb_...",
3326
- ' groundmail emails send --from you@domain.com --to user@example.com --subject "Hello" --html "<p>Hi</p>" [--category transactional|product] [--unsubscribe]',
3327
- " groundmail emails batch --file ./emails.json",
3328
- " groundmail emails list [--limit 25] [--cursor \u2026]",
3698
+ ' groundmail emails send --from you@domain.com --to user@example.com --subject "Hello" --html "<p>Hi</p>" [--category transactional|product] [--unsubscribe] [--tenant <tenant>]',
3699
+ " groundmail emails batch --file ./emails.json [--tenant <tenant>]",
3700
+ " groundmail emails list [--limit 25] [--cursor \u2026] [--test | --live] [--tenant <tenant>]",
3329
3701
  " groundmail emails insights [--window 7d|30d]",
3330
3702
  " groundmail emails get --id msg_\u2026",
3331
3703
  " groundmail emails cancel --id msg_\u2026",
@@ -3341,14 +3713,14 @@ var USAGE_LINES = [
3341
3713
  " groundmail automations pause --id <automation-id>",
3342
3714
  " groundmail automations delete --id <automation-id>",
3343
3715
  " groundmail webhooks list",
3344
- " groundmail webhooks create --url https://yourapp.com/hooks",
3716
+ " groundmail webhooks create --url https://yourapp.com/hooks [--tenants a,b] --tenants: only these tenants' email events",
3345
3717
  " groundmail webhooks get --id wh_\u2026",
3346
- " groundmail webhooks update --id wh_\u2026 [--url \u2026] [--event email.sent] [--enabled true|false]",
3718
+ " groundmail webhooks update --id wh_\u2026 [--url \u2026] [--event email.sent] [--enabled true|false] [--tenants a,b|all]",
3347
3719
  " groundmail webhooks delete --id <webhook-id>",
3348
3720
  " groundmail validate user@example.com Groundmail validation: verdict, reasons, typo suggestion (exit 2 when invalid)",
3349
- " groundmail suppressions list",
3350
- " groundmail suppressions add --email user@example.com [--reason \u2026]",
3351
- " groundmail suppressions remove --id <id> | --email user@example.com",
3721
+ " groundmail suppressions list [--tenant <tenant>]",
3722
+ " groundmail suppressions add --email user@example.com [--reason \u2026] [--tenant <tenant>]",
3723
+ " groundmail suppressions remove --id <id> | --email user@example.com [--tenant <tenant>]",
3352
3724
  " groundmail templates list [--status draft|published]",
3353
3725
  " groundmail templates starters",
3354
3726
  " groundmail templates from-starter password-reset",
@@ -3362,8 +3734,19 @@ var USAGE_LINES = [
3362
3734
  " groundmail templates test-send --id tpl_\u2026 --from you@domain.com --to you@domain.com",
3363
3735
  ' groundmail templates duplicate --id tpl_\u2026 [--name "Copy"]',
3364
3736
  " groundmail templates delete --id tpl_\u2026",
3737
+ " groundmail tenants list [--status active|paused] [--limit 25] [--cursor \u2026] your customers, each with its own domains, suppressions and limits",
3738
+ ` groundmail tenants create <external-id> [--name \u2026] [--default-from-domain \u2026] [--stream-policy all|transactional_only] [--daily-limit N] [--metadata '{"plan":"pro"}']`,
3739
+ " groundmail tenants get <tenant>",
3740
+ " groundmail tenants update <tenant> [--name \u2026] [--default-from-domain \u2026|none] [--stream-policy \u2026] [--daily-limit N|none] [--metadata JSON|null]",
3741
+ " groundmail tenants pause <tenant> [--reason \u2026] stops the tenant's new sends",
3742
+ " groundmail tenants resume <tenant>",
3743
+ " groundmail tenants delete <tenant> --yes",
3744
+ " groundmail tenants stats <tenant> [--days 7]",
3745
+ " groundmail tenants domains list <tenant>",
3746
+ " groundmail tenants domains add <tenant> example.com only this tenant can then send from it",
3747
+ " groundmail tenants domains remove <tenant> example.com gives the domain back to the account",
3365
3748
  " groundmail keys list",
3366
- " groundmail keys create [--name Default] [--scopes emails:send,domains:read] [--allowed-domains example.com]",
3749
+ " groundmail keys create [--name Default] [--scopes emails:send,domains:read] [--allowed-domains example.com] [--test] [--tenant <tenant>] --test: a gm_test_ key, sends are simulated; --tenant: a key limited to one tenant",
3367
3750
  " groundmail keys delete --id <api-key-id>",
3368
3751
  " groundmail migrate sendgrid [--dry-run] [--only templates,suppressions,domains,webhooks] [--region eu] import from SendGrid (Beta)",
3369
3752
  " groundmail migrate postmark [--dry-run] [--only \u2026] import from Postmark (Beta)",
@@ -3387,6 +3770,8 @@ var FOOTER_LINES = [
3387
3770
  "Scopes: full, emails:send, emails:read, domains:read, domains:write, webhooks:write, templates:write,",
3388
3771
  " suppressions:write, account:read",
3389
3772
  "",
3773
+ "<tenant>: the tenant's external id (your id for that customer) or its Groundmail id.",
3774
+ "",
3390
3775
  "Add --help to any command for just its usage. --version prints the CLI version."
3391
3776
  ];
3392
3777
  var CLI_VERSION = version;
@@ -3396,7 +3781,8 @@ var ALIASES = {
3396
3781
  events: { trigger: "send" },
3397
3782
  automations: { add: "create", remove: "delete" },
3398
3783
  suppressions: { create: "add", delete: "remove" },
3399
- templates: { add: "create", remove: "delete" }
3784
+ templates: { add: "create", remove: "delete" },
3785
+ tenants: { add: "create", remove: "delete" }
3400
3786
  };
3401
3787
  function words(line) {
3402
3788
  return line.trim().split(/\s+/);
@@ -3460,6 +3846,7 @@ async function main() {
3460
3846
  const rest = session.argv;
3461
3847
  if (command === "migrate") await finish(await runMigrateCommand(subcommand, rest));
3462
3848
  if (command === "validate") await finish(await runValidateCommand([...subcommand ? [subcommand] : [], ...rest]));
3849
+ if (command === "dmarc") await finish(await runDmarcCommand(subcommand, rest));
3463
3850
  if (command === "domains") {
3464
3851
  if (subcommand === "add" || subcommand === "create") {
3465
3852
  const result = await runAddCommand(rest);
@@ -3721,6 +4108,17 @@ async function main() {
3721
4108
  }
3722
4109
  }
3723
4110
  }
4111
+ if (command === "tenants") {
4112
+ if (subcommand === "list") await finish(await runListCommand8(rest));
4113
+ if (subcommand === "create" || subcommand === "add") await finish(await runCreateCommand4(rest));
4114
+ if (subcommand === "get") await finish(await runGetCommand7(rest));
4115
+ if (subcommand === "update") await finish(await runUpdateCommand5(rest));
4116
+ if (subcommand === "pause") await finish(await runPauseCommand2(rest));
4117
+ if (subcommand === "resume") await finish(await runResumeCommand(rest));
4118
+ if (subcommand === "delete" || subcommand === "remove") await finish(await runDeleteCommand5(rest));
4119
+ if (subcommand === "stats") await finish(await runStatsCommand(rest));
4120
+ if (subcommand === "domains") await finish(await runDomainsCommand(rest));
4121
+ }
3724
4122
  if (command === "keys") {
3725
4123
  if (subcommand === "list") {
3726
4124
  const result = await runListKeysCommandWithClient(rest);