groundmail-cli 0.20.0 → 0.23.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 +563 -158
  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,7 +67,8 @@ 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));
72
74
  if (result.test) {
@@ -104,6 +106,7 @@ function parseSendCommandOptions(args, env) {
104
106
  scheduledAt: parsed.scheduledAt,
105
107
  category: parsed.category,
106
108
  unsubscribe: parsed.unsubscribe,
109
+ tenant: parsed.tenant,
107
110
  apiKey,
108
111
  baseUrl: parsed.baseUrl
109
112
  };
@@ -138,7 +141,7 @@ function readFirstPositional(args) {
138
141
  for (let index = 0; index < args.length; index += 1) {
139
142
  const arg = args[index];
140
143
  if (arg.startsWith("--")) {
141
- 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;
142
145
  continue;
143
146
  }
144
147
  return arg;
@@ -167,7 +170,9 @@ async function runBatchCommand(args, deps) {
167
170
  stderr(payload.error);
168
171
  return { exitCode: 1, error: payload.error };
169
172
  }
170
- 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);
171
176
  stdout(JSON.stringify(result));
172
177
  return { exitCode: 0 };
173
178
  } catch (error) {
@@ -397,11 +402,13 @@ async function runListCommand(args, deps) {
397
402
  return { exitCode: 1, error: "conflicting flags" };
398
403
  }
399
404
  const test = args.includes("--test") ? true : args.includes("--live") ? false : void 0;
405
+ const tenant = readFlag7(args, "--tenant");
400
406
  try {
401
407
  const result = await deps.listEmails({
402
408
  ...limit != null && Number.isFinite(limit) ? { limit } : {},
403
409
  ...cursor ? { cursor } : {},
404
- ...test !== void 0 ? { test } : {}
410
+ ...test !== void 0 ? { test } : {},
411
+ ...tenant ? { tenant } : {}
405
412
  });
406
413
  stdout(JSON.stringify(result));
407
414
  return { exitCode: 0 };
@@ -708,7 +715,8 @@ function parseDomainsCommonArgs(args) {
708
715
  apiKey: readFlag10(args, "--api-key"),
709
716
  baseUrl: readFlag10(args, "--base-url"),
710
717
  name: readFlag10(args, "--name") || readFlag10(args, "--domain"),
711
- id: readFlag10(args, "--id")
718
+ id: readFlag10(args, "--id"),
719
+ tenant: readFlag10(args, "--tenant")
712
720
  };
713
721
  }
714
722
  function resolveApiKey3(options, env) {
@@ -741,7 +749,7 @@ async function runAddCommand(args, env = process.env) {
741
749
  if (!apiKey) {
742
750
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
743
751
  }
744
- 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");
745
753
  if (!name) {
746
754
  return {
747
755
  exitCode: 1,
@@ -753,11 +761,14 @@ async function runAddCommand(args, env = process.env) {
753
761
  const inbound = readFlag10(args, "--inbound");
754
762
  const result = await client2.domains.create({
755
763
  name,
756
- ...inbound === "true" || inbound === "1" ? { inbound_enabled: true } : {}
764
+ ...inbound === "true" || inbound === "1" ? { inbound_enabled: true } : {},
765
+ ...options.tenant ? { tenant: options.tenant } : {}
757
766
  });
758
767
  console.log(JSON.stringify(result));
759
768
  console.error("");
760
- 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
+ );
761
772
  console.error(formatDnsRecords(result.records));
762
773
  console.error("");
763
774
  console.error(`Then: groundmail domains verify ${result.domain.name}`);
@@ -892,7 +903,7 @@ async function runListCommand3(args, env = process.env) {
892
903
  }
893
904
  try {
894
905
  const client2 = await createDomainsClient(apiKey, options.baseUrl);
895
- const result = await client2.domains.list();
906
+ const result = await client2.domains.list(options.tenant ? { tenant: options.tenant } : {});
896
907
  console.log(JSON.stringify(result));
897
908
  return { exitCode: 0 };
898
909
  } catch (error) {
@@ -916,7 +927,9 @@ var POSITIONAL_FLAGS3 = [
916
927
  "--tls-mode",
917
928
  "--open-tracking",
918
929
  "--click-tracking",
919
- "--inbound"
930
+ "--inbound",
931
+ "--inbound-spam-threshold",
932
+ "--inbound-spam-action"
920
933
  ];
921
934
  function readFirstPositional4(args) {
922
935
  for (let i = 0; i < args.length; i += 1) {
@@ -957,10 +970,29 @@ async function runUpdateCommand2(args, env = process.env) {
957
970
  if (clickTracking !== void 0) payload.click_tracking = clickTracking;
958
971
  const inbound = parseBooleanFlag(readFlag10(args, "--inbound"), "--inbound");
959
972
  if (inbound !== void 0) payload.inbound_enabled = inbound;
973
+ const spamThreshold = readFlag10(args, "--inbound-spam-threshold");
974
+ if (spamThreshold != null) {
975
+ if (spamThreshold === "default" || spamThreshold === "null") {
976
+ payload.inbound_spam_threshold = null;
977
+ } else {
978
+ const value = Number(spamThreshold);
979
+ if (!Number.isFinite(value) || value < 1 || value > 100) {
980
+ return { exitCode: 1, error: "--inbound-spam-threshold must be a number from 1 to 100, or default" };
981
+ }
982
+ payload.inbound_spam_threshold = value;
983
+ }
984
+ }
985
+ const spamAction = readFlag10(args, "--inbound-spam-action");
986
+ if (spamAction != null) {
987
+ if (spamAction !== "flag" && spamAction !== "drop") {
988
+ return { exitCode: 1, error: "--inbound-spam-action must be flag or drop" };
989
+ }
990
+ payload.inbound_spam_action = spamAction;
991
+ }
960
992
  if (!Object.keys(payload).length) {
961
993
  return {
962
994
  exitCode: 1,
963
- error: "No updates provided. Pass --inbound true|false, --tls-mode opportunistic|enforced, --open-tracking true|false, or --click-tracking true|false."
995
+ error: "No updates provided. Pass --inbound true|false, --tls-mode opportunistic|enforced, --open-tracking true|false, --click-tracking true|false, --inbound-spam-threshold <1-100|default>, or --inbound-spam-action flag|drop."
964
996
  };
965
997
  }
966
998
  const client2 = await createDomainsClient(apiKey, options.baseUrl);
@@ -1075,13 +1107,46 @@ async function runListCommand4(args, env = process.env) {
1075
1107
  }
1076
1108
  }
1077
1109
 
1078
- // src/commands/webhooks/shared.ts
1110
+ // src/commands/tenants/shared.ts
1111
+ var TENANT_VALUE_FLAGS = [
1112
+ "--api-key",
1113
+ "--base-url",
1114
+ "--tenant",
1115
+ "--id",
1116
+ "--external-id",
1117
+ "--name",
1118
+ "--default-from-domain",
1119
+ "--stream-policy",
1120
+ "--daily-limit",
1121
+ "--metadata",
1122
+ "--status",
1123
+ "--reason",
1124
+ "--limit",
1125
+ "--cursor",
1126
+ "--days",
1127
+ "--domain"
1128
+ ];
1079
1129
  function readFlag12(args, flag) {
1080
1130
  const index = args.indexOf(flag);
1081
1131
  if (index === -1) return void 0;
1082
1132
  return args[index + 1];
1083
1133
  }
1084
- function parseWebhooksCommonArgs(args) {
1134
+ function hasFlag(args, flag) {
1135
+ return args.includes(flag);
1136
+ }
1137
+ function positionals(args, valueFlags = TENANT_VALUE_FLAGS) {
1138
+ const out = [];
1139
+ for (let i = 0; i < args.length; i += 1) {
1140
+ const arg = args[i];
1141
+ if (arg.startsWith("--")) {
1142
+ if (valueFlags.includes(arg)) i += 1;
1143
+ continue;
1144
+ }
1145
+ out.push(arg);
1146
+ }
1147
+ return out;
1148
+ }
1149
+ function parseTenantsCommonArgs(args) {
1085
1150
  return {
1086
1151
  apiKey: readFlag12(args, "--api-key"),
1087
1152
  baseUrl: readFlag12(args, "--base-url")
@@ -1090,6 +1155,82 @@ function parseWebhooksCommonArgs(args) {
1090
1155
  function resolveApiKey5(options, env) {
1091
1156
  return options.apiKey || env.GROUNDMAIL_API_KEY || null;
1092
1157
  }
1158
+ async function createTenantsClient(apiKey, baseUrl) {
1159
+ const { Groundmail } = await import("groundmail");
1160
+ return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
1161
+ }
1162
+ var MISSING_KEY = "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key.";
1163
+ function failure(error) {
1164
+ return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
1165
+ }
1166
+ function parseDailyLimit(value) {
1167
+ if (value === void 0) return {};
1168
+ if (value === "none" || value === "null") return { value: null };
1169
+ const n2 = Number(value);
1170
+ if (!Number.isInteger(n2) || n2 < 0) return { error: "--daily-limit must be a whole number, or none" };
1171
+ return { value: n2 };
1172
+ }
1173
+ function parseMetadata(value) {
1174
+ if (value === void 0) return {};
1175
+ if (value === "null") return { value: null };
1176
+ try {
1177
+ const parsed = JSON.parse(value);
1178
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1179
+ return { error: "--metadata must be a JSON object" };
1180
+ }
1181
+ return { value: parsed };
1182
+ } catch {
1183
+ return { error: "--metadata must be valid JSON" };
1184
+ }
1185
+ }
1186
+ function parseStreamPolicy(value) {
1187
+ if (value === void 0) return {};
1188
+ if (value === "all" || value === "transactional_only") return { value };
1189
+ return { error: "--stream-policy must be all or transactional_only" };
1190
+ }
1191
+ function parseTenantSettings(args) {
1192
+ const dailyLimit = parseDailyLimit(readFlag12(args, "--daily-limit"));
1193
+ if ("error" in dailyLimit) return dailyLimit;
1194
+ const metadata = parseMetadata(readFlag12(args, "--metadata"));
1195
+ if ("error" in metadata) return metadata;
1196
+ const streamPolicy = parseStreamPolicy(readFlag12(args, "--stream-policy"));
1197
+ if ("error" in streamPolicy) return streamPolicy;
1198
+ const name = readFlag12(args, "--name");
1199
+ const fromDomain = readFlag12(args, "--default-from-domain");
1200
+ return {
1201
+ value: {
1202
+ ...name !== void 0 ? { name } : {},
1203
+ ...fromDomain !== void 0 ? { default_from_domain: fromDomain === "none" ? null : fromDomain } : {},
1204
+ ...streamPolicy.value !== void 0 ? { stream_policy: streamPolicy.value } : {},
1205
+ ..."value" in dailyLimit ? { daily_limit: dailyLimit.value } : {},
1206
+ ..."value" in metadata ? { metadata: metadata.value } : {}
1207
+ }
1208
+ };
1209
+ }
1210
+ function tenantRef(args) {
1211
+ return readFlag12(args, "--tenant") || readFlag12(args, "--id") || positionals(args)[0];
1212
+ }
1213
+ function parseTenantsList(value) {
1214
+ if (value === void 0) return void 0;
1215
+ if (value === "all" || value === "none" || value === "") return null;
1216
+ return value.split(/[\s,]+/).map((v) => v.trim()).filter(Boolean);
1217
+ }
1218
+
1219
+ // src/commands/webhooks/shared.ts
1220
+ function readFlag13(args, flag) {
1221
+ const index = args.indexOf(flag);
1222
+ if (index === -1) return void 0;
1223
+ return args[index + 1];
1224
+ }
1225
+ function parseWebhooksCommonArgs(args) {
1226
+ return {
1227
+ apiKey: readFlag13(args, "--api-key"),
1228
+ baseUrl: readFlag13(args, "--base-url")
1229
+ };
1230
+ }
1231
+ function resolveApiKey6(options, env) {
1232
+ return options.apiKey || env.GROUNDMAIL_API_KEY || null;
1233
+ }
1093
1234
  async function createWebhooksClient(apiKey, baseUrl) {
1094
1235
  const { Groundmail } = await import("groundmail");
1095
1236
  return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
@@ -1098,17 +1239,18 @@ async function createWebhooksClient(apiKey, baseUrl) {
1098
1239
  // src/commands/webhooks/create.ts
1099
1240
  async function runCreateCommand2(args, env = process.env) {
1100
1241
  const options = parseWebhooksCommonArgs(args);
1101
- const apiKey = resolveApiKey5(options, env);
1242
+ const apiKey = resolveApiKey6(options, env);
1102
1243
  if (!apiKey) {
1103
1244
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1104
1245
  }
1105
- const url = readFlag12(args, "--url");
1246
+ const url = readFlag13(args, "--url");
1106
1247
  if (!url) {
1107
1248
  return { exitCode: 1, error: "Required flag: --url" };
1108
1249
  }
1109
1250
  try {
1110
1251
  const client2 = await createWebhooksClient(apiKey, options.baseUrl);
1111
- const result = await client2.webhooks.create({ url });
1252
+ const tenants = parseTenantsList(readFlag13(args, "--tenants"));
1253
+ const result = await client2.webhooks.create({ url, ...tenants?.length ? { tenants } : {} });
1112
1254
  console.log(JSON.stringify(result));
1113
1255
  return { exitCode: 0 };
1114
1256
  } catch (error) {
@@ -1120,9 +1262,9 @@ async function runCreateCommand2(args, env = process.env) {
1120
1262
  var POSITIONAL_FLAGS5 = ["--api-key", "--base-url", "--id"];
1121
1263
  async function runGetCommand5(args, env = process.env) {
1122
1264
  const options = parseWebhooksCommonArgs(args);
1123
- const apiKey = resolveApiKey5(options, env);
1265
+ const apiKey = resolveApiKey6(options, env);
1124
1266
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1125
- const id = readFlag13(args, "--id") || readFirstPositional6(args);
1267
+ const id = readFlag14(args, "--id") || readFirstPositional6(args);
1126
1268
  if (!id) return { exitCode: 1, error: "Missing webhook id. Pass --id wh_\u2026" };
1127
1269
  try {
1128
1270
  const client2 = await createWebhooksClient(apiKey, options.baseUrl);
@@ -1132,7 +1274,7 @@ async function runGetCommand5(args, env = process.env) {
1132
1274
  return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
1133
1275
  }
1134
1276
  }
1135
- function readFlag13(args, flag) {
1277
+ function readFlag14(args, flag) {
1136
1278
  const index = args.indexOf(flag);
1137
1279
  return index === -1 ? void 0 : args[index + 1];
1138
1280
  }
@@ -1151,11 +1293,11 @@ function readFirstPositional6(args) {
1151
1293
  // src/commands/webhooks/delete.ts
1152
1294
  async function runDeleteCommand3(args, env = process.env) {
1153
1295
  const options = parseWebhooksCommonArgs(args);
1154
- const apiKey = resolveApiKey5(options, env);
1296
+ const apiKey = resolveApiKey6(options, env);
1155
1297
  if (!apiKey) {
1156
1298
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1157
1299
  }
1158
- const id = readFlag12(args, "--id");
1300
+ const id = readFlag13(args, "--id");
1159
1301
  if (!id) {
1160
1302
  return { exitCode: 1, error: "Required flag: --id" };
1161
1303
  }
@@ -1172,7 +1314,7 @@ async function runDeleteCommand3(args, env = process.env) {
1172
1314
  // src/commands/webhooks/list.ts
1173
1315
  async function runListCommand5(args, env = process.env) {
1174
1316
  const options = parseWebhooksCommonArgs(args);
1175
- const apiKey = resolveApiKey5(options, env);
1317
+ const apiKey = resolveApiKey6(options, env);
1176
1318
  if (!apiKey) {
1177
1319
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1178
1320
  }
@@ -1187,24 +1329,26 @@ async function runListCommand5(args, env = process.env) {
1187
1329
  }
1188
1330
 
1189
1331
  // src/commands/webhooks/update.ts
1190
- var POSITIONAL_FLAGS6 = ["--api-key", "--base-url", "--id", "--url", "--event", "--enabled"];
1332
+ var POSITIONAL_FLAGS6 = ["--api-key", "--base-url", "--id", "--url", "--event", "--enabled", "--tenants"];
1191
1333
  async function runUpdateCommand3(args, env = process.env) {
1192
1334
  const options = parseWebhooksCommonArgs(args);
1193
- const apiKey = resolveApiKey5(options, env);
1335
+ const apiKey = resolveApiKey6(options, env);
1194
1336
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1195
- const id = readFlag14(args, "--id") || readFirstPositional7(args);
1337
+ const id = readFlag15(args, "--id") || readFirstPositional7(args);
1196
1338
  if (!id) return { exitCode: 1, error: "Missing webhook id. Pass --id wh_\u2026" };
1197
- const url = readFlag14(args, "--url");
1339
+ const url = readFlag15(args, "--url");
1198
1340
  const events = readFlags2(args, "--event");
1199
- const enabled = parseEnabledFlag(readFlag14(args, "--enabled"));
1341
+ const enabled = parseEnabledFlag(readFlag15(args, "--enabled"));
1200
1342
  if ("error" in enabled) return { exitCode: 1, error: enabled.error };
1201
- if (!url && events.length === 0 && enabled.value === void 0) {
1202
- return { exitCode: 1, error: "Nothing to update. Pass --url, --event, or --enabled true|false." };
1343
+ const tenants = parseTenantsList(readFlag15(args, "--tenants"));
1344
+ if (!url && events.length === 0 && enabled.value === void 0 && tenants === void 0) {
1345
+ return { exitCode: 1, error: "Nothing to update. Pass --url, --event, --enabled true|false, or --tenants." };
1203
1346
  }
1204
1347
  const payload = {
1205
1348
  ...url ? { url } : {},
1206
1349
  ...events.length > 0 ? { events } : {},
1207
- ...enabled.value !== void 0 ? { enabled: enabled.value } : {}
1350
+ ...enabled.value !== void 0 ? { enabled: enabled.value } : {},
1351
+ ...tenants !== void 0 ? { tenants } : {}
1208
1352
  };
1209
1353
  try {
1210
1354
  const client2 = await createWebhooksClient(apiKey, options.baseUrl);
@@ -1214,7 +1358,7 @@ async function runUpdateCommand3(args, env = process.env) {
1214
1358
  return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
1215
1359
  }
1216
1360
  }
1217
- function readFlag14(args, flag) {
1361
+ function readFlag15(args, flag) {
1218
1362
  const index = args.indexOf(flag);
1219
1363
  return index === -1 ? void 0 : args[index + 1];
1220
1364
  }
@@ -1244,22 +1388,22 @@ function parseEnabledFlag(value) {
1244
1388
  }
1245
1389
 
1246
1390
  // src/commands/keys/list.ts
1247
- function readFlag15(args, flag) {
1391
+ function readFlag16(args, flag) {
1248
1392
  const index = args.indexOf(flag);
1249
1393
  if (index === -1) return void 0;
1250
1394
  return args[index + 1];
1251
1395
  }
1252
1396
  async function runListKeysCommandWithClient(args, env = process.env) {
1253
- const apiKey = readFlag15(args, "--api-key") || env.GROUNDMAIL_API_KEY;
1254
- const baseUrl = readFlag15(args, "--base-url");
1397
+ const apiKey = readFlag16(args, "--api-key") || env.GROUNDMAIL_API_KEY;
1398
+ const baseUrl = readFlag16(args, "--base-url");
1255
1399
  if (!apiKey) {
1256
1400
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
1257
1401
  }
1258
1402
  const { Groundmail } = await import("groundmail");
1259
1403
  const client2 = new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
1260
1404
  try {
1261
- const limitRaw = readFlag15(args, "--limit");
1262
- const cursor = readFlag15(args, "--cursor");
1405
+ const limitRaw = readFlag16(args, "--limit");
1406
+ const cursor = readFlag16(args, "--cursor");
1263
1407
  const result = await client2.apiKeys.list({
1264
1408
  ...limitRaw ? { limit: Number(limitRaw) } : {},
1265
1409
  ...cursor ? { cursor } : {}
@@ -1297,7 +1441,7 @@ function trimUrl(value) {
1297
1441
  }
1298
1442
 
1299
1443
  // package.json
1300
- var version = "0.20.0";
1444
+ var version = "0.23.0";
1301
1445
 
1302
1446
  // src/user-agent.ts
1303
1447
  function buildUserAgent(own) {
@@ -1381,15 +1525,15 @@ function parseArgs(argv, spec) {
1381
1525
  const booleanFlags = new Set(spec.booleans ?? []);
1382
1526
  const values = {};
1383
1527
  const booleans = {};
1384
- const positionals = [];
1528
+ const positionals2 = [];
1385
1529
  for (let i = 0; i < argv.length; i++) {
1386
1530
  const arg = argv[i];
1387
1531
  if (arg === "--") {
1388
- positionals.push(...argv.slice(i + 1));
1532
+ positionals2.push(...argv.slice(i + 1));
1389
1533
  break;
1390
1534
  }
1391
1535
  if (!arg.startsWith("--") || arg === "-") {
1392
- positionals.push(arg);
1536
+ positionals2.push(arg);
1393
1537
  continue;
1394
1538
  }
1395
1539
  const eq = arg.indexOf("=");
@@ -1415,7 +1559,7 @@ function parseArgs(argv, spec) {
1415
1559
  const known = [...valueFlags, ...booleanFlags].sort().join(", ");
1416
1560
  return { ok: false, error: `Unknown flag ${name}.${known ? ` This command takes: ${known}.` : ""}` };
1417
1561
  }
1418
- return { ok: true, args: { values, booleans, positionals } };
1562
+ return { ok: true, args: { values, booleans, positionals: positionals2 } };
1419
1563
  }
1420
1564
  function wantsJson(args, stdout = process.stdout) {
1421
1565
  if (args.booleans["--json"] !== void 0) return args.booleans["--json"];
@@ -1723,10 +1867,10 @@ async function resolveAuth(flags, env = process.env, config = loadConfig(env)) {
1723
1867
  return fromSaved(current.workspace);
1724
1868
  }
1725
1869
  async function applySavedSession(argv, env = process.env, config) {
1726
- const hasFlag = (flag) => argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
1870
+ const hasFlag2 = (flag) => argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
1727
1871
  let workspace = null;
1728
1872
  let notice;
1729
- if (!hasFlag("--api-key") && !envApiKey(env)) {
1873
+ if (!hasFlag2("--api-key") && !envApiKey(env)) {
1730
1874
  const current = currentSavedWorkspace(config ?? loadConfig(env), env);
1731
1875
  if (current.ok) {
1732
1876
  workspace = current.workspace;
@@ -1737,7 +1881,7 @@ async function applySavedSession(argv, env = process.env, config) {
1737
1881
  notice = current.error;
1738
1882
  }
1739
1883
  }
1740
- if (hasFlag("--base-url")) return { argv, notice };
1884
+ if (hasFlag2("--base-url")) return { argv, notice };
1741
1885
  const url = explicitApiUrl(env) || trimUrl(workspace?.apiUrl) || trimUrl(env.GROUNDMAIL_DEFAULT_API_URL);
1742
1886
  return { argv: url ? [...argv, "--base-url", url] : argv, notice };
1743
1887
  }
@@ -2134,28 +2278,29 @@ Usage: ${brand.bin} whoami [--workspace <name>] [--json]` };
2134
2278
  }
2135
2279
 
2136
2280
  // src/commands/keys/create.ts
2137
- function readFlag16(args, flag) {
2281
+ function readFlag17(args, flag) {
2138
2282
  const index = args.indexOf(flag);
2139
2283
  if (index === -1) return void 0;
2140
2284
  return args[index + 1];
2141
2285
  }
2142
2286
  async function runCreateKeyCommandWithClient(args, env = process.env) {
2143
- const apiKey = readFlag16(args, "--api-key") || env.GROUNDMAIL_API_KEY;
2144
- const baseUrl = readFlag16(args, "--base-url") || defaultApiUrl(env);
2287
+ const apiKey = readFlag17(args, "--api-key") || env.GROUNDMAIL_API_KEY;
2288
+ const baseUrl = readFlag17(args, "--base-url") || defaultApiUrl(env);
2145
2289
  if (!apiKey) {
2146
2290
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2147
2291
  }
2148
- const name = readFlag16(args, "--name");
2149
- const scope = readFlag16(args, "--scope");
2292
+ const name = readFlag17(args, "--name");
2293
+ const scope = readFlag17(args, "--scope");
2150
2294
  if (scope && scope !== "full" && scope !== "sending") {
2151
2295
  return { exitCode: 1, error: "--scope must be full or sending (or use --scopes for fine-grained scopes)" };
2152
2296
  }
2153
- const scopesFlag = readFlag16(args, "--scopes");
2297
+ const scopesFlag = readFlag17(args, "--scopes");
2154
2298
  const scopes = scopesFlag !== void 0 ? parseScopeFlag(scopesFlag) : null;
2155
2299
  if (scopes && !scopes.ok) return { exitCode: 1, error: scopes.error };
2156
- const domainsFlag = readFlag16(args, "--allowed-domains");
2300
+ const domainsFlag = readFlag17(args, "--allowed-domains");
2157
2301
  const allowedDomains = domainsFlag?.split(/[\s,]+/).map((domain) => domain.trim()).filter(Boolean);
2158
2302
  const test = args.includes("--test");
2303
+ const tenant = readFlag17(args, "--tenant");
2159
2304
  try {
2160
2305
  const response = await apiRequest(baseUrl, "/api-keys", {
2161
2306
  apiKey,
@@ -2165,7 +2310,8 @@ async function runCreateKeyCommandWithClient(args, env = process.env) {
2165
2310
  ...scope && !scopes ? { scope } : {},
2166
2311
  ...scopes?.ok ? { scopes: scopes.scope.split(" ") } : {},
2167
2312
  ...allowedDomains?.length ? { allowed_domains: allowedDomains } : {},
2168
- ...test ? { test: true } : {}
2313
+ ...test ? { test: true } : {},
2314
+ ...tenant ? { tenant } : {}
2169
2315
  }
2170
2316
  });
2171
2317
  if (!response.ok) {
@@ -2182,15 +2328,15 @@ async function runCreateKeyCommandWithClient(args, env = process.env) {
2182
2328
  }
2183
2329
 
2184
2330
  // src/commands/keys/delete.ts
2185
- function readFlag17(args, flag) {
2331
+ function readFlag18(args, flag) {
2186
2332
  const index = args.indexOf(flag);
2187
2333
  if (index === -1) return void 0;
2188
2334
  return args[index + 1];
2189
2335
  }
2190
2336
  async function runDeleteKeyCommandWithClient(args, env = process.env) {
2191
- const apiKey = readFlag17(args, "--api-key") || env.GROUNDMAIL_API_KEY;
2192
- const baseUrl = readFlag17(args, "--base-url");
2193
- const id = readFlag17(args, "--id");
2337
+ const apiKey = readFlag18(args, "--api-key") || env.GROUNDMAIL_API_KEY;
2338
+ const baseUrl = readFlag18(args, "--base-url");
2339
+ const id = readFlag18(args, "--id");
2194
2340
  if (!apiKey) {
2195
2341
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2196
2342
  }
@@ -2211,18 +2357,18 @@ async function runDeleteKeyCommandWithClient(args, env = process.env) {
2211
2357
  }
2212
2358
 
2213
2359
  // src/commands/suppressions/shared.ts
2214
- function readFlag18(args, flag) {
2360
+ function readFlag19(args, flag) {
2215
2361
  const index = args.indexOf(flag);
2216
2362
  if (index === -1) return void 0;
2217
2363
  return args[index + 1];
2218
2364
  }
2219
2365
  function parseSuppressionsCommonArgs(args) {
2220
2366
  return {
2221
- apiKey: readFlag18(args, "--api-key"),
2222
- baseUrl: readFlag18(args, "--base-url")
2367
+ apiKey: readFlag19(args, "--api-key"),
2368
+ baseUrl: readFlag19(args, "--base-url")
2223
2369
  };
2224
2370
  }
2225
- function resolveApiKey6(options, env) {
2371
+ function resolveApiKey7(options, env) {
2226
2372
  return options.apiKey || env.GROUNDMAIL_API_KEY || null;
2227
2373
  }
2228
2374
  async function createSuppressionsClient(apiKey, baseUrl) {
@@ -2233,19 +2379,21 @@ async function createSuppressionsClient(apiKey, baseUrl) {
2233
2379
  // src/commands/suppressions/list.ts
2234
2380
  async function runListCommand6(args, env = process.env) {
2235
2381
  const options = parseSuppressionsCommonArgs(args);
2236
- const apiKey = resolveApiKey6(options, env);
2382
+ const apiKey = resolveApiKey7(options, env);
2237
2383
  if (!apiKey) {
2238
2384
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2239
2385
  }
2240
2386
  try {
2241
2387
  const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
2242
- const limitRaw = readFlag18(args, "--limit");
2243
- const cursor = readFlag18(args, "--cursor");
2244
- const email = readFlag18(args, "--email");
2388
+ const limitRaw = readFlag19(args, "--limit");
2389
+ const cursor = readFlag19(args, "--cursor");
2390
+ const email = readFlag19(args, "--email");
2391
+ const tenant = readFlag19(args, "--tenant");
2245
2392
  const result = await client2.suppressions.list({
2246
2393
  ...limitRaw ? { limit: Number(limitRaw) } : {},
2247
2394
  ...cursor ? { cursor } : {},
2248
- ...email ? { email } : {}
2395
+ ...email ? { email } : {},
2396
+ ...tenant ? { tenant } : {}
2249
2397
  });
2250
2398
  console.log(JSON.stringify(result));
2251
2399
  return { exitCode: 0 };
@@ -2257,20 +2405,22 @@ async function runListCommand6(args, env = process.env) {
2257
2405
  // src/commands/suppressions/add.ts
2258
2406
  async function runAddCommand2(args, env = process.env) {
2259
2407
  const options = parseSuppressionsCommonArgs(args);
2260
- const apiKey = resolveApiKey6(options, env);
2408
+ const apiKey = resolveApiKey7(options, env);
2261
2409
  if (!apiKey) {
2262
2410
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2263
2411
  }
2264
- const email = readFlag18(args, "--email") || args.find((a) => a.includes("@") && !a.startsWith("-"));
2412
+ const tenant = readFlag19(args, "--tenant");
2413
+ const email = readFlag19(args, "--email") || args.find((a, i) => a.includes("@") && !a.startsWith("-") && args[i - 1] !== "--tenant");
2265
2414
  if (!email) {
2266
2415
  return { exitCode: 1, error: "Missing email. Pass --email user@example.com" };
2267
2416
  }
2268
2417
  try {
2269
2418
  const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
2270
- const reason = readFlag18(args, "--reason");
2419
+ const reason = readFlag19(args, "--reason");
2271
2420
  const result = await client2.suppressions.create({
2272
2421
  email,
2273
- ...reason ? { reason } : {}
2422
+ ...reason ? { reason } : {},
2423
+ ...tenant ? { tenant } : {}
2274
2424
  });
2275
2425
  console.log(JSON.stringify(result));
2276
2426
  return { exitCode: 0 };
@@ -2282,17 +2432,20 @@ async function runAddCommand2(args, env = process.env) {
2282
2432
  // src/commands/suppressions/remove.ts
2283
2433
  async function runRemoveCommand(args, env = process.env) {
2284
2434
  const options = parseSuppressionsCommonArgs(args);
2285
- const apiKey = resolveApiKey6(options, env);
2435
+ const apiKey = resolveApiKey7(options, env);
2286
2436
  if (!apiKey) {
2287
2437
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2288
2438
  }
2289
- const idOrEmail = readFlag18(args, "--id") || readFlag18(args, "--email") || args.find((a) => !a.startsWith("-") && a !== "remove" && a !== "delete");
2439
+ const tenant = readFlag19(args, "--tenant");
2440
+ const idOrEmail = readFlag19(args, "--id") || readFlag19(args, "--email") || args.find(
2441
+ (a, i) => !a.startsWith("-") && a !== "remove" && a !== "delete" && !["--tenant", "--api-key", "--base-url"].includes(args[i - 1] ?? "")
2442
+ );
2290
2443
  if (!idOrEmail) {
2291
2444
  return { exitCode: 1, error: "Missing id or email. Pass --id <uuid> or --email user@example.com" };
2292
2445
  }
2293
2446
  try {
2294
2447
  const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
2295
- const result = await client2.suppressions.remove(idOrEmail);
2448
+ const result = tenant ? await client2.suppressions.remove(idOrEmail, { tenant }) : await client2.suppressions.remove(idOrEmail);
2296
2449
  console.log(JSON.stringify(result));
2297
2450
  return { exitCode: 0 };
2298
2451
  } catch (error) {
@@ -2301,18 +2454,18 @@ async function runRemoveCommand(args, env = process.env) {
2301
2454
  }
2302
2455
 
2303
2456
  // src/commands/templates/shared.ts
2304
- function readFlag19(args, flag) {
2457
+ function readFlag20(args, flag) {
2305
2458
  const index = args.indexOf(flag);
2306
2459
  if (index === -1) return void 0;
2307
2460
  return args[index + 1];
2308
2461
  }
2309
2462
  function parseTemplatesCommonArgs(args) {
2310
2463
  return {
2311
- apiKey: readFlag19(args, "--api-key"),
2312
- baseUrl: readFlag19(args, "--base-url")
2464
+ apiKey: readFlag20(args, "--api-key"),
2465
+ baseUrl: readFlag20(args, "--base-url")
2313
2466
  };
2314
2467
  }
2315
- function resolveApiKey7(options, env) {
2468
+ function resolveApiKey8(options, env) {
2316
2469
  return options.apiKey || env.GROUNDMAIL_API_KEY || null;
2317
2470
  }
2318
2471
  async function createTemplatesClient(apiKey, baseUrl) {
@@ -2324,9 +2477,9 @@ async function createTemplatesClient(apiKey, baseUrl) {
2324
2477
  var POSITIONAL_FLAGS7 = ["--api-key", "--base-url", "--id", "--alias"];
2325
2478
  async function runDeleteCommand4(args, env = process.env) {
2326
2479
  const options = parseTemplatesCommonArgs(args);
2327
- const apiKey = resolveApiKey7(options, env);
2480
+ const apiKey = resolveApiKey8(options, env);
2328
2481
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2329
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional8(args);
2482
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional8(args);
2330
2483
  if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2331
2484
  try {
2332
2485
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
@@ -2352,11 +2505,11 @@ function readFirstPositional8(args) {
2352
2505
  var POSITIONAL_FLAGS8 = ["--api-key", "--base-url", "--id", "--alias", "--name"];
2353
2506
  async function runDuplicateCommand(args, env = process.env) {
2354
2507
  const options = parseTemplatesCommonArgs(args);
2355
- const apiKey = resolveApiKey7(options, env);
2508
+ const apiKey = resolveApiKey8(options, env);
2356
2509
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2357
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional9(args);
2510
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional9(args);
2358
2511
  if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2359
- const name = readFlag19(args, "--name");
2512
+ const name = readFlag20(args, "--name");
2360
2513
  try {
2361
2514
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
2362
2515
  console.log(JSON.stringify(await client2.templates.duplicate(idOrAlias, name ? { name } : {})));
@@ -2381,9 +2534,9 @@ function readFirstPositional9(args) {
2381
2534
  var POSITIONAL_FLAGS9 = ["--api-key", "--base-url", "--id", "--alias"];
2382
2535
  async function runGetCommand6(args, env = process.env) {
2383
2536
  const options = parseTemplatesCommonArgs(args);
2384
- const apiKey = resolveApiKey7(options, env);
2537
+ const apiKey = resolveApiKey8(options, env);
2385
2538
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2386
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional10(args);
2539
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional10(args);
2387
2540
  if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2388
2541
  try {
2389
2542
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
@@ -2408,15 +2561,15 @@ function readFirstPositional10(args) {
2408
2561
  // src/commands/templates/list.ts
2409
2562
  async function runListCommand7(args, env = process.env) {
2410
2563
  const options = parseTemplatesCommonArgs(args);
2411
- const apiKey = resolveApiKey7(options, env);
2564
+ const apiKey = resolveApiKey8(options, env);
2412
2565
  if (!apiKey) {
2413
2566
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2414
2567
  }
2415
2568
  try {
2416
2569
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
2417
- const limitRaw = readFlag19(args, "--limit");
2418
- const cursor = readFlag19(args, "--cursor");
2419
- const status = readFlag19(args, "--status");
2570
+ const limitRaw = readFlag20(args, "--limit");
2571
+ const cursor = readFlag20(args, "--cursor");
2572
+ const status = readFlag20(args, "--status");
2420
2573
  const result = await client2.templates.list({
2421
2574
  ...limitRaw ? { limit: Number(limitRaw) } : {},
2422
2575
  ...cursor ? { cursor } : {},
@@ -2433,9 +2586,9 @@ async function runListCommand7(args, env = process.env) {
2433
2586
  var POSITIONAL_FLAGS10 = ["--api-key", "--base-url", "--id", "--alias"];
2434
2587
  async function runPublishCommand(args, env = process.env) {
2435
2588
  const options = parseTemplatesCommonArgs(args);
2436
- const apiKey = resolveApiKey7(options, env);
2589
+ const apiKey = resolveApiKey8(options, env);
2437
2590
  if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2438
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional11(args);
2591
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional11(args);
2439
2592
  if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2440
2593
  try {
2441
2594
  const client2 = await createTemplatesClient(apiKey, options.baseUrl);
@@ -2574,23 +2727,23 @@ function statusOf(error) {
2574
2727
  }
2575
2728
  async function runPushCommand(args, env = process.env, deps = {}) {
2576
2729
  const options = parseTemplatesCommonArgs(args);
2577
- const apiKey = resolveApiKey7(options, env);
2730
+ const apiKey = resolveApiKey8(options, env);
2578
2731
  if (!apiKey) {
2579
2732
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2580
2733
  }
2581
- const file = readFlag19(args, "--file") ?? readPositional(args);
2734
+ const file = readFlag20(args, "--file") ?? readPositional(args);
2582
2735
  if (!file) {
2583
2736
  return { exitCode: 1, error: "Missing template file. Usage: templates push <file> --alias <alias>" };
2584
2737
  }
2585
- const alias = readFlag19(args, "--alias");
2738
+ const alias = readFlag20(args, "--alias");
2586
2739
  if (!alias) {
2587
2740
  return { exitCode: 1, error: "Missing --alias. The alias identifies which template to update." };
2588
2741
  }
2589
- const subject = readFlag19(args, "--subject");
2590
- const name = readFlag19(args, "--name");
2591
- const preheader = readFlag19(args, "--preheader");
2592
- const textFile = readFlag19(args, "--text");
2593
- const propsRaw = readFlag19(args, "--props");
2742
+ const subject = readFlag20(args, "--subject");
2743
+ const name = readFlag20(args, "--name");
2744
+ const preheader = readFlag20(args, "--preheader");
2745
+ const textFile = readFlag20(args, "--text");
2746
+ const propsRaw = readFlag20(args, "--props");
2594
2747
  const shouldPublish = args.includes("--publish");
2595
2748
  let props = {};
2596
2749
  if (propsRaw) {
@@ -2660,16 +2813,16 @@ async function runPushCommand(args, env = process.env, deps = {}) {
2660
2813
  // src/commands/templates/create.ts
2661
2814
  async function runCreateCommand3(args, env = process.env) {
2662
2815
  const options = parseTemplatesCommonArgs(args);
2663
- const apiKey = resolveApiKey7(options, env);
2816
+ const apiKey = resolveApiKey8(options, env);
2664
2817
  if (!apiKey) {
2665
2818
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2666
2819
  }
2667
- const name = readFlag19(args, "--name");
2668
- const subject = readFlag19(args, "--subject");
2669
- const html = readFlag19(args, "--html");
2670
- const text = readFlag19(args, "--text");
2671
- const alias = readFlag19(args, "--alias");
2672
- const category = readFlag19(args, "--category");
2820
+ const name = readFlag20(args, "--name");
2821
+ const subject = readFlag20(args, "--subject");
2822
+ const html = readFlag20(args, "--html");
2823
+ const text = readFlag20(args, "--text");
2824
+ const alias = readFlag20(args, "--alias");
2825
+ const category = readFlag20(args, "--category");
2673
2826
  const unsubscribeEnabled = args.includes("--unsubscribe-enabled");
2674
2827
  if (!name) {
2675
2828
  return { exitCode: 1, error: 'Missing name. Pass --name "Welcome"' };
@@ -2713,21 +2866,21 @@ var POSITIONAL_FLAGS11 = [
2713
2866
  ];
2714
2867
  async function runUpdateCommand4(args, env = process.env) {
2715
2868
  const options = parseTemplatesCommonArgs(args);
2716
- const apiKey = resolveApiKey7(options, env);
2869
+ const apiKey = resolveApiKey8(options, env);
2717
2870
  if (!apiKey) {
2718
2871
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2719
2872
  }
2720
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional12(args);
2873
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional12(args);
2721
2874
  if (!idOrAlias) {
2722
2875
  return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2723
2876
  }
2724
- const name = readFlag19(args, "--name");
2725
- const subject = readFlag19(args, "--subject");
2726
- const html = readFlag19(args, "--html");
2727
- const text = readFlag19(args, "--text");
2728
- const alias = readFlag19(args, "--set-alias");
2877
+ const name = readFlag20(args, "--name");
2878
+ const subject = readFlag20(args, "--subject");
2879
+ const html = readFlag20(args, "--html");
2880
+ const text = readFlag20(args, "--text");
2881
+ const alias = readFlag20(args, "--set-alias");
2729
2882
  const variables = readFlags3(args, "--variable");
2730
- const category = readFlag19(args, "--category");
2883
+ const category = readFlag20(args, "--category");
2731
2884
  const unsubscribeEnabled = args.includes("--unsubscribe-enabled");
2732
2885
  const unsubscribeDisabled = args.includes("--no-unsubscribe-enabled");
2733
2886
  if (!name && !subject && !html && !text && !alias && variables.length === 0 && !category && !unsubscribeEnabled && !unsubscribeDisabled) {
@@ -2789,11 +2942,11 @@ function readFirstPositional13(args) {
2789
2942
  }
2790
2943
  async function runFromStarterCommand(args, env = process.env) {
2791
2944
  const options = parseTemplatesCommonArgs(args);
2792
- const apiKey = resolveApiKey7(options, env);
2945
+ const apiKey = resolveApiKey8(options, env);
2793
2946
  if (!apiKey) {
2794
2947
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2795
2948
  }
2796
- const alias = readFlag19(args, "--alias") || readFirstPositional13(args);
2949
+ const alias = readFlag20(args, "--alias") || readFirstPositional13(args);
2797
2950
  if (!alias) {
2798
2951
  return {
2799
2952
  exitCode: 1,
@@ -2810,7 +2963,7 @@ async function runFromStarterCommand(args, env = process.env) {
2810
2963
  }
2811
2964
  async function runListStartersCommand(args, env = process.env) {
2812
2965
  const options = parseTemplatesCommonArgs(args);
2813
- const apiKey = resolveApiKey7(options, env);
2966
+ const apiKey = resolveApiKey8(options, env);
2814
2967
  if (!apiKey) {
2815
2968
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2816
2969
  }
@@ -2838,11 +2991,11 @@ function readFirstPositional14(args) {
2838
2991
  }
2839
2992
  async function runVersionsCommand(args, env = process.env) {
2840
2993
  const options = parseTemplatesCommonArgs(args);
2841
- const apiKey = resolveApiKey7(options, env);
2994
+ const apiKey = resolveApiKey8(options, env);
2842
2995
  if (!apiKey) {
2843
2996
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2844
2997
  }
2845
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional14(args);
2998
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional14(args);
2846
2999
  if (!idOrAlias) {
2847
3000
  return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
2848
3001
  }
@@ -2856,12 +3009,12 @@ async function runVersionsCommand(args, env = process.env) {
2856
3009
  }
2857
3010
  async function runRestoreCommand(args, env = process.env) {
2858
3011
  const options = parseTemplatesCommonArgs(args);
2859
- const apiKey = resolveApiKey7(options, env);
3012
+ const apiKey = resolveApiKey8(options, env);
2860
3013
  if (!apiKey) {
2861
3014
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2862
3015
  }
2863
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional14(args);
2864
- const versionRaw = readFlag19(args, "--version");
3016
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional14(args);
3017
+ const versionRaw = readFlag20(args, "--version");
2865
3018
  const version2 = versionRaw ? Number.parseInt(versionRaw, 10) : NaN;
2866
3019
  if (!idOrAlias || !Number.isFinite(version2) || version2 < 1) {
2867
3020
  return {
@@ -2879,13 +3032,13 @@ async function runRestoreCommand(args, env = process.env) {
2879
3032
  }
2880
3033
  async function runTestSendCommand(args, env = process.env) {
2881
3034
  const options = parseTemplatesCommonArgs(args);
2882
- const apiKey = resolveApiKey7(options, env);
3035
+ const apiKey = resolveApiKey8(options, env);
2883
3036
  if (!apiKey) {
2884
3037
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2885
3038
  }
2886
- const idOrAlias = readFlag19(args, "--id") || readFlag19(args, "--alias") || readFirstPositional14(args);
2887
- const from = readFlag19(args, "--from");
2888
- const to = readFlag19(args, "--to");
3039
+ const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional14(args);
3040
+ const from = readFlag20(args, "--from");
3041
+ const to = readFlag20(args, "--to");
2889
3042
  if (!idOrAlias || !from || !to) {
2890
3043
  return {
2891
3044
  exitCode: 1,
@@ -2902,18 +3055,18 @@ async function runTestSendCommand(args, env = process.env) {
2902
3055
  }
2903
3056
 
2904
3057
  // src/commands/integrations/shared.ts
2905
- function readFlag20(args, flag) {
3058
+ function readFlag21(args, flag) {
2906
3059
  const index = args.indexOf(flag);
2907
3060
  if (index === -1) return void 0;
2908
3061
  return args[index + 1];
2909
3062
  }
2910
3063
  function parseIntegrationsCommonArgs(args) {
2911
3064
  return {
2912
- apiKey: readFlag20(args, "--api-key"),
2913
- baseUrl: readFlag20(args, "--base-url")
3065
+ apiKey: readFlag21(args, "--api-key"),
3066
+ baseUrl: readFlag21(args, "--base-url")
2914
3067
  };
2915
3068
  }
2916
- function resolveApiKey8(options, env) {
3069
+ function resolveApiKey9(options, env) {
2917
3070
  return options.apiKey || env.GROUNDMAIL_API_KEY || null;
2918
3071
  }
2919
3072
  async function createIntegrationsClient(apiKey, baseUrl) {
@@ -2924,7 +3077,7 @@ async function createIntegrationsClient(apiKey, baseUrl) {
2924
3077
  // src/commands/integrations/twilio.ts
2925
3078
  async function runTwilioStatusCommand(args, env = process.env) {
2926
3079
  const options = parseIntegrationsCommonArgs(args);
2927
- const apiKey = resolveApiKey8(options, env);
3080
+ const apiKey = resolveApiKey9(options, env);
2928
3081
  if (!apiKey) {
2929
3082
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2930
3083
  }
@@ -2939,14 +3092,14 @@ async function runTwilioStatusCommand(args, env = process.env) {
2939
3092
  }
2940
3093
  async function runTwilioConnectCommand(args, env = process.env) {
2941
3094
  const options = parseIntegrationsCommonArgs(args);
2942
- const apiKey = resolveApiKey8(options, env);
3095
+ const apiKey = resolveApiKey9(options, env);
2943
3096
  if (!apiKey) {
2944
3097
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2945
3098
  }
2946
- const accountSid = readFlag20(args, "--account-sid") || env.TWILIO_ACCOUNT_SID || env.TWILIO_SID || "";
2947
- const authToken = readFlag20(args, "--auth-token") || env.TWILIO_AUTH_TOKEN || env.TWILIO_TOKEN || "";
2948
- const defaultFrom = readFlag20(args, "--from") || readFlag20(args, "--default-from") || env.TWILIO_DEFAULT_FROM || "";
2949
- const messagingServiceSid = readFlag20(args, "--messaging-service-sid") || readFlag20(args, "--messaging-sid") || env.TWILIO_MESSAGING_SERVICE_SID || "";
3099
+ const accountSid = readFlag21(args, "--account-sid") || env.TWILIO_ACCOUNT_SID || env.TWILIO_SID || "";
3100
+ const authToken = readFlag21(args, "--auth-token") || env.TWILIO_AUTH_TOKEN || env.TWILIO_TOKEN || "";
3101
+ const defaultFrom = readFlag21(args, "--from") || readFlag21(args, "--default-from") || env.TWILIO_DEFAULT_FROM || "";
3102
+ const messagingServiceSid = readFlag21(args, "--messaging-service-sid") || readFlag21(args, "--messaging-sid") || env.TWILIO_MESSAGING_SERVICE_SID || "";
2950
3103
  if (!accountSid.trim() || !authToken.trim()) {
2951
3104
  return {
2952
3105
  exitCode: 1,
@@ -2975,7 +3128,7 @@ async function runTwilioConnectCommand(args, env = process.env) {
2975
3128
  }
2976
3129
  async function runTwilioDisconnectCommand(args, env = process.env) {
2977
3130
  const options = parseIntegrationsCommonArgs(args);
2978
- const apiKey = resolveApiKey8(options, env);
3131
+ const apiKey = resolveApiKey9(options, env);
2979
3132
  if (!apiKey) {
2980
3133
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
2981
3134
  }
@@ -2989,6 +3142,125 @@ async function runTwilioDisconnectCommand(args, env = process.env) {
2989
3142
  }
2990
3143
  }
2991
3144
 
3145
+ // src/commands/tenants/commands.ts
3146
+ async function withClient(args, env, run) {
3147
+ const options = parseTenantsCommonArgs(args);
3148
+ const apiKey = resolveApiKey5(options, env);
3149
+ if (!apiKey) return { exitCode: 1, error: MISSING_KEY };
3150
+ try {
3151
+ const client2 = await createTenantsClient(apiKey, options.baseUrl);
3152
+ console.log(JSON.stringify(await run(client2)));
3153
+ return { exitCode: 0 };
3154
+ } catch (error) {
3155
+ return failure(error);
3156
+ }
3157
+ }
3158
+ function missingTenant(usage) {
3159
+ return { exitCode: 1, error: `Missing tenant. Usage: ${usage}` };
3160
+ }
3161
+ async function runListCommand8(args, env = process.env) {
3162
+ const status = readFlag12(args, "--status");
3163
+ if (status !== void 0 && status !== "active" && status !== "paused") {
3164
+ return { exitCode: 1, error: "--status must be active or paused" };
3165
+ }
3166
+ const limitRaw = readFlag12(args, "--limit");
3167
+ const cursor = readFlag12(args, "--cursor");
3168
+ return withClient(
3169
+ args,
3170
+ env,
3171
+ (client2) => client2.tenants.list({
3172
+ ...limitRaw ? { limit: Number(limitRaw) } : {},
3173
+ ...cursor ? { cursor } : {},
3174
+ ...status ? { status } : {}
3175
+ })
3176
+ );
3177
+ }
3178
+ async function runCreateCommand4(args, env = process.env) {
3179
+ const externalId = readFlag12(args, "--external-id") || positionals(args)[0];
3180
+ if (!externalId) {
3181
+ return { exitCode: 1, error: "Missing external id. Usage: groundmail tenants create <external-id> [--name \u2026]" };
3182
+ }
3183
+ const settings = parseTenantSettings(args);
3184
+ if ("error" in settings) return { exitCode: 1, error: settings.error };
3185
+ return withClient(args, env, (client2) => client2.tenants.create({ external_id: externalId, ...settings.value }));
3186
+ }
3187
+ async function runGetCommand7(args, env = process.env) {
3188
+ const tenant = tenantRef(args);
3189
+ if (!tenant) return missingTenant("groundmail tenants get <tenant>");
3190
+ return withClient(args, env, (client2) => client2.tenants.get(tenant));
3191
+ }
3192
+ async function runUpdateCommand5(args, env = process.env) {
3193
+ const tenant = tenantRef(args);
3194
+ if (!tenant) return missingTenant("groundmail tenants update <tenant> [--name \u2026] [--daily-limit N|none]");
3195
+ const settings = parseTenantSettings(args);
3196
+ if ("error" in settings) return { exitCode: 1, error: settings.error };
3197
+ if (Object.keys(settings.value).length === 0) {
3198
+ return {
3199
+ exitCode: 1,
3200
+ error: "Nothing to update. Pass --name, --default-from-domain, --stream-policy, --daily-limit or --metadata (or use tenants pause / resume)."
3201
+ };
3202
+ }
3203
+ return withClient(args, env, (client2) => client2.tenants.update(tenant, settings.value));
3204
+ }
3205
+ async function runPauseCommand2(args, env = process.env) {
3206
+ const tenant = tenantRef(args);
3207
+ if (!tenant) return missingTenant("groundmail tenants pause <tenant> [--reason \u2026]");
3208
+ const reason = readFlag12(args, "--reason");
3209
+ return withClient(args, env, (client2) => client2.tenants.pause(tenant, reason));
3210
+ }
3211
+ async function runResumeCommand(args, env = process.env) {
3212
+ const tenant = tenantRef(args);
3213
+ if (!tenant) return missingTenant("groundmail tenants resume <tenant>");
3214
+ return withClient(args, env, (client2) => client2.tenants.resume(tenant));
3215
+ }
3216
+ async function runDeleteCommand5(args, env = process.env) {
3217
+ const tenant = tenantRef(args);
3218
+ if (!tenant) return missingTenant("groundmail tenants delete <tenant> --yes");
3219
+ if (!hasFlag(args, "--yes")) {
3220
+ return {
3221
+ exitCode: 1,
3222
+ 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.`
3223
+ };
3224
+ }
3225
+ return withClient(args, env, (client2) => client2.tenants.delete(tenant));
3226
+ }
3227
+ async function runStatsCommand(args, env = process.env) {
3228
+ const tenant = tenantRef(args);
3229
+ if (!tenant) return missingTenant("groundmail tenants stats <tenant> [--days 7]");
3230
+ const daysRaw = readFlag12(args, "--days");
3231
+ const days = daysRaw !== void 0 ? Number(daysRaw) : void 0;
3232
+ if (days !== void 0 && (!Number.isInteger(days) || days < 1 || days > 90)) {
3233
+ return { exitCode: 1, error: "--days must be a whole number from 1 to 90" };
3234
+ }
3235
+ return withClient(args, env, (client2) => client2.tenants.stats(tenant, days !== void 0 ? { days } : {}));
3236
+ }
3237
+ async function runDomainsCommand(args, env = process.env) {
3238
+ const [action, ...rest] = args;
3239
+ const pos = positionals(rest);
3240
+ const tenant = readFlag12(rest, "--tenant") || readFlag12(rest, "--id") || pos.shift();
3241
+ const domain = readFlag12(rest, "--domain") || pos.shift();
3242
+ if (action === "list") {
3243
+ if (!tenant) return missingTenant("groundmail tenants domains list <tenant>");
3244
+ return withClient(rest, env, (client2) => client2.tenants.domains.list(tenant));
3245
+ }
3246
+ if (action === "add" || action === "assign") {
3247
+ if (!tenant || !domain) {
3248
+ return { exitCode: 1, error: "Usage: groundmail tenants domains add <tenant> <domain>" };
3249
+ }
3250
+ return withClient(rest, env, (client2) => client2.tenants.domains.assign(tenant, domain));
3251
+ }
3252
+ if (action === "remove" || action === "unassign" || action === "delete") {
3253
+ if (!tenant || !domain) {
3254
+ return { exitCode: 1, error: "Usage: groundmail tenants domains remove <tenant> <domain>" };
3255
+ }
3256
+ return withClient(rest, env, (client2) => client2.tenants.domains.unassign(tenant, domain));
3257
+ }
3258
+ return {
3259
+ exitCode: 1,
3260
+ error: "Usage: groundmail tenants domains list|add|remove <tenant> [<domain>]"
3261
+ };
3262
+ }
3263
+
2992
3264
  // src/commands/auth.ts
2993
3265
  async function runAuthCommand(subcommand, argv, deps = {}) {
2994
3266
  const env = deps.env ?? process.env;
@@ -3135,7 +3407,7 @@ var KEY_ENV = {
3135
3407
  };
3136
3408
  var LABELS = { sendgrid: "SendGrid", postmark: "Postmark", mailgun: "Mailgun" };
3137
3409
  var VALUE_FLAGS2 = /* @__PURE__ */ new Set(["--api-key", "--base-url"]);
3138
- function readFlag21(args, flag) {
3410
+ function readFlag22(args, flag) {
3139
3411
  const index = args.indexOf(flag);
3140
3412
  if (index === -1) return void 0;
3141
3413
  return args[index + 1];
@@ -3204,10 +3476,10 @@ function formatMigrationSummary(m) {
3204
3476
  return lines.join("\n");
3205
3477
  }
3206
3478
  async function client(args, env) {
3207
- const apiKey = readFlag21(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3479
+ const apiKey = readFlag22(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3208
3480
  if (!apiKey) return null;
3209
3481
  const { Groundmail } = await import("groundmail");
3210
- const baseUrl = readFlag21(args, "--base-url");
3482
+ const baseUrl = readFlag22(args, "--base-url");
3211
3483
  return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
3212
3484
  }
3213
3485
  async function runStoredCommand(sub, args, env) {
@@ -3244,11 +3516,11 @@ async function runMigrateCommand(provider, args, env = process.env) {
3244
3516
  };
3245
3517
  }
3246
3518
  const id = provider;
3247
- const apiKey = readFlag21(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3519
+ const apiKey = readFlag22(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3248
3520
  if (!apiKey) {
3249
3521
  return { exitCode: 1, error: "Not signed in. Run `groundmail login`, or set GROUNDMAIL_API_KEY." };
3250
3522
  }
3251
- const providerKey = readFlag21(args, "--key") || env[KEY_ENV[id]];
3523
+ const providerKey = readFlag22(args, "--key") || env[KEY_ENV[id]];
3252
3524
  if (!providerKey) {
3253
3525
  return {
3254
3526
  exitCode: 1,
@@ -3257,14 +3529,14 @@ async function runMigrateCommand(provider, args, env = process.env) {
3257
3529
  }
3258
3530
  const params = { provider: id, api_key: providerKey };
3259
3531
  if (id === "postmark") {
3260
- const accountToken = readFlag21(args, "--account-token") || env.POSTMARK_ACCOUNT_TOKEN;
3532
+ const accountToken = readFlag22(args, "--account-token") || env.POSTMARK_ACCOUNT_TOKEN;
3261
3533
  if (accountToken) params.account_api_key = accountToken;
3262
3534
  } else {
3263
- const region = readFlag21(args, "--region");
3535
+ const region = readFlag22(args, "--region");
3264
3536
  if (region === "us" || region === "eu") params.region = region;
3265
3537
  else if (region) return { exitCode: 1, error: "--region must be us or eu" };
3266
3538
  }
3267
- const only = readFlag21(args, "--only");
3539
+ const only = readFlag22(args, "--only");
3268
3540
  if (only) {
3269
3541
  const list = only.split(",").map((s) => s.trim()).filter(Boolean);
3270
3542
  const bad = list.filter((r) => !RESOURCES.includes(r));
@@ -3276,7 +3548,7 @@ async function runMigrateCommand(provider, args, env = process.env) {
3276
3548
  if (args.includes("--dry-run")) params.dry_run = true;
3277
3549
  try {
3278
3550
  const { Groundmail } = await import("groundmail");
3279
- const baseUrl = readFlag21(args, "--base-url");
3551
+ const baseUrl = readFlag22(args, "--base-url");
3280
3552
  const client2 = new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
3281
3553
  console.error(`${params.dry_run ? "Previewing" : "Importing"} from ${LABELS[id]} (Beta)\u2026`);
3282
3554
  const report = await client2.migrations.run(params, {
@@ -3299,12 +3571,12 @@ async function runMigrateCommand(provider, args, env = process.env) {
3299
3571
  // src/commands/validate.ts
3300
3572
  async function runValidateCommand(args, env = process.env) {
3301
3573
  const options = parseSuppressionsCommonArgs(args);
3302
- const apiKey = resolveApiKey6(options, env);
3574
+ const apiKey = resolveApiKey7(options, env);
3303
3575
  if (!apiKey) {
3304
3576
  return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY, pass --api-key, or run `groundmail login`." };
3305
3577
  }
3306
3578
  const flagValues = new Set([options.apiKey, options.baseUrl].filter(Boolean));
3307
- const email = readFlag18(args, "--email") || args.find((arg) => arg.includes("@") && !arg.startsWith("-") && !flagValues.has(arg));
3579
+ const email = readFlag19(args, "--email") || args.find((arg) => arg.includes("@") && !arg.startsWith("-") && !flagValues.has(arg));
3308
3580
  if (!email) {
3309
3581
  return { exitCode: 1, error: "Missing email. Usage: groundmail validate user@example.com" };
3310
3582
  }
@@ -3318,6 +3590,109 @@ async function runValidateCommand(args, env = process.env) {
3318
3590
  }
3319
3591
  }
3320
3592
 
3593
+ // src/commands/dmarc/dmarc.ts
3594
+ function readFlag23(args, flag) {
3595
+ const index = args.indexOf(flag);
3596
+ if (index === -1) return void 0;
3597
+ return args[index + 1];
3598
+ }
3599
+ var VALUE_FLAGS3 = ["--api-key", "--base-url", "--domain", "--apply"];
3600
+ function readFirstPositional15(args) {
3601
+ for (let i = 0; i < args.length; i += 1) {
3602
+ const arg = args[i];
3603
+ if (arg.startsWith("--")) {
3604
+ if (VALUE_FLAGS3.includes(arg)) i += 1;
3605
+ continue;
3606
+ }
3607
+ return arg;
3608
+ }
3609
+ return void 0;
3610
+ }
3611
+ async function createDmarcClient(apiKey, baseUrl) {
3612
+ const { Groundmail } = await import("groundmail");
3613
+ return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
3614
+ }
3615
+ function pct(rate) {
3616
+ const value = rate * 100;
3617
+ return value === 100 ? "100%" : `${value.toFixed(1).replace(/\.0$/, "")}%`;
3618
+ }
3619
+ function formatDmarcStatus(dmarc) {
3620
+ if (!dmarc.enabled) {
3621
+ return [
3622
+ `DMARC monitoring for ${dmarc.domain}: off`,
3623
+ ` Current record (${dmarc.dns.host}): ${dmarc.dns.record ?? "none"}`,
3624
+ "",
3625
+ `Turn it on: groundmail dmarc enable ${dmarc.domain}`
3626
+ ].join("\n");
3627
+ }
3628
+ const lines = [
3629
+ `DMARC monitoring for ${dmarc.domain}: ${dmarc.status === "waiting" ? "waiting for the first report" : "receiving reports"}`,
3630
+ ` Report address: ${dmarc.report_address}`
3631
+ ];
3632
+ if (!dmarc.dns.rua_present) {
3633
+ lines.push("", ` Add the address to your DMARC record. TXT ${dmarc.dns.host}:`, ` ${dmarc.dns.suggested_record}`);
3634
+ }
3635
+ lines.push(
3636
+ "",
3637
+ ` Last ${dmarc.window_days} days: ${dmarc.summary.messages.toLocaleString("en-US")} messages, ${pct(dmarc.summary.pass_rate)} passed DMARC, ${dmarc.summary.reports} reports`,
3638
+ ` Readiness: ${dmarc.readiness.label}. ${dmarc.readiness.detail}`
3639
+ );
3640
+ if (dmarc.sources.length) {
3641
+ lines.push("", " Sources:");
3642
+ for (const source of dmarc.sources.slice(0, 15)) {
3643
+ lines.push(
3644
+ ` ${source.name.padEnd(26)} ${source.status.padEnd(8)} ${String(source.messages).padStart(9)} msgs ${pct(source.pass_rate).padStart(6)} pass`
3645
+ );
3646
+ }
3647
+ }
3648
+ return lines.join("\n");
3649
+ }
3650
+ async function runDmarcCommand(subcommand, args, env = process.env) {
3651
+ const apiKey = readFlag23(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3652
+ if (!apiKey) return { exitCode: 1, error: "Missing API key. Run groundmail login, set GROUNDMAIL_API_KEY or pass --api-key." };
3653
+ const domain = readFlag23(args, "--domain") || readFirstPositional15(args);
3654
+ if (!subcommand || !["status", "enable", "disable", "digest"].includes(subcommand)) {
3655
+ return { exitCode: 1, error: "Usage: groundmail dmarc status|enable|disable|digest <domain>" };
3656
+ }
3657
+ if (!domain) return { exitCode: 1, error: `Missing domain. Usage: groundmail dmarc ${subcommand} example.com` };
3658
+ const json = args.includes("--json");
3659
+ try {
3660
+ const client2 = await createDmarcClient(apiKey, readFlag23(args, "--base-url"));
3661
+ if (subcommand === "status") {
3662
+ const result2 = await client2.dmarc.get(domain);
3663
+ console.log(json ? JSON.stringify(result2) : formatDmarcStatus(result2.dmarc));
3664
+ return { exitCode: 0 };
3665
+ }
3666
+ if (subcommand === "enable") {
3667
+ const apply = readFlag23(args, "--apply");
3668
+ if (apply && apply !== "cloudflare" && apply !== "vercel" && apply !== "godaddy") {
3669
+ return { exitCode: 1, error: "--apply must be cloudflare, vercel or godaddy" };
3670
+ }
3671
+ const result2 = await client2.dmarc.enable(domain, apply ? { apply } : {});
3672
+ if (json) console.log(JSON.stringify(result2));
3673
+ else {
3674
+ console.log(formatDmarcStatus(result2.dmarc));
3675
+ if (result2.applied) console.log(`
3676
+ Updated the DMARC record with ${result2.applied.provider}.`);
3677
+ }
3678
+ if (result2.apply_error) return { exitCode: 1, error: result2.apply_error };
3679
+ return { exitCode: 0 };
3680
+ }
3681
+ if (subcommand === "disable") {
3682
+ const result2 = await client2.dmarc.disable(domain);
3683
+ console.log(json ? JSON.stringify(result2) : `DMARC monitoring for ${domain} is off. Remove the report address from your DMARC record's rua= tag too.`);
3684
+ return { exitCode: 0 };
3685
+ }
3686
+ const value = args.includes("--off") ? false : args.includes("--on") ? true : void 0;
3687
+ if (value === void 0) return { exitCode: 1, error: "Usage: groundmail dmarc digest example.com --on|--off" };
3688
+ const result = await client2.dmarc.update(domain, { digest_enabled: value });
3689
+ console.log(json ? JSON.stringify(result) : `Weekly DMARC digest for ${domain} is ${value ? "on" : "off"}.`);
3690
+ return { exitCode: 0 };
3691
+ } catch (error) {
3692
+ return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
3693
+ }
3694
+ }
3695
+
3321
3696
  // src/help.ts
3322
3697
  var USAGE_LINES = [
3323
3698
  " groundmail login [--scope <scopes>] [--workspace <name>] sign in through the browser (OAuth; no key to copy)",
@@ -3326,20 +3701,24 @@ var USAGE_LINES = [
3326
3701
  " groundmail auth token [--scope <scopes>] print a short-lived access token (for agents and scripts)",
3327
3702
  " groundmail workspaces list",
3328
3703
  " groundmail workspaces use <name|id>",
3329
- " groundmail domains add example.com",
3704
+ " groundmail domains add example.com [--tenant <tenant>]",
3330
3705
  " groundmail domains apply example.com --provider cloudflare",
3331
3706
  " groundmail domains apply example.com --provider vercel",
3332
3707
  " groundmail domains apply example.com --provider godaddy",
3333
- " groundmail domains list",
3708
+ " groundmail domains list [--tenant <tenant>]",
3334
3709
  " groundmail domains get example.com",
3335
3710
  " groundmail domains update example.com --inbound true",
3336
3711
  " groundmail domains verify example.com",
3337
3712
  " groundmail domains delete example.com",
3713
+ " groundmail dmarc status example.com [--json] DMARC monitoring: sources, pass rates, readiness",
3714
+ " groundmail dmarc enable example.com [--apply cloudflare|vercel|godaddy] report address and the record to publish",
3715
+ " groundmail dmarc digest example.com --on|--off weekly digest email",
3716
+ " groundmail dmarc disable example.com",
3338
3717
  " groundmail received-emails list [--domain inbound.example.com]",
3339
3718
  " groundmail received-emails get inb_...",
3340
- ' groundmail emails send --from you@domain.com --to user@example.com --subject "Hello" --html "<p>Hi</p>" [--category transactional|product] [--unsubscribe]',
3341
- " groundmail emails batch --file ./emails.json",
3342
- " groundmail emails list [--limit 25] [--cursor \u2026] [--test | --live]",
3719
+ ' groundmail emails send --from you@domain.com --to user@example.com --subject "Hello" --html "<p>Hi</p>" [--category transactional|product] [--unsubscribe] [--tenant <tenant>]',
3720
+ " groundmail emails batch --file ./emails.json [--tenant <tenant>]",
3721
+ " groundmail emails list [--limit 25] [--cursor \u2026] [--test | --live] [--tenant <tenant>]",
3343
3722
  " groundmail emails insights [--window 7d|30d]",
3344
3723
  " groundmail emails get --id msg_\u2026",
3345
3724
  " groundmail emails cancel --id msg_\u2026",
@@ -3355,14 +3734,14 @@ var USAGE_LINES = [
3355
3734
  " groundmail automations pause --id <automation-id>",
3356
3735
  " groundmail automations delete --id <automation-id>",
3357
3736
  " groundmail webhooks list",
3358
- " groundmail webhooks create --url https://yourapp.com/hooks",
3737
+ " groundmail webhooks create --url https://yourapp.com/hooks [--tenants a,b] --tenants: only these tenants' email events",
3359
3738
  " groundmail webhooks get --id wh_\u2026",
3360
- " groundmail webhooks update --id wh_\u2026 [--url \u2026] [--event email.sent] [--enabled true|false]",
3739
+ " groundmail webhooks update --id wh_\u2026 [--url \u2026] [--event email.sent] [--enabled true|false] [--tenants a,b|all]",
3361
3740
  " groundmail webhooks delete --id <webhook-id>",
3362
3741
  " groundmail validate user@example.com Groundmail validation: verdict, reasons, typo suggestion (exit 2 when invalid)",
3363
- " groundmail suppressions list",
3364
- " groundmail suppressions add --email user@example.com [--reason \u2026]",
3365
- " groundmail suppressions remove --id <id> | --email user@example.com",
3742
+ " groundmail suppressions list [--tenant <tenant>]",
3743
+ " groundmail suppressions add --email user@example.com [--reason \u2026] [--tenant <tenant>]",
3744
+ " groundmail suppressions remove --id <id> | --email user@example.com [--tenant <tenant>]",
3366
3745
  " groundmail templates list [--status draft|published]",
3367
3746
  " groundmail templates starters",
3368
3747
  " groundmail templates from-starter password-reset",
@@ -3376,8 +3755,19 @@ var USAGE_LINES = [
3376
3755
  " groundmail templates test-send --id tpl_\u2026 --from you@domain.com --to you@domain.com",
3377
3756
  ' groundmail templates duplicate --id tpl_\u2026 [--name "Copy"]',
3378
3757
  " groundmail templates delete --id tpl_\u2026",
3758
+ " groundmail tenants list [--status active|paused] [--limit 25] [--cursor \u2026] your customers, each with its own domains, suppressions and limits",
3759
+ ` groundmail tenants create <external-id> [--name \u2026] [--default-from-domain \u2026] [--stream-policy all|transactional_only] [--daily-limit N] [--metadata '{"plan":"pro"}']`,
3760
+ " groundmail tenants get <tenant>",
3761
+ " groundmail tenants update <tenant> [--name \u2026] [--default-from-domain \u2026|none] [--stream-policy \u2026] [--daily-limit N|none] [--metadata JSON|null]",
3762
+ " groundmail tenants pause <tenant> [--reason \u2026] stops the tenant's new sends",
3763
+ " groundmail tenants resume <tenant>",
3764
+ " groundmail tenants delete <tenant> --yes",
3765
+ " groundmail tenants stats <tenant> [--days 7]",
3766
+ " groundmail tenants domains list <tenant>",
3767
+ " groundmail tenants domains add <tenant> example.com only this tenant can then send from it",
3768
+ " groundmail tenants domains remove <tenant> example.com gives the domain back to the account",
3379
3769
  " groundmail keys list",
3380
- " groundmail keys create [--name Default] [--scopes emails:send,domains:read] [--allowed-domains example.com] [--test] --test: a gm_test_ key, sends are simulated",
3770
+ " 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",
3381
3771
  " groundmail keys delete --id <api-key-id>",
3382
3772
  " groundmail migrate sendgrid [--dry-run] [--only templates,suppressions,domains,webhooks] [--region eu] import from SendGrid (Beta)",
3383
3773
  " groundmail migrate postmark [--dry-run] [--only \u2026] import from Postmark (Beta)",
@@ -3401,6 +3791,8 @@ var FOOTER_LINES = [
3401
3791
  "Scopes: full, emails:send, emails:read, domains:read, domains:write, webhooks:write, templates:write,",
3402
3792
  " suppressions:write, account:read",
3403
3793
  "",
3794
+ "<tenant>: the tenant's external id (your id for that customer) or its Groundmail id.",
3795
+ "",
3404
3796
  "Add --help to any command for just its usage. --version prints the CLI version."
3405
3797
  ];
3406
3798
  var CLI_VERSION = version;
@@ -3410,7 +3802,8 @@ var ALIASES = {
3410
3802
  events: { trigger: "send" },
3411
3803
  automations: { add: "create", remove: "delete" },
3412
3804
  suppressions: { create: "add", delete: "remove" },
3413
- templates: { add: "create", remove: "delete" }
3805
+ templates: { add: "create", remove: "delete" },
3806
+ tenants: { add: "create", remove: "delete" }
3414
3807
  };
3415
3808
  function words(line) {
3416
3809
  return line.trim().split(/\s+/);
@@ -3474,6 +3867,7 @@ async function main() {
3474
3867
  const rest = session.argv;
3475
3868
  if (command === "migrate") await finish(await runMigrateCommand(subcommand, rest));
3476
3869
  if (command === "validate") await finish(await runValidateCommand([...subcommand ? [subcommand] : [], ...rest]));
3870
+ if (command === "dmarc") await finish(await runDmarcCommand(subcommand, rest));
3477
3871
  if (command === "domains") {
3478
3872
  if (subcommand === "add" || subcommand === "create") {
3479
3873
  const result = await runAddCommand(rest);
@@ -3735,6 +4129,17 @@ async function main() {
3735
4129
  }
3736
4130
  }
3737
4131
  }
4132
+ if (command === "tenants") {
4133
+ if (subcommand === "list") await finish(await runListCommand8(rest));
4134
+ if (subcommand === "create" || subcommand === "add") await finish(await runCreateCommand4(rest));
4135
+ if (subcommand === "get") await finish(await runGetCommand7(rest));
4136
+ if (subcommand === "update") await finish(await runUpdateCommand5(rest));
4137
+ if (subcommand === "pause") await finish(await runPauseCommand2(rest));
4138
+ if (subcommand === "resume") await finish(await runResumeCommand(rest));
4139
+ if (subcommand === "delete" || subcommand === "remove") await finish(await runDeleteCommand5(rest));
4140
+ if (subcommand === "stats") await finish(await runStatsCommand(rest));
4141
+ if (subcommand === "domains") await finish(await runDomainsCommand(rest));
4142
+ }
3738
4143
  if (command === "keys") {
3739
4144
  if (subcommand === "list") {
3740
4145
  const result = await runListKeysCommandWithClient(rest);