@koda-sl/baker-cli 0.410.0 → 0.411.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/cli.js CHANGED
@@ -21715,7 +21715,7 @@ var require_entry_node = __commonJS({
21715
21715
  });
21716
21716
 
21717
21717
  // src/cli.ts
21718
- import { defineCommand as defineCommand278, runMain } from "citty";
21718
+ import { defineCommand as defineCommand279, runMain } from "citty";
21719
21719
 
21720
21720
  // src/cache-flag.ts
21721
21721
  var NO_CACHE_ARG = {
@@ -23949,20 +23949,20 @@ var conversionUploadRowSchema = z4.strictObject({
23949
23949
  value: z4.number().nonnegative().optional(),
23950
23950
  currency: z4.string().regex(/^[A-Z]{3}$/, "expected a 3-letter currency code, e.g. EUR").optional()
23951
23951
  }).refine(
23952
- (row) => Boolean(row.gclid ?? row.gbraid ?? row.wbraid ?? row.email ?? row.phone ?? row.address),
23952
+ (row2) => Boolean(row2.gclid ?? row2.gbraid ?? row2.wbraid ?? row2.email ?? row2.phone ?? row2.address),
23953
23953
  "every conversion needs something to match on: a click id (gclid / gbraid / wbraid), or a customer's email, phone or name+address"
23954
23954
  ).refine(
23955
- (row) => [row.gclid, row.gbraid, row.wbraid].filter(Boolean).length <= 1,
23955
+ (row2) => [row2.gclid, row2.gbraid, row2.wbraid].filter(Boolean).length <= 1,
23956
23956
  "pass at most one click id per conversion \u2014 gclid, gbraid and wbraid are three ways of naming the same click"
23957
23957
  ).refine(
23958
- (row) => row.value === void 0 || row.currency !== void 0,
23958
+ (row2) => row2.value === void 0 || row2.currency !== void 0,
23959
23959
  "a conversion with a value needs its currency too"
23960
- ).refine((row) => row.currency === void 0 || row.value !== void 0, "currency was given with no value");
23960
+ ).refine((row2) => row2.currency === void 0 || row2.value !== void 0, "currency was given with no value");
23961
23961
  var conversionUploadSchema = z4.strictObject({
23962
23962
  conversionAction: conversionUploadActionSchema,
23963
23963
  conversions: z4.array(conversionUploadRowSchema).min(1).max(CONVERSION_UPLOAD_MAX_ROWS)
23964
23964
  }).refine((payload) => {
23965
- const ids2 = payload.conversions.map((row) => row.orderId);
23965
+ const ids2 = payload.conversions.map((row2) => row2.orderId);
23966
23966
  return new Set(ids2).size === ids2.length;
23967
23967
  }, "two conversions share an order id \u2014 each order id can appear once per upload");
23968
23968
  var MS_PER_DAY = 24 * 60 * 60 * 1e3;
@@ -28952,7 +28952,8 @@ var researchFetchResponseSchema = z27.object({
28952
28952
  contentChars: z27.number(),
28953
28953
  /** The page was longer than one response may carry, so the tail is missing. */
28954
28954
  truncated: z27.boolean(),
28955
- provider: z27.literal("firecrawl")
28955
+ /** `reddit` when the URL was a Reddit thread, which Firecrawl refuses and our Reddit reader serves instead. */
28956
+ provider: z27.enum(["firecrawl", "reddit"])
28956
28957
  });
28957
28958
  var RESEARCH_KEYWORD_METRICS_MAX_KEYWORDS = 700;
28958
28959
  var RESEARCH_KEYWORD_METRICS_MAX_KEYWORD_CHARS = 80;
@@ -28991,6 +28992,81 @@ var researchKeywordMetricsRowSchema = z27.object({
28991
28992
  var researchKeywordMetricsResponseSchema = z27.object({
28992
28993
  data: z27.array(researchKeywordMetricsRowSchema)
28993
28994
  });
28995
+ var RESEARCH_REDDIT_SEARCH_MAX_LIMIT = 50;
28996
+ var RESEARCH_REDDIT_MAX_SUBREDDITS = 10;
28997
+ var RESEARCH_REDDIT_THREAD_MAX_COMMENTS = 200;
28998
+ var RESEARCH_REDDIT_THREAD_DEFAULT_COMMENTS = 40;
28999
+ var researchRedditSourceSchema = z27.enum(["reddit_api", "reddit_web", "archive"]);
29000
+ var researchRedditSubredditSchema = z27.string().trim().transform((name) => name.replace(/^\/?r\//i, "")).pipe(z27.string().regex(/^[A-Za-z0-9_]{2,21}$/, "Not a subreddit name"));
29001
+ var researchRedditSearchRequestSchema = z27.object({
29002
+ query: z27.string().trim().min(1).max(256),
29003
+ subreddits: z27.array(researchRedditSubredditSchema).max(RESEARCH_REDDIT_MAX_SUBREDDITS).default([]),
29004
+ /** `comments` searches what people replied, which is where most of the verbatim voice is. */
29005
+ kind: z27.enum(["posts", "comments"]).default("posts"),
29006
+ /** Only things written on or after this day, `YYYY-MM-DD`. */
29007
+ since: z27.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Use YYYY-MM-DD").optional(),
29008
+ limit: z27.coerce.number().int().min(1).max(RESEARCH_REDDIT_SEARCH_MAX_LIMIT).default(25)
29009
+ });
29010
+ var researchRedditThreadRequestSchema = z27.object({
29011
+ /** A thread link in any form Reddit hands out — full, `old.`, `redd.it`, `/s/` share — or a bare post id. */
29012
+ target: z27.string().trim().min(1).max(2048),
29013
+ comments: z27.coerce.number().int().min(0).max(RESEARCH_REDDIT_THREAD_MAX_COMMENTS).default(RESEARCH_REDDIT_THREAD_DEFAULT_COMMENTS)
29014
+ });
29015
+ var researchRedditItemSchema = z27.object({
29016
+ kind: z27.enum(["post", "comment"]),
29017
+ id: z27.string(),
29018
+ postId: z27.string(),
29019
+ subreddit: z27.string(),
29020
+ /** `null` once the account is deleted. */
29021
+ author: z27.string().nullable(),
29022
+ /** The post's title; on a comment, the title of the post it sits under when known. */
29023
+ title: z27.string().nullable(),
29024
+ /** Verbatim: the post's own text (empty for a link post) or the comment's. */
29025
+ text: z27.string(),
29026
+ score: z27.number().nullable(),
29027
+ /** Posts only. */
29028
+ commentCount: z27.number().nullable(),
29029
+ createdAt: z27.string(),
29030
+ permalink: z27.string(),
29031
+ /** Comments inside a thread: 0 answers the post, 1 answers a comment, and so on. */
29032
+ depth: z27.number().nullable()
29033
+ });
29034
+ var researchRedditAttemptSchema = z27.object({
29035
+ source: researchRedditSourceSchema,
29036
+ outcome: z27.enum(["ok", "empty", "not_found", "not_configured", "unsupported", "blocked", "unavailable"])
29037
+ });
29038
+ var researchRedditSearchResponseSchema = z27.object({
29039
+ results: z27.array(researchRedditItemSchema),
29040
+ source: researchRedditSourceSchema,
29041
+ /** Every source asked, in order — an empty answer from one is not the same as none from all. */
29042
+ attempts: z27.array(researchRedditAttemptSchema)
29043
+ });
29044
+ var researchRedditThreadResponseSchema = z27.object({
29045
+ post: researchRedditItemSchema,
29046
+ /** Highest-scoring first, each followed by its best replies. Removed and deleted comments are left out. */
29047
+ comments: z27.array(researchRedditItemSchema),
29048
+ source: researchRedditSourceSchema,
29049
+ attempts: z27.array(researchRedditAttemptSchema)
29050
+ });
29051
+ var RESEARCH_REDDIT_SUBREDDITS_MAX_LIMIT = 25;
29052
+ var researchRedditSubredditsRequestSchema = z27.object({
29053
+ query: z27.string().trim().min(2).max(100),
29054
+ limit: z27.coerce.number().int().min(1).max(RESEARCH_REDDIT_SUBREDDITS_MAX_LIMIT).default(10)
29055
+ });
29056
+ var researchRedditSubredditSummarySchema = z27.object({
29057
+ name: z27.string(),
29058
+ /** Members; `null` when the source does not say. Size, not activity — a big subreddit can be quiet. */
29059
+ subscribers: z27.number().nullable(),
29060
+ /** `restricted`: anyone reads, only approved members post. `private`: nothing can be read. */
29061
+ access: z27.enum(["public", "restricted", "private", "other"]).nullable(),
29062
+ description: z27.string(),
29063
+ url: z27.string()
29064
+ });
29065
+ var researchRedditSubredditsResponseSchema = z27.object({
29066
+ results: z27.array(researchRedditSubredditSummarySchema),
29067
+ source: researchRedditSourceSchema,
29068
+ attempts: z27.array(researchRedditAttemptSchema)
29069
+ });
28994
29070
 
28995
29071
  // ../api/src/scheduledAgentRules.ts
28996
29072
  import { z as z28 } from "zod";
@@ -31564,9 +31640,9 @@ function toRow(entry) {
31564
31640
  };
31565
31641
  }
31566
31642
  function renderEntry(entry, full) {
31567
- const row = toRow(entry);
31568
- const suffix = row.status ? ` [${row.status}]` : "";
31569
- const lines = [`- \`${row.id}\` ${row.name}${suffix}`];
31643
+ const row2 = toRow(entry);
31644
+ const suffix = row2.status ? ` [${row2.status}]` : "";
31645
+ const lines = [`- \`${row2.id}\` ${row2.name}${suffix}`];
31570
31646
  const nested = entry.action;
31571
31647
  const doc = nested && typeof nested === "object" ? nested : entry;
31572
31648
  const description = typeof doc.description === "string" ? doc.description : "";
@@ -31576,8 +31652,8 @@ function renderEntry(entry, full) {
31576
31652
  ...text2.trim().split("\n").map((line) => line ? ` ${line}` : "")
31577
31653
  );
31578
31654
  }
31579
- if (row.hint) {
31580
- lines.push(` ${row.hint}`);
31655
+ if (row2.hint) {
31656
+ lines.push(` ${row2.hint}`);
31581
31657
  }
31582
31658
  return lines;
31583
31659
  }
@@ -35288,10 +35364,10 @@ function membersFromFile(content) {
35288
35364
  const emailColumn = header.findIndex((cell3) => EMAIL_HEADER.test(cell3));
35289
35365
  const phoneColumn = header.findIndex((cell3) => !EMAIL_HEADER.test(cell3) && PHONE_HEADER.test(cell3));
35290
35366
  if (emailColumn >= 0 || phoneColumn >= 0) {
35291
- const cells = (column) => column < 0 ? [] : rows.slice(1).flatMap((row) => row[column] ? [row[column]] : []);
35367
+ const cells = (column) => column < 0 ? [] : rows.slice(1).flatMap((row2) => row2[column] ? [row2[column]] : []);
35292
35368
  return { emails: cells(emailColumn), phones: cells(phoneColumn) };
35293
35369
  }
35294
- const values = rows.flatMap((row) => row[0] ? [row[0]] : []);
35370
+ const values = rows.flatMap((row2) => row2[0] ? [row2[0]] : []);
35295
35371
  return values.some((value2) => value2.includes("@")) ? { emails: values, phones: [] } : { emails: [], phones: values };
35296
35372
  }
35297
35373
 
@@ -35820,7 +35896,7 @@ function conversionReportRequestFromArgs(args8) {
35820
35896
  };
35821
35897
  }
35822
35898
  function reportHints(report) {
35823
- const nothing = report.rows.every((row) => row.conversions === 0 && (row.events ?? []).length === 0);
35899
+ const nothing = report.rows.every((row2) => row2.conversions === 0 && (row2.events ?? []).length === 0);
35824
35900
  return nothing ? [
35825
35901
  "No attributed conversions in this window. Before concluding the ads do not convert: `conversions events` shows whether the site's events arrive at all, and `campaigns` whether anything served."
35826
35902
  ] : [
@@ -39331,9 +39407,9 @@ function warnDefaults(ctx) {
39331
39407
  var PLANNER_SOURCE = "keyword_planner_estimate";
39332
39408
  var IDENTITY_FIELDS = /* @__PURE__ */ new Set(["keyword"]);
39333
39409
  function stampPlannerRows(rows) {
39334
- return rows.map((row) => {
39410
+ return rows.map((row2) => {
39335
39411
  const stamped = { source: PLANNER_SOURCE };
39336
- for (const [key, value2] of Object.entries(row)) {
39412
+ for (const [key, value2] of Object.entries(row2)) {
39337
39413
  stamped[IDENTITY_FIELDS.has(key) || key.startsWith("planner_") ? key : `planner_${key}`] = value2;
39338
39414
  }
39339
39415
  return stamped;
@@ -40262,33 +40338,33 @@ function toMicros2(value2) {
40262
40338
  function formatMoney(micros) {
40263
40339
  return (micros / 1e6).toFixed(2);
40264
40340
  }
40265
- function keywordLabel(row) {
40266
- const text2 = row["ad_group_criterion.keyword.text"];
40267
- const id = row["ad_group_criterion.criterion_id"];
40341
+ function keywordLabel(row2) {
40342
+ const text2 = row2["ad_group_criterion.keyword.text"];
40343
+ const id = row2["ad_group_criterion.criterion_id"];
40268
40344
  const name = typeof text2 === "string" && text2 !== "" ? text2 : id == null ? "(unnamed keyword)" : `criterion ${id}`;
40269
- const adGroup = row["ad_group.name"];
40345
+ const adGroup = row2["ad_group.name"];
40270
40346
  return typeof adGroup === "string" && adGroup !== "" ? `${name} (ad group ${adGroup})` : name;
40271
40347
  }
40272
- function keywordKey(row) {
40273
- const adGroup = row["ad_group.id"] ?? row["ad_group.name"] ?? "";
40274
- const criterion = row["ad_group_criterion.criterion_id"] ?? row["ad_group_criterion.keyword.text"] ?? "";
40275
- return `${String(adGroup)}~${String(criterion)}` === "~" ? keywordLabel(row) : `${adGroup}~${criterion}`;
40348
+ function keywordKey(row2) {
40349
+ const adGroup = row2["ad_group.id"] ?? row2["ad_group.name"] ?? "";
40350
+ const criterion = row2["ad_group_criterion.criterion_id"] ?? row2["ad_group_criterion.keyword.text"] ?? "";
40351
+ return `${String(adGroup)}~${String(criterion)}` === "~" ? keywordLabel(row2) : `${adGroup}~${criterion}`;
40276
40352
  }
40277
- function reasonsOn(row) {
40278
- const raw = row["ad_group_criterion.primary_status_reasons"];
40353
+ function reasonsOn(row2) {
40354
+ const raw = row2["ad_group_criterion.primary_status_reasons"];
40279
40355
  if (!Array.isArray(raw)) return null;
40280
40356
  return raw.filter((r) => typeof r === "string");
40281
40357
  }
40282
- function bidShortfall(row) {
40283
- const bid = toMicros2(row["ad_group_criterion.effective_cpc_bid_micros"] ?? row["ad_group_criterion.cpc_bid_micros"]);
40284
- const firstPage = toMicros2(row["ad_group_criterion.position_estimates.first_page_cpc_micros"]);
40358
+ function bidShortfall(row2) {
40359
+ const bid = toMicros2(row2["ad_group_criterion.effective_cpc_bid_micros"] ?? row2["ad_group_criterion.cpc_bid_micros"]);
40360
+ const firstPage = toMicros2(row2["ad_group_criterion.position_estimates.first_page_cpc_micros"]);
40285
40361
  if (bid == null || firstPage == null || bid >= firstPage) return null;
40286
40362
  return `bids ${formatMoney(bid)} against a ${formatMoney(firstPage)} first-page estimate`;
40287
40363
  }
40288
- function limitsOn(row, options) {
40289
- const reasons = reasonsOn(row);
40290
- const shortfall = bidShortfall(row);
40291
- const label = keywordLabel(row);
40364
+ function limitsOn(row2, options) {
40365
+ const reasons = reasonsOn(row2);
40366
+ const shortfall = bidShortfall(row2);
40367
+ const label = keywordLabel(row2);
40292
40368
  const applicable = reasons ? reasons.filter((reason) => reason in ACTIONABLE_REASONS) : options.statusReasonsSelected || shortfall === null ? [] : [BELOW_FIRST_PAGE_BID];
40293
40369
  return applicable.map((reason) => ({
40294
40370
  reason,
@@ -40302,10 +40378,10 @@ function describeLimit(reason, details) {
40302
40378
  return `${label} \u2014 ${details.length}: ${named}${rest}. Fix: ${advice}.`;
40303
40379
  }
40304
40380
  function collectKeywordLimits(rows, options, acc = { detailsByReason: /* @__PURE__ */ new Map(), keywords: /* @__PURE__ */ new Set() }) {
40305
- for (const row of rows) {
40306
- const limits = limitsOn(row, options);
40381
+ for (const row2 of rows) {
40382
+ const limits = limitsOn(row2, options);
40307
40383
  if (limits.length === 0) continue;
40308
- acc.keywords.add(keywordKey(row));
40384
+ acc.keywords.add(keywordKey(row2));
40309
40385
  for (const { reason, detail } of limits) {
40310
40386
  const bucket = acc.detailsByReason.get(reason);
40311
40387
  if (bucket) bucket.push(detail);
@@ -40415,11 +40491,11 @@ function writeRowsToFile(filePath, rows, fields, append) {
40415
40491
  writeFileSync(filePath, `${toCsvRow(fields)}
40416
40492
  `, "utf-8");
40417
40493
  }
40418
- const csvRows = rows.map((row) => toCsvRow(fields.map((f) => String(row[f] ?? ""))));
40494
+ const csvRows = rows.map((row2) => toCsvRow(fields.map((f) => String(row2[f] ?? ""))));
40419
40495
  appendFileSync(filePath, `${csvRows.join("\n")}
40420
40496
  `, "utf-8");
40421
40497
  } else if (filePath.split(".").pop()?.toLowerCase() === "jsonl") {
40422
- const lines = rows.map((row) => JSON.stringify(row));
40498
+ const lines = rows.map((row2) => JSON.stringify(row2));
40423
40499
  if (append && existsSync2(filePath)) {
40424
40500
  appendFileSync(filePath, `${lines.join("\n")}
40425
40501
  `, "utf-8");
@@ -40504,26 +40580,26 @@ function writeLine(line) {
40504
40580
  var writeCsvRows = (rows, fields, notices) => {
40505
40581
  writeLine(toCsvRow(fields));
40506
40582
  for (const line of notices) writeLine(line);
40507
- for (const row of rows) writeLine(toCsvRow(fields.map((f) => String(row[f] ?? ""))));
40583
+ for (const row2 of rows) writeLine(toCsvRow(fields.map((f) => String(row2[f] ?? ""))));
40508
40584
  };
40509
40585
  var writeJsonlRows = (rows, _fields, notices) => {
40510
40586
  for (const line of notices) writeLine(line);
40511
- for (const row of rows) writeLine(JSON.stringify(row));
40587
+ for (const row2 of rows) writeLine(JSON.stringify(row2));
40512
40588
  };
40513
40589
  var OMITTED = "(omitted)";
40514
40590
  var OMITTED_FALSE = `false ${OMITTED}`;
40515
40591
  var OMITTED_LEGEND = `> ${OMITTED} = Google left this field off the row, which is how it sends false, 0 and "not set": the field WAS read. A field with no column here was not read.`;
40516
- function mdCell(row, field, booleanColumns) {
40517
- if (!(field in row)) return booleanColumns.has(field) ? OMITTED_FALSE : OMITTED;
40518
- return String(row[field] ?? "").replace(/\|/g, "\\|");
40592
+ function mdCell(row2, field, booleanColumns) {
40593
+ if (!(field in row2)) return booleanColumns.has(field) ? OMITTED_FALSE : OMITTED;
40594
+ return String(row2[field] ?? "").replace(/\|/g, "\\|");
40519
40595
  }
40520
40596
  var writeMdRows = (rows, fields, notices) => {
40521
- const booleanColumns = new Set(fields.filter((f) => rows.some((row) => typeof row[f] === "boolean")));
40597
+ const booleanColumns = new Set(fields.filter((f) => rows.some((row2) => typeof row2[f] === "boolean")));
40522
40598
  for (const line of notices) writeLine(line);
40523
40599
  writeLine(`| ${fields.join(" | ")} |`);
40524
40600
  writeLine(`| ${fields.map(() => "---").join(" | ")} |`);
40525
- for (const row of rows) writeLine(`| ${fields.map((f) => mdCell(row, f, booleanColumns)).join(" | ")} |`);
40526
- if (rows.some((row) => fields.some((f) => !(f in row)))) writeLine(OMITTED_LEGEND);
40601
+ for (const row2 of rows) writeLine(`| ${fields.map((f) => mdCell(row2, f, booleanColumns)).join(" | ")} |`);
40602
+ if (rows.some((row2) => fields.some((f) => !(f in row2)))) writeLine(OMITTED_LEGEND);
40527
40603
  };
40528
40604
  var ROW_WRITERS = { csv: writeCsvRows, jsonl: writeJsonlRows, md: writeMdRows };
40529
40605
  function writeRows(format, rows, columns, notices) {
@@ -40647,8 +40723,8 @@ function parseSelectedFields(gaql) {
40647
40723
  return list.split(",").map((field) => field.trim()).filter((field) => /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z0-9_]+)*$/.test(field));
40648
40724
  }
40649
40725
  function foldObservedFields(rows, observed) {
40650
- for (const row of rows) {
40651
- for (const key of Object.keys(row)) observed.add(key);
40726
+ for (const row2 of rows) {
40727
+ for (const key of Object.keys(row2)) observed.add(key);
40652
40728
  }
40653
40729
  }
40654
40730
  function unobservedFields(gaql, observed) {
@@ -41483,11 +41559,11 @@ var PROPERTY_ROW_PREFIX = "properties.";
41483
41559
  var PROPERTY_KEY_MAX = 64;
41484
41560
  var PROPERTY_KEY = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/;
41485
41561
  var RESERVED_PROPERTY_KEYS = ["baker_source", "hubspot_property", "hubspot_value"];
41486
- function isPropertyRow(row) {
41487
- return row.startsWith(PROPERTY_ROW_PREFIX);
41562
+ function isPropertyRow(row2) {
41563
+ return row2.startsWith(PROPERTY_ROW_PREFIX);
41488
41564
  }
41489
- function propertyKeyOf(row) {
41490
- return isPropertyRow(row) ? row.slice(PROPERTY_ROW_PREFIX.length) : null;
41565
+ function propertyKeyOf(row2) {
41566
+ return isPropertyRow(row2) ? row2.slice(PROPERTY_ROW_PREFIX.length) : null;
41491
41567
  }
41492
41568
  function propertyKeyProblem(key) {
41493
41569
  const trimmed = key.trim();
@@ -41499,11 +41575,11 @@ function propertyKeyProblem(key) {
41499
41575
  if (RESERVED_PROPERTY_KEYS.includes(trimmed)) return `"${trimmed}" is written by Baker itself; pick another name.`;
41500
41576
  return null;
41501
41577
  }
41502
- function mappingRowProblem(row) {
41503
- if (isSourceField(row)) return null;
41504
- const key = propertyKeyOf(row);
41578
+ function mappingRowProblem(row2) {
41579
+ if (isSourceField(row2)) return null;
41580
+ const key = propertyKeyOf(row2);
41505
41581
  if (key === null) {
41506
- return `"${row}" is not a field an event can carry. A field is one of ${SOURCE_FIELD_NAMES.join(", ")}; an extra property is spelled properties.<name>.`;
41582
+ return `"${row2}" is not a field an event can carry. A field is one of ${SOURCE_FIELD_NAMES.join(", ")}; an extra property is spelled properties.<name>.`;
41507
41583
  }
41508
41584
  return propertyKeyProblem(key);
41509
41585
  }
@@ -42090,13 +42166,13 @@ var BOOKKEEPING_TYPES = /* @__PURE__ */ new Set([
42090
42166
  "side_effect_settled"
42091
42167
  ]);
42092
42168
  var SUBMIT_TRIGGERS = new Set(Object.values(STEP_SUBMIT_TRIGGERS));
42093
- function isEmbedLifecycle(row) {
42094
- return row.eventType === "form_trigger" && triggerDescription2(row.triggerId)?.lifecycle === true;
42169
+ function isEmbedLifecycle(row2) {
42170
+ return row2.eventType === "form_trigger" && triggerDescription2(row2.triggerId)?.lifecycle === true;
42095
42171
  }
42096
- function isBookkeepingRow(row) {
42097
- if (BOOKKEEPING_TYPES.has(row.eventType)) return true;
42098
- if (isEmbedLifecycle(row)) return true;
42099
- return row.eventType === "form_trigger" && SUBMIT_TRIGGERS.has(row.triggerId);
42172
+ function isBookkeepingRow(row2) {
42173
+ if (BOOKKEEPING_TYPES.has(row2.eventType)) return true;
42174
+ if (isEmbedLifecycle(row2)) return true;
42175
+ return row2.eventType === "form_trigger" && SUBMIT_TRIGGERS.has(row2.triggerId);
42100
42176
  }
42101
42177
 
42102
42178
  // ../api/src/analytics/identityLinks.ts
@@ -42325,13 +42401,13 @@ var CLICK_LABELS2 = {
42325
42401
  sccid: "Snapchat click id",
42326
42402
  oppref: "ChatGPT Ads click id"
42327
42403
  };
42328
- var PERSON_FIELDS = MATCH_FIELD_FORWARDING.map((row) => ({
42329
- name: row.field,
42330
- label: PERSON_LABELS[row.field] ?? row.field,
42404
+ var PERSON_FIELDS = MATCH_FIELD_FORWARDING.map((row2) => ({
42405
+ name: row2.field,
42406
+ label: PERSON_LABELS[row2.field] ?? row2.field,
42331
42407
  group: "person",
42332
- hashed: row.hashed,
42333
- ...row.meta ? { meta: row.meta } : {},
42334
- ...row.google ? { google: row.google } : {}
42408
+ hashed: row2.hashed,
42409
+ ...row2.meta ? { meta: row2.meta } : {},
42410
+ ...row2.google ? { google: row2.google } : {}
42335
42411
  }));
42336
42412
  var CLICK_FIELDS = INGEST_CLICK_ID_PARAMS.map((param) => ({
42337
42413
  name: param,
@@ -44707,18 +44783,18 @@ var ADDRESS_KEYS = [
44707
44783
  "city",
44708
44784
  "administrativeArea"
44709
44785
  ];
44710
- function nestAddress(row) {
44786
+ function nestAddress(row2) {
44711
44787
  const address = {};
44712
44788
  for (const key of ADDRESS_KEYS) {
44713
- if (row[key] !== void 0) {
44714
- address[key] = row[key];
44715
- delete row[key];
44789
+ if (row2[key] !== void 0) {
44790
+ address[key] = row2[key];
44791
+ delete row2[key];
44716
44792
  }
44717
44793
  }
44718
- return Object.keys(address).length > 0 ? { ...row, address } : row;
44794
+ return Object.keys(address).length > 0 ? { ...row2, address } : row2;
44719
44795
  }
44720
44796
  function toRow2(record) {
44721
- const row = {};
44797
+ const row2 = {};
44722
44798
  const ignored = [];
44723
44799
  for (const [column, raw] of Object.entries(record)) {
44724
44800
  const key = payloadKeyForColumn(column);
@@ -44730,10 +44806,10 @@ function toRow2(record) {
44730
44806
  }
44731
44807
  const value2 = cellValue(key, raw);
44732
44808
  if (value2 !== void 0) {
44733
- row[key] = value2;
44809
+ row2[key] = value2;
44734
44810
  }
44735
44811
  }
44736
- return { row: nestAddress(row), ignored };
44812
+ return { row: nestAddress(row2), ignored };
44737
44813
  }
44738
44814
  function cellValue(key, raw) {
44739
44815
  const value2 = typeof raw === "string" ? raw.trim() : raw;
@@ -44922,42 +44998,42 @@ function rowFromFlags(args8) {
44922
44998
  ["regionCode", args8.country],
44923
44999
  ["postalCode", args8["postal-code"]]
44924
45000
  ];
44925
- const row = {};
45001
+ const row2 = {};
44926
45002
  for (const [key, value2] of entries) {
44927
45003
  if (typeof value2 === "string" && value2.trim() !== "") {
44928
- row[key] = value2.trim();
45004
+ row2[key] = value2.trim();
44929
45005
  }
44930
45006
  }
44931
- nestAddressFlags(row);
44932
- readNumericFlags(args8, row);
44933
- return Object.keys(row).length > 0 ? row : void 0;
45007
+ nestAddressFlags(row2);
45008
+ readNumericFlags(args8, row2);
45009
+ return Object.keys(row2).length > 0 ? row2 : void 0;
44934
45010
  }
44935
- function nestAddressFlags(row) {
45011
+ function nestAddressFlags(row2) {
44936
45012
  const address = {};
44937
45013
  for (const key of ["givenName", "familyName", "regionCode", "postalCode"]) {
44938
- if (row[key] !== void 0) {
44939
- address[key] = row[key];
44940
- delete row[key];
45014
+ if (row2[key] !== void 0) {
45015
+ address[key] = row2[key];
45016
+ delete row2[key];
44941
45017
  }
44942
45018
  }
44943
45019
  if (Object.keys(address).length > 0) {
44944
- row.address = address;
45020
+ row2.address = address;
44945
45021
  }
44946
45022
  }
44947
- function readNumericFlags(args8, row) {
45023
+ function readNumericFlags(args8, row2) {
44948
45024
  if (args8["conversion-count"] !== void 0 && args8["conversion-count"] !== "") {
44949
45025
  const count2 = Number(args8["conversion-count"]);
44950
45026
  if (!Number.isInteger(count2) || count2 < 1) {
44951
45027
  failWriteValidation2("--conversion-count must be a whole number of conversions, e.g. 3");
44952
45028
  }
44953
- row.conversionCount = count2;
45029
+ row2.conversionCount = count2;
44954
45030
  }
44955
45031
  if (args8.value !== void 0 && args8.value !== "") {
44956
45032
  const numeric = Number(args8.value);
44957
45033
  if (!Number.isFinite(numeric)) {
44958
45034
  failWriteValidation2("--value must be a number in major currency units, e.g. 49.90");
44959
45035
  }
44960
- row.value = numeric;
45036
+ row2.value = numeric;
44961
45037
  }
44962
45038
  }
44963
45039
  var conversionsUploadCommand = defineCommand50({
@@ -46956,16 +47032,16 @@ function parseListFileArg(path58, maxRows) {
46956
47032
  const rows = [];
46957
47033
  for (const line of lines.slice(1)) {
46958
47034
  const cells = parseCsvLine(line);
46959
- const row = {};
47035
+ const row2 = {};
46960
47036
  columns.forEach((column, index) => {
46961
47037
  const value2 = cells[index] ?? "";
46962
47038
  if (!value2) {
46963
47039
  return;
46964
47040
  }
46965
- row[column] = column.toLowerCase() === "email" ? sha256Lower(value2) : value2;
47041
+ row2[column] = column.toLowerCase() === "email" ? sha256Lower(value2) : value2;
46966
47042
  });
46967
- if (Object.keys(row).length > 0) {
46968
- rows.push(row);
47043
+ if (Object.keys(row2).length > 0) {
47044
+ rows.push(row2);
46969
47045
  }
46970
47046
  }
46971
47047
  if (rows.length > maxRows) {
@@ -49830,8 +49906,8 @@ import { defineCommand as defineCommand64 } from "citty";
49830
49906
  function asRecord(value2) {
49831
49907
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) ? value2 : void 0;
49832
49908
  }
49833
- function readPath2(row, path58) {
49834
- let current = row;
49909
+ function readPath2(row2, path58) {
49910
+ let current = row2;
49835
49911
  for (const segment of path58.split(".")) {
49836
49912
  const record = asRecord(current);
49837
49913
  if (!record) return void 0;
@@ -49840,10 +49916,10 @@ function readPath2(row, path58) {
49840
49916
  return current;
49841
49917
  }
49842
49918
  function projectFields(rows, paths) {
49843
- return rows.map((row) => {
49919
+ return rows.map((row2) => {
49844
49920
  const projected = {};
49845
49921
  for (const path58 of paths) {
49846
- const value2 = readPath2(row, path58);
49922
+ const value2 = readPath2(row2, path58);
49847
49923
  if (value2 !== void 0) {
49848
49924
  projected[path58] = value2;
49849
49925
  }
@@ -49857,19 +49933,19 @@ function shapeListRows(rows, args8, compact5) {
49857
49933
  if (fields) {
49858
49934
  const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
49859
49935
  if (paths.length > 0) {
49860
- const projected = projectFields(raw, paths).map((row, i) => {
49861
- const missing = paths.filter((path58) => row[path58] === void 0);
49936
+ const projected = projectFields(raw, paths).map((row2, i) => {
49937
+ const missing = paths.filter((path58) => row2[path58] === void 0);
49862
49938
  const source = rows[i];
49863
- if (missing.length === 0 || source === void 0) return row;
49939
+ if (missing.length === 0 || source === void 0) return row2;
49864
49940
  const compactRow3 = compact5.row(source);
49865
49941
  for (const path58 of missing) {
49866
- if (compactRow3[path58] !== void 0) row[path58] = compactRow3[path58];
49942
+ if (compactRow3[path58] !== void 0) row2[path58] = compactRow3[path58];
49867
49943
  }
49868
- return row;
49944
+ return row2;
49869
49945
  });
49870
- const unmatched = paths.filter((path58) => projected.every((row) => row[path58] === void 0));
49946
+ const unmatched = paths.filter((path58) => projected.every((row2) => row2[path58] === void 0));
49871
49947
  if (projected.length === 0 || unmatched.length === 0) return { rows: projected, fields: paths };
49872
- const carried = /* @__PURE__ */ new Set([...compact5.columns, ...raw.flatMap((row) => Object.keys(row))]);
49948
+ const carried = /* @__PURE__ */ new Set([...compact5.columns, ...raw.flatMap((row2) => Object.keys(row2))]);
49873
49949
  return {
49874
49950
  rows: projected,
49875
49951
  fields: paths,
@@ -49881,7 +49957,7 @@ function shapeListRows(rows, args8, compact5) {
49881
49957
  }
49882
49958
  if (args8.full) {
49883
49959
  const columns = /* @__PURE__ */ new Set();
49884
- for (const row of raw) for (const key of Object.keys(row)) columns.add(key);
49960
+ for (const row2 of raw) for (const key of Object.keys(row2)) columns.add(key);
49885
49961
  return { rows: raw, fields: [...columns] };
49886
49962
  }
49887
49963
  return { rows: rows.map(compact5.row), fields: [...compact5.columns] };
@@ -49917,28 +49993,28 @@ var COMPACT_COLUMNS = [
49917
49993
  "landingUrl",
49918
49994
  "postUrn"
49919
49995
  ];
49920
- function compactRow(row) {
49921
- const destination = creativeDestination(row.content);
49996
+ function compactRow(row2) {
49997
+ const destination = creativeDestination(row2.content);
49922
49998
  return {
49923
- id: row.id,
49924
- urn: row.urn,
49925
- campaign: row.campaign,
49926
- intendedStatus: row.intendedStatus,
49927
- isServing: row.isServing,
49928
- reviewStatus: row.reviewStatus,
49999
+ id: row2.id,
50000
+ urn: row2.urn,
50001
+ campaign: row2.campaign,
50002
+ intendedStatus: row2.intendedStatus,
50003
+ isServing: row2.isServing,
50004
+ reviewStatus: row2.reviewStatus,
49929
50005
  format: destination.format,
49930
50006
  ...destination.landingUrl ? { landingUrl: destination.landingUrl } : {},
49931
50007
  ...destination.postUrn ? { postUrn: destination.postUrn } : {}
49932
50008
  };
49933
50009
  }
49934
50010
  function unmappedShapeWarnings(rows, rawContentReturned) {
49935
- const unmapped = rows.filter((row) => row.content && creativeDestination(row.content).format === "unknown");
50011
+ const unmapped = rows.filter((row2) => row2.content && creativeDestination(row2.content).format === "unknown");
49936
50012
  if (unmapped.length === 0) return [];
49937
50013
  const remedy = rawContentReturned ? "Their raw content is in this response \u2014 read it directly; this command cannot resolve a destination for them." : "Re-read those with --full before drawing any conclusion about where they point.";
49938
50014
  return [
49939
50015
  {
49940
50016
  code: "UNMAPPED_CREATIVE_CONTENT",
49941
- message: `${unmapped.length} of ${rows.length} creatives have a content shape this command can't read, so their destination is missing (ids: ${unmapped.slice(0, 5).map((row) => row.id).join(", ")}). ${remedy}`
50017
+ message: `${unmapped.length} of ${rows.length} creatives have a content shape this command can't read, so their destination is missing (ids: ${unmapped.slice(0, 5).map((row2) => row2.id).join(", ")}). ${remedy}`
49942
50018
  }
49943
50019
  ];
49944
50020
  }
@@ -50976,18 +51052,18 @@ Examples:
50976
51052
  import { defineCommand as defineCommand76 } from "citty";
50977
51053
 
50978
51054
  // src/commands/ads/meta/list-rows.ts
50979
- function pick(row, keys) {
51055
+ function pick(row2, keys) {
50980
51056
  const picked = {};
50981
51057
  for (const key of keys) {
50982
- if (row[key] !== void 0) picked[key] = row[key];
51058
+ if (row2[key] !== void 0) picked[key] = row2[key];
50983
51059
  }
50984
51060
  return picked;
50985
51061
  }
50986
51062
  var CAMPAIGN_COLUMNS = ["id", "name", "effective_status", "daily_budget", "lifetime_budget", "created_date"];
50987
- function compactCampaign(row) {
50988
- const created = row.created_time;
51063
+ function compactCampaign(row2) {
51064
+ const created = row2.created_time;
50989
51065
  return {
50990
- ...pick(row, ["id", "name", "effective_status", "daily_budget", "lifetime_budget"]),
51066
+ ...pick(row2, ["id", "name", "effective_status", "daily_budget", "lifetime_budget"]),
50991
51067
  ...typeof created === "string" ? { created_date: created.slice(0, 10) } : {}
50992
51068
  };
50993
51069
  }
@@ -51006,12 +51082,12 @@ var ADSET_KEYS = [
51006
51082
  "is_dynamic_creative"
51007
51083
  ];
51008
51084
  var ADSET_COLUMNS = [...ADSET_KEYS, "learning_stage", "end_time", "created_time"];
51009
- function compactAdSet(row) {
51010
- const learning = asRecord(row.learning_stage_info)?.status;
51085
+ function compactAdSet(row2) {
51086
+ const learning = asRecord(row2.learning_stage_info)?.status;
51011
51087
  return {
51012
- ...pick(row, ADSET_KEYS),
51088
+ ...pick(row2, ADSET_KEYS),
51013
51089
  ...typeof learning === "string" ? { learning_stage: learning } : {},
51014
- ...pick(row, ["end_time", "created_time"])
51090
+ ...pick(row2, ["end_time", "created_time"])
51015
51091
  };
51016
51092
  }
51017
51093
  var AD_COLUMNS = [
@@ -51032,14 +51108,14 @@ function adIssues(value2) {
51032
51108
  return typeof text2 === "string" && text2 ? [text2] : [];
51033
51109
  });
51034
51110
  }
51035
- function compactAd(row) {
51036
- const creativeId = asRecord(row.creative)?.id;
51037
- const issues = adIssues(row.issues_info);
51111
+ function compactAd(row2) {
51112
+ const creativeId = asRecord(row2.creative)?.id;
51113
+ const issues = adIssues(row2.issues_info);
51038
51114
  return {
51039
- ...pick(row, ["id", "name", "adset_id", "campaign_id", "effective_status"]),
51115
+ ...pick(row2, ["id", "name", "adset_id", "campaign_id", "effective_status"]),
51040
51116
  ...creativeId !== void 0 ? { creative_id: creativeId } : {},
51041
51117
  ...issues.length > 0 ? { issues } : {},
51042
- ...pick(row, ["created_time"])
51118
+ ...pick(row2, ["created_time"])
51043
51119
  };
51044
51120
  }
51045
51121
  var ACTIVITY_COLUMNS = [
@@ -51069,10 +51145,10 @@ function activityChange(extraData) {
51069
51145
  const show = (value2) => value2 === null ? "(none)" : String(value2);
51070
51146
  return `${show(before)} \u2192 ${show(after)}`;
51071
51147
  }
51072
- function compactActivity(row) {
51073
- const change = activityChange(row.extra_data);
51148
+ function compactActivity(row2) {
51149
+ const change = activityChange(row2.extra_data);
51074
51150
  return {
51075
- ...pick(row, ["event_time", "event_type", "actor_name", "object_type", "object_name", "object_id"]),
51151
+ ...pick(row2, ["event_time", "event_type", "actor_name", "object_type", "object_name", "object_id"]),
51076
51152
  ...change !== void 0 ? { change } : {}
51077
51153
  };
51078
51154
  }
@@ -54084,16 +54160,16 @@ Examples:
54084
54160
 
54085
54161
  // src/commands/ads/meta/custom-conversions.ts
54086
54162
  import { defineCommand as defineCommand84 } from "citty";
54087
- function compactCustomConversion(row) {
54163
+ function compactCustomConversion(row2) {
54088
54164
  return {
54089
- id: row.id,
54090
- name: row.name,
54091
- custom_event_type: row.custom_event_type,
54092
- counts: row.rule_summary,
54093
- pixel_id: row.pixel?.id,
54094
- last_fired_time: row.last_fired_time,
54095
- ...row.is_archived ? { archived: true } : {},
54096
- ...row.is_unavailable ? { unavailable: true } : {}
54165
+ id: row2.id,
54166
+ name: row2.name,
54167
+ custom_event_type: row2.custom_event_type,
54168
+ counts: row2.rule_summary,
54169
+ pixel_id: row2.pixel?.id,
54170
+ last_fired_time: row2.last_fired_time,
54171
+ ...row2.is_archived ? { archived: true } : {},
54172
+ ...row2.is_unavailable ? { unavailable: true } : {}
54097
54173
  };
54098
54174
  }
54099
54175
  function customConversionHints(rows) {
@@ -54105,10 +54181,10 @@ function customConversionHints(rows) {
54105
54181
  const hints2 = [
54106
54182
  "To optimize an ad set toward one: `baker ads meta adsets create \u2026 --optimization-goal OFFSITE_CONVERSIONS --custom-conversion-id <id>`. Baker fills the pixel and the event category from the conversion \u2014 do not also pass --pixel-id or --custom-event-type."
54107
54183
  ];
54108
- const neverFired = rows.filter((row) => !row.is_archived && !row.last_fired_time);
54184
+ const neverFired = rows.filter((row2) => !row2.is_archived && !row2.last_fired_time);
54109
54185
  if (neverFired.length > 0) {
54110
54186
  hints2.push(
54111
- `${neverFired.length === 1 ? "This conversion has" : `${neverFired.length} of these have`} never fired (${neverFired.map((row) => row.name ?? row.id).join(
54187
+ `${neverFired.length === 1 ? "This conversion has" : `${neverFired.length} of these have`} never fired (${neverFired.map((row2) => row2.name ?? row2.id).join(
54112
54188
  ", "
54113
54189
  )}). Meta cannot optimize toward an event it has never seen, so an ad set pointed at one delivers without learning \u2014 check the rule matches a page the site actually serves before spending on it.`
54114
54190
  );
@@ -54146,7 +54222,7 @@ var listCommand10 = defineCommand84({
54146
54222
  if (args8.limit) params.limit = args8.limit;
54147
54223
  if (args8["no-cache"]) params["skip-cache"] = "true";
54148
54224
  const rows = await apiGet("/api/ads/meta/custom-conversions", params);
54149
- const data = args8.full ? rows.map((row) => ({ ...row })) : rows.map((row) => ({ ...compactCustomConversion(row) }));
54225
+ const data = args8.full ? rows.map((row2) => ({ ...row2 })) : rows.map((row2) => ({ ...compactCustomConversion(row2) }));
54150
54226
  const fmt = csvOrJson(args8);
54151
54227
  if (fmt !== "json") {
54152
54228
  writeAdsOutput(data, fmt);
@@ -54406,16 +54482,16 @@ function sumActionValues(value2) {
54406
54482
  function aggregate(rows) {
54407
54483
  const byPlacement = /* @__PURE__ */ new Map();
54408
54484
  let totalImpressions = 0;
54409
- for (const row of rows) {
54410
- const position = row.platform_position;
54485
+ for (const row2 of rows) {
54486
+ const position = row2.platform_position;
54411
54487
  if (typeof position !== "string" || position.length === 0) continue;
54412
- const platform = typeof row.publisher_platform === "string" ? row.publisher_platform : "";
54413
- const impressions = toNumber(row.impressions);
54488
+ const platform = typeof row2.publisher_platform === "string" ? row2.publisher_platform : "";
54489
+ const impressions = toNumber(row2.impressions);
54414
54490
  totalImpressions += impressions;
54415
54491
  const key = `${platform}/${position}`;
54416
54492
  const current = byPlacement.get(key) ?? { platform, position, impressions: 0, plays: 0, playRate: 0, share: 0 };
54417
54493
  current.impressions += impressions;
54418
- current.plays += sumActionValues(row.video_play_actions);
54494
+ current.plays += sumActionValues(row2.video_play_actions);
54419
54495
  byPlacement.set(key, current);
54420
54496
  }
54421
54497
  return [...byPlacement.values()].map((placement) => ({
@@ -54430,14 +54506,14 @@ function previewCommand(placement) {
54430
54506
  }
54431
54507
  function isSingleAdQuery(rows) {
54432
54508
  const adIds = /* @__PURE__ */ new Set();
54433
- for (const row of rows) {
54434
- if (typeof row.ad_id !== "string" || row.ad_id.length === 0) return false;
54435
- adIds.add(row.ad_id);
54509
+ for (const row2 of rows) {
54510
+ if (typeof row2.ad_id !== "string" || row2.ad_id.length === 0) return false;
54511
+ adIds.add(row2.ad_id);
54436
54512
  }
54437
54513
  return adIds.size === 1;
54438
54514
  }
54439
54515
  function placementDeliveryHints(rows) {
54440
- const carriesVideoMetrics = rows.some((row) => Array.isArray(row.video_play_actions));
54516
+ const carriesVideoMetrics = rows.some((row2) => Array.isArray(row2.video_play_actions));
54441
54517
  if (!carriesVideoMetrics || !isSingleAdQuery(rows)) return [];
54442
54518
  const placements = aggregate(rows);
54443
54519
  const judgeable = placements.filter((p) => p.impressions >= MIN_IMPRESSIONS);
@@ -58335,7 +58411,7 @@ function forwarding() {
58335
58411
  "| Field | Meta | Google | Held as |",
58336
58412
  "| --- | --- | --- | --- |",
58337
58413
  ...MATCH_FIELD_FORWARDING.map(
58338
- (row) => `| \`${row.field}\` | \`${row.meta}\` | ${row.google === null ? "\u2014" : `\`${row.google}\``} | ${row.hashed ? "SHA-256" : "plain text"} |`
58414
+ (row2) => `| \`${row2.field}\` | \`${row2.meta}\` | ${row2.google === null ? "\u2014" : `\`${row2.google}\``} | ${row2.hashed ? "SHA-256" : "plain text"} |`
58339
58415
  )
58340
58416
  ].join("\n");
58341
58417
  }
@@ -58909,7 +58985,7 @@ function parseConversionsArgs(raw, argv) {
58909
58985
  return args8;
58910
58986
  }
58911
58987
  function existingNames(existing) {
58912
- return [...new Set(existing.map((row) => row.name))];
58988
+ return [...new Set(existing.map((row2) => row2.name))];
58913
58989
  }
58914
58990
  function checkNameCollision(name, existing) {
58915
58991
  const collision = findNameCollision(name, existingNames(existing));
@@ -58919,7 +58995,7 @@ function checkNameCollision(name, existing) {
58919
58995
  );
58920
58996
  }
58921
58997
  function under(name, existing) {
58922
- const rows = existing.filter((row) => row.name === name);
58998
+ const rows = existing.filter((row2) => row2.name === name);
58923
58999
  if (rows.length === 0) {
58924
59000
  throw new ConversionArgsError(
58925
59001
  `Nothing is counted under "${name}". This company counts: ${existingNames(existing).join(", ") || "nothing yet"}.`
@@ -58939,7 +59015,7 @@ function setFromEvents(args8, existing) {
58939
59015
  }));
58940
59016
  }
58941
59017
  return args8.eventKeys.map((eventKey) => {
58942
- const current = existing.find((row) => row.eventKey === eventKey);
59018
+ const current = existing.find((row2) => row2.eventKey === eventKey);
58943
59019
  if (!current) {
58944
59020
  throw new ConversionArgsError(
58945
59021
  `"${eventKey}" is not counted, so there is no count mode to change. Add it with --event "${eventKey}" --name "<what a person calls it>" --count-mode ${args8.countMode}.`
@@ -58952,52 +59028,52 @@ function setFromRename(args8, existing) {
58952
59028
  if (args8.renameFrom === void 0 || args8.renameTo === void 0) return [];
58953
59029
  const renameTo = args8.renameTo;
58954
59030
  checkNameCollision(renameTo, existing);
58955
- return under(args8.renameFrom, existing).map((row) => ({
58956
- eventKey: row.eventKey,
59031
+ return under(args8.renameFrom, existing).map((row2) => ({
59032
+ eventKey: row2.eventKey,
58957
59033
  name: renameTo,
58958
- countMode: row.countMode
59034
+ countMode: row2.countMode
58959
59035
  }));
58960
59036
  }
58961
59037
  function conversionsWrite(args8, existing) {
58962
59038
  const set = [...setFromEvents(args8, existing), ...setFromRename(args8, existing)];
58963
59039
  const remove = [
58964
59040
  ...args8.removeKeys,
58965
- ...args8.removeName === void 0 ? [] : under(args8.removeName, existing).map((row) => row.eventKey)
59041
+ ...args8.removeName === void 0 ? [] : under(args8.removeName, existing).map((row2) => row2.eventKey)
58966
59042
  ];
58967
59043
  if (set.length === 0 && remove.length === 0) return null;
58968
59044
  return { ...set.length > 0 ? { set } : {}, ...remove.length > 0 ? { remove } : {} };
58969
59045
  }
58970
59046
 
58971
59047
  // src/commands/analytics/conversionCandidates.ts
58972
- function compact3(row) {
59048
+ function compact3(row2) {
58973
59049
  return {
58974
- eventKey: row.eventKey,
58975
- events: row.events,
58976
- sessions: row.sessions,
58977
- ...row.isCounted ? { countedAs: row.countedAs } : {}
59050
+ eventKey: row2.eventKey,
59051
+ events: row2.events,
59052
+ sessions: row2.sessions,
59053
+ ...row2.isCounted ? { countedAs: row2.countedAs } : {}
58978
59054
  };
58979
59055
  }
58980
59056
  function foldByForm(rows) {
58981
59057
  const byForm = /* @__PURE__ */ new Map();
58982
- for (const row of rows) {
58983
- const form = byForm.get(row.flowSlug) ?? { form: row.flowSlug, keys: 0, countedKeys: 0, events: 0 };
59058
+ for (const row2 of rows) {
59059
+ const form = byForm.get(row2.flowSlug) ?? { form: row2.flowSlug, keys: 0, countedKeys: 0, events: 0 };
58984
59060
  form.keys += 1;
58985
- if (row.isCounted) form.countedKeys += 1;
58986
- form.events += row.events;
58987
- byForm.set(row.flowSlug, form);
59061
+ if (row2.isCounted) form.countedKeys += 1;
59062
+ form.events += row2.events;
59063
+ byForm.set(row2.flowSlug, form);
58988
59064
  }
58989
59065
  return [...byForm.values()];
58990
59066
  }
58991
59067
  function candidatesForFlow(catalogue, flowSlug) {
58992
- return flowSlug === void 0 ? [...catalogue] : catalogue.filter((row) => row.flowSlug === flowSlug);
59068
+ return flowSlug === void 0 ? [...catalogue] : catalogue.filter((row2) => row2.flowSlug === flowSlug);
58993
59069
  }
58994
59070
  var FOLDED_FORMS_HINT = "A Form's candidates are folded into its `flows` row (`keys` fired, `events`; no `counted` means it counts nothing), or a `formCandidates` row when not listed. `--candidates --flow <slug>` lists that Form's keys; `--full` adds every field of every key.";
58995
59071
  function candidatesView(rows, options) {
58996
59072
  if (options.full) return { candidates: rows };
58997
59073
  if (options.flowSlug !== void 0) return { candidates: rows.map(compact3) };
58998
- const forms = foldByForm(rows.filter((row) => row.flowSlug !== ""));
59074
+ const forms = foldByForm(rows.filter((row2) => row2.flowSlug !== ""));
58999
59075
  return {
59000
- candidates: rows.filter((row) => row.flowSlug === "").map(compact3),
59076
+ candidates: rows.filter((row2) => row2.flowSlug === "").map(compact3),
59001
59077
  ...forms.length > 0 ? { formCandidates: forms } : {}
59002
59078
  };
59003
59079
  }
@@ -59043,24 +59119,24 @@ function conversionHints2(definitions, candidates, askedForCandidates) {
59043
59119
  "Nothing is counted as a conversion, so EVERY conversion number in every other report is zero \u2014 whatever visitors actually did. Baker never guesses which event is the point of a page. Run `baker analytics conversions --candidates` to see what these pages produce, then count the ones that are worth money."
59044
59120
  );
59045
59121
  if (!askedForCandidates && candidates.length > 0) {
59046
- const top = candidates.slice(0, NAMED_CANDIDATES).map((row) => row.eventKey).join(", ");
59122
+ const top = candidates.slice(0, NAMED_CANDIDATES).map((row2) => row2.eventKey).join(", ");
59047
59123
  hints2.push(`${candidates.length} distinct events are available to count. Busiest: ${top}.`);
59048
59124
  }
59049
59125
  return hints2;
59050
59126
  }
59051
- const silent = definitions.filter((row) => row.lastSeen === null);
59127
+ const silent = definitions.filter((row2) => row2.lastSeen === null);
59052
59128
  if (silent.length > 0) {
59053
59129
  hints2.push(
59054
- `${silent.length} counted outcome(s) have never fired in this window: ${silent.slice(0, NAMED_CANDIDATES).map((row) => row.name).join(
59130
+ `${silent.length} counted outcome(s) have never fired in this window: ${silent.slice(0, NAMED_CANDIDATES).map((row2) => row2.name).join(
59055
59131
  ", "
59056
59132
  )}. Either the page never reaches them, or the wrong event was named \u2014 check against --candidates before reporting a conversion rate.`
59057
59133
  );
59058
59134
  }
59059
- const uncounted = candidates.filter((row) => !row.isCounted);
59135
+ const uncounted = candidates.filter((row2) => !row2.isCounted);
59060
59136
  const busiest = uncounted[0];
59061
59137
  if (busiest && busiest.sessions > 0) {
59062
- const counted = candidates.filter((row) => row.isCounted);
59063
- const busiestCounted = counted.reduce((max, row) => Math.max(max, row.sessions), 0);
59138
+ const counted = candidates.filter((row2) => row2.isCounted);
59139
+ const busiestCounted = counted.reduce((max, row2) => Math.max(max, row2.sessions), 0);
59064
59140
  if (busiest.sessions > busiestCounted) {
59065
59141
  hints2.push(
59066
59142
  `\`${busiest.eventKey}\` happened in ${busiest.sessions} visits and is not counted \u2014 more than anything that is. Check it is not the real outcome.`
@@ -59070,7 +59146,7 @@ function conversionHints2(definitions, candidates, askedForCandidates) {
59070
59146
  return hints2;
59071
59147
  }
59072
59148
  function namingHints(definitions) {
59073
- const names = [...new Set(definitions.map((row) => row.name))];
59149
+ const names = [...new Set(definitions.map((row2) => row2.name))];
59074
59150
  if (names.length === 0) {
59075
59151
  return [
59076
59152
  `Nothing is named yet, so the first name sets the vocabulary for this company. Start from the standard set \u2014 ${STANDARD_CONVERSION_NAMES.map((entry) => entry.name).join(", ")} \u2014 and stay in the company's own language once one exists.`
@@ -59081,29 +59157,29 @@ function namingHints(definitions) {
59081
59157
  ];
59082
59158
  }
59083
59159
  function stagedHints(definitions) {
59084
- const staged = definitions.filter((row) => row.staged === true);
59160
+ const staged = definitions.filter((row2) => row2.staged === true);
59085
59161
  if (staged.length === 0) return [];
59086
59162
  return [
59087
- `Staged on this chat, not counted yet: ${staged.map((row) => `"${row.name}"`).join(", ")}. The person sees ${staged.length === 1 ? "it" : "them"} under Sources in the chat's changes, on the event's own row, and ${staged.length === 1 ? "it counts" : "they count"} from when they publish the chat; discarding the chat drops ${staged.length === 1 ? "it" : "them"}. Publishing also rescores events Baker ALREADY RECORDED under the key \u2014 never anything that happened in HubSpot or elsewhere before the event existed, so an event this chat just made starts at zero. Tell them that, not that it is already counted.`,
59163
+ `Staged on this chat, not counted yet: ${staged.map((row2) => `"${row2.name}"`).join(", ")}. The person sees ${staged.length === 1 ? "it" : "them"} under Sources in the chat's changes, on the event's own row, and ${staged.length === 1 ? "it counts" : "they count"} from when they publish the chat; discarding the chat drops ${staged.length === 1 ? "it" : "them"}. Publishing also rescores events Baker ALREADY RECORDED under the key \u2014 never anything that happened in HubSpot or elsewhere before the event existed, so an event this chat just made starts at zero. Tell them that, not that it is already counted.`,
59088
59164
  'When you talk to the person, call each event by its name ("Appointment scheduled"), never its key (`page:Appointment scheduled`): the key is how Baker spells it internally, and the `page:` part means nothing to them.'
59089
59165
  ];
59090
59166
  }
59091
59167
 
59092
59168
  // src/commands/analytics/conversionList.ts
59093
59169
  function mergeStoredWithCounts(stored, reported) {
59094
- const counts = new Map(reported.map((row) => [row.name, row]));
59170
+ const counts = new Map(reported.map((row2) => [row2.name, row2]));
59095
59171
  const groups = /* @__PURE__ */ new Map();
59096
- for (const row of stored) {
59097
- const group = groups.get(row.name) ?? [];
59098
- group.push(row);
59099
- groups.set(row.name, group);
59172
+ for (const row2 of stored) {
59173
+ const group = groups.get(row2.name) ?? [];
59174
+ group.push(row2);
59175
+ groups.set(row2.name, group);
59100
59176
  }
59101
59177
  return [...groups.entries()].map(([name, rows]) => {
59102
- const modes = new Set(rows.map((row) => row.countMode));
59178
+ const modes = new Set(rows.map((row2) => row2.countMode));
59103
59179
  const counted = counts.get(name);
59104
59180
  return {
59105
59181
  name,
59106
- eventKeys: rows.map((row) => row.eventKey).sort(),
59182
+ eventKeys: rows.map((row2) => row2.eventKey).sort(),
59107
59183
  // A real state and usually an editing accident, which is why it is said
59108
59184
  // out loud rather than resolved to one of them.
59109
59185
  countMode: modes.size === 1 ? [...modes][0] : "mixed",
@@ -59115,7 +59191,7 @@ function mergeStoredWithCounts(stored, reported) {
59115
59191
  unplaced: counted?.unplaced ?? 0,
59116
59192
  lastSeen: counted?.lastSeen ?? null,
59117
59193
  doors: counted?.doors ?? [],
59118
- ...rows.some((row) => row.staged === true) ? { staged: true } : {}
59194
+ ...rows.some((row2) => row2.staged === true) ? { staged: true } : {}
59119
59195
  };
59120
59196
  }).sort((a, b) => a.name.localeCompare(b.name));
59121
59197
  }
@@ -59152,9 +59228,9 @@ function pruneValue(value2, full) {
59152
59228
  }
59153
59229
  return value2;
59154
59230
  }
59155
- function pruneRow(row, full) {
59231
+ function pruneRow(row2, full) {
59156
59232
  const out = {};
59157
- for (const [key, value2] of Object.entries(row)) {
59233
+ for (const [key, value2] of Object.entries(row2)) {
59158
59234
  if (DASHBOARD_ONLY_FIELDS.has(key)) continue;
59159
59235
  if (key === "sparkline" && Array.isArray(value2)) {
59160
59236
  if (!full) continue;
@@ -59171,7 +59247,7 @@ function pruneForAgent(data, full) {
59171
59247
  }
59172
59248
  function isTabular(rows) {
59173
59249
  return rows.every(
59174
- (row) => typeof row === "object" && row !== null && !Array.isArray(row) && Object.values(row).every(
59250
+ (row2) => typeof row2 === "object" && row2 !== null && !Array.isArray(row2) && Object.values(row2).every(
59175
59251
  (value2) => value2 === null || typeof value2 !== "object" || Array.isArray(value2) && value2.every((e) => typeof e !== "object")
59176
59252
  )
59177
59253
  );
@@ -59225,40 +59301,40 @@ function byBlockOrder(a, b) {
59225
59301
  }
59226
59302
  function columnsOf(rows) {
59227
59303
  const seen = [];
59228
- for (const row of rows) {
59229
- for (const key of Object.keys(row)) {
59304
+ for (const row2 of rows) {
59305
+ for (const key of Object.keys(row2)) {
59230
59306
  if (!seen.includes(key)) seen.push(key);
59231
59307
  }
59232
59308
  }
59233
- return seen.filter((key) => rows.some((row) => !isAbsent(row[key]) && row[key] !== null && row[key] !== void 0)).sort(byIdentityFirst);
59309
+ return seen.filter((key) => rows.some((row2) => !isAbsent(row2[key]) && row2[key] !== null && row2[key] !== void 0)).sort(byIdentityFirst);
59234
59310
  }
59235
59311
  function table2(rows) {
59236
59312
  const cols = columnsOf(rows);
59237
59313
  if (cols.length === 0) return "";
59238
59314
  const head = `| ${cols.join(" | ")} |`;
59239
59315
  const rule = `| ${cols.map(() => "---").join(" | ")} |`;
59240
- const body = rows.map((row) => `| ${cols.map((col) => cell2(row[col], col)).join(" | ")} |`);
59316
+ const body = rows.map((row2) => `| ${cols.map((col) => cell2(row2[col], col)).join(" | ")} |`);
59241
59317
  return [head, rule, ...body].join("\n");
59242
59318
  }
59243
59319
  function isRowList(value2) {
59244
59320
  return Array.isArray(value2) && value2.length > 0 && value2.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
59245
59321
  }
59246
- function labelOf(row, index) {
59322
+ function labelOf(row2, index) {
59247
59323
  for (const key of IDENTITY_COLUMNS) {
59248
- const value2 = row[key];
59324
+ const value2 = row2[key];
59249
59325
  if (typeof value2 === "string" && value2 !== "") return value2;
59250
59326
  }
59251
59327
  return `#${index + 1}`;
59252
59328
  }
59253
59329
  function nestedBlock(name, rows) {
59254
59330
  const parents = rows.map(
59255
- (row) => Object.fromEntries(Object.entries(row).filter(([, value2]) => !isRowList(value2) && !isPlainRow(value2)))
59331
+ (row2) => Object.fromEntries(Object.entries(row2).filter(([, value2]) => !isRowList(value2) && !isPlainRow(value2)))
59256
59332
  );
59257
59333
  const parts = [`### ${name}`, table2(parents)];
59258
- rows.forEach((row, index) => {
59259
- for (const [key, value2] of Object.entries(row)) {
59260
- if (isRowList(value2)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2(value2));
59261
- else if (isPlainRow(value2)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2([value2]));
59334
+ rows.forEach((row2, index) => {
59335
+ for (const [key, value2] of Object.entries(row2)) {
59336
+ if (isRowList(value2)) parts.push(`#### ${name}[${labelOf(row2, index)}].${key}`, table2(value2));
59337
+ else if (isPlainRow(value2)) parts.push(`#### ${name}[${labelOf(row2, index)}].${key}`, table2([value2]));
59262
59338
  }
59263
59339
  });
59264
59340
  return parts.filter((part) => part !== "").join("\n");
@@ -59303,7 +59379,7 @@ function missingBreakdownHint(data, asked) {
59303
59379
  if (asked.eventName === void 0) return [];
59304
59380
  const breakdown = data.eventBreakdown;
59305
59381
  if (Array.isArray(breakdown) && breakdown.length > 0) return [];
59306
- const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((row) => isPlainRow(row) && typeof row.eventName === "string" ? [row.eventName] : []) : [];
59382
+ const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((row2) => isPlainRow(row2) && typeof row2.eventName === "string" ? [row2.eventName] : []) : [];
59307
59383
  return [
59308
59384
  `No rows came back for --event ${asked.eventName}${asked.property ? ` --property ${asked.property}` : ""}. That event or property does not exist on these pages${known.length > 0 ? `; the ones that do are: ${known.slice(0, 12).join(", ")}${known.length > 12 ? ", \u2026" : ""}` : ""}.`
59309
59385
  ];
@@ -59348,13 +59424,13 @@ function eventKeysCell(eventKeys) {
59348
59424
  }
59349
59425
  function trafficByForm(candidates) {
59350
59426
  const byForm = /* @__PURE__ */ new Map();
59351
- for (const row of candidates) {
59352
- const form = byForm.get(row.flowSlug) ?? { "endings seen": 0, "endings counted": 0, events: 0, lastSeen: "" };
59353
- if (row.events > 0) form["endings seen"] += 1;
59354
- if (row.isCounted) form["endings counted"] += 1;
59355
- form.events += row.events;
59356
- if (row.lastSeen && row.lastSeen.slice(0, 10) > form.lastSeen) form.lastSeen = row.lastSeen.slice(0, 10);
59357
- byForm.set(row.flowSlug, form);
59427
+ for (const row2 of candidates) {
59428
+ const form = byForm.get(row2.flowSlug) ?? { "endings seen": 0, "endings counted": 0, events: 0, lastSeen: "" };
59429
+ if (row2.events > 0) form["endings seen"] += 1;
59430
+ if (row2.isCounted) form["endings counted"] += 1;
59431
+ form.events += row2.events;
59432
+ if (row2.lastSeen && row2.lastSeen.slice(0, 10) > form.lastSeen) form.lastSeen = row2.lastSeen.slice(0, 10);
59433
+ byForm.set(row2.flowSlug, form);
59358
59434
  }
59359
59435
  return byForm;
59360
59436
  }
@@ -59363,32 +59439,32 @@ var FORM_KEYS_HINT = "A Form's candidates are one row per Form: `baker analytics
59363
59439
  function renderConversionsMarkdown(envelope) {
59364
59440
  const { definitions, flow, flows, candidates } = envelope.data;
59365
59441
  const data = {
59366
- definitions: definitions.map(({ name, eventKeys, ...row }) => ({
59442
+ definitions: definitions.map(({ name, eventKeys, ...row2 }) => ({
59367
59443
  name,
59368
59444
  eventKeys: eventKeysCell(eventKeys),
59369
- ...row
59445
+ ...row2
59370
59446
  }))
59371
59447
  };
59372
59448
  if (flow) {
59373
59449
  data.flow = { slug: flow.slug, noConversionReason: flow.noConversionReason };
59374
59450
  data.flowEndings = flow.endings;
59375
59451
  }
59376
- const traffic = candidates ? trafficByForm(candidates.filter((row) => row.flowSlug !== "")) : /* @__PURE__ */ new Map();
59452
+ const traffic = candidates ? trafficByForm(candidates.filter((row2) => row2.flowSlug !== "")) : /* @__PURE__ */ new Map();
59377
59453
  if (flows) {
59378
- data.flows = flows.map((row) => ({
59379
- slug: row.slug,
59380
- counted: row.counted.map((entry) => `${entry.name} @ ${entry.step}`).join("; "),
59381
- noConversion: row.noConversion,
59382
- ...candidates ? traffic.get(row.slug) ?? NO_TRAFFIC : {}
59454
+ data.flows = flows.map((row2) => ({
59455
+ slug: row2.slug,
59456
+ counted: row2.counted.map((entry) => `${entry.name} @ ${entry.step}`).join("; "),
59457
+ noConversion: row2.noConversion,
59458
+ ...candidates ? traffic.get(row2.slug) ?? NO_TRAFFIC : {}
59383
59459
  }));
59384
59460
  }
59385
59461
  const hints2 = [...envelope.hints ?? []];
59386
59462
  if (candidates) {
59387
- const listed2 = new Set(flows?.map((row) => row.slug));
59463
+ const listed2 = new Set(flows?.map((row2) => row2.slug));
59388
59464
  const unlisted = [...traffic].filter(([slug]) => !listed2.has(slug));
59389
- if (unlisted.length > 0) data.formCandidates = unlisted.map(([form, row]) => ({ form, ...row }));
59390
- data.candidates = candidates.filter((row) => row.flowSlug === "").map(({ doors, flowSlug: _flowSlug, nodeType: _nodeType, ...row }) => ({
59391
- ...row,
59465
+ if (unlisted.length > 0) data.formCandidates = unlisted.map(([form, row2]) => ({ form, ...row2 }));
59466
+ data.candidates = candidates.filter((row2) => row2.flowSlug === "").map(({ doors, flowSlug: _flowSlug, nodeType: _nodeType, ...row2 }) => ({
59467
+ ...row2,
59392
59468
  "doors (events/sessions)": doorsCell(doors)
59393
59469
  }));
59394
59470
  if (traffic.size > 0) hints2.push(FORM_KEYS_HINT);
@@ -59412,18 +59488,18 @@ function analyticsErrorEnvelope(err) {
59412
59488
  function isStreamRow(value2) {
59413
59489
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
59414
59490
  }
59415
- function enrich(row) {
59416
- const out = { ...row };
59417
- const destination = typeof row.sideEffectType === "string" ? row.sideEffectType : "";
59491
+ function enrich(row2) {
59492
+ const out = { ...row2 };
59493
+ const destination = typeof row2.sideEffectType === "string" ? row2.sideEffectType : "";
59418
59494
  if (destination !== "") out.destination = destinationName(destination);
59419
- const keys = rowMatchKeys(row.userData);
59495
+ const keys = rowMatchKeys(row2.userData);
59420
59496
  if (keys.length > 0) out.matchedBy = keys.map((key) => key.field);
59421
59497
  return out;
59422
59498
  }
59423
59499
  function shapeFeed(stream, everything) {
59424
59500
  if (!Array.isArray(stream)) return { rows: [], heldBack: 0 };
59425
59501
  const rows = stream.filter(isStreamRow);
59426
- const kept = everything ? rows : rows.filter((row) => !isBookkeepingRow({ eventType: row.eventType ?? "", triggerId: row.triggerId ?? "" }));
59502
+ const kept = everything ? rows : rows.filter((row2) => !isBookkeepingRow({ eventType: row2.eventType ?? "", triggerId: row2.triggerId ?? "" }));
59427
59503
  return { rows: kept.map(enrich), heldBack: rows.length - kept.length };
59428
59504
  }
59429
59505
  function feedHints(shape) {
@@ -59433,7 +59509,7 @@ function feedHints(shape) {
59433
59509
  `${shape.heldBack} ${shape.heldBack === 1 ? "row is" : "rows are"} not listed: rows recorded to work the reports out \u2014 identifications, step views, session starts, a destination's answer already folded onto its dispatch \u2014 and embedded forms and calendars announcing that they drew. Add --everything to see them.`
59434
59510
  );
59435
59511
  }
59436
- const upload = shape.rows.find((row) => row.sideEffectType !== void 0 && row.eventId !== void 0);
59512
+ const upload = shape.rows.find((row2) => row2.sideEffectType !== void 0 && row2.eventId !== void 0);
59437
59513
  if (upload !== void 0) {
59438
59514
  hints2.push(
59439
59515
  `A delivery row carries the event it was made for in \`eventId\`. \`baker analytics feed --uid ${String(upload.eventId)}\` returns that event and every upload made for it, each with the platform's verdict.`
@@ -59442,7 +59518,7 @@ function feedHints(shape) {
59442
59518
  return hints2;
59443
59519
  }
59444
59520
  function peopleSearchHints(search, rows) {
59445
- const found = new Set(rows.flatMap((row) => Array.isArray(row.personTerms) ? row.personTerms : []));
59521
+ const found = new Set(rows.flatMap((row2) => Array.isArray(row2.personTerms) ? row2.personTerms : []));
59446
59522
  return search.filter((term) => !found.has(term.term)).map(
59447
59523
  (term) => term.problem !== "" ? `${term.input}: ${term.problem}` : `${term.input}: nothing in this window on this page. \`baker analytics lookup --person '${term.input}'\` reads everything Baker ever saw of them.`
59448
59524
  );
@@ -59474,10 +59550,10 @@ function joinFlowConversions(candidates, definitions) {
59474
59550
  return candidates.map((candidate) => ({ ...candidate, countedAs: byKey.get(candidate.eventKey) ?? null }));
59475
59551
  }
59476
59552
  function flowConversionHints(slug, rows, noConversionReason) {
59477
- const counted = rows.filter((row) => row.countedAs !== null);
59553
+ const counted = rows.filter((row2) => row2.countedAs !== null);
59478
59554
  if (counted.length > 0) {
59479
59555
  return [
59480
- `Baker counts ${counted.length} of this Form's endings: ${counted.map((row) => `${row.countedAs} (${row.nodeName})`).join(
59556
+ `Baker counts ${counted.length} of this Form's endings: ${counted.map((row2) => `${row2.countedAs} (${row2.nodeName})`).join(
59481
59557
  ", "
59482
59558
  )}. Every other ending here scores nothing \u2014 count another with \`--event <key> --name <outcome>\`, reusing the SAME name to make two Forms one number.`
59483
59559
  ];
@@ -59499,21 +59575,21 @@ function flowCoverage(forms, definitions) {
59499
59575
  const seen = /* @__PURE__ */ new Set();
59500
59576
  return {
59501
59577
  slug,
59502
- counted: rows.filter((row) => row.countedAs !== null).filter((row) => {
59503
- const key = `${row.countedAs} ${row.nodeName}`;
59578
+ counted: rows.filter((row2) => row2.countedAs !== null).filter((row2) => {
59579
+ const key = `${row2.countedAs} ${row2.nodeName}`;
59504
59580
  if (seen.has(key)) return false;
59505
59581
  seen.add(key);
59506
59582
  return true;
59507
- }).map((row) => ({ name: String(row.countedAs), step: row.nodeName })),
59583
+ }).map((row2) => ({ name: String(row2.countedAs), step: row2.nodeName })),
59508
59584
  noConversion: typeof declared === "string" && declared.trim() !== "" ? declared.trim() : null
59509
59585
  };
59510
59586
  });
59511
59587
  }
59512
59588
  function flowCoverageHints(coverage) {
59513
- const unset = coverage.filter((row) => row.counted.length === 0 && row.noConversion === null);
59589
+ const unset = coverage.filter((row2) => row2.counted.length === 0 && row2.noConversion === null);
59514
59590
  if (unset.length === 0) return [];
59515
59591
  return [
59516
- `${unset.length} of ${coverage.length} Form(s) here count nothing, so every report shows them converting nobody: ${unset.map((row) => row.slug).join(
59592
+ `${unset.length} of ${coverage.length} Form(s) here count nothing, so every report shows them converting nobody: ${unset.map((row2) => row2.slug).join(
59517
59593
  ", "
59518
59594
  )}. Read each one's endings with \`baker analytics conversions --flow <slug>\` and name the outcome, or declare on its first step why it has none.`
59519
59595
  ];
@@ -59542,9 +59618,9 @@ function shapeHints(data) {
59542
59618
  );
59543
59619
  }
59544
59620
  }
59545
- const unread = (data.adParams ?? []).filter((row) => row.role === void 0 && row.sessions > 0);
59621
+ const unread = (data.adParams ?? []).filter((row2) => row2.role === void 0 && row2.sessions > 0);
59546
59622
  if (unread.length > 0) {
59547
- const named = unread.slice(0, 8).map((row) => `${row.name} (${row.sessions} visits)`).join(", ");
59623
+ const named = unread.slice(0, 8).map((row2) => `${row2.name} (${row2.sessions} visits)`).join(", ");
59548
59624
  hints2.push(
59549
59625
  `Nothing reads these parameters yet: ${named}${unread.length > 8 ? ", \u2026" : ""}. Their values are not being stored \u2014 only the fact that the name appeared \u2014 so any breakdown over them is empty rather than wrong. Name each one with \`baker analytics map --platform <platform> --set <name>=<role>\`; mapping also fixes the history for a parameter Baker was already capturing.`
59550
59626
  );
@@ -59552,7 +59628,7 @@ function shapeHints(data) {
59552
59628
  const unplaceable = data.adParamsUnattributed ?? [];
59553
59629
  if (unplaceable.length > 0) {
59554
59630
  hints2.push(
59555
- `Some parameters arrive on visits with no click id and no recognised source, so no platform can be named for them: ${unplaceable.map((row) => row.name).join(", ")}. If you know where they come from, map them with --platform on that platform.`
59631
+ `Some parameters arrive on visits with no click id and no recognised source, so no platform can be named for them: ${unplaceable.map((row2) => row2.name).join(", ")}. If you know where they come from, map them with --platform on that platform.`
59556
59632
  );
59557
59633
  }
59558
59634
  return hints2;
@@ -59563,7 +59639,7 @@ function adHierarchyHints(data, platform) {
59563
59639
  if (traffic === void 0 || traffic.sessions < MIN_SESSIONS_FOR_TAGGING_CLAIM) return [];
59564
59640
  if (traffic.taggedSessions === 0) return [];
59565
59641
  const carried = new Set(
59566
- (data.adParams ?? []).filter((row) => row.role !== void 0 && row.awaitingTraffic !== true).map((row) => row.role)
59642
+ (data.adParams ?? []).filter((row2) => row2.role !== void 0 && row2.awaitingTraffic !== true).map((row2) => row2.role)
59567
59643
  );
59568
59644
  const missing = essentialAdDimensions(platform).filter((entry) => !carried.has(entry.role));
59569
59645
  if (missing.length === 0) return [];
@@ -59631,7 +59707,7 @@ function buildAnalyticsHints(data, platform, origin) {
59631
59707
  }
59632
59708
  function campaignOriginHints(rows) {
59633
59709
  const campaigns = rows ?? [];
59634
- const fromAds = campaigns.filter((row) => row.clickPlatform !== "");
59710
+ const fromAds = campaigns.filter((row2) => row2.clickPlatform !== "");
59635
59711
  if (fromAds.length === 0) return [];
59636
59712
  const own = campaigns.length - fromAds.length;
59637
59713
  return [
@@ -59675,28 +59751,28 @@ function peopleLimitError(args8) {
59675
59751
  if (dropped === 0) return void 0;
59676
59752
  return `${terms.length + dropped} people named; one search takes up to ${PEOPLE_SEARCH_MAX_TERMS}. Split the list and run each part.`;
59677
59753
  }
59678
- function compactRow2(row) {
59679
- const clicks = [...row.googleClick ? ["google"] : [], ...row.metaClick ? ["meta"] : []];
59754
+ function compactRow2(row2) {
59755
+ const clicks = [...row2.googleClick ? ["google"] : [], ...row2.metaClick ? ["meta"] : []];
59680
59756
  const candidates = {
59681
- input: row.input,
59682
- readAs: row.kind,
59683
- found: row.found,
59684
- problem: row.problem,
59685
- contacts: row.contacts,
59686
- events: row.events,
59687
- firstSeen: row.firstSeen,
59688
- lastSeen: row.lastSeen,
59689
- pageViews: row.pageViews,
59690
- landings: row.landings,
59691
- visits: row.visits,
59692
- formSubmits: row.formSubmits,
59693
- firstSource: row.firstSource,
59694
- systemEvents: row.systemEvents,
59695
- pageEvents: row.pageEvents,
59696
- uploads: row.uploads,
59697
- uploadedTo: row.uploadedTo,
59757
+ input: row2.input,
59758
+ readAs: row2.kind,
59759
+ found: row2.found,
59760
+ problem: row2.problem,
59761
+ contacts: row2.contacts,
59762
+ events: row2.events,
59763
+ firstSeen: row2.firstSeen,
59764
+ lastSeen: row2.lastSeen,
59765
+ pageViews: row2.pageViews,
59766
+ landings: row2.landings,
59767
+ visits: row2.visits,
59768
+ formSubmits: row2.formSubmits,
59769
+ firstSource: row2.firstSource,
59770
+ systemEvents: row2.systemEvents,
59771
+ pageEvents: row2.pageEvents,
59772
+ uploads: row2.uploads,
59773
+ uploadedTo: row2.uploadedTo,
59698
59774
  clicks,
59699
- knownBy: row.knownBy
59775
+ knownBy: row2.knownBy
59700
59776
  };
59701
59777
  return Object.fromEntries(
59702
59778
  Object.entries(candidates).filter(
@@ -59711,17 +59787,17 @@ function lookupHints(data) {
59711
59787
  `${data.dropped} lines past the limit of ${PEOPLE_SEARCH_MAX_TERMS} were not looked up. Run the rest separately.`
59712
59788
  );
59713
59789
  }
59714
- const unseen = data.people.filter((row) => !row.found && row.problem === "");
59790
+ const unseen = data.people.filter((row2) => !row2.found && row2.problem === "");
59715
59791
  if (unseen.length > 0) {
59716
59792
  hints2.push(
59717
- `Not seen by Baker: ${unseen.map((row) => row.input).join(", ")}. Baker knows a person by what its pages, Forms and connected systems recorded \u2014 a lead that came in through a form or site Baker does not measure, or a source not connected, is not here.`
59793
+ `Not seen by Baker: ${unseen.map((row2) => row2.input).join(", ")}. Baker knows a person by what its pages, Forms and connected systems recorded \u2014 a lead that came in through a form or site Baker does not measure, or a source not connected, is not here.`
59718
59794
  );
59719
59795
  }
59720
- for (const row of data.people.filter((person) => person.problem !== "")) {
59721
- hints2.push(`${row.input}: ${row.problem}`);
59796
+ for (const row2 of data.people.filter((person) => person.problem !== "")) {
59797
+ hints2.push(`${row2.input}: ${row2.problem}`);
59722
59798
  }
59723
- const found = data.people.filter((row) => row.found);
59724
- const neverVisited = found.filter((row) => row.pageViews === 0);
59799
+ const found = data.people.filter((row2) => row2.found);
59800
+ const neverVisited = found.filter((row2) => row2.pageViews === 0);
59725
59801
  if (neverVisited.length > 0) {
59726
59802
  hints2.push(
59727
59803
  `${neverVisited.length} of ${found.length} found never visited a page Baker publishes: Baker knows them only from what their systems reported, so no landing can be credited with them.`
@@ -59735,13 +59811,13 @@ function lookupHints(data) {
59735
59811
  return hints2;
59736
59812
  }
59737
59813
  function lookupReport(data, full = false) {
59738
- const found = data.people.filter((row) => row.found);
59814
+ const found = data.people.filter((row2) => row2.found);
59739
59815
  return {
59740
59816
  summary: {
59741
59817
  searched: data.people.length,
59742
59818
  found: found.length,
59743
59819
  notFound: data.people.length - found.length,
59744
- visitedABakerPage: found.filter((row) => row.pageViews > 0).length
59820
+ visitedABakerPage: found.filter((row2) => row2.pageViews > 0).length
59745
59821
  },
59746
59822
  people: full ? data.people : data.people.map(compactRow2),
59747
59823
  hints: lookupHints(data)
@@ -60216,9 +60292,9 @@ var SOURCE_NAME_MAX = 80;
60216
60292
  var sourceNameSchema = z49.string().trim().min(1).max(SOURCE_NAME_MAX).refine((name) => validateEventName(name) === null, REJECTION_MESSAGES.invalid_name);
60217
60293
  var sourceIdSchema = z49.string().min(1);
60218
60294
  var sourceMappingSchema = z49.record(z49.string(), z49.string().min(1)).superRefine((mapping, ctx) => {
60219
- for (const row of Object.keys(mapping)) {
60220
- const problem = mappingRowProblem(row);
60221
- if (problem !== null) ctx.addIssue({ code: "custom", message: problem, path: [row] });
60295
+ for (const row2 of Object.keys(mapping)) {
60296
+ const problem = mappingRowProblem(row2);
60297
+ if (problem !== null) ctx.addIssue({ code: "custom", message: problem, path: [row2] });
60222
60298
  }
60223
60299
  });
60224
60300
  var currencyCodeSchema = z49.string().trim().toUpperCase().regex(/^[A-Z]{3}$/, "A currency is a three-letter code, like EUR or USD.");
@@ -77167,43 +77243,43 @@ var experimentFinishResponseSchema = z62.object({
77167
77243
 
77168
77244
  // src/commands/experiment/hints.ts
77169
77245
  var LIVE_HINT = {
77170
- keep_running: (row, now) => `${row.experimentId} cannot yet tell the two pages apart \u2014 do not end it hoping for a result. ${describeWait2(row, now)} To the user, say only this in plain words: "${row.summary}" plus how much more data it needs and roughly when. Never quote confidence, chance-better or intervals to them \u2014 those numbers confuse marketers and decide nothing.`,
77171
- winner: (row) => row.winner === "variant" ? `${row.experimentId} has a winner: the new variant. Run \`baker experiment finish --id ${row.experimentId} --keep variant\` to show it to everyone, then \`baker experiment fold\`.` : `${row.experimentId}: the new version is proven worse and is STILL being shown to half the visitors. Run \`baker experiment finish --id ${row.experimentId} --keep original\` now.`,
77172
- no_difference: (row) => `${row.experimentId} measured the two pages as alike \u2014 it collected enough traffic to see the change it was looking for, and there was none. End it with \`--keep original\` and test a bigger change \u2014 a headline, an offer, the shape of the page, not a button colour.`,
77173
- cannot_win: (row) => `${row.experimentId}: the new version cannot win \u2014 even its best case is under the lift this test was looking for, so there is nothing left to find. Run \`baker experiment finish --id ${row.experimentId} --keep original --reason numbers_clear\` and write down what it taught.`,
77174
- stopped_early_harmful: (row) => `${row.experimentId} is doing damage and is STILL being shown to visitors. Run \`baker experiment finish --id ${row.experimentId} --keep original\` now.`,
77246
+ keep_running: (row2, now) => `${row2.experimentId} cannot yet tell the two pages apart \u2014 do not end it hoping for a result. ${describeWait2(row2, now)} To the user, say only this in plain words: "${row2.summary}" plus how much more data it needs and roughly when. Never quote confidence, chance-better or intervals to them \u2014 those numbers confuse marketers and decide nothing.`,
77247
+ winner: (row2) => row2.winner === "variant" ? `${row2.experimentId} has a winner: the new variant. Run \`baker experiment finish --id ${row2.experimentId} --keep variant\` to show it to everyone, then \`baker experiment fold\`.` : `${row2.experimentId}: the new version is proven worse and is STILL being shown to half the visitors. Run \`baker experiment finish --id ${row2.experimentId} --keep original\` now.`,
77248
+ no_difference: (row2) => `${row2.experimentId} measured the two pages as alike \u2014 it collected enough traffic to see the change it was looking for, and there was none. End it with \`--keep original\` and test a bigger change \u2014 a headline, an offer, the shape of the page, not a button colour.`,
77249
+ cannot_win: (row2) => `${row2.experimentId}: the new version cannot win \u2014 even its best case is under the lift this test was looking for, so there is nothing left to find. Run \`baker experiment finish --id ${row2.experimentId} --keep original --reason numbers_clear\` and write down what it taught.`,
77250
+ stopped_early_harmful: (row2) => `${row2.experimentId} is doing damage and is STILL being shown to visitors. Run \`baker experiment finish --id ${row2.experimentId} --keep original\` now.`,
77175
77251
  // Unreachable on a live test — only ending one produces it. The Record is
77176
77252
  // exhaustive by type so a verdict added upstream fails to compile here rather
77177
77253
  // than printing nothing.
77178
- inconclusive: (row) => `${row.experimentId} was ended before it concluded and found nothing.`,
77179
- invalid: (row) => `${row.experimentId} cannot be read: ${row.invalidReason === "crossover" ? "too many visitors saw both versions" : "the traffic did not split evenly"}. End it with \`--keep original\` and start again rather than reporting anything from it.`
77254
+ inconclusive: (row2) => `${row2.experimentId} was ended before it concluded and found nothing.`,
77255
+ invalid: (row2) => `${row2.experimentId} cannot be read: ${row2.invalidReason === "crossover" ? "too many visitors saw both versions" : "the traffic did not split evenly"}. End it with \`--keep original\` and start again rather than reporting anything from it.`
77180
77256
  };
77181
- function describeWait2(row, now) {
77182
- if (row.willNotConclude) {
77257
+ function describeWait2(row2, now) {
77258
+ if (row2.willNotConclude) {
77183
77259
  return "This page's traffic will not settle it at any point, so end it with `--keep original` and test a bigger change.";
77184
77260
  }
77185
- if (row.earliestDecisionAt === null) {
77261
+ if (row2.earliestDecisionAt === null) {
77186
77262
  return "This page has produced no visitors yet, so there is nothing to project a finish date from.";
77187
77263
  }
77188
- if (row.stillNeeded === null) {
77189
- return `It already has the data it needs; the verdict waits only until it has seen a full day of visitors \u2014 ${describeWait(row.earliestDecisionAt, now)}.`;
77264
+ if (row2.stillNeeded === null) {
77265
+ return `It already has the data it needs; the verdict waits only until it has seen a full day of visitors \u2014 ${describeWait(row2.earliestDecisionAt, now)}.`;
77190
77266
  }
77191
- const needed = `It needs ${roughCount(row.stillNeeded.visitors)} more visitors (${roughCount(row.stillNeeded.conversions)} more conversions) for an answer if the gap holds \u2014 `;
77192
- return `${needed}${describeWait(row.earliestDecisionAt, now)} at today's traffic (${new Date(row.earliestDecisionAt).toISOString().slice(0, 10)}). A test is gated on this data, never on days.`;
77267
+ const needed = `It needs ${roughCount(row2.stillNeeded.visitors)} more visitors (${roughCount(row2.stillNeeded.conversions)} more conversions) for an answer if the gap holds \u2014 `;
77268
+ return `${needed}${describeWait(row2.earliestDecisionAt, now)} at today's traffic (${new Date(row2.earliestDecisionAt).toISOString().slice(0, 10)}). A test is gated on this data, never on days.`;
77193
77269
  }
77194
- function pausedNote(row) {
77195
- return row.status === "paused" ? `${row.experimentId} is paused \u2014 everyone sees the original. \`baker experiment resume --id ${row.experimentId}\` restarts the split with the same sides. ` : "";
77270
+ function pausedNote(row2) {
77271
+ return row2.status === "paused" ? `${row2.experimentId} is paused \u2014 everyone sees the original. \`baker experiment resume --id ${row2.experimentId}\` restarts the split with the same sides. ` : "";
77196
77272
  }
77197
77273
  function unlearnedHint(experiments) {
77198
77274
  const unlearned = experiments.filter(
77199
- (row) => row.status === "finished" && row.learning === null && row.verdict !== "inconclusive"
77275
+ (row2) => row2.status === "finished" && row2.learning === null && row2.verdict !== "inconclusive"
77200
77276
  );
77201
77277
  if (unlearned.length === 0) return null;
77202
- const ids2 = unlearned.map((row) => row.experimentId).join(", ");
77278
+ const ids2 = unlearned.map((row2) => row2.experimentId).join(", ");
77203
77279
  return `${unlearned.length === 1 ? "One finished test has" : `${unlearned.length} finished tests have`} no learning written down yet (${ids2}). Say what it taught in one or two sentences \u2014 what the client should believe about this page now \u2014 with \`baker experiment update --id <id> --learning "\u2026"\`. It is what \`plan\` hands the next test.`;
77204
77280
  }
77205
77281
  function buildStatusHints(experiments, now = Date.now()) {
77206
- const hints2 = experiments.filter((row) => row.status !== "finished").map((row) => pausedNote(row) + LIVE_HINT[row.verdict](row, now));
77282
+ const hints2 = experiments.filter((row2) => row2.status !== "finished").map((row2) => pausedNote(row2) + LIVE_HINT[row2.verdict](row2, now));
77207
77283
  const unlearned = unlearnedHint(experiments);
77208
77284
  if (unlearned) hints2.push(unlearned);
77209
77285
  if (experiments.length === 0) {
@@ -77483,52 +77559,52 @@ function fullArm(arm) {
77483
77559
  ...arm.snapshotCapturedAt === void 0 ? {} : { snapshotCapturedAt: isoMoment(arm.snapshotCapturedAt) }
77484
77560
  };
77485
77561
  }
77486
- function statusReadout(row, { full, now }) {
77562
+ function statusReadout(row2, { full, now }) {
77487
77563
  const decisionWhen = {
77488
- earliestDecisionAt: isoDay2(row.earliestDecisionAt),
77489
- ...row.earliestDecisionAt === null ? {} : { earliestDecisionIn: describeWait(row.earliestDecisionAt, now) }
77564
+ earliestDecisionAt: isoDay2(row2.earliestDecisionAt),
77565
+ ...row2.earliestDecisionAt === null ? {} : { earliestDecisionIn: describeWait(row2.earliestDecisionAt, now) }
77490
77566
  };
77491
77567
  const moments = {
77492
- startedAt: isoMoment(row.startedAt),
77493
- pausedAt: isoMoment(row.pausedAt),
77494
- finishedAt: isoMoment(row.finishedAt)
77568
+ startedAt: isoMoment(row2.startedAt),
77569
+ pausedAt: isoMoment(row2.pausedAt),
77570
+ finishedAt: isoMoment(row2.finishedAt)
77495
77571
  };
77496
77572
  if (full) {
77497
77573
  return {
77498
- ...row,
77574
+ ...row2,
77499
77575
  ...moments,
77500
77576
  ...decisionWhen,
77501
- control: fullArm(row.control),
77502
- variant: fullArm(row.variant)
77577
+ control: fullArm(row2.control),
77578
+ variant: fullArm(row2.variant)
77503
77579
  };
77504
77580
  }
77505
77581
  return {
77506
- experimentId: row.experimentId,
77507
- landingSlug: row.landingSlug,
77508
- variantSlug: row.variantSlug,
77509
- status: row.status,
77510
- verdict: row.verdict,
77511
- summary: row.summary,
77512
- ...row.winner === null ? {} : { winner: row.winner },
77513
- ...row.invalidReason === null ? {} : { invalidReason: row.invalidReason },
77514
- stillNeeded: row.stillNeeded,
77582
+ experimentId: row2.experimentId,
77583
+ landingSlug: row2.landingSlug,
77584
+ variantSlug: row2.variantSlug,
77585
+ status: row2.status,
77586
+ verdict: row2.verdict,
77587
+ summary: row2.summary,
77588
+ ...row2.winner === null ? {} : { winner: row2.winner },
77589
+ ...row2.invalidReason === null ? {} : { invalidReason: row2.invalidReason },
77590
+ stillNeeded: row2.stillNeeded,
77515
77591
  ...decisionWhen,
77516
- willNotConclude: row.willNotConclude,
77517
- control: compactArm(row.control),
77518
- variant: compactArm(row.variant),
77519
- secondary: row.secondary.map(compactMetric),
77520
- hypothesisStatement: row.hypothesisStatement,
77521
- minDetectableRelativeLift: row.minDetectableRelativeLift,
77522
- goal: row.goal,
77523
- ...row.variantGoal === null ? {} : { variantGoal: row.variantGoal },
77524
- goalLabel: row.goalLabel,
77592
+ willNotConclude: row2.willNotConclude,
77593
+ control: compactArm(row2.control),
77594
+ variant: compactArm(row2.variant),
77595
+ secondary: row2.secondary.map(compactMetric),
77596
+ hypothesisStatement: row2.hypothesisStatement,
77597
+ minDetectableRelativeLift: row2.minDetectableRelativeLift,
77598
+ goal: row2.goal,
77599
+ ...row2.variantGoal === null ? {} : { variantGoal: row2.variantGoal },
77600
+ goalLabel: row2.goalLabel,
77525
77601
  ...moments,
77526
- ...row.status === "finished" ? {
77527
- survivor: row.survivor,
77528
- hypothesisOutcome: row.hypothesisOutcome,
77529
- fold: row.fold,
77530
- learning: row.learning,
77531
- endReason: row.endReason
77602
+ ...row2.status === "finished" ? {
77603
+ survivor: row2.survivor,
77604
+ hypothesisOutcome: row2.hypothesisOutcome,
77605
+ fold: row2.fold,
77606
+ learning: row2.learning,
77607
+ endReason: row2.endReason
77532
77608
  } : {}
77533
77609
  };
77534
77610
  }
@@ -77806,7 +77882,7 @@ var statusCommand7 = defineCommand149({
77806
77882
  writeJsonEnvelope({
77807
77883
  ok: true,
77808
77884
  data: {
77809
- experiments: response.data.experiments.map((row) => statusReadout(row, { full: args8.full === true, now }))
77885
+ experiments: response.data.experiments.map((row2) => statusReadout(row2, { full: args8.full === true, now }))
77810
77886
  },
77811
77887
  hints: buildStatusHints(response.data.experiments, now)
77812
77888
  });
@@ -79202,7 +79278,7 @@ function assertDestinationReady(slug, tree, chosen, into) {
79202
79278
  function collectConfigStatusFor(tree, _slug, sideEffectId) {
79203
79279
  const rows = [];
79204
79280
  collectConfigStatus(tree, rows);
79205
- return rows.find((row) => row.sideEffectId === sideEffectId);
79281
+ return rows.find((row2) => row2.sideEffectId === sideEffectId);
79206
79282
  }
79207
79283
  function slotHintFor(type, into) {
79208
79284
  const others = otherSlots(type, shapeFor(type, into));
@@ -79629,9 +79705,9 @@ function statusFor(slug) {
79629
79705
  return config;
79630
79706
  }
79631
79707
  function pendingInputHints(slug, config) {
79632
- const pending = config.filter((row) => row.needsInput);
79708
+ const pending = config.filter((row2) => row2.needsInput);
79633
79709
  if (pending.length === 0) return [];
79634
- const calls = pending.slice(0, MAX_PENDING_HINTS).map((row) => `${row.kind}: ${requestFlowInputCall(slug, row)}`);
79710
+ const calls = pending.slice(0, MAX_PENDING_HINTS).map((row2) => `${row2.kind}: ${requestFlowInputCall(slug, row2)}`);
79635
79711
  const rest = pending.length - calls.length;
79636
79712
  return [
79637
79713
  `${pending.length} item${pending.length === 1 ? "" : "s"} still need the user. Each is one blocking call, and its result carries the destination \u2014 read the names from there rather than looking them up again.`,
@@ -80555,7 +80631,7 @@ var funnelCommand2 = defineCommand155({
80555
80631
  ...namedByCaller.length === 0 ? {} : { namedByCaller }
80556
80632
  };
80557
80633
  const response = await apiPost("/api/funnel", request);
80558
- const counted = new Map((response.data.counted ?? []).map((row) => [row.eventKey, row.name]));
80634
+ const counted = new Map((response.data.counted ?? []).map((row2) => [row2.eventKey, row2.name]));
80559
80635
  const data = response.data.moments === void 0 ? response.data : { ...response.data, moments: withFormMoments(response.data.moments, forms, counted) };
80560
80636
  const hints2 = [...response.hints ?? [], ...unplacedFormHints(response.data.steps ?? [], forms, counted)];
80561
80637
  writeJsonEnvelope({
@@ -81173,11 +81249,11 @@ function writeRowsToFile2(filePath, rows, append) {
81173
81249
  writeFileSync4(filePath, `${toCsvRow(fields)}
81174
81250
  `, "utf-8");
81175
81251
  }
81176
- const csvRows = rows.map((row) => toCsvRow(fields.map((f) => String(row[f] ?? ""))));
81252
+ const csvRows = rows.map((row2) => toCsvRow(fields.map((f) => String(row2[f] ?? ""))));
81177
81253
  appendFileSync2(filePath, `${csvRows.join("\n")}
81178
81254
  `, "utf-8");
81179
81255
  } else if (ext === "jsonl") {
81180
- const lines = rows.map((row) => JSON.stringify(row));
81256
+ const lines = rows.map((row2) => JSON.stringify(row2));
81181
81257
  const content = `${lines.join("\n")}
81182
81258
  `;
81183
81259
  if (append && existsSync10(filePath)) {
@@ -82174,11 +82250,11 @@ function writeRowsToFile3(filePath, rows, append) {
82174
82250
  writeFileSync5(filePath, `${toCsvRow(fields)}
82175
82251
  `, "utf-8");
82176
82252
  }
82177
- const csvRows = rows.map((row) => toCsvRow(fields.map((f) => String(row[f] ?? ""))));
82253
+ const csvRows = rows.map((row2) => toCsvRow(fields.map((f) => String(row2[f] ?? ""))));
82178
82254
  appendFileSync3(filePath, `${csvRows.join("\n")}
82179
82255
  `, "utf-8");
82180
82256
  } else if (ext === "jsonl") {
82181
- const content = `${rows.map((row) => JSON.stringify(row)).join("\n")}
82257
+ const content = `${rows.map((row2) => JSON.stringify(row2)).join("\n")}
82182
82258
  `;
82183
82259
  if (append && existsSync11(filePath)) {
82184
82260
  appendFileSync3(filePath, content, "utf-8");
@@ -90311,7 +90387,7 @@ async function composeFilmstrip(frames) {
90311
90387
  const width = FRAME_WIDTH * Math.min(GRID_COLUMNS, scaled.length);
90312
90388
  const composites = scaled.flatMap((frame, index) => {
90313
90389
  const column = index % GRID_COLUMNS;
90314
- const row = Math.floor(index / GRID_COLUMNS);
90390
+ const row2 = Math.floor(index / GRID_COLUMNS);
90315
90391
  const label = Buffer.from(
90316
90392
  `<svg width="${FRAME_WIDTH}" height="${LABEL_HEIGHT}">
90317
90393
  <rect width="100%" height="100%" fill="#111827"/>
@@ -90319,8 +90395,8 @@ async function composeFilmstrip(frames) {
90319
90395
  </svg>`
90320
90396
  );
90321
90397
  return [
90322
- { input: label, left: column * FRAME_WIDTH, top: row * cellHeight },
90323
- { input: frame, left: column * FRAME_WIDTH, top: row * cellHeight + LABEL_HEIGHT }
90398
+ { input: label, left: column * FRAME_WIDTH, top: row2 * cellHeight },
90399
+ { input: frame, left: column * FRAME_WIDTH, top: row2 * cellHeight + LABEL_HEIGHT }
90324
90400
  ];
90325
90401
  });
90326
90402
  return await sharp6({
@@ -92477,7 +92553,7 @@ Full guide: __tooling__/docs/tools/baker/media-sources.md`
92477
92553
  });
92478
92554
 
92479
92555
  // src/commands/research/index.ts
92480
- import { defineCommand as defineCommand227 } from "citty";
92556
+ import { defineCommand as defineCommand228 } from "citty";
92481
92557
 
92482
92558
  // src/commands/research/advertisers.ts
92483
92559
  import { defineCommand as defineCommand215 } from "citty";
@@ -92559,7 +92635,7 @@ function keywordMetricsHints({
92559
92635
  }
92560
92636
  const provenance = "DataForSEO resells Google's Keyword Planner volume \u2014 planner_search_volume here is the SAME number `baker ads google keywords metrics` returns, not an independent reading of it. Quoting the two as agreeing sources is not corroboration." + (clickstream ? " clickstream_search_volume IS independent (DataForSEO's own panels); when the two disagree, say which one you are quoting." : " For a genuinely independent estimate of demand, re-run with --clickstream (costs 2x), or use first-party data: `baker gsc query` for what already reaches the site, or the account's own search terms.");
92561
92637
  const hints2 = [provenance];
92562
- const empty = rows.filter((row) => row.search_volume === null).map((row) => row.keyword);
92638
+ const empty = rows.filter((row2) => row2.search_volume === null).map((row2) => row2.keyword);
92563
92639
  if (empty.length > 0) {
92564
92640
  hints2.push(
92565
92641
  `${empty.length} of ${asked.length} keywords came back with no data in ${queryContext.location} / ${queryContext.language}: ${empty.join(", ")}. That is missing coverage, NOT zero demand \u2014 do not report them as unsearched, and do not re-query them reworded. \`baker research autocomplete\` on the head term is the way to find the phrasing that market uses.`
@@ -92567,6 +92643,70 @@ function keywordMetricsHints({
92567
92643
  }
92568
92644
  return hints2;
92569
92645
  }
92646
+ var ARCHIVE_NOTE = "Read from the Reddit archive: the words are as posted, but points and comment counts on anything under ~36h old are as first captured \u2014 don't quote those numbers as current.";
92647
+ function redditSearchHints({
92648
+ resultCount,
92649
+ kind,
92650
+ source
92651
+ }) {
92652
+ const hints2 = [];
92653
+ if (resultCount === 0) {
92654
+ hints2.push(
92655
+ 'Nothing matched. Reddit search matches words, not meaning: try the audience\'s own phrasing (one distinctive word, or a seed from `baker research autocomplete`), a longer --since window, or other subreddits \u2014 `baker research reddit subreddits "<topic>"` finds where the audience actually talks' + (kind === "posts" ? ", and try --kind comments \u2014 most of the voice is in the replies." : ".")
92656
+ );
92657
+ } else if (kind === "posts") {
92658
+ hints2.push(
92659
+ "Titles rarely carry the voice. Read the promising threads with `baker research reddit thread <permalink>` for the replies, or search the replies directly with --kind comments."
92660
+ );
92661
+ }
92662
+ if (source === "archive" && resultCount > 0) hints2.push(ARCHIVE_NOTE);
92663
+ return hints2;
92664
+ }
92665
+ function redditThreadHints({
92666
+ commentsShown,
92667
+ commentsAsked,
92668
+ commentCount,
92669
+ source
92670
+ }) {
92671
+ const hints2 = [];
92672
+ if (commentsAsked > 0 && commentsShown === 0 && (commentCount ?? 0) > 0) {
92673
+ hints2.push(
92674
+ `This thread has ${commentCount} comments but none could be read just now. Quote the post only, and don't describe the replies.`
92675
+ );
92676
+ }
92677
+ if (source === "archive") hints2.push(ARCHIVE_NOTE);
92678
+ return hints2;
92679
+ }
92680
+ var REDDIT_HOST = /(^|\.)reddit\.com$|^redd\.it$/i;
92681
+ function redditSubredditsHints({ resultCount }) {
92682
+ if (resultCount === 0) {
92683
+ return [
92684
+ 'No subreddits matched. Try the audience\'s role or the platform they work on ("amazon flex", "deliveroo", "nurses"), one word at a time, or a web search for `site:reddit.com <topic>`.'
92685
+ ];
92686
+ }
92687
+ return [
92688
+ 'Members say how big a subreddit is, not how active it is. Before mining one, run `baker research reddit search "<word>" --subreddit <name> --since <a recent date>`: nothing back means it is quiet, so pick another. `restricted` can still be read; `private` cannot.'
92689
+ ];
92690
+ }
92691
+ var REDDIT_LINK_REJECTED = {
92692
+ action: "reject",
92693
+ explanation: "Do not quote anything attributed to this link \u2014 it does not lead to those words. Find the real thread with `baker research reddit search`, and if you already wrote a quote from it, remove it."
92694
+ };
92695
+ function redditPageFix(url, code) {
92696
+ let host;
92697
+ try {
92698
+ host = new URL(url).hostname;
92699
+ } catch {
92700
+ return null;
92701
+ }
92702
+ if (!REDDIT_HOST.test(host)) return null;
92703
+ if (code === "NOT_FOUND") return REDDIT_LINK_REJECTED;
92704
+ if (code !== "FORBIDDEN") return null;
92705
+ return {
92706
+ action: "retry_with_changes",
92707
+ explanation: 'Read Reddit with its own commands: `baker research reddit search "<words>" --subreddit <name>` to find what people said, `baker research reddit thread <url>` for one thread.'
92708
+ };
92709
+ }
92570
92710
 
92571
92711
  // src/commands/research/output.ts
92572
92712
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -92582,9 +92722,9 @@ function toCsvRow2(values) {
92582
92722
  return v;
92583
92723
  }).join(",");
92584
92724
  }
92585
- function flattenRow(row) {
92725
+ function flattenRow(row2) {
92586
92726
  const flat = {};
92587
- for (const [key, val] of Object.entries(row)) {
92727
+ for (const [key, val] of Object.entries(row2)) {
92588
92728
  flat[key] = typeof val === "object" && val !== null ? JSON.stringify(val) : String(val ?? "");
92589
92729
  }
92590
92730
  return flat;
@@ -92594,15 +92734,15 @@ function writeCsv(data, fields) {
92594
92734
  const cols = fields ?? Object.keys(data[0] ?? {});
92595
92735
  process.stdout.write(`${toCsvRow2(cols)}
92596
92736
  `);
92597
- for (const row of data) {
92598
- const flat = flattenRow(row);
92737
+ for (const row2 of data) {
92738
+ const flat = flattenRow(row2);
92599
92739
  process.stdout.write(`${toCsvRow2(cols.map((f) => flat[f] ?? ""))}
92600
92740
  `);
92601
92741
  }
92602
92742
  }
92603
92743
  function writeJsonl(data) {
92604
- for (const row of data) {
92605
- process.stdout.write(`${JSON.stringify(row)}
92744
+ for (const row2 of data) {
92745
+ process.stdout.write(`${JSON.stringify(row2)}
92606
92746
  `);
92607
92747
  }
92608
92748
  }
@@ -92613,8 +92753,8 @@ function writeMd(data, fields) {
92613
92753
  `);
92614
92754
  process.stdout.write(`| ${cols.map(() => "---").join(" | ")} |
92615
92755
  `);
92616
- for (const row of data) {
92617
- const flat = flattenRow(row);
92756
+ for (const row2 of data) {
92757
+ const flat = flattenRow(row2);
92618
92758
  process.stdout.write(`| ${cols.map((f) => flat[f] ?? "").join(" | ")} |
92619
92759
  `);
92620
92760
  }
@@ -93028,7 +93168,7 @@ Examples:
93028
93168
  })
93029
93169
  });
93030
93170
  } catch (err) {
93031
- handleResearchError(err, fetchFix);
93171
+ handleResearchError(err, (code) => redditPageFix(url, code) ?? fetchFix(code));
93032
93172
  }
93033
93173
  }
93034
93174
  });
@@ -93213,24 +93353,24 @@ import { defineCommand as defineCommand221 } from "citty";
93213
93353
 
93214
93354
  // src/commands/research/keyword-metrics-rows.ts
93215
93355
  var KEYWORD_METRICS_SOURCE = "keyword_planner_estimate_via_dataforseo";
93216
- function toOutputRow(row, options) {
93356
+ function toOutputRow(row2, options) {
93217
93357
  const out = {
93218
93358
  source: KEYWORD_METRICS_SOURCE,
93219
- keyword: row.keyword,
93220
- planner_search_volume: row.search_volume,
93221
- planner_cpc: row.cpc,
93222
- planner_low_top_of_page_bid: row.low_top_of_page_bid,
93223
- planner_high_top_of_page_bid: row.high_top_of_page_bid,
93224
- planner_competition: row.competition,
93225
- planner_competition_index: row.competition_index
93359
+ keyword: row2.keyword,
93360
+ planner_search_volume: row2.search_volume,
93361
+ planner_cpc: row2.cpc,
93362
+ planner_low_top_of_page_bid: row2.low_top_of_page_bid,
93363
+ planner_high_top_of_page_bid: row2.high_top_of_page_bid,
93364
+ planner_competition: row2.competition,
93365
+ planner_competition_index: row2.competition_index
93226
93366
  };
93227
93367
  if (options.clickstream) {
93228
- out.clickstream_search_volume = row.clickstream_search_volume;
93368
+ out.clickstream_search_volume = row2.clickstream_search_volume;
93229
93369
  }
93230
93370
  if (options.full) {
93231
- out.keyword_difficulty = row.keyword_difficulty;
93232
- out.main_intent = row.main_intent;
93233
- out.planner_monthly_searches = row.monthly_searches;
93371
+ out.keyword_difficulty = row2.keyword_difficulty;
93372
+ out.main_intent = row2.main_intent;
93373
+ out.planner_monthly_searches = row2.monthly_searches;
93234
93374
  }
93235
93375
  return out;
93236
93376
  }
@@ -93347,7 +93487,7 @@ Examples:
93347
93487
  skipCache: args8["no-cache"] ? true : void 0
93348
93488
  });
93349
93489
  const columns = { clickstream, full };
93350
- const rows = result.data.map((row) => toOutputRow(row, columns));
93490
+ const rows = result.data.map((row2) => toOutputRow(row2, columns));
93351
93491
  const hints2 = keywordMetricsHints({ rows: result.data, asked: keywords, clickstream, queryContext });
93352
93492
  warnDefaults(queryContext);
93353
93493
  const format = args8.output || "json";
@@ -93563,8 +93703,273 @@ Examples:
93563
93703
  }
93564
93704
  });
93565
93705
 
93566
- // src/commands/research/relevant-pages.ts
93706
+ // src/commands/research/reddit.ts
93567
93707
  import { defineCommand as defineCommand225 } from "citty";
93708
+ var TIMEOUT_MS2 = 18e4;
93709
+ var TEXT_PREVIEW_CHARS = 1500;
93710
+ var ITEM_FIELDS = {
93711
+ kind: "post or comment",
93712
+ subreddit: "Where it was said",
93713
+ title: "The post's title (on a comment: the post it answers)",
93714
+ text: "Verbatim \u2014 quote this, exactly",
93715
+ text_truncated: "The text was cut; re-run with --full before quoting the end of it",
93716
+ author: "Reddit username, or null once the account is deleted",
93717
+ score: "Points",
93718
+ comment_count: "Posts only",
93719
+ created: "Date written (UTC)",
93720
+ permalink: "Cite THIS link \u2014 it is built from where the words actually are",
93721
+ depth: "Thread comments: 0 answers the post, 1 answers a comment",
93722
+ source: "reddit_api / reddit_web = live Reddit; archive = the Arctic Shift archive of Reddit"
93723
+ };
93724
+ registerSchema({
93725
+ command: "research.reddit.search",
93726
+ description: "Search Reddit posts or comments for the audience's own words. Returns verbatim text with a real permalink for each hit. Start here for voice-of-customer work on Reddit.",
93727
+ args: {
93728
+ query: {
93729
+ type: "string",
93730
+ description: "Words to match \u2014 the audience's phrasing, not a description of it",
93731
+ required: true
93732
+ },
93733
+ subreddit: {
93734
+ type: "string",
93735
+ description: "Subreddits to search (up to 10): comma-separated (couriersofreddit,AmazonFlexUK) or the flag repeated. Needed for --kind comments",
93736
+ required: false
93737
+ },
93738
+ kind: {
93739
+ type: "string",
93740
+ description: "posts (default) or comments \u2014 the replies are where most of the voice is",
93741
+ required: false,
93742
+ enum: ["posts", "comments"]
93743
+ },
93744
+ since: { type: "string", description: "Only things written on or after this day, YYYY-MM-DD", required: false },
93745
+ limit: { type: "number", description: "Results to return (1-50, default 25)", required: false },
93746
+ full: {
93747
+ type: "boolean",
93748
+ description: "Return every text whole instead of a preview",
93749
+ required: false,
93750
+ default: false
93751
+ }
93752
+ }
93753
+ });
93754
+ registerSchema({
93755
+ command: "research.reddit.thread",
93756
+ description: "Read one Reddit thread verbatim: the post and its best comments, each with its own permalink. Use it on any Reddit link before quoting it \u2014 it also exposes links that point somewhere other than they claim.",
93757
+ args: {
93758
+ target: {
93759
+ type: "string",
93760
+ description: "The thread's link (any Reddit form, including share links) or post id",
93761
+ required: true
93762
+ },
93763
+ comments: { type: "number", description: "Comments to return, best first (0-200, default 40)", required: false },
93764
+ full: {
93765
+ type: "boolean",
93766
+ description: "Return every text whole instead of a preview",
93767
+ required: false,
93768
+ default: false
93769
+ }
93770
+ }
93771
+ });
93772
+ registerSchema({
93773
+ command: "research.reddit.subreddits",
93774
+ description: "Find the subreddits where an audience talks about a topic, with member counts and whether they can be read. Run it before searching, instead of guessing subreddit names.",
93775
+ args: {
93776
+ query: { type: "string", description: 'The audience or topic, e.g. "amazon flex uk", "deliveroo"', required: true },
93777
+ limit: { type: "number", description: "Subreddits to return (1-25, default 10)", required: false }
93778
+ }
93779
+ });
93780
+ function row(item, full) {
93781
+ const cut = !full && item.text.length > TEXT_PREVIEW_CHARS;
93782
+ return {
93783
+ kind: item.kind,
93784
+ subreddit: item.subreddit,
93785
+ title: item.title,
93786
+ text: cut ? `${item.text.slice(0, TEXT_PREVIEW_CHARS)}\u2026` : item.text,
93787
+ ...cut ? { text_truncated: true } : {},
93788
+ author: item.author,
93789
+ score: item.score,
93790
+ ...item.kind === "post" ? { comment_count: item.commentCount } : {},
93791
+ created: item.createdAt.slice(0, 10),
93792
+ permalink: item.permalink,
93793
+ ...item.depth !== null ? { depth: item.depth } : {}
93794
+ };
93795
+ }
93796
+ function redditFix(code) {
93797
+ if (code === "NOT_FOUND") return REDDIT_LINK_REJECTED;
93798
+ if (code === "BAD_REQUEST" || code === "VALIDATION_ERROR") {
93799
+ return { action: "retry_with_changes", explanation: "Follow the message above and run the command again." };
93800
+ }
93801
+ return {
93802
+ action: "continue_without",
93803
+ explanation: "Don't loop on it. Finish the job with the other sources, say plainly that Reddit is a gap in this research, and never stand in a summary's paraphrase for a Reddit quote."
93804
+ };
93805
+ }
93806
+ function failValidation4(message) {
93807
+ writeResearchJson({ ok: false, error: { code: "VALIDATION_ERROR", message, fix: redditFix("VALIDATION_ERROR") } });
93808
+ process.exit(1);
93809
+ }
93810
+ function subredditsFromArgs(rawArgs, parsed) {
93811
+ return repeatedValues(rawArgs, "subreddit", parsed).flatMap((value2) => value2.split(",")).map((name) => name.trim()).filter(Boolean);
93812
+ }
93813
+ var searchCommand3 = defineCommand225({
93814
+ meta: {
93815
+ name: "search",
93816
+ description: `Search Reddit for the audience's own words, verbatim, each with a permalink you can cite.
93817
+
93818
+ Start here for Reddit voice-of-customer. Matches words, not meaning \u2014 search the way the audience writes, and quote a phrase to keep it together ('"right to work"').
93819
+
93820
+ Examples:
93821
+ baker research reddit search "agency never paid" --subreddit couriersofreddit,AmazonFlexUK
93822
+ baker research reddit search '"right to work"' --subreddit UKJobs,AmazonFlexUK
93823
+ baker research reddit search "visa" --subreddit UberEATS --kind comments --since 2025-01-01
93824
+ baker research reddit search "side hustle student deliveries"`
93825
+ },
93826
+ args: {
93827
+ query: { type: "positional", description: "Words to match", required: false },
93828
+ subreddit: {
93829
+ type: "string",
93830
+ description: "Subreddits (up to 10): comma-separated, or repeat the flag",
93831
+ required: false
93832
+ },
93833
+ kind: { type: "string", description: "posts (default) or comments", required: false },
93834
+ since: { type: "string", description: "YYYY-MM-DD", required: false },
93835
+ limit: { type: "string", description: "1-50, default 25", required: false },
93836
+ full: { type: "boolean", description: "Whole texts, not previews", required: false }
93837
+ },
93838
+ run: async ({ args: args8, rawArgs }) => {
93839
+ const query = args8.query?.trim();
93840
+ if (!query) failValidation4('Say what to search for: baker research reddit search "<words>"');
93841
+ const kind = args8.kind ?? "posts";
93842
+ if (kind !== "posts" && kind !== "comments") failValidation4("--kind must be posts or comments");
93843
+ const body = {
93844
+ query,
93845
+ kind,
93846
+ subreddits: subredditsFromArgs(rawArgs, args8.subreddit)
93847
+ };
93848
+ if (args8.since) body.since = args8.since;
93849
+ if (args8.limit) body.limit = Number(args8.limit);
93850
+ try {
93851
+ const data = await apiPost("/api/research/reddit/search", body, {
93852
+ timeoutMs: TIMEOUT_MS2
93853
+ });
93854
+ writeResearchJson({
93855
+ ok: true,
93856
+ data: data.results.map((item) => row(item, Boolean(args8.full))),
93857
+ source: data.source,
93858
+ fields: ITEM_FIELDS,
93859
+ hints: redditSearchHints({ resultCount: data.results.length, kind, source: data.source })
93860
+ });
93861
+ } catch (err) {
93862
+ handleResearchError(err, redditFix);
93863
+ }
93864
+ }
93865
+ });
93866
+ var threadCommand = defineCommand225({
93867
+ meta: {
93868
+ name: "thread",
93869
+ description: `Read one Reddit thread verbatim \u2014 the post and its best comments, each with its own permalink.
93870
+
93871
+ Run it on every Reddit link before you quote from it, including links a research summary gave you: it refuses a link whose post is somewhere other than the link claims.
93872
+
93873
+ Examples:
93874
+ baker research reddit thread "https://www.reddit.com/r/AmazonFlexUK/comments/1abc2de/agency_still_hasnt_paid_me/"
93875
+ baker research reddit thread "https://www.reddit.com/r/UberEATS/s/3TzXiyxaMD" --comments 100
93876
+ baker research reddit thread 1abc2de --full`
93877
+ },
93878
+ args: {
93879
+ target: { type: "positional", description: "Thread link or post id", required: false },
93880
+ comments: { type: "string", description: "0-200, default 40", required: false },
93881
+ full: { type: "boolean", description: "Whole texts, not previews", required: false }
93882
+ },
93883
+ run: async ({ args: args8 }) => {
93884
+ const target2 = args8.target?.trim();
93885
+ if (!target2) failValidation4("Pass the thread's link or post id: baker research reddit thread <url>");
93886
+ const commentsAsked = args8.comments ? Number(args8.comments) : RESEARCH_REDDIT_THREAD_DEFAULT_COMMENTS;
93887
+ const body = { target: target2, comments: commentsAsked };
93888
+ try {
93889
+ const data = await apiPost("/api/research/reddit/thread", body, {
93890
+ timeoutMs: TIMEOUT_MS2
93891
+ });
93892
+ const full = Boolean(args8.full);
93893
+ writeResearchJson({
93894
+ ok: true,
93895
+ data: { post: row(data.post, full), comments: data.comments.map((item) => row(item, full)) },
93896
+ source: data.source,
93897
+ fields: ITEM_FIELDS,
93898
+ hints: redditThreadHints({
93899
+ commentsShown: data.comments.length,
93900
+ commentsAsked,
93901
+ commentCount: data.post.commentCount,
93902
+ source: data.source
93903
+ })
93904
+ });
93905
+ } catch (err) {
93906
+ handleResearchError(err, redditFix);
93907
+ }
93908
+ }
93909
+ });
93910
+ var subredditsCommand = defineCommand225({
93911
+ meta: {
93912
+ name: "subreddits",
93913
+ description: `Find the subreddits where an audience talks \u2014 name, members, and whether it can be read.
93914
+
93915
+ Run it before \`reddit search\` instead of guessing names: a plausible-sounding subreddit can be dead (r/CouriersUK has one post), while the conversation sits in r/AmazonFlexUK or r/deliveroos.
93916
+
93917
+ Examples:
93918
+ baker research reddit subreddits "amazon flex uk"
93919
+ baker research reddit subreddits "deliveroo" --limit 5`
93920
+ },
93921
+ args: {
93922
+ query: { type: "positional", description: "Audience or topic", required: false },
93923
+ limit: { type: "string", description: "1-25, default 10", required: false }
93924
+ },
93925
+ run: async ({ args: args8 }) => {
93926
+ const query = args8.query?.trim();
93927
+ if (!query) failValidation4('Say what to look for: baker research reddit subreddits "<audience or topic>"');
93928
+ const body = { query };
93929
+ if (args8.limit) body.limit = Number(args8.limit);
93930
+ try {
93931
+ const data = await apiPost("/api/research/reddit/subreddits", body, {
93932
+ timeoutMs: TIMEOUT_MS2
93933
+ });
93934
+ writeResearchJson({
93935
+ ok: true,
93936
+ data: data.results,
93937
+ source: data.source,
93938
+ fields: {
93939
+ name: "Pass this to --subreddit",
93940
+ subscribers: "Members \u2014 size, not activity",
93941
+ access: "public / restricted (readable, members post) / private (unreadable) / null when the source doesn't say",
93942
+ description: "The subreddit's own summary",
93943
+ url: "The subreddit"
93944
+ },
93945
+ hints: redditSubredditsHints({ resultCount: data.results.length })
93946
+ });
93947
+ } catch (err) {
93948
+ handleResearchError(err, redditFix);
93949
+ }
93950
+ }
93951
+ });
93952
+ var redditCommand = defineCommand225({
93953
+ meta: {
93954
+ name: "reddit",
93955
+ description: `Read Reddit verbatim \u2014 the one site no page reader will open for us.
93956
+
93957
+ Commands:
93958
+ subreddits \u2014 Find where the audience talks (start here)
93959
+ search \u2014 Find posts or comments in the audience's own words
93960
+ thread \u2014 Read one thread: the post and its best comments
93961
+
93962
+ Examples:
93963
+ baker research reddit subreddits "amazon flex uk"
93964
+ baker research reddit search "agency never paid" --subreddit couriersofreddit,AmazonFlexUK --kind comments
93965
+ baker research reddit thread "https://www.reddit.com/r/AmazonFlexUK/comments/1abc2de/"
93966
+ Full guide: __tooling__/docs/tools/baker/research.md`
93967
+ },
93968
+ subCommands: { subreddits: subredditsCommand, search: searchCommand3, thread: threadCommand }
93969
+ });
93970
+
93971
+ // src/commands/research/relevant-pages.ts
93972
+ import { defineCommand as defineCommand226 } from "citty";
93568
93973
  registerSchema({
93569
93974
  command: "research.relevant-pages",
93570
93975
  description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -93590,7 +93995,7 @@ var FIELDS12 = {
93590
93995
  keywords: "Total organic keywords the page ranks for",
93591
93996
  top_10: "Keywords in positions 1-10"
93592
93997
  };
93593
- var relevantPagesCommand = defineCommand225({
93998
+ var relevantPagesCommand = defineCommand226({
93594
93999
  meta: {
93595
94000
  name: "relevant-pages",
93596
94001
  description: `Get the top pages of a competitor domain with traffic data.
@@ -93637,7 +94042,7 @@ Examples:
93637
94042
  });
93638
94043
 
93639
94044
  // src/commands/research/web.ts
93640
- import { defineCommand as defineCommand226 } from "citty";
94045
+ import { defineCommand as defineCommand227 } from "citty";
93641
94046
  registerSchema({
93642
94047
  command: "research.web",
93643
94048
  description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
@@ -93655,7 +94060,7 @@ registerSchema({
93655
94060
  }
93656
94061
  }
93657
94062
  });
93658
- var TIMEOUT_MS2 = {
94063
+ var TIMEOUT_MS3 = {
93659
94064
  medium: 18e4,
93660
94065
  high: 36e4,
93661
94066
  xhigh: 9e5
@@ -93671,7 +94076,7 @@ async function runDeepResearch(question) {
93671
94076
  const { interactionId } = await apiPost("/api/research/deep-research/start", { question });
93672
94077
  process.stderr.write(`Deep research started (${interactionId}). Polling...
93673
94078
  `);
93674
- const deadline = Date.now() + TIMEOUT_MS2.xhigh;
94079
+ const deadline = Date.now() + TIMEOUT_MS3.xhigh;
93675
94080
  while (Date.now() < deadline) {
93676
94081
  await sleep3(1e4);
93677
94082
  const poll = await apiGet("/api/research/deep-research/poll", {
@@ -93688,7 +94093,7 @@ async function runDeepResearch(question) {
93688
94093
  }
93689
94094
  throw new Error("Deep research timed out");
93690
94095
  }
93691
- var webCommand = defineCommand226({
94096
+ var webCommand = defineCommand227({
93692
94097
  meta: {
93693
94098
  name: "web",
93694
94099
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -93726,7 +94131,7 @@ Examples:
93726
94131
  process.exit(1);
93727
94132
  }
93728
94133
  try {
93729
- const data = depth === "xhigh" ? await runDeepResearch(question) : await apiPost("/api/research/explore", { question, depth }, { timeoutMs: TIMEOUT_MS2[depth] });
94134
+ const data = depth === "xhigh" ? await runDeepResearch(question) : await apiPost("/api/research/explore", { question, depth }, { timeoutMs: TIMEOUT_MS3[depth] });
93730
94135
  const format = args8.output || "json";
93731
94136
  const hints2 = webDepthHints({ answer: data.answer, sourceCount: data.sources.length, depth });
93732
94137
  if (format === "md") {
@@ -93750,7 +94155,7 @@ Examples:
93750
94155
  });
93751
94156
 
93752
94157
  // src/commands/research/index.ts
93753
- var researchCommand = defineCommand227({
94158
+ var researchCommand = defineCommand228({
93754
94159
  meta: {
93755
94160
  name: "research",
93756
94161
  description: `Competitive intelligence and AI-powered research commands.
@@ -93758,6 +94163,7 @@ var researchCommand = defineCommand227({
93758
94163
  Commands:
93759
94164
  web \u2014 Search the web with AI (ask any question)
93760
94165
  fetch \u2014 Read a page an ordinary web fetch could not
94166
+ reddit \u2014 Read Reddit verbatim: search posts/comments, read a thread
93761
94167
  advertisers \u2014 Who's bidding on a keyword?
93762
94168
  autocomplete \u2014 Google Autocomplete suggestions
93763
94169
  intent \u2014 Google Search intent classification
@@ -93773,6 +94179,7 @@ Examples:
93773
94179
  baker research web "Who are the main competitors of HubSpot?"
93774
94180
  baker research web "What is the ICP for CRM buyers?" --depth high
93775
94181
  baker research fetch "https://competitor.com/pricing"
94182
+ baker research reddit search "agency never paid" --subreddit AmazonFlexUK --kind comments
93776
94183
  baker research advertisers "running shoes"
93777
94184
  baker research intent "buy running shoes,best running shoes 2026"
93778
94185
  baker research keyword-metrics "chatbot whatsapp,crm whatsapp" --location mx --language es
@@ -93783,6 +94190,7 @@ Full guide: __tooling__/docs/tools/baker/research.md`
93783
94190
  subCommands: {
93784
94191
  web: webCommand,
93785
94192
  fetch: fetchCommand,
94193
+ reddit: redditCommand,
93786
94194
  advertisers: advertisersCommand,
93787
94195
  autocomplete: autocompleteCommand,
93788
94196
  countries: countriesCommand,
@@ -93797,10 +94205,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
93797
94205
  });
93798
94206
 
93799
94207
  // src/commands/scheduled-agents/index.ts
93800
- import { defineCommand as defineCommand238 } from "citty";
94208
+ import { defineCommand as defineCommand239 } from "citty";
93801
94209
 
93802
94210
  // src/commands/scheduled-agents/create.ts
93803
- import { defineCommand as defineCommand228 } from "citty";
94211
+ import { defineCommand as defineCommand229 } from "citty";
93804
94212
 
93805
94213
  // src/commands/scheduled-agents/shared.ts
93806
94214
  var TEMP_SCHEDULED_AGENT_PREFIX = "temp_agent_";
@@ -93808,7 +94216,7 @@ var TEMP_ID_PREFIX = "temp_";
93808
94216
  function writeOk2(data, hints2) {
93809
94217
  writeJson({ ok: true, data: data ?? null, ...hints2 && hints2.length > 0 ? { hints: hints2 } : {} });
93810
94218
  }
93811
- function failValidation4(message) {
94219
+ function failValidation5(message) {
93812
94220
  writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message } });
93813
94221
  process.exit(1);
93814
94222
  }
@@ -93856,7 +94264,7 @@ function isOtherTempId(id) {
93856
94264
  }
93857
94265
  function validateScheduledAgentRef(ref2) {
93858
94266
  if (isOtherTempId(ref2)) {
93859
- failValidation4(`Scheduled agent temp IDs must start with ${TEMP_SCHEDULED_AGENT_PREFIX}.`);
94267
+ failValidation5(`Scheduled agent temp IDs must start with ${TEMP_SCHEDULED_AGENT_PREFIX}.`);
93860
94268
  }
93861
94269
  if (!isTempScheduledAgentId(ref2)) {
93862
94270
  validateConvexId(ref2);
@@ -93903,14 +94311,14 @@ var NARROWING_ARGS = {
93903
94311
  };
93904
94312
  function validateRunAt(runAt) {
93905
94313
  if (!runAt.endsWith("Z")) {
93906
- failValidation4("--run-at must be an ISO UTC timestamp ending in Z.");
94314
+ failValidation5("--run-at must be an ISO UTC timestamp ending in Z.");
93907
94315
  }
93908
94316
  const parsed = Date.parse(runAt);
93909
94317
  if (!Number.isFinite(parsed)) {
93910
- failValidation4("--run-at must be a valid ISO timestamp.");
94318
+ failValidation5("--run-at must be a valid ISO timestamp.");
93911
94319
  }
93912
94320
  if (parsed <= Date.now()) {
93913
- failValidation4(
94321
+ failValidation5(
93914
94322
  `--run-at ${runAt} has already passed. A one-off item only runs at a time still to come \u2014 pass a later ISO UTC timestamp, and check which timezone you converted from if you worked it out from a local time.`
93915
94323
  );
93916
94324
  }
@@ -93922,7 +94330,7 @@ function buildTriggerBody(args8, options) {
93922
94330
  const kind = args8.trigger;
93923
94331
  const given = [cron, runAt, kind].filter(Boolean).length;
93924
94332
  if (given > 1) {
93925
- failValidation4("--cron, --run-at and --trigger are mutually exclusive.");
94333
+ failValidation5("--cron, --run-at and --trigger are mutually exclusive.");
93926
94334
  }
93927
94335
  refuseNarrowingWithoutKind(args8, kind, given);
93928
94336
  if (runAt) {
@@ -93939,7 +94347,7 @@ function buildTriggerBody(args8, options) {
93939
94347
  return { timezone };
93940
94348
  }
93941
94349
  if (options.required) {
93942
- failValidation4(
94350
+ failValidation5(
93943
94351
  `A trigger is required. Pass --cron or --run-at for a time-based one, or --trigger with one of: ${TRIGGER_KIND_IDS.join(", ")}.`
93944
94352
  );
93945
94353
  }
@@ -93951,13 +94359,13 @@ function flagText(args8, key) {
93951
94359
  }
93952
94360
  function refuseMisusedKinds(kind, args8) {
93953
94361
  if (kind === "time.once") {
93954
- failValidation4("Use --run-at for a one-off, rather than --trigger time.once.");
94362
+ failValidation5("Use --run-at for a one-off, rather than --trigger time.once.");
93955
94363
  }
93956
94364
  if (kind === "time.cron") {
93957
- failValidation4("Use --cron for a repeating schedule, rather than --trigger time.cron.");
94365
+ failValidation5("Use --cron for a repeating schedule, rather than --trigger time.cron.");
93958
94366
  }
93959
94367
  if (kind === "experiment.started" && flagText(args8, "experiment")) {
93960
- failValidation4(
94368
+ failValidation5(
93961
94369
  'A test that exists has already started, so --experiment never fires on experiment.started. Leave it off and add --when "page = <page>" to watch one page.'
93962
94370
  );
93963
94371
  }
@@ -93970,7 +94378,7 @@ function refuseStrayFlags(kind, args8) {
93970
94378
  if (stray.length === 0) {
93971
94379
  return;
93972
94380
  }
93973
- failValidation4(
94381
+ failValidation5(
93974
94382
  `${stray.map((flag) => `--${flag}`).join(", ")} does not narrow ${kind}. ${allowed.size > 0 ? `It can be pointed with ${[...allowed].map((flag) => `--${flag}`).join(", ")}.` : "It runs every time it happens \u2014 narrow it with --when rules over its payload instead."}`
93975
94383
  );
93976
94384
  }
@@ -93984,7 +94392,7 @@ function refuseNarrowingWithoutKind(args8, kind, given) {
93984
94392
  return;
93985
94393
  }
93986
94394
  const flags = stray.map((flag) => `--${flag}`).join(", ");
93987
- failValidation4(
94395
+ failValidation5(
93988
94396
  given > 0 ? `${flags} does not narrow --cron or --run-at: a time trigger runs every time it comes round. Use --trigger <kind> with ${flags} instead, or leave ${flags} off.` : `${flags} narrows a trigger, and no trigger was given. Pass --trigger <kind> too \u2014 the item's whole trigger, with ${flags} \u2014 or leave ${flags} off.`
93989
94397
  );
93990
94398
  }
@@ -93994,7 +94402,7 @@ function configFromFlag(kind, args8) {
93994
94402
  return {};
93995
94403
  }
93996
94404
  if (kind !== "app.event") {
93997
- failValidation4("--config is only for --trigger app.event: the settings of what happens in the tool.");
94405
+ failValidation5("--config is only for --trigger app.event: the settings of what happens in the tool.");
93998
94406
  }
93999
94407
  let parsed;
94000
94408
  try {
@@ -94003,13 +94411,13 @@ function configFromFlag(kind, args8) {
94003
94411
  parsed = null;
94004
94412
  }
94005
94413
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
94006
- failValidation4(`--config must be a JSON object, e.g. '{"labelIds":"INBOX"}'.`);
94414
+ failValidation5(`--config must be a JSON object, e.g. '{"labelIds":"INBOX"}'.`);
94007
94415
  }
94008
94416
  return { configJson: config };
94009
94417
  }
94010
94418
  function triggerFromFlags(kind, args8) {
94011
94419
  if (!isTriggerKind(kind)) {
94012
- failValidation4(`--trigger must be one of: ${TRIGGER_KIND_IDS.join(", ")}.`);
94420
+ failValidation5(`--trigger must be one of: ${TRIGGER_KIND_IDS.join(", ")}.`);
94013
94421
  }
94014
94422
  refuseMisusedKinds(kind, args8);
94015
94423
  refuseStrayFlags(kind, args8);
@@ -94019,7 +94427,7 @@ function triggerFromFlags(kind, args8) {
94019
94427
  if (value2) {
94020
94428
  narrowings[field] = value2;
94021
94429
  } else if (required) {
94022
- failValidation4(
94430
+ failValidation5(
94023
94431
  `--trigger ${kind} needs --${TRIGGER_TARGET_FIELDS[field].flag}. \`baker scheduled-agents targets --trigger ${kind} --field ${field}\` lists what it can be.`
94024
94432
  );
94025
94433
  }
@@ -94031,12 +94439,12 @@ function parseOccurrences(raw) {
94031
94439
  return void 0;
94032
94440
  }
94033
94441
  if (typeof raw !== "string") {
94034
- failValidation4("--occurrences must be a comma-separated list of whole numbers, e.g. 1 or 1,3.");
94442
+ failValidation5("--occurrences must be a comma-separated list of whole numbers, e.g. 1 or 1,3.");
94035
94443
  }
94036
94444
  const parts = raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
94037
94445
  const numbers = parts.map((part) => Number(part));
94038
94446
  if (numbers.length === 0 || numbers.some((n) => !Number.isInteger(n) || n <= 0)) {
94039
- failValidation4("--occurrences must be a comma-separated list of whole numbers above zero, e.g. 1 or 1,3.");
94447
+ failValidation5("--occurrences must be a comma-separated list of whole numbers above zero, e.g. 1 or 1,3.");
94040
94448
  }
94041
94449
  return numbers;
94042
94450
  }
@@ -94050,7 +94458,7 @@ function parseBooleanFlag(raw, flagName) {
94050
94458
  if (raw === false || raw === "false") {
94051
94459
  return false;
94052
94460
  }
94053
- failValidation4(`${flagName} must be true or false.`);
94461
+ failValidation5(`${flagName} must be true or false.`);
94054
94462
  }
94055
94463
  var REACTION_ARGS = {
94056
94464
  when: {
@@ -94080,7 +94488,7 @@ function wholeNumber(raw, flag, { min }) {
94080
94488
  }
94081
94489
  const value2 = Number(raw);
94082
94490
  if (!Number.isInteger(value2) || value2 < min) {
94083
- failValidation4(`${flag} must be a whole number of at least ${min}.`);
94491
+ failValidation5(`${flag} must be a whole number of at least ${min}.`);
94084
94492
  }
94085
94493
  return value2;
94086
94494
  }
@@ -94100,7 +94508,7 @@ function occurrencesFromFlags(args8) {
94100
94508
  const times = wholeNumber(args8.times, "--times", { min: 1 });
94101
94509
  const occurrences = parseOccurrences(args8.occurrences);
94102
94510
  if (times !== void 0 && occurrences !== void 0) {
94103
- failValidation4("Pass --times or --occurrences, not both \u2014 --times 3 is --occurrences 1,2,3.");
94511
+ failValidation5("Pass --times or --occurrences, not both \u2014 --times 3 is --occurrences 1,2,3.");
94104
94512
  }
94105
94513
  return times !== void 0 ? firstTimes(times) : occurrences;
94106
94514
  }
@@ -94114,7 +94522,7 @@ function whenFromFlag(raw) {
94114
94522
  }
94115
94523
  const parsed = parseConditionsExpression(text2);
94116
94524
  if (!parsed.ok) {
94117
- failValidation4(parsed.error);
94525
+ failValidation5(parsed.error);
94118
94526
  }
94119
94527
  return text2;
94120
94528
  }
@@ -94123,7 +94531,7 @@ function payloadFromFlag(raw) {
94123
94531
  return void 0;
94124
94532
  }
94125
94533
  if (raw !== "include" && raw !== "omit") {
94126
- failValidation4("--payload must be include or omit.");
94534
+ failValidation5("--payload must be include or omit.");
94127
94535
  }
94128
94536
  return raw === "include";
94129
94537
  }
@@ -94167,7 +94575,7 @@ registerSchema({
94167
94575
  }
94168
94576
  }
94169
94577
  });
94170
- var createCommand3 = defineCommand228({
94578
+ var createCommand3 = defineCommand229({
94171
94579
  meta: {
94172
94580
  name: "create",
94173
94581
  description: 'Stage a scheduled agent. Examples: baker scheduled-agents create --name "Weekly report" --description "..." --prompt "..." --cron "0 9 * * MON" | baker scheduled-agents create --name "Chase a new lead" --description "..." --prompt "Research {{payload.answers.company}} and draft a reply" --trigger form.submitted --form quote | baker scheduled-agents create --name "Big deals" --description "..." --prompt "..." --trigger webhook.received --when "deal.stage = won AND deal.amount > 10000" --times 3'
@@ -94195,13 +94603,13 @@ var createCommand3 = defineCommand228({
94195
94603
  const prompt = args8.prompt;
94196
94604
  const template = typeof args8.template === "string" ? args8.template.trim() : "";
94197
94605
  if (!name || name.trim().length === 0) {
94198
- failValidation4("--name is required.");
94606
+ failValidation5("--name is required.");
94199
94607
  }
94200
94608
  if (!description || description.trim().length === 0) {
94201
- failValidation4("--description is required.");
94609
+ failValidation5("--description is required.");
94202
94610
  }
94203
94611
  if (template.length === 0 && (!prompt || prompt.trim().length === 0)) {
94204
- failValidation4("--prompt is required: it is the whole of what a run does. Or pass --template to copy a brief.");
94612
+ failValidation5("--prompt is required: it is the whole of what a run does. Or pass --template to copy a brief.");
94205
94613
  }
94206
94614
  const trigger = buildTriggerBody(args8, { required: true });
94207
94615
  const reaction = reactionBody(args8);
@@ -94224,7 +94632,7 @@ var createCommand3 = defineCommand228({
94224
94632
  });
94225
94633
 
94226
94634
  // src/commands/scheduled-agents/delete.ts
94227
- import { defineCommand as defineCommand229 } from "citty";
94635
+ import { defineCommand as defineCommand230 } from "citty";
94228
94636
  registerSchema({
94229
94637
  command: "scheduled-agents.delete",
94230
94638
  description: "Stage deletion of a published scheduled agent, or cancel one this chat staged (temp_agent_*).",
@@ -94232,7 +94640,7 @@ registerSchema({
94232
94640
  id: { type: "string", description: "Published scheduled agent ID or temp_agent_* draft ID", required: true }
94233
94641
  }
94234
94642
  });
94235
- var deleteCommand3 = defineCommand229({
94643
+ var deleteCommand3 = defineCommand230({
94236
94644
  meta: {
94237
94645
  name: "delete",
94238
94646
  description: "Stage a scheduled agent deletion. Example: baker scheduled-agents delete <id-or-temp_agent_id>"
@@ -94249,7 +94657,7 @@ var deleteCommand3 = defineCommand229({
94249
94657
  try {
94250
94658
  const id = args8.id || args8["scheduled-agent-id"];
94251
94659
  if (!id) {
94252
- failValidation4("Scheduled agent ID is required.");
94660
+ failValidation5("Scheduled agent ID is required.");
94253
94661
  }
94254
94662
  validateScheduledAgentRef(id);
94255
94663
  await apiPost("/api/scheduled-agents/delete", { chatId: requireChatId(), id });
@@ -94261,7 +94669,7 @@ var deleteCommand3 = defineCommand229({
94261
94669
  });
94262
94670
 
94263
94671
  // src/commands/scheduled-agents/get.ts
94264
- import { defineCommand as defineCommand230 } from "citty";
94672
+ import { defineCommand as defineCommand231 } from "citty";
94265
94673
  registerSchema({
94266
94674
  command: "scheduled-agents.get",
94267
94675
  description: "Get a published scheduled agent, or one this chat has staged (temp_agent_*). A published one comes with runsAs: who each run acts as ({ name, personalTools, companyTools, notSignedIn?, toolsOff? }) \u2014 the tools a run loads, which decides whether a job like reading someone's inbox can work. Its recentRuns and any draftOps this chat staged on it come with it.",
@@ -94270,7 +94678,7 @@ registerSchema({
94270
94678
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
94271
94679
  }
94272
94680
  });
94273
- var getCommand4 = defineCommand230({
94681
+ var getCommand4 = defineCommand231({
94274
94682
  meta: {
94275
94683
  name: "get",
94276
94684
  description: "Get a scheduled agent. Example: baker scheduled-agents get <id-or-temp_agent_id>"
@@ -94288,13 +94696,13 @@ var getCommand4 = defineCommand230({
94288
94696
  try {
94289
94697
  const id = args8.id || args8["scheduled-agent-id"];
94290
94698
  if (!id) {
94291
- failValidation4("Scheduled agent ID is required.");
94699
+ failValidation5("Scheduled agent ID is required.");
94292
94700
  }
94293
94701
  validateScheduledAgentRef(id);
94294
94702
  const env = getEnv();
94295
94703
  const chatId = typeof args8.chat === "string" && args8.chat.length > 0 ? args8.chat : env.BAKER_CHAT_ID;
94296
94704
  if (isTempScheduledAgentId(id) && !chatId) {
94297
- failValidation4("BAKER_CHAT_ID or --chat is required to get a staged scheduled agent ID.");
94705
+ failValidation5("BAKER_CHAT_ID or --chat is required to get a staged scheduled agent ID.");
94298
94706
  }
94299
94707
  const body = { id };
94300
94708
  if (chatId) {
@@ -94309,7 +94717,7 @@ var getCommand4 = defineCommand230({
94309
94717
  });
94310
94718
 
94311
94719
  // src/commands/scheduled-agents/list.ts
94312
- import { defineCommand as defineCommand231 } from "citty";
94720
+ import { defineCommand as defineCommand232 } from "citty";
94313
94721
  var FULL_DESCRIPTION2 = "Return every item and staged change as stored, briefs included (tens of KB). Prefer `get <id>` for one item.";
94314
94722
  registerSchema({
94315
94723
  command: "scheduled-agents.list",
@@ -94319,7 +94727,7 @@ registerSchema({
94319
94727
  full: { type: "boolean", description: FULL_DESCRIPTION2, required: false }
94320
94728
  }
94321
94729
  });
94322
- var listCommand19 = defineCommand231({
94730
+ var listCommand19 = defineCommand232({
94323
94731
  meta: {
94324
94732
  name: "list",
94325
94733
  description: "List scheduled agents: id, name, trigger and status for each, plus this chat's staged changes when BAKER_CHAT_ID is set (or --chat <id> for an earlier chat's). Briefs are left out. Start here: baker scheduled-agents list; then baker scheduled-agents get <id> for one item's brief."
@@ -94345,7 +94753,7 @@ var listCommand19 = defineCommand231({
94345
94753
  });
94346
94754
 
94347
94755
  // src/commands/scheduled-agents/run.ts
94348
- import { defineCommand as defineCommand232 } from "citty";
94756
+ import { defineCommand as defineCommand233 } from "citty";
94349
94757
  registerSchema({
94350
94758
  command: "scheduled-agents.run",
94351
94759
  description: "Run a published scheduled agent now, whatever its trigger says. Opens a Session immediately and returns its chatId. Does not need BAKER_CHAT_ID, and refuses a temp_agent_* id \u2014 a staged item has to be published before it can run.",
@@ -94353,7 +94761,7 @@ registerSchema({
94353
94761
  id: { type: "string", description: "Published scheduled agent ID", required: true }
94354
94762
  }
94355
94763
  });
94356
- var runCommand2 = defineCommand232({
94764
+ var runCommand2 = defineCommand233({
94357
94765
  meta: {
94358
94766
  name: "run",
94359
94767
  description: "Run a published scheduled agent now. Example: baker scheduled-agents run <id>"
@@ -94370,10 +94778,10 @@ var runCommand2 = defineCommand232({
94370
94778
  try {
94371
94779
  const id = args8.id || args8["scheduled-agent-id"];
94372
94780
  if (!id) {
94373
- failValidation4("Scheduled agent ID is required.");
94781
+ failValidation5("Scheduled agent ID is required.");
94374
94782
  }
94375
94783
  if (isTempScheduledAgentId(id)) {
94376
- failValidation4("run needs a published scheduled agent ID, not temp_agent_*. Publish this chat first.");
94784
+ failValidation5("run needs a published scheduled agent ID, not temp_agent_*. Publish this chat first.");
94377
94785
  }
94378
94786
  validateConvexId(id);
94379
94787
  const response = await apiPost("/api/scheduled-agents/run", { id });
@@ -94385,7 +94793,7 @@ var runCommand2 = defineCommand232({
94385
94793
  });
94386
94794
 
94387
94795
  // src/commands/scheduled-agents/targets.ts
94388
- import { defineCommand as defineCommand233 } from "citty";
94796
+ import { defineCommand as defineCommand234 } from "citty";
94389
94797
  var ARGS9 = {
94390
94798
  trigger: {
94391
94799
  type: "string",
@@ -94419,7 +94827,7 @@ registerSchema({
94419
94827
  description: "What a trigger can be pointed at: the open Tasks, the company's scheduled agents, its review sources, followed competitors, pages, Forms and their steps, Knowledge collections, tools. Returns each option's value (pass it to the matching flag on create/update) with its name.",
94420
94828
  args: ARGS9
94421
94829
  });
94422
- var targetsCommand = defineCommand233({
94830
+ var targetsCommand = defineCommand234({
94423
94831
  meta: {
94424
94832
  name: "targets",
94425
94833
  description: `What a trigger can be pointed at \u2014 so "when a Task is picked up" becomes "when THIS Task is picked up".
@@ -94440,7 +94848,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
94440
94848
  run: async ({ args: args8 }) => {
94441
94849
  const trigger = args8.trigger;
94442
94850
  if (!TRIGGER_KIND_IDS.some((kind) => kind === trigger)) {
94443
- failValidation4(`--trigger must be one of: ${TRIGGER_KIND_IDS.join(", ")}.`);
94851
+ failValidation5(`--trigger must be one of: ${TRIGGER_KIND_IDS.join(", ")}.`);
94444
94852
  }
94445
94853
  try {
94446
94854
  const response = await apiPost("/api/scheduled-agents/targets", {
@@ -94462,7 +94870,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
94462
94870
  // src/commands/scheduled-agents/templates.ts
94463
94871
  import { readFile as readFile42 } from "fs/promises";
94464
94872
  import path55 from "path";
94465
- import { defineCommand as defineCommand234 } from "citty";
94873
+ import { defineCommand as defineCommand235 } from "citty";
94466
94874
  registerSchema({
94467
94875
  command: "scheduled-agents.templates",
94468
94876
  description: "The recipes available to this company: the ones Baker ships plus the ones they wrote themselves, each with its id, what it produces and how often it is meant to run. Read this before proposing a company's automation, so every recipe you name is one that exists. Also how a company gets a recipe of its own \u2014 save a brief, prove it runs, then publish it.",
@@ -94517,7 +94925,7 @@ registerSchema({
94517
94925
  }
94518
94926
  }
94519
94927
  });
94520
- var templatesCommand = defineCommand234({
94928
+ var templatesCommand = defineCommand235({
94521
94929
  meta: {
94522
94930
  name: "templates",
94523
94931
  description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
@@ -94564,13 +94972,13 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
94564
94972
  const publish = flag("publish");
94565
94973
  const remove = flag("delete");
94566
94974
  if ([save, publish, remove].filter((id) => id.length > 0).length > 1) {
94567
- failValidation4("Pass one of --save, --publish or --delete at a time.");
94975
+ failValidation5("Pass one of --save, --publish or --delete at a time.");
94568
94976
  }
94569
94977
  if (save.length > 0) {
94570
94978
  const briefFile = flag("brief-file");
94571
94979
  const brief2 = briefFile.length > 0 ? await readFile42(path55.resolve(briefFile), "utf8") : flag("brief");
94572
94980
  if (brief2.trim().length === 0) {
94573
- failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
94981
+ failValidation5("--brief-file (preferred) or --brief is required: the brief is the recipe.");
94574
94982
  }
94575
94983
  const response2 = await apiPost("/api/marketplace/save", {
94576
94984
  chatId: requireChatId(),
@@ -94613,7 +95021,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
94613
95021
  });
94614
95022
 
94615
95023
  // src/commands/scheduled-agents/triggers.ts
94616
- import { defineCommand as defineCommand235 } from "citty";
95024
+ import { defineCommand as defineCommand236 } from "citty";
94617
95025
  var ARGS10 = {
94618
95026
  trigger: {
94619
95027
  type: "string",
@@ -94699,7 +95107,7 @@ function shapeTriggerCatalog(response, filter) {
94699
95107
  }
94700
95108
  return { ok: true, data: { groups, reacting: catalog.reacting } };
94701
95109
  }
94702
- var triggersCommand2 = defineCommand235({
95110
+ var triggersCommand2 = defineCommand236({
94703
95111
  meta: {
94704
95112
  name: "triggers",
94705
95113
  description: `Everything that can start a scheduled agent.
@@ -94729,7 +95137,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
94729
95137
  });
94730
95138
 
94731
95139
  // src/commands/scheduled-agents/unstage.ts
94732
- import { defineCommand as defineCommand236 } from "citty";
95140
+ import { defineCommand as defineCommand237 } from "citty";
94733
95141
  registerSchema({
94734
95142
  command: "scheduled-agents.unstage",
94735
95143
  description: "Take back what this chat staged on one scheduled agent, before Publish \u2014 the same as discarding its row in the change list. With --index, only that one change (its `index` in `baker scheduled-agents list` \u2192 draft.ops). Taking back a create also takes back the edits staged on it. Never touches a published item: to change or remove one, stage update/delete instead.",
@@ -94749,13 +95157,13 @@ function unstageBody(args8, chatId) {
94749
95157
  }
94750
95158
  const index = Number(args8.index);
94751
95159
  if (args8.index.trim() === "" || !Number.isInteger(index) || index < 0) {
94752
- failValidation4(
95160
+ failValidation5(
94753
95161
  "--index must be a whole number from `baker scheduled-agents list` \u2192 draft.ops[].index. Leave it off to take back everything staged on the item."
94754
95162
  );
94755
95163
  }
94756
95164
  return { chatId, ref: args8.ref, index };
94757
95165
  }
94758
- var unstageCommand = defineCommand236({
95166
+ var unstageCommand = defineCommand237({
94759
95167
  meta: {
94760
95168
  name: "unstage",
94761
95169
  description: "Take back what this chat staged on a scheduled agent, before Publish. Examples: baker scheduled-agents unstage temp_agent_abc123 | baker scheduled-agents unstage <id> --index 2"
@@ -94773,7 +95181,7 @@ var unstageCommand = defineCommand236({
94773
95181
  try {
94774
95182
  const ref2 = args8.id || args8["scheduled-agent-id"];
94775
95183
  if (!ref2) {
94776
- failValidation4("Scheduled agent ID is required.");
95184
+ failValidation5("Scheduled agent ID is required.");
94777
95185
  }
94778
95186
  const index = typeof args8.index === "string" ? args8.index : void 0;
94779
95187
  const body = unstageBody({ ref: ref2, ...index !== void 0 ? { index } : {} }, requireChatId());
@@ -94786,7 +95194,7 @@ var unstageCommand = defineCommand236({
94786
95194
  });
94787
95195
 
94788
95196
  // src/commands/scheduled-agents/update.ts
94789
- import { defineCommand as defineCommand237 } from "citty";
95197
+ import { defineCommand as defineCommand238 } from "citty";
94790
95198
  registerSchema({
94791
95199
  command: "scheduled-agents.update",
94792
95200
  description: "Stage an update to a published scheduled agent, or to one this chat staged (temp_agent_*).",
@@ -94813,7 +95221,7 @@ registerSchema({
94813
95221
  enabled: { type: "string", description: "Replacement enabled state", required: false, enum: ["true", "false"] }
94814
95222
  }
94815
95223
  });
94816
- var updateCommand4 = defineCommand237({
95224
+ var updateCommand4 = defineCommand238({
94817
95225
  meta: {
94818
95226
  name: "update",
94819
95227
  description: "Stage a scheduled agent update. Examples: baker scheduled-agents update <id> --enabled false | baker scheduled-agents update <id> --trigger form.submitted --form quote"
@@ -94843,7 +95251,7 @@ var updateCommand4 = defineCommand237({
94843
95251
  refuseAutoPublish(args8);
94844
95252
  const id = args8.id || args8["scheduled-agent-id"];
94845
95253
  if (!id) {
94846
- failValidation4("Scheduled agent ID is required.");
95254
+ failValidation5("Scheduled agent ID is required.");
94847
95255
  }
94848
95256
  validateScheduledAgentRef(id);
94849
95257
  const body = { id };
@@ -94870,7 +95278,7 @@ var updateCommand4 = defineCommand237({
94870
95278
  hasPatch = true;
94871
95279
  }
94872
95280
  if (!hasPatch) {
94873
- failValidation4(
95281
+ failValidation5(
94874
95282
  "Provide at least one of --name, --description, --prompt, --cron, --run-at, --trigger, --occurrences, --times, --when, --payload, --cooldown, --timezone, --enabled."
94875
95283
  );
94876
95284
  }
@@ -94884,7 +95292,7 @@ var updateCommand4 = defineCommand237({
94884
95292
  });
94885
95293
 
94886
95294
  // src/commands/scheduled-agents/index.ts
94887
- var scheduledAgentsCommand = defineCommand238({
95295
+ var scheduledAgentsCommand = defineCommand239({
94888
95296
  meta: {
94889
95297
  name: "scheduled-agents",
94890
95298
  description: `Manage Scheduled agents. Subcommands: list, get, triggers, targets, create, update, delete, unstage, run, templates.
@@ -94927,14 +95335,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
94927
95335
  });
94928
95336
 
94929
95337
  // src/commands/schema.ts
94930
- import { defineCommand as defineCommand239 } from "citty";
95338
+ import { defineCommand as defineCommand240 } from "citty";
94931
95339
  function narrowToFamily(commandName, available) {
94932
95340
  const segments = commandName.split(".");
94933
95341
  const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
94934
95342
  const siblings = available.filter((name) => name.startsWith(prefix));
94935
95343
  return siblings.length > 0 ? siblings : available;
94936
95344
  }
94937
- var schemaCommand2 = defineCommand239({
95345
+ var schemaCommand2 = defineCommand240({
94938
95346
  meta: {
94939
95347
  name: "schema",
94940
95348
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images search (or images.search)"
@@ -94979,10 +95387,10 @@ var schemaCommand2 = defineCommand239({
94979
95387
  });
94980
95388
 
94981
95389
  // src/commands/studio/index.ts
94982
- import { defineCommand as defineCommand248 } from "citty";
95390
+ import { defineCommand as defineCommand249 } from "citty";
94983
95391
 
94984
95392
  // src/commands/studio/animate.ts
94985
- import { defineCommand as defineCommand240 } from "citty";
95393
+ import { defineCommand as defineCommand241 } from "citty";
94986
95394
 
94987
95395
  // src/commands/studio/spokenPace.ts
94988
95396
  var WORDS_PER_SECOND = SPOKEN_WORDS_PER_SECOND;
@@ -95495,7 +95903,7 @@ async function adByHandHintFor(body) {
95495
95903
  return null;
95496
95904
  }
95497
95905
  }
95498
- var animateCommand = defineCommand240({
95906
+ var animateCommand = defineCommand241({
95499
95907
  meta: {
95500
95908
  name: "animate",
95501
95909
  description: "Render a clip. With an image the look is already fixed, so the prompt describes MOVEMENT \u2014 what the camera does, what the subject does, in what order. With --from text there is no image and the prompt is the whole shot.\n\nA rendered clip is NOT usable anywhere until you keep it: `baker studio keep <id> --slot N` is what puts it in the video library. Takes nobody keeps are never ingested, which is what makes a rejected batch cheap.\n\nFor anything longer than 15 seconds, or to build on footage that already exists, use --model bytedance/seedance-2.5: it renders 4-30s and is the only model that reads an existing clip or an existing soundtrack.\n\nExamples:\n baker studio animate 'slow push in, model turns to camera and smiles' --image j57abc123def456ghi789\n baker studio animate 'she looks to camera and says: \"Hola, soy Elena\"' --avatar elena --quality 720p --aspect-ratio 9:16\n baker studio animate 'handheld drift right, steam rising from the cup' --image './out/hero.png' --duration 6 --quality 1080p\n baker studio animate 'product rotates once on a turntable' --image j57abc\u2026,j57def\u2026 --from references\n baker studio animate 'she keeps walking, camera stays with her, then she stops and looks up' --image j57abc\u2026 --from references --from-clip j57batch\u2026:0 --model bytedance/seedance-2.5 --duration 20\n baker studio animate 'hold on the product, then a slow push-in' --from references --from-video j57vid\u2026 --model bytedance/seedance-2.5\n baker studio animate 'slow drone pull-back over a solar farm at golden hour, no people' --from text --model bytedance/seedance-2.5 --duration 12"
@@ -95620,7 +96028,7 @@ var animateCommand = defineCommand240({
95620
96028
  });
95621
96029
 
95622
96030
  // src/commands/studio/generate.ts
95623
- import { defineCommand as defineCommand241 } from "citty";
96031
+ import { defineCommand as defineCommand242 } from "citty";
95624
96032
  var MODEL_LIST2 = IMAGE_MODEL_IDS;
95625
96033
  var DEFAULT_MAX_WAIT_MS2 = 24e4;
95626
96034
  registerSchema({
@@ -95756,7 +96164,7 @@ function buildGenerateBody(args8, prompt) {
95756
96164
  }
95757
96165
  return body;
95758
96166
  }
95759
- var generateCommand = defineCommand241({
96167
+ var generateCommand = defineCommand242({
95760
96168
  meta: {
95761
96169
  name: "generate",
95762
96170
  description: "Start here to make an image. Renders 1-8 takes of one brief, ingests each into the media library as it lands, and shows the batch in the dashboard Studio next to the ones the client ran.\n\nModel choice: google/gemini-3.1-flash-image (default \u2014 fast, best at editing a reference and at extreme ratios), google/gemini-3-pro-image (highest fidelity, slower), openai/gpt-image-2 (photoreal and the cleanest in-image text \u2014 no --image-size, no 4:5 / 5:4), recraft/recraft-v4.1-pro-vector (vector/flat marks with palette control).\n\n--reference is the biggest quality lever there is: a real logo, product shot, Pinterest pin or sandbox screenshot beats any amount of adjectives.\n\nExamples:\n baker studio generate 'matte black bottle on wet marble, hard studio light, 35mm' --aspect-ratio 3:2 --count 3\n baker studio generate 'this bottle on a sunlit kitchen counter' --reference './src/brand/product.png,https://\u2026/kitchen.jpg'\n baker studio generate 'founder-style selfie, kitchen background, natural light' --skill ugc-selfie-hook\n baker studio generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
@@ -95839,7 +96247,7 @@ var generateCommand = defineCommand241({
95839
96247
  });
95840
96248
 
95841
96249
  // src/commands/studio/get.ts
95842
- import { defineCommand as defineCommand242 } from "citty";
96250
+ import { defineCommand as defineCommand243 } from "citty";
95843
96251
  registerSchema({
95844
96252
  command: "studio.get",
95845
96253
  description: "Read one Studio batch: every take, where it lives, and why a take is missing. This is how you pick up a batch that was still rendering when the start command returned.",
@@ -95848,7 +96256,7 @@ registerSchema({
95848
96256
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
95849
96257
  }
95850
96258
  });
95851
- var getCommand5 = defineCommand242({
96259
+ var getCommand5 = defineCommand243({
95852
96260
  meta: {
95853
96261
  name: "get",
95854
96262
  description: "Read one Studio batch \u2014 the takes, their urls, whether each is in the library, and the reason for any that failed.\n\nExample: baker studio get j57abc123def456ghi789\nExample: baker studio get j57abc123def456ghi789 --full"
@@ -95877,7 +96285,7 @@ var getCommand5 = defineCommand242({
95877
96285
  });
95878
96286
 
95879
96287
  // src/commands/studio/improve.ts
95880
- import { defineCommand as defineCommand243 } from "citty";
96288
+ import { defineCommand as defineCommand244 } from "citty";
95881
96289
  var DESCRIPTION7 = "Sharpen a rough brief into directed art direction \u2014 the same rewrite the client gets from the wand in the Studio prompt bar. Reach for it when you are relaying the CLIENT's own words and want them shaped without substituting your voice; when you are writing the art direction yourself, just write it, because you will do a better job than this does.";
95882
96290
  registerSchema({
95883
96291
  command: "studio.improve",
@@ -95902,7 +96310,7 @@ registerSchema({
95902
96310
  }
95903
96311
  }
95904
96312
  });
95905
- var improveCommand = defineCommand243({
96313
+ var improveCommand = defineCommand244({
95906
96314
  meta: {
95907
96315
  name: "improve",
95908
96316
  description: `${DESCRIPTION7}
@@ -95946,7 +96354,7 @@ Examples:
95946
96354
  });
95947
96355
 
95948
96356
  // src/commands/studio/keep.ts
95949
- import { defineCommand as defineCommand244 } from "citty";
96357
+ import { defineCommand as defineCommand245 } from "citty";
95950
96358
  registerSchema({
95951
96359
  command: "studio.keep",
95952
96360
  description: "Mark one take as the keeper. For an image this stars it, so the client reviewing the batch sees which one you used. For a CLIP it is the step that puts it in the video library \u2014 until then the clip cannot be used in a canvas, a landing, or an ad.",
@@ -95956,7 +96364,7 @@ registerSchema({
95956
96364
  undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
95957
96365
  }
95958
96366
  });
95959
- var keepCommand = defineCommand244({
96367
+ var keepCommand = defineCommand245({
95960
96368
  meta: {
95961
96369
  name: "keep",
95962
96370
  description: "Mark one take as the keeper. An image gets starred (it was already in the library); a clip gets INGESTED into the video library, which is what makes it usable anywhere else.\n\nExample: baker studio keep j57abc123def456ghi789 --slot 2\nExample: baker studio keep j57abc123def456ghi789 --slot 2 --undo"
@@ -96007,7 +96415,7 @@ var keepCommand = defineCommand244({
96007
96415
  });
96008
96416
 
96009
96417
  // src/commands/studio/list.ts
96010
- import { defineCommand as defineCommand245 } from "citty";
96418
+ import { defineCommand as defineCommand246 } from "citty";
96011
96419
  registerSchema({
96012
96420
  command: "studio.list",
96013
96421
  description: "Recent Studio batches for THIS conversation, newest first \u2014 what you have already generated, so you re-use a take instead of paying for it twice. `--all` widens it to everything the company generated, including what people ran themselves in the dashboard.",
@@ -96018,7 +96426,7 @@ registerSchema({
96018
96426
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
96019
96427
  }
96020
96428
  });
96021
- var listCommand20 = defineCommand245({
96429
+ var listCommand20 = defineCommand246({
96022
96430
  meta: {
96023
96431
  name: "list",
96024
96432
  description: "Recent Studio batches, newest first. Scoped to this conversation unless you pass --all.\n\nExample: baker studio list\nExample: baker studio list --kind video --limit 5\nExample: baker studio list --all # includes batches the client ran in the dashboard"
@@ -96052,7 +96460,7 @@ var listCommand20 = defineCommand245({
96052
96460
  });
96053
96461
 
96054
96462
  // src/commands/studio/models.ts
96055
- import { defineCommand as defineCommand246 } from "citty";
96463
+ import { defineCommand as defineCommand247 } from "citty";
96056
96464
  var DESCRIPTION8 = "What each Studio model actually accepts: its shapes, resolutions, clip lengths, prompt character cap, how many reference images it takes, and which knobs it has. Read this before a batch you care about \u2014 the models disagree far more than they look like they do, and a setting the chosen model does not have is REFUSED, not ignored.";
96057
96465
  registerSchema({
96058
96466
  command: "studio.models",
@@ -96139,7 +96547,7 @@ function buildModelCards(kind, model) {
96139
96547
  const selected = model ? ids2.filter((id) => id === model) : ids2;
96140
96548
  return selected.map((id) => build(id));
96141
96549
  }
96142
- var modelsCommand = defineCommand246({
96550
+ var modelsCommand = defineCommand247({
96143
96551
  meta: {
96144
96552
  name: "models",
96145
96553
  description: `${DESCRIPTION8}
@@ -96186,13 +96594,13 @@ Examples:
96186
96594
  });
96187
96595
 
96188
96596
  // src/commands/studio/skills.ts
96189
- import { defineCommand as defineCommand247 } from "citty";
96597
+ import { defineCommand as defineCommand248 } from "citty";
96190
96598
  registerSchema({
96191
96599
  command: "studio.skills",
96192
96600
  description: "The craft directions `studio generate --skill <id>` accepts. Each one carries directed art direction plus the model, shape and take count it wants, so you pick a look by name instead of writing the boilerplate yourself.",
96193
96601
  args: {}
96194
96602
  });
96195
- var skillsCommand = defineCommand247({
96603
+ var skillsCommand = defineCommand248({
96196
96604
  meta: {
96197
96605
  name: "skills",
96198
96606
  description: "List the craft directions available to `baker studio generate --skill <id>` \u2014 what each one is for, whether it wants a reference image, and the model/shape/count it defaults to.\n\nExample: baker studio skills"
@@ -96216,7 +96624,7 @@ var skillsCommand = defineCommand247({
96216
96624
  });
96217
96625
 
96218
96626
  // src/commands/studio/index.ts
96219
- var studioCommand = defineCommand248({
96627
+ var studioCommand = defineCommand249({
96220
96628
  meta: {
96221
96629
  name: "studio",
96222
96630
  description: `Make new imagery and clips. Every batch is recorded and shows up in the dashboard Studio for the client to review, labelled with this conversation.
@@ -96259,14 +96667,14 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
96259
96667
  });
96260
96668
 
96261
96669
  // src/commands/tag-manager/index.ts
96262
- import { defineCommand as defineCommand252 } from "citty";
96670
+ import { defineCommand as defineCommand253 } from "citty";
96263
96671
 
96264
96672
  // src/commands/tag-manager/draft.ts
96265
- import { defineCommand as defineCommand249 } from "citty";
96673
+ import { defineCommand as defineCommand250 } from "citty";
96266
96674
 
96267
96675
  // src/commands/tag-manager/shared.ts
96268
96676
  import { readFileSync as readFileSync23 } from "fs";
96269
- function failValidation5(message) {
96677
+ function failValidation6(message) {
96270
96678
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
96271
96679
  process.exit(1);
96272
96680
  }
@@ -96274,7 +96682,7 @@ function requireTarget7(args8, entity) {
96274
96682
  const positional = Array.isArray(args8._) ? args8._[0] : void 0;
96275
96683
  const target2 = args8.id ?? args8.target ?? positional;
96276
96684
  if (typeof target2 !== "string" || target2.length === 0) {
96277
- failValidation5(`pass the ${entity} id or path as the positional argument`);
96685
+ failValidation6(`pass the ${entity} id or path as the positional argument`);
96278
96686
  }
96279
96687
  return target2;
96280
96688
  }
@@ -96283,16 +96691,16 @@ function loadJsonArg2(args8, flag = "json") {
96283
96691
  const file = args8.file;
96284
96692
  const raw = typeof file === "string" && file.length > 0 ? readFileSync23(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
96285
96693
  if (raw === void 0) {
96286
- failValidation5(`pass --${flag} with inline JSON or --file with a path to a JSON file`);
96694
+ failValidation6(`pass --${flag} with inline JSON or --file with a path to a JSON file`);
96287
96695
  }
96288
96696
  try {
96289
96697
  const parsed = JSON.parse(raw);
96290
96698
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
96291
- failValidation5(`--${flag} must be a JSON object`);
96699
+ failValidation6(`--${flag} must be a JSON object`);
96292
96700
  }
96293
96701
  return parsed;
96294
96702
  } catch {
96295
- return failValidation5(`--${flag} is not valid JSON`);
96703
+ return failValidation6(`--${flag} is not valid JSON`);
96296
96704
  }
96297
96705
  }
96298
96706
  var RETRYABLE_CODES2 = /* @__PURE__ */ new Set(["RATE_LIMITED", "UNAVAILABLE", "INTERNAL_ERROR", "NETWORK_ERROR", "TIMEOUT"]);
@@ -96388,13 +96796,13 @@ registerSchema({
96388
96796
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
96389
96797
  }
96390
96798
  });
96391
- var draftCommand6 = defineCommand249({
96799
+ var draftCommand6 = defineCommand250({
96392
96800
  meta: {
96393
96801
  name: "draft",
96394
96802
  description: "List, show, amend, remove, or clear staged Tag Manager changes for this chat. `list` and `show` take --chat <id> to read an earlier chat's changes instead."
96395
96803
  },
96396
96804
  subCommands: {
96397
- list: defineCommand249({
96805
+ list: defineCommand250({
96398
96806
  meta: {
96399
96807
  name: "list",
96400
96808
  description: "Review everything staged on this chat (--json for the raw envelope)"
@@ -96407,7 +96815,7 @@ var draftCommand6 = defineCommand249({
96407
96815
  await draftList2(args8.json === true, args8.chat);
96408
96816
  }
96409
96817
  }),
96410
- show: defineCommand249({
96818
+ show: defineCommand250({
96411
96819
  meta: {
96412
96820
  name: "show",
96413
96821
  description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
@@ -96424,7 +96832,7 @@ var draftCommand6 = defineCommand249({
96424
96832
  );
96425
96833
  }
96426
96834
  }),
96427
- amend: defineCommand249({
96835
+ amend: defineCommand250({
96428
96836
  meta: {
96429
96837
  name: "amend",
96430
96838
  description: "Update a staged change in place \u2014 merges a JSON patch into its payload (objects deep-merge, null deletes a key, arrays/scalars replace) and re-validates. Use this instead of remove + re-create."
@@ -96441,7 +96849,7 @@ var draftCommand6 = defineCommand249({
96441
96849
  });
96442
96850
  }
96443
96851
  }),
96444
- remove: defineCommand249({
96852
+ remove: defineCommand250({
96445
96853
  meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
96446
96854
  args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
96447
96855
  run: async ({ args: args8 }) => {
@@ -96450,7 +96858,7 @@ var draftCommand6 = defineCommand249({
96450
96858
  });
96451
96859
  }
96452
96860
  }),
96453
- clear: defineCommand249({
96861
+ clear: defineCommand250({
96454
96862
  meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
96455
96863
  run: async () => {
96456
96864
  await draftAction3("/api/tag-manager/draft/clear", {});
@@ -96460,7 +96868,7 @@ var draftCommand6 = defineCommand249({
96460
96868
  });
96461
96869
 
96462
96870
  // src/commands/tag-manager/read.ts
96463
- import { defineCommand as defineCommand250 } from "citty";
96871
+ import { defineCommand as defineCommand251 } from "citty";
96464
96872
  registerSchema({
96465
96873
  command: "tagManager.containers",
96466
96874
  description: "List the Google Tag Manager containers this company selected. There can be several, and Baker may read and change all of them. Start here to confirm which containers you are managing.",
@@ -96508,7 +96916,7 @@ function containersHints(containers, unavailableContainers = []) {
96508
96916
  }
96509
96917
  return hints2;
96510
96918
  }
96511
- var containersCommand = defineCommand250({
96919
+ var containersCommand = defineCommand251({
96512
96920
  meta: {
96513
96921
  name: "containers",
96514
96922
  description: `List Tag Manager containers selected by this company.
@@ -96525,7 +96933,7 @@ Start here:
96525
96933
  }
96526
96934
  }
96527
96935
  });
96528
- var readCommand = defineCommand250({
96936
+ var readCommand = defineCommand251({
96529
96937
  meta: {
96530
96938
  name: "read",
96531
96939
  description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
@@ -96567,7 +96975,7 @@ Examples:
96567
96975
  });
96568
96976
 
96569
96977
  // src/commands/tag-manager/write-commands.ts
96570
- import { defineCommand as defineCommand251 } from "citty";
96978
+ import { defineCommand as defineCommand252 } from "citty";
96571
96979
  var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
96572
96980
  var ENTITIES = [
96573
96981
  {
@@ -96623,10 +97031,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
96623
97031
  });
96624
97032
  }
96625
97033
  function entityCommand(entity, noun, example) {
96626
- return defineCommand251({
97034
+ return defineCommand252({
96627
97035
  meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
96628
97036
  subCommands: {
96629
- create: defineCommand251({
97037
+ create: defineCommand252({
96630
97038
  meta: {
96631
97039
  name: "create",
96632
97040
  description: `Stage a new ${noun}
@@ -96648,7 +97056,7 @@ Examples:
96648
97056
  });
96649
97057
  }
96650
97058
  }),
96651
- update: defineCommand251({
97059
+ update: defineCommand252({
96652
97060
  meta: {
96653
97061
  name: "update",
96654
97062
  description: `Stage an update to an existing ${noun} (pass its id or path)`
@@ -96668,7 +97076,7 @@ Examples:
96668
97076
  });
96669
97077
  }
96670
97078
  }),
96671
- delete: defineCommand251({
97079
+ delete: defineCommand252({
96672
97080
  meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
96673
97081
  args: {
96674
97082
  id: { type: "positional", description: `${noun} id or path`, required: false },
@@ -96709,7 +97117,7 @@ function builtinTypes(args8) {
96709
97117
  }
96710
97118
  return raw.split(",").map((entry) => entry.trim());
96711
97119
  }
96712
- var builtinCommand = defineCommand251({
97120
+ var builtinCommand = defineCommand252({
96713
97121
  meta: {
96714
97122
  name: "builtin",
96715
97123
  description: `Enable or disable built-in variables
@@ -96719,7 +97127,7 @@ Examples:
96719
97127
  baker tag-manager builtin disable --types formId`
96720
97128
  },
96721
97129
  subCommands: {
96722
- enable: defineCommand251({
97130
+ enable: defineCommand252({
96723
97131
  meta: { name: "enable", description: "Stage enabling built-in variables" },
96724
97132
  args: {
96725
97133
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -96733,7 +97141,7 @@ Examples:
96733
97141
  });
96734
97142
  }
96735
97143
  }),
96736
- disable: defineCommand251({
97144
+ disable: defineCommand252({
96737
97145
  meta: { name: "disable", description: "Stage disabling built-in variables" },
96738
97146
  args: {
96739
97147
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -96751,7 +97159,7 @@ Examples:
96751
97159
  });
96752
97160
 
96753
97161
  // src/commands/tag-manager/index.ts
96754
- var tagManagerCommand = defineCommand252({
97162
+ var tagManagerCommand = defineCommand253({
96755
97163
  meta: {
96756
97164
  name: "tag-manager",
96757
97165
  description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
@@ -96788,7 +97196,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
96788
97196
  });
96789
97197
 
96790
97198
  // src/commands/tags/index.ts
96791
- import { defineCommand as defineCommand253 } from "citty";
97199
+ import { defineCommand as defineCommand254 } from "citty";
96792
97200
 
96793
97201
  // src/commands/tags/shared.ts
96794
97202
  function failApi3(err) {
@@ -96868,7 +97276,7 @@ async function listTags(json) {
96868
97276
  var listArgs12 = {
96869
97277
  json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
96870
97278
  };
96871
- var listCommand21 = defineCommand253({
97279
+ var listCommand21 = defineCommand254({
96872
97280
  meta: {
96873
97281
  name: "list",
96874
97282
  description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
@@ -96887,7 +97295,7 @@ async function listDraft4(chat) {
96887
97295
  failApi3(err);
96888
97296
  }
96889
97297
  }
96890
- var draftCommand7 = defineCommand253({
97298
+ var draftCommand7 = defineCommand254({
96891
97299
  meta: {
96892
97300
  name: "draft",
96893
97301
  description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create). Takes --chat <id> to read an earlier chat's staged changes instead."
@@ -96897,7 +97305,7 @@ var draftCommand7 = defineCommand253({
96897
97305
  await listDraft4(args8.chat);
96898
97306
  }
96899
97307
  });
96900
- var tagsCommand4 = defineCommand253({
97308
+ var tagsCommand4 = defineCommand254({
96901
97309
  meta: {
96902
97310
  name: "tags",
96903
97311
  description: `Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) \u2014 production tags plus the changes staged in this chat.
@@ -96926,10 +97334,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
96926
97334
  });
96927
97335
 
96928
97336
  // src/commands/testimonials/index.ts
96929
- import { defineCommand as defineCommand257 } from "citty";
97337
+ import { defineCommand as defineCommand258 } from "citty";
96930
97338
 
96931
97339
  // src/commands/testimonials/get.ts
96932
- import { defineCommand as defineCommand254 } from "citty";
97340
+ import { defineCommand as defineCommand255 } from "citty";
96933
97341
  registerSchema({
96934
97342
  command: "testimonials.get",
96935
97343
  description: "Get a single testimonial by ID",
@@ -96937,7 +97345,7 @@ registerSchema({
96937
97345
  id: { type: "string", description: "Testimonial ID", required: true }
96938
97346
  }
96939
97347
  });
96940
- var getCommand6 = defineCommand254({
97348
+ var getCommand6 = defineCommand255({
96941
97349
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
96942
97350
  args: {
96943
97351
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -96974,7 +97382,7 @@ var getCommand6 = defineCommand254({
96974
97382
  });
96975
97383
 
96976
97384
  // src/commands/testimonials/list.ts
96977
- import { defineCommand as defineCommand255 } from "citty";
97385
+ import { defineCommand as defineCommand256 } from "citty";
96978
97386
 
96979
97387
  // src/commands/testimonials/emptyCorpusHints.ts
96980
97388
  function resolveEmptyReason({
@@ -97131,7 +97539,7 @@ function buildListParams(args8) {
97131
97539
  }
97132
97540
  return params;
97133
97541
  }
97134
- var listCommand22 = defineCommand255({
97542
+ var listCommand22 = defineCommand256({
97135
97543
  meta: {
97136
97544
  name: "list",
97137
97545
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -97191,7 +97599,7 @@ var listCommand22 = defineCommand255({
97191
97599
  });
97192
97600
 
97193
97601
  // src/commands/testimonials/search.ts
97194
- import { defineCommand as defineCommand256 } from "citty";
97602
+ import { defineCommand as defineCommand257 } from "citty";
97195
97603
  var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
97196
97604
  function languageBiasHint(results, requestedLanguage) {
97197
97605
  if (requestedLanguage) {
@@ -97270,7 +97678,7 @@ function buildSearchRequest(query, args8) {
97270
97678
  }
97271
97679
  return body;
97272
97680
  }
97273
- var searchCommand3 = defineCommand256({
97681
+ var searchCommand4 = defineCommand257({
97274
97682
  meta: {
97275
97683
  name: "search",
97276
97684
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -97334,7 +97742,7 @@ var searchCommand3 = defineCommand256({
97334
97742
  var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
97335
97743
 
97336
97744
  // src/commands/testimonials/index.ts
97337
- var testimonialsCommand = defineCommand257({
97745
+ var testimonialsCommand = defineCommand258({
97338
97746
  meta: {
97339
97747
  name: "testimonials",
97340
97748
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -97349,17 +97757,17 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
97349
97757
  },
97350
97758
  subCommands: {
97351
97759
  get: getCommand6,
97352
- search: searchCommand3,
97760
+ search: searchCommand4,
97353
97761
  list: listCommand22,
97354
97762
  tags: tagsCommand5
97355
97763
  }
97356
97764
  });
97357
97765
 
97358
97766
  // src/commands/videos/index.ts
97359
- import { defineCommand as defineCommand266 } from "citty";
97767
+ import { defineCommand as defineCommand267 } from "citty";
97360
97768
 
97361
97769
  // src/commands/videos/delete.ts
97362
- import { defineCommand as defineCommand258 } from "citty";
97770
+ import { defineCommand as defineCommand259 } from "citty";
97363
97771
  registerSchema({
97364
97772
  command: "videos.delete",
97365
97773
  description: "Delete a video by ID",
@@ -97373,7 +97781,7 @@ registerSchema({
97373
97781
  }
97374
97782
  }
97375
97783
  });
97376
- var deleteCommand4 = defineCommand258({
97784
+ var deleteCommand4 = defineCommand259({
97377
97785
  meta: {
97378
97786
  name: "delete",
97379
97787
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -97414,7 +97822,7 @@ var deleteCommand4 = defineCommand258({
97414
97822
  });
97415
97823
 
97416
97824
  // src/commands/videos/describe.ts
97417
- import { defineCommand as defineCommand259 } from "citty";
97825
+ import { defineCommand as defineCommand260 } from "citty";
97418
97826
  var DESCRIPTION_HELP2 = "What the clip actually shows, in the words someone would search for it by. This is what `baker videos search` retrieves on.";
97419
97827
  registerSchema({
97420
97828
  command: "videos.describe",
@@ -97431,7 +97839,7 @@ registerSchema({
97431
97839
  full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
97432
97840
  }
97433
97841
  });
97434
- var describeCommand2 = defineCommand259({
97842
+ var describeCommand2 = defineCommand260({
97435
97843
  meta: {
97436
97844
  name: "describe",
97437
97845
  description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
@@ -97493,7 +97901,7 @@ import { constants } from "fs";
97493
97901
  import { copyFile as copyFile2, mkdir as mkdir17, mkdtemp as mkdtemp3, rm as rm10 } from "fs/promises";
97494
97902
  import { tmpdir as tmpdir4 } from "os";
97495
97903
  import path56 from "path";
97496
- import { defineCommand as defineCommand260 } from "citty";
97904
+ import { defineCommand as defineCommand261 } from "citty";
97497
97905
 
97498
97906
  // src/commands/videos/videoPlatforms.ts
97499
97907
  var PLATFORM_DOMAINS = {
@@ -97631,7 +98039,7 @@ function positiveInt(value2) {
97631
98039
  const parsed = typeof value2 === "string" ? Number.parseInt(value2, 10) : Number.NaN;
97632
98040
  return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
97633
98041
  }
97634
- var fetchCommand2 = defineCommand260({
98042
+ var fetchCommand2 = defineCommand261({
97635
98043
  meta: {
97636
98044
  name: "fetch",
97637
98045
  description: "Download a video page to a local .mp4 without adding it to the library. Same downloader as `ingest` (proxy fallback, duration check).\n\nExample: baker videos fetch https://www.youtube.com/watch?v=abc123 --out ./clips"
@@ -97717,7 +98125,7 @@ async function placeInto(outDir, source, stem) {
97717
98125
  }
97718
98126
 
97719
98127
  // src/commands/videos/get.ts
97720
- import { defineCommand as defineCommand261 } from "citty";
98128
+ import { defineCommand as defineCommand262 } from "citty";
97721
98129
  registerSchema({
97722
98130
  command: "videos.get",
97723
98131
  description: "Get a single video by ID",
@@ -97725,7 +98133,7 @@ registerSchema({
97725
98133
  id: { type: "string", description: "Video ID", required: true }
97726
98134
  }
97727
98135
  });
97728
- var getCommand7 = defineCommand261({
98136
+ var getCommand7 = defineCommand262({
97729
98137
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
97730
98138
  args: {
97731
98139
  id: { type: "positional", description: "Video ID", required: false },
@@ -97762,7 +98170,7 @@ var getCommand7 = defineCommand261({
97762
98170
  });
97763
98171
 
97764
98172
  // src/commands/videos/group.ts
97765
- import { defineCommand as defineCommand262 } from "citty";
98173
+ import { defineCommand as defineCommand263 } from "citty";
97766
98174
  registerSchema({
97767
98175
  command: "videos.group",
97768
98176
  description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
@@ -97771,7 +98179,7 @@ registerSchema({
97771
98179
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
97772
98180
  }
97773
98181
  });
97774
- var groupCommand2 = defineCommand262({
98182
+ var groupCommand2 = defineCommand263({
97775
98183
  meta: {
97776
98184
  name: "group",
97777
98185
  description: "List every asset that arrived in the same set as this clip \u2014 the other slides of the Instagram post it came from, stills included. A carousel is authored to be read in order, so a clip pulled out of one is usually missing half its meaning. Example: baker videos group <videoId>"
@@ -97794,7 +98202,7 @@ var groupCommand2 = defineCommand262({
97794
98202
  import { mkdtemp as mkdtemp4, rm as rm11, stat as stat12 } from "fs/promises";
97795
98203
  import { tmpdir as tmpdir5 } from "os";
97796
98204
  import path57 from "path";
97797
- import { defineCommand as defineCommand263 } from "citty";
98205
+ import { defineCommand as defineCommand264 } from "citty";
97798
98206
 
97799
98207
  // src/lib/streamUpload.ts
97800
98208
  import { createHash as createHash3 } from "crypto";
@@ -97847,7 +98255,7 @@ registerSchema({
97847
98255
  "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
97848
98256
  }
97849
98257
  });
97850
- var ingestCommand2 = defineCommand263({
98258
+ var ingestCommand2 = defineCommand264({
97851
98259
  meta: {
97852
98260
  name: "ingest",
97853
98261
  description: "Add a video to the library from a URL. A direct file URL is handed straight to Baker, which fetches it. A page URL (YouTube, TikTok, Vimeo, Instagram) is downloaded here first, then uploaded \u2014 and a direct URL that Baker cannot fetch falls back to that same path automatically.\n\nExample: baker videos ingest https://www.youtube.com/watch?v=abc123"
@@ -98109,7 +98517,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
98109
98517
  }
98110
98518
 
98111
98519
  // src/commands/videos/search.ts
98112
- import { defineCommand as defineCommand264 } from "citty";
98520
+ import { defineCommand as defineCommand265 } from "citty";
98113
98521
  registerSchema({
98114
98522
  command: "videos.search",
98115
98523
  description: "Search videos by text query. Only returns ready videos.",
@@ -98119,7 +98527,7 @@ registerSchema({
98119
98527
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
98120
98528
  }
98121
98529
  });
98122
- var searchCommand4 = defineCommand264({
98530
+ var searchCommand5 = defineCommand265({
98123
98531
  meta: {
98124
98532
  name: "search",
98125
98533
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -98171,7 +98579,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
98171
98579
  // src/commands/videos/upload.ts
98172
98580
  import { stat as stat13 } from "fs/promises";
98173
98581
  import { basename as basename4 } from "path";
98174
- import { defineCommand as defineCommand265 } from "citty";
98582
+ import { defineCommand as defineCommand266 } from "citty";
98175
98583
  registerSchema({
98176
98584
  command: "videos.upload",
98177
98585
  description: "Upload a video to Baker \u2014 a local file (via Mux direct upload) or a remote http(s) URL.",
@@ -98198,7 +98606,7 @@ registerSchema({
98198
98606
  function isRemoteUrl3(value2) {
98199
98607
  return /^https?:\/\//i.test(value2);
98200
98608
  }
98201
- var uploadCommand2 = defineCommand265({
98609
+ var uploadCommand2 = defineCommand266({
98202
98610
  meta: {
98203
98611
  name: "upload",
98204
98612
  description: "Upload a video to Baker \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: auto-detects content type and uploads via Mux direct upload.\nRemote: hands off to `videos ingest` (direct fetch, or download-then-upload for a YouTube/TikTok/Vimeo page).\n\nExamples:\n baker videos upload ./demo.mp4\n baker videos upload https://www.youtube.com/watch?v=abc123"
@@ -98267,7 +98675,7 @@ var uploadCommand2 = defineCommand265({
98267
98675
  });
98268
98676
 
98269
98677
  // src/commands/videos/index.ts
98270
- var videosCommand = defineCommand266({
98678
+ var videosCommand = defineCommand267({
98271
98679
  meta: {
98272
98680
  name: "videos",
98273
98681
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, fetch, describe, delete, tags.
@@ -98287,7 +98695,7 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
98287
98695
  subCommands: {
98288
98696
  get: getCommand7,
98289
98697
  group: groupCommand2,
98290
- search: searchCommand4,
98698
+ search: searchCommand5,
98291
98699
  upload: uploadCommand2,
98292
98700
  ingest: ingestCommand2,
98293
98701
  fetch: fetchCommand2,
@@ -98298,10 +98706,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
98298
98706
  });
98299
98707
 
98300
98708
  // src/commands/winning-ads/index.ts
98301
- import { defineCommand as defineCommand277 } from "citty";
98709
+ import { defineCommand as defineCommand278 } from "citty";
98302
98710
 
98303
98711
  // src/commands/winning-ads/advertisers.ts
98304
- import { defineCommand as defineCommand267 } from "citty";
98712
+ import { defineCommand as defineCommand268 } from "citty";
98305
98713
 
98306
98714
  // src/commands/winning-ads/shared.ts
98307
98715
  function splitList4(value2) {
@@ -98354,7 +98762,7 @@ function advertiserNormalizer(record, full) {
98354
98762
  last_synced_at: record.last_synced_at ?? null
98355
98763
  };
98356
98764
  }
98357
- var advertisersCommand2 = defineCommand267({
98765
+ var advertisersCommand2 = defineCommand268({
98358
98766
  meta: {
98359
98767
  name: "advertisers",
98360
98768
  description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
@@ -98412,7 +98820,7 @@ var advertisersCommand2 = defineCommand267({
98412
98820
  });
98413
98821
 
98414
98822
  // src/commands/winning-ads/content.ts
98415
- import { defineCommand as defineCommand268 } from "citty";
98823
+ import { defineCommand as defineCommand269 } from "citty";
98416
98824
  registerSchema({
98417
98825
  command: "winning-ads.content",
98418
98826
  description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Static and carousel ads return the text drawn into the image with the job each piece does (headline, benefit, cta\u2026), the card it sits on, and their format_pattern. Add --full for how it is built (composition, colours, CTA, markers), how it persuades (awareness, mechanism, pains, desires, proof, objections), what it offers (offer type, price, guarantee, urgency), plus speech, pacing and soundtrack for video.",
@@ -98425,7 +98833,7 @@ registerSchema({
98425
98833
  }
98426
98834
  }
98427
98835
  });
98428
- var contentCommand = defineCommand268({
98836
+ var contentCommand = defineCommand269({
98429
98837
  meta: {
98430
98838
  name: "content",
98431
98839
  description: "Read the transcript + on-screen text + copy of one winning ad; --full adds its build, persuasion and offer. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -98474,7 +98882,7 @@ var contentCommand = defineCommand268({
98474
98882
  });
98475
98883
 
98476
98884
  // src/commands/winning-ads/feed.ts
98477
- import { defineCommand as defineCommand269 } from "citty";
98885
+ import { defineCommand as defineCommand270 } from "citty";
98478
98886
  function buildFeedParams(input) {
98479
98887
  const params = {};
98480
98888
  const advertiser = splitList4(input.advertiser);
@@ -98526,7 +98934,7 @@ registerSchema({
98526
98934
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
98527
98935
  }
98528
98936
  });
98529
- var feedCommand2 = defineCommand269({
98937
+ var feedCommand2 = defineCommand270({
98530
98938
  meta: {
98531
98939
  name: "feed",
98532
98940
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -98611,7 +99019,7 @@ var feedCommand2 = defineCommand269({
98611
99019
  });
98612
99020
 
98613
99021
  // src/commands/winning-ads/follow.ts
98614
- import { defineCommand as defineCommand270 } from "citty";
99022
+ import { defineCommand as defineCommand271 } from "citty";
98615
99023
  var PLATFORMS = adLibraryPlatformSchema.options;
98616
99024
  registerSchema({
98617
99025
  command: "winning-ads.follow",
@@ -98626,7 +99034,7 @@ registerSchema({
98626
99034
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
98627
99035
  }
98628
99036
  });
98629
- var followCommand = defineCommand270({
99037
+ var followCommand = defineCommand271({
98630
99038
  meta: {
98631
99039
  name: "follow",
98632
99040
  description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks every platform we can resolve. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -98673,7 +99081,7 @@ var followCommand = defineCommand270({
98673
99081
  });
98674
99082
 
98675
99083
  // src/commands/winning-ads/follow-competitors.ts
98676
- import { defineCommand as defineCommand271 } from "citty";
99084
+ import { defineCommand as defineCommand272 } from "citty";
98677
99085
  var PLATFORMS2 = adLibraryPlatformSchema.options;
98678
99086
  var BATCH_TIMEOUT_MS = 3e5;
98679
99087
  function buildFollowBatchBody(input) {
@@ -98706,7 +99114,7 @@ registerSchema({
98706
99114
  }
98707
99115
  }
98708
99116
  });
98709
- var followCompetitorsCommand = defineCommand271({
99117
+ var followCompetitorsCommand = defineCommand272({
98710
99118
  meta: {
98711
99119
  name: "follow-competitors",
98712
99120
  description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
@@ -98781,7 +99189,7 @@ var followCompetitorsCommand = defineCommand271({
98781
99189
  });
98782
99190
 
98783
99191
  // src/commands/winning-ads/following.ts
98784
- import { defineCommand as defineCommand272 } from "citty";
99192
+ import { defineCommand as defineCommand273 } from "citty";
98785
99193
  registerSchema({
98786
99194
  command: "winning-ads.following",
98787
99195
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts. A brand still adding has counts that are a lie in progress; one with `discovery_failed` has counts that are short because we couldn't finish looking, which is not the same as it running no ads.",
@@ -98835,7 +99243,7 @@ function followingNormalizer(record, full) {
98835
99243
  platforms: Array.isArray(record.platforms) ? record.platforms : []
98836
99244
  };
98837
99245
  }
98838
- var followingCommand = defineCommand272({
99246
+ var followingCommand = defineCommand273({
98839
99247
  meta: {
98840
99248
  name: "following",
98841
99249
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. A brand's counts are only final once it is ready. Example: baker winning-ads following --output md"
@@ -98871,7 +99279,7 @@ var followingCommand = defineCommand272({
98871
99279
  });
98872
99280
 
98873
99281
  // src/commands/winning-ads/search.ts
98874
- import { defineCommand as defineCommand273 } from "citty";
99282
+ import { defineCommand as defineCommand274 } from "citty";
98875
99283
  registerSchema({
98876
99284
  command: "winning-ads.search",
98877
99285
  description: "Search the ad-dna corpus of scored winning ads, the company's followed brands first. Returns a lean shortlist (advertiser, summary, scores, days_active/active/winner_category as proof, media_url, and visual/container/mechanism when known) to pick a reference to reproduce. Each search is a paid reranker + judge call: one strong search, at most one adjusted retry. A value outside a closed list (--platform, --format, --winner-category, --awareness, --format-pattern, --sort) is refused with error.fix.validValues.",
@@ -99149,7 +99557,7 @@ function writeSearchResult(data, opts) {
99149
99557
  writeOutput({ ok: true, data: rawResults, ...withHints2 }, opts.output, opts.fields, opts.full, winningAdNormalizer);
99150
99558
  }
99151
99559
  var SEARCH_TIMEOUT_MS = 12e4;
99152
- var searchCommand5 = defineCommand273({
99560
+ var searchCommand6 = defineCommand274({
99153
99561
  meta: {
99154
99562
  name: "search",
99155
99563
  description: "Search winning reference ads, the company's followed brands first. Start here: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --exclude-advertiser adv_123 --output md. A set of references for N concepts in one call: add --diverse --limit N. The best proven ad in one container from any category: --format-pattern comparison_table (no query needed; add --sort longest_running to order by staying power). Each search is a paid reranker + judge call: one strong search, at most one adjusted retry."
@@ -99275,7 +99683,7 @@ var searchCommand5 = defineCommand273({
99275
99683
  });
99276
99684
 
99277
99685
  // src/commands/winning-ads/seeds.ts
99278
- import { defineCommand as defineCommand274 } from "citty";
99686
+ import { defineCommand as defineCommand275 } from "citty";
99279
99687
  function leanRow(r) {
99280
99688
  return {
99281
99689
  key: r.key,
@@ -99303,7 +99711,7 @@ function makeSeedCommand(opts) {
99303
99711
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
99304
99712
  }
99305
99713
  });
99306
- return defineCommand274({
99714
+ return defineCommand275({
99307
99715
  meta: { name: opts.name, description: opts.description },
99308
99716
  args: {
99309
99717
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -99352,7 +99760,7 @@ var formatsCommand = makeSeedCommand({
99352
99760
  });
99353
99761
 
99354
99762
  // src/commands/winning-ads/unfollow.ts
99355
- import { defineCommand as defineCommand275 } from "citty";
99763
+ import { defineCommand as defineCommand276 } from "citty";
99356
99764
  registerSchema({
99357
99765
  command: "winning-ads.unfollow",
99358
99766
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -99360,7 +99768,7 @@ registerSchema({
99360
99768
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
99361
99769
  }
99362
99770
  });
99363
- var unfollowCommand = defineCommand275({
99771
+ var unfollowCommand = defineCommand276({
99364
99772
  meta: {
99365
99773
  name: "unfollow",
99366
99774
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -99381,7 +99789,7 @@ var unfollowCommand = defineCommand275({
99381
99789
  });
99382
99790
 
99383
99791
  // src/commands/winning-ads/winners.ts
99384
- import { defineCommand as defineCommand276 } from "citty";
99792
+ import { defineCommand as defineCommand277 } from "citty";
99385
99793
  registerSchema({
99386
99794
  command: "winning-ads.winners",
99387
99795
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -99391,7 +99799,7 @@ registerSchema({
99391
99799
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
99392
99800
  }
99393
99801
  });
99394
- var winnersCommand = defineCommand276({
99802
+ var winnersCommand = defineCommand277({
99395
99803
  meta: {
99396
99804
  name: "winners",
99397
99805
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -99441,7 +99849,7 @@ var winnersCommand = defineCommand276({
99441
99849
  });
99442
99850
 
99443
99851
  // src/commands/winning-ads/index.ts
99444
- var winningAdsCommand = defineCommand277({
99852
+ var winningAdsCommand = defineCommand278({
99445
99853
  meta: {
99446
99854
  name: "winning-ads",
99447
99855
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -99477,7 +99885,7 @@ Examples:
99477
99885
  Full guide: __tooling__/docs/tools/baker/winning-ads.md`
99478
99886
  },
99479
99887
  subCommands: {
99480
- search: searchCommand5,
99888
+ search: searchCommand6,
99481
99889
  advertisers: advertisersCommand2,
99482
99890
  follow: followCommand,
99483
99891
  "follow-competitors": followCompetitorsCommand,
@@ -99509,7 +99917,7 @@ function getCliVersion() {
99509
99917
  }
99510
99918
 
99511
99919
  // src/cli.ts
99512
- var main = defineCommand278({
99920
+ var main = defineCommand279({
99513
99921
  meta: {
99514
99922
  name: "baker",
99515
99923
  version: getCliVersion(),