@brew.new/cli 0.8.0 → 0.9.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/dist/bin.js +730 -174
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -281,7 +281,7 @@ function shellQuote(token) {
|
|
|
281
281
|
|
|
282
282
|
// src/version.ts
|
|
283
283
|
var CLI_NAME = "brew-cli";
|
|
284
|
-
var CLI_VERSION = "0.
|
|
284
|
+
var CLI_VERSION = "0.9.0" ;
|
|
285
285
|
var CONFIG_KEYS = ["apiKey", "brandId", "apiUrl"];
|
|
286
286
|
function isConfigKey(value) {
|
|
287
287
|
return CONFIG_KEYS.includes(value);
|
|
@@ -653,6 +653,10 @@ var IDEMPOTENCY_FLAG = {
|
|
|
653
653
|
flag: "--idempotency-key <key>",
|
|
654
654
|
summary: "Idempotency-Key for safe retries (auto-generated otherwise)"
|
|
655
655
|
};
|
|
656
|
+
var NON_REPLAYING_IDEMPOTENCY_FLAG = {
|
|
657
|
+
flag: "--idempotency-key <key>",
|
|
658
|
+
summary: "Accepted for compatibility only: this route never replays, so a retry runs again"
|
|
659
|
+
};
|
|
656
660
|
var INPUT_FLAG = {
|
|
657
661
|
flag: "--input <json>",
|
|
658
662
|
summary: "Full JSON request body, or - to read stdin (flags override it)"
|
|
@@ -857,6 +861,201 @@ var analyticsCampaignsCommand = defineCommand({
|
|
|
857
861
|
}
|
|
858
862
|
});
|
|
859
863
|
|
|
864
|
+
// src/lib/raw-request.ts
|
|
865
|
+
async function rawRequest(ctx, request) {
|
|
866
|
+
const auth = resolveAuth({
|
|
867
|
+
globals: ctx.globals,
|
|
868
|
+
env: ctx.io.env,
|
|
869
|
+
...request.allowAnonymous === true ? { allowAnonymous: true } : {}
|
|
870
|
+
});
|
|
871
|
+
const hasBody = request.body !== void 0;
|
|
872
|
+
const idempotencyKey = resolveRawIdempotencyKey(
|
|
873
|
+
request.method,
|
|
874
|
+
request.idempotencyKey
|
|
875
|
+
);
|
|
876
|
+
const headers = buildRawHeaders({
|
|
877
|
+
auth,
|
|
878
|
+
path: request.path,
|
|
879
|
+
hasJsonBody: hasBody,
|
|
880
|
+
idempotencyKey
|
|
881
|
+
});
|
|
882
|
+
const url = new URL(`${auth.apiUrl}${request.path}`);
|
|
883
|
+
for (const [key, value] of Object.entries(request.query ?? {})) {
|
|
884
|
+
if (value !== void 0) {
|
|
885
|
+
url.searchParams.set(key, value);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
const response = await fetch(url, {
|
|
889
|
+
method: request.method,
|
|
890
|
+
headers,
|
|
891
|
+
...hasBody ? { body: JSON.stringify(request.body) } : {},
|
|
892
|
+
...request.signal === void 0 ? {} : { signal: request.signal }
|
|
893
|
+
});
|
|
894
|
+
const text = await response.text();
|
|
895
|
+
const parsed = tryParseJson(text);
|
|
896
|
+
if (!response.ok) {
|
|
897
|
+
throw responseToApiError(response, parsed);
|
|
898
|
+
}
|
|
899
|
+
if (parsed === void 0) {
|
|
900
|
+
throw new CliApiError({
|
|
901
|
+
status: response.status,
|
|
902
|
+
code: "UNEXPECTED_RESPONSE",
|
|
903
|
+
type: "internal_error",
|
|
904
|
+
message: "The API returned a non-JSON success response."
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
return parsed;
|
|
908
|
+
}
|
|
909
|
+
function resolveRawIdempotencyKey(method, provided) {
|
|
910
|
+
if (provided !== void 0 && provided !== "") {
|
|
911
|
+
return provided;
|
|
912
|
+
}
|
|
913
|
+
return method === "POST" ? crypto.randomUUID() : void 0;
|
|
914
|
+
}
|
|
915
|
+
function buildRawHeaders(input) {
|
|
916
|
+
const headers = new Headers({
|
|
917
|
+
authorization: `Bearer ${input.auth.apiKey}`,
|
|
918
|
+
"user-agent": `${CLI_NAME}/${CLI_VERSION}`,
|
|
919
|
+
accept: "application/json, text/plain;q=0.9"
|
|
920
|
+
});
|
|
921
|
+
if (input.hasJsonBody) {
|
|
922
|
+
headers.set("content-type", "application/json");
|
|
923
|
+
}
|
|
924
|
+
if (input.auth.brandId !== void 0 && !isOrgLevelPath(input.path)) {
|
|
925
|
+
headers.set("x-brand-id", input.auth.brandId);
|
|
926
|
+
}
|
|
927
|
+
if (input.idempotencyKey !== void 0 && input.idempotencyKey !== "") {
|
|
928
|
+
headers.set("idempotency-key", input.idempotencyKey);
|
|
929
|
+
}
|
|
930
|
+
return headers;
|
|
931
|
+
}
|
|
932
|
+
function responseToApiError(response, parsed) {
|
|
933
|
+
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
934
|
+
const record = isRecord2(parsed) ? parsed : void 0;
|
|
935
|
+
const standard = record !== void 0 && isRecord2(record.error) ? record.error : void 0;
|
|
936
|
+
const envelope = standard ?? record;
|
|
937
|
+
const readField = (key) => {
|
|
938
|
+
const value = envelope?.[key];
|
|
939
|
+
return typeof value === "string" ? value : void 0;
|
|
940
|
+
};
|
|
941
|
+
const param = readField("param");
|
|
942
|
+
const suggestion = readField("suggestion") ?? (envelope === void 0 ? void 0 : suggestionForStatus(response.status));
|
|
943
|
+
const docs = readField("docs");
|
|
944
|
+
const details = isRecord2(envelope?.details) ? envelope.details : void 0;
|
|
945
|
+
return new CliApiError({
|
|
946
|
+
status: response.status,
|
|
947
|
+
code: readField("code") ?? "HTTP_ERROR",
|
|
948
|
+
type: readField("type") ?? errorTypeForStatus(response.status),
|
|
949
|
+
message: readField("message") ?? `Request failed with status ${response.status}`,
|
|
950
|
+
...param === void 0 ? {} : { param },
|
|
951
|
+
...suggestion === void 0 ? {} : { suggestion },
|
|
952
|
+
...docs === void 0 ? {} : { docs },
|
|
953
|
+
...requestId === void 0 ? {} : { requestId },
|
|
954
|
+
...details === void 0 ? {} : { details },
|
|
955
|
+
...parsed === void 0 ? {} : { body: parsed }
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
function isRecord2(value) {
|
|
959
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
960
|
+
}
|
|
961
|
+
function tryParseJson(text) {
|
|
962
|
+
try {
|
|
963
|
+
return JSON.parse(text);
|
|
964
|
+
} catch {
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// src/commands/analytics/event-counts.ts
|
|
969
|
+
var analyticsEventCountsCommand = defineCommand({
|
|
970
|
+
path: ["analytics", "event-counts"],
|
|
971
|
+
summary: "Count email events per field and/or period (clicks per link, events per day, unsubscribe reasons)",
|
|
972
|
+
sdkMethod: null,
|
|
973
|
+
isRawTransport: true,
|
|
974
|
+
route: { method: "GET", path: "/v1/analytics/events" },
|
|
975
|
+
commandClass: "read",
|
|
976
|
+
flags: [
|
|
977
|
+
{
|
|
978
|
+
flag: "--group-by <fields>",
|
|
979
|
+
summary: "One or two comma-separated fields: eventType, emailId, automationId, sendId, source, link, recipientDomain, unsubscribeReason"
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
flag: "--bucket <period>",
|
|
983
|
+
summary: "Count per UTC day, week (Monday start) or month"
|
|
984
|
+
},
|
|
985
|
+
{ flag: "--since <datetime>", summary: "Window start (ISO-8601)" },
|
|
986
|
+
{ flag: "--until <datetime>", summary: "Window end (ISO-8601)" },
|
|
987
|
+
{
|
|
988
|
+
flag: "--event-type <type>",
|
|
989
|
+
summary: "Only one email event type (e.g. clicked)"
|
|
990
|
+
},
|
|
991
|
+
{ flag: "--send-id <sendId>", summary: "Only one send" },
|
|
992
|
+
{ flag: "--email-id <emailId>", summary: "Only one design" },
|
|
993
|
+
{
|
|
994
|
+
flag: "--recipient <rules>",
|
|
995
|
+
summary: "CSV of recipient rules (max 10): an address, @domain, or substring; prefix ! to exclude"
|
|
996
|
+
},
|
|
997
|
+
INPUT_FLAG
|
|
998
|
+
],
|
|
999
|
+
examples: [
|
|
1000
|
+
"brew-cli analytics event-counts --send-id snd_123 --event-type clicked --group-by link",
|
|
1001
|
+
"brew-cli analytics event-counts --group-by eventType --bucket day",
|
|
1002
|
+
"brew-cli analytics event-counts --group-by unsubscribeReason --json"
|
|
1003
|
+
],
|
|
1004
|
+
run: async ({ ctx, flags }) => {
|
|
1005
|
+
const base = await readJsonFlag(ctx, flags.input, "--input");
|
|
1006
|
+
const input = mergeInput(base, {
|
|
1007
|
+
groupBy: flagString(flags.groupBy),
|
|
1008
|
+
bucket: flagString(flags.bucket),
|
|
1009
|
+
from: flagString(flags.since),
|
|
1010
|
+
to: flagString(flags.until),
|
|
1011
|
+
eventType: flagString(flags.eventType),
|
|
1012
|
+
sendId: flagString(flags.sendId),
|
|
1013
|
+
emailId: flagString(flags.emailId),
|
|
1014
|
+
recipient: flagString(flags.recipient)
|
|
1015
|
+
});
|
|
1016
|
+
if (input.groupBy === void 0 && input.bucket === void 0) {
|
|
1017
|
+
throw new CliUsageError(
|
|
1018
|
+
"Pass --group-by, --bucket, or both (`analytics events` lists the rows)."
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
const query = {};
|
|
1022
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1023
|
+
if (key === "cursor" || key === "limit") {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
query[key] = value === void 0 || value === null ? void 0 : Array.isArray(value) ? value.map(String).join(",") : String(value);
|
|
1027
|
+
}
|
|
1028
|
+
const body = await rawRequest(ctx, {
|
|
1029
|
+
method: "GET",
|
|
1030
|
+
path: "/v1/analytics/events",
|
|
1031
|
+
query
|
|
1032
|
+
});
|
|
1033
|
+
return { data: body, human: renderCounts(body) };
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
function renderCounts(body) {
|
|
1037
|
+
if (body.groups.length === 0) {
|
|
1038
|
+
return `No events counted (total ${body.count}).`;
|
|
1039
|
+
}
|
|
1040
|
+
const rows = body.groups.map((group) => ({
|
|
1041
|
+
group: Object.entries(group.key ?? {}).map(([field, value]) => `${field}=${String(value)}`).join(" "),
|
|
1042
|
+
...group.bucket === void 0 ? {} : { bucket: group.bucket },
|
|
1043
|
+
count: group.count
|
|
1044
|
+
}));
|
|
1045
|
+
const table = renderTable(rows, [
|
|
1046
|
+
{ key: "group", header: "GROUP" },
|
|
1047
|
+
...rows.some((row) => "bucket" in row) ? [{ key: "bucket", header: "PERIOD" }] : [],
|
|
1048
|
+
{ key: "count", header: "COUNT" }
|
|
1049
|
+
]);
|
|
1050
|
+
const footer = [
|
|
1051
|
+
`total ${body.count}`,
|
|
1052
|
+
body.otherCount > 0 ? `${body.otherCount} in unlisted groups` : "",
|
|
1053
|
+
body.truncated ? `truncated (counted from ${body.coveredFrom ?? "?"})` : ""
|
|
1054
|
+
].filter(Boolean).join(" \xB7 ");
|
|
1055
|
+
return `${table}
|
|
1056
|
+
${footer}`;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
860
1059
|
// src/commands/analytics/events.ts
|
|
861
1060
|
var analyticsEventsCommand = defineCommand({
|
|
862
1061
|
path: ["analytics", "events"],
|
|
@@ -1205,110 +1404,6 @@ var analyticsTriggerInstancesListCommand = defineCommand({
|
|
|
1205
1404
|
}
|
|
1206
1405
|
});
|
|
1207
1406
|
|
|
1208
|
-
// src/lib/raw-request.ts
|
|
1209
|
-
async function rawRequest(ctx, request) {
|
|
1210
|
-
const auth = resolveAuth({
|
|
1211
|
-
globals: ctx.globals,
|
|
1212
|
-
env: ctx.io.env,
|
|
1213
|
-
...request.allowAnonymous === true ? { allowAnonymous: true } : {}
|
|
1214
|
-
});
|
|
1215
|
-
const hasBody = request.body !== void 0;
|
|
1216
|
-
const idempotencyKey = resolveRawIdempotencyKey(
|
|
1217
|
-
request.method,
|
|
1218
|
-
request.idempotencyKey
|
|
1219
|
-
);
|
|
1220
|
-
const headers = buildRawHeaders({
|
|
1221
|
-
auth,
|
|
1222
|
-
path: request.path,
|
|
1223
|
-
hasJsonBody: hasBody,
|
|
1224
|
-
idempotencyKey
|
|
1225
|
-
});
|
|
1226
|
-
const url = new URL(`${auth.apiUrl}${request.path}`);
|
|
1227
|
-
for (const [key, value] of Object.entries(request.query ?? {})) {
|
|
1228
|
-
if (value !== void 0) {
|
|
1229
|
-
url.searchParams.set(key, value);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
const response = await fetch(url, {
|
|
1233
|
-
method: request.method,
|
|
1234
|
-
headers,
|
|
1235
|
-
...hasBody ? { body: JSON.stringify(request.body) } : {},
|
|
1236
|
-
...request.signal === void 0 ? {} : { signal: request.signal }
|
|
1237
|
-
});
|
|
1238
|
-
const text = await response.text();
|
|
1239
|
-
const parsed = tryParseJson(text);
|
|
1240
|
-
if (!response.ok) {
|
|
1241
|
-
throw responseToApiError(response, parsed);
|
|
1242
|
-
}
|
|
1243
|
-
if (parsed === void 0) {
|
|
1244
|
-
throw new CliApiError({
|
|
1245
|
-
status: response.status,
|
|
1246
|
-
code: "UNEXPECTED_RESPONSE",
|
|
1247
|
-
type: "internal_error",
|
|
1248
|
-
message: "The API returned a non-JSON success response."
|
|
1249
|
-
});
|
|
1250
|
-
}
|
|
1251
|
-
return parsed;
|
|
1252
|
-
}
|
|
1253
|
-
function resolveRawIdempotencyKey(method, provided) {
|
|
1254
|
-
if (provided !== void 0 && provided !== "") {
|
|
1255
|
-
return provided;
|
|
1256
|
-
}
|
|
1257
|
-
return method === "POST" ? crypto.randomUUID() : void 0;
|
|
1258
|
-
}
|
|
1259
|
-
function buildRawHeaders(input) {
|
|
1260
|
-
const headers = new Headers({
|
|
1261
|
-
authorization: `Bearer ${input.auth.apiKey}`,
|
|
1262
|
-
"user-agent": `${CLI_NAME}/${CLI_VERSION}`,
|
|
1263
|
-
accept: "application/json, text/plain;q=0.9"
|
|
1264
|
-
});
|
|
1265
|
-
if (input.hasJsonBody) {
|
|
1266
|
-
headers.set("content-type", "application/json");
|
|
1267
|
-
}
|
|
1268
|
-
if (input.auth.brandId !== void 0 && !isOrgLevelPath(input.path)) {
|
|
1269
|
-
headers.set("x-brand-id", input.auth.brandId);
|
|
1270
|
-
}
|
|
1271
|
-
if (input.idempotencyKey !== void 0 && input.idempotencyKey !== "") {
|
|
1272
|
-
headers.set("idempotency-key", input.idempotencyKey);
|
|
1273
|
-
}
|
|
1274
|
-
return headers;
|
|
1275
|
-
}
|
|
1276
|
-
function responseToApiError(response, parsed) {
|
|
1277
|
-
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
1278
|
-
const record = isRecord2(parsed) ? parsed : void 0;
|
|
1279
|
-
const standard = record !== void 0 && isRecord2(record.error) ? record.error : void 0;
|
|
1280
|
-
const envelope = standard ?? record;
|
|
1281
|
-
const readField = (key) => {
|
|
1282
|
-
const value = envelope?.[key];
|
|
1283
|
-
return typeof value === "string" ? value : void 0;
|
|
1284
|
-
};
|
|
1285
|
-
const param = readField("param");
|
|
1286
|
-
const suggestion = readField("suggestion") ?? (envelope === void 0 ? void 0 : suggestionForStatus(response.status));
|
|
1287
|
-
const docs = readField("docs");
|
|
1288
|
-
const details = isRecord2(envelope?.details) ? envelope.details : void 0;
|
|
1289
|
-
return new CliApiError({
|
|
1290
|
-
status: response.status,
|
|
1291
|
-
code: readField("code") ?? "HTTP_ERROR",
|
|
1292
|
-
type: readField("type") ?? errorTypeForStatus(response.status),
|
|
1293
|
-
message: readField("message") ?? `Request failed with status ${response.status}`,
|
|
1294
|
-
...param === void 0 ? {} : { param },
|
|
1295
|
-
...suggestion === void 0 ? {} : { suggestion },
|
|
1296
|
-
...docs === void 0 ? {} : { docs },
|
|
1297
|
-
...requestId === void 0 ? {} : { requestId },
|
|
1298
|
-
...details === void 0 ? {} : { details },
|
|
1299
|
-
...parsed === void 0 ? {} : { body: parsed }
|
|
1300
|
-
});
|
|
1301
|
-
}
|
|
1302
|
-
function isRecord2(value) {
|
|
1303
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1304
|
-
}
|
|
1305
|
-
function tryParseJson(text) {
|
|
1306
|
-
try {
|
|
1307
|
-
return JSON.parse(text);
|
|
1308
|
-
} catch {
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
1407
|
// src/commands/api.ts
|
|
1313
1408
|
var METHODS = ["GET", "POST", "PATCH", "DELETE"];
|
|
1314
1409
|
var ANONYMOUS_PATHS = ["/v1/health", "/v1/help", "/v1/llms.txt"];
|
|
@@ -1451,7 +1546,9 @@ var apiKeysCreateCommand = defineCommand({
|
|
|
1451
1546
|
summary: "Bind the NEW key to this brand id (omit for an organization-wide key); not the acting --brand"
|
|
1452
1547
|
},
|
|
1453
1548
|
INPUT_FLAG,
|
|
1454
|
-
|
|
1549
|
+
// The route never replays (a replay would disclose the plaintext key
|
|
1550
|
+
// again): a retry mints a second key, whatever the key says.
|
|
1551
|
+
NON_REPLAYING_IDEMPOTENCY_FLAG
|
|
1455
1552
|
],
|
|
1456
1553
|
examples: [
|
|
1457
1554
|
"brew-cli api-keys create --name CI --permissions emails domains",
|
|
@@ -1495,9 +1592,31 @@ var apiKeysListCommand = defineCommand({
|
|
|
1495
1592
|
sdkMethod: "apiKeys.list",
|
|
1496
1593
|
route: { method: "GET", path: "/v1/api-keys" },
|
|
1497
1594
|
commandClass: "read",
|
|
1595
|
+
flags: [LIMIT_FLAG, CURSOR_FLAG, ALL_FLAG],
|
|
1498
1596
|
examples: ["brew-cli api-keys list", "brew-cli api-keys list --json"],
|
|
1499
|
-
run: async ({ ctx }) => {
|
|
1500
|
-
const
|
|
1597
|
+
run: async ({ ctx, flags }) => {
|
|
1598
|
+
const limit = flagInt(flags.limit, "--limit");
|
|
1599
|
+
const cursor = flagString(flags.cursor);
|
|
1600
|
+
if (limit === void 0 && cursor === void 0 && flags.all !== true) {
|
|
1601
|
+
const result2 = await ctx.client().apiKeys.list();
|
|
1602
|
+
return { data: result2, human: renderApiKeys(result2.data) };
|
|
1603
|
+
}
|
|
1604
|
+
const page = (after) => rawRequest(ctx, {
|
|
1605
|
+
method: "GET",
|
|
1606
|
+
path: "/v1/api-keys",
|
|
1607
|
+
query: {
|
|
1608
|
+
limit: limit === void 0 ? void 0 : String(limit),
|
|
1609
|
+
cursor: after
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1612
|
+
if (flags.all === true) {
|
|
1613
|
+
const rows = await collectAll(ctx, (next) => page(next ?? cursor));
|
|
1614
|
+
return {
|
|
1615
|
+
data: { data: rows, pagination: { cursor: null, hasMore: false } },
|
|
1616
|
+
human: renderApiKeys(rows)
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
const result = await page(cursor);
|
|
1501
1620
|
return { data: result, human: renderApiKeys(result.data) };
|
|
1502
1621
|
}
|
|
1503
1622
|
});
|
|
@@ -3038,21 +3157,37 @@ var brandGetCommand = defineCommand({
|
|
|
3038
3157
|
});
|
|
3039
3158
|
|
|
3040
3159
|
// src/commands/brand/get-images.ts
|
|
3160
|
+
var RETIRED_FILTERS = {
|
|
3161
|
+
type: "--type (type) was retired by the API: use --kind logo|brand|generated.",
|
|
3162
|
+
aspectRatio: "--aspect-ratio (aspectRatio) was retired by the API: every image carries its width and height."
|
|
3163
|
+
};
|
|
3041
3164
|
var brandGetImagesCommand = defineCommand({
|
|
3042
3165
|
path: ["brand", "get-images"],
|
|
3043
|
-
summary: "Browse or semantically search the brand's
|
|
3044
|
-
sdkMethod:
|
|
3166
|
+
summary: "Browse or semantically search the brand's assets (logos, brand images, images made with Brew)",
|
|
3167
|
+
sdkMethod: null,
|
|
3168
|
+
isRawTransport: true,
|
|
3045
3169
|
route: { method: "GET", path: "/v1/brand/images" },
|
|
3046
3170
|
commandClass: "read",
|
|
3047
3171
|
flags: [
|
|
3048
3172
|
{
|
|
3049
3173
|
flag: "--query <text>",
|
|
3050
|
-
summary: "Semantic search over
|
|
3174
|
+
summary: "Semantic search over what the images show (1 credit per new search; logos are not searchable)"
|
|
3175
|
+
},
|
|
3176
|
+
{
|
|
3177
|
+
flag: "--kind <kind>",
|
|
3178
|
+
summary: "logo, brand (from the site or uploaded) or generated"
|
|
3179
|
+
},
|
|
3180
|
+
{
|
|
3181
|
+
flag: "--sort <order>",
|
|
3182
|
+
summary: "Browse order: newest (default) or oldest; ignored by --query"
|
|
3183
|
+
},
|
|
3184
|
+
{
|
|
3185
|
+
flag: "--type <type>",
|
|
3186
|
+
summary: "Retired by the API: use --kind"
|
|
3051
3187
|
},
|
|
3052
|
-
{ flag: "--type <type>", summary: "Filter by image category" },
|
|
3053
3188
|
{
|
|
3054
3189
|
flag: "--aspect-ratio <ratio>",
|
|
3055
|
-
summary: "
|
|
3190
|
+
summary: "Retired by the API: rows carry width and height instead of a shape filter"
|
|
3056
3191
|
},
|
|
3057
3192
|
LIMIT_FLAG,
|
|
3058
3193
|
CURSOR_FLAG,
|
|
@@ -3061,48 +3196,68 @@ var brandGetImagesCommand = defineCommand({
|
|
|
3061
3196
|
],
|
|
3062
3197
|
examples: [
|
|
3063
3198
|
"brew-cli brand get-images",
|
|
3064
|
-
|
|
3199
|
+
"brew-cli brand get-images --kind generated --sort oldest --all",
|
|
3200
|
+
'brew-cli brand get-images --query "team photo" --kind brand'
|
|
3065
3201
|
],
|
|
3066
3202
|
run: async ({ ctx, flags }) => {
|
|
3067
3203
|
const base = await readJsonFlag(ctx, flags.input, "--input");
|
|
3068
3204
|
const input = mergeInput(base, {
|
|
3069
3205
|
q: flagString(flags.query),
|
|
3070
|
-
|
|
3071
|
-
|
|
3206
|
+
kind: flagString(flags.kind),
|
|
3207
|
+
sort: flagString(flags.sort),
|
|
3072
3208
|
limit: flagInt(flags.limit, "--limit"),
|
|
3073
|
-
cursor: flagString(flags.cursor)
|
|
3209
|
+
cursor: flagString(flags.cursor),
|
|
3210
|
+
type: flagString(flags.type),
|
|
3211
|
+
aspectRatio: flagString(flags.aspectRatio)
|
|
3212
|
+
});
|
|
3213
|
+
for (const [key, message] of Object.entries(RETIRED_FILTERS)) {
|
|
3214
|
+
if (input[key] !== void 0) {
|
|
3215
|
+
throw new CliUsageError(message);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
const page = (cursor) => rawRequest(ctx, {
|
|
3219
|
+
method: "GET",
|
|
3220
|
+
path: "/v1/brand/images",
|
|
3221
|
+
query: toQuery(cursor === void 0 ? input : { ...input, cursor })
|
|
3074
3222
|
});
|
|
3075
|
-
const brand = ctx.client().brand;
|
|
3076
3223
|
if (flags.all === true) {
|
|
3077
|
-
const rows = await collectAll(
|
|
3078
|
-
ctx,
|
|
3079
|
-
(cursor) => brand.getImages(
|
|
3080
|
-
asSdkInput({
|
|
3081
|
-
...input,
|
|
3082
|
-
...cursor === void 0 ? {} : { cursor }
|
|
3083
|
-
})
|
|
3084
|
-
)
|
|
3085
|
-
);
|
|
3224
|
+
const rows = await collectAll(ctx, page);
|
|
3086
3225
|
return {
|
|
3087
3226
|
data: { data: rows, pagination: { cursor: null, hasMore: false } },
|
|
3088
|
-
human:
|
|
3227
|
+
human: renderAssets(rows)
|
|
3089
3228
|
};
|
|
3090
3229
|
}
|
|
3091
|
-
const result = await
|
|
3092
|
-
|
|
3093
|
-
);
|
|
3094
|
-
return { data: result, human: renderImages(result.data) };
|
|
3230
|
+
const result = await page(void 0);
|
|
3231
|
+
return { data: result, human: renderAssets(result.data) };
|
|
3095
3232
|
}
|
|
3096
3233
|
});
|
|
3097
|
-
function
|
|
3234
|
+
function toQuery(input) {
|
|
3235
|
+
const query = {};
|
|
3236
|
+
for (const [key, value] of Object.entries(input)) {
|
|
3237
|
+
query[key] = value === void 0 || value === null ? void 0 : String(value);
|
|
3238
|
+
}
|
|
3239
|
+
return query;
|
|
3240
|
+
}
|
|
3241
|
+
function renderAssets(rows) {
|
|
3098
3242
|
if (rows.length === 0) {
|
|
3099
|
-
return "No brand
|
|
3243
|
+
return "No brand assets found.";
|
|
3100
3244
|
}
|
|
3101
|
-
return renderTable(
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3245
|
+
return renderTable(
|
|
3246
|
+
rows.map((row) => ({
|
|
3247
|
+
assetId: row.assetId,
|
|
3248
|
+
kind: row.kind,
|
|
3249
|
+
url: row.url,
|
|
3250
|
+
size: row.width !== void 0 && row.height !== void 0 ? `${row.width}x${row.height}` : "",
|
|
3251
|
+
added: row.addedAt ?? ""
|
|
3252
|
+
})),
|
|
3253
|
+
[
|
|
3254
|
+
{ key: "assetId", header: "ASSET ID" },
|
|
3255
|
+
{ key: "kind", header: "KIND" },
|
|
3256
|
+
{ key: "url", header: "URL" },
|
|
3257
|
+
{ key: "size", header: "SIZE" },
|
|
3258
|
+
{ key: "added", header: "ADDED" }
|
|
3259
|
+
]
|
|
3260
|
+
);
|
|
3106
3261
|
}
|
|
3107
3262
|
|
|
3108
3263
|
// src/commands/brand/update.ts
|
|
@@ -3204,10 +3359,43 @@ var brandsListCommand = defineCommand({
|
|
|
3204
3359
|
sdkMethod: "brands.list",
|
|
3205
3360
|
route: { method: "GET", path: "/v1/brands" },
|
|
3206
3361
|
commandClass: "read",
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3362
|
+
flags: [
|
|
3363
|
+
{
|
|
3364
|
+
flag: "--status <status>",
|
|
3365
|
+
summary: "Only brands in one lifecycle state: extracting | completed | failed | deleting"
|
|
3366
|
+
},
|
|
3367
|
+
LIMIT_FLAG,
|
|
3368
|
+
CURSOR_FLAG,
|
|
3369
|
+
ALL_FLAG
|
|
3370
|
+
],
|
|
3371
|
+
examples: [
|
|
3372
|
+
"brew-cli brands list --json",
|
|
3373
|
+
"brew-cli brands list --status completed",
|
|
3374
|
+
"brew-cli brands list --all --json"
|
|
3375
|
+
],
|
|
3376
|
+
run: async ({ ctx, flags }) => {
|
|
3377
|
+
const input = {
|
|
3378
|
+
status: flagString(flags.status),
|
|
3379
|
+
limit: flagInt(flags.limit, "--limit"),
|
|
3380
|
+
cursor: flagString(flags.cursor)
|
|
3381
|
+
};
|
|
3382
|
+
const brands = ctx.client().brands;
|
|
3383
|
+
if (flags.all === true) {
|
|
3384
|
+
const rows = await collectAll(
|
|
3385
|
+
ctx,
|
|
3386
|
+
(cursor) => brands.list(
|
|
3387
|
+
asSdkInput({
|
|
3388
|
+
...input,
|
|
3389
|
+
...cursor === void 0 ? {} : { cursor }
|
|
3390
|
+
})
|
|
3391
|
+
)
|
|
3392
|
+
);
|
|
3393
|
+
return {
|
|
3394
|
+
data: { data: rows, pagination: { cursor: null, hasMore: false } }
|
|
3395
|
+
};
|
|
3396
|
+
}
|
|
3397
|
+
return { data: await brands.list(asSdkInput(input)) };
|
|
3398
|
+
}
|
|
3211
3399
|
});
|
|
3212
3400
|
|
|
3213
3401
|
// src/commands/chats/get.ts
|
|
@@ -3439,35 +3627,65 @@ var contactsGetCommand = defineCommand({
|
|
|
3439
3627
|
// src/commands/contacts/import-csv.ts
|
|
3440
3628
|
var contactsImportCsvCommand = defineCommand({
|
|
3441
3629
|
path: ["contacts", "import-csv"],
|
|
3442
|
-
summary: "Bulk-import contacts from a CSV file or stdin",
|
|
3630
|
+
summary: "Bulk-import contacts from a CSV file or stdin (free; --validate costs 2 credits per address)",
|
|
3443
3631
|
sdkMethod: "contacts.importCsv",
|
|
3444
3632
|
route: { method: "POST", path: "/v1/contacts/import-csv" },
|
|
3445
3633
|
commandClass: "write",
|
|
3446
3634
|
flags: [
|
|
3447
|
-
{
|
|
3635
|
+
{
|
|
3636
|
+
flag: "--file <path>",
|
|
3637
|
+
summary: "CSV file to import, or - for stdin (or csv in --input)"
|
|
3638
|
+
},
|
|
3448
3639
|
{
|
|
3449
3640
|
flag: "--mapping <pairs...>",
|
|
3450
3641
|
summary: "Column mapping csvColumn=fieldName, repeatable"
|
|
3451
3642
|
},
|
|
3643
|
+
{
|
|
3644
|
+
flag: "--date-order <order>",
|
|
3645
|
+
summary: "month_first or day_first: how to read dates like 03/04/2026 (a day over 12 in the column wins; default month-first, with a DATE_ORDER_ASSUMED warning)"
|
|
3646
|
+
},
|
|
3647
|
+
{
|
|
3648
|
+
flag: "--validate",
|
|
3649
|
+
summary: "Deliverability-check every imported address (2 credits per address)"
|
|
3650
|
+
},
|
|
3651
|
+
{
|
|
3652
|
+
flag: "--consent-source <source>",
|
|
3653
|
+
summary: "Stamp a marketing consent record on every row: api, form or import (sets the source of a record given in --input)"
|
|
3654
|
+
},
|
|
3655
|
+
INPUT_FLAG,
|
|
3452
3656
|
IDEMPOTENCY_FLAG
|
|
3453
3657
|
],
|
|
3454
3658
|
examples: [
|
|
3455
3659
|
"brew-cli contacts import-csv --file contacts.csv",
|
|
3456
|
-
"cat contacts.csv | brew-cli contacts import-csv --file - --mapping Email=email"
|
|
3660
|
+
"cat contacts.csv | brew-cli contacts import-csv --file - --mapping Email=email",
|
|
3661
|
+
"brew-cli contacts import-csv --file eu-signups.csv --date-order day_first --consent-source import"
|
|
3457
3662
|
],
|
|
3458
3663
|
run: async ({ ctx, flags }) => {
|
|
3459
|
-
|
|
3460
|
-
|
|
3664
|
+
if (flags.input === "-" && flags.file === "-") {
|
|
3665
|
+
throw new CliUsageError("Only one of --input and --file can read stdin.");
|
|
3666
|
+
}
|
|
3667
|
+
const base = await readJsonFlag(ctx, flags.input, "--input");
|
|
3668
|
+
const consentSource = flagString(flags.consentSource);
|
|
3669
|
+
const input = mergeInput(base, {
|
|
3670
|
+
csv: await readTextFlag(ctx, flags.file, "--file"),
|
|
3671
|
+
mapping: parseKeyValues(flags.mapping, "--mapping"),
|
|
3672
|
+
dateOrder: flagString(flags.dateOrder),
|
|
3673
|
+
validate: flags.validate === true ? true : void 0
|
|
3674
|
+
});
|
|
3675
|
+
if (typeof input.csv !== "string" || input.csv.trim() === "") {
|
|
3461
3676
|
throw new CliUsageError(
|
|
3462
|
-
"--file
|
|
3677
|
+
"Pass the CSV with --file (a path, or - for stdin) or as csv in --input."
|
|
3463
3678
|
);
|
|
3464
3679
|
}
|
|
3465
|
-
|
|
3680
|
+
if (consentSource !== void 0) {
|
|
3681
|
+
const record = input.consent;
|
|
3682
|
+
input.consent = {
|
|
3683
|
+
...typeof record === "object" && record !== null ? record : {},
|
|
3684
|
+
source: consentSource
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3466
3687
|
const result = await ctx.client().contacts.importCsv(
|
|
3467
|
-
asSdkInput(
|
|
3468
|
-
csv,
|
|
3469
|
-
...mapping === void 0 ? {} : { mapping }
|
|
3470
|
-
}),
|
|
3688
|
+
asSdkInput(input),
|
|
3471
3689
|
requestOptions(flags)
|
|
3472
3690
|
);
|
|
3473
3691
|
return { data: result };
|
|
@@ -4071,14 +4289,17 @@ var contractsInferCommand = defineCommand({
|
|
|
4071
4289
|
examples: [
|
|
4072
4290
|
`brew-cli contracts infer --input '{"email":"jane@example.com","order":{"total":9.5}}'`
|
|
4073
4291
|
],
|
|
4074
|
-
// Accepts either the bare example object or `{ example: {...} }
|
|
4075
|
-
//
|
|
4292
|
+
// Accepts either the bare example object or `{ example: {...} }`; only the
|
|
4293
|
+
// envelope carries `subjectKind` (a bare example may have its own field of
|
|
4294
|
+
// that name). Un-inferable spots (nulls, empty arrays) come back under
|
|
4295
|
+
// `issues`.
|
|
4076
4296
|
run: async ({ ctx, flags }) => {
|
|
4077
4297
|
const input = mergeInput(
|
|
4078
4298
|
await readJsonFlag(ctx, flags.input, "--input"),
|
|
4079
4299
|
{}
|
|
4080
4300
|
);
|
|
4081
|
-
const
|
|
4301
|
+
const isEnvelope = input.example !== void 0;
|
|
4302
|
+
const example = isEnvelope ? input.example : input;
|
|
4082
4303
|
if (typeof example !== "object" || example === null || Array.isArray(example) || Object.keys(example).length === 0) {
|
|
4083
4304
|
throw new CliUsageError(
|
|
4084
4305
|
"--input with a non-empty example object is required (the JSON your system sends)."
|
|
@@ -4087,7 +4308,10 @@ var contractsInferCommand = defineCommand({
|
|
|
4087
4308
|
const body = await rawRequest(ctx, {
|
|
4088
4309
|
method: "POST",
|
|
4089
4310
|
path: "/v1/payload-contracts/infer",
|
|
4090
|
-
body: {
|
|
4311
|
+
body: {
|
|
4312
|
+
example,
|
|
4313
|
+
...isEnvelope && input.subjectKind !== void 0 ? { subjectKind: input.subjectKind } : {}
|
|
4314
|
+
}
|
|
4091
4315
|
});
|
|
4092
4316
|
return { data: body };
|
|
4093
4317
|
}
|
|
@@ -4139,6 +4363,10 @@ var SDK_SKIP_LIST = [
|
|
|
4139
4363
|
sdkPath: "automations.triggers.validatePayload",
|
|
4140
4364
|
reason: "covered by `automations triggers contract validate` (raw route, bound pre-SDK-v9)"
|
|
4141
4365
|
},
|
|
4366
|
+
{
|
|
4367
|
+
sdkPath: "brand.getImages",
|
|
4368
|
+
reason: "covered by `brand get-images` (raw route: SDK 10 cannot send `kind` or `sort`, brew-v2#1713; bind SDK 11.2)"
|
|
4369
|
+
},
|
|
4142
4370
|
{
|
|
4143
4371
|
sdkPath: "payloadContracts.infer",
|
|
4144
4372
|
reason: "covered by `contracts infer` (raw route, bound pre-SDK-v9)"
|
|
@@ -4513,6 +4741,194 @@ function renderDomains(rows) {
|
|
|
4513
4741
|
]);
|
|
4514
4742
|
}
|
|
4515
4743
|
|
|
4744
|
+
// src/commands/domains/unsubscribes/add.ts
|
|
4745
|
+
var domainsUnsubscribesAddCommand = defineCommand({
|
|
4746
|
+
path: ["domains", "unsubscribes", "add"],
|
|
4747
|
+
summary: "Add addresses to a marketing domain's unsubscribe list (that domain only)",
|
|
4748
|
+
sdkMethod: null,
|
|
4749
|
+
isRawTransport: true,
|
|
4750
|
+
route: { method: "POST", path: "/v1/domains/{domainId}/unsubscribes" },
|
|
4751
|
+
commandClass: "write",
|
|
4752
|
+
args: [
|
|
4753
|
+
{ name: "domainId", summary: "Marketing domain id", isRequired: true }
|
|
4754
|
+
],
|
|
4755
|
+
flags: [
|
|
4756
|
+
{
|
|
4757
|
+
flag: "--email <addresses...>",
|
|
4758
|
+
summary: "Address to suppress, repeatable (up to 1,000)"
|
|
4759
|
+
},
|
|
4760
|
+
IDEMPOTENCY_FLAG
|
|
4761
|
+
],
|
|
4762
|
+
examples: [
|
|
4763
|
+
"brew-cli domains unsubscribes add dom_123 --email ada@example.com --email bo@example.com"
|
|
4764
|
+
],
|
|
4765
|
+
run: async ({ ctx, args, flags }) => {
|
|
4766
|
+
const emails = toStringArray(flags.email);
|
|
4767
|
+
if (emails === void 0) {
|
|
4768
|
+
throw new CliUsageError("Pass at least one --email.");
|
|
4769
|
+
}
|
|
4770
|
+
const body = await rawRequest(ctx, {
|
|
4771
|
+
method: "POST",
|
|
4772
|
+
path: `/v1/domains/${encodeURIComponent(args.domainId ?? "")}/unsubscribes`,
|
|
4773
|
+
body: { emails },
|
|
4774
|
+
idempotencyKey: flagString(flags.idempotencyKey)
|
|
4775
|
+
});
|
|
4776
|
+
return { data: body };
|
|
4777
|
+
}
|
|
4778
|
+
});
|
|
4779
|
+
|
|
4780
|
+
// src/commands/domains/unsubscribes/export.ts
|
|
4781
|
+
var domainsUnsubscribesExportCommand = defineCommand({
|
|
4782
|
+
path: ["domains", "unsubscribes", "export"],
|
|
4783
|
+
summary: "A domain's unsubscribe list as CSV (truncated when capped)",
|
|
4784
|
+
sdkMethod: null,
|
|
4785
|
+
isRawTransport: true,
|
|
4786
|
+
route: {
|
|
4787
|
+
method: "GET",
|
|
4788
|
+
path: "/v1/domains/{domainId}/unsubscribes/export"
|
|
4789
|
+
},
|
|
4790
|
+
commandClass: "read",
|
|
4791
|
+
args: [
|
|
4792
|
+
{ name: "domainId", summary: "Marketing domain id", isRequired: true }
|
|
4793
|
+
],
|
|
4794
|
+
flags: [
|
|
4795
|
+
{
|
|
4796
|
+
flag: "--scope <scope>",
|
|
4797
|
+
summary: "any (default), domain or all"
|
|
4798
|
+
}
|
|
4799
|
+
],
|
|
4800
|
+
examples: [
|
|
4801
|
+
"brew-cli domains unsubscribes export dom_123 > optouts.csv",
|
|
4802
|
+
"brew-cli domains unsubscribes export dom_123 --scope all --json"
|
|
4803
|
+
],
|
|
4804
|
+
run: async ({ ctx, args, flags }) => {
|
|
4805
|
+
const body = await rawRequest(ctx, {
|
|
4806
|
+
method: "GET",
|
|
4807
|
+
path: `/v1/domains/${encodeURIComponent(args.domainId ?? "")}/unsubscribes/export`,
|
|
4808
|
+
query: { scope: flagString(flags.scope) }
|
|
4809
|
+
});
|
|
4810
|
+
return { data: body, human: body.csv };
|
|
4811
|
+
}
|
|
4812
|
+
});
|
|
4813
|
+
|
|
4814
|
+
// src/commands/domains/unsubscribes/import.ts
|
|
4815
|
+
var domainsUnsubscribesImportCommand = defineCommand({
|
|
4816
|
+
path: ["domains", "unsubscribes", "import"],
|
|
4817
|
+
summary: "Add every address in a CSV to a domain's unsubscribe list",
|
|
4818
|
+
sdkMethod: null,
|
|
4819
|
+
isRawTransport: true,
|
|
4820
|
+
route: {
|
|
4821
|
+
method: "POST",
|
|
4822
|
+
path: "/v1/domains/{domainId}/unsubscribes/import"
|
|
4823
|
+
},
|
|
4824
|
+
commandClass: "write",
|
|
4825
|
+
args: [
|
|
4826
|
+
{ name: "domainId", summary: "Marketing domain id", isRequired: true }
|
|
4827
|
+
],
|
|
4828
|
+
flags: [
|
|
4829
|
+
{ flag: "--file <path>", summary: "CSV file, or - for stdin" },
|
|
4830
|
+
{
|
|
4831
|
+
flag: "--column <header>",
|
|
4832
|
+
summary: "The column holding the addresses (by header)"
|
|
4833
|
+
},
|
|
4834
|
+
IDEMPOTENCY_FLAG
|
|
4835
|
+
],
|
|
4836
|
+
examples: [
|
|
4837
|
+
"brew-cli domains unsubscribes import dom_123 --file optouts.csv --column Email"
|
|
4838
|
+
],
|
|
4839
|
+
run: async ({ ctx, args, flags }) => {
|
|
4840
|
+
const csv = await readTextFlag(ctx, flags.file, "--file");
|
|
4841
|
+
if (csv === void 0 || csv.trim() === "") {
|
|
4842
|
+
throw new CliUsageError(
|
|
4843
|
+
"--file is required (a CSV path, or - for stdin)."
|
|
4844
|
+
);
|
|
4845
|
+
}
|
|
4846
|
+
const column = flagString(flags.column);
|
|
4847
|
+
const body = await rawRequest(ctx, {
|
|
4848
|
+
method: "POST",
|
|
4849
|
+
path: `/v1/domains/${encodeURIComponent(args.domainId ?? "")}/unsubscribes/import`,
|
|
4850
|
+
body: { csv, ...column === void 0 ? {} : { column } },
|
|
4851
|
+
idempotencyKey: flagString(flags.idempotencyKey)
|
|
4852
|
+
});
|
|
4853
|
+
return { data: body };
|
|
4854
|
+
}
|
|
4855
|
+
});
|
|
4856
|
+
|
|
4857
|
+
// src/commands/domains/unsubscribes/list.ts
|
|
4858
|
+
var domainsUnsubscribesListCommand = defineCommand({
|
|
4859
|
+
path: ["domains", "unsubscribes", "list"],
|
|
4860
|
+
summary: "One page of a marketing domain's unsubscribe list, newest first",
|
|
4861
|
+
sdkMethod: null,
|
|
4862
|
+
isRawTransport: true,
|
|
4863
|
+
route: { method: "GET", path: "/v1/domains/{domainId}/unsubscribes" },
|
|
4864
|
+
commandClass: "read",
|
|
4865
|
+
args: [
|
|
4866
|
+
{ name: "domainId", summary: "Marketing domain id", isRequired: true }
|
|
4867
|
+
],
|
|
4868
|
+
flags: [
|
|
4869
|
+
{ flag: "--q <text>", summary: "Search by address" },
|
|
4870
|
+
{
|
|
4871
|
+
flag: "--scope <scope>",
|
|
4872
|
+
summary: "any (default), domain (this list only) or all (brand-wide opt-outs)"
|
|
4873
|
+
},
|
|
4874
|
+
LIMIT_FLAG,
|
|
4875
|
+
CURSOR_FLAG
|
|
4876
|
+
],
|
|
4877
|
+
examples: [
|
|
4878
|
+
"brew-cli domains unsubscribes list dom_123",
|
|
4879
|
+
"brew-cli domains unsubscribes list dom_123 --scope domain --q ada"
|
|
4880
|
+
],
|
|
4881
|
+
run: async ({ ctx, args, flags }) => {
|
|
4882
|
+
const limit = flagInt(flags.limit, "--limit");
|
|
4883
|
+
const body = await rawRequest(ctx, {
|
|
4884
|
+
method: "GET",
|
|
4885
|
+
path: `/v1/domains/${encodeURIComponent(args.domainId ?? "")}/unsubscribes`,
|
|
4886
|
+
query: {
|
|
4887
|
+
q: flagString(flags.q),
|
|
4888
|
+
scope: flagString(flags.scope),
|
|
4889
|
+
limit: limit === void 0 ? void 0 : String(limit),
|
|
4890
|
+
cursor: flagString(flags.cursor)
|
|
4891
|
+
}
|
|
4892
|
+
});
|
|
4893
|
+
return { data: body, human: renderRows(body.data) };
|
|
4894
|
+
}
|
|
4895
|
+
});
|
|
4896
|
+
function renderRows(rows) {
|
|
4897
|
+
if (rows.length === 0) {
|
|
4898
|
+
return "No unsubscribes on this list.";
|
|
4899
|
+
}
|
|
4900
|
+
return renderTable(rows, [
|
|
4901
|
+
{ key: "email", header: "EMAIL" },
|
|
4902
|
+
{ key: "scope", header: "SCOPE" },
|
|
4903
|
+
{ key: "unsubscribedAt", header: "UNSUBSCRIBED" }
|
|
4904
|
+
]);
|
|
4905
|
+
}
|
|
4906
|
+
|
|
4907
|
+
// src/commands/domains/unsubscribes/remove.ts
|
|
4908
|
+
var domainsUnsubscribesRemoveCommand = defineCommand({
|
|
4909
|
+
path: ["domains", "unsubscribes", "remove"],
|
|
4910
|
+
summary: "Take one address off a domain's unsubscribe list (never re-subscribes a brand-wide opt-out)",
|
|
4911
|
+
sdkMethod: null,
|
|
4912
|
+
isRawTransport: true,
|
|
4913
|
+
route: {
|
|
4914
|
+
method: "DELETE",
|
|
4915
|
+
path: "/v1/domains/{domainId}/unsubscribes/{email}"
|
|
4916
|
+
},
|
|
4917
|
+
commandClass: "write",
|
|
4918
|
+
args: [
|
|
4919
|
+
{ name: "domainId", summary: "Marketing domain id", isRequired: true },
|
|
4920
|
+
{ name: "email", summary: "Address to remove", isRequired: true }
|
|
4921
|
+
],
|
|
4922
|
+
flags: [],
|
|
4923
|
+
examples: ["brew-cli domains unsubscribes remove dom_123 ada@example.com"],
|
|
4924
|
+
run: async ({ ctx, args }) => ({
|
|
4925
|
+
data: await rawRequest(ctx, {
|
|
4926
|
+
method: "DELETE",
|
|
4927
|
+
path: `/v1/domains/${encodeURIComponent(args.domainId ?? "")}/unsubscribes/${encodeURIComponent(args.email ?? "")}`
|
|
4928
|
+
})
|
|
4929
|
+
})
|
|
4930
|
+
});
|
|
4931
|
+
|
|
4516
4932
|
// src/commands/domains/update.ts
|
|
4517
4933
|
var domainsUpdateCommand = defineCommand({
|
|
4518
4934
|
path: ["domains", "update"],
|
|
@@ -4658,19 +5074,41 @@ var emailsCloneCommand = defineCommand({
|
|
|
4658
5074
|
flag: "--email-version-id <id>",
|
|
4659
5075
|
summary: "Exact source version to clone (default: latest)"
|
|
4660
5076
|
},
|
|
5077
|
+
{
|
|
5078
|
+
flag: "--title <title>",
|
|
5079
|
+
summary: "Name for the clone (default: Copy of <source title>)"
|
|
5080
|
+
},
|
|
5081
|
+
{
|
|
5082
|
+
flag: "--group-id <groupId>",
|
|
5083
|
+
summary: "File the clone under an existing group (grp_\u2026), or ungrouped"
|
|
5084
|
+
},
|
|
5085
|
+
{
|
|
5086
|
+
flag: "--group-name <name>",
|
|
5087
|
+
summary: "File the clone under a group found (or created) by this name; not with --group-id"
|
|
5088
|
+
},
|
|
4661
5089
|
IDEMPOTENCY_FLAG
|
|
4662
5090
|
],
|
|
4663
5091
|
examples: [
|
|
4664
5092
|
"brew-cli emails clone eml_2SmZOWV3ZQ7W5x6g3m4p",
|
|
4665
|
-
"brew-cli emails clone eml_2SmZOWV3ZQ7W5x6g3m4p --email-version-id emv_9f2kX"
|
|
5093
|
+
"brew-cli emails clone eml_2SmZOWV3ZQ7W5x6g3m4p --email-version-id emv_9f2kX",
|
|
5094
|
+
'brew-cli emails clone eml_2SmZOWV3ZQ7W5x6g3m4p --title "Fall sale (B)" --group-name "Fall campaign"'
|
|
4666
5095
|
],
|
|
4667
5096
|
run: async ({ ctx, args, flags }) => {
|
|
4668
5097
|
const emailVersionId = flagString(flags.emailVersionId);
|
|
5098
|
+
const title = flagString(flags.title);
|
|
5099
|
+
const groupId = flagString(flags.groupId);
|
|
5100
|
+
const groupName = flagString(flags.groupName);
|
|
5101
|
+
if (groupId !== void 0 && groupName !== void 0) {
|
|
5102
|
+
throw new CliUsageError("Pass --group-id or --group-name, not both.");
|
|
5103
|
+
}
|
|
4669
5104
|
return {
|
|
4670
5105
|
data: await ctx.client().emails.clone(
|
|
4671
5106
|
asSdkInput({
|
|
4672
5107
|
emailId: args.emailId ?? "",
|
|
4673
|
-
...emailVersionId === void 0 ? {} : { emailVersionId }
|
|
5108
|
+
...emailVersionId === void 0 ? {} : { emailVersionId },
|
|
5109
|
+
...title === void 0 ? {} : { title },
|
|
5110
|
+
...groupId === void 0 ? {} : { groupId },
|
|
5111
|
+
...groupName === void 0 ? {} : { groupName }
|
|
4674
5112
|
}),
|
|
4675
5113
|
requestOptions(flags)
|
|
4676
5114
|
)
|
|
@@ -4853,12 +5291,16 @@ var emailsExportCommand = defineCommand({
|
|
|
4853
5291
|
flags: [
|
|
4854
5292
|
{
|
|
4855
5293
|
flag: "--provider <provider>",
|
|
4856
|
-
summary: "Connected ESP: braze, hubspot, klaviyo, mailchimp, iterable, postmark, onesignal, mailgun, sendgrid"
|
|
5294
|
+
summary: "Connected ESP: braze, brevo, hubspot, klaviyo, mailchimp, mailjet, iterable, postmark, onesignal, mailgun, sendgrid"
|
|
4857
5295
|
},
|
|
4858
5296
|
{
|
|
4859
5297
|
flag: "--template-name <name>",
|
|
4860
5298
|
summary: "Template name in the ESP (default: the email title)"
|
|
4861
5299
|
},
|
|
5300
|
+
{
|
|
5301
|
+
flag: "--sender-email <email>",
|
|
5302
|
+
summary: "Brevo or Mailjet: the active sender to use (omit when the account has exactly one)"
|
|
5303
|
+
},
|
|
4862
5304
|
{
|
|
4863
5305
|
flag: "--dry-run",
|
|
4864
5306
|
summary: "Validate design, ownership, and ESP connection without creating a template"
|
|
@@ -4873,16 +5315,18 @@ var emailsExportCommand = defineCommand({
|
|
|
4873
5315
|
const provider = flagString(flags.provider);
|
|
4874
5316
|
if (provider === void 0) {
|
|
4875
5317
|
throw new CliUsageError(
|
|
4876
|
-
"--provider is required: braze, hubspot, klaviyo, mailchimp, iterable, postmark, onesignal, mailgun, or sendgrid."
|
|
5318
|
+
"--provider is required: braze, brevo, hubspot, klaviyo, mailchimp, mailjet, iterable, postmark, onesignal, mailgun, or sendgrid."
|
|
4877
5319
|
);
|
|
4878
5320
|
}
|
|
4879
5321
|
const templateName = flagString(flags.templateName);
|
|
5322
|
+
const senderEmail = flagString(flags.senderEmail);
|
|
4880
5323
|
return {
|
|
4881
5324
|
data: await ctx.client().emails.export(
|
|
4882
5325
|
asSdkInput({
|
|
4883
5326
|
emailId: args.emailId ?? "",
|
|
4884
5327
|
provider,
|
|
4885
5328
|
...templateName === void 0 ? {} : { templateName },
|
|
5329
|
+
...senderEmail === void 0 ? {} : { senderEmail },
|
|
4886
5330
|
// The spec field is snake_case, unlike the rest of the API surface.
|
|
4887
5331
|
...flags.dryRun === true ? { dryRun: true } : {}
|
|
4888
5332
|
}),
|
|
@@ -4967,20 +5411,44 @@ var emailsGetCommand = defineCommand({
|
|
|
4967
5411
|
flags: [
|
|
4968
5412
|
{
|
|
4969
5413
|
flag: "--include <tokens>",
|
|
4970
|
-
summary: "Comma-separated expansions: html, versions"
|
|
5414
|
+
summary: "Comma-separated expansions: html, versions, text, links"
|
|
5415
|
+
},
|
|
5416
|
+
{
|
|
5417
|
+
flag: "--email-version-id <emailVersionId>",
|
|
5418
|
+
summary: "Read this saved version instead of the current head (ids from --include versions)"
|
|
5419
|
+
},
|
|
5420
|
+
{
|
|
5421
|
+
flag: "--run-id <runId>",
|
|
5422
|
+
summary: "Read the version a generate or edit run produced (the runId it returned)"
|
|
4971
5423
|
}
|
|
4972
5424
|
],
|
|
4973
5425
|
examples: [
|
|
4974
5426
|
"brew-cli emails get eml_2SmZOWV3ZQ7W5x6g3m4p",
|
|
4975
|
-
"brew-cli emails get eml_2SmZOWV3ZQ7W5x6g3m4p --include html,versions"
|
|
5427
|
+
"brew-cli emails get eml_2SmZOWV3ZQ7W5x6g3m4p --include html,versions",
|
|
5428
|
+
"brew-cli emails get eml_2SmZOWV3ZQ7W5x6g3m4p --include text,links",
|
|
5429
|
+
"brew-cli emails get eml_2SmZOWV3ZQ7W5x6g3m4p --email-version-id emv_2SmZOWV3ZQ7W5x6g3m4p --include html"
|
|
4976
5430
|
],
|
|
4977
5431
|
run: async ({ ctx, args, flags }) => {
|
|
5432
|
+
const emailId = args.emailId ?? "";
|
|
4978
5433
|
const include = flagString(flags.include);
|
|
5434
|
+
const emailVersionId = flagString(flags.emailVersionId);
|
|
5435
|
+
const runId = flagString(flags.runId);
|
|
5436
|
+
if (emailVersionId !== void 0 && runId !== void 0) {
|
|
5437
|
+
throw new CliUsageError(
|
|
5438
|
+
"Pass --email-version-id or --run-id, not both: each selects the version to read."
|
|
5439
|
+
);
|
|
5440
|
+
}
|
|
5441
|
+
if (emailVersionId === void 0 && runId === void 0) {
|
|
5442
|
+
return {
|
|
5443
|
+
data: await ctx.client().emails.get(emailId, include === void 0 ? void 0 : { include })
|
|
5444
|
+
};
|
|
5445
|
+
}
|
|
4979
5446
|
return {
|
|
4980
|
-
data: await ctx
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
5447
|
+
data: await rawRequest(ctx, {
|
|
5448
|
+
method: "GET",
|
|
5449
|
+
path: `/v1/emails/${encodeURIComponent(emailId)}`,
|
|
5450
|
+
query: { include, emailVersionId, runId }
|
|
5451
|
+
})
|
|
4984
5452
|
};
|
|
4985
5453
|
}
|
|
4986
5454
|
});
|
|
@@ -5000,7 +5468,12 @@ var emailsGetAuditCommand = defineCommand({
|
|
|
5000
5468
|
isRequired: true
|
|
5001
5469
|
}
|
|
5002
5470
|
],
|
|
5003
|
-
|
|
5471
|
+
// Its own page size: a saved audit pages 1-50 findings (default 10), not
|
|
5472
|
+
// the shared list's 1-100.
|
|
5473
|
+
flags: [
|
|
5474
|
+
{ flag: "--limit <n>", summary: "Findings per page, 1-50 (default 10)" },
|
|
5475
|
+
CURSOR_FLAG
|
|
5476
|
+
],
|
|
5004
5477
|
examples: [
|
|
5005
5478
|
"brew-cli emails get-audit 6f1e2d3c-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
|
|
5006
5479
|
"brew-cli emails get-audit 6f1e2d3c-4b5a-4c7d-8e9f-0a1b2c3d4e5f --limit 20"
|
|
@@ -5340,6 +5813,14 @@ var emailsImportFigmaCommand = defineCommand({
|
|
|
5340
5813
|
flag: "--subject-line <text>",
|
|
5341
5814
|
summary: "The design's default inbox subject line"
|
|
5342
5815
|
},
|
|
5816
|
+
{
|
|
5817
|
+
flag: "--group-id <groupId>",
|
|
5818
|
+
summary: "File the design under an existing group (grp_\u2026), or ungrouped"
|
|
5819
|
+
},
|
|
5820
|
+
{
|
|
5821
|
+
flag: "--group-name <name>",
|
|
5822
|
+
summary: "File the design under a group found (or created) by this name; not with --group-id"
|
|
5823
|
+
},
|
|
5343
5824
|
IDEMPOTENCY_FLAG
|
|
5344
5825
|
],
|
|
5345
5826
|
examples: [
|
|
@@ -5356,13 +5837,20 @@ var emailsImportFigmaCommand = defineCommand({
|
|
|
5356
5837
|
const title = flagString(flags.title);
|
|
5357
5838
|
const format = flagString(flags.format);
|
|
5358
5839
|
const subjectLine = flagString(flags.subjectLine);
|
|
5840
|
+
const groupId = flagString(flags.groupId);
|
|
5841
|
+
const groupName = flagString(flags.groupName);
|
|
5842
|
+
if (groupId !== void 0 && groupName !== void 0) {
|
|
5843
|
+
throw new CliUsageError("Pass --group-id or --group-name, not both.");
|
|
5844
|
+
}
|
|
5359
5845
|
return {
|
|
5360
5846
|
data: await ctx.client().emails.importFigma(
|
|
5361
5847
|
asSdkInput({
|
|
5362
5848
|
figmaUrl,
|
|
5363
5849
|
...title === void 0 ? {} : { title },
|
|
5364
5850
|
...format === void 0 ? {} : { format },
|
|
5365
|
-
...subjectLine === void 0 ? {} : { subjectLine }
|
|
5851
|
+
...subjectLine === void 0 ? {} : { subjectLine },
|
|
5852
|
+
...groupId === void 0 ? {} : { groupId },
|
|
5853
|
+
...groupName === void 0 ? {} : { groupName }
|
|
5366
5854
|
}),
|
|
5367
5855
|
requestOptions(flags)
|
|
5368
5856
|
)
|
|
@@ -5828,8 +6316,48 @@ var fieldsListCommand = defineCommand({
|
|
|
5828
6316
|
sdkMethod: "fields.list",
|
|
5829
6317
|
route: { method: "GET", path: "/v1/fields" },
|
|
5830
6318
|
commandClass: "read",
|
|
5831
|
-
|
|
5832
|
-
|
|
6319
|
+
flags: [
|
|
6320
|
+
{
|
|
6321
|
+
flag: "--include <tokens>",
|
|
6322
|
+
summary: "Expansions: coverage (per-field fill stats: percent of contacts with a value, and top values)"
|
|
6323
|
+
},
|
|
6324
|
+
{
|
|
6325
|
+
flag: "--audience-id <audienceId>",
|
|
6326
|
+
summary: "Scope --include coverage to one saved audience (ignored without it)"
|
|
6327
|
+
},
|
|
6328
|
+
LIMIT_FLAG,
|
|
6329
|
+
CURSOR_FLAG,
|
|
6330
|
+
ALL_FLAG
|
|
6331
|
+
],
|
|
6332
|
+
examples: [
|
|
6333
|
+
"brew-cli fields list",
|
|
6334
|
+
"brew-cli fields list --include coverage",
|
|
6335
|
+
"brew-cli fields list --include coverage --audience-id aud_2SmZOWV3ZQ7W5x6g3m4p --all --json"
|
|
6336
|
+
],
|
|
6337
|
+
run: async ({ ctx, flags }) => {
|
|
6338
|
+
const input = {
|
|
6339
|
+
include: flagString(flags.include),
|
|
6340
|
+
audienceId: flagString(flags.audienceId),
|
|
6341
|
+
limit: flagInt(flags.limit, "--limit"),
|
|
6342
|
+
cursor: flagString(flags.cursor)
|
|
6343
|
+
};
|
|
6344
|
+
const fields = ctx.client().fields;
|
|
6345
|
+
if (flags.all === true) {
|
|
6346
|
+
const rows = await collectAll(
|
|
6347
|
+
ctx,
|
|
6348
|
+
(cursor) => fields.list(
|
|
6349
|
+
asSdkInput({
|
|
6350
|
+
...input,
|
|
6351
|
+
...cursor === void 0 ? {} : { cursor }
|
|
6352
|
+
})
|
|
6353
|
+
)
|
|
6354
|
+
);
|
|
6355
|
+
return {
|
|
6356
|
+
data: { data: rows, pagination: { cursor: null, hasMore: false } }
|
|
6357
|
+
};
|
|
6358
|
+
}
|
|
6359
|
+
return { data: await fields.list(asSdkInput(input)) };
|
|
6360
|
+
}
|
|
5833
6361
|
});
|
|
5834
6362
|
|
|
5835
6363
|
// src/commands/flows/get.ts
|
|
@@ -6024,9 +6552,31 @@ var integrationsListCommand = defineCommand({
|
|
|
6024
6552
|
sdkMethod: "integrations.list",
|
|
6025
6553
|
route: { method: "GET", path: "/v1/integrations" },
|
|
6026
6554
|
commandClass: "read",
|
|
6555
|
+
flags: [LIMIT_FLAG, CURSOR_FLAG, ALL_FLAG],
|
|
6027
6556
|
examples: ["brew-cli integrations list", "brew-cli integrations list --json"],
|
|
6028
|
-
run: async ({ ctx }) => {
|
|
6029
|
-
const
|
|
6557
|
+
run: async ({ ctx, flags }) => {
|
|
6558
|
+
const limit = flagInt(flags.limit, "--limit");
|
|
6559
|
+
const cursor = flagString(flags.cursor);
|
|
6560
|
+
if (limit === void 0 && cursor === void 0 && flags.all !== true) {
|
|
6561
|
+
const result2 = await ctx.client().integrations.list();
|
|
6562
|
+
return { data: result2, human: renderIntegrations(result2.data) };
|
|
6563
|
+
}
|
|
6564
|
+
const page = (after) => rawRequest(ctx, {
|
|
6565
|
+
method: "GET",
|
|
6566
|
+
path: "/v1/integrations",
|
|
6567
|
+
query: {
|
|
6568
|
+
limit: limit === void 0 ? void 0 : String(limit),
|
|
6569
|
+
cursor: after
|
|
6570
|
+
}
|
|
6571
|
+
});
|
|
6572
|
+
if (flags.all === true) {
|
|
6573
|
+
const rows = await collectAll(ctx, (next) => page(next ?? cursor));
|
|
6574
|
+
return {
|
|
6575
|
+
data: { data: rows, pagination: { cursor: null, hasMore: false } },
|
|
6576
|
+
human: renderIntegrations(rows)
|
|
6577
|
+
};
|
|
6578
|
+
}
|
|
6579
|
+
const result = await page(cursor);
|
|
6030
6580
|
return { data: result, human: renderIntegrations(result.data) };
|
|
6031
6581
|
}
|
|
6032
6582
|
});
|
|
@@ -6618,6 +7168,7 @@ var ALL_COMMANDS = [
|
|
|
6618
7168
|
analyticsCampaignsCommand,
|
|
6619
7169
|
analyticsAutomationsCommand,
|
|
6620
7170
|
analyticsEventsCommand,
|
|
7171
|
+
analyticsEventCountsCommand,
|
|
6621
7172
|
analyticsSendsListCommand,
|
|
6622
7173
|
analyticsSendsGetCommand,
|
|
6623
7174
|
analyticsTriggerInstancesListCommand,
|
|
@@ -6637,6 +7188,11 @@ var ALL_COMMANDS = [
|
|
|
6637
7188
|
domainsHealthCommand,
|
|
6638
7189
|
domainsUpdateCommand,
|
|
6639
7190
|
domainsDeleteCommand,
|
|
7191
|
+
domainsUnsubscribesListCommand,
|
|
7192
|
+
domainsUnsubscribesAddCommand,
|
|
7193
|
+
domainsUnsubscribesRemoveCommand,
|
|
7194
|
+
domainsUnsubscribesImportCommand,
|
|
7195
|
+
domainsUnsubscribesExportCommand,
|
|
6640
7196
|
contentGenerateImageCommand,
|
|
6641
7197
|
contentGifCommand,
|
|
6642
7198
|
contentTransformCommand,
|