@brew.new/cli 0.4.0 → 0.5.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
@@ -215,7 +215,7 @@ function shellQuote(token) {
215
215
 
216
216
  // src/version.ts
217
217
  var CLI_NAME = "brew-cli";
218
- var CLI_VERSION = "0.4.0" ;
218
+ var CLI_VERSION = "0.5.0" ;
219
219
  var CONFIG_KEYS = ["apiKey", "brandId", "apiUrl"];
220
220
  function isConfigKey(value) {
221
221
  return CONFIG_KEYS.includes(value);
@@ -1038,6 +1038,56 @@ function renderInstances(rows) {
1038
1038
  }
1039
1039
 
1040
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);
1062
+ }
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."
1081
+ });
1082
+ }
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
+ }
1041
1091
  function buildRawHeaders(input) {
1042
1092
  const headers = new Headers({
1043
1093
  authorization: `Bearer ${input.auth.apiKey}`,
@@ -2033,6 +2083,175 @@ var automationsTestCommand = defineCommand({
2033
2083
  }
2034
2084
  });
2035
2085
 
2086
+ // src/commands/automations/triggers/contract.ts
2087
+ var FORMAT_FLAG = {
2088
+ flag: "--format <format>",
2089
+ summary: "Rendering: json (default, the contract object) or ts | zod | jsonschema | skill ({format, content})"
2090
+ };
2091
+ var FORMATS = /* @__PURE__ */ new Set(["json", "ts", "zod", "jsonschema", "skill"]);
2092
+ var ENFORCEMENT_MODES = /* @__PURE__ */ new Set(["off", "prune", "strict"]);
2093
+ var VALIDATE_ENFORCEMENT_MODES = /* @__PURE__ */ new Set(["prune", "strict"]);
2094
+ function formatQuery(value) {
2095
+ const format = flagString(value);
2096
+ if (format === void 0) {
2097
+ return "";
2098
+ }
2099
+ if (!FORMATS.has(format)) {
2100
+ throw new CliUsageError(
2101
+ `Unknown --format '${format}' (expected json | ts | zod | jsonschema | skill).`
2102
+ );
2103
+ }
2104
+ return `?format=${format}`;
2105
+ }
2106
+ function contentHuman(body) {
2107
+ return typeof body.content === "string" ? { human: body.content } : {};
2108
+ }
2109
+ var automationsTriggersContractGetCommand = defineCommand({
2110
+ path: ["automations", "triggers", "contract", "get"],
2111
+ summary: "Read a trigger payload contract: stored when declared, derived otherwise; --format renders ts/zod/jsonschema/skill",
2112
+ sdkMethod: null,
2113
+ isRawTransport: true,
2114
+ route: {
2115
+ method: "GET",
2116
+ path: "/v1/automations/triggers/{triggerEventId}/contract"
2117
+ },
2118
+ commandClass: "read",
2119
+ args: [
2120
+ {
2121
+ name: "triggerEventId",
2122
+ summary: "Trigger id (tri_\u2026, or an integration composite id)",
2123
+ isRequired: true
2124
+ }
2125
+ ],
2126
+ flags: [FORMAT_FLAG],
2127
+ examples: [
2128
+ "brew-cli automations triggers contract get tri_signup",
2129
+ "brew-cli automations triggers contract get tri_signup --format ts",
2130
+ "brew-cli automations triggers contract get tri_signup --format skill --json | jq -r .content"
2131
+ ],
2132
+ run: async ({ ctx, args, flags }) => {
2133
+ const body = await rawRequest(ctx, {
2134
+ method: "GET",
2135
+ path: `/v1/automations/triggers/${encodeURIComponent(args.triggerEventId ?? "")}/contract${formatQuery(flags.format)}`
2136
+ });
2137
+ return { data: body, ...contentHuman(body) };
2138
+ }
2139
+ });
2140
+ var automationsTriggersContractPutCommand = defineCommand({
2141
+ path: ["automations", "triggers", "contract", "put"],
2142
+ summary: "Declare (or replace) the stored payload contract for a trigger \u2014 tree-validated before any write; omitting --enforcement leaves the stored setting unchanged",
2143
+ sdkMethod: null,
2144
+ isRawTransport: true,
2145
+ route: {
2146
+ method: "PUT",
2147
+ path: "/v1/automations/triggers/{triggerEventId}/contract"
2148
+ },
2149
+ commandClass: "write",
2150
+ args: [
2151
+ {
2152
+ name: "triggerEventId",
2153
+ summary: "Trigger id (tri_\u2026)",
2154
+ isRequired: true
2155
+ }
2156
+ ],
2157
+ flags: [
2158
+ INPUT_FLAG,
2159
+ {
2160
+ flag: "--enforcement <mode>",
2161
+ summary: "off (advisory, the default) | prune (drop fields not on the list) | strict (reject a payload carrying them)"
2162
+ }
2163
+ ],
2164
+ examples: [
2165
+ `brew-cli automations triggers contract put tri_signup --input '{"fields":[{"key":"email","type":"string","required":true}]}'`,
2166
+ "brew-cli automations triggers contract put tri_signup --enforcement strict"
2167
+ ],
2168
+ // Trigger contracts must keep a top-level required `email` string;
2169
+ // nested object/array fields need a Liquid-enabled workspace. The
2170
+ // response is the same body a follow-up `contract get` returns.
2171
+ run: async ({ ctx, args, flags }) => {
2172
+ const input = mergeInput(
2173
+ await readJsonFlag(ctx, flags.input, "--input"),
2174
+ {}
2175
+ );
2176
+ if (input.fields !== void 0 && !Array.isArray(input.fields)) {
2177
+ throw new CliUsageError(
2178
+ "--input .fields must be an array of contract field nodes."
2179
+ );
2180
+ }
2181
+ const enforcement = flagString(flags.enforcement);
2182
+ if (enforcement !== void 0 && !ENFORCEMENT_MODES.has(enforcement)) {
2183
+ throw new CliUsageError(
2184
+ `Unknown --enforcement '${enforcement}' (expected off | prune | strict).`
2185
+ );
2186
+ }
2187
+ const requestBody = {
2188
+ ...input,
2189
+ ...enforcement !== void 0 ? { enforcement } : {}
2190
+ };
2191
+ if (Object.keys(requestBody).length === 0) {
2192
+ throw new CliUsageError(
2193
+ "Empty contract body \u2014 pass --input (fields and/or name) or --enforcement <mode>."
2194
+ );
2195
+ }
2196
+ const body = await rawRequest(ctx, {
2197
+ method: "PUT",
2198
+ path: `/v1/automations/triggers/${encodeURIComponent(args.triggerEventId ?? "")}/contract`,
2199
+ body: requestBody
2200
+ });
2201
+ return { data: body };
2202
+ }
2203
+ });
2204
+ var automationsTriggersContractValidateCommand = defineCommand({
2205
+ path: ["automations", "triggers", "contract", "validate"],
2206
+ summary: "Dry-run a payload against a trigger's contract (the fire path's validator) \u2014 never fires; invalid payloads still exit 0",
2207
+ sdkMethod: null,
2208
+ isRawTransport: true,
2209
+ route: {
2210
+ method: "POST",
2211
+ path: "/v1/automations/triggers/{triggerEventId}/contract/validate"
2212
+ },
2213
+ commandClass: "read",
2214
+ args: [
2215
+ {
2216
+ name: "triggerEventId",
2217
+ summary: "Trigger id (tri_\u2026)",
2218
+ isRequired: true
2219
+ }
2220
+ ],
2221
+ flags: [
2222
+ INPUT_FLAG,
2223
+ {
2224
+ flag: "--enforcement <mode>",
2225
+ summary: "Preview a mode other than the stored one: prune | strict"
2226
+ }
2227
+ ],
2228
+ examples: [
2229
+ `brew-cli automations triggers contract validate tri_signup --input '{"payload":{"email":"jane@example.com"}}'`
2230
+ ],
2231
+ run: async ({ ctx, args, flags }) => {
2232
+ const input = mergeInput(
2233
+ await readJsonFlag(ctx, flags.input, "--input"),
2234
+ {}
2235
+ );
2236
+ const payload = input.payload ?? input;
2237
+ const enforcement = flagString(flags.enforcement);
2238
+ if (enforcement !== void 0 && !VALIDATE_ENFORCEMENT_MODES.has(enforcement)) {
2239
+ throw new CliUsageError(
2240
+ `Unknown --enforcement '${enforcement}' (expected prune | strict).`
2241
+ );
2242
+ }
2243
+ const body = await rawRequest(ctx, {
2244
+ method: "POST",
2245
+ path: `/v1/automations/triggers/${encodeURIComponent(args.triggerEventId ?? "")}/contract/validate`,
2246
+ body: {
2247
+ payload,
2248
+ ...enforcement !== void 0 ? { enforcement } : {}
2249
+ }
2250
+ });
2251
+ return { data: body };
2252
+ }
2253
+ });
2254
+
2036
2255
  // src/commands/automations/triggers/create.ts
2037
2256
  var automationsTriggersCreateCommand = defineCommand({
2038
2257
  path: ["automations", "triggers", "create"],
@@ -2116,7 +2335,7 @@ var automationsTriggersFireCommand = defineCommand({
2116
2335
  ],
2117
2336
  flags: [INPUT_FLAG, IDEMPOTENCY_FLAG],
2118
2337
  examples: [
2119
- `brew-cli automations triggers fire tev_123 --input '{"payload":{"userId":"u_1"}}' --yes`
2338
+ `brew-cli automations triggers fire tri_signup --input '{"payload":{"email":"jane@example.com"}}' --yes`
2120
2339
  ],
2121
2340
  confirmSummary: ({ args }) => `Fire trigger ${args.triggerEventId ?? ""} \u2014 starts REAL runs on every published automation listening to it.`,
2122
2341
  run: async ({ ctx, args, flags }) => {
@@ -3329,6 +3548,40 @@ var contentTransformCommand = defineCommand({
3329
3548
  }
3330
3549
  });
3331
3550
 
3551
+ // src/commands/contracts/infer.ts
3552
+ var contractsInferCommand = defineCommand({
3553
+ path: ["contracts", "infer"],
3554
+ summary: "Draft a payload contract from a real example payload \u2014 nothing is saved; PUT the draft on a trigger",
3555
+ sdkMethod: null,
3556
+ isRawTransport: true,
3557
+ route: { method: "POST", path: "/v1/payload-contracts/infer" },
3558
+ commandClass: "read",
3559
+ flags: [INPUT_FLAG],
3560
+ examples: [
3561
+ `brew-cli contracts infer --input '{"email":"jane@example.com","order":{"total":9.5}}'`
3562
+ ],
3563
+ // Accepts either the bare example object or `{ example: {...} }`.
3564
+ // Un-inferable spots (nulls, empty arrays) come back under `issues`.
3565
+ run: async ({ ctx, flags }) => {
3566
+ const input = mergeInput(
3567
+ await readJsonFlag(ctx, flags.input, "--input"),
3568
+ {}
3569
+ );
3570
+ const example = input.example ?? input;
3571
+ if (typeof example !== "object" || example === null || Array.isArray(example) || Object.keys(example).length === 0) {
3572
+ throw new CliUsageError(
3573
+ "--input with a non-empty example object is required (the JSON your system sends)."
3574
+ );
3575
+ }
3576
+ const body = await rawRequest(ctx, {
3577
+ method: "POST",
3578
+ path: "/v1/payload-contracts/infer",
3579
+ body: { example }
3580
+ });
3581
+ return { data: body };
3582
+ }
3583
+ });
3584
+
3332
3585
  // src/skip-list.ts
3333
3586
  var SDK_SKIP_LIST = [
3334
3587
  {
@@ -4994,36 +5247,6 @@ function renderTemplates(rows) {
4994
5247
  { key: "brand", header: "BRAND" }
4995
5248
  ]);
4996
5249
  }
4997
-
4998
- // src/commands/transactional/get.ts
4999
- var transactionalGetCommand = defineCommand({
5000
- path: ["transactional", "get"],
5001
- summary: "Read a transactional email object: locked design/domain/envelope; Liquid workspaces add `variableTree` + a fireable `examplePayload`",
5002
- sdkMethod: "transactional.get",
5003
- route: { method: "GET", path: "/v1/transactional/{transactionId}" },
5004
- commandClass: "read",
5005
- args: [
5006
- {
5007
- name: "transactionId",
5008
- summary: "Transactional email id (txn_\u2026) from Email Actions \u2192 Transactional Email",
5009
- isRequired: true
5010
- }
5011
- ],
5012
- examples: ["brew-cli transactional get txn_8fK2mQ4pLx"],
5013
- // `variables` (legacy merge tags) is always present. `templating.engine`,
5014
- // `variableTree`, and `examplePayload` only appear on Liquid-enabled
5015
- // workspaces: variableTree lists every trigger.*/customer.* path the
5016
- // pinned template references, and examplePayload is a ready-to-send
5017
- // `payload` body for `emails send` (or `POST /v1/sends { transactionId,
5018
- // to, payload }`). Nested payload values are Liquid-only — the same
5019
- // shape sent to a non-Liquid workspace is rejected with 400. A `--test`
5020
- // send on `emails send` accepts the identical `payload` shape with the
5021
- // same live-fire rendering parity, so an examplePayload preview matches
5022
- // what a real fire produces.
5023
- run: async ({ ctx, args }) => ({
5024
- data: await ctx.client().transactional.get(args.transactionId ?? "")
5025
- })
5026
- });
5027
5250
  var HEADER_PREFIX = "// brew:contracts sha256:";
5028
5251
  function pascalCase(value) {
5029
5252
  const cleaned = value.replace(/[^a-zA-Z0-9]+/g, " ").trim();
@@ -5043,77 +5266,56 @@ var IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
5043
5266
  function propertyKey(key) {
5044
5267
  return IDENTIFIER_RE.test(key) ? key : JSON.stringify(key);
5045
5268
  }
5046
- function triggerFieldType(type) {
5047
- if (type === "int") {
5048
- return "number";
5049
- }
5050
- if (type === "boolean") {
5051
- return "boolean";
5269
+ function scalarTsType(type, node) {
5270
+ switch (type) {
5271
+ case "string":
5272
+ return "string";
5273
+ case "int":
5274
+ case "float":
5275
+ return "number";
5276
+ case "boolean":
5277
+ return "boolean";
5278
+ case "date":
5279
+ return "string";
5280
+ case "enum":
5281
+ return node.enumValues && node.enumValues.length > 0 ? node.enumValues.map((value) => JSON.stringify(value)).join(" | ") : "string";
5282
+ default:
5283
+ return "unknown";
5052
5284
  }
5053
- return "string";
5054
- }
5055
- function emitTriggerType(trigger, name) {
5056
- const fields = trigger.payloadSchema?.fields ?? [];
5057
- const lines = fields.map((field) => {
5058
- const optional = field.required ? "" : "?";
5059
- return ` ${propertyKey(field.key)}${optional}: ${triggerFieldType(field.type)}`;
5060
- });
5061
- const body = fields.length === 0 ? `export type ${name} = Record<string, never>` : [`export type ${name} = {`, ...lines, "}"].join("\n");
5062
- return [
5063
- `/** Fire: POST /v1/automations/triggers/${trigger.triggerEventId}/fire \u2014 body { payload: ${name} } */`,
5064
- body
5065
- ].join("\n");
5066
5285
  }
5067
- function scalarType(node) {
5068
- if (node.inferredType === "number" || node.inferredType === "boolean") {
5069
- return node.inferredType;
5286
+ function triggerFieldType(node, indent) {
5287
+ if (node.type === "object") {
5288
+ return triggerObjectLiteral(node.children ?? [], indent);
5070
5289
  }
5071
- if (node.inferredType === "string") {
5072
- return "string";
5290
+ if (node.type === "array") {
5291
+ if (node.children && node.children.length > 0) {
5292
+ return `Array<${triggerObjectLiteral(node.children, indent)}>`;
5293
+ }
5294
+ const element = node.itemType ? scalarTsType(node.itemType, node) : "unknown";
5295
+ return `Array<${element}>`;
5073
5296
  }
5074
- return node.fallback === null ? "unknown" : "string";
5075
- }
5076
- function emitTreeNode(node, indent) {
5077
- const optional = node.fallback === null ? "" : "?";
5078
- const key = `${indent}${propertyKey(node.key)}${optional}: `;
5079
- if (node.kind === "object") {
5080
- const children = node.children ?? [];
5081
- return `${key}{
5082
- ${children.map((child) => emitTreeNode(child, `${indent} `)).join("\n")}
5083
- ${indent}}`;
5084
- }
5085
- if (node.kind === "array") {
5086
- const children = node.children ?? [];
5087
- if (children.length === 0) {
5088
- return `${key}Array<unknown>`;
5089
- }
5090
- return `${key}Array<{
5091
- ${children.map((child) => emitTreeNode(child, `${indent} `)).join("\n")}
5092
- ${indent}}>`;
5093
- }
5094
- return `${key}${scalarType(node)}`;
5297
+ return scalarTsType(node.type, node);
5095
5298
  }
5096
- function emitTransactionalType(row, name) {
5097
- const roots = row.variableTree ?? [];
5098
- const payloadNodes = [];
5099
- for (const root of roots) {
5100
- if (root.namespace === "customer") {
5101
- continue;
5102
- }
5103
- if (root.key === "trigger" || root.key === "payload") {
5104
- payloadNodes.push(
5105
- ...root.children ?? []
5106
- );
5107
- continue;
5108
- }
5109
- payloadNodes.push(root);
5299
+ function triggerObjectLiteral(nodes, indent) {
5300
+ if (nodes.length === 0) {
5301
+ return indent === "" ? "Record<string, never>" : "Record<string, unknown>";
5302
+ }
5303
+ const inner = `${indent} `;
5304
+ const lines = ["{"];
5305
+ for (const node of nodes) {
5306
+ const optional = node.required ? "" : "?";
5307
+ lines.push(
5308
+ `${inner}${propertyKey(node.key)}${optional}: ${triggerFieldType(node, inner)}`
5309
+ );
5110
5310
  }
5111
- const body = payloadNodes.length === 0 ? `export type ${name} = Record<string, never>` : `export type ${name} = {
5112
- ${payloadNodes.map((node) => emitTreeNode(node, " ")).join("\n")}
5113
- }`;
5311
+ lines.push(`${indent}}`);
5312
+ return lines.join("\n");
5313
+ }
5314
+ function emitTriggerType(trigger, name) {
5315
+ const fields = trigger.payloadSchema?.fields ?? [];
5114
5316
  return [
5115
- `/** Fire: POST /v1/sends \u2014 body { transactionId: ${JSON.stringify(row.transactionId)}, to, payload: ${name} } */`,
5116
- body
5317
+ `/** Fire: POST /v1/automations/triggers/${trigger.triggerEventId}/fire \u2014 body { payload: ${name} } */`,
5318
+ `export type ${name} = ${triggerObjectLiteral(fields, "")}`
5117
5319
  ].join("\n");
5118
5320
  }
5119
5321
  function byCodepoint(a, b) {
@@ -5143,31 +5345,18 @@ function buildFileText(args) {
5143
5345
  const triggers = [...args.triggers].sort(
5144
5346
  (a, b) => byCodepoint(a.triggerEventId, b.triggerEventId)
5145
5347
  );
5146
- const transactionals = [...args.transactionals].sort(
5147
- (a, b) => byCodepoint(a.transactionId, b.transactionId)
5348
+ const names = uniqueNames(
5349
+ triggers.map((t) => ({ id: t.triggerEventId, label: t.title }))
5148
5350
  );
5149
- const names = uniqueNames([
5150
- ...triggers.map((t) => ({ id: t.triggerEventId, label: t.title })),
5151
- ...transactionals.map((t) => ({
5152
- id: t.transactionId,
5153
- label: t.subject?.trim() || t.transactionId
5154
- }))
5155
- ]);
5156
5351
  const triggerBlocks = triggers.map(
5157
5352
  (t, i) => emitTriggerType(t, names[i] ?? typeName(t.title))
5158
5353
  );
5159
- const transactionalBlocks = transactionals.map(
5160
- (t, i) => emitTransactionalType(
5161
- t,
5162
- names[triggers.length + i] ?? typeName(t.transactionId)
5163
- )
5164
- );
5165
5354
  const body = [
5166
5355
  "// Generated by `brew-cli types` \u2014 do not edit by hand.",
5167
- "// Re-run `brew-cli types` after changing a trigger schema or a",
5168
- "// transactional design; gate drift in CI with `brew-cli types --check`.",
5356
+ "// Re-run `brew-cli types` after changing a trigger schema; gate",
5357
+ "// drift in CI with `brew-cli types --check`.",
5169
5358
  "",
5170
- [...triggerBlocks, ...transactionalBlocks].join("\n\n"),
5359
+ triggerBlocks.join("\n\n"),
5171
5360
  ""
5172
5361
  ].join("\n");
5173
5362
  const hash = createHash("sha256").update(body).digest("hex");
@@ -5176,7 +5365,7 @@ ${body}`;
5176
5365
  }
5177
5366
  var typesCommand = defineCommand({
5178
5367
  path: ["types"],
5179
- summary: "Generate TypeScript payload contracts (triggers + transactional objects) into your codebase; --check is the CI drift gate (exit 1 on drift). Needs the automations scope; --transaction also needs sends",
5368
+ summary: "Generate TypeScript payload contracts for this workspace's triggers into your codebase; --check is the CI drift gate (exit 1 on drift). Needs the automations scope",
5180
5369
  sdkMethod: null,
5181
5370
  derivedFrom: "automations.triggers.list",
5182
5371
  route: { method: "GET", path: "/v1/automations/triggers" },
@@ -5187,10 +5376,6 @@ var typesCommand = defineCommand({
5187
5376
  summary: "Output file (default brew-contracts.ts)",
5188
5377
  defaultValue: "brew-contracts.ts"
5189
5378
  },
5190
- {
5191
- flag: "--transaction <transactionIds...>",
5192
- summary: "Transactional object ids (txn_\u2026) to include, contract derived from each pinned template"
5193
- },
5194
5379
  {
5195
5380
  flag: "--check",
5196
5381
  summary: "Verify the output file is up to date instead of writing; exits 1 on drift"
@@ -5198,7 +5383,7 @@ var typesCommand = defineCommand({
5198
5383
  ],
5199
5384
  examples: [
5200
5385
  "brew-cli types",
5201
- "brew-cli types --out src/brew-contracts.ts --transaction txn_8fK2mQ4pLx",
5386
+ "brew-cli types --out src/brew-contracts.ts",
5202
5387
  "brew-cli types --check"
5203
5388
  ],
5204
5389
  run: async ({ ctx, flags }) => {
@@ -5217,12 +5402,7 @@ var typesCommand = defineCommand({
5217
5402
  }
5218
5403
  cursor = listResponse.pagination.cursor;
5219
5404
  }
5220
- const transactionIds = Array.isArray(flags.transaction) ? flags.transaction : [];
5221
- const transactionals = [];
5222
- for (const transactionId of transactionIds) {
5223
- transactionals.push(await ctx.client().transactional.get(transactionId));
5224
- }
5225
- const text = buildFileText({ transactionals, triggers });
5405
+ const text = buildFileText({ triggers });
5226
5406
  const outPath = typeof flags.out === "string" ? flags.out : "brew-contracts.ts";
5227
5407
  if (flags.check === true) {
5228
5408
  let existing = null;
@@ -5249,10 +5429,9 @@ var typesCommand = defineCommand({
5249
5429
  return {
5250
5430
  data: {
5251
5431
  out: outPath,
5252
- transactionals: transactionals.length,
5253
5432
  triggers: triggers.length
5254
5433
  },
5255
- human: `Wrote ${outPath} (${triggers.length} trigger${triggers.length === 1 ? "" : "s"}, ${transactionals.length} transactional).`
5434
+ human: `Wrote ${outPath} (${triggers.length} trigger${triggers.length === 1 ? "" : "s"}).`
5256
5435
  };
5257
5436
  }
5258
5437
  });
@@ -5343,7 +5522,6 @@ var ALL_COMMANDS = [
5343
5522
  sendsCancelCommand,
5344
5523
  sendsPauseCommand,
5345
5524
  sendsResumeCommand,
5346
- transactionalGetCommand,
5347
5525
  typesCommand,
5348
5526
  audiencesListCommand,
5349
5527
  audiencesGetCommand,
@@ -5363,6 +5541,10 @@ var ALL_COMMANDS = [
5363
5541
  automationsRunCommand,
5364
5542
  automationsTriggersListCommand,
5365
5543
  automationsTriggersReadyCommand,
5544
+ automationsTriggersContractGetCommand,
5545
+ automationsTriggersContractPutCommand,
5546
+ automationsTriggersContractValidateCommand,
5547
+ contractsInferCommand,
5366
5548
  automationsTriggersCreateCommand,
5367
5549
  automationsTriggersUpdateCommand,
5368
5550
  automationsTriggersDeleteCommand,
@@ -5429,7 +5611,8 @@ var GROUP_SUMMARIES = {
5429
5611
  integrations: "Read the connected-integration catalog",
5430
5612
  sends: "Control in-flight or scheduled sends",
5431
5613
  templates: "Browse the template gallery",
5432
- transactional: "Read reusable transactional email objects",
5614
+ contract: "Stored payload contracts (get, put, validate)",
5615
+ contracts: "Payload-contract helpers",
5433
5616
  triggers: "Manage trigger events",
5434
5617
  runs: "Automation run history",
5435
5618
  "trigger-instances": "Fired trigger instances"