groundmail-cli 0.20.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 +540 -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,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
|
|
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(
|
|
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) {
|
|
@@ -1075,13 +1086,46 @@ async function runListCommand4(args, env = process.env) {
|
|
|
1075
1086
|
}
|
|
1076
1087
|
}
|
|
1077
1088
|
|
|
1078
|
-
// 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
|
+
];
|
|
1079
1108
|
function readFlag12(args, flag) {
|
|
1080
1109
|
const index = args.indexOf(flag);
|
|
1081
1110
|
if (index === -1) return void 0;
|
|
1082
1111
|
return args[index + 1];
|
|
1083
1112
|
}
|
|
1084
|
-
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) {
|
|
1085
1129
|
return {
|
|
1086
1130
|
apiKey: readFlag12(args, "--api-key"),
|
|
1087
1131
|
baseUrl: readFlag12(args, "--base-url")
|
|
@@ -1090,6 +1134,82 @@ function parseWebhooksCommonArgs(args) {
|
|
|
1090
1134
|
function resolveApiKey5(options, env) {
|
|
1091
1135
|
return options.apiKey || env.GROUNDMAIL_API_KEY || null;
|
|
1092
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
|
+
}
|
|
1093
1213
|
async function createWebhooksClient(apiKey, baseUrl) {
|
|
1094
1214
|
const { Groundmail } = await import("groundmail");
|
|
1095
1215
|
return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
|
|
@@ -1098,17 +1218,18 @@ async function createWebhooksClient(apiKey, baseUrl) {
|
|
|
1098
1218
|
// src/commands/webhooks/create.ts
|
|
1099
1219
|
async function runCreateCommand2(args, env = process.env) {
|
|
1100
1220
|
const options = parseWebhooksCommonArgs(args);
|
|
1101
|
-
const apiKey =
|
|
1221
|
+
const apiKey = resolveApiKey6(options, env);
|
|
1102
1222
|
if (!apiKey) {
|
|
1103
1223
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
1104
1224
|
}
|
|
1105
|
-
const url =
|
|
1225
|
+
const url = readFlag13(args, "--url");
|
|
1106
1226
|
if (!url) {
|
|
1107
1227
|
return { exitCode: 1, error: "Required flag: --url" };
|
|
1108
1228
|
}
|
|
1109
1229
|
try {
|
|
1110
1230
|
const client2 = await createWebhooksClient(apiKey, options.baseUrl);
|
|
1111
|
-
const
|
|
1231
|
+
const tenants = parseTenantsList(readFlag13(args, "--tenants"));
|
|
1232
|
+
const result = await client2.webhooks.create({ url, ...tenants?.length ? { tenants } : {} });
|
|
1112
1233
|
console.log(JSON.stringify(result));
|
|
1113
1234
|
return { exitCode: 0 };
|
|
1114
1235
|
} catch (error) {
|
|
@@ -1120,9 +1241,9 @@ async function runCreateCommand2(args, env = process.env) {
|
|
|
1120
1241
|
var POSITIONAL_FLAGS5 = ["--api-key", "--base-url", "--id"];
|
|
1121
1242
|
async function runGetCommand5(args, env = process.env) {
|
|
1122
1243
|
const options = parseWebhooksCommonArgs(args);
|
|
1123
|
-
const apiKey =
|
|
1244
|
+
const apiKey = resolveApiKey6(options, env);
|
|
1124
1245
|
if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
1125
|
-
const id =
|
|
1246
|
+
const id = readFlag14(args, "--id") || readFirstPositional6(args);
|
|
1126
1247
|
if (!id) return { exitCode: 1, error: "Missing webhook id. Pass --id wh_\u2026" };
|
|
1127
1248
|
try {
|
|
1128
1249
|
const client2 = await createWebhooksClient(apiKey, options.baseUrl);
|
|
@@ -1132,7 +1253,7 @@ async function runGetCommand5(args, env = process.env) {
|
|
|
1132
1253
|
return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
|
|
1133
1254
|
}
|
|
1134
1255
|
}
|
|
1135
|
-
function
|
|
1256
|
+
function readFlag14(args, flag) {
|
|
1136
1257
|
const index = args.indexOf(flag);
|
|
1137
1258
|
return index === -1 ? void 0 : args[index + 1];
|
|
1138
1259
|
}
|
|
@@ -1151,11 +1272,11 @@ function readFirstPositional6(args) {
|
|
|
1151
1272
|
// src/commands/webhooks/delete.ts
|
|
1152
1273
|
async function runDeleteCommand3(args, env = process.env) {
|
|
1153
1274
|
const options = parseWebhooksCommonArgs(args);
|
|
1154
|
-
const apiKey =
|
|
1275
|
+
const apiKey = resolveApiKey6(options, env);
|
|
1155
1276
|
if (!apiKey) {
|
|
1156
1277
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
1157
1278
|
}
|
|
1158
|
-
const id =
|
|
1279
|
+
const id = readFlag13(args, "--id");
|
|
1159
1280
|
if (!id) {
|
|
1160
1281
|
return { exitCode: 1, error: "Required flag: --id" };
|
|
1161
1282
|
}
|
|
@@ -1172,7 +1293,7 @@ async function runDeleteCommand3(args, env = process.env) {
|
|
|
1172
1293
|
// src/commands/webhooks/list.ts
|
|
1173
1294
|
async function runListCommand5(args, env = process.env) {
|
|
1174
1295
|
const options = parseWebhooksCommonArgs(args);
|
|
1175
|
-
const apiKey =
|
|
1296
|
+
const apiKey = resolveApiKey6(options, env);
|
|
1176
1297
|
if (!apiKey) {
|
|
1177
1298
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
1178
1299
|
}
|
|
@@ -1187,24 +1308,26 @@ async function runListCommand5(args, env = process.env) {
|
|
|
1187
1308
|
}
|
|
1188
1309
|
|
|
1189
1310
|
// src/commands/webhooks/update.ts
|
|
1190
|
-
var POSITIONAL_FLAGS6 = ["--api-key", "--base-url", "--id", "--url", "--event", "--enabled"];
|
|
1311
|
+
var POSITIONAL_FLAGS6 = ["--api-key", "--base-url", "--id", "--url", "--event", "--enabled", "--tenants"];
|
|
1191
1312
|
async function runUpdateCommand3(args, env = process.env) {
|
|
1192
1313
|
const options = parseWebhooksCommonArgs(args);
|
|
1193
|
-
const apiKey =
|
|
1314
|
+
const apiKey = resolveApiKey6(options, env);
|
|
1194
1315
|
if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
1195
|
-
const id =
|
|
1316
|
+
const id = readFlag15(args, "--id") || readFirstPositional7(args);
|
|
1196
1317
|
if (!id) return { exitCode: 1, error: "Missing webhook id. Pass --id wh_\u2026" };
|
|
1197
|
-
const url =
|
|
1318
|
+
const url = readFlag15(args, "--url");
|
|
1198
1319
|
const events = readFlags2(args, "--event");
|
|
1199
|
-
const enabled = parseEnabledFlag(
|
|
1320
|
+
const enabled = parseEnabledFlag(readFlag15(args, "--enabled"));
|
|
1200
1321
|
if ("error" in enabled) return { exitCode: 1, error: enabled.error };
|
|
1201
|
-
|
|
1202
|
-
|
|
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." };
|
|
1203
1325
|
}
|
|
1204
1326
|
const payload = {
|
|
1205
1327
|
...url ? { url } : {},
|
|
1206
1328
|
...events.length > 0 ? { events } : {},
|
|
1207
|
-
...enabled.value !== void 0 ? { enabled: enabled.value } : {}
|
|
1329
|
+
...enabled.value !== void 0 ? { enabled: enabled.value } : {},
|
|
1330
|
+
...tenants !== void 0 ? { tenants } : {}
|
|
1208
1331
|
};
|
|
1209
1332
|
try {
|
|
1210
1333
|
const client2 = await createWebhooksClient(apiKey, options.baseUrl);
|
|
@@ -1214,7 +1337,7 @@ async function runUpdateCommand3(args, env = process.env) {
|
|
|
1214
1337
|
return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
|
|
1215
1338
|
}
|
|
1216
1339
|
}
|
|
1217
|
-
function
|
|
1340
|
+
function readFlag15(args, flag) {
|
|
1218
1341
|
const index = args.indexOf(flag);
|
|
1219
1342
|
return index === -1 ? void 0 : args[index + 1];
|
|
1220
1343
|
}
|
|
@@ -1244,22 +1367,22 @@ function parseEnabledFlag(value) {
|
|
|
1244
1367
|
}
|
|
1245
1368
|
|
|
1246
1369
|
// src/commands/keys/list.ts
|
|
1247
|
-
function
|
|
1370
|
+
function readFlag16(args, flag) {
|
|
1248
1371
|
const index = args.indexOf(flag);
|
|
1249
1372
|
if (index === -1) return void 0;
|
|
1250
1373
|
return args[index + 1];
|
|
1251
1374
|
}
|
|
1252
1375
|
async function runListKeysCommandWithClient(args, env = process.env) {
|
|
1253
|
-
const apiKey =
|
|
1254
|
-
const baseUrl =
|
|
1376
|
+
const apiKey = readFlag16(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
1377
|
+
const baseUrl = readFlag16(args, "--base-url");
|
|
1255
1378
|
if (!apiKey) {
|
|
1256
1379
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
1257
1380
|
}
|
|
1258
1381
|
const { Groundmail } = await import("groundmail");
|
|
1259
1382
|
const client2 = new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
|
|
1260
1383
|
try {
|
|
1261
|
-
const limitRaw =
|
|
1262
|
-
const cursor =
|
|
1384
|
+
const limitRaw = readFlag16(args, "--limit");
|
|
1385
|
+
const cursor = readFlag16(args, "--cursor");
|
|
1263
1386
|
const result = await client2.apiKeys.list({
|
|
1264
1387
|
...limitRaw ? { limit: Number(limitRaw) } : {},
|
|
1265
1388
|
...cursor ? { cursor } : {}
|
|
@@ -1297,7 +1420,7 @@ function trimUrl(value) {
|
|
|
1297
1420
|
}
|
|
1298
1421
|
|
|
1299
1422
|
// package.json
|
|
1300
|
-
var version = "0.
|
|
1423
|
+
var version = "0.22.0";
|
|
1301
1424
|
|
|
1302
1425
|
// src/user-agent.ts
|
|
1303
1426
|
function buildUserAgent(own) {
|
|
@@ -1381,15 +1504,15 @@ function parseArgs(argv, spec) {
|
|
|
1381
1504
|
const booleanFlags = new Set(spec.booleans ?? []);
|
|
1382
1505
|
const values = {};
|
|
1383
1506
|
const booleans = {};
|
|
1384
|
-
const
|
|
1507
|
+
const positionals2 = [];
|
|
1385
1508
|
for (let i = 0; i < argv.length; i++) {
|
|
1386
1509
|
const arg = argv[i];
|
|
1387
1510
|
if (arg === "--") {
|
|
1388
|
-
|
|
1511
|
+
positionals2.push(...argv.slice(i + 1));
|
|
1389
1512
|
break;
|
|
1390
1513
|
}
|
|
1391
1514
|
if (!arg.startsWith("--") || arg === "-") {
|
|
1392
|
-
|
|
1515
|
+
positionals2.push(arg);
|
|
1393
1516
|
continue;
|
|
1394
1517
|
}
|
|
1395
1518
|
const eq = arg.indexOf("=");
|
|
@@ -1415,7 +1538,7 @@ function parseArgs(argv, spec) {
|
|
|
1415
1538
|
const known = [...valueFlags, ...booleanFlags].sort().join(", ");
|
|
1416
1539
|
return { ok: false, error: `Unknown flag ${name}.${known ? ` This command takes: ${known}.` : ""}` };
|
|
1417
1540
|
}
|
|
1418
|
-
return { ok: true, args: { values, booleans, positionals } };
|
|
1541
|
+
return { ok: true, args: { values, booleans, positionals: positionals2 } };
|
|
1419
1542
|
}
|
|
1420
1543
|
function wantsJson(args, stdout = process.stdout) {
|
|
1421
1544
|
if (args.booleans["--json"] !== void 0) return args.booleans["--json"];
|
|
@@ -1723,10 +1846,10 @@ async function resolveAuth(flags, env = process.env, config = loadConfig(env)) {
|
|
|
1723
1846
|
return fromSaved(current.workspace);
|
|
1724
1847
|
}
|
|
1725
1848
|
async function applySavedSession(argv, env = process.env, config) {
|
|
1726
|
-
const
|
|
1849
|
+
const hasFlag2 = (flag) => argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
|
|
1727
1850
|
let workspace = null;
|
|
1728
1851
|
let notice;
|
|
1729
|
-
if (!
|
|
1852
|
+
if (!hasFlag2("--api-key") && !envApiKey(env)) {
|
|
1730
1853
|
const current = currentSavedWorkspace(config ?? loadConfig(env), env);
|
|
1731
1854
|
if (current.ok) {
|
|
1732
1855
|
workspace = current.workspace;
|
|
@@ -1737,7 +1860,7 @@ async function applySavedSession(argv, env = process.env, config) {
|
|
|
1737
1860
|
notice = current.error;
|
|
1738
1861
|
}
|
|
1739
1862
|
}
|
|
1740
|
-
if (
|
|
1863
|
+
if (hasFlag2("--base-url")) return { argv, notice };
|
|
1741
1864
|
const url = explicitApiUrl(env) || trimUrl(workspace?.apiUrl) || trimUrl(env.GROUNDMAIL_DEFAULT_API_URL);
|
|
1742
1865
|
return { argv: url ? [...argv, "--base-url", url] : argv, notice };
|
|
1743
1866
|
}
|
|
@@ -2134,28 +2257,29 @@ Usage: ${brand.bin} whoami [--workspace <name>] [--json]` };
|
|
|
2134
2257
|
}
|
|
2135
2258
|
|
|
2136
2259
|
// src/commands/keys/create.ts
|
|
2137
|
-
function
|
|
2260
|
+
function readFlag17(args, flag) {
|
|
2138
2261
|
const index = args.indexOf(flag);
|
|
2139
2262
|
if (index === -1) return void 0;
|
|
2140
2263
|
return args[index + 1];
|
|
2141
2264
|
}
|
|
2142
2265
|
async function runCreateKeyCommandWithClient(args, env = process.env) {
|
|
2143
|
-
const apiKey =
|
|
2144
|
-
const baseUrl =
|
|
2266
|
+
const apiKey = readFlag17(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
2267
|
+
const baseUrl = readFlag17(args, "--base-url") || defaultApiUrl(env);
|
|
2145
2268
|
if (!apiKey) {
|
|
2146
2269
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2147
2270
|
}
|
|
2148
|
-
const name =
|
|
2149
|
-
const scope =
|
|
2271
|
+
const name = readFlag17(args, "--name");
|
|
2272
|
+
const scope = readFlag17(args, "--scope");
|
|
2150
2273
|
if (scope && scope !== "full" && scope !== "sending") {
|
|
2151
2274
|
return { exitCode: 1, error: "--scope must be full or sending (or use --scopes for fine-grained scopes)" };
|
|
2152
2275
|
}
|
|
2153
|
-
const scopesFlag =
|
|
2276
|
+
const scopesFlag = readFlag17(args, "--scopes");
|
|
2154
2277
|
const scopes = scopesFlag !== void 0 ? parseScopeFlag(scopesFlag) : null;
|
|
2155
2278
|
if (scopes && !scopes.ok) return { exitCode: 1, error: scopes.error };
|
|
2156
|
-
const domainsFlag =
|
|
2279
|
+
const domainsFlag = readFlag17(args, "--allowed-domains");
|
|
2157
2280
|
const allowedDomains = domainsFlag?.split(/[\s,]+/).map((domain) => domain.trim()).filter(Boolean);
|
|
2158
2281
|
const test = args.includes("--test");
|
|
2282
|
+
const tenant = readFlag17(args, "--tenant");
|
|
2159
2283
|
try {
|
|
2160
2284
|
const response = await apiRequest(baseUrl, "/api-keys", {
|
|
2161
2285
|
apiKey,
|
|
@@ -2165,7 +2289,8 @@ async function runCreateKeyCommandWithClient(args, env = process.env) {
|
|
|
2165
2289
|
...scope && !scopes ? { scope } : {},
|
|
2166
2290
|
...scopes?.ok ? { scopes: scopes.scope.split(" ") } : {},
|
|
2167
2291
|
...allowedDomains?.length ? { allowed_domains: allowedDomains } : {},
|
|
2168
|
-
...test ? { test: true } : {}
|
|
2292
|
+
...test ? { test: true } : {},
|
|
2293
|
+
...tenant ? { tenant } : {}
|
|
2169
2294
|
}
|
|
2170
2295
|
});
|
|
2171
2296
|
if (!response.ok) {
|
|
@@ -2182,15 +2307,15 @@ async function runCreateKeyCommandWithClient(args, env = process.env) {
|
|
|
2182
2307
|
}
|
|
2183
2308
|
|
|
2184
2309
|
// src/commands/keys/delete.ts
|
|
2185
|
-
function
|
|
2310
|
+
function readFlag18(args, flag) {
|
|
2186
2311
|
const index = args.indexOf(flag);
|
|
2187
2312
|
if (index === -1) return void 0;
|
|
2188
2313
|
return args[index + 1];
|
|
2189
2314
|
}
|
|
2190
2315
|
async function runDeleteKeyCommandWithClient(args, env = process.env) {
|
|
2191
|
-
const apiKey =
|
|
2192
|
-
const baseUrl =
|
|
2193
|
-
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");
|
|
2194
2319
|
if (!apiKey) {
|
|
2195
2320
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2196
2321
|
}
|
|
@@ -2211,18 +2336,18 @@ async function runDeleteKeyCommandWithClient(args, env = process.env) {
|
|
|
2211
2336
|
}
|
|
2212
2337
|
|
|
2213
2338
|
// src/commands/suppressions/shared.ts
|
|
2214
|
-
function
|
|
2339
|
+
function readFlag19(args, flag) {
|
|
2215
2340
|
const index = args.indexOf(flag);
|
|
2216
2341
|
if (index === -1) return void 0;
|
|
2217
2342
|
return args[index + 1];
|
|
2218
2343
|
}
|
|
2219
2344
|
function parseSuppressionsCommonArgs(args) {
|
|
2220
2345
|
return {
|
|
2221
|
-
apiKey:
|
|
2222
|
-
baseUrl:
|
|
2346
|
+
apiKey: readFlag19(args, "--api-key"),
|
|
2347
|
+
baseUrl: readFlag19(args, "--base-url")
|
|
2223
2348
|
};
|
|
2224
2349
|
}
|
|
2225
|
-
function
|
|
2350
|
+
function resolveApiKey7(options, env) {
|
|
2226
2351
|
return options.apiKey || env.GROUNDMAIL_API_KEY || null;
|
|
2227
2352
|
}
|
|
2228
2353
|
async function createSuppressionsClient(apiKey, baseUrl) {
|
|
@@ -2233,19 +2358,21 @@ async function createSuppressionsClient(apiKey, baseUrl) {
|
|
|
2233
2358
|
// src/commands/suppressions/list.ts
|
|
2234
2359
|
async function runListCommand6(args, env = process.env) {
|
|
2235
2360
|
const options = parseSuppressionsCommonArgs(args);
|
|
2236
|
-
const apiKey =
|
|
2361
|
+
const apiKey = resolveApiKey7(options, env);
|
|
2237
2362
|
if (!apiKey) {
|
|
2238
2363
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2239
2364
|
}
|
|
2240
2365
|
try {
|
|
2241
2366
|
const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
|
|
2242
|
-
const limitRaw =
|
|
2243
|
-
const cursor =
|
|
2244
|
-
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");
|
|
2245
2371
|
const result = await client2.suppressions.list({
|
|
2246
2372
|
...limitRaw ? { limit: Number(limitRaw) } : {},
|
|
2247
2373
|
...cursor ? { cursor } : {},
|
|
2248
|
-
...email ? { email } : {}
|
|
2374
|
+
...email ? { email } : {},
|
|
2375
|
+
...tenant ? { tenant } : {}
|
|
2249
2376
|
});
|
|
2250
2377
|
console.log(JSON.stringify(result));
|
|
2251
2378
|
return { exitCode: 0 };
|
|
@@ -2257,20 +2384,22 @@ async function runListCommand6(args, env = process.env) {
|
|
|
2257
2384
|
// src/commands/suppressions/add.ts
|
|
2258
2385
|
async function runAddCommand2(args, env = process.env) {
|
|
2259
2386
|
const options = parseSuppressionsCommonArgs(args);
|
|
2260
|
-
const apiKey =
|
|
2387
|
+
const apiKey = resolveApiKey7(options, env);
|
|
2261
2388
|
if (!apiKey) {
|
|
2262
2389
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2263
2390
|
}
|
|
2264
|
-
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");
|
|
2265
2393
|
if (!email) {
|
|
2266
2394
|
return { exitCode: 1, error: "Missing email. Pass --email user@example.com" };
|
|
2267
2395
|
}
|
|
2268
2396
|
try {
|
|
2269
2397
|
const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
|
|
2270
|
-
const reason =
|
|
2398
|
+
const reason = readFlag19(args, "--reason");
|
|
2271
2399
|
const result = await client2.suppressions.create({
|
|
2272
2400
|
email,
|
|
2273
|
-
...reason ? { reason } : {}
|
|
2401
|
+
...reason ? { reason } : {},
|
|
2402
|
+
...tenant ? { tenant } : {}
|
|
2274
2403
|
});
|
|
2275
2404
|
console.log(JSON.stringify(result));
|
|
2276
2405
|
return { exitCode: 0 };
|
|
@@ -2282,17 +2411,20 @@ async function runAddCommand2(args, env = process.env) {
|
|
|
2282
2411
|
// src/commands/suppressions/remove.ts
|
|
2283
2412
|
async function runRemoveCommand(args, env = process.env) {
|
|
2284
2413
|
const options = parseSuppressionsCommonArgs(args);
|
|
2285
|
-
const apiKey =
|
|
2414
|
+
const apiKey = resolveApiKey7(options, env);
|
|
2286
2415
|
if (!apiKey) {
|
|
2287
2416
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2288
2417
|
}
|
|
2289
|
-
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
|
+
);
|
|
2290
2422
|
if (!idOrEmail) {
|
|
2291
2423
|
return { exitCode: 1, error: "Missing id or email. Pass --id <uuid> or --email user@example.com" };
|
|
2292
2424
|
}
|
|
2293
2425
|
try {
|
|
2294
2426
|
const client2 = await createSuppressionsClient(apiKey, options.baseUrl);
|
|
2295
|
-
const result = await client2.suppressions.remove(idOrEmail);
|
|
2427
|
+
const result = tenant ? await client2.suppressions.remove(idOrEmail, { tenant }) : await client2.suppressions.remove(idOrEmail);
|
|
2296
2428
|
console.log(JSON.stringify(result));
|
|
2297
2429
|
return { exitCode: 0 };
|
|
2298
2430
|
} catch (error) {
|
|
@@ -2301,18 +2433,18 @@ async function runRemoveCommand(args, env = process.env) {
|
|
|
2301
2433
|
}
|
|
2302
2434
|
|
|
2303
2435
|
// src/commands/templates/shared.ts
|
|
2304
|
-
function
|
|
2436
|
+
function readFlag20(args, flag) {
|
|
2305
2437
|
const index = args.indexOf(flag);
|
|
2306
2438
|
if (index === -1) return void 0;
|
|
2307
2439
|
return args[index + 1];
|
|
2308
2440
|
}
|
|
2309
2441
|
function parseTemplatesCommonArgs(args) {
|
|
2310
2442
|
return {
|
|
2311
|
-
apiKey:
|
|
2312
|
-
baseUrl:
|
|
2443
|
+
apiKey: readFlag20(args, "--api-key"),
|
|
2444
|
+
baseUrl: readFlag20(args, "--base-url")
|
|
2313
2445
|
};
|
|
2314
2446
|
}
|
|
2315
|
-
function
|
|
2447
|
+
function resolveApiKey8(options, env) {
|
|
2316
2448
|
return options.apiKey || env.GROUNDMAIL_API_KEY || null;
|
|
2317
2449
|
}
|
|
2318
2450
|
async function createTemplatesClient(apiKey, baseUrl) {
|
|
@@ -2324,9 +2456,9 @@ async function createTemplatesClient(apiKey, baseUrl) {
|
|
|
2324
2456
|
var POSITIONAL_FLAGS7 = ["--api-key", "--base-url", "--id", "--alias"];
|
|
2325
2457
|
async function runDeleteCommand4(args, env = process.env) {
|
|
2326
2458
|
const options = parseTemplatesCommonArgs(args);
|
|
2327
|
-
const apiKey =
|
|
2459
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2328
2460
|
if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2329
|
-
const idOrAlias =
|
|
2461
|
+
const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional8(args);
|
|
2330
2462
|
if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
|
|
2331
2463
|
try {
|
|
2332
2464
|
const client2 = await createTemplatesClient(apiKey, options.baseUrl);
|
|
@@ -2352,11 +2484,11 @@ function readFirstPositional8(args) {
|
|
|
2352
2484
|
var POSITIONAL_FLAGS8 = ["--api-key", "--base-url", "--id", "--alias", "--name"];
|
|
2353
2485
|
async function runDuplicateCommand(args, env = process.env) {
|
|
2354
2486
|
const options = parseTemplatesCommonArgs(args);
|
|
2355
|
-
const apiKey =
|
|
2487
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2356
2488
|
if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2357
|
-
const idOrAlias =
|
|
2489
|
+
const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional9(args);
|
|
2358
2490
|
if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
|
|
2359
|
-
const name =
|
|
2491
|
+
const name = readFlag20(args, "--name");
|
|
2360
2492
|
try {
|
|
2361
2493
|
const client2 = await createTemplatesClient(apiKey, options.baseUrl);
|
|
2362
2494
|
console.log(JSON.stringify(await client2.templates.duplicate(idOrAlias, name ? { name } : {})));
|
|
@@ -2381,9 +2513,9 @@ function readFirstPositional9(args) {
|
|
|
2381
2513
|
var POSITIONAL_FLAGS9 = ["--api-key", "--base-url", "--id", "--alias"];
|
|
2382
2514
|
async function runGetCommand6(args, env = process.env) {
|
|
2383
2515
|
const options = parseTemplatesCommonArgs(args);
|
|
2384
|
-
const apiKey =
|
|
2516
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2385
2517
|
if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2386
|
-
const idOrAlias =
|
|
2518
|
+
const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional10(args);
|
|
2387
2519
|
if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
|
|
2388
2520
|
try {
|
|
2389
2521
|
const client2 = await createTemplatesClient(apiKey, options.baseUrl);
|
|
@@ -2408,15 +2540,15 @@ function readFirstPositional10(args) {
|
|
|
2408
2540
|
// src/commands/templates/list.ts
|
|
2409
2541
|
async function runListCommand7(args, env = process.env) {
|
|
2410
2542
|
const options = parseTemplatesCommonArgs(args);
|
|
2411
|
-
const apiKey =
|
|
2543
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2412
2544
|
if (!apiKey) {
|
|
2413
2545
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2414
2546
|
}
|
|
2415
2547
|
try {
|
|
2416
2548
|
const client2 = await createTemplatesClient(apiKey, options.baseUrl);
|
|
2417
|
-
const limitRaw =
|
|
2418
|
-
const cursor =
|
|
2419
|
-
const status =
|
|
2549
|
+
const limitRaw = readFlag20(args, "--limit");
|
|
2550
|
+
const cursor = readFlag20(args, "--cursor");
|
|
2551
|
+
const status = readFlag20(args, "--status");
|
|
2420
2552
|
const result = await client2.templates.list({
|
|
2421
2553
|
...limitRaw ? { limit: Number(limitRaw) } : {},
|
|
2422
2554
|
...cursor ? { cursor } : {},
|
|
@@ -2433,9 +2565,9 @@ async function runListCommand7(args, env = process.env) {
|
|
|
2433
2565
|
var POSITIONAL_FLAGS10 = ["--api-key", "--base-url", "--id", "--alias"];
|
|
2434
2566
|
async function runPublishCommand(args, env = process.env) {
|
|
2435
2567
|
const options = parseTemplatesCommonArgs(args);
|
|
2436
|
-
const apiKey =
|
|
2568
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2437
2569
|
if (!apiKey) return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2438
|
-
const idOrAlias =
|
|
2570
|
+
const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional11(args);
|
|
2439
2571
|
if (!idOrAlias) return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
|
|
2440
2572
|
try {
|
|
2441
2573
|
const client2 = await createTemplatesClient(apiKey, options.baseUrl);
|
|
@@ -2574,23 +2706,23 @@ function statusOf(error) {
|
|
|
2574
2706
|
}
|
|
2575
2707
|
async function runPushCommand(args, env = process.env, deps = {}) {
|
|
2576
2708
|
const options = parseTemplatesCommonArgs(args);
|
|
2577
|
-
const apiKey =
|
|
2709
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2578
2710
|
if (!apiKey) {
|
|
2579
2711
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2580
2712
|
}
|
|
2581
|
-
const file =
|
|
2713
|
+
const file = readFlag20(args, "--file") ?? readPositional(args);
|
|
2582
2714
|
if (!file) {
|
|
2583
2715
|
return { exitCode: 1, error: "Missing template file. Usage: templates push <file> --alias <alias>" };
|
|
2584
2716
|
}
|
|
2585
|
-
const alias =
|
|
2717
|
+
const alias = readFlag20(args, "--alias");
|
|
2586
2718
|
if (!alias) {
|
|
2587
2719
|
return { exitCode: 1, error: "Missing --alias. The alias identifies which template to update." };
|
|
2588
2720
|
}
|
|
2589
|
-
const subject =
|
|
2590
|
-
const name =
|
|
2591
|
-
const preheader =
|
|
2592
|
-
const textFile =
|
|
2593
|
-
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");
|
|
2594
2726
|
const shouldPublish = args.includes("--publish");
|
|
2595
2727
|
let props = {};
|
|
2596
2728
|
if (propsRaw) {
|
|
@@ -2660,16 +2792,16 @@ async function runPushCommand(args, env = process.env, deps = {}) {
|
|
|
2660
2792
|
// src/commands/templates/create.ts
|
|
2661
2793
|
async function runCreateCommand3(args, env = process.env) {
|
|
2662
2794
|
const options = parseTemplatesCommonArgs(args);
|
|
2663
|
-
const apiKey =
|
|
2795
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2664
2796
|
if (!apiKey) {
|
|
2665
2797
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2666
2798
|
}
|
|
2667
|
-
const name =
|
|
2668
|
-
const subject =
|
|
2669
|
-
const html =
|
|
2670
|
-
const text =
|
|
2671
|
-
const alias =
|
|
2672
|
-
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");
|
|
2673
2805
|
const unsubscribeEnabled = args.includes("--unsubscribe-enabled");
|
|
2674
2806
|
if (!name) {
|
|
2675
2807
|
return { exitCode: 1, error: 'Missing name. Pass --name "Welcome"' };
|
|
@@ -2713,21 +2845,21 @@ var POSITIONAL_FLAGS11 = [
|
|
|
2713
2845
|
];
|
|
2714
2846
|
async function runUpdateCommand4(args, env = process.env) {
|
|
2715
2847
|
const options = parseTemplatesCommonArgs(args);
|
|
2716
|
-
const apiKey =
|
|
2848
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2717
2849
|
if (!apiKey) {
|
|
2718
2850
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2719
2851
|
}
|
|
2720
|
-
const idOrAlias =
|
|
2852
|
+
const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional12(args);
|
|
2721
2853
|
if (!idOrAlias) {
|
|
2722
2854
|
return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
|
|
2723
2855
|
}
|
|
2724
|
-
const name =
|
|
2725
|
-
const subject =
|
|
2726
|
-
const html =
|
|
2727
|
-
const text =
|
|
2728
|
-
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");
|
|
2729
2861
|
const variables = readFlags3(args, "--variable");
|
|
2730
|
-
const category =
|
|
2862
|
+
const category = readFlag20(args, "--category");
|
|
2731
2863
|
const unsubscribeEnabled = args.includes("--unsubscribe-enabled");
|
|
2732
2864
|
const unsubscribeDisabled = args.includes("--no-unsubscribe-enabled");
|
|
2733
2865
|
if (!name && !subject && !html && !text && !alias && variables.length === 0 && !category && !unsubscribeEnabled && !unsubscribeDisabled) {
|
|
@@ -2789,11 +2921,11 @@ function readFirstPositional13(args) {
|
|
|
2789
2921
|
}
|
|
2790
2922
|
async function runFromStarterCommand(args, env = process.env) {
|
|
2791
2923
|
const options = parseTemplatesCommonArgs(args);
|
|
2792
|
-
const apiKey =
|
|
2924
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2793
2925
|
if (!apiKey) {
|
|
2794
2926
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2795
2927
|
}
|
|
2796
|
-
const alias =
|
|
2928
|
+
const alias = readFlag20(args, "--alias") || readFirstPositional13(args);
|
|
2797
2929
|
if (!alias) {
|
|
2798
2930
|
return {
|
|
2799
2931
|
exitCode: 1,
|
|
@@ -2810,7 +2942,7 @@ async function runFromStarterCommand(args, env = process.env) {
|
|
|
2810
2942
|
}
|
|
2811
2943
|
async function runListStartersCommand(args, env = process.env) {
|
|
2812
2944
|
const options = parseTemplatesCommonArgs(args);
|
|
2813
|
-
const apiKey =
|
|
2945
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2814
2946
|
if (!apiKey) {
|
|
2815
2947
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2816
2948
|
}
|
|
@@ -2838,11 +2970,11 @@ function readFirstPositional14(args) {
|
|
|
2838
2970
|
}
|
|
2839
2971
|
async function runVersionsCommand(args, env = process.env) {
|
|
2840
2972
|
const options = parseTemplatesCommonArgs(args);
|
|
2841
|
-
const apiKey =
|
|
2973
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2842
2974
|
if (!apiKey) {
|
|
2843
2975
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2844
2976
|
}
|
|
2845
|
-
const idOrAlias =
|
|
2977
|
+
const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional14(args);
|
|
2846
2978
|
if (!idOrAlias) {
|
|
2847
2979
|
return { exitCode: 1, error: "Missing template id or alias. Pass --id tpl_\u2026 or --alias welcome" };
|
|
2848
2980
|
}
|
|
@@ -2856,12 +2988,12 @@ async function runVersionsCommand(args, env = process.env) {
|
|
|
2856
2988
|
}
|
|
2857
2989
|
async function runRestoreCommand(args, env = process.env) {
|
|
2858
2990
|
const options = parseTemplatesCommonArgs(args);
|
|
2859
|
-
const apiKey =
|
|
2991
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2860
2992
|
if (!apiKey) {
|
|
2861
2993
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2862
2994
|
}
|
|
2863
|
-
const idOrAlias =
|
|
2864
|
-
const versionRaw =
|
|
2995
|
+
const idOrAlias = readFlag20(args, "--id") || readFlag20(args, "--alias") || readFirstPositional14(args);
|
|
2996
|
+
const versionRaw = readFlag20(args, "--version");
|
|
2865
2997
|
const version2 = versionRaw ? Number.parseInt(versionRaw, 10) : NaN;
|
|
2866
2998
|
if (!idOrAlias || !Number.isFinite(version2) || version2 < 1) {
|
|
2867
2999
|
return {
|
|
@@ -2879,13 +3011,13 @@ async function runRestoreCommand(args, env = process.env) {
|
|
|
2879
3011
|
}
|
|
2880
3012
|
async function runTestSendCommand(args, env = process.env) {
|
|
2881
3013
|
const options = parseTemplatesCommonArgs(args);
|
|
2882
|
-
const apiKey =
|
|
3014
|
+
const apiKey = resolveApiKey8(options, env);
|
|
2883
3015
|
if (!apiKey) {
|
|
2884
3016
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2885
3017
|
}
|
|
2886
|
-
const idOrAlias =
|
|
2887
|
-
const from =
|
|
2888
|
-
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");
|
|
2889
3021
|
if (!idOrAlias || !from || !to) {
|
|
2890
3022
|
return {
|
|
2891
3023
|
exitCode: 1,
|
|
@@ -2902,18 +3034,18 @@ async function runTestSendCommand(args, env = process.env) {
|
|
|
2902
3034
|
}
|
|
2903
3035
|
|
|
2904
3036
|
// src/commands/integrations/shared.ts
|
|
2905
|
-
function
|
|
3037
|
+
function readFlag21(args, flag) {
|
|
2906
3038
|
const index = args.indexOf(flag);
|
|
2907
3039
|
if (index === -1) return void 0;
|
|
2908
3040
|
return args[index + 1];
|
|
2909
3041
|
}
|
|
2910
3042
|
function parseIntegrationsCommonArgs(args) {
|
|
2911
3043
|
return {
|
|
2912
|
-
apiKey:
|
|
2913
|
-
baseUrl:
|
|
3044
|
+
apiKey: readFlag21(args, "--api-key"),
|
|
3045
|
+
baseUrl: readFlag21(args, "--base-url")
|
|
2914
3046
|
};
|
|
2915
3047
|
}
|
|
2916
|
-
function
|
|
3048
|
+
function resolveApiKey9(options, env) {
|
|
2917
3049
|
return options.apiKey || env.GROUNDMAIL_API_KEY || null;
|
|
2918
3050
|
}
|
|
2919
3051
|
async function createIntegrationsClient(apiKey, baseUrl) {
|
|
@@ -2924,7 +3056,7 @@ async function createIntegrationsClient(apiKey, baseUrl) {
|
|
|
2924
3056
|
// src/commands/integrations/twilio.ts
|
|
2925
3057
|
async function runTwilioStatusCommand(args, env = process.env) {
|
|
2926
3058
|
const options = parseIntegrationsCommonArgs(args);
|
|
2927
|
-
const apiKey =
|
|
3059
|
+
const apiKey = resolveApiKey9(options, env);
|
|
2928
3060
|
if (!apiKey) {
|
|
2929
3061
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2930
3062
|
}
|
|
@@ -2939,14 +3071,14 @@ async function runTwilioStatusCommand(args, env = process.env) {
|
|
|
2939
3071
|
}
|
|
2940
3072
|
async function runTwilioConnectCommand(args, env = process.env) {
|
|
2941
3073
|
const options = parseIntegrationsCommonArgs(args);
|
|
2942
|
-
const apiKey =
|
|
3074
|
+
const apiKey = resolveApiKey9(options, env);
|
|
2943
3075
|
if (!apiKey) {
|
|
2944
3076
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2945
3077
|
}
|
|
2946
|
-
const accountSid =
|
|
2947
|
-
const authToken =
|
|
2948
|
-
const defaultFrom =
|
|
2949
|
-
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 || "";
|
|
2950
3082
|
if (!accountSid.trim() || !authToken.trim()) {
|
|
2951
3083
|
return {
|
|
2952
3084
|
exitCode: 1,
|
|
@@ -2975,7 +3107,7 @@ async function runTwilioConnectCommand(args, env = process.env) {
|
|
|
2975
3107
|
}
|
|
2976
3108
|
async function runTwilioDisconnectCommand(args, env = process.env) {
|
|
2977
3109
|
const options = parseIntegrationsCommonArgs(args);
|
|
2978
|
-
const apiKey =
|
|
3110
|
+
const apiKey = resolveApiKey9(options, env);
|
|
2979
3111
|
if (!apiKey) {
|
|
2980
3112
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY or pass --api-key." };
|
|
2981
3113
|
}
|
|
@@ -2989,6 +3121,125 @@ async function runTwilioDisconnectCommand(args, env = process.env) {
|
|
|
2989
3121
|
}
|
|
2990
3122
|
}
|
|
2991
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
|
+
|
|
2992
3243
|
// src/commands/auth.ts
|
|
2993
3244
|
async function runAuthCommand(subcommand, argv, deps = {}) {
|
|
2994
3245
|
const env = deps.env ?? process.env;
|
|
@@ -3135,7 +3386,7 @@ var KEY_ENV = {
|
|
|
3135
3386
|
};
|
|
3136
3387
|
var LABELS = { sendgrid: "SendGrid", postmark: "Postmark", mailgun: "Mailgun" };
|
|
3137
3388
|
var VALUE_FLAGS2 = /* @__PURE__ */ new Set(["--api-key", "--base-url"]);
|
|
3138
|
-
function
|
|
3389
|
+
function readFlag22(args, flag) {
|
|
3139
3390
|
const index = args.indexOf(flag);
|
|
3140
3391
|
if (index === -1) return void 0;
|
|
3141
3392
|
return args[index + 1];
|
|
@@ -3204,10 +3455,10 @@ function formatMigrationSummary(m) {
|
|
|
3204
3455
|
return lines.join("\n");
|
|
3205
3456
|
}
|
|
3206
3457
|
async function client(args, env) {
|
|
3207
|
-
const apiKey =
|
|
3458
|
+
const apiKey = readFlag22(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
3208
3459
|
if (!apiKey) return null;
|
|
3209
3460
|
const { Groundmail } = await import("groundmail");
|
|
3210
|
-
const baseUrl =
|
|
3461
|
+
const baseUrl = readFlag22(args, "--base-url");
|
|
3211
3462
|
return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
|
|
3212
3463
|
}
|
|
3213
3464
|
async function runStoredCommand(sub, args, env) {
|
|
@@ -3244,11 +3495,11 @@ async function runMigrateCommand(provider, args, env = process.env) {
|
|
|
3244
3495
|
};
|
|
3245
3496
|
}
|
|
3246
3497
|
const id = provider;
|
|
3247
|
-
const apiKey =
|
|
3498
|
+
const apiKey = readFlag22(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
3248
3499
|
if (!apiKey) {
|
|
3249
3500
|
return { exitCode: 1, error: "Not signed in. Run `groundmail login`, or set GROUNDMAIL_API_KEY." };
|
|
3250
3501
|
}
|
|
3251
|
-
const providerKey =
|
|
3502
|
+
const providerKey = readFlag22(args, "--key") || env[KEY_ENV[id]];
|
|
3252
3503
|
if (!providerKey) {
|
|
3253
3504
|
return {
|
|
3254
3505
|
exitCode: 1,
|
|
@@ -3257,14 +3508,14 @@ async function runMigrateCommand(provider, args, env = process.env) {
|
|
|
3257
3508
|
}
|
|
3258
3509
|
const params = { provider: id, api_key: providerKey };
|
|
3259
3510
|
if (id === "postmark") {
|
|
3260
|
-
const accountToken =
|
|
3511
|
+
const accountToken = readFlag22(args, "--account-token") || env.POSTMARK_ACCOUNT_TOKEN;
|
|
3261
3512
|
if (accountToken) params.account_api_key = accountToken;
|
|
3262
3513
|
} else {
|
|
3263
|
-
const region =
|
|
3514
|
+
const region = readFlag22(args, "--region");
|
|
3264
3515
|
if (region === "us" || region === "eu") params.region = region;
|
|
3265
3516
|
else if (region) return { exitCode: 1, error: "--region must be us or eu" };
|
|
3266
3517
|
}
|
|
3267
|
-
const only =
|
|
3518
|
+
const only = readFlag22(args, "--only");
|
|
3268
3519
|
if (only) {
|
|
3269
3520
|
const list = only.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3270
3521
|
const bad = list.filter((r) => !RESOURCES.includes(r));
|
|
@@ -3276,7 +3527,7 @@ async function runMigrateCommand(provider, args, env = process.env) {
|
|
|
3276
3527
|
if (args.includes("--dry-run")) params.dry_run = true;
|
|
3277
3528
|
try {
|
|
3278
3529
|
const { Groundmail } = await import("groundmail");
|
|
3279
|
-
const baseUrl =
|
|
3530
|
+
const baseUrl = readFlag22(args, "--base-url");
|
|
3280
3531
|
const client2 = new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
|
|
3281
3532
|
console.error(`${params.dry_run ? "Previewing" : "Importing"} from ${LABELS[id]} (Beta)\u2026`);
|
|
3282
3533
|
const report = await client2.migrations.run(params, {
|
|
@@ -3299,12 +3550,12 @@ async function runMigrateCommand(provider, args, env = process.env) {
|
|
|
3299
3550
|
// src/commands/validate.ts
|
|
3300
3551
|
async function runValidateCommand(args, env = process.env) {
|
|
3301
3552
|
const options = parseSuppressionsCommonArgs(args);
|
|
3302
|
-
const apiKey =
|
|
3553
|
+
const apiKey = resolveApiKey7(options, env);
|
|
3303
3554
|
if (!apiKey) {
|
|
3304
3555
|
return { exitCode: 1, error: "Missing API key. Set GROUNDMAIL_API_KEY, pass --api-key, or run `groundmail login`." };
|
|
3305
3556
|
}
|
|
3306
3557
|
const flagValues = new Set([options.apiKey, options.baseUrl].filter(Boolean));
|
|
3307
|
-
const email =
|
|
3558
|
+
const email = readFlag19(args, "--email") || args.find((arg) => arg.includes("@") && !arg.startsWith("-") && !flagValues.has(arg));
|
|
3308
3559
|
if (!email) {
|
|
3309
3560
|
return { exitCode: 1, error: "Missing email. Usage: groundmail validate user@example.com" };
|
|
3310
3561
|
}
|
|
@@ -3318,6 +3569,109 @@ async function runValidateCommand(args, env = process.env) {
|
|
|
3318
3569
|
}
|
|
3319
3570
|
}
|
|
3320
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
|
+
|
|
3321
3675
|
// src/help.ts
|
|
3322
3676
|
var USAGE_LINES = [
|
|
3323
3677
|
" groundmail login [--scope <scopes>] [--workspace <name>] sign in through the browser (OAuth; no key to copy)",
|
|
@@ -3326,20 +3680,24 @@ var USAGE_LINES = [
|
|
|
3326
3680
|
" groundmail auth token [--scope <scopes>] print a short-lived access token (for agents and scripts)",
|
|
3327
3681
|
" groundmail workspaces list",
|
|
3328
3682
|
" groundmail workspaces use <name|id>",
|
|
3329
|
-
" groundmail domains add example.com",
|
|
3683
|
+
" groundmail domains add example.com [--tenant <tenant>]",
|
|
3330
3684
|
" groundmail domains apply example.com --provider cloudflare",
|
|
3331
3685
|
" groundmail domains apply example.com --provider vercel",
|
|
3332
3686
|
" groundmail domains apply example.com --provider godaddy",
|
|
3333
|
-
" groundmail domains list",
|
|
3687
|
+
" groundmail domains list [--tenant <tenant>]",
|
|
3334
3688
|
" groundmail domains get example.com",
|
|
3335
3689
|
" groundmail domains update example.com --inbound true",
|
|
3336
3690
|
" groundmail domains verify example.com",
|
|
3337
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",
|
|
3338
3696
|
" groundmail received-emails list [--domain inbound.example.com]",
|
|
3339
3697
|
" 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]",
|
|
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>]",
|
|
3343
3701
|
" groundmail emails insights [--window 7d|30d]",
|
|
3344
3702
|
" groundmail emails get --id msg_\u2026",
|
|
3345
3703
|
" groundmail emails cancel --id msg_\u2026",
|
|
@@ -3355,14 +3713,14 @@ var USAGE_LINES = [
|
|
|
3355
3713
|
" groundmail automations pause --id <automation-id>",
|
|
3356
3714
|
" groundmail automations delete --id <automation-id>",
|
|
3357
3715
|
" groundmail webhooks list",
|
|
3358
|
-
" 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",
|
|
3359
3717
|
" groundmail webhooks get --id wh_\u2026",
|
|
3360
|
-
" 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]",
|
|
3361
3719
|
" groundmail webhooks delete --id <webhook-id>",
|
|
3362
3720
|
" 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",
|
|
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>]",
|
|
3366
3724
|
" groundmail templates list [--status draft|published]",
|
|
3367
3725
|
" groundmail templates starters",
|
|
3368
3726
|
" groundmail templates from-starter password-reset",
|
|
@@ -3376,8 +3734,19 @@ var USAGE_LINES = [
|
|
|
3376
3734
|
" groundmail templates test-send --id tpl_\u2026 --from you@domain.com --to you@domain.com",
|
|
3377
3735
|
' groundmail templates duplicate --id tpl_\u2026 [--name "Copy"]',
|
|
3378
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",
|
|
3379
3748
|
" 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",
|
|
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",
|
|
3381
3750
|
" groundmail keys delete --id <api-key-id>",
|
|
3382
3751
|
" groundmail migrate sendgrid [--dry-run] [--only templates,suppressions,domains,webhooks] [--region eu] import from SendGrid (Beta)",
|
|
3383
3752
|
" groundmail migrate postmark [--dry-run] [--only \u2026] import from Postmark (Beta)",
|
|
@@ -3401,6 +3770,8 @@ var FOOTER_LINES = [
|
|
|
3401
3770
|
"Scopes: full, emails:send, emails:read, domains:read, domains:write, webhooks:write, templates:write,",
|
|
3402
3771
|
" suppressions:write, account:read",
|
|
3403
3772
|
"",
|
|
3773
|
+
"<tenant>: the tenant's external id (your id for that customer) or its Groundmail id.",
|
|
3774
|
+
"",
|
|
3404
3775
|
"Add --help to any command for just its usage. --version prints the CLI version."
|
|
3405
3776
|
];
|
|
3406
3777
|
var CLI_VERSION = version;
|
|
@@ -3410,7 +3781,8 @@ var ALIASES = {
|
|
|
3410
3781
|
events: { trigger: "send" },
|
|
3411
3782
|
automations: { add: "create", remove: "delete" },
|
|
3412
3783
|
suppressions: { create: "add", delete: "remove" },
|
|
3413
|
-
templates: { add: "create", remove: "delete" }
|
|
3784
|
+
templates: { add: "create", remove: "delete" },
|
|
3785
|
+
tenants: { add: "create", remove: "delete" }
|
|
3414
3786
|
};
|
|
3415
3787
|
function words(line) {
|
|
3416
3788
|
return line.trim().split(/\s+/);
|
|
@@ -3474,6 +3846,7 @@ async function main() {
|
|
|
3474
3846
|
const rest = session.argv;
|
|
3475
3847
|
if (command === "migrate") await finish(await runMigrateCommand(subcommand, rest));
|
|
3476
3848
|
if (command === "validate") await finish(await runValidateCommand([...subcommand ? [subcommand] : [], ...rest]));
|
|
3849
|
+
if (command === "dmarc") await finish(await runDmarcCommand(subcommand, rest));
|
|
3477
3850
|
if (command === "domains") {
|
|
3478
3851
|
if (subcommand === "add" || subcommand === "create") {
|
|
3479
3852
|
const result = await runAddCommand(rest);
|
|
@@ -3735,6 +4108,17 @@ async function main() {
|
|
|
3735
4108
|
}
|
|
3736
4109
|
}
|
|
3737
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
|
+
}
|
|
3738
4122
|
if (command === "keys") {
|
|
3739
4123
|
if (subcommand === "list") {
|
|
3740
4124
|
const result = await runListKeysCommandWithClient(rest);
|