@brew.new/cli 0.6.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 CHANGED
@@ -31,6 +31,14 @@ var CliApiError = class extends Error {
31
31
  suggestion;
32
32
  docs;
33
33
  requestId;
34
+ /**
35
+ * The envelope's `details` object when the API sent one — for a
36
+ * trigger-fire `payload_mismatch` that is `{ errors[], warnings[],
37
+ * payloadSchema, … }`, naming every field the payload got wrong.
38
+ */
39
+ details;
40
+ /** The parsed response body exactly as received (`undefined` if not JSON). */
41
+ body;
34
42
  constructor(input) {
35
43
  super(input.message);
36
44
  this.status = input.status;
@@ -40,8 +48,38 @@ var CliApiError = class extends Error {
40
48
  this.suggestion = input.suggestion;
41
49
  this.docs = input.docs;
42
50
  this.requestId = input.requestId;
51
+ this.details = input.details;
52
+ this.body = input.body;
43
53
  }
44
54
  };
55
+ function errorTypeForStatus(status) {
56
+ switch (status) {
57
+ case 401:
58
+ return "authentication_error";
59
+ case 402:
60
+ return "payment_required";
61
+ case 403:
62
+ return "authorization_error";
63
+ case 404:
64
+ return "not_found";
65
+ case 409:
66
+ return "conflict";
67
+ case 429:
68
+ return "rate_limit";
69
+ case 501:
70
+ return "not_implemented";
71
+ case 503:
72
+ return "service_unavailable";
73
+ default:
74
+ return status >= 400 && status < 500 ? "invalid_request" : "internal_error";
75
+ }
76
+ }
77
+ function suggestionForStatus(status) {
78
+ if (status === 408 || status === 429 || status >= 500) {
79
+ return "Retry the request. If it keeps failing, contact support.";
80
+ }
81
+ return "Fix the request before sending it again \u2014 the same request fails the same way. See `details` for the specifics when present.";
82
+ }
45
83
  var ConfirmationRequiredError = class extends Error {
46
84
  envelope;
47
85
  constructor(envelope) {
@@ -67,6 +105,7 @@ function toExitCode(error) {
67
105
  }
68
106
  function toErrorEnvelope(error) {
69
107
  if (error instanceof BrewApiError || error instanceof CliApiError) {
108
+ const details = readErrorDetails(error);
70
109
  return {
71
110
  code: error.code,
72
111
  type: error.type,
@@ -75,6 +114,7 @@ function toErrorEnvelope(error) {
75
114
  ...error.suggestion ? { suggestion: error.suggestion } : {},
76
115
  ...error.docs ? { docs: error.docs } : {},
77
116
  ...error.requestId ? { requestId: error.requestId } : {},
117
+ ...details === void 0 ? {} : { details },
78
118
  status: error.status
79
119
  };
80
120
  }
@@ -134,7 +174,10 @@ function printError(ctx, error) {
134
174
  `);
135
175
  return;
136
176
  }
137
- const lines = [`brew-cli: ${envelope.message}`];
177
+ const lines = [
178
+ `brew-cli: ${envelope.message}`,
179
+ ...formatDetailLines(envelope.details)
180
+ ];
138
181
  if (envelope.suggestion) {
139
182
  lines.push(envelope.suggestion);
140
183
  }
@@ -147,6 +190,29 @@ function printError(ctx, error) {
147
190
  ctx.io.stderr.write(`${lines.join("\n")}
148
191
  `);
149
192
  }
193
+ function readErrorDetails(error) {
194
+ const details = error.details;
195
+ return isRecord(details) ? details : void 0;
196
+ }
197
+ function formatDetailLines(details) {
198
+ if (details === void 0) {
199
+ return [];
200
+ }
201
+ const issues = details.errors;
202
+ if (Array.isArray(issues) && issues.length > 0 && issues.every(isIssue)) {
203
+ return issues.map((issue) => {
204
+ const types = typeof issue.expectedType === "string" ? typeof issue.actualType === "string" ? ` (expected ${issue.expectedType}, got ${issue.actualType})` : ` (expected ${issue.expectedType})` : "";
205
+ return ` - ${issue.field}: ${issue.message}${types}`;
206
+ });
207
+ }
208
+ return [`Details: ${JSON.stringify(details)}`];
209
+ }
210
+ function isIssue(value) {
211
+ return isRecord(value) && typeof value.field === "string" && typeof value.message === "string";
212
+ }
213
+ function isRecord(value) {
214
+ return typeof value === "object" && value !== null && !Array.isArray(value);
215
+ }
150
216
  function apiErrorStatus(error) {
151
217
  if (error instanceof BrewApiError || error instanceof CliApiError) {
152
218
  return error.status;
@@ -215,7 +281,7 @@ function shellQuote(token) {
215
281
 
216
282
  // src/version.ts
217
283
  var CLI_NAME = "brew-cli";
218
- var CLI_VERSION = "0.6.0" ;
284
+ var CLI_VERSION = "0.9.0" ;
219
285
  var CONFIG_KEYS = ["apiKey", "brandId", "apiUrl"];
220
286
  function isConfigKey(value) {
221
287
  return CONFIG_KEYS.includes(value);
@@ -286,6 +352,7 @@ var ORG_LEVEL_PATH_PREFIXES = [
286
352
  "/v1/brands",
287
353
  "/v1/usage",
288
354
  "/v1/templates",
355
+ "/v1/flows",
289
356
  "/v1/health",
290
357
  "/v1/help",
291
358
  "/v1/llms.txt",
@@ -586,6 +653,10 @@ var IDEMPOTENCY_FLAG = {
586
653
  flag: "--idempotency-key <key>",
587
654
  summary: "Idempotency-Key for safe retries (auto-generated otherwise)"
588
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
+ };
589
660
  var INPUT_FLAG = {
590
661
  flag: "--input <json>",
591
662
  summary: "Full JSON request body, or - to read stdin (flags override it)"
@@ -668,12 +739,111 @@ function renderAutomationRows(rows) {
668
739
  ]);
669
740
  }
670
741
 
742
+ // src/commands/sends/list.ts
743
+ var SEND_STATUS_SUMMARY = "scheduled | queued | running | paused | completed | partially_completed | failed | canceled";
744
+ var sendsListCommand = defineCommand({
745
+ path: ["sends", "list"],
746
+ summary: "List sends (the unit of delivery and analytics) with lifetime stats",
747
+ sdkMethod: "sends.list",
748
+ route: { method: "GET", path: "/v1/sends" },
749
+ commandClass: "read",
750
+ flags: [
751
+ { flag: "--email <emailId>", summary: "Only sends of this design" },
752
+ { flag: "--kind <kind>", summary: "campaign | automation" },
753
+ { flag: "--automation <automationId>", summary: "Filter by automation" },
754
+ {
755
+ flag: "--automation-run <automationRunId>",
756
+ summary: "Deliveries of one automation run"
757
+ },
758
+ {
759
+ flag: "--audience-run <audienceRunId>",
760
+ summary: "Deliveries of one manual-audience run"
761
+ },
762
+ {
763
+ flag: "--trigger-instance <triggerInstanceId>",
764
+ summary: "Deliveries started by one fired trigger instance"
765
+ },
766
+ { flag: "--status <status>", summary: SEND_STATUS_SUMMARY },
767
+ {
768
+ flag: "--message-class <class>",
769
+ summary: "marketing | transactional"
770
+ },
771
+ {
772
+ flag: "--since <datetime>",
773
+ summary: "Inclusive lower bound on updatedAt (ISO-8601)"
774
+ },
775
+ {
776
+ flag: "--until <datetime>",
777
+ summary: "Inclusive upper bound on updatedAt (ISO-8601)"
778
+ },
779
+ LIMIT_FLAG,
780
+ CURSOR_FLAG,
781
+ ALL_FLAG,
782
+ INPUT_FLAG
783
+ ],
784
+ examples: [
785
+ "brew-cli sends list --status completed",
786
+ "brew-cli sends list --email eml_1 --all --json",
787
+ "brew-cli sends list --automation-run arun_9f2kX"
788
+ ],
789
+ run: async ({ ctx, flags }) => {
790
+ const base = await readJsonFlag(ctx, flags.input, "--input");
791
+ const input = mergeInput(base, {
792
+ emailId: flagString(flags.email),
793
+ kind: flagString(flags.kind),
794
+ automationId: flagString(flags.automation),
795
+ automationRunId: flagString(flags.automationRun),
796
+ audienceRunId: flagString(flags.audienceRun),
797
+ triggerInstanceId: flagString(flags.triggerInstance),
798
+ status: flagString(flags.status),
799
+ messageClass: flagString(flags.messageClass),
800
+ from: flagString(flags.since),
801
+ to: flagString(flags.until),
802
+ limit: flagInt(flags.limit, "--limit"),
803
+ cursor: flagString(flags.cursor)
804
+ });
805
+ return await listSends(ctx, input, flags.all === true);
806
+ }
807
+ });
808
+ async function listSends(ctx, input, followCursor) {
809
+ const sends = ctx.client().sends;
810
+ if (followCursor) {
811
+ const rows = await collectAll(
812
+ ctx,
813
+ (cursor) => sends.list(
814
+ asSdkInput({
815
+ ...input,
816
+ ...cursor === void 0 ? {} : { cursor }
817
+ })
818
+ )
819
+ );
820
+ return {
821
+ data: { data: rows, pagination: { cursor: null, hasMore: false } },
822
+ human: renderSends(rows)
823
+ };
824
+ }
825
+ const result = await sends.list(asSdkInput(input));
826
+ return { data: result, human: renderSends(result.data) };
827
+ }
828
+ function renderSends(rows) {
829
+ if (rows.length === 0) {
830
+ return "No sends found.";
831
+ }
832
+ return renderTable(rows, [
833
+ { key: "sendId", header: "SEND" },
834
+ { key: "kind", header: "KIND" },
835
+ { key: "status", header: "STATUS" },
836
+ { key: "subject", header: "SUBJECT" }
837
+ ]);
838
+ }
839
+
671
840
  // src/commands/analytics/campaigns.ts
672
841
  var analyticsCampaignsCommand = defineCommand({
673
842
  path: ["analytics", "campaigns"],
674
- summary: "Lifetime per-campaign KPIs (sent, opened, clicked, bounced)",
675
- sdkMethod: "analytics.campaigns",
676
- route: { method: "GET", path: "/v1/analytics/campaigns" },
843
+ summary: "Lifetime per-campaign KPIs (`sends list --kind campaign`; stats ride each row)",
844
+ sdkMethod: null,
845
+ derivedFrom: "sends.list",
846
+ route: { method: "GET", path: "/v1/sends" },
677
847
  commandClass: "read",
678
848
  flags: [LIMIT_FLAG, CURSOR_FLAG, ALL_FLAG, INPUT_FLAG],
679
849
  examples: [
@@ -683,41 +853,207 @@ var analyticsCampaignsCommand = defineCommand({
683
853
  run: async ({ ctx, flags }) => {
684
854
  const base = await readJsonFlag(ctx, flags.input, "--input");
685
855
  const input = mergeInput(base, {
856
+ kind: "campaign",
686
857
  limit: flagInt(flags.limit, "--limit"),
687
858
  cursor: flagString(flags.cursor)
688
859
  });
689
- const analytics = ctx.client().analytics;
690
- if (flags.all === true) {
691
- const rows = await collectAll(
692
- ctx,
693
- (cursor) => analytics.campaigns(
694
- asSdkInput({
695
- ...input,
696
- ...cursor === void 0 ? {} : { cursor }
697
- })
698
- )
860
+ return await listSends(ctx, input, flags.all === true);
861
+ }
862
+ });
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)."
699
1019
  );
700
- return {
701
- data: { data: rows, pagination: { cursor: null, hasMore: false } },
702
- human: renderCampaigns(rows)
703
- };
704
1020
  }
705
- const result = await analytics.campaigns(
706
- asSdkInput(input)
707
- );
708
- return { data: result, human: renderCampaigns(result.data) };
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) };
709
1034
  }
710
1035
  });
711
- function renderCampaigns(rows) {
712
- if (rows.length === 0) {
713
- return "No campaign analytics rows.";
714
- }
715
- return renderTable(rows, [
716
- { key: "sendId", header: "SEND" },
717
- { key: "title", header: "TITLE" },
718
- { key: "status", header: "STATUS" },
719
- { key: "sentAt", header: "SENT AT" }
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" }
720
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}`;
721
1057
  }
722
1058
 
723
1059
  // src/commands/analytics/events.ts
@@ -730,7 +1066,10 @@ var analyticsEventsCommand = defineCommand({
730
1066
  flags: [
731
1067
  { flag: "--since <datetime>", summary: "Window start (ISO-8601)" },
732
1068
  { flag: "--until <datetime>", summary: "Window end (ISO-8601)" },
733
- { flag: "--recipient <email>", summary: "Filter by recipient email" },
1069
+ {
1070
+ flag: "--recipient <rules>",
1071
+ summary: "CSV of recipient rules (max 10): an address, @domain, or substring; prefix ! to exclude"
1072
+ },
734
1073
  {
735
1074
  flag: "--event-type <type>",
736
1075
  summary: "Filter by event type (e.g. opened, clicked)"
@@ -751,7 +1090,7 @@ var analyticsEventsCommand = defineCommand({
751
1090
  const input = mergeInput(base, {
752
1091
  from: flagString(flags.since),
753
1092
  to: flagString(flags.until),
754
- recipientEmail: flagString(flags.recipient),
1093
+ recipient: flagString(flags.recipient),
755
1094
  eventType: flagString(flags.eventType),
756
1095
  automationId: flagString(flags.automation),
757
1096
  sendId: flagString(flags.send),
@@ -852,10 +1191,10 @@ var analyticsOverviewCommand = defineCommand({
852
1191
  // src/commands/analytics/sends/get.ts
853
1192
  var analyticsSendsGetCommand = defineCommand({
854
1193
  path: ["analytics", "sends", "get"],
855
- summary: "Fetch one send by id",
1194
+ summary: "Fetch one send by id \u2014 the bare row (`sends get`)",
856
1195
  sdkMethod: null,
857
- derivedFrom: "analytics.sends.list",
858
- route: { method: "GET", path: "/v1/analytics/sends" },
1196
+ derivedFrom: "sends.get",
1197
+ route: { method: "GET", path: "/v1/sends/{sendId}" },
859
1198
  commandClass: "read",
860
1199
  args: [
861
1200
  {
@@ -875,46 +1214,54 @@ var analyticsSendsGetCommand = defineCommand({
875
1214
  "brew-cli analytics sends get snd_123 --include events"
876
1215
  ],
877
1216
  run: async ({ ctx, args, flags }) => {
878
- const sendId = args.sendId ?? "";
879
1217
  const include = flagString(flags.include);
880
- const result = await ctx.client().analytics.sends.list(
881
- asSdkInput({
882
- sendId,
883
- ...include === void 0 ? {} : { include }
884
- })
885
- );
886
- const send = result.data[0];
887
- if (send === void 0) {
888
- throw new CliApiError({
889
- status: 404,
890
- code: "SEND_NOT_FOUND",
891
- type: "not_found",
892
- message: `No send found for ${sendId}`,
893
- suggestion: "List sends with `brew-cli analytics sends list`."
894
- });
895
- }
896
- return { data: send };
1218
+ return {
1219
+ data: await ctx.client().sends.get(
1220
+ args.sendId ?? "",
1221
+ include === void 0 ? void 0 : { include }
1222
+ )
1223
+ };
897
1224
  }
898
1225
  });
899
1226
 
1227
+ // src/lib/compat.ts
1228
+ function singleRowPage(row) {
1229
+ return { data: [row], pagination: { cursor: null, hasMore: false } };
1230
+ }
1231
+ function inputField(base, key) {
1232
+ if (base === null || typeof base !== "object") {
1233
+ return void 0;
1234
+ }
1235
+ const value = base[key];
1236
+ return typeof value === "string" && value !== "" ? value : void 0;
1237
+ }
1238
+ function includeRidesDetailRead(getCommand, idFlag) {
1239
+ const pairing = idFlag === void 0 ? "" : `, or pair it with ${idFlag}`;
1240
+ return new CliUsageError(
1241
+ `--include rides the detail read: use \`brew-cli ${getCommand} <id> --include \u2026\`${pairing}.`
1242
+ );
1243
+ }
1244
+
900
1245
  // src/commands/analytics/sends/list.ts
901
1246
  var analyticsSendsListCommand = defineCommand({
902
1247
  path: ["analytics", "sends", "list"],
903
- summary: "List campaign/automation sends with delivery stats",
904
- sdkMethod: "analytics.sends.list",
905
- route: { method: "GET", path: "/v1/analytics/sends" },
1248
+ summary: "List campaign/automation sends with delivery stats (`sends list`)",
1249
+ sdkMethod: null,
1250
+ derivedFrom: "sends.list",
1251
+ route: { method: "GET", path: "/v1/sends" },
906
1252
  commandClass: "read",
907
1253
  flags: [
908
- { flag: "--send <sendId>", summary: "Fetch one send (single-row page)" },
909
- { flag: "--email <emailId>", summary: "Filter by email design" },
910
1254
  {
911
- flag: "--include <tokens>",
912
- summary: "Comma-separated expansions: events"
1255
+ flag: "--send <sendId>",
1256
+ summary: "0.6 shim: read ONE send as a single-row page (`sends get` is the real read)"
913
1257
  },
914
1258
  {
915
- flag: "--status <status>",
916
- summary: "scheduled | queued | sending | sent | failed | canceled"
1259
+ flag: "--include <tokens>",
1260
+ summary: "With --send only: detail includes (`events`)"
917
1261
  },
1262
+ { flag: "--email <emailId>", summary: "Filter by email design" },
1263
+ { flag: "--kind <kind>", summary: "campaign | automation" },
1264
+ { flag: "--status <status>", summary: SEND_STATUS_SUMMARY },
918
1265
  { flag: "--since <datetime>", summary: "Window start (ISO-8601)" },
919
1266
  { flag: "--until <datetime>", summary: "Window end (ISO-8601)" },
920
1267
  LIMIT_FLAG,
@@ -923,108 +1270,86 @@ var analyticsSendsListCommand = defineCommand({
923
1270
  INPUT_FLAG
924
1271
  ],
925
1272
  examples: [
926
- "brew-cli analytics sends list --status sent",
927
- "brew-cli analytics sends list --email em_123 --all --json"
1273
+ "brew-cli analytics sends list --status completed",
1274
+ "brew-cli analytics sends list --email eml_1 --all --json"
928
1275
  ],
929
1276
  run: async ({ ctx, flags }) => {
930
1277
  const base = await readJsonFlag(ctx, flags.input, "--input");
1278
+ const sendId = flagString(flags.send) ?? inputField(base, "sendId");
1279
+ const include = flagString(flags.include) ?? inputField(base, "include");
1280
+ if (sendId !== void 0) {
1281
+ const row = await ctx.client().sends.get(sendId, include === void 0 ? void 0 : { include });
1282
+ return { data: singleRowPage(row), human: renderSends([row]) };
1283
+ }
1284
+ if (include !== void 0) {
1285
+ throw includeRidesDetailRead("sends get", "--send");
1286
+ }
931
1287
  const input = mergeInput(base, {
932
- sendId: flagString(flags.send),
933
1288
  emailId: flagString(flags.email),
934
- include: flagString(flags.include),
1289
+ kind: flagString(flags.kind),
935
1290
  status: flagString(flags.status),
936
1291
  from: flagString(flags.since),
937
1292
  to: flagString(flags.until),
938
1293
  limit: flagInt(flags.limit, "--limit"),
939
1294
  cursor: flagString(flags.cursor)
940
1295
  });
941
- const sends = ctx.client().analytics.sends;
942
- if (flags.all === true) {
943
- const rows = await collectAll(
944
- ctx,
945
- (cursor) => sends.list(
946
- asSdkInput({
947
- ...input,
948
- ...cursor === void 0 ? {} : { cursor }
949
- })
950
- )
951
- );
952
- return {
953
- data: { data: rows, pagination: { cursor: null, hasMore: false } },
954
- human: renderSends(rows)
955
- };
956
- }
957
- const result = await sends.list(asSdkInput(input));
958
- return { data: result, human: renderSends(result.data) };
1296
+ return await listSends(ctx, input, flags.all === true);
959
1297
  }
960
1298
  });
961
- function renderSends(rows) {
962
- if (rows.length === 0) {
963
- return "No sends found.";
964
- }
965
- return renderTable(rows, [
966
- { key: "sendId", header: "SEND" },
967
- { key: "kind", header: "KIND" },
968
- { key: "status", header: "STATUS" },
969
- { key: "subject", header: "SUBJECT" }
970
- ]);
971
- }
972
1299
 
973
- // src/commands/analytics/trigger-instances/list.ts
974
- var analyticsTriggerInstancesListCommand = defineCommand({
975
- path: ["analytics", "trigger-instances", "list"],
976
- summary: "List fired-trigger instances (ingest + match history)",
977
- sdkMethod: "analytics.triggerInstances.list",
978
- route: { method: "GET", path: "/v1/analytics/trigger-instances" },
1300
+ // src/commands/automations/trigger-instances/list.ts
1301
+ var automationsTriggerInstancesListCommand = defineCommand({
1302
+ path: ["automations", "trigger-instances", "list"],
1303
+ summary: "List fired-trigger instances (the inbound-fire audit log); each row carries a lifecycle `state`",
1304
+ sdkMethod: "automations.triggerInstances.list",
1305
+ route: { method: "GET", path: "/v1/automations/trigger-instances" },
979
1306
  commandClass: "read",
980
1307
  flags: [
981
1308
  {
982
1309
  flag: "--trigger <triggerEventId>",
983
1310
  summary: "Filter by trigger event"
984
1311
  },
985
- {
986
- flag: "--trigger-instance <triggerInstanceId>",
987
- summary: "Fetch one instance (single-row page)"
988
- },
989
1312
  LIMIT_FLAG,
990
1313
  CURSOR_FLAG,
991
1314
  ALL_FLAG,
992
1315
  INPUT_FLAG
993
1316
  ],
994
1317
  examples: [
995
- "brew-cli analytics trigger-instances list --trigger tev_123",
996
- "brew-cli analytics trigger-instances list --all --json"
1318
+ "brew-cli automations trigger-instances list --trigger tri_signup",
1319
+ "brew-cli automations trigger-instances list --all --json"
997
1320
  ],
998
1321
  run: async ({ ctx, flags }) => {
999
1322
  const base = await readJsonFlag(ctx, flags.input, "--input");
1000
1323
  const input = mergeInput(base, {
1001
1324
  triggerEventId: flagString(flags.trigger),
1002
- triggerInstanceId: flagString(flags.triggerInstance),
1003
1325
  limit: flagInt(flags.limit, "--limit"),
1004
1326
  cursor: flagString(flags.cursor)
1005
1327
  });
1006
- const triggerInstances = ctx.client().analytics.triggerInstances;
1007
- if (flags.all === true) {
1008
- const rows = await collectAll(
1009
- ctx,
1010
- (cursor) => triggerInstances.list(
1011
- asSdkInput({
1012
- ...input,
1013
- ...cursor === void 0 ? {} : { cursor }
1014
- })
1015
- )
1016
- );
1017
- return {
1018
- data: { data: rows, pagination: { cursor: null, hasMore: false } },
1019
- human: renderInstances(rows)
1020
- };
1021
- }
1022
- const result = await triggerInstances.list(
1023
- asSdkInput(input)
1024
- );
1025
- return { data: result, human: renderInstances(result.data) };
1328
+ return await listTriggerInstances(ctx, input, flags.all === true);
1026
1329
  }
1027
1330
  });
1331
+ async function listTriggerInstances(ctx, input, followCursor) {
1332
+ const triggerInstances = ctx.client().automations.triggerInstances;
1333
+ if (followCursor) {
1334
+ const rows = await collectAll(
1335
+ ctx,
1336
+ (cursor) => triggerInstances.list(
1337
+ asSdkInput({
1338
+ ...input,
1339
+ ...cursor === void 0 ? {} : { cursor }
1340
+ })
1341
+ )
1342
+ );
1343
+ return {
1344
+ data: { data: rows, pagination: { cursor: null, hasMore: false } },
1345
+ human: renderInstances(rows)
1346
+ };
1347
+ }
1348
+ const result = await triggerInstances.list(
1349
+ asSdkInput(input)
1350
+ );
1351
+ return { data: result, human: renderInstances(result.data) };
1352
+ }
1028
1353
  function renderInstances(rows) {
1029
1354
  if (rows.length === 0) {
1030
1355
  return "No trigger instances found.";
@@ -1037,102 +1362,47 @@ function renderInstances(rows) {
1037
1362
  ]);
1038
1363
  }
1039
1364
 
1040
- // src/lib/raw-request.ts
1041
- async function rawRequest(ctx, request) {
1042
- const auth = resolveAuth({
1043
- globals: ctx.globals,
1044
- env: ctx.io.env,
1045
- ...request.allowAnonymous === true ? { allowAnonymous: true } : {}
1046
- });
1047
- const hasBody = request.body !== void 0;
1048
- const idempotencyKey = resolveRawIdempotencyKey(
1049
- request.method,
1050
- request.idempotencyKey
1051
- );
1052
- const headers = buildRawHeaders({
1053
- auth,
1054
- path: request.path,
1055
- hasJsonBody: hasBody,
1056
- idempotencyKey
1057
- });
1058
- const url = new URL(`${auth.apiUrl}${request.path}`);
1059
- for (const [key, value] of Object.entries(request.query ?? {})) {
1060
- if (value !== void 0) {
1061
- url.searchParams.set(key, value);
1365
+ // src/commands/analytics/trigger-instances/list.ts
1366
+ var analyticsTriggerInstancesListCommand = defineCommand({
1367
+ path: ["analytics", "trigger-instances", "list"],
1368
+ summary: "List fired-trigger instances with their lifecycle `state` (`automations trigger-instances list`)",
1369
+ sdkMethod: null,
1370
+ derivedFrom: "automations.triggerInstances.list",
1371
+ route: { method: "GET", path: "/v1/automations/trigger-instances" },
1372
+ commandClass: "read",
1373
+ flags: [
1374
+ {
1375
+ flag: "--trigger <triggerEventId>",
1376
+ summary: "Filter by trigger event"
1377
+ },
1378
+ {
1379
+ flag: "--trigger-instance <triggerInstanceId>",
1380
+ summary: "0.6 shim: read ONE instance as a single-row page (`automations trigger-instances get` is the real read)"
1381
+ },
1382
+ LIMIT_FLAG,
1383
+ CURSOR_FLAG,
1384
+ ALL_FLAG,
1385
+ INPUT_FLAG
1386
+ ],
1387
+ examples: [
1388
+ "brew-cli analytics trigger-instances list --trigger tri_signup",
1389
+ "brew-cli analytics trigger-instances list --all --json"
1390
+ ],
1391
+ run: async ({ ctx, flags }) => {
1392
+ const base = await readJsonFlag(ctx, flags.input, "--input");
1393
+ const triggerInstanceId = flagString(flags.triggerInstance) ?? inputField(base, "triggerInstanceId");
1394
+ if (triggerInstanceId !== void 0) {
1395
+ const row = await ctx.client().automations.triggerInstances.get(triggerInstanceId);
1396
+ return { data: singleRowPage(row), human: renderInstances([row]) };
1062
1397
  }
1063
- }
1064
- const response = await fetch(url, {
1065
- method: request.method,
1066
- headers,
1067
- ...hasBody ? { body: JSON.stringify(request.body) } : {},
1068
- ...request.signal === void 0 ? {} : { signal: request.signal }
1069
- });
1070
- const text = await response.text();
1071
- const parsed = tryParseJson(text);
1072
- if (!response.ok) {
1073
- throw responseToApiError(response, parsed);
1074
- }
1075
- if (parsed === void 0) {
1076
- throw new CliApiError({
1077
- status: response.status,
1078
- code: "UNEXPECTED_RESPONSE",
1079
- type: "internal_error",
1080
- message: "The API returned a non-JSON success response."
1398
+ const input = mergeInput(base, {
1399
+ triggerEventId: flagString(flags.trigger),
1400
+ limit: flagInt(flags.limit, "--limit"),
1401
+ cursor: flagString(flags.cursor)
1081
1402
  });
1403
+ return await listTriggerInstances(ctx, input, flags.all === true);
1082
1404
  }
1083
- return parsed;
1084
- }
1085
- function resolveRawIdempotencyKey(method, provided) {
1086
- if (provided !== void 0 && provided !== "") {
1087
- return provided;
1088
- }
1089
- return method === "POST" ? crypto.randomUUID() : void 0;
1090
- }
1091
- function buildRawHeaders(input) {
1092
- const headers = new Headers({
1093
- authorization: `Bearer ${input.auth.apiKey}`,
1094
- "user-agent": `${CLI_NAME}/${CLI_VERSION}`,
1095
- accept: "application/json, text/plain;q=0.9"
1096
- });
1097
- if (input.hasJsonBody) {
1098
- headers.set("content-type", "application/json");
1099
- }
1100
- if (input.auth.brandId !== void 0 && !isOrgLevelPath(input.path)) {
1101
- headers.set("x-brand-id", input.auth.brandId);
1102
- }
1103
- if (input.idempotencyKey !== void 0 && input.idempotencyKey !== "") {
1104
- headers.set("idempotency-key", input.idempotencyKey);
1105
- }
1106
- return headers;
1107
- }
1108
- function responseToApiError(response, parsed) {
1109
- const requestId = response.headers.get("x-request-id") ?? void 0;
1110
- const record = parsed !== null && typeof parsed === "object" ? parsed : void 0;
1111
- const envelope = record !== void 0 && record.error !== null && typeof record.error === "object" ? record.error : record;
1112
- const readField = (key) => {
1113
- const value = envelope?.[key];
1114
- return typeof value === "string" ? value : void 0;
1115
- };
1116
- const param = readField("param");
1117
- const suggestion = readField("suggestion");
1118
- const docs = readField("docs");
1119
- return new CliApiError({
1120
- status: response.status,
1121
- code: readField("code") ?? "HTTP_ERROR",
1122
- type: readField("type") ?? "internal_error",
1123
- message: readField("message") ?? `Request failed with status ${response.status}`,
1124
- ...param === void 0 ? {} : { param },
1125
- ...suggestion === void 0 ? {} : { suggestion },
1126
- ...docs === void 0 ? {} : { docs },
1127
- ...requestId === void 0 ? {} : { requestId }
1128
- });
1129
- }
1130
- function tryParseJson(text) {
1131
- try {
1132
- return JSON.parse(text);
1133
- } catch {
1134
- }
1135
- }
1405
+ });
1136
1406
 
1137
1407
  // src/commands/api.ts
1138
1408
  var METHODS = ["GET", "POST", "PATCH", "DELETE"];
@@ -1276,7 +1546,9 @@ var apiKeysCreateCommand = defineCommand({
1276
1546
  summary: "Bind the NEW key to this brand id (omit for an organization-wide key); not the acting --brand"
1277
1547
  },
1278
1548
  INPUT_FLAG,
1279
- IDEMPOTENCY_FLAG
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
1280
1552
  ],
1281
1553
  examples: [
1282
1554
  "brew-cli api-keys create --name CI --permissions emails domains",
@@ -1320,9 +1592,31 @@ var apiKeysListCommand = defineCommand({
1320
1592
  sdkMethod: "apiKeys.list",
1321
1593
  route: { method: "GET", path: "/v1/api-keys" },
1322
1594
  commandClass: "read",
1595
+ flags: [LIMIT_FLAG, CURSOR_FLAG, ALL_FLAG],
1323
1596
  examples: ["brew-cli api-keys list", "brew-cli api-keys list --json"],
1324
- run: async ({ ctx }) => {
1325
- const result = await ctx.client().apiKeys.list();
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);
1326
1620
  return { data: result, human: renderApiKeys(result.data) };
1327
1621
  }
1328
1622
  });
@@ -1479,10 +1773,9 @@ var audiencesFromEventsCommand = defineCommand({
1479
1773
  // src/commands/audiences/get.ts
1480
1774
  var audiencesGetCommand = defineCommand({
1481
1775
  path: ["audiences", "get"],
1482
- summary: "Fetch one audience segment by id",
1483
- sdkMethod: null,
1484
- derivedFrom: "audiences.list",
1485
- route: { method: "GET", path: "/v1/audiences" },
1776
+ summary: "Fetch one audience segment by id \u2014 the bare row",
1777
+ sdkMethod: "audiences.get",
1778
+ route: { method: "GET", path: "/v1/audiences/{audienceId}" },
1486
1779
  commandClass: "read",
1487
1780
  args: [
1488
1781
  { name: "audienceId", summary: "Audience id to fetch", isRequired: true }
@@ -1490,42 +1783,35 @@ var audiencesGetCommand = defineCommand({
1490
1783
  flags: [
1491
1784
  {
1492
1785
  flag: "--include <tokens>",
1493
- summary: "Comma-separated expansions: count"
1786
+ summary: "Comma-separated expansions: count, build"
1494
1787
  }
1495
1788
  ],
1496
- examples: ["brew-cli audiences get aud_3k9sQ"],
1789
+ examples: [
1790
+ "brew-cli audiences get aud_3k9sQ",
1791
+ "brew-cli audiences get aud_3k9sQ --include count,build"
1792
+ ],
1497
1793
  run: async ({ ctx, args, flags }) => {
1498
- const audienceId = args.audienceId ?? "";
1499
1794
  const include = flagString(flags.include);
1500
- const result = await ctx.client().audiences.list({
1501
- audienceId,
1502
- ...include === void 0 ? {} : { include }
1503
- });
1504
- const audience = result.data[0];
1505
- if (audience === void 0) {
1506
- throw new CliApiError({
1507
- status: 404,
1508
- code: "AUDIENCE_NOT_FOUND",
1509
- type: "not_found",
1510
- message: `No audience found for ${audienceId}`,
1511
- suggestion: "List audiences with `brew-cli audiences list`."
1512
- });
1513
- }
1514
- return { data: audience };
1795
+ return {
1796
+ data: await ctx.client().audiences.get(
1797
+ args.audienceId ?? "",
1798
+ include === void 0 ? void 0 : { include }
1799
+ )
1800
+ };
1515
1801
  }
1516
1802
  });
1517
1803
 
1518
1804
  // src/commands/audiences/list.ts
1519
1805
  var audiencesListCommand = defineCommand({
1520
1806
  path: ["audiences", "list"],
1521
- summary: "List audience segments",
1807
+ summary: "List audience segments; one segment is `audiences get`",
1522
1808
  sdkMethod: "audiences.list",
1523
1809
  route: { method: "GET", path: "/v1/audiences" },
1524
1810
  commandClass: "read",
1525
1811
  flags: [
1526
1812
  {
1527
1813
  flag: "--include <tokens>",
1528
- summary: "Comma-separated expansions: count"
1814
+ summary: "0.6 flag: includes ride the detail read now (`audiences get --include count,build`)"
1529
1815
  },
1530
1816
  LIMIT_FLAG,
1531
1817
  CURSOR_FLAG,
@@ -1534,13 +1820,15 @@ var audiencesListCommand = defineCommand({
1534
1820
  ],
1535
1821
  examples: [
1536
1822
  "brew-cli audiences list",
1537
- "brew-cli audiences list --include count --limit 10",
1823
+ "brew-cli audiences list --limit 10",
1538
1824
  "brew-cli audiences list --all --json"
1539
1825
  ],
1540
1826
  run: async ({ ctx, flags }) => {
1541
1827
  const base = await readJsonFlag(ctx, flags.input, "--input");
1828
+ if ((flagString(flags.include) ?? inputField(base, "include")) !== void 0) {
1829
+ throw includeRidesDetailRead("audiences get");
1830
+ }
1542
1831
  const input = mergeInput(base, {
1543
- include: flagString(flags.include),
1544
1832
  limit: flagInt(flags.limit, "--limit"),
1545
1833
  cursor: flagString(flags.cursor)
1546
1834
  });
@@ -1607,16 +1895,45 @@ var audiencesUpdateCommand = defineCommand({
1607
1895
  }
1608
1896
  });
1609
1897
 
1610
- // src/commands/automations/audience-runs/control.ts
1611
- var automationsAudienceRunsControlCommand = defineCommand({
1612
- path: ["automations", "audience-runs", "control"],
1613
- summary: "Pause, resume, or cancel an in-flight manual-audience run",
1614
- sdkMethod: "automations.audienceRuns.control",
1898
+ // src/commands/automations/audience-runs/cancel.ts
1899
+ var automationsAudienceRunsCancelCommand = defineCommand({
1900
+ path: ["automations", "audience-runs", "cancel"],
1901
+ summary: "Cancel a manual-audience run for good \u2014 it can never be resumed",
1902
+ sdkMethod: "automations.audienceRuns.cancel",
1615
1903
  route: {
1616
1904
  method: "POST",
1617
- path: "/v1/automations/audience-runs/{audienceRunId}/control"
1905
+ path: "/v1/automations/audience-runs/{audienceRunId}/cancel"
1618
1906
  },
1619
1907
  commandClass: "destructive",
1908
+ args: [
1909
+ {
1910
+ name: "audienceRunId",
1911
+ summary: "Audience run id to cancel",
1912
+ isRequired: true
1913
+ }
1914
+ ],
1915
+ flags: [IDEMPOTENCY_FLAG],
1916
+ examples: ["brew-cli automations audience-runs cancel arun_01HZ --yes"],
1917
+ confirmSummary: ({ args }) => `Cancel audience run ${args.audienceRunId ?? ""} for good. Emails already sent are not recalled, and a canceled run cannot be resumed.`,
1918
+ run: async ({ ctx, args, flags }) => ({
1919
+ data: await ctx.client().automations.audienceRuns.cancel(
1920
+ args.audienceRunId ?? "",
1921
+ requestOptions(flags)
1922
+ )
1923
+ })
1924
+ });
1925
+
1926
+ // src/commands/automations/audience-runs/control.ts
1927
+ var automationsAudienceRunsControlCommand = defineCommand({
1928
+ path: ["automations", "audience-runs", "control"],
1929
+ summary: "Pause, resume, or cancel an in-flight manual-audience run (0.6 form of `audience-runs pause|resume|cancel`)",
1930
+ sdkMethod: null,
1931
+ derivedFrom: [
1932
+ "automations.audienceRuns.pause",
1933
+ "automations.audienceRuns.resume",
1934
+ "automations.audienceRuns.cancel"
1935
+ ],
1936
+ commandClass: "destructive",
1620
1937
  args: [
1621
1938
  {
1622
1939
  name: "audienceRunId",
@@ -1628,7 +1945,8 @@ var automationsAudienceRunsControlCommand = defineCommand({
1628
1945
  {
1629
1946
  flag: "--action <action>",
1630
1947
  summary: "pause (resumable) | resume | cancel (final)"
1631
- }
1948
+ },
1949
+ IDEMPOTENCY_FLAG
1632
1950
  ],
1633
1951
  examples: [
1634
1952
  "brew-cli automations audience-runs control arun_01HZ --action pause",
@@ -1637,58 +1955,182 @@ var automationsAudienceRunsControlCommand = defineCommand({
1637
1955
  // Only cancel is irreversible; pause and resume proceed ungated.
1638
1956
  confirmSummary: ({ args, flags }) => flagString(flags.action) === "cancel" ? `Cancel audience run ${args.audienceRunId ?? ""} for good. Emails already sent are not recalled, and a canceled run cannot be resumed.` : void 0,
1639
1957
  run: async ({ ctx, args, flags }) => {
1640
- const action = flagString(flags.action);
1641
- if (action === void 0) {
1642
- throw new CliUsageError("--action is required: pause, resume, or cancel.");
1958
+ const audienceRuns = ctx.client().automations.audienceRuns;
1959
+ const audienceRunId = args.audienceRunId ?? "";
1960
+ const options = requestOptions(flags);
1961
+ switch (flagString(flags.action)) {
1962
+ case "pause":
1963
+ return { data: await audienceRuns.pause(audienceRunId, options) };
1964
+ case "resume":
1965
+ return { data: await audienceRuns.resume(audienceRunId, options) };
1966
+ case "cancel":
1967
+ return { data: await audienceRuns.cancel(audienceRunId, options) };
1968
+ default:
1969
+ throw new CliUsageError(
1970
+ "--action is required: pause, resume, or cancel."
1971
+ );
1643
1972
  }
1644
- return {
1645
- data: await ctx.client().automations.audienceRuns.control(
1646
- asSdkInput({
1647
- audienceRunId: args.audienceRunId ?? "",
1648
- action
1649
- })
1650
- )
1651
- };
1652
1973
  }
1653
1974
  });
1654
1975
 
1976
+ // src/commands/automations/audience-runs/get.ts
1977
+ var automationsAudienceRunsGetCommand = defineCommand({
1978
+ path: ["automations", "audience-runs", "get"],
1979
+ summary: "Fetch one manual-audience run by id \u2014 the bare row",
1980
+ sdkMethod: "automations.audienceRuns.get",
1981
+ route: {
1982
+ method: "GET",
1983
+ path: "/v1/automations/audience-runs/{audienceRunId}"
1984
+ },
1985
+ commandClass: "read",
1986
+ args: [
1987
+ {
1988
+ name: "audienceRunId",
1989
+ summary: "Audience run id to fetch",
1990
+ isRequired: true
1991
+ }
1992
+ ],
1993
+ examples: ["brew-cli automations audience-runs get arun_01HZ"],
1994
+ run: async ({ ctx, args }) => ({
1995
+ data: await ctx.client().automations.audienceRuns.get(args.audienceRunId ?? "")
1996
+ })
1997
+ });
1998
+
1655
1999
  // src/commands/automations/audience-runs/list.ts
1656
2000
  var automationsAudienceRunsListCommand = defineCommand({
1657
2001
  path: ["automations", "audience-runs", "list"],
1658
- summary: "List manual-audience runs (newest first)",
2002
+ summary: "List manual-audience runs, newest first; one run is `automations audience-runs get`",
1659
2003
  sdkMethod: "automations.audienceRuns.list",
1660
2004
  route: { method: "GET", path: "/v1/automations/audience-runs" },
1661
2005
  commandClass: "read",
1662
- // The spec paginates by --limit only: no cursor, and the response carries
1663
- // no pagination envelope, so there is no --all here.
1664
2006
  flags: [
1665
2007
  {
1666
- flag: "--audience-run-id <id>",
1667
- summary: "Fetch a single audience run by id"
2008
+ flag: "--automation <automationId>",
2009
+ summary: "Only runs of this automation"
2010
+ },
2011
+ {
2012
+ flag: "--automation-id <automationId>",
2013
+ summary: "0.6 alias of --automation"
2014
+ },
2015
+ {
2016
+ flag: "--audience-run-id <audienceRunId>",
2017
+ summary: "0.6 shim: read ONE run as a single-row page (`automations audience-runs get` is the real read)"
1668
2018
  },
1669
2019
  {
1670
- flag: "--automation-id <id>",
1671
- summary: "Filter runs to a single automation"
2020
+ flag: "--status <status>",
2021
+ summary: "queued | scheduled | running | paused | completed | failed | canceled"
1672
2022
  },
1673
- { flag: "--limit <n>", summary: "Max rows, 1-200 (default 50)" }
2023
+ LIMIT_FLAG,
2024
+ CURSOR_FLAG,
2025
+ ALL_FLAG,
2026
+ INPUT_FLAG
1674
2027
  ],
1675
2028
  examples: [
1676
2029
  "brew-cli automations audience-runs list",
1677
- "brew-cli automations audience-runs list --automation-id auto_abc --limit 20"
2030
+ "brew-cli automations audience-runs list --automation am_123 --status running"
1678
2031
  ],
1679
2032
  run: async ({ ctx, flags }) => {
1680
- const limit = flagInt(flags.limit, "--limit");
1681
- return {
1682
- data: await ctx.client().automations.audienceRuns.list(
1683
- asSdkInput({
1684
- audienceRunId: flagString(flags.audienceRunId),
1685
- automationId: flagString(flags.automationId),
1686
- limit
1687
- })
1688
- )
1689
- };
2033
+ const audienceRuns = ctx.client().automations.audienceRuns;
2034
+ const base = await readJsonFlag(ctx, flags.input, "--input");
2035
+ const audienceRunId = flagString(flags.audienceRunId) ?? inputField(base, "audienceRunId");
2036
+ if (audienceRunId !== void 0) {
2037
+ const row = await audienceRuns.get(audienceRunId);
2038
+ return { data: singleRowPage(row), human: renderAudienceRuns([row]) };
2039
+ }
2040
+ const input = mergeInput(base, {
2041
+ automationId: flagString(flags.automation) ?? flagString(flags.automationId),
2042
+ status: flagString(flags.status),
2043
+ limit: flagInt(flags.limit, "--limit"),
2044
+ cursor: flagString(flags.cursor)
2045
+ });
2046
+ if (flags.all === true) {
2047
+ const rows = await collectAll(
2048
+ ctx,
2049
+ (cursor) => audienceRuns.list(
2050
+ asSdkInput({
2051
+ ...input,
2052
+ ...cursor === void 0 ? {} : { cursor }
2053
+ })
2054
+ )
2055
+ );
2056
+ return {
2057
+ data: { data: rows, pagination: { cursor: null, hasMore: false } },
2058
+ human: renderAudienceRuns(rows)
2059
+ };
2060
+ }
2061
+ const result = await audienceRuns.list(
2062
+ asSdkInput(input)
2063
+ );
2064
+ return { data: result, human: renderAudienceRuns(result.data) };
1690
2065
  }
1691
2066
  });
2067
+ function renderAudienceRuns(rows) {
2068
+ if (rows.length === 0) {
2069
+ return "No audience runs found.";
2070
+ }
2071
+ return renderTable(rows, [
2072
+ { key: "audienceRunId", header: "AUDIENCE RUN" },
2073
+ { key: "automationId", header: "AUTOMATION" },
2074
+ { key: "status", header: "STATUS" },
2075
+ { key: "totalRecipients", header: "RECIPIENTS" }
2076
+ ]);
2077
+ }
2078
+
2079
+ // src/commands/automations/audience-runs/pause.ts
2080
+ var automationsAudienceRunsPauseCommand = defineCommand({
2081
+ path: ["automations", "audience-runs", "pause"],
2082
+ summary: "Pause a running manual-audience run at its next step boundary (resumable)",
2083
+ sdkMethod: "automations.audienceRuns.pause",
2084
+ route: {
2085
+ method: "POST",
2086
+ path: "/v1/automations/audience-runs/{audienceRunId}/pause"
2087
+ },
2088
+ commandClass: "write",
2089
+ args: [
2090
+ {
2091
+ name: "audienceRunId",
2092
+ summary: "Audience run id to pause",
2093
+ isRequired: true
2094
+ }
2095
+ ],
2096
+ flags: [IDEMPOTENCY_FLAG],
2097
+ examples: ["brew-cli automations audience-runs pause arun_01HZ"],
2098
+ run: async ({ ctx, args, flags }) => ({
2099
+ data: await ctx.client().automations.audienceRuns.pause(
2100
+ args.audienceRunId ?? "",
2101
+ requestOptions(flags)
2102
+ )
2103
+ })
2104
+ });
2105
+
2106
+ // src/commands/automations/audience-runs/resume.ts
2107
+ var automationsAudienceRunsResumeCommand = defineCommand({
2108
+ path: ["automations", "audience-runs", "resume"],
2109
+ summary: "Resume a paused manual-audience run, or restart a failed one from its first undelivered send",
2110
+ sdkMethod: "automations.audienceRuns.resume",
2111
+ route: {
2112
+ method: "POST",
2113
+ path: "/v1/automations/audience-runs/{audienceRunId}/resume"
2114
+ },
2115
+ commandClass: "write",
2116
+ args: [
2117
+ {
2118
+ name: "audienceRunId",
2119
+ summary: "Audience run id to resume",
2120
+ isRequired: true
2121
+ }
2122
+ ],
2123
+ flags: [IDEMPOTENCY_FLAG],
2124
+ // `resumedFrom` on the response reports which case ran: `paused` or
2125
+ // `failed`. A failed restart never resends a step the failed run finished.
2126
+ examples: ["brew-cli automations audience-runs resume arun_01HZ"],
2127
+ run: async ({ ctx, args, flags }) => ({
2128
+ data: await ctx.client().automations.audienceRuns.resume(
2129
+ args.audienceRunId ?? "",
2130
+ requestOptions(flags)
2131
+ )
2132
+ })
2133
+ });
1692
2134
 
1693
2135
  // src/commands/automations/create.ts
1694
2136
  var automationsCreateCommand = defineCommand({
@@ -1755,10 +2197,9 @@ var automationsDeleteCommand = defineCommand({
1755
2197
  // src/commands/automations/get.ts
1756
2198
  var automationsGetCommand = defineCommand({
1757
2199
  path: ["automations", "get"],
1758
- summary: "Fetch one automation by id",
1759
- sdkMethod: null,
1760
- derivedFrom: "automations.list",
1761
- route: { method: "GET", path: "/v1/automations" },
2200
+ summary: "Fetch one automation by id \u2014 the bare row, lean by default",
2201
+ sdkMethod: "automations.get",
2202
+ route: { method: "GET", path: "/v1/automations/{automationId}" },
1762
2203
  commandClass: "read",
1763
2204
  args: [
1764
2205
  {
@@ -1770,7 +2211,7 @@ var automationsGetCommand = defineCommand({
1770
2211
  flags: [
1771
2212
  {
1772
2213
  flag: "--include <tokens>",
1773
- summary: "Comma-separated expansions: graph | versions"
2214
+ summary: "Comma-separated expansions: graph, versions"
1774
2215
  }
1775
2216
  ],
1776
2217
  examples: [
@@ -1778,23 +2219,13 @@ var automationsGetCommand = defineCommand({
1778
2219
  "brew-cli automations get am_123 --include graph,versions"
1779
2220
  ],
1780
2221
  run: async ({ ctx, args, flags }) => {
1781
- const automationId = args.automationId ?? "";
1782
2222
  const include = flagString(flags.include);
1783
- const result = await ctx.client().automations.list({
1784
- automationId,
1785
- ...include === void 0 ? {} : { include }
1786
- });
1787
- const automation = result.data[0];
1788
- if (automation === void 0) {
1789
- throw new CliApiError({
1790
- status: 404,
1791
- code: "AUTOMATION_NOT_FOUND",
1792
- type: "not_found",
1793
- message: `No automation found for ${automationId}`,
1794
- suggestion: "List automations with `brew-cli automations list`."
1795
- });
1796
- }
1797
- return { data: automation };
2223
+ return {
2224
+ data: await ctx.client().automations.get(
2225
+ args.automationId ?? "",
2226
+ include === void 0 ? void 0 : { include }
2227
+ )
2228
+ };
1798
2229
  }
1799
2230
  });
1800
2231
 
@@ -1918,7 +2349,7 @@ var automationsRunCommand = defineCommand({
1918
2349
  ],
1919
2350
  confirmSummary: ({ args, flags }) => {
1920
2351
  const inline = parseInlineInput(flags.input);
1921
- if (flags.dryRun === true || inline?.dry_run === true) {
2352
+ if (flags.dryRun === true || inline?.dryRun === true) {
1922
2353
  return;
1923
2354
  }
1924
2355
  const scheduleAt = flagString(flags.scheduleAt) ?? asString(inline?.scheduledAt);
@@ -1928,8 +2359,7 @@ var automationsRunCommand = defineCommand({
1928
2359
  run: async ({ ctx, args, flags }) => {
1929
2360
  const base = await readJsonFlag(ctx, flags.input, "--input");
1930
2361
  const input = mergeInput(base, {
1931
- // The spec field is snake_case, unlike the rest of the API surface.
1932
- dry_run: flags.dryRun === true ? true : void 0,
2362
+ dryRun: flags.dryRun === true ? true : void 0,
1933
2363
  scheduledAt: flagString(flags.scheduleAt)
1934
2364
  });
1935
2365
  return {
@@ -1957,22 +2387,89 @@ function asString(value) {
1957
2387
  return typeof value === "string" && value !== "" ? value : void 0;
1958
2388
  }
1959
2389
 
2390
+ // src/commands/automations/runs/cancel.ts
2391
+ var automationsRunsCancelCommand = defineCommand({
2392
+ path: ["automations", "runs", "cancel"],
2393
+ summary: "Cancel one in-flight automation run (event execution or test run) \u2014 nothing further is sent, and it can never be resumed",
2394
+ sdkMethod: "automations.runs.cancel",
2395
+ route: {
2396
+ method: "POST",
2397
+ path: "/v1/automations/runs/{automationRunId}/cancel"
2398
+ },
2399
+ commandClass: "destructive",
2400
+ args: [
2401
+ {
2402
+ name: "automationRunId",
2403
+ summary: "Run id to cancel (from `automations runs list`, a test start, or a fire response)",
2404
+ isRequired: true
2405
+ }
2406
+ ],
2407
+ flags: [
2408
+ { flag: "--reason <text>", summary: "Operator note stored on the run" },
2409
+ IDEMPOTENCY_FLAG
2410
+ ],
2411
+ examples: [
2412
+ 'brew-cli automations runs cancel run_9f2kX --reason "wrong audience" --yes'
2413
+ ],
2414
+ confirmSummary: ({ args }) => `Cancel automation run ${args.automationRunId ?? ""}. Nothing further is sent; emails already delivered are not recalled, and a canceled run can never be resumed.`,
2415
+ run: async ({ ctx, args, flags }) => {
2416
+ const reason = flagString(flags.reason);
2417
+ const options = {
2418
+ ...requestOptions(flags),
2419
+ ...reason === void 0 ? {} : { reason }
2420
+ };
2421
+ return {
2422
+ data: await ctx.client().automations.runs.cancel(
2423
+ args.automationRunId ?? "",
2424
+ Object.keys(options).length === 0 ? void 0 : options
2425
+ )
2426
+ };
2427
+ }
2428
+ });
2429
+
2430
+ // src/commands/automations/runs/get.ts
2431
+ var automationsRunsGetCommand = defineCommand({
2432
+ path: ["automations", "runs", "get"],
2433
+ summary: "Fetch one automation run by id \u2014 the bare row",
2434
+ sdkMethod: "automations.runs.get",
2435
+ route: { method: "GET", path: "/v1/automations/runs/{automationRunId}" },
2436
+ commandClass: "read",
2437
+ args: [
2438
+ {
2439
+ name: "automationRunId",
2440
+ summary: "Run id (from `automations runs list`, a test start, or a fire)",
2441
+ isRequired: true
2442
+ }
2443
+ ],
2444
+ flags: [
2445
+ {
2446
+ flag: "--include <tokens>",
2447
+ summary: "Expansions: logs (newest 100 per-node execution logs)"
2448
+ }
2449
+ ],
2450
+ examples: [
2451
+ "brew-cli automations runs get run_9f2kX",
2452
+ "brew-cli automations runs get run_9f2kX --include logs"
2453
+ ],
2454
+ run: async ({ ctx, args, flags }) => {
2455
+ const include = flagString(flags.include);
2456
+ return {
2457
+ data: await ctx.client().automations.runs.get(
2458
+ args.automationRunId ?? "",
2459
+ include === void 0 ? void 0 : { include }
2460
+ )
2461
+ };
2462
+ }
2463
+ });
2464
+
1960
2465
  // src/commands/automations/runs/list.ts
1961
2466
  var automationsRunsListCommand = defineCommand({
1962
2467
  path: ["automations", "runs", "list"],
1963
- summary: "List automation runs (live + test history)",
2468
+ summary: "List automation runs (live + test history); one run is `automations runs get`",
1964
2469
  sdkMethod: "automations.runs.list",
1965
2470
  route: { method: "GET", path: "/v1/automations/runs" },
1966
2471
  commandClass: "read",
1967
2472
  flags: [
1968
- {
1969
- flag: "--run <automationRunId>",
1970
- summary: "Fetch one run (single-row page)"
1971
- },
1972
- {
1973
- flag: "--include <tokens>",
1974
- summary: "Comma-separated expansions: logs"
1975
- },
1976
2473
  { flag: "--automation <automationId>", summary: "Filter by automation" },
1977
2474
  {
1978
2475
  flag: "--trigger <triggerEventId>",
@@ -1982,12 +2479,23 @@ var automationsRunsListCommand = defineCommand({
1982
2479
  flag: "--trigger-instance <triggerInstanceId>",
1983
2480
  summary: "Filter by fired trigger instance"
1984
2481
  },
1985
- { flag: "--recipient <email>", summary: "Filter by recipient email" },
1986
2482
  {
1987
2483
  flag: "--status <status>",
1988
- summary: "pending | running | completed | failed | cancelled"
2484
+ summary: "queued | running | completed | failed | canceled"
1989
2485
  },
1990
2486
  { flag: "--mode <mode>", summary: "live | test" },
2487
+ {
2488
+ flag: "--recipient <email>",
2489
+ summary: "Only runs for this recipient (case-insensitive match on the run row's recipientEmail)"
2490
+ },
2491
+ {
2492
+ flag: "--run <automationRunId>",
2493
+ summary: "0.6 shim: read ONE run as a single-row page (`automations runs get` is the real read)"
2494
+ },
2495
+ {
2496
+ flag: "--include <tokens>",
2497
+ summary: "With --run only: detail includes (`logs`)"
2498
+ },
1991
2499
  { flag: "--since <datetime>", summary: "Runs started at/after (ISO-8601)" },
1992
2500
  {
1993
2501
  flag: "--until <datetime>",
@@ -2000,13 +2508,24 @@ var automationsRunsListCommand = defineCommand({
2000
2508
  ],
2001
2509
  examples: [
2002
2510
  "brew-cli automations runs list --automation am_123 --status failed",
2003
- "brew-cli automations runs list --run arun_123 --include logs"
2511
+ "brew-cli automations runs list --trigger-instance tin_2f1c9d8a"
2004
2512
  ],
2005
2513
  run: async ({ ctx, flags }) => {
2514
+ const runs = ctx.client().automations.runs;
2006
2515
  const base = await readJsonFlag(ctx, flags.input, "--input");
2516
+ const runId = flagString(flags.run) ?? inputField(base, "automationRunId");
2517
+ const include = flagString(flags.include) ?? inputField(base, "include");
2518
+ if (runId !== void 0) {
2519
+ const row = await runs.get(
2520
+ runId,
2521
+ include === void 0 ? void 0 : { include }
2522
+ );
2523
+ return { data: singleRowPage(row), human: renderRuns([row]) };
2524
+ }
2525
+ if (include !== void 0) {
2526
+ throw includeRidesDetailRead("automations runs get", "--run");
2527
+ }
2007
2528
  const input = mergeInput(base, {
2008
- automationRunId: flagString(flags.run),
2009
- include: flagString(flags.include),
2010
2529
  automationId: flagString(flags.automation),
2011
2530
  triggerEventId: flagString(flags.trigger),
2012
2531
  triggerInstanceId: flagString(flags.triggerInstance),
@@ -2018,7 +2537,6 @@ var automationsRunsListCommand = defineCommand({
2018
2537
  limit: flagInt(flags.limit, "--limit"),
2019
2538
  cursor: flagString(flags.cursor)
2020
2539
  });
2021
- const runs = ctx.client().automations.runs;
2022
2540
  if (flags.all === true) {
2023
2541
  const rows = await collectAll(
2024
2542
  ctx,
@@ -2083,6 +2601,29 @@ var automationsTestCommand = defineCommand({
2083
2601
  }
2084
2602
  });
2085
2603
 
2604
+ // src/commands/automations/trigger-instances/get.ts
2605
+ var automationsTriggerInstancesGetCommand = defineCommand({
2606
+ path: ["automations", "trigger-instances", "get"],
2607
+ summary: "Fetch one fired-trigger instance by id \u2014 the bare row, with its lifecycle `state` and the runs it started",
2608
+ sdkMethod: "automations.triggerInstances.get",
2609
+ route: {
2610
+ method: "GET",
2611
+ path: "/v1/automations/trigger-instances/{triggerInstanceId}"
2612
+ },
2613
+ commandClass: "read",
2614
+ args: [
2615
+ {
2616
+ name: "triggerInstanceId",
2617
+ summary: "Instance id returned by a fire, or by the instances list",
2618
+ isRequired: true
2619
+ }
2620
+ ],
2621
+ examples: ["brew-cli automations trigger-instances get tin_2f1c9d8a"],
2622
+ run: async ({ ctx, args }) => ({
2623
+ data: await ctx.client().automations.triggerInstances.get(args.triggerInstanceId ?? "")
2624
+ })
2625
+ });
2626
+
2086
2627
  // src/commands/automations/triggers/contract.ts
2087
2628
  var FORMAT_FLAG = {
2088
2629
  flag: "--format <format>",
@@ -2355,17 +2896,52 @@ var automationsTriggersFireCommand = defineCommand({
2355
2896
  }
2356
2897
  });
2357
2898
 
2899
+ // src/commands/automations/triggers/get.ts
2900
+ var automationsTriggersGetCommand = defineCommand({
2901
+ path: ["automations", "triggers", "get"],
2902
+ summary: "Fetch one trigger by id \u2014 the bare row with its payload schema",
2903
+ sdkMethod: "automations.triggers.get",
2904
+ route: { method: "GET", path: "/v1/automations/triggers/{triggerEventId}" },
2905
+ commandClass: "read",
2906
+ args: [
2907
+ {
2908
+ name: "triggerEventId",
2909
+ summary: "Trigger id (tri_\u2026, or an integration composite id)",
2910
+ isRequired: true
2911
+ }
2912
+ ],
2913
+ flags: [
2914
+ {
2915
+ flag: "--include <tokens>",
2916
+ summary: "Expansions: skill (a SKILL.md-shaped wiring brief)"
2917
+ }
2918
+ ],
2919
+ examples: [
2920
+ "brew-cli automations triggers get tri_signup",
2921
+ "brew-cli automations triggers get tri_signup --include skill"
2922
+ ],
2923
+ run: async ({ ctx, args, flags }) => {
2924
+ const include = flagString(flags.include);
2925
+ return {
2926
+ data: await ctx.client().automations.triggers.get(
2927
+ args.triggerEventId ?? "",
2928
+ include === void 0 ? void 0 : { include }
2929
+ )
2930
+ };
2931
+ }
2932
+ });
2933
+
2358
2934
  // src/commands/automations/triggers/list.ts
2359
2935
  var automationsTriggersListCommand = defineCommand({
2360
2936
  path: ["automations", "triggers", "list"],
2361
- summary: "List trigger events (their payload schemas drive fires)",
2937
+ summary: "List trigger events (their payload schemas drive fires); one trigger is `automations triggers get`",
2362
2938
  sdkMethod: "automations.triggers.list",
2363
2939
  route: { method: "GET", path: "/v1/automations/triggers" },
2364
2940
  commandClass: "read",
2365
2941
  flags: [
2366
2942
  {
2367
2943
  flag: "--trigger <triggerEventId>",
2368
- summary: "Fetch one trigger event (single-row page)"
2944
+ summary: "0.6 shim: read ONE trigger as a single-row page (`automations triggers get` is the real read)"
2369
2945
  },
2370
2946
  LIMIT_FLAG,
2371
2947
  CURSOR_FLAG,
@@ -2374,16 +2950,20 @@ var automationsTriggersListCommand = defineCommand({
2374
2950
  ],
2375
2951
  examples: [
2376
2952
  "brew-cli automations triggers list",
2377
- "brew-cli automations triggers list --trigger tev_123"
2953
+ "brew-cli automations triggers list --all --json"
2378
2954
  ],
2379
2955
  run: async ({ ctx, flags }) => {
2956
+ const triggers = ctx.client().automations.triggers;
2380
2957
  const base = await readJsonFlag(ctx, flags.input, "--input");
2958
+ const triggerEventId = flagString(flags.trigger) ?? inputField(base, "triggerEventId");
2959
+ if (triggerEventId !== void 0) {
2960
+ const row = await triggers.get(triggerEventId);
2961
+ return { data: singleRowPage(row), human: renderTriggers([row]) };
2962
+ }
2381
2963
  const input = mergeInput(base, {
2382
- triggerEventId: flagString(flags.trigger),
2383
2964
  limit: flagInt(flags.limit, "--limit"),
2384
2965
  cursor: flagString(flags.cursor)
2385
2966
  });
2386
- const triggers = ctx.client().automations.triggers;
2387
2967
  if (flags.all === true) {
2388
2968
  const rows = await collectAll(
2389
2969
  ctx,
@@ -2418,10 +2998,10 @@ function renderTriggers(rows) {
2418
2998
  var automationsTriggersReadyCommand = defineCommand({
2419
2999
  path: ["automations", "triggers", "ready"],
2420
3000
  summary: "Preflight a trigger without firing: key + scope + permissions pass/fail, the payload contract, and what a fire would start",
2421
- sdkMethod: "automations.triggers.ready",
3001
+ sdkMethod: "automations.triggers.readiness",
2422
3002
  route: {
2423
3003
  method: "GET",
2424
- path: "/v1/automations/triggers/{triggerEventId}/fire"
3004
+ path: "/v1/automations/triggers/{triggerEventId}/readiness"
2425
3005
  },
2426
3006
  commandClass: "read",
2427
3007
  args: [
@@ -2432,17 +3012,13 @@ var automationsTriggersReadyCommand = defineCommand({
2432
3012
  }
2433
3013
  ],
2434
3014
  examples: ["brew-cli automations triggers ready tri_signup"],
2435
- // 200 `status: "ready"` means the exact credential in use can fire this
2436
- // trigger; `details.counts.automations: 0` means fires are accepted and
2437
- // logged but start no runs until a wired automation is published.
2438
- run: async ({ ctx, args }) => {
2439
- const result = await ctx.client().automations.triggers.ready(
2440
- asSdkInput({
2441
- triggerEventId: args.triggerEventId ?? ""
2442
- })
2443
- );
2444
- return { data: result };
2445
- }
3015
+ // 200 `ready: true` means the exact credential in use can fire this
3016
+ // trigger. A `NO_PUBLISHED_AUTOMATION` blocker with `ready: false` means
3017
+ // fires are accepted and logged but start no runs until a wired
3018
+ // automation is published — still a 200, because a probe has to say why.
3019
+ run: async ({ ctx, args }) => ({
3020
+ data: await ctx.client().automations.triggers.readiness(args.triggerEventId ?? "")
3021
+ })
2446
3022
  });
2447
3023
 
2448
3024
  // src/commands/automations/triggers/update.ts
@@ -2581,21 +3157,37 @@ var brandGetCommand = defineCommand({
2581
3157
  });
2582
3158
 
2583
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
+ };
2584
3164
  var brandGetImagesCommand = defineCommand({
2585
3165
  path: ["brand", "get-images"],
2586
- summary: "Browse or semantically search the brand's image library",
2587
- sdkMethod: "brand.getImages",
3166
+ summary: "Browse or semantically search the brand's assets (logos, brand images, images made with Brew)",
3167
+ sdkMethod: null,
3168
+ isRawTransport: true,
2588
3169
  route: { method: "GET", path: "/v1/brand/images" },
2589
3170
  commandClass: "read",
2590
3171
  flags: [
2591
3172
  {
2592
3173
  flag: "--query <text>",
2593
- summary: "Semantic search over image descriptions"
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"
2594
3187
  },
2595
- { flag: "--type <type>", summary: "Filter by image category" },
2596
3188
  {
2597
3189
  flag: "--aspect-ratio <ratio>",
2598
- summary: "Filter by aspect ratio (e.g. 16:9)"
3190
+ summary: "Retired by the API: rows carry width and height instead of a shape filter"
2599
3191
  },
2600
3192
  LIMIT_FLAG,
2601
3193
  CURSOR_FLAG,
@@ -2604,48 +3196,68 @@ var brandGetImagesCommand = defineCommand({
2604
3196
  ],
2605
3197
  examples: [
2606
3198
  "brew-cli brand get-images",
2607
- 'brew-cli brand get-images --query "team photo" --aspect-ratio 16:9'
3199
+ "brew-cli brand get-images --kind generated --sort oldest --all",
3200
+ 'brew-cli brand get-images --query "team photo" --kind brand'
2608
3201
  ],
2609
3202
  run: async ({ ctx, flags }) => {
2610
3203
  const base = await readJsonFlag(ctx, flags.input, "--input");
2611
3204
  const input = mergeInput(base, {
2612
3205
  q: flagString(flags.query),
2613
- type: flagString(flags.type),
2614
- aspectRatio: flagString(flags.aspectRatio),
3206
+ kind: flagString(flags.kind),
3207
+ sort: flagString(flags.sort),
2615
3208
  limit: flagInt(flags.limit, "--limit"),
2616
- 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 })
2617
3222
  });
2618
- const brand = ctx.client().brand;
2619
3223
  if (flags.all === true) {
2620
- const rows = await collectAll(
2621
- ctx,
2622
- (cursor) => brand.getImages(
2623
- asSdkInput({
2624
- ...input,
2625
- ...cursor === void 0 ? {} : { cursor }
2626
- })
2627
- )
2628
- );
3224
+ const rows = await collectAll(ctx, page);
2629
3225
  return {
2630
3226
  data: { data: rows, pagination: { cursor: null, hasMore: false } },
2631
- human: renderImages(rows)
3227
+ human: renderAssets(rows)
2632
3228
  };
2633
3229
  }
2634
- const result = await brand.getImages(
2635
- asSdkInput(input)
2636
- );
2637
- return { data: result, human: renderImages(result.data) };
3230
+ const result = await page(void 0);
3231
+ return { data: result, human: renderAssets(result.data) };
2638
3232
  }
2639
3233
  });
2640
- function renderImages(rows) {
2641
- if (rows.length === 0) {
2642
- return "No brand images found.";
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);
2643
3238
  }
2644
- return renderTable(rows, [
2645
- { key: "url", header: "URL" },
2646
- { key: "category", header: "CATEGORY" },
2647
- { key: "aspectRatio", header: "ASPECT" }
2648
- ]);
3239
+ return query;
3240
+ }
3241
+ function renderAssets(rows) {
3242
+ if (rows.length === 0) {
3243
+ return "No brand assets found.";
3244
+ }
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
+ );
2649
3261
  }
2650
3262
 
2651
3263
  // src/commands/brand/update.ts
@@ -2747,33 +3359,66 @@ var brandsListCommand = defineCommand({
2747
3359
  sdkMethod: "brands.list",
2748
3360
  route: { method: "GET", path: "/v1/brands" },
2749
3361
  commandClass: "read",
2750
- examples: ["brew-cli brands list --json"],
2751
- run: async ({ ctx }) => ({
2752
- data: await ctx.client().brands.list()
2753
- })
2754
- });
2755
-
2756
- // src/commands/chats/get.ts
2757
- var chatsGetCommand = defineCommand({
2758
- path: ["chats", "get"],
2759
- summary: "Brand-scoped digest of a Brew chat (artifacts + transcript tail)",
2760
- sdkMethod: "chats.get",
2761
- route: { method: "GET", path: "/v1/chats/{chatId}" },
2762
- commandClass: "read",
2763
- args: [
3362
+ flags: [
2764
3363
  {
2765
- name: "chatId",
2766
- summary: "Brew chat id (from the chat URL / the app)",
2767
- isRequired: true
2768
- }
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
2769
3370
  ],
2770
- examples: ["brew-cli chats get Hk2mZ8t9QbY3sW1vR0pLd"],
2771
- run: async ({ ctx, args }) => ({
2772
- data: await ctx.client().chats.get(args.chatId ?? "")
2773
- })
2774
- });
2775
-
2776
- // src/commands/config.ts
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
+ }
3399
+ });
3400
+
3401
+ // src/commands/chats/get.ts
3402
+ var chatsGetCommand = defineCommand({
3403
+ path: ["chats", "get"],
3404
+ summary: "Brand-scoped digest of a Brew chat (artifacts + transcript tail)",
3405
+ sdkMethod: "chats.get",
3406
+ route: { method: "GET", path: "/v1/chats/{chatId}" },
3407
+ commandClass: "read",
3408
+ args: [
3409
+ {
3410
+ name: "chatId",
3411
+ summary: "Brew chat id (from the chat URL / the app)",
3412
+ isRequired: true
3413
+ }
3414
+ ],
3415
+ examples: ["brew-cli chats get Hk2mZ8t9QbY3sW1vR0pLd"],
3416
+ run: async ({ ctx, args }) => ({
3417
+ data: await ctx.client().chats.get(args.chatId ?? "")
3418
+ })
3419
+ });
3420
+
3421
+ // src/commands/config.ts
2777
3422
  var SETTABLE_KEYS = ["brandId", "apiUrl"];
2778
3423
  var configListCommand = defineCommand({
2779
3424
  path: ["config", "list"],
@@ -2962,77 +3607,161 @@ function inlineEmailCount(input) {
2962
3607
  // src/commands/contacts/get.ts
2963
3608
  var contactsGetCommand = defineCommand({
2964
3609
  path: ["contacts", "get"],
2965
- summary: "Fetch one contact by email",
2966
- sdkMethod: null,
2967
- derivedFrom: "contacts.search",
2968
- route: { method: "POST", path: "/v1/contacts/search" },
3610
+ summary: "Fetch one contact by email \u2014 the bare row",
3611
+ sdkMethod: "contacts.get",
3612
+ route: { method: "GET", path: "/v1/contacts/{email}" },
2969
3613
  commandClass: "read",
2970
3614
  args: [
2971
3615
  {
2972
3616
  name: "email",
2973
- summary: "Email address of the contact",
3617
+ summary: "Email address of the contact (the contact primary key)",
2974
3618
  isRequired: true
2975
3619
  }
2976
3620
  ],
2977
3621
  examples: ["brew-cli contacts get jane@example.com"],
2978
- run: async ({ ctx, args }) => {
2979
- const email = args.email ?? "";
2980
- const result = await ctx.client().contacts.search({
2981
- filters: [{ field: "email", operator: "equals", value: email }],
2982
- limit: 1
2983
- });
2984
- const contact = result.data[0];
2985
- if (contact === void 0) {
2986
- throw new CliApiError({
2987
- status: 404,
2988
- code: "CONTACT_NOT_FOUND",
2989
- type: "not_found",
2990
- message: `No contact found for ${email}`,
2991
- suggestion: "Check the address, or create the contact with `brew-cli contacts upsert`."
2992
- });
2993
- }
2994
- return { data: contact };
2995
- }
3622
+ run: async ({ ctx, args }) => ({
3623
+ data: await ctx.client().contacts.get(args.email ?? "")
3624
+ })
2996
3625
  });
2997
3626
 
2998
3627
  // src/commands/contacts/import-csv.ts
2999
3628
  var contactsImportCsvCommand = defineCommand({
3000
3629
  path: ["contacts", "import-csv"],
3001
- 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)",
3002
3631
  sdkMethod: "contacts.importCsv",
3003
3632
  route: { method: "POST", path: "/v1/contacts/import-csv" },
3004
3633
  commandClass: "write",
3005
3634
  flags: [
3006
- { flag: "--file <path>", summary: "CSV file to import, or - for stdin" },
3635
+ {
3636
+ flag: "--file <path>",
3637
+ summary: "CSV file to import, or - for stdin (or csv in --input)"
3638
+ },
3007
3639
  {
3008
3640
  flag: "--mapping <pairs...>",
3009
3641
  summary: "Column mapping csvColumn=fieldName, repeatable"
3010
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,
3011
3656
  IDEMPOTENCY_FLAG
3012
3657
  ],
3013
3658
  examples: [
3014
3659
  "brew-cli contacts import-csv --file contacts.csv",
3015
- "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"
3016
3662
  ],
3017
3663
  run: async ({ ctx, flags }) => {
3018
- const csv = await readTextFlag(ctx, flags.file, "--file");
3019
- if (csv === void 0 || csv.trim() === "") {
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() === "") {
3020
3676
  throw new CliUsageError(
3021
- "--file is required (a CSV path, or - for stdin)."
3677
+ "Pass the CSV with --file (a path, or - for stdin) or as csv in --input."
3022
3678
  );
3023
3679
  }
3024
- const mapping = parseKeyValues(flags.mapping, "--mapping");
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
+ }
3025
3687
  const result = await ctx.client().contacts.importCsv(
3026
- asSdkInput({
3027
- csv,
3028
- ...mapping === void 0 ? {} : { mapping }
3029
- }),
3688
+ asSdkInput(input),
3030
3689
  requestOptions(flags)
3031
3690
  );
3032
3691
  return { data: result };
3033
3692
  }
3034
3693
  });
3035
3694
 
3695
+ // src/commands/contacts/list.ts
3696
+ var contactsListCommand = defineCommand({
3697
+ path: ["contacts", "list"],
3698
+ summary: "List contacts, newest first \u2014 free-text search and one audience; typed clauses are `contacts search`",
3699
+ sdkMethod: "contacts.list",
3700
+ route: { method: "GET", path: "/v1/contacts" },
3701
+ commandClass: "read",
3702
+ flags: [
3703
+ { flag: "--search <text>", summary: "Free-text search" },
3704
+ {
3705
+ flag: "--audience <audienceId>",
3706
+ summary: "Only members of this saved audience"
3707
+ },
3708
+ {
3709
+ flag: "--sort <field>",
3710
+ summary: "Any core column or custom field (default createdAt)"
3711
+ },
3712
+ { flag: "--order <order>", summary: "Sort order: asc | desc" },
3713
+ LIMIT_FLAG,
3714
+ CURSOR_FLAG,
3715
+ ALL_FLAG,
3716
+ INPUT_FLAG
3717
+ ],
3718
+ examples: [
3719
+ "brew-cli contacts list --limit 20",
3720
+ "brew-cli contacts list --audience aud_3k9sQ --all --json",
3721
+ "brew-cli contacts list --search acme --sort email --order asc"
3722
+ ],
3723
+ run: async ({ ctx, flags }) => {
3724
+ const base = await readJsonFlag(ctx, flags.input, "--input");
3725
+ const input = mergeInput(base, {
3726
+ search: flagString(flags.search),
3727
+ audienceId: flagString(flags.audience),
3728
+ sort: flagString(flags.sort),
3729
+ order: flagString(flags.order),
3730
+ limit: flagInt(flags.limit, "--limit"),
3731
+ cursor: flagString(flags.cursor)
3732
+ });
3733
+ const contacts = ctx.client().contacts;
3734
+ if (flags.all === true) {
3735
+ const rows = await collectAll(
3736
+ ctx,
3737
+ (cursor) => contacts.list(
3738
+ asSdkInput({
3739
+ ...input,
3740
+ ...cursor === void 0 ? {} : { cursor }
3741
+ })
3742
+ )
3743
+ );
3744
+ return {
3745
+ data: { data: rows, pagination: { cursor: null, hasMore: false } },
3746
+ human: renderContacts(rows)
3747
+ };
3748
+ }
3749
+ const result = await contacts.list(asSdkInput(input));
3750
+ return { data: result, human: renderContacts(result.data) };
3751
+ }
3752
+ });
3753
+ function renderContacts(rows) {
3754
+ if (rows.length === 0) {
3755
+ return "No contacts found.";
3756
+ }
3757
+ return renderTable(rows, [
3758
+ { key: "email", header: "EMAIL" },
3759
+ { key: "firstName", header: "FIRST" },
3760
+ { key: "lastName", header: "LAST" },
3761
+ { key: "subscribed", header: "SUBSCRIBED" }
3762
+ ]);
3763
+ }
3764
+
3036
3765
  // src/commands/contacts/search.ts
3037
3766
  var contactsSearchCommand = defineCommand({
3038
3767
  path: ["contacts", "search"],
@@ -3085,14 +3814,14 @@ var contactsSearchCommand = defineCommand({
3085
3814
  );
3086
3815
  return {
3087
3816
  data: { data: rows, pagination: { cursor: null, hasMore: false } },
3088
- human: renderContacts(rows)
3817
+ human: renderContacts2(rows)
3089
3818
  };
3090
3819
  }
3091
3820
  const result = await contacts.search(asSdkInput(input));
3092
- return { data: result, human: renderContacts(result.data) };
3821
+ return { data: result, human: renderContacts2(result.data) };
3093
3822
  }
3094
3823
  });
3095
- function renderContacts(rows) {
3824
+ function renderContacts2(rows) {
3096
3825
  if (rows.length === 0) {
3097
3826
  return "No contacts matched.";
3098
3827
  }
@@ -3560,14 +4289,17 @@ var contractsInferCommand = defineCommand({
3560
4289
  examples: [
3561
4290
  `brew-cli contracts infer --input '{"email":"jane@example.com","order":{"total":9.5}}'`
3562
4291
  ],
3563
- // Accepts either the bare example object or `{ example: {...} }`.
3564
- // Un-inferable spots (nulls, empty arrays) come back under `issues`.
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`.
3565
4296
  run: async ({ ctx, flags }) => {
3566
4297
  const input = mergeInput(
3567
4298
  await readJsonFlag(ctx, flags.input, "--input"),
3568
4299
  {}
3569
4300
  );
3570
- const example = input.example ?? input;
4301
+ const isEnvelope = input.example !== void 0;
4302
+ const example = isEnvelope ? input.example : input;
3571
4303
  if (typeof example !== "object" || example === null || Array.isArray(example) || Object.keys(example).length === 0) {
3572
4304
  throw new CliUsageError(
3573
4305
  "--input with a non-empty example object is required (the JSON your system sends)."
@@ -3576,7 +4308,10 @@ var contractsInferCommand = defineCommand({
3576
4308
  const body = await rawRequest(ctx, {
3577
4309
  method: "POST",
3578
4310
  path: "/v1/payload-contracts/infer",
3579
- body: { example }
4311
+ body: {
4312
+ example,
4313
+ ...isEnvelope && input.subjectKind !== void 0 ? { subjectKind: input.subjectKind } : {}
4314
+ }
3580
4315
  });
3581
4316
  return { data: body };
3582
4317
  }
@@ -3628,6 +4363,10 @@ var SDK_SKIP_LIST = [
3628
4363
  sdkPath: "automations.triggers.validatePayload",
3629
4364
  reason: "covered by `automations triggers contract validate` (raw route, bound pre-SDK-v9)"
3630
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
+ },
3631
4370
  {
3632
4371
  sdkPath: "payloadContracts.infer",
3633
4372
  reason: "covered by `contracts infer` (raw route, bound pre-SDK-v9)"
@@ -3641,12 +4380,12 @@ var SDK_SKIP_LIST = [
3641
4380
  reason: "auto-pager covered by `analytics events --all`"
3642
4381
  },
3643
4382
  {
3644
- sdkPath: "analytics.sends.listAll",
3645
- reason: "auto-pager covered by `analytics sends list --all`"
4383
+ sdkPath: "sends.listAll",
4384
+ reason: "auto-pager covered by `sends list --all`"
3646
4385
  },
3647
4386
  {
3648
- sdkPath: "analytics.triggerInstances.listAll",
3649
- reason: "auto-pager covered by `analytics trigger-instances list --all`"
4387
+ sdkPath: "automations.triggerInstances.listAll",
4388
+ reason: "auto-pager covered by `automations trigger-instances list --all`"
3650
4389
  },
3651
4390
  {
3652
4391
  sdkPath: "brand.update",
@@ -3722,7 +4461,7 @@ function buildManifest() {
3722
4461
 
3723
4462
  // src/commands/docs.ts
3724
4463
  var DOCS_POINTERS = {
3725
- documentation: "https://docs.getbrew.io",
4464
+ documentation: "https://docs.brew.new",
3726
4465
  openapiSpec: "https://brew.new/openapi/public-api-v1.yaml",
3727
4466
  agentGuide: "https://brew.new/api/v1/llms.txt",
3728
4467
  apiCatalog: "https://brew.new/api/v1/help",
@@ -3913,28 +4652,15 @@ var domainsDeleteCommand = defineCommand({
3913
4652
  // src/commands/domains/get.ts
3914
4653
  var domainsGetCommand = defineCommand({
3915
4654
  path: ["domains", "get"],
3916
- summary: "Fetch one sending domain by id",
3917
- sdkMethod: null,
3918
- derivedFrom: "domains.list",
3919
- route: { method: "GET", path: "/v1/domains" },
4655
+ summary: "Fetch one sending domain by id \u2014 the bare row",
4656
+ sdkMethod: "domains.get",
4657
+ route: { method: "GET", path: "/v1/domains/{domainId}" },
3920
4658
  commandClass: "read",
3921
4659
  args: [{ name: "domainId", summary: "Domain id to fetch", isRequired: true }],
3922
4660
  examples: ["brew-cli domains get dom_3k9sQ"],
3923
- run: async ({ ctx, args }) => {
3924
- const domainId = args.domainId ?? "";
3925
- const result = await ctx.client().domains.list({ domainId });
3926
- const domain = result.data[0];
3927
- if (domain === void 0) {
3928
- throw new CliApiError({
3929
- status: 404,
3930
- code: "DOMAIN_NOT_FOUND",
3931
- type: "not_found",
3932
- message: `No domain found for ${domainId}`,
3933
- suggestion: "List domains with `brew-cli domains list`."
3934
- });
3935
- }
3936
- return { data: domain };
3937
- }
4661
+ run: async ({ ctx, args }) => ({
4662
+ data: await ctx.client().domains.get(args.domainId ?? "")
4663
+ })
3938
4664
  });
3939
4665
 
3940
4666
  // src/commands/domains/health.ts
@@ -4015,109 +4741,297 @@ function renderDomains(rows) {
4015
4741
  ]);
4016
4742
  }
4017
4743
 
4018
- // src/commands/domains/update.ts
4019
- var domainsUpdateCommand = defineCommand({
4020
- path: ["domains", "update"],
4021
- summary: "Update default sender settings for a domain",
4022
- sdkMethod: "domains.updateSettings",
4023
- route: { method: "PATCH", path: "/v1/domains/{domainId}" },
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" },
4024
4751
  commandClass: "write",
4025
4752
  args: [
4026
- { name: "domainId", summary: "Domain id to update", isRequired: true }
4753
+ { name: "domainId", summary: "Marketing domain id", isRequired: true }
4027
4754
  ],
4028
4755
  flags: [
4029
4756
  {
4030
- flag: "--default-sender-name <name>",
4031
- summary: "Default From display name"
4032
- },
4033
- {
4034
- flag: "--default-from-email <email>",
4035
- summary: "Default From address on this domain"
4036
- },
4037
- {
4038
- flag: "--default-reply-to-email <email>",
4039
- summary: "Default Reply-To address"
4757
+ flag: "--email <addresses...>",
4758
+ summary: "Address to suppress, repeatable (up to 1,000)"
4040
4759
  },
4041
- INPUT_FLAG
4760
+ IDEMPOTENCY_FLAG
4042
4761
  ],
4043
4762
  examples: [
4044
- 'brew-cli domains update dom_8s1Kj --default-sender-name "Brew Coffee"'
4763
+ "brew-cli domains unsubscribes add dom_123 --email ada@example.com --email bo@example.com"
4045
4764
  ],
4046
4765
  run: async ({ ctx, args, flags }) => {
4047
- const base = await readJsonFlag(ctx, flags.input, "--input");
4048
- const input = mergeInput(base, {
4049
- defaultSenderName: flagString(flags.defaultSenderName),
4050
- defaultFromEmail: flagString(flags.defaultFromEmail),
4051
- defaultReplyToEmail: flagString(flags.defaultReplyToEmail)
4052
- });
4053
- if (Object.keys(input).length === 0) {
4054
- throw new CliUsageError("Nothing to update \u2014 pass flags or --input.");
4766
+ const emails = toStringArray(flags.email);
4767
+ if (emails === void 0) {
4768
+ throw new CliUsageError("Pass at least one --email.");
4055
4769
  }
4056
- const result = await ctx.client().domains.updateSettings(
4057
- asSdkInput({
4058
- ...input,
4059
- domainId: args.domainId ?? ""
4060
- })
4061
- );
4062
- return { data: result };
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 };
4063
4777
  }
4064
4778
  });
4065
4779
 
4066
- // src/commands/domains/verify.ts
4067
- var domainsVerifyCommand = defineCommand({
4068
- path: ["domains", "verify"],
4069
- summary: "Re-check DNS records and refresh domain verification",
4070
- sdkMethod: "domains.verify",
4071
- route: { method: "POST", path: "/v1/domains/{domainId}/verify" },
4072
- commandClass: "write",
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",
4073
4791
  args: [
4074
- { name: "domainId", summary: "Domain id to verify", isRequired: true }
4792
+ { name: "domainId", summary: "Marketing domain id", isRequired: true }
4075
4793
  ],
4076
- examples: ["brew-cli domains verify dom_8s1Kj"],
4077
- run: async ({ ctx, args }) => ({
4078
- data: await ctx.client().domains.verify({ domainId: args.domainId ?? "" })
4079
- })
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
+ }
4080
4812
  });
4081
- var SENDING_PURPOSES = /* @__PURE__ */ new Set(["marketing", "transactional"]);
4082
- function optionalText(value) {
4083
- return typeof value === "string" ? value : void 0;
4084
- }
4085
- var emailsAuditCommand = defineCommand({
4086
- path: ["emails", "audit"],
4087
- summary: "Audit raw email content for production readiness (5 credits when complete)",
4088
- sdkMethod: "emails.auditEmail",
4089
- route: { method: "POST", path: "/v1/emails/audit" },
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
+ },
4090
4824
  commandClass: "write",
4091
- isCredited: true,
4825
+ args: [
4826
+ { name: "domainId", summary: "Marketing domain id", isRequired: true }
4827
+ ],
4092
4828
  flags: [
4829
+ { flag: "--file <path>", summary: "CSV file, or - for stdin" },
4093
4830
  {
4094
- flag: "--file <path>",
4095
- summary: "Email HTML file to audit, or - for stdin"
4096
- },
4097
- { flag: "--subject <text>", summary: "Inbox subject line" },
4098
- {
4099
- flag: "--preview-text <text>",
4100
- summary: "Inbox preview text; an explicit empty value stays empty"
4101
- },
4102
- {
4103
- flag: "--sending-purpose <purpose>",
4104
- summary: "marketing | transactional (default: marketing)"
4831
+ flag: "--column <header>",
4832
+ summary: "The column holding the addresses (by header)"
4105
4833
  },
4106
- INPUT_FLAG,
4107
4834
  IDEMPOTENCY_FLAG
4108
4835
  ],
4109
4836
  examples: [
4110
- 'brew-cli emails audit --file newsletter.html --subject "August update" --sending-purpose marketing',
4111
- 'cat email.html | brew-cli emails audit --file - --subject "Receipt" --sending-purpose transactional',
4112
- `brew-cli emails audit --input '{"emailHtml":"<p>Hello</p>","subject":"Hello"}'`
4837
+ "brew-cli domains unsubscribes import dom_123 --file optouts.csv --column Email"
4113
4838
  ],
4114
- run: async ({ ctx, flags }) => {
4115
- const base = await readJsonFlag(ctx, flags.input, "--input");
4116
- const emailHtml = await readTextFlag(ctx, flags.file, "--file");
4117
- const sendingPurpose = flagString(flags.sendingPurpose);
4118
- if (sendingPurpose !== void 0 && !SENDING_PURPOSES.has(sendingPurpose)) {
4119
- throw new CliUsageError(
4120
- `Unknown --sending-purpose '${sendingPurpose}' (expected marketing | transactional).`
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
+
4932
+ // src/commands/domains/update.ts
4933
+ var domainsUpdateCommand = defineCommand({
4934
+ path: ["domains", "update"],
4935
+ summary: "Update default sender settings for a domain",
4936
+ sdkMethod: "domains.updateSettings",
4937
+ route: { method: "PATCH", path: "/v1/domains/{domainId}" },
4938
+ commandClass: "write",
4939
+ args: [
4940
+ { name: "domainId", summary: "Domain id to update", isRequired: true }
4941
+ ],
4942
+ flags: [
4943
+ {
4944
+ flag: "--default-sender-name <name>",
4945
+ summary: "Default From display name"
4946
+ },
4947
+ {
4948
+ flag: "--default-from-email <email>",
4949
+ summary: "Default From address on this domain"
4950
+ },
4951
+ {
4952
+ flag: "--default-reply-to-email <email>",
4953
+ summary: "Default Reply-To address"
4954
+ },
4955
+ INPUT_FLAG
4956
+ ],
4957
+ examples: [
4958
+ 'brew-cli domains update dom_8s1Kj --default-sender-name "Brew Coffee"'
4959
+ ],
4960
+ run: async ({ ctx, args, flags }) => {
4961
+ const base = await readJsonFlag(ctx, flags.input, "--input");
4962
+ const input = mergeInput(base, {
4963
+ defaultSenderName: flagString(flags.defaultSenderName),
4964
+ defaultFromEmail: flagString(flags.defaultFromEmail),
4965
+ defaultReplyToEmail: flagString(flags.defaultReplyToEmail)
4966
+ });
4967
+ if (Object.keys(input).length === 0) {
4968
+ throw new CliUsageError("Nothing to update \u2014 pass flags or --input.");
4969
+ }
4970
+ const result = await ctx.client().domains.updateSettings(
4971
+ asSdkInput({
4972
+ ...input,
4973
+ domainId: args.domainId ?? ""
4974
+ })
4975
+ );
4976
+ return { data: result };
4977
+ }
4978
+ });
4979
+
4980
+ // src/commands/domains/verify.ts
4981
+ var domainsVerifyCommand = defineCommand({
4982
+ path: ["domains", "verify"],
4983
+ summary: "Re-check DNS records and refresh domain verification",
4984
+ sdkMethod: "domains.verify",
4985
+ route: { method: "POST", path: "/v1/domains/{domainId}/verify" },
4986
+ commandClass: "write",
4987
+ args: [
4988
+ { name: "domainId", summary: "Domain id to verify", isRequired: true }
4989
+ ],
4990
+ examples: ["brew-cli domains verify dom_8s1Kj"],
4991
+ run: async ({ ctx, args }) => ({
4992
+ data: await ctx.client().domains.verify({ domainId: args.domainId ?? "" })
4993
+ })
4994
+ });
4995
+ var SENDING_PURPOSES = /* @__PURE__ */ new Set(["marketing", "transactional"]);
4996
+ function optionalText(value) {
4997
+ return typeof value === "string" ? value : void 0;
4998
+ }
4999
+ var emailsAuditCommand = defineCommand({
5000
+ path: ["emails", "audit"],
5001
+ summary: "Audit raw email content for production readiness (5 credits when complete)",
5002
+ sdkMethod: "emails.auditEmail",
5003
+ route: { method: "POST", path: "/v1/emails/audit" },
5004
+ commandClass: "write",
5005
+ isCredited: true,
5006
+ flags: [
5007
+ {
5008
+ flag: "--file <path>",
5009
+ summary: "Email HTML file to audit, or - for stdin"
5010
+ },
5011
+ { flag: "--subject <text>", summary: "Inbox subject line" },
5012
+ {
5013
+ flag: "--preview-text <text>",
5014
+ summary: "Inbox preview text; an explicit empty value stays empty"
5015
+ },
5016
+ {
5017
+ flag: "--sending-purpose <purpose>",
5018
+ summary: "marketing | transactional (default: marketing)"
5019
+ },
5020
+ INPUT_FLAG,
5021
+ IDEMPOTENCY_FLAG
5022
+ ],
5023
+ examples: [
5024
+ 'brew-cli emails audit --file newsletter.html --subject "August update" --sending-purpose marketing',
5025
+ 'cat email.html | brew-cli emails audit --file - --subject "Receipt" --sending-purpose transactional',
5026
+ `brew-cli emails audit --input '{"emailHtml":"<p>Hello</p>","subject":"Hello"}'`
5027
+ ],
5028
+ run: async ({ ctx, flags }) => {
5029
+ const base = await readJsonFlag(ctx, flags.input, "--input");
5030
+ const emailHtml = await readTextFlag(ctx, flags.file, "--file");
5031
+ const sendingPurpose = flagString(flags.sendingPurpose);
5032
+ if (sendingPurpose !== void 0 && !SENDING_PURPOSES.has(sendingPurpose)) {
5033
+ throw new CliUsageError(
5034
+ `Unknown --sending-purpose '${sendingPurpose}' (expected marketing | transactional).`
4121
5035
  );
4122
5036
  }
4123
5037
  const input = mergeInput(base, {
@@ -4160,19 +5074,41 @@ var emailsCloneCommand = defineCommand({
4160
5074
  flag: "--email-version-id <id>",
4161
5075
  summary: "Exact source version to clone (default: latest)"
4162
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
+ },
4163
5089
  IDEMPOTENCY_FLAG
4164
5090
  ],
4165
5091
  examples: [
4166
5092
  "brew-cli emails clone eml_2SmZOWV3ZQ7W5x6g3m4p",
4167
- "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"'
4168
5095
  ],
4169
5096
  run: async ({ ctx, args, flags }) => {
4170
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
+ }
4171
5104
  return {
4172
5105
  data: await ctx.client().emails.clone(
4173
5106
  asSdkInput({
4174
5107
  emailId: args.emailId ?? "",
4175
- ...emailVersionId === void 0 ? {} : { emailVersionId }
5108
+ ...emailVersionId === void 0 ? {} : { emailVersionId },
5109
+ ...title === void 0 ? {} : { title },
5110
+ ...groupId === void 0 ? {} : { groupId },
5111
+ ...groupName === void 0 ? {} : { groupName }
4176
5112
  }),
4177
5113
  requestOptions(flags)
4178
5114
  )
@@ -4184,7 +5120,7 @@ var emailsCloneCommand = defineCommand({
4184
5120
  var emailsCreateInboxPlacementTestCommand = defineCommand({
4185
5121
  path: ["emails", "create-inbox-placement-test"],
4186
5122
  summary: "Seed-test where the design lands (inbox vs spam) via a real small send (10 credits)",
4187
- sdkMethod: "emails.createInboxPlacementTest",
5123
+ sdkMethod: "emails.inboxPlacementTests.create",
4188
5124
  route: { method: "POST", path: "/v1/emails/{emailId}/inbox-placement-tests" },
4189
5125
  commandClass: "write",
4190
5126
  isCredited: true,
@@ -4228,9 +5164,9 @@ var emailsCreateInboxPlacementTestCommand = defineCommand({
4228
5164
  const emailVersionId = flagString(flags.emailVersionId);
4229
5165
  const providers = toStringArray(flags.providers);
4230
5166
  return {
4231
- // The API answers 202 with a pending test (status: "collecting");
4232
- // poll `emails get-inbox-placement-results --test-id` for the outcome.
4233
- data: await ctx.client().emails.createInboxPlacementTest(
5167
+ // The API answers 202 with a pending test (status: "queued"); poll
5168
+ // `emails get-inbox-placement-results --test-id` for the outcome.
5169
+ data: await ctx.client().emails.inboxPlacementTests.create(
4234
5170
  asSdkInput({
4235
5171
  emailId: args.emailId ?? "",
4236
5172
  domainId,
@@ -4261,9 +5197,13 @@ var emailsDeleteCommand = defineCommand({
4261
5197
  });
4262
5198
 
4263
5199
  // src/commands/emails/edit.ts
5200
+ var ENVELOPE_FIELDS = ["title", "subjectLine", "groupId"];
5201
+ function isText(value) {
5202
+ return typeof value === "string" && value !== "";
5203
+ }
4264
5204
  var emailsEditCommand = defineCommand({
4265
5205
  path: ["emails", "edit"],
4266
- summary: "AI-edit an email design, and/or set its subject line (subject-only is free)",
5206
+ summary: "AI-edit an email design, and/or set its subject line, title or group (free without a prompt)",
4267
5207
  sdkMethod: "emails.edit",
4268
5208
  route: { method: "PATCH", path: "/v1/emails/{emailId}" },
4269
5209
  commandClass: "write",
@@ -4283,30 +5223,54 @@ var emailsEditCommand = defineCommand({
4283
5223
  flag: "--subject-line <text>",
4284
5224
  summary: "The design's default inbox subject line; alone it skips the AI run"
4285
5225
  },
5226
+ {
5227
+ flag: "--title <text>",
5228
+ summary: "Rename the design (its canvas name); alone it skips the AI run"
5229
+ },
5230
+ {
5231
+ flag: "--group-id <groupId>",
5232
+ summary: "Move the design into this existing group; alone it skips the AI run"
5233
+ },
5234
+ {
5235
+ flag: "--ungroup",
5236
+ summary: "Move the design to Ungrouped (groupId: null)"
5237
+ },
4286
5238
  INPUT_FLAG
4287
5239
  ],
4288
5240
  examples: [
4289
5241
  'brew-cli emails edit eml_2SmZOWV3ZQ7W5x6g3m4p --prompt "Tighten the hero copy"',
4290
- 'brew-cli emails edit eml_2SmZOWV3ZQ7W5x6g3m4p --subject-line "Your September roundup"'
5242
+ 'brew-cli emails edit eml_2SmZOWV3ZQ7W5x6g3m4p --subject-line "Your September roundup"',
5243
+ 'brew-cli emails edit eml_2SmZOWV3ZQ7W5x6g3m4p --title "Fall sale v2" --group-id grp_7Hq2',
5244
+ "brew-cli emails edit eml_2SmZOWV3ZQ7W5x6g3m4p --ungroup"
4291
5245
  ],
4292
5246
  run: async ({ ctx, args, flags }) => {
5247
+ const groupId = flagString(flags.groupId);
5248
+ if (groupId !== void 0 && flags.ungroup === true) {
5249
+ throw new CliUsageError(
5250
+ "--group-id and --ungroup conflict: pick one group move."
5251
+ );
5252
+ }
4293
5253
  const base = await readJsonFlag(ctx, flags.input, "--input");
4294
5254
  const input = mergeInput(base, {
4295
5255
  prompt: flagString(flags.prompt),
4296
5256
  emailVersionId: flagString(flags.emailVersionId),
4297
5257
  contentUrls: toStringArray(flags.contentUrls),
4298
- subjectLine: flagString(flags.subjectLine)
5258
+ subjectLine: flagString(flags.subjectLine),
5259
+ title: flagString(flags.title),
5260
+ groupId: flags.ungroup === true ? null : groupId
4299
5261
  });
4300
- const isPromptEdit = typeof input.prompt === "string" && input.prompt !== "";
4301
- const hasSubjectLine = typeof input.subjectLine === "string" && input.subjectLine !== "";
4302
- if (!(isPromptEdit || hasSubjectLine)) {
5262
+ const isPromptEdit = isText(input.prompt);
5263
+ const envelope = ENVELOPE_FIELDS.filter(
5264
+ (field) => isText(input[field]) || field === "groupId" && input[field] === null
5265
+ );
5266
+ if (!isPromptEdit && envelope.length === 0) {
4303
5267
  throw new CliUsageError(
4304
- "--prompt or --subject-line is required (or provide either via --input)."
5268
+ "Pass --prompt, or at least one of --subject-line, --title, --group-id or --ungroup (or those fields via --input)."
4305
5269
  );
4306
5270
  }
4307
5271
  progress(
4308
5272
  ctx,
4309
- isPromptEdit ? "Editing email\u2026 (typically 30-90s)" : "Setting the subject line\u2026"
5273
+ isPromptEdit ? "Editing email\u2026 (typically 30-90s)" : envelope.length === 1 && envelope[0] === "subjectLine" ? "Setting the subject line\u2026" : "Updating the design (no AI run)\u2026"
4310
5274
  );
4311
5275
  const result = await ctx.client().emails.edit(
4312
5276
  asSdkInput({ ...input, emailId: args.emailId ?? "" }),
@@ -4327,12 +5291,16 @@ var emailsExportCommand = defineCommand({
4327
5291
  flags: [
4328
5292
  {
4329
5293
  flag: "--provider <provider>",
4330
- 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"
4331
5295
  },
4332
5296
  {
4333
5297
  flag: "--template-name <name>",
4334
5298
  summary: "Template name in the ESP (default: the email title)"
4335
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
+ },
4336
5304
  {
4337
5305
  flag: "--dry-run",
4338
5306
  summary: "Validate design, ownership, and ESP connection without creating a template"
@@ -4347,18 +5315,20 @@ var emailsExportCommand = defineCommand({
4347
5315
  const provider = flagString(flags.provider);
4348
5316
  if (provider === void 0) {
4349
5317
  throw new CliUsageError(
4350
- "--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."
4351
5319
  );
4352
5320
  }
4353
5321
  const templateName = flagString(flags.templateName);
5322
+ const senderEmail = flagString(flags.senderEmail);
4354
5323
  return {
4355
5324
  data: await ctx.client().emails.export(
4356
5325
  asSdkInput({
4357
5326
  emailId: args.emailId ?? "",
4358
5327
  provider,
4359
5328
  ...templateName === void 0 ? {} : { templateName },
5329
+ ...senderEmail === void 0 ? {} : { senderEmail },
4360
5330
  // The spec field is snake_case, unlike the rest of the API surface.
4361
- ...flags.dryRun === true ? { dry_run: true } : {}
5331
+ ...flags.dryRun === true ? { dryRun: true } : {}
4362
5332
  }),
4363
5333
  requestOptions(flags)
4364
5334
  )
@@ -4427,10 +5397,9 @@ var emailsGenerateCommand = defineCommand({
4427
5397
  // src/commands/emails/get.ts
4428
5398
  var emailsGetCommand = defineCommand({
4429
5399
  path: ["emails", "get"],
4430
- summary: "Fetch one email design by id",
4431
- sdkMethod: null,
4432
- derivedFrom: "emails.list",
4433
- route: { method: "GET", path: "/v1/emails" },
5400
+ summary: "Fetch one email design by id \u2014 the bare row",
5401
+ sdkMethod: "emails.get",
5402
+ route: { method: "GET", path: "/v1/emails/{emailId}" },
4434
5403
  commandClass: "read",
4435
5404
  args: [
4436
5405
  {
@@ -4442,39 +5411,126 @@ var emailsGetCommand = defineCommand({
4442
5411
  flags: [
4443
5412
  {
4444
5413
  flag: "--include <tokens>",
4445
- 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)"
4446
5423
  }
4447
5424
  ],
4448
5425
  examples: [
4449
5426
  "brew-cli emails get eml_2SmZOWV3ZQ7W5x6g3m4p",
4450
- "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"
4451
5430
  ],
4452
5431
  run: async ({ ctx, args, flags }) => {
4453
5432
  const emailId = args.emailId ?? "";
4454
5433
  const include = flagString(flags.include);
4455
- const result = await ctx.client().emails.list({
4456
- emailId,
4457
- ...include === void 0 ? {} : { 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
+ }
5446
+ return {
5447
+ data: await rawRequest(ctx, {
5448
+ method: "GET",
5449
+ path: `/v1/emails/${encodeURIComponent(emailId)}`,
5450
+ query: { include, emailVersionId, runId }
5451
+ })
5452
+ };
5453
+ }
5454
+ });
5455
+
5456
+ // src/commands/emails/get-audit.ts
5457
+ var emailsGetAuditCommand = defineCommand({
5458
+ path: ["emails", "get-audit"],
5459
+ summary: "Read a saved email audit: one page of its findings (free; never reruns the audit)",
5460
+ sdkMethod: null,
5461
+ isRawTransport: true,
5462
+ route: { method: "GET", path: "/v1/emails/audits/{auditId}" },
5463
+ commandClass: "read",
5464
+ args: [
5465
+ {
5466
+ name: "auditId",
5467
+ summary: "The auditId `emails audit` returned (reports are kept 7 days)",
5468
+ isRequired: true
5469
+ }
5470
+ ],
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
+ ],
5477
+ examples: [
5478
+ "brew-cli emails get-audit 6f1e2d3c-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
5479
+ "brew-cli emails get-audit 6f1e2d3c-4b5a-4c7d-8e9f-0a1b2c3d4e5f --limit 20"
5480
+ ],
5481
+ // The next page is `--cursor <pagination.cursor>`; `pagination.hasMore`
5482
+ // says whether one exists.
5483
+ run: async ({ ctx, args, flags }) => {
5484
+ const limit = flagInt(flags.limit, "--limit");
5485
+ const body = await rawRequest(ctx, {
5486
+ method: "GET",
5487
+ path: `/v1/emails/audits/${encodeURIComponent(args.auditId ?? "")}`,
5488
+ query: {
5489
+ cursor: flagString(flags.cursor),
5490
+ limit: limit === void 0 ? void 0 : String(limit)
5491
+ }
4458
5492
  });
4459
- const email = result.data[0];
4460
- if (email === void 0) {
4461
- throw new CliApiError({
4462
- status: 404,
4463
- code: "EMAIL_NOT_FOUND",
4464
- type: "not_found",
4465
- message: `No email design found for ${emailId}`,
4466
- suggestion: "List designs with `brew-cli emails list`."
4467
- });
5493
+ return { data: body };
5494
+ }
5495
+ });
5496
+
5497
+ // src/commands/emails/get-client-preview.ts
5498
+ var emailsGetClientPreviewCommand = defineCommand({
5499
+ path: ["emails", "get-client-preview"],
5500
+ summary: "Poll a client-preview rendering job: per-client screenshot links once it settles (free; never re-renders)",
5501
+ sdkMethod: null,
5502
+ isRawTransport: true,
5503
+ route: { method: "GET", path: "/v1/emails/client-previews/{previewId}" },
5504
+ commandClass: "read",
5505
+ args: [
5506
+ {
5507
+ name: "previewId",
5508
+ summary: "The previewId `emails preview-clients` returned",
5509
+ isRequired: true
4468
5510
  }
4469
- return { data: email };
5511
+ ],
5512
+ examples: [
5513
+ "brew-cli emails get-client-preview prv_0b7f3c1e-9a2d-4e8b-b6c5-3d1f2a9e8c47",
5514
+ "brew-cli emails get-client-preview prv_0b7f3c1e-9a2d-4e8b-b6c5-3d1f2a9e8c47 --json | jq '.previews[] | {label, status, imageUrl}'"
5515
+ ],
5516
+ // While `status` is `queued` or `running`, poll again after
5517
+ // `nextPollAfterMs`. It settles as `completed`, `partially_completed` or
5518
+ // `failed`; each client then carries its `imageUrl`, or a `reason` and
5519
+ // whether it is `retryable`.
5520
+ run: async ({ ctx, args }) => {
5521
+ const body = await rawRequest(ctx, {
5522
+ method: "GET",
5523
+ path: `/v1/emails/client-previews/${encodeURIComponent(args.previewId ?? "")}`
5524
+ });
5525
+ return { data: body };
4470
5526
  }
4471
5527
  });
4472
5528
 
4473
5529
  // src/commands/emails/get-inbox-placement-results.ts
4474
5530
  var emailsGetInboxPlacementResultsCommand = defineCommand({
4475
5531
  path: ["emails", "get-inbox-placement-results"],
4476
- summary: "Inbox placement results: one test with --test-id, else the recent tests",
4477
- sdkMethod: "emails.getInboxPlacementResults",
5532
+ summary: "Inbox placement results: the recent tests, or one test with --test-id",
5533
+ sdkMethod: "emails.inboxPlacementTests.list",
4478
5534
  route: { method: "GET", path: "/v1/emails/{emailId}/inbox-placement-tests" },
4479
5535
  commandClass: "read",
4480
5536
  args: [
@@ -4486,22 +5542,38 @@ var emailsGetInboxPlacementResultsCommand = defineCommand({
4486
5542
  ],
4487
5543
  flags: [
4488
5544
  {
5545
+ // The list no longer filters by test id: --test-id reads the real
5546
+ // detail route, the same one `emails inbox-placement-tests get` binds.
4489
5547
  flag: "--test-id <id>",
4490
5548
  summary: "One test: live status + per-provider placement (re-poll ~30s until completed)"
4491
- }
5549
+ },
5550
+ LIMIT_FLAG,
5551
+ CURSOR_FLAG,
5552
+ INPUT_FLAG
4492
5553
  ],
4493
5554
  examples: [
4494
5555
  "brew-cli emails get-inbox-placement-results eml_2SmZOWV3ZQ7W5x6g3m4p",
4495
5556
  "brew-cli emails get-inbox-placement-results eml_2SmZOWV3ZQ7W5x6g3m4p --test-id ibp_2f1c9d8a"
4496
5557
  ],
4497
- run: async ({ ctx, args, flags }) => ({
4498
- data: await ctx.client().emails.getInboxPlacementResults(
4499
- asSdkInput({
4500
- emailId: args.emailId ?? "",
4501
- testId: flagString(flags.testId)
4502
- })
4503
- )
4504
- })
5558
+ run: async ({ ctx, args, flags }) => {
5559
+ const emailId = args.emailId ?? "";
5560
+ const testId = flagString(flags.testId);
5561
+ const inboxPlacementTests = ctx.client().emails.inboxPlacementTests;
5562
+ if (testId !== void 0) {
5563
+ return { data: await inboxPlacementTests.get(emailId, testId) };
5564
+ }
5565
+ const base = await readJsonFlag(ctx, flags.input, "--input");
5566
+ const input = mergeInput(base, {
5567
+ emailId,
5568
+ limit: flagInt(flags.limit, "--limit"),
5569
+ cursor: flagString(flags.cursor)
5570
+ });
5571
+ return {
5572
+ data: await inboxPlacementTests.list(
5573
+ asSdkInput(input)
5574
+ )
5575
+ };
5576
+ }
4505
5577
  });
4506
5578
 
4507
5579
  // src/commands/emails/groups/create.ts
@@ -4558,6 +5630,29 @@ var emailsGroupsDeleteCommand = defineCommand({
4558
5630
  })
4559
5631
  });
4560
5632
 
5633
+ // src/commands/emails/groups/get.ts
5634
+ var emailsGroupsGetCommand = defineCommand({
5635
+ path: ["emails", "groups", "get"],
5636
+ summary: "Fetch one email group by id \u2014 the bare row",
5637
+ sdkMethod: "emailGroups.get",
5638
+ route: { method: "GET", path: "/v1/email-groups/{groupId}" },
5639
+ commandClass: "read",
5640
+ args: [
5641
+ {
5642
+ name: "groupId",
5643
+ summary: "Group id (`grp_\u2026`, or the literal `ungrouped`)",
5644
+ isRequired: true
5645
+ }
5646
+ ],
5647
+ examples: [
5648
+ "brew-cli emails groups get grp_2f1c9d8a",
5649
+ "brew-cli emails groups get ungrouped"
5650
+ ],
5651
+ run: async ({ ctx, args }) => ({
5652
+ data: await ctx.client().emailGroups.get(args.groupId ?? "")
5653
+ })
5654
+ });
5655
+
4561
5656
  // src/commands/emails/groups/list.ts
4562
5657
  var emailsGroupsListCommand = defineCommand({
4563
5658
  path: ["emails", "groups", "list"],
@@ -4715,8 +5810,16 @@ var emailsImportFigmaCommand = defineCommand({
4715
5810
  summary: "Representation returned in content: jsx (default) or html"
4716
5811
  },
4717
5812
  {
4718
- flag: "--subject-line <text>",
4719
- summary: "The design's default inbox subject line"
5813
+ flag: "--subject-line <text>",
5814
+ summary: "The design's default inbox subject line"
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"
4720
5823
  },
4721
5824
  IDEMPOTENCY_FLAG
4722
5825
  ],
@@ -4734,13 +5837,20 @@ var emailsImportFigmaCommand = defineCommand({
4734
5837
  const title = flagString(flags.title);
4735
5838
  const format = flagString(flags.format);
4736
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
+ }
4737
5845
  return {
4738
5846
  data: await ctx.client().emails.importFigma(
4739
5847
  asSdkInput({
4740
5848
  figmaUrl,
4741
5849
  ...title === void 0 ? {} : { title },
4742
5850
  ...format === void 0 ? {} : { format },
4743
- ...subjectLine === void 0 ? {} : { subjectLine }
5851
+ ...subjectLine === void 0 ? {} : { subjectLine },
5852
+ ...groupId === void 0 ? {} : { groupId },
5853
+ ...groupName === void 0 ? {} : { groupName }
4744
5854
  }),
4745
5855
  requestOptions(flags)
4746
5856
  )
@@ -4748,53 +5858,108 @@ var emailsImportFigmaCommand = defineCommand({
4748
5858
  }
4749
5859
  });
4750
5860
 
5861
+ // src/commands/emails/inbox-placement-tests/get.ts
5862
+ var emailsInboxPlacementTestsGetCommand = defineCommand({
5863
+ path: ["emails", "inbox-placement-tests", "get"],
5864
+ summary: "Fetch one inbox-placement (seed) test \u2014 the bare row, re-poll ~30s until completed",
5865
+ sdkMethod: "emails.inboxPlacementTests.get",
5866
+ route: {
5867
+ method: "GET",
5868
+ path: "/v1/emails/{emailId}/inbox-placement-tests/{testId}"
5869
+ },
5870
+ commandClass: "read",
5871
+ args: [
5872
+ {
5873
+ name: "emailId",
5874
+ summary: "Design id the test ran on",
5875
+ isRequired: true
5876
+ },
5877
+ {
5878
+ name: "testId",
5879
+ summary: "Test id returned by `emails create-inbox-placement-test`",
5880
+ isRequired: true
5881
+ }
5882
+ ],
5883
+ examples: [
5884
+ "brew-cli emails inbox-placement-tests get eml_2SmZOWV3ZQ7W5x6g3m4p ibp_2f1c9d8a"
5885
+ ],
5886
+ run: async ({ ctx, args }) => ({
5887
+ data: await ctx.client().emails.inboxPlacementTests.get(args.emailId ?? "", args.testId ?? "")
5888
+ })
5889
+ });
5890
+
4751
5891
  // src/commands/emails/list.ts
4752
5892
  var emailsListCommand = defineCommand({
4753
5893
  path: ["emails", "list"],
4754
- summary: "List email designs (the single email read)",
5894
+ summary: "List email designs; one design is `emails get`",
4755
5895
  sdkMethod: "emails.list",
4756
5896
  route: { method: "GET", path: "/v1/emails" },
4757
5897
  commandClass: "read",
4758
5898
  flags: [
4759
5899
  {
4760
5900
  flag: "--status <status>",
4761
- summary: "Filter by status: streaming | complete | error"
5901
+ summary: "Filter by status: generating | ready | failed"
4762
5902
  },
4763
5903
  {
4764
5904
  flag: "--group-id <groupId>",
4765
5905
  summary: "Filter by one group id; use ungrouped for no saved group"
4766
5906
  },
4767
5907
  {
4768
- flag: "--sort <field>",
4769
- summary: "Sort by updatedAt | createdAt | title"
5908
+ flag: "--sort-by <field>",
5909
+ summary: "Timestamp the page is ordered by and that --since/--until bound: updatedAt (default) | createdAt"
5910
+ },
5911
+ {
5912
+ flag: "--since <iso>",
5913
+ summary: "Inclusive lower bound on the --sort-by timestamp (ISO-8601)"
5914
+ },
5915
+ {
5916
+ flag: "--until <iso>",
5917
+ summary: "Inclusive upper bound on the --sort-by timestamp (ISO-8601)"
5918
+ },
5919
+ { flag: "--sort <field>", summary: "0.6 alias of --sort-by" },
5920
+ {
5921
+ flag: "--order <order>",
5922
+ summary: "0.6 flag: pages are newest first; only desc is accepted"
5923
+ },
5924
+ {
5925
+ flag: "--created-at-from <iso>",
5926
+ summary: "0.6 alias of --sort-by createdAt --since <iso>"
5927
+ },
5928
+ {
5929
+ flag: "--created-at-to <iso>",
5930
+ summary: "0.6 alias of --sort-by createdAt --until <iso>"
5931
+ },
5932
+ {
5933
+ flag: "--updated-at-from <iso>",
5934
+ summary: "0.6 alias of --sort-by updatedAt --since <iso>"
5935
+ },
5936
+ {
5937
+ flag: "--updated-at-to <iso>",
5938
+ summary: "0.6 alias of --sort-by updatedAt --until <iso>"
4770
5939
  },
4771
- { flag: "--order <order>", summary: "Sort order: asc | desc" },
4772
- { flag: "--created-at-from <iso>", summary: "Created at or after (ISO)" },
4773
- { flag: "--created-at-to <iso>", summary: "Created at or before (ISO)" },
4774
- { flag: "--updated-at-from <iso>", summary: "Updated at or after (ISO)" },
4775
- { flag: "--updated-at-to <iso>", summary: "Updated at or before (ISO)" },
4776
5940
  LIMIT_FLAG,
4777
5941
  CURSOR_FLAG,
4778
5942
  ALL_FLAG,
4779
5943
  INPUT_FLAG
4780
5944
  ],
4781
5945
  examples: [
4782
- "brew-cli emails list --status complete --limit 10",
4783
- "brew-cli emails list --group-id ungrouped --sort title --order asc",
4784
- "brew-cli emails list --updated-at-from 2026-08-01T00:00:00Z",
5946
+ "brew-cli emails list --status ready --limit 10",
5947
+ "brew-cli emails list --group-id ungrouped",
5948
+ "brew-cli emails list --sort-by createdAt --since 2026-08-01T00:00:00Z",
4785
5949
  "brew-cli emails list --all --json"
4786
5950
  ],
4787
5951
  run: async ({ ctx, flags }) => {
5952
+ const legacy = legacyWindow(
5953
+ flags,
5954
+ flagString(flags.sortBy) ?? flagString(flags.sort)
5955
+ );
4788
5956
  const base = await readJsonFlag(ctx, flags.input, "--input");
4789
5957
  const input = mergeInput(base, {
4790
5958
  status: flagString(flags.status),
4791
5959
  groupId: flagString(flags.groupId),
4792
- sort: flagString(flags.sort),
4793
- order: flagString(flags.order),
4794
- createdAtFrom: flagString(flags.createdAtFrom),
4795
- createdAtTo: flagString(flags.createdAtTo),
4796
- updatedAtFrom: flagString(flags.updatedAtFrom),
4797
- updatedAtTo: flagString(flags.updatedAtTo),
5960
+ sortBy: flagString(flags.sortBy) ?? flagString(flags.sort) ?? legacy.sortBy,
5961
+ from: flagString(flags.since) ?? legacy.from,
5962
+ to: flagString(flags.until) ?? legacy.to,
4798
5963
  limit: flagInt(flags.limit, "--limit"),
4799
5964
  cursor: flagString(flags.cursor)
4800
5965
  });
@@ -4818,6 +5983,40 @@ var emailsListCommand = defineCommand({
4818
5983
  return { data: result, human: renderEmails(result.data) };
4819
5984
  }
4820
5985
  });
5986
+ function legacyWindow(flags, explicitSortBy) {
5987
+ const order = flagString(flags.order);
5988
+ if (order !== void 0 && order !== "desc") {
5989
+ throw new CliUsageError(
5990
+ "GET /v1/emails pages newest first only; --order asc has no equivalent."
5991
+ );
5992
+ }
5993
+ const created = {
5994
+ from: flagString(flags.createdAtFrom),
5995
+ to: flagString(flags.createdAtTo)
5996
+ };
5997
+ const updated = {
5998
+ from: flagString(flags.updatedAtFrom),
5999
+ to: flagString(flags.updatedAtTo)
6000
+ };
6001
+ const hasCreated = created.from !== void 0 || created.to !== void 0;
6002
+ const hasUpdated = updated.from !== void 0 || updated.to !== void 0;
6003
+ if (hasCreated && hasUpdated) {
6004
+ throw new CliUsageError(
6005
+ "The API bounds one timestamp per page: pass --created-at-* or --updated-at-*, not both (or --sort-by with --since/--until)."
6006
+ );
6007
+ }
6008
+ if (!(hasCreated || hasUpdated)) {
6009
+ return { sortBy: void 0, from: void 0, to: void 0 };
6010
+ }
6011
+ const column = hasCreated ? "createdAt" : "updatedAt";
6012
+ if (explicitSortBy !== void 0 && explicitSortBy !== column) {
6013
+ const prefix = hasCreated ? "--created-at-*" : "--updated-at-*";
6014
+ throw new CliUsageError(
6015
+ `${prefix} bounds ${column}, but the page is ordered by ${explicitSortBy}; the API bounds the --sort-by timestamp only.`
6016
+ );
6017
+ }
6018
+ return { sortBy: column, ...hasCreated ? created : updated };
6019
+ }
4821
6020
  function renderEmails(rows) {
4822
6021
  if (rows.length === 0) {
4823
6022
  return "No email designs found.";
@@ -4834,7 +6033,7 @@ function renderEmails(rows) {
4834
6033
  // src/commands/emails/preview-clients.ts
4835
6034
  var emailsPreviewClientsCommand = defineCommand({
4836
6035
  path: ["emails", "preview-clients"],
4837
- summary: "Render the design across real email clients (10 credits)",
6036
+ summary: "Start a rendering job across real email clients (10 credits); poll it with `emails get-client-preview`",
4838
6037
  sdkMethod: "emails.previewClients",
4839
6038
  route: { method: "POST", path: "/v1/emails/{emailId}/client-previews" },
4840
6039
  commandClass: "write",
@@ -4847,25 +6046,49 @@ var emailsPreviewClientsCommand = defineCommand({
4847
6046
  flag: "--clients <ids...>",
4848
6047
  summary: "Client id(s) to render, repeatable (e.g. applemail16 iphone16_18); default: a popular spread"
4849
6048
  },
6049
+ {
6050
+ flag: "--email-version-id <id>",
6051
+ summary: "Render this saved version (default: the latest)"
6052
+ },
4850
6053
  IDEMPOTENCY_FLAG
4851
6054
  ],
4852
6055
  examples: [
4853
6056
  "brew-cli emails preview-clients eml_2SmZOWV3ZQ7W5x6g3m4p",
4854
- "brew-cli emails preview-clients eml_2SmZOWV3ZQ7W5x6g3m4p --clients applemail16 outlook2021_win11_lm_dt"
6057
+ "brew-cli emails preview-clients eml_2SmZOWV3ZQ7W5x6g3m4p --clients applemail16 outlook2021_win11_lm_dt",
6058
+ 'PREVIEW_ID=$(brew-cli emails preview-clients eml_2SmZOWV3ZQ7W5x6g3m4p --json | jq -r .previewId) && brew-cli emails get-client-preview "$PREVIEW_ID"'
4855
6059
  ],
6060
+ // The API answers 202 with the admitted job (or 200 with the existing job
6061
+ // for the same version and clients): `previewId`, its `status` and
6062
+ // per-client progress. Screenshots arrive on the job once it settles, so
6063
+ // `emails get-client-preview <previewId>` is how they are read.
4856
6064
  run: async ({ ctx, args, flags }) => {
4857
6065
  const clients = toStringArray(flags.clients);
4858
- return {
4859
- data: await ctx.client().emails.previewClients(
4860
- asSdkInput({
4861
- emailId: args.emailId ?? "",
4862
- ...clients === void 0 ? {} : { clients }
4863
- }),
4864
- requestOptions(flags)
4865
- )
4866
- };
6066
+ const emailVersionId = flagString(flags.emailVersionId);
6067
+ const job = await ctx.client().emails.previewClients(
6068
+ asSdkInput({
6069
+ emailId: args.emailId ?? "",
6070
+ ...clients === void 0 ? {} : { clients },
6071
+ ...emailVersionId === void 0 ? {} : { emailVersionId }
6072
+ }),
6073
+ requestOptions(flags)
6074
+ );
6075
+ const hint = pollHint(job);
6076
+ if (hint !== void 0) {
6077
+ progress(ctx, hint);
6078
+ }
6079
+ return { data: job };
4867
6080
  }
4868
6081
  });
6082
+ function pollHint(job) {
6083
+ if (typeof job !== "object" || job === null || !("previewId" in job) || !("status" in job)) {
6084
+ return;
6085
+ }
6086
+ const { previewId, status } = job;
6087
+ if (typeof previewId !== "string" || status !== "queued" && status !== "running") {
6088
+ return;
6089
+ }
6090
+ return `Rendering job ${previewId} is ${status}. Poll it with: brew-cli emails get-client-preview ${previewId}`;
6091
+ }
4869
6092
 
4870
6093
  // src/commands/emails/restore.ts
4871
6094
  var emailsRestoreCommand = defineCommand({
@@ -4879,19 +6102,24 @@ var emailsRestoreCommand = defineCommand({
4879
6102
  ],
4880
6103
  // --version is taken by the CLI itself, hence --to-version.
4881
6104
  flags: [
4882
- { flag: "--to-version <n>", summary: "Version number to restore" },
6105
+ {
6106
+ flag: "--to-version <emailVersionId>",
6107
+ summary: "Version id to restore (from `emails get <emailId> --include versions`)"
6108
+ },
4883
6109
  IDEMPOTENCY_FLAG
4884
6110
  ],
4885
- examples: ["brew-cli emails restore eml_2SmZOWV3ZQ7W5x6g3m4p --to-version 2"],
6111
+ examples: [
6112
+ "brew-cli emails restore eml_2SmZOWV3ZQ7W5x6g3m4p --to-version emv_7Hq2"
6113
+ ],
4886
6114
  run: async ({ ctx, args, flags }) => {
4887
- const version = flagInt(flags.toVersion, "--to-version");
4888
- if (version === void 0) {
6115
+ const emailVersionId = flagString(flags.toVersion);
6116
+ if (emailVersionId === void 0) {
4889
6117
  throw new CliUsageError(
4890
- "--to-version is required (see `brew-cli emails get <emailId> --include versions`)."
6118
+ "--to-version is required (a version id; see `brew-cli emails get <emailId> --include versions`)."
4891
6119
  );
4892
6120
  }
4893
6121
  const result = await ctx.client().emails.restore(
4894
- { emailId: args.emailId ?? "", version },
6122
+ { emailId: args.emailId ?? "", emailVersionId },
4895
6123
  requestOptions(flags)
4896
6124
  );
4897
6125
  return { data: result };
@@ -5061,6 +6289,26 @@ var fieldsDeleteCommand = defineCommand({
5061
6289
  })
5062
6290
  });
5063
6291
 
6292
+ // src/commands/fields/get.ts
6293
+ var fieldsGetCommand = defineCommand({
6294
+ path: ["fields", "get"],
6295
+ summary: "Fetch one contact field definition by name \u2014 the bare row",
6296
+ sdkMethod: "fields.get",
6297
+ route: { method: "GET", path: "/v1/fields/{fieldName}" },
6298
+ commandClass: "read",
6299
+ args: [
6300
+ {
6301
+ name: "fieldName",
6302
+ summary: "Field name (a core column or a custom field)",
6303
+ isRequired: true
6304
+ }
6305
+ ],
6306
+ examples: ["brew-cli fields get loyalty_tier"],
6307
+ run: async ({ ctx, args }) => ({
6308
+ data: await ctx.client().fields.get(args.fieldName ?? "")
6309
+ })
6310
+ });
6311
+
5064
6312
  // src/commands/fields/list.ts
5065
6313
  var fieldsListCommand = defineCommand({
5066
6314
  path: ["fields", "list"],
@@ -5068,9 +6316,221 @@ var fieldsListCommand = defineCommand({
5068
6316
  sdkMethod: "fields.list",
5069
6317
  route: { method: "GET", path: "/v1/fields" },
5070
6318
  commandClass: "read",
5071
- examples: ["brew-cli fields list"],
5072
- run: async ({ ctx }) => ({ data: await ctx.client().fields.list() })
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
+ }
6361
+ });
6362
+
6363
+ // src/commands/flows/get.ts
6364
+ var flowsGetCommand = defineCommand({
6365
+ path: ["flows", "get"],
6366
+ summary: "Fetch one public email flow by brand domain, with every step (day offset, wait, subject, template id)",
6367
+ sdkMethod: "flows.get",
6368
+ route: { method: "GET", path: "/v1/flows/{slug}" },
6369
+ commandClass: "read",
6370
+ args: [
6371
+ {
6372
+ name: "slug",
6373
+ summary: "The brand domain a `flows list` card carries (e.g. brew.new)",
6374
+ isRequired: true
6375
+ }
6376
+ ],
6377
+ flags: [
6378
+ {
6379
+ flag: "--include <keys>",
6380
+ summary: "Comma-separated expansions: html (each step\u2019s rendered HTML, best-effort per step)"
6381
+ }
6382
+ ],
6383
+ examples: [
6384
+ "brew-cli flows get brew.new",
6385
+ "brew-cli flows get brew.new --include html --json"
6386
+ ],
6387
+ run: async ({ ctx, args, flags }) => {
6388
+ const include = flagString(flags.include);
6389
+ const flow = await ctx.client().flows.get(
6390
+ args.slug ?? "",
6391
+ include === void 0 ? void 0 : { include }
6392
+ );
6393
+ return { data: flow, human: renderSteps(flow) };
6394
+ }
6395
+ });
6396
+ function renderSteps(flow) {
6397
+ const header = `${flow.title} \u2014 ${flow.brand.name} (${flow.slug}), ${flow.emailCount} emails over ${flow.spanDays} days${flow.anchor === void 0 ? "" : `, day 0 = ${flow.anchor}`}`;
6398
+ const table = renderTable(
6399
+ (flow.steps ?? []).map((step) => ({
6400
+ order: step.order,
6401
+ day: step.dayOffset,
6402
+ wait: step.delayDays,
6403
+ subject: step.subject,
6404
+ category: step.category,
6405
+ emailId: step.emailId
6406
+ })),
6407
+ [
6408
+ { key: "order", header: "#" },
6409
+ { key: "day", header: "DAY" },
6410
+ { key: "wait", header: "WAIT" },
6411
+ { key: "subject", header: "SUBJECT" },
6412
+ { key: "category", header: "CATEGORY" },
6413
+ { key: "emailId", header: "TEMPLATE" }
6414
+ ]
6415
+ );
6416
+ return `${header}
6417
+
6418
+ ${table}`;
6419
+ }
6420
+
6421
+ // src/commands/flows/list.ts
6422
+ var flowsListCommand = defineCommand({
6423
+ path: ["flows", "list"],
6424
+ summary: "List public email flows (real multi-step sequences by brand) as cards; `flows get <slug>` reads one",
6425
+ sdkMethod: "flows.list",
6426
+ route: { method: "GET", path: "/v1/flows" },
6427
+ commandClass: "read",
6428
+ flags: [
6429
+ {
6430
+ // --brand is taken by the global brand-selection flag.
6431
+ flag: "--brand-domain <domain>",
6432
+ summary: "Filter the list by brand domain"
6433
+ },
6434
+ {
6435
+ flag: "--category <category>",
6436
+ summary: "Filter by dominant step category (welcome, newsletter, \u2026)"
6437
+ },
6438
+ {
6439
+ flag: "--type <type>",
6440
+ summary: "Filter by how the sequence starts: signup | newsletter"
6441
+ },
6442
+ {
6443
+ flag: "--semantic <text>",
6444
+ summary: "Semantic search over the sequences (relevance order)"
6445
+ },
6446
+ {
6447
+ flag: "--sort <order>",
6448
+ summary: "List order: newest (default) | emails | span | remixes"
6449
+ },
6450
+ {
6451
+ flag: "--slug <domain>",
6452
+ summary: "0.6 shim: read ONE flow with its steps as a single-row page (`flows get <slug>` is the real read)"
6453
+ },
6454
+ {
6455
+ flag: "--include <keys>",
6456
+ summary: "With --slug only: `html` adds each step's rendered HTML"
6457
+ },
6458
+ LIMIT_FLAG,
6459
+ CURSOR_FLAG,
6460
+ ALL_FLAG,
6461
+ INPUT_FLAG
6462
+ ],
6463
+ examples: [
6464
+ "brew-cli flows list --type signup --sort emails",
6465
+ "brew-cli flows list --brand-domain brew.new --json",
6466
+ 'brew-cli flows list --semantic "developer onboarding drip"'
6467
+ ],
6468
+ run: async ({ ctx, flags }) => {
6469
+ const flows = ctx.client().flows;
6470
+ const base = await readJsonFlag(ctx, flags.input, "--input");
6471
+ const slug = flagString(flags.slug) ?? inputField(base, "slug");
6472
+ const include = flagString(flags.include) ?? inputField(base, "include");
6473
+ if (slug !== void 0) {
6474
+ const flow = await flows.get(
6475
+ slug,
6476
+ include === void 0 ? void 0 : { include }
6477
+ );
6478
+ return { data: singleRowPage(flow), human: renderFlows([flow]) };
6479
+ }
6480
+ if (include !== void 0) {
6481
+ throw includeRidesDetailRead("flows get", "--slug");
6482
+ }
6483
+ const input = mergeInput(base, {
6484
+ brand: flagString(flags.brandDomain),
6485
+ category: flagString(flags.category),
6486
+ type: flagString(flags.type),
6487
+ semantic: flagString(flags.semantic),
6488
+ sort: flagString(flags.sort),
6489
+ limit: flagInt(flags.limit, "--limit"),
6490
+ cursor: flagString(flags.cursor)
6491
+ });
6492
+ if (flags.all === true) {
6493
+ const rows = await collectAll(
6494
+ ctx,
6495
+ (cursor) => flows.list(
6496
+ asSdkInput({
6497
+ ...input,
6498
+ ...cursor === void 0 ? {} : { cursor }
6499
+ })
6500
+ )
6501
+ );
6502
+ return {
6503
+ data: { data: rows, pagination: { cursor: null, hasMore: false } },
6504
+ human: renderFlows(rows)
6505
+ };
6506
+ }
6507
+ const result = await flows.list(asSdkInput(input));
6508
+ return { data: result, human: renderFlows(result.data) };
6509
+ }
5073
6510
  });
6511
+ function renderFlows(rows) {
6512
+ if (rows.length === 0) {
6513
+ return "No flows found.";
6514
+ }
6515
+ return renderTable(
6516
+ rows.map((row) => ({
6517
+ slug: row.slug,
6518
+ brand: row.brand.name,
6519
+ type: row.type,
6520
+ category: row.category,
6521
+ emails: row.emailCount,
6522
+ spanDays: row.spanDays
6523
+ })),
6524
+ [
6525
+ { key: "slug", header: "SLUG" },
6526
+ { key: "brand", header: "BRAND" },
6527
+ { key: "type", header: "TYPE" },
6528
+ { key: "category", header: "CATEGORY" },
6529
+ { key: "emails", header: "EMAILS" },
6530
+ { key: "spanDays", header: "SPAN (DAYS)" }
6531
+ ]
6532
+ );
6533
+ }
5074
6534
 
5075
6535
  // src/commands/health.ts
5076
6536
  var healthCommand = defineCommand({
@@ -5092,9 +6552,31 @@ var integrationsListCommand = defineCommand({
5092
6552
  sdkMethod: "integrations.list",
5093
6553
  route: { method: "GET", path: "/v1/integrations" },
5094
6554
  commandClass: "read",
6555
+ flags: [LIMIT_FLAG, CURSOR_FLAG, ALL_FLAG],
5095
6556
  examples: ["brew-cli integrations list", "brew-cli integrations list --json"],
5096
- run: async ({ ctx }) => {
5097
- const result = await ctx.client().integrations.list();
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);
5098
6580
  return { data: result, human: renderIntegrations(result.data) };
5099
6581
  }
5100
6582
  });
@@ -5198,6 +6680,35 @@ var sendsCancelCommand = defineCommand({
5198
6680
  })
5199
6681
  });
5200
6682
 
6683
+ // src/commands/sends/get.ts
6684
+ var sendsGetCommand = defineCommand({
6685
+ path: ["sends", "get"],
6686
+ summary: "Fetch one send by id \u2014 the bare row with its lifetime stats",
6687
+ sdkMethod: "sends.get",
6688
+ route: { method: "GET", path: "/v1/sends/{sendId}" },
6689
+ commandClass: "read",
6690
+ args: [{ name: "sendId", summary: "Send id to fetch", isRequired: true }],
6691
+ flags: [
6692
+ {
6693
+ flag: "--include <tokens>",
6694
+ summary: "Comma-separated expansions: events"
6695
+ }
6696
+ ],
6697
+ examples: [
6698
+ "brew-cli sends get snd_9f2kX",
6699
+ "brew-cli sends get snd_9f2kX --include events"
6700
+ ],
6701
+ run: async ({ ctx, args, flags }) => {
6702
+ const include = flagString(flags.include);
6703
+ return {
6704
+ data: await ctx.client().sends.get(
6705
+ args.sendId ?? "",
6706
+ include === void 0 ? void 0 : { include }
6707
+ )
6708
+ };
6709
+ }
6710
+ });
6711
+
5201
6712
  // src/commands/sends/pause.ts
5202
6713
  var sendsPauseCommand = defineCommand({
5203
6714
  path: ["sends", "pause"],
@@ -5216,7 +6727,7 @@ var sendsPauseCommand = defineCommand({
5216
6727
  // src/commands/sends/resume.ts
5217
6728
  var sendsResumeCommand = defineCommand({
5218
6729
  path: ["sends", "resume"],
5219
- summary: "Resume a paused gradual send (the unsent tail is re-spread)",
6730
+ summary: "Resume a paused gradual send (the unsent tail is re-spread); the send reports `running` again",
5220
6731
  sdkMethod: "sends.resume",
5221
6732
  route: { method: "POST", path: "/v1/sends/{sendId}/resume" },
5222
6733
  commandClass: "write",
@@ -5228,6 +6739,41 @@ var sendsResumeCommand = defineCommand({
5228
6739
  })
5229
6740
  });
5230
6741
 
6742
+ // src/commands/templates/get.ts
6743
+ var templatesGetCommand = defineCommand({
6744
+ path: ["templates", "get"],
6745
+ summary: "Fetch one public template: its links and the referenceEmailId to remix; --include html adds its HTML",
6746
+ sdkMethod: null,
6747
+ isRawTransport: true,
6748
+ route: { method: "GET", path: "/v1/templates/{templateId}" },
6749
+ commandClass: "read",
6750
+ args: [
6751
+ {
6752
+ name: "templateId",
6753
+ summary: "Template id: the TEMPLATE column (emailId) of `templates list`",
6754
+ isRequired: true
6755
+ }
6756
+ ],
6757
+ flags: [
6758
+ {
6759
+ flag: "--include <tokens>",
6760
+ summary: "Expansions: html (the rendered HTML; a large page arrives as a content.url download link instead)"
6761
+ }
6762
+ ],
6763
+ examples: [
6764
+ "brew-cli templates get seed-vercel-newsletter",
6765
+ "brew-cli templates get seed-vercel-newsletter --include html --json"
6766
+ ],
6767
+ run: async ({ ctx, args, flags }) => {
6768
+ const body = await rawRequest(ctx, {
6769
+ method: "GET",
6770
+ path: `/v1/templates/${encodeURIComponent(args.templateId ?? "")}`,
6771
+ query: { include: flagString(flags.include) }
6772
+ });
6773
+ return { data: body };
6774
+ }
6775
+ });
6776
+
5231
6777
  // src/commands/templates/list.ts
5232
6778
  var templatesListCommand = defineCommand({
5233
6779
  path: ["templates", "list"],
@@ -5535,6 +7081,7 @@ var ALL_COMMANDS = [
5535
7081
  configGetCommand,
5536
7082
  configSetCommand,
5537
7083
  configUnsetCommand,
7084
+ contactsListCommand,
5538
7085
  contactsSearchCommand,
5539
7086
  contactsGetCommand,
5540
7087
  contactsCountCommand,
@@ -5546,10 +7093,12 @@ var ALL_COMMANDS = [
5546
7093
  contactsValidateCommand,
5547
7094
  contactsImportCsvCommand,
5548
7095
  fieldsListCommand,
7096
+ fieldsGetCommand,
5549
7097
  fieldsCreateCommand,
5550
7098
  fieldsDeleteCommand,
5551
7099
  emailsListCommand,
5552
7100
  emailsGroupsListCommand,
7101
+ emailsGroupsGetCommand,
5553
7102
  emailsGroupsCreateCommand,
5554
7103
  emailsGroupsUpdateCommand,
5555
7104
  emailsGroupsDeleteCommand,
@@ -5563,10 +7112,15 @@ var ALL_COMMANDS = [
5563
7112
  emailsDeleteCommand,
5564
7113
  emailsExportCommand,
5565
7114
  emailsAuditCommand,
7115
+ emailsGetAuditCommand,
5566
7116
  emailsPreviewClientsCommand,
7117
+ emailsGetClientPreviewCommand,
5567
7118
  emailsCreateInboxPlacementTestCommand,
5568
7119
  emailsGetInboxPlacementResultsCommand,
7120
+ emailsInboxPlacementTestsGetCommand,
5569
7121
  emailsSendCommand,
7122
+ sendsListCommand,
7123
+ sendsGetCommand,
5570
7124
  sendsCancelCommand,
5571
7125
  sendsPauseCommand,
5572
7126
  sendsResumeCommand,
@@ -5588,6 +7142,7 @@ var ALL_COMMANDS = [
5588
7142
  automationsTestCommand,
5589
7143
  automationsRunCommand,
5590
7144
  automationsTriggersListCommand,
7145
+ automationsTriggersGetCommand,
5591
7146
  automationsTriggersReadyCommand,
5592
7147
  automationsTriggersContractGetCommand,
5593
7148
  automationsTriggersContractPutCommand,
@@ -5599,12 +7154,21 @@ var ALL_COMMANDS = [
5599
7154
  automationsTriggersDeleteCommand,
5600
7155
  automationsTriggersFireCommand,
5601
7156
  automationsRunsListCommand,
7157
+ automationsRunsGetCommand,
7158
+ automationsRunsCancelCommand,
5602
7159
  automationsAudienceRunsListCommand,
7160
+ automationsAudienceRunsGetCommand,
7161
+ automationsAudienceRunsPauseCommand,
7162
+ automationsAudienceRunsResumeCommand,
7163
+ automationsAudienceRunsCancelCommand,
5603
7164
  automationsAudienceRunsControlCommand,
7165
+ automationsTriggerInstancesListCommand,
7166
+ automationsTriggerInstancesGetCommand,
5604
7167
  analyticsOverviewCommand,
5605
7168
  analyticsCampaignsCommand,
5606
7169
  analyticsAutomationsCommand,
5607
7170
  analyticsEventsCommand,
7171
+ analyticsEventCountsCommand,
5608
7172
  analyticsSendsListCommand,
5609
7173
  analyticsSendsGetCommand,
5610
7174
  analyticsTriggerInstancesListCommand,
@@ -5624,12 +7188,20 @@ var ALL_COMMANDS = [
5624
7188
  domainsHealthCommand,
5625
7189
  domainsUpdateCommand,
5626
7190
  domainsDeleteCommand,
7191
+ domainsUnsubscribesListCommand,
7192
+ domainsUnsubscribesAddCommand,
7193
+ domainsUnsubscribesRemoveCommand,
7194
+ domainsUnsubscribesImportCommand,
7195
+ domainsUnsubscribesExportCommand,
5627
7196
  contentGenerateImageCommand,
5628
7197
  contentGifCommand,
5629
7198
  contentTransformCommand,
5630
7199
  contentHtmlToPngCommand,
5631
7200
  contentAddImageCommand,
5632
7201
  templatesListCommand,
7202
+ templatesGetCommand,
7203
+ flowsListCommand,
7204
+ flowsGetCommand,
5633
7205
  integrationsListCommand,
5634
7206
  chatsGetCommand,
5635
7207
  healthCommand,
@@ -5642,7 +7214,8 @@ var ALL_COMMANDS = [
5642
7214
 
5643
7215
  // src/cli.ts
5644
7216
  var GROUP_SUMMARIES = {
5645
- analytics: "Read campaign, automation, send, and event analytics",
7217
+ analytics: "Reports: overview, events, and automation analytics",
7218
+ "audience-runs": "Manual-audience launches: read, pause, resume, cancel",
5646
7219
  "api-keys": "Manage organization API keys",
5647
7220
  audiences: "Manage audience segments",
5648
7221
  automations: "Manage automation graphs, triggers, and runs",
@@ -5656,15 +7229,17 @@ var GROUP_SUMMARIES = {
5656
7229
  domains: "Manage sending domains",
5657
7230
  emails: "Manage email designs and send campaigns",
5658
7231
  fields: "Manage custom contact fields",
7232
+ flows: "Browse public email flows (real multi-step sequences by brand)",
5659
7233
  groups: "Manage email folders (groups)",
5660
7234
  integrations: "Read the connected-integration catalog",
5661
- sends: "Control in-flight or scheduled sends",
7235
+ "inbox-placement-tests": "Inbox placement (seed) test results",
7236
+ sends: "Read sends, and control one in flight or scheduled",
5662
7237
  templates: "Browse the template gallery",
5663
7238
  contract: "Stored payload contracts (get, put, validate)",
5664
7239
  contracts: "Payload-contract helpers",
5665
7240
  triggers: "Manage trigger events",
5666
- runs: "Automation run history",
5667
- "trigger-instances": "Fired trigger instances"
7241
+ runs: "Automation run history, and cancel one in-flight run",
7242
+ "trigger-instances": "Fired trigger instances (the inbound-fire audit log)"
5668
7243
  };
5669
7244
  var AGENT_HELP_FOOTER = `
5670
7245
  Environment: