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