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.
- package/README.md +18 -0
- package/dist/cli.js +554 -156
- 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
|
|
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(
|
|
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/
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1318
|
+
const url = readFlag15(args, "--url");
|
|
1189
1319
|
const events = readFlags2(args, "--event");
|
|
1190
|
-
const enabled = parseEnabledFlag(
|
|
1320
|
+
const enabled = parseEnabledFlag(readFlag15(args, "--enabled"));
|
|
1191
1321
|
if ("error" in enabled) return { exitCode: 1, error: enabled.error };
|
|
1192
|
-
|
|
1193
|
-
|
|
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
|
|
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
|
|
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 =
|
|
1245
|
-
const baseUrl =
|
|
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 =
|
|
1253
|
-
const 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.
|
|
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
|
|
1507
|
+
const positionals2 = [];
|
|
1376
1508
|
for (let i = 0; i < argv.length; i++) {
|
|
1377
1509
|
const arg = argv[i];
|
|
1378
1510
|
if (arg === "--") {
|
|
1379
|
-
|
|
1511
|
+
positionals2.push(...argv.slice(i + 1));
|
|
1380
1512
|
break;
|
|
1381
1513
|
}
|
|
1382
1514
|
if (!arg.startsWith("--") || arg === "-") {
|
|
1383
|
-
|
|
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
|
|
1849
|
+
const hasFlag2 = (flag) => argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
|
|
1718
1850
|
let workspace = null;
|
|
1719
1851
|
let notice;
|
|
1720
|
-
if (!
|
|
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 (
|
|
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
|
|
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 =
|
|
2135
|
-
const baseUrl =
|
|
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 =
|
|
2140
|
-
const 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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
2178
|
-
const baseUrl =
|
|
2179
|
-
const 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
|
|
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:
|
|
2208
|
-
baseUrl:
|
|
2346
|
+
apiKey: readFlag19(args, "--api-key"),
|
|
2347
|
+
baseUrl: readFlag19(args, "--base-url")
|
|
2209
2348
|
};
|
|
2210
2349
|
}
|
|
2211
|
-
function
|
|
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 =
|
|
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 =
|
|
2229
|
-
const cursor =
|
|
2230
|
-
const 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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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:
|
|
2298
|
-
baseUrl:
|
|
2443
|
+
apiKey: readFlag20(args, "--api-key"),
|
|
2444
|
+
baseUrl: readFlag20(args, "--base-url")
|
|
2299
2445
|
};
|
|
2300
2446
|
}
|
|
2301
|
-
function
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2404
|
-
const cursor =
|
|
2405
|
-
const 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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2576
|
-
const name =
|
|
2577
|
-
const preheader =
|
|
2578
|
-
const textFile =
|
|
2579
|
-
const propsRaw =
|
|
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 =
|
|
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 =
|
|
2654
|
-
const subject =
|
|
2655
|
-
const html =
|
|
2656
|
-
const text =
|
|
2657
|
-
const alias =
|
|
2658
|
-
const 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 =
|
|
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 =
|
|
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 =
|
|
2711
|
-
const subject =
|
|
2712
|
-
const html =
|
|
2713
|
-
const text =
|
|
2714
|
-
const 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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2850
|
-
const versionRaw =
|
|
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 =
|
|
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 =
|
|
2873
|
-
const from =
|
|
2874
|
-
const 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
|
|
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:
|
|
2899
|
-
baseUrl:
|
|
3044
|
+
apiKey: readFlag21(args, "--api-key"),
|
|
3045
|
+
baseUrl: readFlag21(args, "--base-url")
|
|
2900
3046
|
};
|
|
2901
3047
|
}
|
|
2902
|
-
function
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2933
|
-
const authToken =
|
|
2934
|
-
const defaultFrom =
|
|
2935
|
-
const messagingServiceSid =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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);
|