@koda-sl/baker-cli 0.383.0-dev.2caa8a15e → 0.383.0-dev.9779e8b49

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
@@ -70,7 +70,7 @@ import {
70
70
  validateCanvasDeep,
71
71
  withoutCastName,
72
72
  ytDlpBlockSignal
73
- } from "./chunk-CHJOTJTZ.js";
73
+ } from "./chunk-ZW6DHSDG.js";
74
74
  import {
75
75
  NAME_CONTAINS_ARG,
76
76
  csvOrJson,
@@ -143,7 +143,7 @@ import {
143
143
  } from "./chunk-HJEMOSPH.js";
144
144
 
145
145
  // src/cli.ts
146
- import { defineCommand as defineCommand248, runMain } from "citty";
146
+ import { defineCommand as defineCommand255, runMain } from "citty";
147
147
 
148
148
  // src/cache-flag.ts
149
149
  var NO_CACHE_ARG = {
@@ -299,7 +299,6 @@ var chatActionDraftOpViewSchema = z.object({
299
299
  targetActionId: z.string().optional(),
300
300
  tempId: z.string().optional(),
301
301
  tags: z.array(z.string()).optional(),
302
- priority: actionPrioritySchema.nullable().optional(),
303
302
  reason: z.string().optional(),
304
303
  note: z.string().optional(),
305
304
  name: z.string().optional(),
@@ -976,9 +975,6 @@ var OBJECTIVE_CAMPAIGN_TYPES = {
976
975
  ENGAGEMENT: ["SPONSORED_UPDATES", "DYNAMIC"]
977
976
  };
978
977
  var STAGEABLE_CREATE_STATUSES = ["DRAFT", "ACTIVE", "PAUSED"];
979
- var CAMPAIGN_GROUP_CREATE_STATUSES = ["DRAFT", "ACTIVE"];
980
- var CAMPAIGN_GROUP_STATUSES = ["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "DRAFT"];
981
- var POLITICAL_INTENTS = ["NOT_DECLARED", "NOT_POLITICAL", "POLITICAL"];
982
978
  var CREATIVE_INTENDED_STATUSES = ["ACTIVE", "PAUSED", "DRAFT"];
983
979
  var CTA_TYPES = [
984
980
  "APPLY",
@@ -1109,26 +1105,17 @@ var entityStatusSchema = z3.enum(["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "D
1109
1105
  function currencyMinimums(currencyCode) {
1110
1106
  return CURRENCY_MINIMUMS[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM;
1111
1107
  }
1112
- var campaignGroupFieldsSchema = z3.object({
1108
+ var campaignGroupCreateSchema = z3.object({
1113
1109
  name: z3.string().min(1).max(LINKEDIN_LIMITS.campaignGroup.nameMax),
1114
1110
  runSchedule: runScheduleSchema,
1115
1111
  totalBudget: moneySchema.optional(),
1116
- status: z3.enum(CAMPAIGN_GROUP_CREATE_STATUSES).default("DRAFT")
1117
- });
1118
- var campaignGroupCreateSchema = campaignGroupFieldsSchema.superRefine((p, ctx) => {
1119
- if (p.totalBudget && !p.runSchedule.end) {
1120
- ctx.addIssue({
1121
- code: "custom",
1122
- path: ["runSchedule", "end"],
1123
- message: "a group with totalBudget needs runSchedule.end"
1124
- });
1125
- }
1112
+ status: stageableStatusSchema.default("DRAFT")
1126
1113
  });
1127
1114
  var campaignGroupUpdateSchema = z3.object({
1128
1115
  name: z3.string().min(1).max(LINKEDIN_LIMITS.campaignGroup.nameMax).optional(),
1129
1116
  runSchedule: runScheduleSchema.optional(),
1130
1117
  totalBudget: moneySchema.optional(),
1131
- status: z3.enum(CAMPAIGN_GROUP_STATUSES).optional()
1118
+ status: entityStatusSchema.optional()
1132
1119
  }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1133
1120
  var campaignFields = {
1134
1121
  name: z3.string().min(1).max(LINKEDIN_LIMITS.campaign.nameMax),
@@ -1147,7 +1134,6 @@ var campaignFields = {
1147
1134
  connectedTelevisionOnly: z3.boolean().default(false),
1148
1135
  creativeSelection: z3.enum(CREATIVE_SELECTIONS).optional(),
1149
1136
  associatedEntity: z3.string().regex(ORGANIZATION_URN_REGEX).optional(),
1150
- politicalIntent: z3.enum(POLITICAL_INTENTS).default("NOT_DECLARED"),
1151
1137
  status: stageableStatusSchema.default("DRAFT")
1152
1138
  };
1153
1139
  function validateCampaignBudgets(p, ctx, { requireBudget }) {
@@ -1205,7 +1191,6 @@ var campaignUpdateSchema = z3.object({
1205
1191
  offsiteDeliveryEnabled: z3.boolean().optional(),
1206
1192
  connectedTelevisionOnly: z3.boolean().optional(),
1207
1193
  creativeSelection: z3.enum(CREATIVE_SELECTIONS).optional(),
1208
- politicalIntent: z3.enum(POLITICAL_INTENTS).optional(),
1209
1194
  status: entityStatusSchema.optional(),
1210
1195
  // Create-only on LinkedIn — a live campaign can't change these in place.
1211
1196
  // Accepted here only so `draft amend` can carry them into a staged CREATE
@@ -1831,17 +1816,6 @@ var linkedinDraftOpInputSchema = z3.discriminatedUnion("kind", [
1831
1816
  updateOp("leadForm.update", leadFormUpdateSchema)
1832
1817
  ]);
1833
1818
 
1834
- // ../api/src/ads-linkedin/search-statuses.ts
1835
- var LINKEDIN_SEARCH_STATUSES = {
1836
- campaignGroups: ["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "DRAFT", "PENDING_DELETION", "REMOVED"],
1837
- campaigns: ["ACTIVE", "PAUSED", "ARCHIVED", "COMPLETED", "CANCELED", "DRAFT", "PENDING_DELETION", "REMOVED"],
1838
- creatives: ["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "DRAFT", "PENDING_DELETION", "REMOVED"]
1839
- };
1840
- function unsupportedLinkedinSearchStatuses(level, statuses) {
1841
- const accepted = LINKEDIN_SEARCH_STATUSES[level];
1842
- return statuses.filter((status) => !accepted.includes(status));
1843
- }
1844
-
1845
1819
  // ../api/src/ads-linkedin/wire.ts
1846
1820
  import { z as z7 } from "zod";
1847
1821
 
@@ -4560,6 +4534,9 @@ var chatChangeTypeSchema = z13.enum([
4560
4534
  "image",
4561
4535
  "video",
4562
4536
  "followed-advertiser",
4537
+ // A connected social account feeding the image and video libraries, keyed
4538
+ // on `platform:handle`. Immediate — see convex/mediaSources/mutations.ts.
4539
+ "media-source",
4563
4540
  // A landing page saved to the reference library, keyed on the page.
4564
4541
  "landing-reference",
4565
4542
  // One Studio batch — the takes of one brief, as a set. See convex/lib/validators.ts.
@@ -6424,6 +6401,10 @@ var imageSourceSchema = z20.enum([
6424
6401
  "google_testimonial",
6425
6402
  "trustpilot_testimonial",
6426
6403
  "instagram",
6404
+ "tiktok",
6405
+ "linkedin",
6406
+ "facebook",
6407
+ "google_drive",
6427
6408
  "pexels",
6428
6409
  "pixabay",
6429
6410
  // Retired stock provider — kept so existing library rows stay filterable.
@@ -8234,7 +8215,9 @@ var videoSourceSchema = z30.enum([
8234
8215
  "bluesky",
8235
8216
  "dailymotion",
8236
8217
  "facebook",
8218
+ "google_drive",
8237
8219
  "instagram",
8220
+ "linkedin",
8238
8221
  "loom",
8239
8222
  "newgrounds",
8240
8223
  "ok",
@@ -12128,24 +12111,6 @@ function adCopyHints(payload) {
12128
12111
  return tells.slice(0, MAX_HINTS).map(hintFor);
12129
12112
  }
12130
12113
 
12131
- // src/commands/ads/hints.ts
12132
- function backendHints(envelope) {
12133
- const hints2 = envelope.hints;
12134
- return Array.isArray(hints2) ? hints2.filter((hint) => typeof hint === "string") : [];
12135
- }
12136
- function withHints(envelope, extra) {
12137
- const hints2 = [...backendHints(envelope), ...extra];
12138
- return hints2.length > 0 ? { ...envelope, hints: hints2 } : envelope;
12139
- }
12140
- function hintLines(envelope) {
12141
- return backendHints(envelope).map((hint) => `
12142
- > ${hint}`).join("");
12143
- }
12144
- function splitHints(response) {
12145
- const { hints: hints2, ...data } = response;
12146
- return { data, hints: hints2 ?? [] };
12147
- }
12148
-
12149
12114
  // src/commands/ads/google/copy-list-usage.ts
12150
12115
  var AD_COPY_LIST_USAGE = `repeat the flag once per item, or pass one comma-separated list and wrap an item that contains a comma in double quotes: '"Fast, free quotes",Book today'. A flag given once is read as a list, so a lone item with a comma needs its double quotes too: '"Fast, free quotes"'`;
12151
12116
  var COPY_LIST_FLAG = {
@@ -12153,9 +12118,9 @@ var COPY_LIST_FLAG = {
12153
12118
  descriptions: "--descriptions",
12154
12119
  longHeadlines: "--long-headlines"
12155
12120
  };
12156
- function valueAt(root, path48) {
12121
+ function valueAt(root, path49) {
12157
12122
  let node = root;
12158
- for (const key of path48) {
12123
+ for (const key of path49) {
12159
12124
  if (node === null || typeof node !== "object") return void 0;
12160
12125
  node = node[key];
12161
12126
  }
@@ -12388,11 +12353,11 @@ function parseKeywordEntry(raw, defaultMatch) {
12388
12353
  function rawTextEntries(value, rawArgs) {
12389
12354
  return repeatedValues(rawArgs, "text", value).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
12390
12355
  }
12391
- function rawFileEntries(path48) {
12392
- if (typeof path48 !== "string" || path48.length === 0) {
12356
+ function rawFileEntries(path49) {
12357
+ if (typeof path49 !== "string" || path49.length === 0) {
12393
12358
  return [];
12394
12359
  }
12395
- return readFileSync2(path48, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
12360
+ return readFileSync2(path49, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
12396
12361
  }
12397
12362
  function keywordEntries(args5, rawArgs) {
12398
12363
  const defaultMatch = typeof args5["match-type"] === "string" ? args5["match-type"].toUpperCase() : void 0;
@@ -12415,14 +12380,14 @@ function keywordEntries(args5, rawArgs) {
12415
12380
  }
12416
12381
  return entries;
12417
12382
  }
12418
- function loadJsonFileArg(path48) {
12419
- if (typeof path48 !== "string" || path48.length === 0) {
12383
+ function loadJsonFileArg(path49) {
12384
+ if (typeof path49 !== "string" || path49.length === 0) {
12420
12385
  return {};
12421
12386
  }
12422
- const inline = path48.trimStart().startsWith("{");
12423
- const source = inline ? "inline JSON" : path48;
12387
+ const inline = path49.trimStart().startsWith("{");
12388
+ const source = inline ? "inline JSON" : path49;
12424
12389
  try {
12425
- const parsed = JSON.parse(inline ? path48 : readFileSync2(path48, "utf8"));
12390
+ const parsed = JSON.parse(inline ? path49 : readFileSync2(path49, "utf8"));
12426
12391
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
12427
12392
  failWriteValidation(`${source} must contain a JSON object`);
12428
12393
  }
@@ -12510,7 +12475,7 @@ async function stageGoogleOp(raw, hints2) {
12510
12475
  const chatId = requireChatId();
12511
12476
  const response = await apiPost("/api/ads/google/draft/stage", { chatId, op: preflight.data });
12512
12477
  const allHints = [...noopHints(response.data), ...hints2 ?? [], ...adCopyHints(preflight.data)];
12513
- writeJsonEnvelope(withHints(response, allHints));
12478
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
12514
12479
  } catch (err) {
12515
12480
  handleGoogleError(err);
12516
12481
  }
@@ -12548,7 +12513,7 @@ async function stageGoogleOps(rawOps, hints2) {
12548
12513
  const chatId = requireChatId();
12549
12514
  const response = await apiPost("/api/ads/google/draft/stage-batch", { chatId, ops });
12550
12515
  const allHints = [...skippedHints(response.data.skipped ?? [], ops.length), ...hints2 ?? [], ...adCopyHints(ops)];
12551
- writeJsonEnvelope(withHints(response, allHints));
12516
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
12552
12517
  } catch (err) {
12553
12518
  handleGoogleError(err);
12554
12519
  }
@@ -12559,10 +12524,10 @@ async function stageUpdate(kind, customerId, target2, payload, hints2) {
12559
12524
  async function stageTarget(kind, customerId, target2, hints2) {
12560
12525
  await stageGoogleOp({ kind, customerId, target: target2 }, hints2);
12561
12526
  }
12562
- async function draftAction(path48, body, chat) {
12527
+ async function draftAction(path49, body, chat) {
12563
12528
  try {
12564
12529
  const chatId = resolveChatId(chat);
12565
- const response = await apiPost(path48, { chatId, ...body });
12530
+ const response = await apiPost(path49, { chatId, ...body });
12566
12531
  writeJsonEnvelope(response);
12567
12532
  } catch (err) {
12568
12533
  handleGoogleError(err);
@@ -12576,7 +12541,7 @@ async function draftListAction(json, chat) {
12576
12541
  writeJsonEnvelope(response);
12577
12542
  return;
12578
12543
  }
12579
- process.stdout.write(`${renderDraftStatus(response.data)}${hintLines(response)}
12544
+ process.stdout.write(`${renderDraftStatus(response.data)}
12580
12545
  `);
12581
12546
  } catch (err) {
12582
12547
  handleGoogleError(err);
@@ -14980,14 +14945,14 @@ function mappingRowProblem(row) {
14980
14945
 
14981
14946
  // ../api/src/event-sources/template.ts
14982
14947
  var MENTION = /\{\{\s*([^{}]+?)\s*\}\}/g;
14983
- function mentionOf(path48) {
14984
- return `{{${path48}}}`;
14948
+ function mentionOf(path49) {
14949
+ return `{{${path49}}}`;
14985
14950
  }
14986
14951
  function templateMentions(template) {
14987
14952
  const seen = [];
14988
14953
  for (const match of template.matchAll(MENTION)) {
14989
- const path48 = (match[1] ?? "").trim();
14990
- if (path48 !== "" && !seen.includes(path48)) seen.push(path48);
14954
+ const path49 = (match[1] ?? "").trim();
14955
+ if (path49 !== "" && !seen.includes(path49)) seen.push(path49);
14991
14956
  }
14992
14957
  return seen;
14993
14958
  }
@@ -18342,17 +18307,17 @@ function ignoredColumnHints(columns) {
18342
18307
  `Ignored ${columns.length} column(s) that are not part of a conversion: ${columns.join(", ")}. A conversion carries an order id, a date, and something to match on: a click id (gclid / gbraid / wbraid), an email, a phone, or all four of givenName + familyName + country + postalCode. Optionally a value + currency, conversionCount, and eventSource (WEB / APP / IN_STORE / PHONE / OTHER). A column named just \`Name\` is ignored on purpose \u2014 Google needs the first and last name separately, so split it before re-running.`
18343
18308
  ];
18344
18309
  }
18345
- function readRowsFile(path48) {
18346
- const inline = path48.trimStart().startsWith("[") || path48.trimStart().startsWith("{");
18310
+ function readRowsFile(path49) {
18311
+ const inline = path49.trimStart().startsWith("[") || path49.trimStart().startsWith("{");
18347
18312
  let text2;
18348
18313
  try {
18349
- text2 = inline ? path48 : readFileSync4(path48, "utf8");
18314
+ text2 = inline ? path49 : readFileSync4(path49, "utf8");
18350
18315
  } catch (err) {
18351
- failWriteValidation(`could not read ${path48}: ${err instanceof Error ? err.message : String(err)}`);
18316
+ failWriteValidation(`could not read ${path49}: ${err instanceof Error ? err.message : String(err)}`);
18352
18317
  }
18353
18318
  const parsed = parseConversionRowsText(text2);
18354
18319
  if (!parsed.ok) {
18355
- failWriteValidation(`${inline ? "the conversions passed inline" : path48}: ${parsed.error}`);
18320
+ failWriteValidation(`${inline ? "the conversions passed inline" : path49}: ${parsed.error}`);
18356
18321
  }
18357
18322
  return { rows: parsed.rows, ignoredColumns: parsed.ignoredColumns };
18358
18323
  }
@@ -20110,6 +20075,10 @@ Full guide: __tooling__/docs/tools/baker/ads-google.md`
20110
20075
  // src/commands/ads/linkedin/index.ts
20111
20076
  import { defineCommand as defineCommand53 } from "citty";
20112
20077
 
20078
+ // src/commands/ads/linkedin/write-shared.ts
20079
+ import { createHash } from "crypto";
20080
+ import { readFileSync as readFileSync5 } from "fs";
20081
+
20113
20082
  // src/commands/ads/linkedin/shared.ts
20114
20083
  var DAY_MS2 = 864e5;
20115
20084
  function handleLinkedinError(err) {
@@ -20202,48 +20171,18 @@ function daysAgoIso2(days) {
20202
20171
  function csvOrJson2(args5) {
20203
20172
  return args5.output ?? "json";
20204
20173
  }
20205
- function statusesArgDescription(level) {
20206
- return `CSV of statuses (${LINKEDIN_SEARCH_STATUSES[level].join(",")})`;
20207
- }
20208
- function statusFilterError(level, statuses) {
20209
- const unsupported = unsupportedLinkedinSearchStatuses(level, statuses);
20210
- if (unsupported.length === 0) {
20211
- return null;
20212
- }
20213
- const accepted = LINKEDIN_SEARCH_STATUSES[level].join(",");
20214
- return {
20215
- ok: false,
20216
- error: {
20217
- code: "INVALID_STATUS",
20218
- message: `LinkedIn does not accept status ${unsupported.join(", ")} for this listing.`,
20219
- fix: {
20220
- action: "retry_with_flag",
20221
- explanation: `Re-run with --statuses from ${accepted}, or --all-statuses to drop the filter.`
20222
- },
20223
- retryable: false
20224
- }
20225
- };
20226
- }
20227
- function resolveStatusFilter(args5, level) {
20174
+ function resolveStatusFilter(args5) {
20228
20175
  if (args5["all-statuses"]) {
20229
20176
  return void 0;
20230
20177
  }
20231
20178
  const explicit = args5.statuses;
20232
- if (!explicit) {
20233
- return ["ACTIVE"];
20234
- }
20235
- const statuses = explicit.split(",").map((s) => s.trim().toUpperCase()).filter(Boolean);
20236
- const error = statusFilterError(level, statuses);
20237
- if (error) {
20238
- writeAdsJson(error);
20239
- process.exit(1);
20179
+ if (explicit) {
20180
+ return explicit.split(",").map((s) => s.trim().toUpperCase()).filter(Boolean);
20240
20181
  }
20241
- return statuses;
20182
+ return ["ACTIVE"];
20242
20183
  }
20243
20184
 
20244
20185
  // src/commands/ads/linkedin/write-shared.ts
20245
- import { createHash } from "crypto";
20246
- import { readFileSync as readFileSync5 } from "fs";
20247
20186
  var TOTAL_BUDGET_HELP = "Lifetime budget \u2014 on something already running it must exceed spend to date, or LinkedIn rejects it";
20248
20187
  function bareAccountId(args5) {
20249
20188
  const raw = resolveAccountIdArg2(args5);
@@ -20253,19 +20192,19 @@ function failWriteValidation2(message) {
20253
20192
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
20254
20193
  process.exit(1);
20255
20194
  }
20256
- function loadJsonFileArg2(path48) {
20257
- if (typeof path48 !== "string" || path48.length === 0) {
20195
+ function loadJsonFileArg2(path49) {
20196
+ if (typeof path49 !== "string" || path49.length === 0) {
20258
20197
  return {};
20259
20198
  }
20260
20199
  try {
20261
- const parsed = JSON.parse(readFileSync5(path48, "utf8"));
20200
+ const parsed = JSON.parse(readFileSync5(path49, "utf8"));
20262
20201
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
20263
- failWriteValidation2(`${path48} must contain a JSON object`);
20202
+ failWriteValidation2(`${path49} must contain a JSON object`);
20264
20203
  }
20265
20204
  return parsed;
20266
20205
  } catch (err) {
20267
20206
  if (err instanceof SyntaxError) {
20268
- failWriteValidation2(`${path48} is not valid JSON: ${err.message}`);
20207
+ failWriteValidation2(`${path49} is not valid JSON: ${err.message}`);
20269
20208
  }
20270
20209
  throw err;
20271
20210
  }
@@ -20350,15 +20289,15 @@ function parseLocaleFlag(value) {
20350
20289
  }
20351
20290
  return { language: match[1], country: match[2].toUpperCase() };
20352
20291
  }
20353
- function loadTargetingFileArg(path48) {
20354
- if (typeof path48 !== "string" || path48.length === 0) {
20292
+ function loadTargetingFileArg(path49) {
20293
+ if (typeof path49 !== "string" || path49.length === 0) {
20355
20294
  return void 0;
20356
20295
  }
20357
- const parsed = loadJsonFileArg2(path48);
20296
+ const parsed = loadJsonFileArg2(path49);
20358
20297
  const criteria = parsed.targetingCriteria ?? parsed;
20359
20298
  if (!criteria.include) {
20360
20299
  failWriteValidation2(
20361
- `${path48} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
20300
+ `${path49} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
20362
20301
  );
20363
20302
  }
20364
20303
  return criteria;
@@ -20393,14 +20332,14 @@ function parseCsvLine(line) {
20393
20332
  cells.push(current);
20394
20333
  return cells.map((cell3) => cell3.trim());
20395
20334
  }
20396
- function parseListFileArg(path48, maxRows) {
20397
- if (typeof path48 !== "string" || path48.length === 0) {
20335
+ function parseListFileArg(path49, maxRows) {
20336
+ if (typeof path49 !== "string" || path49.length === 0) {
20398
20337
  return void 0;
20399
20338
  }
20400
- const raw = readFileSync5(path48, "utf8");
20339
+ const raw = readFileSync5(path49, "utf8");
20401
20340
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
20402
20341
  if (lines.length < 2) {
20403
- failWriteValidation2(`${path48} needs a header row and at least one data row`);
20342
+ failWriteValidation2(`${path49} needs a header row and at least one data row`);
20404
20343
  }
20405
20344
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
20406
20345
  const rows = [];
@@ -20419,7 +20358,7 @@ function parseListFileArg(path48, maxRows) {
20419
20358
  }
20420
20359
  }
20421
20360
  if (rows.length > maxRows) {
20422
- failWriteValidation2(`${path48} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
20361
+ failWriteValidation2(`${path49} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
20423
20362
  }
20424
20363
  return { columns, rows };
20425
20364
  }
@@ -20451,7 +20390,7 @@ async function stageOp(raw, hints2) {
20451
20390
  op: preflight.data
20452
20391
  });
20453
20392
  const allHints = [...batchNudge(response.data), ...hints2 ?? [], ...adCopyHints(preflight.data)];
20454
- writeJsonEnvelope(withHints(response, allHints));
20393
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
20455
20394
  } catch (err) {
20456
20395
  handleLinkedinError(err);
20457
20396
  }
@@ -20478,7 +20417,7 @@ async function stageLinkedinOps(rawOps, hints2) {
20478
20417
  const chatId = requireChatId();
20479
20418
  const response = await apiPost("/api/ads/linkedin/draft/stage-batch", { chatId, ops });
20480
20419
  const allHints = [...hints2 ?? [], ...adCopyHints(ops)];
20481
- writeJsonEnvelope(withHints(response, allHints));
20420
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
20482
20421
  } catch (err) {
20483
20422
  handleLinkedinError(err);
20484
20423
  }
@@ -20515,11 +20454,11 @@ function readPositionals(args5) {
20515
20454
  function splitIdList(raw) {
20516
20455
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
20517
20456
  }
20518
- function idsFileEntries(path48) {
20519
- if (typeof path48 !== "string" || path48.length === 0) {
20457
+ function idsFileEntries(path49) {
20458
+ if (typeof path49 !== "string" || path49.length === 0) {
20520
20459
  return [];
20521
20460
  }
20522
- return readFileSync5(path48, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
20461
+ return readFileSync5(path49, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
20523
20462
  }
20524
20463
  function requireTargets(args5, entity) {
20525
20464
  const positionals = readPositionals(args5);
@@ -20570,7 +20509,7 @@ registerSchema({
20570
20509
  "account-id": { type: "string", description: "Account ID", required: false },
20571
20510
  "account-urn": { type: "string", description: "Alias for --account-id", required: false },
20572
20511
  "all-statuses": { type: "boolean", description: "Drop the ACTIVE filter", required: false },
20573
- statuses: { type: "string", description: statusesArgDescription("campaignGroups"), required: false },
20512
+ statuses: { type: "string", description: "CSV of statuses", required: false },
20574
20513
  limit: { type: "string", description: "Max rows (default 500)", required: false },
20575
20514
  "no-cache": NO_CACHE_ARG,
20576
20515
  output: { type: "string", description: "json|csv|jsonl|md", required: false, default: "json" }
@@ -20584,7 +20523,7 @@ registerSchema({
20584
20523
  "account-urn": { type: "string", description: "Alias", required: false },
20585
20524
  "campaign-group-id": { type: "string", description: "Filter by campaign group", required: false },
20586
20525
  "all-statuses": { type: "boolean", description: "Drop the ACTIVE filter", required: false },
20587
- statuses: { type: "string", description: statusesArgDescription("campaigns"), required: false },
20526
+ statuses: { type: "string", description: "CSV of statuses", required: false },
20588
20527
  limit: { type: "string", description: "Max rows", required: false },
20589
20528
  "no-cache": NO_CACHE_ARG,
20590
20529
  output: { type: "string", description: "json|csv|jsonl|md", required: false, default: "json" }
@@ -20598,7 +20537,7 @@ registerSchema({
20598
20537
  "account-urn": { type: "string", description: "Alias", required: false },
20599
20538
  "campaign-id": { type: "string", description: "Filter by campaign", required: false },
20600
20539
  "all-statuses": { type: "boolean", description: "Drop the ACTIVE filter", required: false },
20601
- statuses: { type: "string", description: statusesArgDescription("creatives"), required: false },
20540
+ statuses: { type: "string", description: "CSV of statuses", required: false },
20602
20541
  fields: {
20603
20542
  type: "string",
20604
20543
  description: "CSV of dotted paths to return instead of the compact row, e.g. id,content.textAd.landingPage",
@@ -20871,11 +20810,7 @@ registerSchema({
20871
20810
  description: "3-letter currency code (defaults to the ad account currency)",
20872
20811
  required: false
20873
20812
  },
20874
- status: {
20875
- type: "string",
20876
- description: "DRAFT|ACTIVE (default DRAFT) \u2014 LinkedIn refuses a new group created PAUSED",
20877
- required: false
20878
- },
20813
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)", required: false },
20879
20814
  file: { type: "string", description: "JSON payload file; flags override file keys", required: false }
20880
20815
  }
20881
20816
  });
@@ -20933,16 +20868,7 @@ registerSchema({
20933
20868
  ctv: { type: "string", description: "Connected TV on|off (default off)", required: false },
20934
20869
  "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN", required: false },
20935
20870
  "associated-entity": { type: "string", description: "Organization URN", required: false },
20936
- "political-intent": {
20937
- type: "string",
20938
- description: "NOT_DECLARED (default)|NOT_POLITICAL|POLITICAL \u2014 the advertiser's declaration; NOT_POLITICAL only after the user confirms their ads are not political advertising",
20939
- required: false
20940
- },
20941
- status: {
20942
- type: "string",
20943
- description: "DRAFT|ACTIVE|PAUSED (default DRAFT) \u2014 PAUSED needs a group that is not DRAFT",
20944
- required: false
20945
- },
20871
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)", required: false },
20946
20872
  file: { type: "string", description: "JSON payload file; flags override file keys", required: false }
20947
20873
  }
20948
20874
  });
@@ -20970,11 +20896,6 @@ registerSchema({
20970
20896
  lan: { type: "string", description: "on|off", required: false },
20971
20897
  ctv: { type: "string", description: "on|off", required: false },
20972
20898
  "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN", required: false },
20973
- "political-intent": {
20974
- type: "string",
20975
- description: "NOT_DECLARED (default)|NOT_POLITICAL|POLITICAL \u2014 the advertiser's declaration; NOT_POLITICAL only after the user confirms their ads are not political advertising",
20976
- required: false
20977
- },
20978
20899
  status: { type: "string", description: "ACTIVE|PAUSED|ARCHIVED", required: false },
20979
20900
  file: { type: "string", description: "JSON file with fields to change", required: false }
20980
20901
  }
@@ -21370,8 +21291,7 @@ var campaignGroupsCreateCommand = defineCommand33({
21370
21291
  meta: {
21371
21292
  name: "create",
21372
21293
  description: `Stage a new campaign group. ${STAGED_NOTE}
21373
- A group can't be created PAUSED, and a --total-budget needs an --end.
21374
- Example: baker ads linkedin campaign-groups create --name "Q3 ABM" --start 2026-07-10 --end 2026-09-30 --total-budget 5000 --currency EUR`
21294
+ Example: baker ads linkedin campaign-groups create --name "Q3 ABM" --start 2026-07-10 --total-budget 5000 --currency EUR`
21375
21295
  },
21376
21296
  args: {
21377
21297
  ...accountArgs,
@@ -21380,10 +21300,7 @@ Example: baker ads linkedin campaign-groups create --name "Q3 ABM" --start 2026-
21380
21300
  end: { type: "string", description: "Run schedule end" },
21381
21301
  "total-budget": { type: "string", description: TOTAL_BUDGET_HELP },
21382
21302
  currency: { type: "string", description: "3-letter currency code (defaults to the ad account currency)" },
21383
- status: {
21384
- type: "string",
21385
- description: "DRAFT|ACTIVE (default DRAFT) \u2014 LinkedIn refuses a new group created PAUSED; pause it once it exists"
21386
- },
21303
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)" },
21387
21304
  file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
21388
21305
  },
21389
21306
  run: async ({ args: args5 }) => {
@@ -21449,11 +21366,9 @@ function campaignPayloadFromFlags(args5) {
21449
21366
  connectedTelevisionOnly: parseOnOffFlag(args5.ctv, "--ctv"),
21450
21367
  creativeSelection: args5["creative-selection"] ? String(args5["creative-selection"]).toUpperCase() : void 0,
21451
21368
  associatedEntity: args5["associated-entity"],
21452
- politicalIntent: args5["political-intent"] ? String(args5["political-intent"]).toUpperCase() : void 0,
21453
21369
  status: args5.status ? String(args5.status).toUpperCase() : void 0
21454
21370
  };
21455
21371
  }
21456
- var POLITICAL_INTENT_HELP = `NOT_DECLARED (default)|NOT_POLITICAL|POLITICAL \u2014 the advertiser's own declaration. Pass NOT_POLITICAL only after the user confirms: "I confirm this is not political advertising. None of my ads qualify as political advertising under the law of the targeted countries, including EU law for ads targeted to the EU."`;
21457
21372
  var campaignFlagArgs = {
21458
21373
  ...accountArgs,
21459
21374
  name: { type: "string", description: "Campaign name (\u2264255 chars)" },
@@ -21474,8 +21389,7 @@ var campaignFlagArgs = {
21474
21389
  ctv: { type: "string", description: "Connected TV on|off" },
21475
21390
  "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN" },
21476
21391
  "associated-entity": { type: "string", description: "Organization URN" },
21477
- "political-intent": { type: "string", description: POLITICAL_INTENT_HELP },
21478
- status: { type: "string", description: "DRAFT|ACTIVE|PAUSED \u2014 PAUSED needs a group that is not DRAFT" },
21392
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED" },
21479
21393
  file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
21480
21394
  };
21481
21395
  var campaignsCreateCommand = defineCommand33({
@@ -22992,7 +22906,7 @@ var listArgs = {
22992
22906
  "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
22993
22907
  "account-urn": { type: "string", description: "Alias for --account-id" },
22994
22908
  "all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
22995
- statuses: { type: "string", description: statusesArgDescription("campaignGroups") },
22909
+ statuses: { type: "string", description: "CSV of statuses (ACTIVE,PAUSED,ARCHIVED,COMPLETED,DRAFT)" },
22996
22910
  limit: { type: "string", description: "Max rows (default: 500)" },
22997
22911
  "no-cache": NO_CACHE_ARG,
22998
22912
  output: { type: "string", description: "json|csv|jsonl|md", default: "json" }
@@ -23007,7 +22921,7 @@ var campaignGroupsListCommand = defineCommand40({
23007
22921
  const accountId = resolveAccountIdArg2(args5);
23008
22922
  try {
23009
22923
  const params = { "account-id": accountId };
23010
- const statuses = resolveStatusFilter(args5, "campaignGroups");
22924
+ const statuses = resolveStatusFilter(args5);
23011
22925
  if (statuses) params.statuses = statuses.join(",");
23012
22926
  if (args5.limit) params.limit = String(args5.limit);
23013
22927
  if (args5["no-cache"]) params["skip-cache"] = "true";
@@ -23058,7 +22972,7 @@ var listArgs2 = {
23058
22972
  "account-urn": { type: "string", description: "Alias for --account-id" },
23059
22973
  "campaign-group-id": { type: "string", description: "Filter by campaign group" },
23060
22974
  "all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
23061
- statuses: { type: "string", description: statusesArgDescription("campaigns") },
22975
+ statuses: { type: "string", description: "CSV of statuses" },
23062
22976
  limit: { type: "string", description: "Max rows (default: 500)" },
23063
22977
  "no-cache": NO_CACHE_ARG,
23064
22978
  output: { type: "string", description: "json|csv|jsonl|md", default: "json" }
@@ -23073,7 +22987,7 @@ var campaignsListCommand = defineCommand41({
23073
22987
  const accountId = resolveAccountIdArg2(args5);
23074
22988
  try {
23075
22989
  const params = { "account-id": accountId };
23076
- const statuses = resolveStatusFilter(args5, "campaigns");
22990
+ const statuses = resolveStatusFilter(args5);
23077
22991
  if (statuses) params.statuses = statuses.join(",");
23078
22992
  if (args5["campaign-group-id"]) params["campaign-group-id"] = String(args5["campaign-group-id"]);
23079
22993
  if (args5.limit) params.limit = String(args5.limit);
@@ -23306,9 +23220,9 @@ import { defineCommand as defineCommand44 } from "citty";
23306
23220
  function asRecord(value) {
23307
23221
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
23308
23222
  }
23309
- function readPath2(row, path48) {
23223
+ function readPath2(row, path49) {
23310
23224
  let current = row;
23311
- for (const segment of path48.split(".")) {
23225
+ for (const segment of path49.split(".")) {
23312
23226
  const record = asRecord(current);
23313
23227
  if (!record) return void 0;
23314
23228
  current = record[segment];
@@ -23318,10 +23232,10 @@ function readPath2(row, path48) {
23318
23232
  function projectFields(rows, paths) {
23319
23233
  return rows.map((row) => {
23320
23234
  const projected = {};
23321
- for (const path48 of paths) {
23322
- const value = readPath2(row, path48);
23235
+ for (const path49 of paths) {
23236
+ const value = readPath2(row, path49);
23323
23237
  if (value !== void 0) {
23324
- projected[path48] = value;
23238
+ projected[path49] = value;
23325
23239
  }
23326
23240
  }
23327
23241
  return projected;
@@ -23334,16 +23248,16 @@ function shapeListRows(rows, args5, compact4) {
23334
23248
  const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
23335
23249
  if (paths.length > 0) {
23336
23250
  const projected = projectFields(raw, paths).map((row, i) => {
23337
- const missing = paths.filter((path48) => row[path48] === void 0);
23251
+ const missing = paths.filter((path49) => row[path49] === void 0);
23338
23252
  const source = rows[i];
23339
23253
  if (missing.length === 0 || source === void 0) return row;
23340
23254
  const compactRow3 = compact4.row(source);
23341
- for (const path48 of missing) {
23342
- if (compactRow3[path48] !== void 0) row[path48] = compactRow3[path48];
23255
+ for (const path49 of missing) {
23256
+ if (compactRow3[path49] !== void 0) row[path49] = compactRow3[path49];
23343
23257
  }
23344
23258
  return row;
23345
23259
  });
23346
- const unmatched = paths.filter((path48) => projected.every((row) => row[path48] === void 0));
23260
+ const unmatched = paths.filter((path49) => projected.every((row) => row[path49] === void 0));
23347
23261
  if (projected.length === 0 || unmatched.length === 0) return { rows: projected, fields: paths };
23348
23262
  const carried = /* @__PURE__ */ new Set([...compact4.columns, ...raw.flatMap((row) => Object.keys(row))]);
23349
23263
  return {
@@ -23423,7 +23337,7 @@ var listArgs3 = {
23423
23337
  "account-urn": { type: "string", description: "Alias for --account-id" },
23424
23338
  "campaign-id": { type: "string", description: "Filter by campaign ID" },
23425
23339
  "all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
23426
- statuses: { type: "string", description: statusesArgDescription("creatives") },
23340
+ statuses: { type: "string", description: "CSV of intended statuses (ACTIVE,PAUSED,DRAFT,ARCHIVED)" },
23427
23341
  fields: {
23428
23342
  type: "string",
23429
23343
  description: "CSV of dotted paths to return instead of the compact row, e.g. id,content.textAd.landingPage"
@@ -23443,7 +23357,7 @@ var creativesListCommand = defineCommand44({
23443
23357
  const accountId = resolveAccountIdArg2(args5);
23444
23358
  try {
23445
23359
  const params = { "account-id": accountId };
23446
- const statuses = resolveStatusFilter(args5, "creatives");
23360
+ const statuses = resolveStatusFilter(args5);
23447
23361
  if (statuses) params.statuses = statuses.join(",");
23448
23362
  if (args5["campaign-id"]) params["campaign-id"] = String(args5["campaign-id"]);
23449
23363
  if (args5.limit) params.limit = String(args5.limit);
@@ -23750,7 +23664,7 @@ async function listDraft(json, chat) {
23750
23664
  writeJsonEnvelope(response);
23751
23665
  return;
23752
23666
  }
23753
- process.stdout.write(`${renderDraftStatus2(response.data)}${hintLines(response)}
23667
+ process.stdout.write(`${renderDraftStatus2(response.data)}
23754
23668
  `);
23755
23669
  } catch (err) {
23756
23670
  handleLinkedinError(err);
@@ -24713,54 +24627,54 @@ function buildCustomConversionRule(input) {
24713
24627
  function customConversionRuleRefusal(rule) {
24714
24628
  return ruleNodeRefusal(rule, "rule");
24715
24629
  }
24716
- function branchRefusal(key, value, path48) {
24630
+ function branchRefusal(key, value, path49) {
24717
24631
  if (!Array.isArray(value) || value.length === 0) {
24718
- return `${path48}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
24632
+ return `${path49}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
24719
24633
  }
24720
24634
  for (const [index, child] of value.entries()) {
24721
- const refusal = ruleNodeRefusal(child, `${path48}.${key}[${index}]`);
24635
+ const refusal = ruleNodeRefusal(child, `${path49}.${key}[${index}]`);
24722
24636
  if (refusal) {
24723
24637
  return refusal;
24724
24638
  }
24725
24639
  }
24726
24640
  return void 0;
24727
24641
  }
24728
- function conditionRefusal(key, value, path48) {
24729
- return key === "and" || key === "or" ? branchRefusal(key, value, path48) : leafRefusal(key, value, path48);
24642
+ function conditionRefusal(key, value, path49) {
24643
+ return key === "and" || key === "or" ? branchRefusal(key, value, path49) : leafRefusal(key, value, path49);
24730
24644
  }
24731
- function ruleNodeRefusal(node, path48) {
24645
+ function ruleNodeRefusal(node, path49) {
24732
24646
  if (!isRecord2(node)) {
24733
- return `${path48} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
24647
+ return `${path49} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
24734
24648
  }
24735
24649
  const entries = Object.entries(node);
24736
24650
  if (entries.length === 0) {
24737
- return `${path48} is empty \u2014 a rule with no condition counts every event the pixel sends`;
24651
+ return `${path49} is empty \u2014 a rule with no condition counts every event the pixel sends`;
24738
24652
  }
24739
24653
  if (entries.length > 1) {
24740
- return `${path48} names ${entries.length} conditions side by side (${entries.map(([key]) => key).join(", ")}) \u2014 Meta reads one branch or one field test per object, so combine them with {"and":[\u2026]}`;
24654
+ return `${path49} names ${entries.length} conditions side by side (${entries.map(([key]) => key).join(", ")}) \u2014 Meta reads one branch or one field test per object, so combine them with {"and":[\u2026]}`;
24741
24655
  }
24742
24656
  for (const [key, value] of entries) {
24743
- const refusal = conditionRefusal(key, value, path48);
24657
+ const refusal = conditionRefusal(key, value, path49);
24744
24658
  if (refusal) {
24745
24659
  return refusal;
24746
24660
  }
24747
24661
  }
24748
24662
  return void 0;
24749
24663
  }
24750
- function leafRefusal(field, condition, path48) {
24664
+ function leafRefusal(field, condition, path49) {
24751
24665
  if (!isRecord2(condition)) {
24752
- return `${path48}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
24666
+ return `${path49}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
24753
24667
  }
24754
24668
  const tests = Object.entries(condition);
24755
24669
  if (tests.length === 0) {
24756
- return `${path48}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
24670
+ return `${path49}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
24757
24671
  }
24758
24672
  if (tests.length > 1) {
24759
- return `${path48}.${field} names ${tests.length} operators at once (${tests.map(([operator]) => operator).join(", ")}) \u2014 test a field once per condition and combine them with {"and":[\u2026]}`;
24673
+ return `${path49}.${field} names ${tests.length} operators at once (${tests.map(([operator]) => operator).join(", ")}) \u2014 test a field once per condition and combine them with {"and":[\u2026]}`;
24760
24674
  }
24761
24675
  for (const [operator] of tests) {
24762
24676
  if (!RULE_OPERATORS[operator]) {
24763
- return `${path48}.${field} uses "${operator}", which is not a Meta rule operator \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")} (URL matching is i_contains, not contains or includes)`;
24677
+ return `${path49}.${field} uses "${operator}", which is not a Meta rule operator \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")} (URL matching is i_contains, not contains or includes)`;
24764
24678
  }
24765
24679
  }
24766
24680
  return void 0;
@@ -25476,11 +25390,11 @@ var updateStatusSchema = z37.enum(UPDATE_STATUSES);
25476
25390
  function currencyMinimums2(currencyCode) {
25477
25391
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
25478
25392
  }
25479
- function validateDailyBudgetFloor(money, ctx, path48) {
25393
+ function validateDailyBudgetFloor(money, ctx, path49) {
25480
25394
  if (money?.currencyCode) {
25481
25395
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
25482
25396
  if (Number(money.amount) < min) {
25483
- ctx.addIssue({ code: "custom", path: path48, message: `below the ${min} ${money.currencyCode} daily minimum` });
25397
+ ctx.addIssue({ code: "custom", path: path49, message: `below the ${min} ${money.currencyCode} daily minimum` });
25484
25398
  }
25485
25399
  }
25486
25400
  }
@@ -26352,19 +26266,19 @@ function refuseDeleteByUpdate(payload, group) {
26352
26266
  );
26353
26267
  }
26354
26268
  }
26355
- function loadJsonFileArg3(path48) {
26356
- if (typeof path48 !== "string" || path48.length === 0) {
26269
+ function loadJsonFileArg3(path49) {
26270
+ if (typeof path49 !== "string" || path49.length === 0) {
26357
26271
  return {};
26358
26272
  }
26359
26273
  try {
26360
- const parsed = JSON.parse(readFileSync9(path48, "utf8"));
26274
+ const parsed = JSON.parse(readFileSync9(path49, "utf8"));
26361
26275
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
26362
- failWriteValidation3(`${path48} must contain a JSON object`);
26276
+ failWriteValidation3(`${path49} must contain a JSON object`);
26363
26277
  }
26364
26278
  return parsed;
26365
26279
  } catch (err) {
26366
26280
  if (err instanceof SyntaxError) {
26367
- failWriteValidation3(`${path48} is not valid JSON: ${err.message}`);
26281
+ failWriteValidation3(`${path49} is not valid JSON: ${err.message}`);
26368
26282
  }
26369
26283
  throw err;
26370
26284
  }
@@ -26455,7 +26369,7 @@ async function stageOp2(raw) {
26455
26369
  const chatId = requireChatId();
26456
26370
  const response = await apiPost("/api/ads/meta/draft/stage", { chatId, op: preflight.data });
26457
26371
  const copyHints = adCopyHints(preflight.data);
26458
- writeJsonEnvelope(withHints(response, copyHints));
26372
+ writeJsonEnvelope(copyHints.length > 0 ? { ...response, hints: copyHints } : response);
26459
26373
  } catch (err) {
26460
26374
  handleMetaError(err);
26461
26375
  }
@@ -27728,7 +27642,7 @@ async function listDraft2(json, chat) {
27728
27642
  writeJsonEnvelope(response);
27729
27643
  return;
27730
27644
  }
27731
- process.stdout.write(`${renderDraftStatus3(response.data)}${hintLines(response)}
27645
+ process.stdout.write(`${renderDraftStatus3(response.data)}
27732
27646
  `);
27733
27647
  } catch (err) {
27734
27648
  handleMetaError(err);
@@ -28398,10 +28312,19 @@ var metaMediaCommand = defineCommand68({
28398
28312
 
28399
28313
  // src/commands/ads/meta/pages.ts
28400
28314
  import { defineCommand as defineCommand69 } from "citty";
28315
+ function advertisablePages(data) {
28316
+ return data.filter((p) => Array.isArray(p.tasks) && p.tasks.includes("ADVERTISE"));
28317
+ }
28401
28318
  function pageSelectionHints(data) {
28402
- if (data.length > 1) {
28319
+ const advertisable = advertisablePages(data);
28320
+ if (advertisable.length > 1) {
28321
+ return [
28322
+ "MULTIPLE Pages can run ads \u2014 do NOT guess. Confirm with the user which Page an ad should run from before passing --page to `creatives create`; a wrong Page publishes the ad under the wrong brand identity and staging will not catch it."
28323
+ ];
28324
+ }
28325
+ if (data.length > 0 && advertisable.length === 0) {
28403
28326
  return [
28404
- "MULTIPLE Pages can run ads from the selected Meta ad accounts \u2014 do NOT guess. Confirm with the user which Page an ad should run from before passing --page to `creatives create`; a wrong Page publishes the ad under the wrong brand identity and staging will not catch it."
28327
+ "NO Page can run ads \u2014 none of the connected user's Pages carry the ADVERTISE task. Ask the user to grant Advertise access to the right Page before creating an ad; a readable-but-non-advertising Page still passes staging and would fail or run under the wrong identity."
28405
28328
  ];
28406
28329
  }
28407
28330
  return [];
@@ -28409,7 +28332,7 @@ function pageSelectionHints(data) {
28409
28332
  var pagesCommand = defineCommand69({
28410
28333
  meta: {
28411
28334
  name: "pages",
28412
- description: `List the Facebook Pages that the selected Meta ad accounts can promote. Every Meta ad publishes from a Page, so use this to identify the Page an ad should run from. If more than one Page is listed, confirm with the user which Page to use \u2014 a wrong Page publishes under the wrong brand identity.
28335
+ description: `List the Facebook Pages the connected user manages. Every Meta ad publishes from a Page, so use this to identify the Page an ad should run from. The 'tasks' field shows what the user can do on each Page (ADVERTISE means it can back ads). If exactly one Page can ADVERTISE, use it; if more than one can, confirm with the user which Page to use; if none can, ask the user to grant Advertise access \u2014 a wrong or non-advertising Page publishes under the wrong brand identity.
28413
28336
 
28414
28337
  Examples:
28415
28338
  baker ads meta pages`
@@ -28670,7 +28593,7 @@ Discovery:
28670
28593
  baker ads meta accounts \u2014 accounts in this company's connected scope
28671
28594
  baker ads meta accounts --include-all \u2014 every account the token can see
28672
28595
  baker ads meta businesses \u2014 Business Manager IDs
28673
- baker ads meta pages \u2014 Facebook Pages the selected ad accounts can promote (an ad's identity)
28596
+ baker ads meta pages \u2014 Facebook Pages the user manages (an ad's identity)
28674
28597
  baker ads meta lead-forms --page-id 100635183117758 \u2014 instant (lead) forms on a Page, with lead counts
28675
28598
  baker ads meta leads --form-id 987654321 \u2014 the leads one form collected (contact details masked)
28676
28599
 
@@ -32051,12 +31974,12 @@ function flowExists(slug) {
32051
31974
  return existsSync3(join2(flowsDir(), slug, "_data.json"));
32052
31975
  }
32053
31976
  function readFlowTree(slug) {
32054
- const path48 = join2(flowsDir(), slug, "_data.json");
32055
- if (!existsSync3(path48)) {
31977
+ const path49 = join2(flowsDir(), slug, "_data.json");
31978
+ if (!existsSync3(path49)) {
32056
31979
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
32057
31980
  }
32058
31981
  try {
32059
- return JSON.parse(readFileSync11(path48, "utf-8"));
31982
+ return JSON.parse(readFileSync11(path49, "utf-8"));
32060
31983
  } catch (error) {
32061
31984
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
32062
31985
  }
@@ -33016,12 +32939,12 @@ function readPeopleFile(args5) {
33016
32939
  }
33017
32940
  function unreadablePeopleFile(args5) {
33018
32941
  if (!args5["people-file"]) return void 0;
33019
- const path48 = String(args5["people-file"]);
32942
+ const path49 = String(args5["people-file"]);
33020
32943
  try {
33021
- readFileSync12(path48, "utf8");
32944
+ readFileSync12(path49, "utf8");
33022
32945
  return void 0;
33023
32946
  } catch {
33024
- return `Couldn't read --people-file ${path48}. Check the path, or pass the people inline with --person.`;
32947
+ return `Couldn't read --people-file ${path49}. Check the path, or pass the people inline with --person.`;
33025
32948
  }
33026
32949
  }
33027
32950
  function peopleLimitError(args5) {
@@ -33993,11 +33916,11 @@ function parseFieldArgs(argv) {
33993
33916
  for (const pair of parseRepeated(argv, "--field")) {
33994
33917
  const eq = pair.indexOf("=");
33995
33918
  const field = eq === -1 ? pair : pair.slice(0, eq);
33996
- const path48 = eq === -1 ? "" : pair.slice(eq + 1).trim();
33919
+ const path49 = eq === -1 ? "" : pair.slice(eq + 1).trim();
33997
33920
  const problem = mappingRowProblem(field);
33998
33921
  if (problem !== null) throw new ConversionArgsError(problem);
33999
- if (path48 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
34000
- mapping[field] = path48;
33922
+ if (path49 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
33923
+ mapping[field] = path49;
34001
33924
  }
34002
33925
  return mapping;
34003
33926
  }
@@ -35599,11 +35522,11 @@ function unwrap(response) {
35599
35522
  }
35600
35523
  return response.data;
35601
35524
  }
35602
- async function readAvatars(path48, params) {
35603
- return unwrap(await apiGet(path48, params));
35525
+ async function readAvatars(path49, params) {
35526
+ return unwrap(await apiGet(path49, params));
35604
35527
  }
35605
- async function writeAvatars(path48, body) {
35606
- return unwrap(await apiPost(path48, body));
35528
+ async function writeAvatars(path49, body) {
35529
+ return unwrap(await apiPost(path49, body));
35607
35530
  }
35608
35531
 
35609
35532
  // src/commands/avatars/create.ts
@@ -36512,12 +36435,12 @@ function missingFontFiles(urls, available) {
36512
36435
  function planFontAdoption(sources, families) {
36513
36436
  const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
36514
36437
  const byFamily = /* @__PURE__ */ new Map();
36515
- for (const { path: path48, source } of sources) {
36516
- const dir = posix.dirname(path48);
36438
+ for (const { path: path49, source } of sources) {
36439
+ const dir = posix.dirname(path49);
36517
36440
  for (const face of declaredFontFaces(source)) {
36518
36441
  if (!wanted.has(face.family)) continue;
36519
36442
  const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
36520
- perFile.set(path48, [...perFile.get(path48) ?? [], rebaseFontFaceSrc(face.block, dir)]);
36443
+ perFile.set(path49, [...perFile.get(path49) ?? [], rebaseFontFaceSrc(face.block, dir)]);
36521
36444
  byFamily.set(face.family, perFile);
36522
36445
  }
36523
36446
  }
@@ -40782,18 +40705,10 @@ function dropUncastVoices(nodes) {
40782
40705
  if (node && node.type === "voice_select" && !referenced.has(node.id)) nodes.splice(i, 1);
40783
40706
  }
40784
40707
  }
40785
- function framesReuseRefusal(blueprint, elements, requested) {
40786
- if (requested !== "reuse") return void 0;
40787
- const showsPeople = (blueprint.global?.cast ?? []).length > 0 || elements.some((e) => e.type.toLowerCase() === "person");
40788
- if (!showsPeople) return void 0;
40789
- return "--frames reuse was not applied: this video shows people, and reusing its frames would put the source's actors into our ad. Every frame is generated instead, recast to the el_* references. Reuse applies only to a video with nobody in it.";
40790
- }
40791
- function scaffoldVideoCanvas(input, elementsInput, requested) {
40708
+ function scaffoldVideoCanvas(input, elementsInput, opts) {
40792
40709
  const blueprint = VideoBlueprint.parse(input);
40793
40710
  injectHookPhysicality(blueprint);
40794
40711
  const elements = RecurringElements.parse(elementsInput);
40795
- const reuseRefused = framesReuseRefusal(blueprint, elements, requested.frames);
40796
- const opts = reuseRefused ? { ...requested, frames: "generate" } : requested;
40797
40712
  const clock = spineClockFor(blueprint);
40798
40713
  const nodes = [];
40799
40714
  emitBlueprintIngests(opts, nodes);
@@ -40896,7 +40811,6 @@ function scaffoldVideoCanvas(input, elementsInput, requested) {
40896
40811
  }
40897
40812
  const todo = {
40898
40813
  ...buildVideoTodo(videoReport(input, elementsInput), overlays.length, floating.length, opts, blueprint),
40899
- ...reuseRefused ? { frames_reuse_refused: reuseRefused } : {},
40900
40814
  ...aspectRemapTodo(resolveAspect(blueprint.source?.aspect_ratio, opts.aspect, genAspectsFor(opts.videoModel))),
40901
40815
  model_constraints: buildModelConstraints()
40902
40816
  };
@@ -45823,12 +45737,6 @@ function videoDefinitionDescription(blueprint) {
45823
45737
  const product = g.branding?.product;
45824
45738
  return typeof product === "string" && product.trim() ? product.trim() : void 0;
45825
45739
  }
45826
- function framesModeReport(canvas, requested) {
45827
- const todo = canvas.metadata?.todo;
45828
- const mode = typeof todo?.frames_mode === "string" ? todo.frames_mode : requested;
45829
- const refused = typeof todo?.frames_reuse_refused === "string" ? todo.frames_reuse_refused : void 0;
45830
- return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
45831
- }
45832
45740
  async function materializeReferenceVideo(fileArg2) {
45833
45741
  if (!/^https?:\/\//i.test(fileArg2)) return path26.resolve(fileArg2);
45834
45742
  let bytes;
@@ -45986,10 +45894,7 @@ var scaffoldVideoCommand = defineCommand116({
45986
45894
  type: "string",
45987
45895
  description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
45988
45896
  },
45989
- frames: {
45990
- type: "string",
45991
- description: `"generate" (default, every frame regenerated for our ad) or "reuse" (wire the source's real frames in). Reuse applies only to a video with nobody in it \u2014 on a video showing people it is refused and every frame is generated, so the source's actors never reach our ad.`
45992
- },
45897
+ frames: { type: "string", description: '"generate" (default, anchored regen) or "reuse" (wire real frames in)' },
45993
45898
  ambient: {
45994
45899
  type: "boolean",
45995
45900
  description: "Give silent b-roll scenes native diegetic ambient mixed deep under the music bed (off by default)"
@@ -46192,7 +46097,6 @@ var scaffoldVideoCommand = defineCommand116({
46192
46097
  error: { code: "validation", issues: validation.issues },
46193
46098
  canvas_path: outPath,
46194
46099
  prompt_path: blueprintPath,
46195
- ...framesModeReport(canvas, frames),
46196
46100
  note: "The canvas WAS written despite the blocking issue(s) \u2014 fix them in place, then `baker canvas validate` before running.",
46197
46101
  ...uncastHint
46198
46102
  },
@@ -46244,7 +46148,7 @@ var scaffoldVideoCommand = defineCommand116({
46244
46148
  composition_dir: compositionDest,
46245
46149
  output: canvas.output,
46246
46150
  ...uncastHint,
46247
- ...framesModeReport(canvas, frames),
46151
+ frames_mode: frames,
46248
46152
  models: {
46249
46153
  deconstruct: deconstructModel,
46250
46154
  select: selectModel,
@@ -49362,10 +49266,10 @@ function parseValueExpression(raw) {
49362
49266
  return parts.map(parsePart);
49363
49267
  }
49364
49268
  function trackingFieldIds() {
49365
- const path48 = join4(flowsDir(), "..", "tracking.ts");
49366
- if (!existsSync6(path48)) return null;
49269
+ const path49 = join4(flowsDir(), "..", "tracking.ts");
49270
+ if (!existsSync6(path49)) return null;
49367
49271
  try {
49368
- const source = readFileSync15(path48, "utf-8");
49272
+ const source = readFileSync15(path49, "utf-8");
49369
49273
  const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
49370
49274
  if (!block2) return null;
49371
49275
  const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
@@ -49939,13 +49843,13 @@ function specsFromFile(parsed) {
49939
49843
  return `${destField}${type}=${entry?.value ?? ""}`;
49940
49844
  });
49941
49845
  }
49942
- function readSpecFile(path48) {
49846
+ function readSpecFile(path49) {
49943
49847
  let raw;
49944
49848
  try {
49945
- raw = path48 === "-" ? readFileSync16(0, "utf-8") : readFileSync16(path48, "utf-8");
49849
+ raw = path49 === "-" ? readFileSync16(0, "utf-8") : readFileSync16(path49, "utf-8");
49946
49850
  } catch (error) {
49947
49851
  refuse(
49948
- `Could not read ${path48 === "-" ? "the mapping from stdin" : `"${path48}"`}: ${error instanceof Error ? error.message : String(error)}`
49852
+ `Could not read ${path49 === "-" ? "the mapping from stdin" : `"${path49}"`}: ${error instanceof Error ? error.message : String(error)}`
49949
49853
  );
49950
49854
  }
49951
49855
  let parsed;
@@ -49953,7 +49857,7 @@ function readSpecFile(path48) {
49953
49857
  parsed = JSON.parse(raw);
49954
49858
  } catch (error) {
49955
49859
  refuse(
49956
- `${path48 === "-" ? "stdin" : `"${path48}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
49860
+ `${path49 === "-" ? "stdin" : `"${path49}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
49957
49861
  'Expected { "map": { "<destField>": "<value>", \u2026 } }'
49958
49862
  );
49959
49863
  }
@@ -50252,21 +50156,21 @@ var ARRAY_FIELDS = [
50252
50156
  "tagIds"
50253
50157
  ];
50254
50158
  var ARRAY_OWNERS = ["", "body"];
50255
- function dropUnsetOptionals(sideEffect, path48) {
50159
+ function dropUnsetOptionals(sideEffect, path49) {
50256
50160
  return OPTIONAL_STRINGS.flatMap((key) => {
50257
50161
  if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
50258
50162
  delete sideEffect[key];
50259
- return [{ path: path48, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
50163
+ return [{ path: path49, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
50260
50164
  });
50261
50165
  }
50262
- function fillNulledArrays(target2, prefix, path48) {
50166
+ function fillNulledArrays(target2, prefix, path49) {
50263
50167
  return ARRAY_FIELDS.flatMap((key) => {
50264
50168
  if (!(key in target2) || target2[key] !== null) return [];
50265
50169
  target2[key] = [];
50266
- return [{ path: path48, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
50170
+ return [{ path: path49, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
50267
50171
  });
50268
50172
  }
50269
- function tidyEmailRecipients(sideEffect, path48) {
50173
+ function tidyEmailRecipients(sideEffect, path49) {
50270
50174
  if (sideEffect.type !== "email") return [];
50271
50175
  const config = sideEffect.encryptedConfig;
50272
50176
  if (!config || typeof config !== "object" || Array.isArray(config)) return [];
@@ -50277,7 +50181,7 @@ function tidyEmailRecipients(sideEffect, path48) {
50277
50181
  const tidied = to.join(", ");
50278
50182
  if (tidied === raw) return [];
50279
50183
  config.email = tidied;
50280
- return [{ path: path48, change: "tidied the recipient list's spacing and separators" }];
50184
+ return [{ path: path49, change: "tidied the recipient list's spacing and separators" }];
50281
50185
  }
50282
50186
  function sideEffectsOf(node) {
50283
50187
  if (!Array.isArray(node.sideEffects)) return [];
@@ -50286,13 +50190,13 @@ function sideEffectsOf(node) {
50286
50190
  );
50287
50191
  }
50288
50192
  function normalizeSideEffect(sideEffect, where) {
50289
- const path48 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
50193
+ const path49 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
50290
50194
  const arrays = ARRAY_OWNERS.flatMap((owner) => {
50291
50195
  const target2 = owner ? sideEffect[owner] : sideEffect;
50292
50196
  if (!target2 || typeof target2 !== "object") return [];
50293
- return fillNulledArrays(target2, owner ? `${owner}.` : "", path48);
50197
+ return fillNulledArrays(target2, owner ? `${owner}.` : "", path49);
50294
50198
  });
50295
- return [...dropUnsetOptionals(sideEffect, path48), ...arrays, ...tidyEmailRecipients(sideEffect, path48)];
50199
+ return [...dropUnsetOptionals(sideEffect, path49), ...arrays, ...tidyEmailRecipients(sideEffect, path49)];
50296
50200
  }
50297
50201
  function normalizeFlowTree(tree) {
50298
50202
  const changes = [];
@@ -51654,11 +51558,11 @@ var WARNING_HINT = "Read the warnings above before telling the user this is read
51654
51558
  async function stageOp3(op) {
51655
51559
  const chatId = requireChatId();
51656
51560
  try {
51657
- const { data, hints: hints2 } = splitHints(await apiPost("/api/ga4/draft/stage", { chatId, op }));
51561
+ const data = await apiPost("/api/ga4/draft/stage", { chatId, op });
51658
51562
  writeJsonEnvelope({
51659
51563
  ok: true,
51660
51564
  data,
51661
- hints: [...hints2, ...STAGE_HINTS, ...data.warnings.length > 0 ? [WARNING_HINT] : []]
51565
+ hints: [...STAGE_HINTS, ...data.warnings.length > 0 ? [WARNING_HINT] : []]
51662
51566
  });
51663
51567
  } catch (err) {
51664
51568
  handleError5(err);
@@ -51672,23 +51576,21 @@ async function stageOps(ops) {
51672
51576
  }
51673
51577
  const chatId = requireChatId();
51674
51578
  try {
51675
- const { data, hints: hints2 } = splitHints(
51676
- await apiPost("/api/ga4/draft/stage-batch", { chatId, ops })
51677
- );
51579
+ const data = await apiPost("/api/ga4/draft/stage-batch", { chatId, ops });
51678
51580
  writeJsonEnvelope({
51679
51581
  ok: true,
51680
51582
  data,
51681
- hints: [...hints2, ...STAGE_HINTS, ...data.ops.some((op) => op.warnings.length > 0) ? [WARNING_HINT] : []]
51583
+ hints: [...STAGE_HINTS, ...data.ops.some((op) => op.warnings.length > 0) ? [WARNING_HINT] : []]
51682
51584
  });
51683
51585
  } catch (err) {
51684
51586
  handleError5(err);
51685
51587
  }
51686
51588
  }
51687
- async function draftAction2(path48, body, chat) {
51589
+ async function draftAction2(path49, body, chat) {
51688
51590
  const chatId = resolveChatId(chat);
51689
51591
  try {
51690
- const { data, hints: hints2 } = splitHints(await apiPost(path48, { chatId, ...body }));
51691
- writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
51592
+ const data = await apiPost(path49, { chatId, ...body });
51593
+ writeJsonEnvelope({ ok: true, data });
51692
51594
  return data;
51693
51595
  } catch (err) {
51694
51596
  handleError5(err);
@@ -51720,13 +51622,12 @@ function renderDraft(response) {
51720
51622
  async function draftList(json, chat) {
51721
51623
  const chatId = resolveChatId(chat);
51722
51624
  try {
51723
- const response = await apiPost("/api/ga4/draft", { chatId });
51724
- const { data, hints: hints2 } = splitHints(response);
51625
+ const data = await apiPost("/api/ga4/draft", { chatId });
51725
51626
  if (json) {
51726
- writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
51627
+ writeJsonEnvelope({ ok: true, data });
51727
51628
  return;
51728
51629
  }
51729
- process.stdout.write(`${renderDraft(data)}${hintLines(response)}
51630
+ process.stdout.write(`${renderDraft(data)}
51730
51631
  `);
51731
51632
  } catch (err) {
51732
51633
  handleError5(err);
@@ -54217,9 +54118,9 @@ async function readImageBuffer(pathOrUrl) {
54217
54118
  }
54218
54119
  return readFile28(pathOrUrl);
54219
54120
  }
54220
- async function isDirectory(path48) {
54121
+ async function isDirectory(path49) {
54221
54122
  try {
54222
- const s = await stat7(path48);
54123
+ const s = await stat7(path49);
54223
54124
  return s.isDirectory();
54224
54125
  } catch {
54225
54126
  return false;
@@ -54647,13 +54548,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
54647
54548
  }
54648
54549
  function disambiguate(paths) {
54649
54550
  const taken = /* @__PURE__ */ new Set();
54650
- return paths.map((path48) => {
54651
- if (!taken.has(path48)) {
54652
- taken.add(path48);
54653
- return path48;
54551
+ return paths.map((path49) => {
54552
+ if (!taken.has(path49)) {
54553
+ taken.add(path49);
54554
+ return path49;
54654
54555
  }
54655
- const ext = extname3(path48);
54656
- const stem = path48.slice(0, path48.length - ext.length);
54556
+ const ext = extname3(path49);
54557
+ const stem = path49.slice(0, path49.length - ext.length);
54657
54558
  let n = 2;
54658
54559
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
54659
54560
  const unique = `${stem}-${n}${ext}`;
@@ -54779,10 +54680,10 @@ async function runDownloads(plan) {
54779
54680
  const paths = disambiguate(fetched.map((item) => item.path));
54780
54681
  const downloaded = [];
54781
54682
  for (const [index, item] of fetched.entries()) {
54782
- const path48 = paths[index] ?? item.path;
54683
+ const path49 = paths[index] ?? item.path;
54783
54684
  try {
54784
- await atomicWrite(path48, item.buffer);
54785
- downloaded.push({ input: item.input, output: path48, bytes: item.buffer.length, contentType: item.contentType });
54685
+ await atomicWrite(path49, item.buffer);
54686
+ downloaded.push({ input: item.input, output: path49, bytes: item.buffer.length, contentType: item.contentType });
54786
54687
  } catch (err) {
54787
54688
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
54788
54689
  }
@@ -62969,11 +62870,365 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
62969
62870
  }
62970
62871
  });
62971
62872
 
62873
+ // src/commands/media-sources/index.ts
62874
+ import { defineCommand as defineCommand189 } from "citty";
62875
+
62876
+ // ../api/src/media-sources.ts
62877
+ import { z as z51 } from "zod";
62878
+ var mediaSourcePlatformSchema = z51.enum([
62879
+ "instagram",
62880
+ "tiktok",
62881
+ "youtube",
62882
+ "linkedin",
62883
+ "facebook",
62884
+ "google_drive"
62885
+ ]);
62886
+ var mediaSourceRefreshIntervalSchema = z51.enum(["never", "daily", "weekly", "monthly"]);
62887
+ var mediaSourceSyncStatusSchema = z51.enum(["idle", "fetching", "processing", "error"]);
62888
+ var mediaSourceHalfSchema = z51.object({
62889
+ sourceId: z51.string(),
62890
+ syncStatus: mediaSourceSyncStatusSchema,
62891
+ storedCount: z51.number().optional(),
62892
+ pendingItems: z51.number().optional(),
62893
+ failedItems: z51.number().optional(),
62894
+ lastSyncedAt: z51.number().optional(),
62895
+ errorMessage: z51.string().optional()
62896
+ });
62897
+ var mediaSourceSummarySchema = z51.object({
62898
+ key: z51.string(),
62899
+ platform: mediaSourcePlatformSchema,
62900
+ platformLabel: z51.string(),
62901
+ handle: z51.string(),
62902
+ label: z51.string(),
62903
+ profileUrl: z51.string(),
62904
+ profileImageUrl: z51.string().optional(),
62905
+ refreshInterval: mediaSourceRefreshIntervalSchema,
62906
+ /** Absent for a platform that feeds no images (YouTube). */
62907
+ images: mediaSourceHalfSchema.optional(),
62908
+ videos: mediaSourceHalfSchema.optional(),
62909
+ createdAt: z51.number()
62910
+ });
62911
+ var mediaSourceProfileSchema = z51.object({
62912
+ platform: mediaSourcePlatformSchema,
62913
+ handle: z51.string(),
62914
+ displayName: z51.string(),
62915
+ avatarUrl: z51.string(),
62916
+ bio: z51.string(),
62917
+ followers: z51.number().optional(),
62918
+ postCount: z51.number().optional(),
62919
+ isPrivate: z51.boolean(),
62920
+ profileUrl: z51.string(),
62921
+ channelId: z51.string().optional()
62922
+ });
62923
+ var mediaSourcesAddRequestSchema = z51.object({
62924
+ /** A profile URL or a Drive folder link, or an @handle together with `platform`. */
62925
+ input: z51.string().trim().min(1, "Pass the account's profile URL or @handle"),
62926
+ /** Needed only when `input` is a bare handle; a URL names its own platform. */
62927
+ platform: mediaSourcePlatformSchema.optional(),
62928
+ /** Auto-sync cadence. Omitted = weekly, for every platform. */
62929
+ every: mediaSourceRefreshIntervalSchema.optional()
62930
+ });
62931
+ var mediaSourceKeySchema = z51.string().trim().regex(/^(instagram|tiktok|youtube|linkedin|facebook|google_drive):.+$/, "Pass a source key like tiktok:nike");
62932
+ var mediaSourcesSyncRequestSchema = z51.object({ key: mediaSourceKeySchema });
62933
+ var mediaSourcesRemoveRequestSchema = z51.object({ key: mediaSourceKeySchema });
62934
+ var mediaSourcesScheduleRequestSchema = z51.object({
62935
+ key: mediaSourceKeySchema,
62936
+ every: mediaSourceRefreshIntervalSchema
62937
+ });
62938
+
62939
+ // src/commands/media-sources/add.ts
62940
+ import { defineCommand as defineCommand184 } from "citty";
62941
+
62942
+ // src/commands/media-sources/shared.ts
62943
+ function driveRefusalFix(input, code) {
62944
+ if (!/drive\.google\.com/i.test(input) || code !== "BAD_REQUEST") return void 0;
62945
+ return `A Google Drive folder is never added by link \u2014 don't retry. Call request_connection with platform "google-drive" and choose "media-folder": the card lets the user pick the folder in the chat (only the Baker team can for now; anyone else is told the team will set it up). A connected result carries the new source's mediaSourceKey; if it is declined, carry on with the rest of the task.`;
62946
+ }
62947
+ function reportError2(err, input) {
62948
+ if (err instanceof ApiError) {
62949
+ const fix = input ? driveRefusalFix(input, err.code) : void 0;
62950
+ writeJson({ ok: false, error: { code: err.code, message: err.message, ...fix ? { fix } : {} } });
62951
+ process.exit(1);
62952
+ }
62953
+ writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
62954
+ process.exit(1);
62955
+ }
62956
+ function halfState(half) {
62957
+ if (!half) return null;
62958
+ return {
62959
+ status: half.syncStatus === "fetching" || half.syncStatus === "processing" ? "importing" : half.syncStatus,
62960
+ stored: half.storedCount ?? 0,
62961
+ ...half.pendingItems ? { pending: half.pendingItems } : {},
62962
+ ...half.failedItems ? { failed: half.failedItems } : {},
62963
+ ...half.lastSyncedAt ? { lastSyncedAt: new Date(half.lastSyncedAt).toISOString() } : {},
62964
+ ...half.errorMessage ? { error: half.errorMessage } : {}
62965
+ };
62966
+ }
62967
+ function compactSource(source) {
62968
+ return {
62969
+ key: source.key,
62970
+ platform: source.platform,
62971
+ label: source.label,
62972
+ url: source.profileUrl,
62973
+ autoSync: source.refreshInterval,
62974
+ images: halfState(source.images),
62975
+ videos: halfState(source.videos)
62976
+ };
62977
+ }
62978
+ function isImporting(source) {
62979
+ return [source.images, source.videos].some(
62980
+ (half) => half?.syncStatus === "fetching" || half?.syncStatus === "processing"
62981
+ );
62982
+ }
62983
+
62984
+ // src/commands/media-sources/add.ts
62985
+ registerSchema({
62986
+ command: "media-sources.add",
62987
+ description: "Connect a public social account as a source for the image and video libraries \u2014 Instagram, TikTok, YouTube (videos only), LinkedIn company pages, Facebook pages. Its recent posts (up to a year back) are imported. A Google Drive folder is not added here: ask for it with request_connection (platform google-drive, choose media-folder) and the user picks it in the chat. Everything is described and made searchable in the background. Applies immediately and shows on the chat as an already-applied change. Adding a source that is already connected is a no-op.",
62988
+ args: {
62989
+ input: {
62990
+ type: "string",
62991
+ description: "Profile URL (names its own platform), or @handle together with --platform",
62992
+ required: true
62993
+ },
62994
+ platform: {
62995
+ type: "string",
62996
+ description: `Needed only for a bare handle: ${mediaSourcePlatformSchema.options.join("|")}`,
62997
+ required: false
62998
+ },
62999
+ every: {
63000
+ type: "string",
63001
+ description: `Auto-sync cadence: ${mediaSourceRefreshIntervalSchema.options.join("|")} (default weekly for every source)`,
63002
+ required: false
63003
+ }
63004
+ }
63005
+ });
63006
+ var addCommand3 = defineCommand184({
63007
+ meta: {
63008
+ name: "add",
63009
+ description: "Connect a social account (Instagram, TikTok, YouTube, LinkedIn page, Facebook page) as a media source. Start here with the profile URL. A Google Drive folder is never added by link: call request_connection with platform google-drive and choose media-folder, and the user picks the folder in the chat.\n\nExamples:\n baker media-sources add https://www.tiktok.com/@acme\n baker media-sources add @acme --platform instagram --every daily"
63010
+ },
63011
+ args: {
63012
+ input: { type: "positional", description: "Profile URL or @handle", required: true },
63013
+ platform: {
63014
+ type: "string",
63015
+ description: "instagram|tiktok|youtube|linkedin|facebook \u2014 only for a bare handle",
63016
+ required: false
63017
+ },
63018
+ every: { type: "string", description: "Auto-sync: never|daily|weekly|monthly", required: false }
63019
+ },
63020
+ run: async ({ args: args5 }) => {
63021
+ const parsed = mediaSourcesAddRequestSchema.safeParse({
63022
+ input: String(args5.input ?? ""),
63023
+ platform: args5.platform ? String(args5.platform).toLowerCase() : void 0,
63024
+ every: args5.every ? String(args5.every).toLowerCase() : void 0
63025
+ });
63026
+ if (!parsed.success) {
63027
+ const issue = parsed.error.issues[0];
63028
+ writeJson({
63029
+ ok: false,
63030
+ error: {
63031
+ code: "VALIDATION_ERROR",
63032
+ message: issue ? `${issue.path.join(".") || "input"}: ${issue.message}` : "Invalid request"
63033
+ }
63034
+ });
63035
+ process.exit(1);
63036
+ }
63037
+ try {
63038
+ const data = await apiPost("/api/media-sources/add", parsed.data, {
63039
+ timeoutMs: 12e4
63040
+ });
63041
+ const hints2 = [
63042
+ data.created ? `Connected ${data.source.label} on ${data.source.platformLabel}. Its posts are importing in the background \u2014 they become searchable with \`baker images search\` / \`baker videos search\` as each one is analysed. Check progress with \`baker media-sources list\`.` : `${data.source.label} on ${data.source.platformLabel} was already connected; a fresh import was started if one wasn't running.`
63043
+ ];
63044
+ if (data.source.platform === "youtube") {
63045
+ hints2.push(
63046
+ "YouTube feeds the video library only, newest first, a batch per sync; videos over 20 minutes are skipped."
63047
+ );
63048
+ }
63049
+ if (data.source.refreshInterval === "never") {
63050
+ hints2.push(
63051
+ `Auto-sync is off. If the user wants new posts to keep arriving: \`baker media-sources schedule ${data.source.key} --every weekly\`.`
63052
+ );
63053
+ }
63054
+ writeJson({
63055
+ ok: true,
63056
+ data: { created: data.created, source: compactSource(data.source), name: data.profile.displayName },
63057
+ hints: hints2
63058
+ });
63059
+ } catch (err) {
63060
+ reportError2(err, parsed.data.input);
63061
+ }
63062
+ }
63063
+ });
63064
+
63065
+ // src/commands/media-sources/list.ts
63066
+ import { defineCommand as defineCommand185 } from "citty";
63067
+ registerSchema({
63068
+ command: "media-sources.list",
63069
+ description: "List the social accounts connected as image/video sources, with each one's key, import status and how many images and videos it has brought in.",
63070
+ args: {}
63071
+ });
63072
+ var listCommand17 = defineCommand185({
63073
+ meta: {
63074
+ name: "list",
63075
+ description: "List connected media sources (key, platform, import status, stored images/videos).\n\nExample: baker media-sources list"
63076
+ },
63077
+ args: {},
63078
+ run: async () => {
63079
+ try {
63080
+ const { sources } = await apiGet("/api/media-sources/list");
63081
+ const hints2 = [];
63082
+ if (sources.length === 0) {
63083
+ hints2.push(
63084
+ "No sources connected. Connect the brand's own accounts with `baker media-sources add <profile URL>` to fill the image and video libraries."
63085
+ );
63086
+ } else if (sources.some(isImporting)) {
63087
+ hints2.push("Some sources are still importing \u2014 their counts will grow over the next minutes.");
63088
+ }
63089
+ writeJson({ ok: true, data: sources.map(compactSource), hints: hints2.length > 0 ? hints2 : void 0 });
63090
+ } catch (err) {
63091
+ reportError2(err);
63092
+ }
63093
+ }
63094
+ });
63095
+
63096
+ // src/commands/media-sources/remove.ts
63097
+ import { defineCommand as defineCommand186 } from "citty";
63098
+ registerSchema({
63099
+ command: "media-sources.remove",
63100
+ description: "Disconnect a media source. Stops future imports; images and videos it already brought in stay in the library. Applies immediately.",
63101
+ args: { key: { type: "string", description: "Source key, e.g. tiktok:acme", required: true } }
63102
+ });
63103
+ var removeCommand6 = defineCommand186({
63104
+ meta: {
63105
+ name: "remove",
63106
+ description: "Disconnect a media source (already-imported media stays).\n\nExample: baker media-sources remove tiktok:acme"
63107
+ },
63108
+ args: { key: { type: "positional", description: "Source key, e.g. tiktok:acme", required: true } },
63109
+ run: async ({ args: args5 }) => {
63110
+ const parsed = mediaSourcesRemoveRequestSchema.safeParse({ key: String(args5.key ?? "") });
63111
+ if (!parsed.success) {
63112
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: parsed.error.issues[0]?.message ?? "" } });
63113
+ process.exit(1);
63114
+ }
63115
+ try {
63116
+ const data = await apiPost("/api/media-sources/remove", parsed.data);
63117
+ writeJson({ ok: true, data });
63118
+ } catch (err) {
63119
+ reportError2(err);
63120
+ }
63121
+ }
63122
+ });
63123
+
63124
+ // src/commands/media-sources/schedule.ts
63125
+ import { defineCommand as defineCommand187 } from "citty";
63126
+ registerSchema({
63127
+ command: "media-sources.schedule",
63128
+ description: "Set how often a connected source imports new posts on its own.",
63129
+ args: {
63130
+ key: { type: "string", description: "Source key, e.g. tiktok:acme", required: true },
63131
+ every: {
63132
+ type: "string",
63133
+ description: `Cadence: ${mediaSourceRefreshIntervalSchema.options.join("|")} (never = only on demand)`,
63134
+ required: true
63135
+ }
63136
+ }
63137
+ });
63138
+ var scheduleCommand = defineCommand187({
63139
+ meta: {
63140
+ name: "schedule",
63141
+ description: "Set a source's auto-sync cadence.\n\nExample: baker media-sources schedule tiktok:acme --every weekly"
63142
+ },
63143
+ args: {
63144
+ key: { type: "positional", description: "Source key, e.g. tiktok:acme", required: true },
63145
+ every: { type: "string", description: "never|daily|weekly|monthly", required: true }
63146
+ },
63147
+ run: async ({ args: args5 }) => {
63148
+ const parsed = mediaSourcesScheduleRequestSchema.safeParse({
63149
+ key: String(args5.key ?? ""),
63150
+ every: String(args5.every ?? "").toLowerCase()
63151
+ });
63152
+ if (!parsed.success) {
63153
+ const issue = parsed.error.issues[0];
63154
+ writeJson({
63155
+ ok: false,
63156
+ error: { code: "VALIDATION_ERROR", message: issue ? `${issue.path.join(".")}: ${issue.message}` : "" }
63157
+ });
63158
+ process.exit(1);
63159
+ }
63160
+ try {
63161
+ const data = await apiPost("/api/media-sources/schedule", parsed.data);
63162
+ writeJson({ ok: true, data: { ...data, autoSync: parsed.data.every } });
63163
+ } catch (err) {
63164
+ reportError2(err);
63165
+ }
63166
+ }
63167
+ });
63168
+
63169
+ // src/commands/media-sources/sync.ts
63170
+ import { defineCommand as defineCommand188 } from "citty";
63171
+ registerSchema({
63172
+ command: "media-sources.sync",
63173
+ description: "Import a connected source's new posts now. Runs in the background; only posts not yet in the library are fetched.",
63174
+ args: {
63175
+ key: { type: "string", description: "Source key from `media-sources list`, e.g. tiktok:acme", required: true }
63176
+ }
63177
+ });
63178
+ var syncCommand = defineCommand188({
63179
+ meta: {
63180
+ name: "sync",
63181
+ description: "Import a source's new posts now.\n\nExample: baker media-sources sync tiktok:acme"
63182
+ },
63183
+ args: { key: { type: "positional", description: "Source key, e.g. tiktok:acme", required: true } },
63184
+ run: async ({ args: args5 }) => {
63185
+ const parsed = mediaSourcesSyncRequestSchema.safeParse({ key: String(args5.key ?? "") });
63186
+ if (!parsed.success) {
63187
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: parsed.error.issues[0]?.message ?? "" } });
63188
+ process.exit(1);
63189
+ }
63190
+ try {
63191
+ const data = await apiPost("/api/media-sources/sync", parsed.data);
63192
+ writeJson({
63193
+ ok: true,
63194
+ data,
63195
+ hints: data.started ? void 0 : ["An import was already running for this source; nothing new was started."]
63196
+ });
63197
+ } catch (err) {
63198
+ reportError2(err);
63199
+ }
63200
+ }
63201
+ });
63202
+
63203
+ // src/commands/media-sources/index.ts
63204
+ var mediaSourcesCommand = defineCommand189({
63205
+ meta: {
63206
+ name: "media-sources",
63207
+ description: `Connect social accounts whose images and videos fill the libraries: Instagram, TikTok, YouTube (videos), LinkedIn company pages, Facebook pages. A Google Drive folder is picked by the user in the chat (request_connection, platform google-drive, choose media-folder), never added here; once connected, list, sync, schedule and remove manage it like any source. Subcommands: add, list, sync, schedule, remove.
63208
+
63209
+ Examples:
63210
+ baker media-sources add https://www.tiktok.com/@acme
63211
+ baker media-sources add @acme --platform instagram --every daily
63212
+ baker media-sources list
63213
+ baker media-sources sync youtube:acme
63214
+ baker media-sources schedule linkedin:acme --every monthly
63215
+ baker media-sources remove facebook:acme
63216
+ Full guide: __tooling__/docs/tools/baker/media-sources.md`
63217
+ },
63218
+ subCommands: {
63219
+ add: addCommand3,
63220
+ list: listCommand17,
63221
+ sync: syncCommand,
63222
+ schedule: scheduleCommand,
63223
+ remove: removeCommand6
63224
+ }
63225
+ });
63226
+
62972
63227
  // src/commands/research/index.ts
62973
- import { defineCommand as defineCommand196 } from "citty";
63228
+ import { defineCommand as defineCommand202 } from "citty";
62974
63229
 
62975
63230
  // src/commands/research/advertisers.ts
62976
- import { defineCommand as defineCommand184 } from "citty";
63231
+ import { defineCommand as defineCommand190 } from "citty";
62977
63232
 
62978
63233
  // src/commands/research/hints.ts
62979
63234
  var AD_COPY_ROUTE = 'This returns competing domains and their SERP economics \u2014 no ad copy, no headlines, no creative. For the actual copy of a competitor\'s ads: `baker winning-ads advertisers "<brand>"` \u2192 `baker winning-ads search "<brief>" --advertiser-id <id>` \u2192 `baker winning-ads content <adId>` (`primary_text` / `headline` / `cta`, plus the spoken transcript and on-screen text for video). That corpus is Meta and LinkedIn only \u2014 Google SERP ad copy is not available through any Baker command, so do not keep querying for it here.';
@@ -63175,7 +63430,7 @@ var FIELDS3 = {
63175
63430
  etv: "Estimated traffic value (USD)",
63176
63431
  visibility: "SERP visibility score (0-1)"
63177
63432
  };
63178
- var advertisersCommand = defineCommand184({
63433
+ var advertisersCommand = defineCommand190({
63179
63434
  meta: {
63180
63435
  name: "advertisers",
63181
63436
  description: `Domains competing for a keyword in Google SERPs, with position, relevance, traffic value and visibility. Returns NO ad copy \u2014 for a competitor's headlines and body copy use \`baker winning-ads content <adId>\` (Meta/LinkedIn only).
@@ -63231,7 +63486,7 @@ Examples:
63231
63486
  });
63232
63487
 
63233
63488
  // src/commands/research/autocomplete.ts
63234
- import { defineCommand as defineCommand185 } from "citty";
63489
+ import { defineCommand as defineCommand191 } from "citty";
63235
63490
  registerSchema({
63236
63491
  command: "research.autocomplete",
63237
63492
  description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -63254,7 +63509,7 @@ registerSchema({
63254
63509
  var FIELDS4 = {
63255
63510
  suggestion: "Autocomplete suggestion from Google"
63256
63511
  };
63257
- var autocompleteCommand = defineCommand185({
63512
+ var autocompleteCommand = defineCommand191({
63258
63513
  meta: {
63259
63514
  name: "autocomplete",
63260
63515
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -63309,7 +63564,7 @@ Examples:
63309
63564
  });
63310
63565
 
63311
63566
  // src/commands/research/countries.ts
63312
- import { defineCommand as defineCommand186 } from "citty";
63567
+ import { defineCommand as defineCommand192 } from "citty";
63313
63568
  registerSchema({
63314
63569
  command: "research.countries",
63315
63570
  description: "List all supported country codes for --location flag in research commands.",
@@ -63366,7 +63621,7 @@ var FIELDS5 = {
63366
63621
  code: "Country code to pass as --location",
63367
63622
  name: "Country name"
63368
63623
  };
63369
- var countriesCommand = defineCommand186({
63624
+ var countriesCommand = defineCommand192({
63370
63625
  meta: {
63371
63626
  name: "countries",
63372
63627
  description: "List all supported country codes for --location flag."
@@ -63377,7 +63632,7 @@ var countriesCommand = defineCommand186({
63377
63632
  });
63378
63633
 
63379
63634
  // src/commands/research/fetch.ts
63380
- import { defineCommand as defineCommand187 } from "citty";
63635
+ import { defineCommand as defineCommand193 } from "citty";
63381
63636
  var CONTENT_PREVIEW_CHARS = 2e4;
63382
63637
  var TIMEOUT_MS = 18e4;
63383
63638
  registerSchema({
@@ -63450,7 +63705,7 @@ function fetchFix(code) {
63450
63705
  explanation: "This read failed. Don't build a retry ladder around it \u2014 finish the rest of the job with what you can reach and name this page as a gap."
63451
63706
  };
63452
63707
  }
63453
- var fetchCommand = defineCommand187({
63708
+ var fetchCommand = defineCommand193({
63454
63709
  meta: {
63455
63710
  name: "fetch",
63456
63711
  description: `Read a page an ordinary web fetch could not. Bot walls and JavaScript-rendered pages are resolved for you \u2014 you never have to retry, wait, or drive a browser yourself.
@@ -63527,7 +63782,7 @@ Examples:
63527
63782
  });
63528
63783
 
63529
63784
  // src/commands/research/intent.ts
63530
- import { defineCommand as defineCommand188 } from "citty";
63785
+ import { defineCommand as defineCommand194 } from "citty";
63531
63786
  registerSchema({
63532
63787
  command: "research.intent",
63533
63788
  description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
@@ -63550,7 +63805,7 @@ var FIELDS7 = {
63550
63805
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
63551
63806
  probability: "Confidence score 0.0-1.0"
63552
63807
  };
63553
- var intentCommand = defineCommand188({
63808
+ var intentCommand = defineCommand194({
63554
63809
  meta: {
63555
63810
  name: "intent",
63556
63811
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -63598,7 +63853,7 @@ Examples:
63598
63853
  });
63599
63854
 
63600
63855
  // src/commands/research/keyword-gap.ts
63601
- import { defineCommand as defineCommand189 } from "citty";
63856
+ import { defineCommand as defineCommand195 } from "citty";
63602
63857
  registerSchema({
63603
63858
  command: "research.keyword-gap",
63604
63859
  description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -63627,7 +63882,7 @@ var FIELDS8 = {
63627
63882
  cpc: "Cost per click USD",
63628
63883
  their_position: "Competitor's ranking position"
63629
63884
  };
63630
- var keywordGapCommand = defineCommand189({
63885
+ var keywordGapCommand = defineCommand195({
63631
63886
  meta: {
63632
63887
  name: "keyword-gap",
63633
63888
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -63702,7 +63957,7 @@ Examples:
63702
63957
  });
63703
63958
 
63704
63959
  // src/commands/research/keyword-metrics.ts
63705
- import { defineCommand as defineCommand190 } from "citty";
63960
+ import { defineCommand as defineCommand196 } from "citty";
63706
63961
 
63707
63962
  // src/commands/research/keyword-metrics-rows.ts
63708
63963
  var KEYWORD_METRICS_SOURCE = "keyword_planner_estimate_via_dataforseo";
@@ -63782,7 +64037,7 @@ registerSchema({
63782
64037
  "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false }
63783
64038
  }
63784
64039
  });
63785
- var keywordMetricsCommand = defineCommand190({
64040
+ var keywordMetricsCommand = defineCommand196({
63786
64041
  meta: {
63787
64042
  name: "keyword-metrics",
63788
64043
  description: `Volume, CPC and competition for keywords you name. No domain required.
@@ -63864,7 +64119,7 @@ Examples:
63864
64119
  });
63865
64120
 
63866
64121
  // src/commands/research/keywords-for-site.ts
63867
- import { defineCommand as defineCommand191 } from "citty";
64122
+ import { defineCommand as defineCommand197 } from "citty";
63868
64123
  registerSchema({
63869
64124
  command: "research.keywords-for-site",
63870
64125
  description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -63897,7 +64152,7 @@ var FIELDS9 = {
63897
64152
  competition: "LOW, MEDIUM, or HIGH",
63898
64153
  competition_index: "Competition score 0-100"
63899
64154
  };
63900
- var keywordsForSiteCommand = defineCommand191({
64155
+ var keywordsForSiteCommand = defineCommand197({
63901
64156
  meta: {
63902
64157
  name: "keywords-for-site",
63903
64158
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -63959,7 +64214,7 @@ Examples:
63959
64214
  });
63960
64215
 
63961
64216
  // src/commands/research/languages.ts
63962
- import { defineCommand as defineCommand192 } from "citty";
64217
+ import { defineCommand as defineCommand198 } from "citty";
63963
64218
  registerSchema({
63964
64219
  command: "research.languages",
63965
64220
  description: "List all supported language codes for --language flag in research commands.",
@@ -63989,7 +64244,7 @@ var FIELDS10 = {
63989
64244
  code: "Language code to pass as --language",
63990
64245
  name: "Language name (also accepted by --language)"
63991
64246
  };
63992
- var languagesCommand2 = defineCommand192({
64247
+ var languagesCommand2 = defineCommand198({
63993
64248
  meta: {
63994
64249
  name: "languages",
63995
64250
  description: "List all supported language codes for --language flag."
@@ -64000,7 +64255,7 @@ var languagesCommand2 = defineCommand192({
64000
64255
  });
64001
64256
 
64002
64257
  // src/commands/research/lighthouse.ts
64003
- import { defineCommand as defineCommand193 } from "citty";
64258
+ import { defineCommand as defineCommand199 } from "citty";
64004
64259
  registerSchema({
64005
64260
  command: "research.lighthouse",
64006
64261
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -64019,7 +64274,7 @@ var FIELDS11 = {
64019
64274
  speed_index_ms: "Speed Index in ms (good: < 3400)",
64020
64275
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
64021
64276
  };
64022
- var lighthouseCommand = defineCommand193({
64277
+ var lighthouseCommand = defineCommand199({
64023
64278
  meta: {
64024
64279
  name: "lighthouse",
64025
64280
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -64057,7 +64312,7 @@ Examples:
64057
64312
  });
64058
64313
 
64059
64314
  // src/commands/research/relevant-pages.ts
64060
- import { defineCommand as defineCommand194 } from "citty";
64315
+ import { defineCommand as defineCommand200 } from "citty";
64061
64316
  registerSchema({
64062
64317
  command: "research.relevant-pages",
64063
64318
  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).",
@@ -64083,7 +64338,7 @@ var FIELDS12 = {
64083
64338
  keywords: "Total organic keywords the page ranks for",
64084
64339
  top_10: "Keywords in positions 1-10"
64085
64340
  };
64086
- var relevantPagesCommand = defineCommand194({
64341
+ var relevantPagesCommand = defineCommand200({
64087
64342
  meta: {
64088
64343
  name: "relevant-pages",
64089
64344
  description: `Get the top pages of a competitor domain with traffic data.
@@ -64130,7 +64385,7 @@ Examples:
64130
64385
  });
64131
64386
 
64132
64387
  // src/commands/research/web.ts
64133
- import { defineCommand as defineCommand195 } from "citty";
64388
+ import { defineCommand as defineCommand201 } from "citty";
64134
64389
  registerSchema({
64135
64390
  command: "research.web",
64136
64391
  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).",
@@ -64181,7 +64436,7 @@ async function runDeepResearch(question) {
64181
64436
  }
64182
64437
  throw new Error("Deep research timed out");
64183
64438
  }
64184
- var webCommand = defineCommand195({
64439
+ var webCommand = defineCommand201({
64185
64440
  meta: {
64186
64441
  name: "web",
64187
64442
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -64243,7 +64498,7 @@ Examples:
64243
64498
  });
64244
64499
 
64245
64500
  // src/commands/research/index.ts
64246
- var researchCommand = defineCommand196({
64501
+ var researchCommand = defineCommand202({
64247
64502
  meta: {
64248
64503
  name: "research",
64249
64504
  description: `Competitive intelligence and AI-powered research commands.
@@ -64290,10 +64545,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
64290
64545
  });
64291
64546
 
64292
64547
  // src/commands/scheduled-agents/index.ts
64293
- import { defineCommand as defineCommand207 } from "citty";
64548
+ import { defineCommand as defineCommand213 } from "citty";
64294
64549
 
64295
64550
  // src/commands/scheduled-agents/create.ts
64296
- import { defineCommand as defineCommand197 } from "citty";
64551
+ import { defineCommand as defineCommand203 } from "citty";
64297
64552
 
64298
64553
  // src/commands/scheduled-agents/shared.ts
64299
64554
  var TEMP_SCHEDULED_AGENT_PREFIX = "temp_agent_";
@@ -64660,7 +64915,7 @@ registerSchema({
64660
64915
  }
64661
64916
  }
64662
64917
  });
64663
- var createCommand3 = defineCommand197({
64918
+ var createCommand3 = defineCommand203({
64664
64919
  meta: {
64665
64920
  name: "create",
64666
64921
  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'
@@ -64717,7 +64972,7 @@ var createCommand3 = defineCommand197({
64717
64972
  });
64718
64973
 
64719
64974
  // src/commands/scheduled-agents/delete.ts
64720
- import { defineCommand as defineCommand198 } from "citty";
64975
+ import { defineCommand as defineCommand204 } from "citty";
64721
64976
  registerSchema({
64722
64977
  command: "scheduled-agents.delete",
64723
64978
  description: "Stage deletion of a published scheduled agent, or cancel one this chat staged (temp_agent_*).",
@@ -64725,7 +64980,7 @@ registerSchema({
64725
64980
  id: { type: "string", description: "Published scheduled agent ID or temp_agent_* draft ID", required: true }
64726
64981
  }
64727
64982
  });
64728
- var deleteCommand3 = defineCommand198({
64983
+ var deleteCommand3 = defineCommand204({
64729
64984
  meta: {
64730
64985
  name: "delete",
64731
64986
  description: "Stage a scheduled agent deletion. Example: baker scheduled-agents delete <id-or-temp_agent_id>"
@@ -64754,7 +65009,7 @@ var deleteCommand3 = defineCommand198({
64754
65009
  });
64755
65010
 
64756
65011
  // src/commands/scheduled-agents/get.ts
64757
- import { defineCommand as defineCommand199 } from "citty";
65012
+ import { defineCommand as defineCommand205 } from "citty";
64758
65013
  registerSchema({
64759
65014
  command: "scheduled-agents.get",
64760
65015
  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.",
@@ -64763,7 +65018,7 @@ registerSchema({
64763
65018
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
64764
65019
  }
64765
65020
  });
64766
- var getCommand4 = defineCommand199({
65021
+ var getCommand4 = defineCommand205({
64767
65022
  meta: {
64768
65023
  name: "get",
64769
65024
  description: "Get a scheduled agent. Example: baker scheduled-agents get <id-or-temp_agent_id>"
@@ -64802,7 +65057,7 @@ var getCommand4 = defineCommand199({
64802
65057
  });
64803
65058
 
64804
65059
  // src/commands/scheduled-agents/list.ts
64805
- import { defineCommand as defineCommand200 } from "citty";
65060
+ import { defineCommand as defineCommand206 } from "citty";
64806
65061
  var FULL_DESCRIPTION = "Return every item and staged change as stored, briefs included (tens of KB). Prefer `get <id>` for one item.";
64807
65062
  registerSchema({
64808
65063
  command: "scheduled-agents.list",
@@ -64812,7 +65067,7 @@ registerSchema({
64812
65067
  full: { type: "boolean", description: FULL_DESCRIPTION, required: false }
64813
65068
  }
64814
65069
  });
64815
- var listCommand17 = defineCommand200({
65070
+ var listCommand18 = defineCommand206({
64816
65071
  meta: {
64817
65072
  name: "list",
64818
65073
  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."
@@ -64838,7 +65093,7 @@ var listCommand17 = defineCommand200({
64838
65093
  });
64839
65094
 
64840
65095
  // src/commands/scheduled-agents/run.ts
64841
- import { defineCommand as defineCommand201 } from "citty";
65096
+ import { defineCommand as defineCommand207 } from "citty";
64842
65097
  registerSchema({
64843
65098
  command: "scheduled-agents.run",
64844
65099
  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.",
@@ -64846,7 +65101,7 @@ registerSchema({
64846
65101
  id: { type: "string", description: "Published scheduled agent ID", required: true }
64847
65102
  }
64848
65103
  });
64849
- var runCommand2 = defineCommand201({
65104
+ var runCommand2 = defineCommand207({
64850
65105
  meta: {
64851
65106
  name: "run",
64852
65107
  description: "Run a published scheduled agent now. Example: baker scheduled-agents run <id>"
@@ -64878,7 +65133,7 @@ var runCommand2 = defineCommand201({
64878
65133
  });
64879
65134
 
64880
65135
  // src/commands/scheduled-agents/targets.ts
64881
- import { defineCommand as defineCommand202 } from "citty";
65136
+ import { defineCommand as defineCommand208 } from "citty";
64882
65137
  var ARGS2 = {
64883
65138
  trigger: {
64884
65139
  type: "string",
@@ -64912,7 +65167,7 @@ registerSchema({
64912
65167
  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.",
64913
65168
  args: ARGS2
64914
65169
  });
64915
- var targetsCommand = defineCommand202({
65170
+ var targetsCommand = defineCommand208({
64916
65171
  meta: {
64917
65172
  name: "targets",
64918
65173
  description: `What a trigger can be pointed at \u2014 so "when a Task is picked up" becomes "when THIS Task is picked up".
@@ -64955,7 +65210,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
64955
65210
  // src/commands/scheduled-agents/templates.ts
64956
65211
  import { readFile as readFile32 } from "fs/promises";
64957
65212
  import path46 from "path";
64958
- import { defineCommand as defineCommand203 } from "citty";
65213
+ import { defineCommand as defineCommand209 } from "citty";
64959
65214
  registerSchema({
64960
65215
  command: "scheduled-agents.templates",
64961
65216
  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.",
@@ -65010,7 +65265,7 @@ registerSchema({
65010
65265
  }
65011
65266
  }
65012
65267
  });
65013
- var templatesCommand = defineCommand203({
65268
+ var templatesCommand = defineCommand209({
65014
65269
  meta: {
65015
65270
  name: "templates",
65016
65271
  description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
@@ -65106,7 +65361,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
65106
65361
  });
65107
65362
 
65108
65363
  // src/commands/scheduled-agents/triggers.ts
65109
- import { defineCommand as defineCommand204 } from "citty";
65364
+ import { defineCommand as defineCommand210 } from "citty";
65110
65365
  var ARGS3 = {
65111
65366
  trigger: {
65112
65367
  type: "string",
@@ -65192,7 +65447,7 @@ function shapeTriggerCatalog(response, filter) {
65192
65447
  }
65193
65448
  return { ok: true, data: { groups, reacting: catalog.reacting } };
65194
65449
  }
65195
- var triggersCommand2 = defineCommand204({
65450
+ var triggersCommand2 = defineCommand210({
65196
65451
  meta: {
65197
65452
  name: "triggers",
65198
65453
  description: `Everything that can start a scheduled agent.
@@ -65222,7 +65477,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
65222
65477
  });
65223
65478
 
65224
65479
  // src/commands/scheduled-agents/unstage.ts
65225
- import { defineCommand as defineCommand205 } from "citty";
65480
+ import { defineCommand as defineCommand211 } from "citty";
65226
65481
  registerSchema({
65227
65482
  command: "scheduled-agents.unstage",
65228
65483
  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.",
@@ -65248,7 +65503,7 @@ function unstageBody(args5, chatId) {
65248
65503
  }
65249
65504
  return { chatId, ref: args5.ref, index };
65250
65505
  }
65251
- var unstageCommand = defineCommand205({
65506
+ var unstageCommand = defineCommand211({
65252
65507
  meta: {
65253
65508
  name: "unstage",
65254
65509
  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"
@@ -65279,7 +65534,7 @@ var unstageCommand = defineCommand205({
65279
65534
  });
65280
65535
 
65281
65536
  // src/commands/scheduled-agents/update.ts
65282
- import { defineCommand as defineCommand206 } from "citty";
65537
+ import { defineCommand as defineCommand212 } from "citty";
65283
65538
  registerSchema({
65284
65539
  command: "scheduled-agents.update",
65285
65540
  description: "Stage an update to a published scheduled agent, or to one this chat staged (temp_agent_*).",
@@ -65306,7 +65561,7 @@ registerSchema({
65306
65561
  enabled: { type: "string", description: "Replacement enabled state", required: false, enum: ["true", "false"] }
65307
65562
  }
65308
65563
  });
65309
- var updateCommand4 = defineCommand206({
65564
+ var updateCommand4 = defineCommand212({
65310
65565
  meta: {
65311
65566
  name: "update",
65312
65567
  description: "Stage a scheduled agent update. Examples: baker scheduled-agents update <id> --enabled false | baker scheduled-agents update <id> --trigger form.submitted --form quote"
@@ -65377,7 +65632,7 @@ var updateCommand4 = defineCommand206({
65377
65632
  });
65378
65633
 
65379
65634
  // src/commands/scheduled-agents/index.ts
65380
- var scheduledAgentsCommand = defineCommand207({
65635
+ var scheduledAgentsCommand = defineCommand213({
65381
65636
  meta: {
65382
65637
  name: "scheduled-agents",
65383
65638
  description: `Manage Scheduled agents. Subcommands: list, get, triggers, targets, create, update, delete, unstage, run, templates.
@@ -65406,7 +65661,7 @@ Examples:
65406
65661
  Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
65407
65662
  },
65408
65663
  subCommands: {
65409
- list: listCommand17,
65664
+ list: listCommand18,
65410
65665
  get: getCommand4,
65411
65666
  triggers: triggersCommand2,
65412
65667
  targets: targetsCommand,
@@ -65420,14 +65675,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
65420
65675
  });
65421
65676
 
65422
65677
  // src/commands/schema.ts
65423
- import { defineCommand as defineCommand208 } from "citty";
65678
+ import { defineCommand as defineCommand214 } from "citty";
65424
65679
  function narrowToFamily(commandName, available) {
65425
65680
  const segments = commandName.split(".");
65426
65681
  const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
65427
65682
  const siblings = available.filter((name) => name.startsWith(prefix));
65428
65683
  return siblings.length > 0 ? siblings : available;
65429
65684
  }
65430
- var schemaCommand2 = defineCommand208({
65685
+ var schemaCommand2 = defineCommand214({
65431
65686
  meta: {
65432
65687
  name: "schema",
65433
65688
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images search (or images.search)"
@@ -65472,10 +65727,10 @@ var schemaCommand2 = defineCommand208({
65472
65727
  });
65473
65728
 
65474
65729
  // src/commands/studio/index.ts
65475
- import { defineCommand as defineCommand217 } from "citty";
65730
+ import { defineCommand as defineCommand223 } from "citty";
65476
65731
 
65477
65732
  // src/commands/studio/animate.ts
65478
- import { defineCommand as defineCommand209 } from "citty";
65733
+ import { defineCommand as defineCommand215 } from "citty";
65479
65734
 
65480
65735
  // src/commands/studio/spokenPace.ts
65481
65736
  var WORDS_PER_SECOND3 = SPOKEN_WORDS_PER_SECOND;
@@ -65701,7 +65956,7 @@ function parseImageRefs(spec) {
65701
65956
  }
65702
65957
  var defaultDeps = {
65703
65958
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
65704
- upload: (path48) => uploadLocalImage({ file: path48, contentType: detectImageContentType(path48), source: "uploaded" })
65959
+ upload: (path49) => uploadLocalImage({ file: path49, contentType: detectImageContentType(path49), source: "uploaded" })
65705
65960
  };
65706
65961
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
65707
65962
  const refs = parseImageRefs(spec);
@@ -65721,10 +65976,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
65721
65976
  }
65722
65977
  return { imageIds, added };
65723
65978
  }
65724
- function uploadFailure(path48) {
65979
+ function uploadFailure(path49) {
65725
65980
  return (error) => {
65726
65981
  if (error instanceof ApiError) throw error;
65727
- throw new ApiError("VALIDATION_ERROR", `Could not read "${path48}" as an image.`);
65982
+ throw new ApiError("VALIDATION_ERROR", `Could not read "${path49}" as an image.`);
65728
65983
  };
65729
65984
  }
65730
65985
 
@@ -65988,7 +66243,7 @@ async function adByHandHintFor(body) {
65988
66243
  return null;
65989
66244
  }
65990
66245
  }
65991
- var animateCommand = defineCommand209({
66246
+ var animateCommand = defineCommand215({
65992
66247
  meta: {
65993
66248
  name: "animate",
65994
66249
  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"
@@ -66113,7 +66368,7 @@ var animateCommand = defineCommand209({
66113
66368
  });
66114
66369
 
66115
66370
  // src/commands/studio/generate.ts
66116
- import { defineCommand as defineCommand210 } from "citty";
66371
+ import { defineCommand as defineCommand216 } from "citty";
66117
66372
  var MODEL_LIST2 = IMAGE_MODEL_IDS;
66118
66373
  var DEFAULT_MAX_WAIT_MS2 = 24e4;
66119
66374
  registerSchema({
@@ -66249,7 +66504,7 @@ function buildGenerateBody(args5, prompt) {
66249
66504
  }
66250
66505
  return body;
66251
66506
  }
66252
- var generateCommand = defineCommand210({
66507
+ var generateCommand = defineCommand216({
66253
66508
  meta: {
66254
66509
  name: "generate",
66255
66510
  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-preview (default \u2014 fast, best at editing a reference and at extreme ratios), google/gemini-3-pro-image-preview (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]]'"
@@ -66332,7 +66587,7 @@ var generateCommand = defineCommand210({
66332
66587
  });
66333
66588
 
66334
66589
  // src/commands/studio/get.ts
66335
- import { defineCommand as defineCommand211 } from "citty";
66590
+ import { defineCommand as defineCommand217 } from "citty";
66336
66591
  registerSchema({
66337
66592
  command: "studio.get",
66338
66593
  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.",
@@ -66341,7 +66596,7 @@ registerSchema({
66341
66596
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
66342
66597
  }
66343
66598
  });
66344
- var getCommand5 = defineCommand211({
66599
+ var getCommand5 = defineCommand217({
66345
66600
  meta: {
66346
66601
  name: "get",
66347
66602
  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"
@@ -66370,7 +66625,7 @@ var getCommand5 = defineCommand211({
66370
66625
  });
66371
66626
 
66372
66627
  // src/commands/studio/improve.ts
66373
- import { defineCommand as defineCommand212 } from "citty";
66628
+ import { defineCommand as defineCommand218 } from "citty";
66374
66629
  var DESCRIPTION2 = "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.";
66375
66630
  registerSchema({
66376
66631
  command: "studio.improve",
@@ -66395,7 +66650,7 @@ registerSchema({
66395
66650
  }
66396
66651
  }
66397
66652
  });
66398
- var improveCommand = defineCommand212({
66653
+ var improveCommand = defineCommand218({
66399
66654
  meta: {
66400
66655
  name: "improve",
66401
66656
  description: `${DESCRIPTION2}
@@ -66439,7 +66694,7 @@ Examples:
66439
66694
  });
66440
66695
 
66441
66696
  // src/commands/studio/keep.ts
66442
- import { defineCommand as defineCommand213 } from "citty";
66697
+ import { defineCommand as defineCommand219 } from "citty";
66443
66698
  registerSchema({
66444
66699
  command: "studio.keep",
66445
66700
  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.",
@@ -66449,7 +66704,7 @@ registerSchema({
66449
66704
  undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
66450
66705
  }
66451
66706
  });
66452
- var keepCommand = defineCommand213({
66707
+ var keepCommand = defineCommand219({
66453
66708
  meta: {
66454
66709
  name: "keep",
66455
66710
  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"
@@ -66500,7 +66755,7 @@ var keepCommand = defineCommand213({
66500
66755
  });
66501
66756
 
66502
66757
  // src/commands/studio/list.ts
66503
- import { defineCommand as defineCommand214 } from "citty";
66758
+ import { defineCommand as defineCommand220 } from "citty";
66504
66759
  registerSchema({
66505
66760
  command: "studio.list",
66506
66761
  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.",
@@ -66511,7 +66766,7 @@ registerSchema({
66511
66766
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
66512
66767
  }
66513
66768
  });
66514
- var listCommand18 = defineCommand214({
66769
+ var listCommand19 = defineCommand220({
66515
66770
  meta: {
66516
66771
  name: "list",
66517
66772
  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"
@@ -66545,7 +66800,7 @@ var listCommand18 = defineCommand214({
66545
66800
  });
66546
66801
 
66547
66802
  // src/commands/studio/models.ts
66548
- import { defineCommand as defineCommand215 } from "citty";
66803
+ import { defineCommand as defineCommand221 } from "citty";
66549
66804
  var DESCRIPTION3 = "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.";
66550
66805
  registerSchema({
66551
66806
  command: "studio.models",
@@ -66632,7 +66887,7 @@ function buildModelCards(kind, model) {
66632
66887
  const selected = model ? ids.filter((id) => id === model) : ids;
66633
66888
  return selected.map((id) => build(id));
66634
66889
  }
66635
- var modelsCommand = defineCommand215({
66890
+ var modelsCommand = defineCommand221({
66636
66891
  meta: {
66637
66892
  name: "models",
66638
66893
  description: `${DESCRIPTION3}
@@ -66679,13 +66934,13 @@ Examples:
66679
66934
  });
66680
66935
 
66681
66936
  // src/commands/studio/skills.ts
66682
- import { defineCommand as defineCommand216 } from "citty";
66937
+ import { defineCommand as defineCommand222 } from "citty";
66683
66938
  registerSchema({
66684
66939
  command: "studio.skills",
66685
66940
  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.",
66686
66941
  args: {}
66687
66942
  });
66688
- var skillsCommand = defineCommand216({
66943
+ var skillsCommand = defineCommand222({
66689
66944
  meta: {
66690
66945
  name: "skills",
66691
66946
  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"
@@ -66709,7 +66964,7 @@ var skillsCommand = defineCommand216({
66709
66964
  });
66710
66965
 
66711
66966
  // src/commands/studio/index.ts
66712
- var studioCommand = defineCommand217({
66967
+ var studioCommand = defineCommand223({
66713
66968
  meta: {
66714
66969
  name: "studio",
66715
66970
  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.
@@ -66743,7 +66998,7 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
66743
66998
  generate: generateCommand,
66744
66999
  animate: animateCommand,
66745
67000
  get: getCommand5,
66746
- list: listCommand18,
67001
+ list: listCommand19,
66747
67002
  keep: keepCommand,
66748
67003
  models: modelsCommand,
66749
67004
  skills: skillsCommand,
@@ -66752,10 +67007,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
66752
67007
  });
66753
67008
 
66754
67009
  // src/commands/tag-manager/index.ts
66755
- import { defineCommand as defineCommand221 } from "citty";
67010
+ import { defineCommand as defineCommand227 } from "citty";
66756
67011
 
66757
67012
  // src/commands/tag-manager/draft.ts
66758
- import { defineCommand as defineCommand218 } from "citty";
67013
+ import { defineCommand as defineCommand224 } from "citty";
66759
67014
 
66760
67015
  // src/commands/tag-manager/shared.ts
66761
67016
  import { readFileSync as readFileSync21 } from "fs";
@@ -66816,19 +67071,17 @@ var STAGE_HINTS2 = [
66816
67071
  async function stageOp4(op) {
66817
67072
  const chatId = requireChatId();
66818
67073
  try {
66819
- const { data, hints: hints2 } = splitHints(
66820
- await apiPost("/api/tag-manager/draft/stage", { chatId, op })
66821
- );
66822
- writeJsonEnvelope({ ok: true, data, hints: [...hints2, ...STAGE_HINTS2] });
67074
+ const data = await apiPost("/api/tag-manager/draft/stage", { chatId, op });
67075
+ writeJsonEnvelope({ ok: true, data, hints: STAGE_HINTS2 });
66823
67076
  } catch (err) {
66824
67077
  handleError8(err);
66825
67078
  }
66826
67079
  }
66827
- async function draftAction3(path48, body, chat) {
67080
+ async function draftAction3(path49, body, chat) {
66828
67081
  const chatId = resolveChatId(chat);
66829
67082
  try {
66830
- const { data, hints: hints2 } = splitHints(await apiPost(path48, { chatId, ...body }));
66831
- writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
67083
+ const data = await apiPost(path49, { chatId, ...body });
67084
+ writeJsonEnvelope({ ok: true, data });
66832
67085
  return data;
66833
67086
  } catch (err) {
66834
67087
  handleError8(err);
@@ -66859,13 +67112,12 @@ function renderDraft2(response) {
66859
67112
  async function draftList2(json, chat) {
66860
67113
  const chatId = resolveChatId(chat);
66861
67114
  try {
66862
- const response = await apiPost("/api/tag-manager/draft", { chatId });
66863
- const { data, hints: hints2 } = splitHints(response);
67115
+ const data = await apiPost("/api/tag-manager/draft", { chatId });
66864
67116
  if (json) {
66865
- writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
67117
+ writeJsonEnvelope({ ok: true, data });
66866
67118
  return;
66867
67119
  }
66868
- process.stdout.write(`${renderDraft2(data)}${hintLines(response)}
67120
+ process.stdout.write(`${renderDraft2(data)}
66869
67121
  `);
66870
67122
  } catch (err) {
66871
67123
  handleError8(err);
@@ -66881,13 +67133,13 @@ registerSchema({
66881
67133
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
66882
67134
  }
66883
67135
  });
66884
- var draftCommand5 = defineCommand218({
67136
+ var draftCommand5 = defineCommand224({
66885
67137
  meta: {
66886
67138
  name: "draft",
66887
67139
  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."
66888
67140
  },
66889
67141
  subCommands: {
66890
- list: defineCommand218({
67142
+ list: defineCommand224({
66891
67143
  meta: {
66892
67144
  name: "list",
66893
67145
  description: "Review everything staged on this chat (--json for the raw envelope)"
@@ -66900,7 +67152,7 @@ var draftCommand5 = defineCommand218({
66900
67152
  await draftList2(args5.json === true, args5.chat);
66901
67153
  }
66902
67154
  }),
66903
- show: defineCommand218({
67155
+ show: defineCommand224({
66904
67156
  meta: {
66905
67157
  name: "show",
66906
67158
  description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
@@ -66917,7 +67169,7 @@ var draftCommand5 = defineCommand218({
66917
67169
  );
66918
67170
  }
66919
67171
  }),
66920
- amend: defineCommand218({
67172
+ amend: defineCommand224({
66921
67173
  meta: {
66922
67174
  name: "amend",
66923
67175
  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."
@@ -66934,7 +67186,7 @@ var draftCommand5 = defineCommand218({
66934
67186
  });
66935
67187
  }
66936
67188
  }),
66937
- remove: defineCommand218({
67189
+ remove: defineCommand224({
66938
67190
  meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
66939
67191
  args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
66940
67192
  run: async ({ args: args5 }) => {
@@ -66943,7 +67195,7 @@ var draftCommand5 = defineCommand218({
66943
67195
  });
66944
67196
  }
66945
67197
  }),
66946
- clear: defineCommand218({
67198
+ clear: defineCommand224({
66947
67199
  meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
66948
67200
  run: async () => {
66949
67201
  await draftAction3("/api/tag-manager/draft/clear", {});
@@ -66953,7 +67205,7 @@ var draftCommand5 = defineCommand218({
66953
67205
  });
66954
67206
 
66955
67207
  // src/commands/tag-manager/read.ts
66956
- import { defineCommand as defineCommand219 } from "citty";
67208
+ import { defineCommand as defineCommand225 } from "citty";
66957
67209
  registerSchema({
66958
67210
  command: "tagManager.containers",
66959
67211
  description: "List the Google Tag Manager containers this company's connection can reach. Every container the company connected is flagged `connected: true` \u2014 there can be several, and Baker may read and change all of them. Start here to confirm which containers you are managing.",
@@ -66994,7 +67246,7 @@ function containersHints(containers) {
66994
67246
  }))
66995
67247
  });
66996
67248
  }
66997
- var containersCommand = defineCommand219({
67249
+ var containersCommand = defineCommand225({
66998
67250
  meta: {
66999
67251
  name: "containers",
67000
67252
  description: `List Tag Manager containers reachable by this company's connection.
@@ -67011,7 +67263,7 @@ Start here:
67011
67263
  }
67012
67264
  }
67013
67265
  });
67014
- var readCommand = defineCommand219({
67266
+ var readCommand = defineCommand225({
67015
67267
  meta: {
67016
67268
  name: "read",
67017
67269
  description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
@@ -67053,7 +67305,7 @@ Examples:
67053
67305
  });
67054
67306
 
67055
67307
  // src/commands/tag-manager/write-commands.ts
67056
- import { defineCommand as defineCommand220 } from "citty";
67308
+ import { defineCommand as defineCommand226 } from "citty";
67057
67309
  var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
67058
67310
  var ENTITIES = [
67059
67311
  {
@@ -67109,10 +67361,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
67109
67361
  });
67110
67362
  }
67111
67363
  function entityCommand(entity, noun, example) {
67112
- return defineCommand220({
67364
+ return defineCommand226({
67113
67365
  meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
67114
67366
  subCommands: {
67115
- create: defineCommand220({
67367
+ create: defineCommand226({
67116
67368
  meta: {
67117
67369
  name: "create",
67118
67370
  description: `Stage a new ${noun}
@@ -67134,7 +67386,7 @@ Examples:
67134
67386
  });
67135
67387
  }
67136
67388
  }),
67137
- update: defineCommand220({
67389
+ update: defineCommand226({
67138
67390
  meta: {
67139
67391
  name: "update",
67140
67392
  description: `Stage an update to an existing ${noun} (pass its id or path)`
@@ -67154,7 +67406,7 @@ Examples:
67154
67406
  });
67155
67407
  }
67156
67408
  }),
67157
- delete: defineCommand220({
67409
+ delete: defineCommand226({
67158
67410
  meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
67159
67411
  args: {
67160
67412
  id: { type: "positional", description: `${noun} id or path`, required: false },
@@ -67195,7 +67447,7 @@ function builtinTypes(args5) {
67195
67447
  }
67196
67448
  return raw.split(",").map((entry) => entry.trim());
67197
67449
  }
67198
- var builtinCommand = defineCommand220({
67450
+ var builtinCommand = defineCommand226({
67199
67451
  meta: {
67200
67452
  name: "builtin",
67201
67453
  description: `Enable or disable built-in variables
@@ -67205,7 +67457,7 @@ Examples:
67205
67457
  baker tag-manager builtin disable --types formId`
67206
67458
  },
67207
67459
  subCommands: {
67208
- enable: defineCommand220({
67460
+ enable: defineCommand226({
67209
67461
  meta: { name: "enable", description: "Stage enabling built-in variables" },
67210
67462
  args: {
67211
67463
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -67219,7 +67471,7 @@ Examples:
67219
67471
  });
67220
67472
  }
67221
67473
  }),
67222
- disable: defineCommand220({
67474
+ disable: defineCommand226({
67223
67475
  meta: { name: "disable", description: "Stage disabling built-in variables" },
67224
67476
  args: {
67225
67477
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -67237,7 +67489,7 @@ Examples:
67237
67489
  });
67238
67490
 
67239
67491
  // src/commands/tag-manager/index.ts
67240
- var tagManagerCommand = defineCommand221({
67492
+ var tagManagerCommand = defineCommand227({
67241
67493
  meta: {
67242
67494
  name: "tag-manager",
67243
67495
  description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
@@ -67274,7 +67526,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
67274
67526
  });
67275
67527
 
67276
67528
  // src/commands/tags/index.ts
67277
- import { defineCommand as defineCommand222 } from "citty";
67529
+ import { defineCommand as defineCommand228 } from "citty";
67278
67530
 
67279
67531
  // src/commands/tags/shared.ts
67280
67532
  function failApi3(err) {
@@ -67354,7 +67606,7 @@ async function listTags(json) {
67354
67606
  var listArgs12 = {
67355
67607
  json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
67356
67608
  };
67357
- var listCommand19 = defineCommand222({
67609
+ var listCommand20 = defineCommand228({
67358
67610
  meta: {
67359
67611
  name: "list",
67360
67612
  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"
@@ -67373,7 +67625,7 @@ async function listDraft4(chat) {
67373
67625
  failApi3(err);
67374
67626
  }
67375
67627
  }
67376
- var draftCommand6 = defineCommand222({
67628
+ var draftCommand6 = defineCommand228({
67377
67629
  meta: {
67378
67630
  name: "draft",
67379
67631
  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."
@@ -67383,7 +67635,7 @@ var draftCommand6 = defineCommand222({
67383
67635
  await listDraft4(args5.chat);
67384
67636
  }
67385
67637
  });
67386
- var tagsCommand4 = defineCommand222({
67638
+ var tagsCommand4 = defineCommand228({
67387
67639
  meta: {
67388
67640
  name: "tags",
67389
67641
  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.
@@ -67400,7 +67652,7 @@ Examples:
67400
67652
  Full guide: __tooling__/docs/tools/baker/tags.md`
67401
67653
  },
67402
67654
  subCommands: {
67403
- list: listCommand19,
67655
+ list: listCommand20,
67404
67656
  draft: draftCommand6
67405
67657
  },
67406
67658
  // Bare `baker tags` lists the effective tags. `args` stays so citty knows this
@@ -67412,10 +67664,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
67412
67664
  });
67413
67665
 
67414
67666
  // src/commands/testimonials/index.ts
67415
- import { defineCommand as defineCommand226 } from "citty";
67667
+ import { defineCommand as defineCommand232 } from "citty";
67416
67668
 
67417
67669
  // src/commands/testimonials/get.ts
67418
- import { defineCommand as defineCommand223 } from "citty";
67670
+ import { defineCommand as defineCommand229 } from "citty";
67419
67671
  registerSchema({
67420
67672
  command: "testimonials.get",
67421
67673
  description: "Get a single testimonial by ID",
@@ -67423,7 +67675,7 @@ registerSchema({
67423
67675
  id: { type: "string", description: "Testimonial ID", required: true }
67424
67676
  }
67425
67677
  });
67426
- var getCommand6 = defineCommand223({
67678
+ var getCommand6 = defineCommand229({
67427
67679
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
67428
67680
  args: {
67429
67681
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -67460,7 +67712,7 @@ var getCommand6 = defineCommand223({
67460
67712
  });
67461
67713
 
67462
67714
  // src/commands/testimonials/list.ts
67463
- import { defineCommand as defineCommand224 } from "citty";
67715
+ import { defineCommand as defineCommand230 } from "citty";
67464
67716
 
67465
67717
  // src/commands/testimonials/emptyCorpusHints.ts
67466
67718
  function resolveEmptyReason({
@@ -67617,7 +67869,7 @@ function buildListParams(args5) {
67617
67869
  }
67618
67870
  return params;
67619
67871
  }
67620
- var listCommand20 = defineCommand224({
67872
+ var listCommand21 = defineCommand230({
67621
67873
  meta: {
67622
67874
  name: "list",
67623
67875
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -67677,7 +67929,7 @@ var listCommand20 = defineCommand224({
67677
67929
  });
67678
67930
 
67679
67931
  // src/commands/testimonials/search.ts
67680
- import { defineCommand as defineCommand225 } from "citty";
67932
+ import { defineCommand as defineCommand231 } from "citty";
67681
67933
  var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
67682
67934
  function languageBiasHint(results, requestedLanguage) {
67683
67935
  if (requestedLanguage) {
@@ -67756,7 +68008,7 @@ function buildSearchRequest(query, args5) {
67756
68008
  }
67757
68009
  return body;
67758
68010
  }
67759
- var searchCommand3 = defineCommand225({
68011
+ var searchCommand3 = defineCommand231({
67760
68012
  meta: {
67761
68013
  name: "search",
67762
68014
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -67820,7 +68072,7 @@ var searchCommand3 = defineCommand225({
67820
68072
  var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
67821
68073
 
67822
68074
  // src/commands/testimonials/index.ts
67823
- var testimonialsCommand = defineCommand226({
68075
+ var testimonialsCommand = defineCommand232({
67824
68076
  meta: {
67825
68077
  name: "testimonials",
67826
68078
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -67836,16 +68088,16 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
67836
68088
  subCommands: {
67837
68089
  get: getCommand6,
67838
68090
  search: searchCommand3,
67839
- list: listCommand20,
68091
+ list: listCommand21,
67840
68092
  tags: tagsCommand5
67841
68093
  }
67842
68094
  });
67843
68095
 
67844
68096
  // src/commands/videos/index.ts
67845
- import { defineCommand as defineCommand234 } from "citty";
68097
+ import { defineCommand as defineCommand241 } from "citty";
67846
68098
 
67847
68099
  // src/commands/videos/delete.ts
67848
- import { defineCommand as defineCommand227 } from "citty";
68100
+ import { defineCommand as defineCommand233 } from "citty";
67849
68101
  registerSchema({
67850
68102
  command: "videos.delete",
67851
68103
  description: "Delete a video by ID",
@@ -67859,7 +68111,7 @@ registerSchema({
67859
68111
  }
67860
68112
  }
67861
68113
  });
67862
- var deleteCommand4 = defineCommand227({
68114
+ var deleteCommand4 = defineCommand233({
67863
68115
  meta: {
67864
68116
  name: "delete",
67865
68117
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -67900,7 +68152,7 @@ var deleteCommand4 = defineCommand227({
67900
68152
  });
67901
68153
 
67902
68154
  // src/commands/videos/describe.ts
67903
- import { defineCommand as defineCommand228 } from "citty";
68155
+ import { defineCommand as defineCommand234 } from "citty";
67904
68156
  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.";
67905
68157
  registerSchema({
67906
68158
  command: "videos.describe",
@@ -67917,7 +68169,7 @@ registerSchema({
67917
68169
  full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
67918
68170
  }
67919
68171
  });
67920
- var describeCommand2 = defineCommand228({
68172
+ var describeCommand2 = defineCommand234({
67921
68173
  meta: {
67922
68174
  name: "describe",
67923
68175
  description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
@@ -67974,108 +68226,12 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
67974
68226
  }
67975
68227
  });
67976
68228
 
67977
- // src/commands/videos/get.ts
67978
- import { defineCommand as defineCommand229 } from "citty";
67979
- registerSchema({
67980
- command: "videos.get",
67981
- description: "Get a single video by ID",
67982
- args: {
67983
- id: { type: "string", description: "Video ID", required: true }
67984
- }
67985
- });
67986
- var getCommand7 = defineCommand229({
67987
- meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
67988
- args: {
67989
- id: { type: "positional", description: "Video ID", required: false },
67990
- "video-id": { type: "string", description: "Video ID (alternative to positional)", required: false },
67991
- output: { type: "string", description: "Output format: json|files|md", required: false, default: "json" },
67992
- fields: { type: "string", description: "Comma-separated field names to include", required: false },
67993
- full: { type: "boolean", description: "Include full metadata", required: false, default: false }
67994
- },
67995
- run: async ({ args: args5 }) => {
67996
- try {
67997
- const id = args5.id || args5["video-id"];
67998
- if (!id) {
67999
- writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Video ID is required" } });
68000
- process.exit(1);
68001
- }
68002
- validateConvexId(id);
68003
- const data = await apiGet("/api/videos/get", { id });
68004
- writeOutput(
68005
- { ok: true, data },
68006
- args5.output || "json",
68007
- args5.fields ? args5.fields.split(",") : void 0,
68008
- args5.full,
68009
- videoNormalizer
68010
- );
68011
- } catch (err) {
68012
- if (err instanceof ApiError) {
68013
- writeJson({ ok: false, error: { code: err.code, message: err.message } });
68014
- process.exit(1);
68015
- }
68016
- writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
68017
- process.exit(1);
68018
- }
68019
- }
68020
- });
68021
-
68022
- // src/commands/videos/group.ts
68023
- import { defineCommand as defineCommand230 } from "citty";
68024
- registerSchema({
68025
- command: "videos.group",
68026
- description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
68027
- args: {
68028
- id: { type: "string", description: "A video or image id \u2014 returns that asset's whole set", required: false },
68029
- "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
68030
- }
68031
- });
68032
- var groupCommand2 = defineCommand230({
68033
- meta: {
68034
- name: "group",
68035
- 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>"
68036
- },
68037
- args: {
68038
- id: { type: "positional", description: "A video or image id from the set", required: false },
68039
- "group-key": { type: "string", description: "The set key, if you already have it", required: false }
68040
- },
68041
- run: async ({ args: args5 }) => {
68042
- await runMediaGroupLookup({
68043
- id: args5.id,
68044
- groupKey: args5["group-key"],
68045
- path: "/api/videos/group",
68046
- idLabel: "a video or image id"
68047
- });
68048
- }
68049
- });
68050
-
68051
- // src/commands/videos/ingest.ts
68052
- import { mkdtemp as mkdtemp3, rm as rm9, stat as stat11 } from "fs/promises";
68229
+ // src/commands/videos/fetch.ts
68230
+ import { constants } from "fs";
68231
+ import { copyFile as copyFile2, mkdir as mkdir15, mkdtemp as mkdtemp3, rm as rm9 } from "fs/promises";
68053
68232
  import { tmpdir as tmpdir4 } from "os";
68054
68233
  import path47 from "path";
68055
- import { defineCommand as defineCommand231 } from "citty";
68056
-
68057
- // src/lib/streamUpload.ts
68058
- import { createHash as createHash2 } from "crypto";
68059
- import { createReadStream } from "fs";
68060
- import { Readable } from "stream";
68061
- import { pipeline } from "stream/promises";
68062
- async function hashFile(filePath) {
68063
- const hash = createHash2("sha256");
68064
- await pipeline(createReadStream(filePath), hash);
68065
- return hash.digest("hex");
68066
- }
68067
- async function putFileStreamed(args5) {
68068
- const body = Readable.toWeb(createReadStream(args5.filePath));
68069
- return await fetch(args5.putUrl, {
68070
- method: "PUT",
68071
- body,
68072
- headers: {
68073
- "Content-Type": args5.contentType,
68074
- "Content-Length": String(args5.sizeBytes)
68075
- },
68076
- duplex: "half"
68077
- });
68078
- }
68234
+ import { defineCommand as defineCommand235 } from "citty";
68079
68235
 
68080
68236
  // src/commands/videos/videoPlatforms.ts
68081
68237
  var PLATFORM_DOMAINS = {
@@ -68084,6 +68240,7 @@ var PLATFORM_DOMAINS = {
68084
68240
  tiktok: ["tiktok.com"],
68085
68241
  twitter: ["twitter.com", "x.com", "vxtwitter.com", "fixvx.com"],
68086
68242
  facebook: ["facebook.com", "fb.watch", "fb.com"],
68243
+ linkedin: ["linkedin.com", "lnkd.in"],
68087
68244
  reddit: ["reddit.com", "redd.it"],
68088
68245
  vimeo: ["vimeo.com"],
68089
68246
  dailymotion: ["dailymotion.com", "dai.ly"],
@@ -68188,6 +68345,218 @@ function safePathname(rawUrl) {
68188
68345
  }
68189
68346
  }
68190
68347
 
68348
+ // src/commands/videos/fetch.ts
68349
+ registerSchema({
68350
+ command: "videos.fetch",
68351
+ description: "Download a video page (YouTube, TikTok, Vimeo, \u2026) to a local .mp4 without adding it to the library. For editing a clip locally; use `videos ingest` to add one to the library.",
68352
+ args: {
68353
+ url: { type: "string", description: "Video page URL", required: true },
68354
+ out: { type: "string", description: "Directory to write the file into (created if missing)", required: true },
68355
+ "max-seconds": {
68356
+ type: "string",
68357
+ description: `Refuse videos longer than this many seconds (default ${MAX_VIDEO_INGEST_SECONDS})`,
68358
+ required: false
68359
+ },
68360
+ "timeout-ms": { type: "string", description: "Download time limit in milliseconds", required: false },
68361
+ "max-height": {
68362
+ type: "string",
68363
+ description: "Keep a version whose shorter side is at most this many pixels (e.g. 1080 \u2014 a vertical Short keeps 1080\xD71920) when the site offers one",
68364
+ required: false
68365
+ }
68366
+ }
68367
+ });
68368
+ function positiveInt(value) {
68369
+ const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
68370
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
68371
+ }
68372
+ var fetchCommand2 = defineCommand235({
68373
+ meta: {
68374
+ name: "fetch",
68375
+ 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"
68376
+ },
68377
+ args: {
68378
+ url: { type: "positional", description: "Video page URL", required: true },
68379
+ out: { type: "string", description: "Directory to write the file into", required: true },
68380
+ "max-seconds": { type: "string", description: "Refuse videos longer than this", required: false },
68381
+ "timeout-ms": { type: "string", description: "Download time limit in milliseconds", required: false },
68382
+ "max-height": {
68383
+ type: "string",
68384
+ description: "Cap the shorter side at this many pixels (e.g. 1080)",
68385
+ required: false
68386
+ }
68387
+ },
68388
+ run: async ({ args: args5 }) => {
68389
+ const result = await fetchVideo({
68390
+ url: String(args5.url ?? ""),
68391
+ out: String(args5.out ?? ""),
68392
+ maxSeconds: positiveInt(args5["max-seconds"]),
68393
+ timeoutMs: positiveInt(args5["timeout-ms"]),
68394
+ maxHeight: positiveInt(args5["max-height"])
68395
+ });
68396
+ writeJson(result);
68397
+ if (!result.ok) process.exit(1);
68398
+ }
68399
+ });
68400
+ async function fetchVideo(args5) {
68401
+ const outDir = path47.resolve(args5.out);
68402
+ const workDir = await mkdtemp3(path47.join(tmpdir4(), "videos-fetch-"));
68403
+ try {
68404
+ await mkdir15(outDir, { recursive: true });
68405
+ const vimeoCookie = captureVimeoCookie();
68406
+ const probe = await probeYtDlp({ url: args5.url, vimeoCookie, cookieDir: workDir });
68407
+ if (isAudioOnly(probe.info)) {
68408
+ return { ok: false, error: { code: "VALIDATION_ERROR", message: "That link is audio, not video." } };
68409
+ }
68410
+ const seconds = durationSeconds(probe.info);
68411
+ const tooLong = refuseByDuration(seconds, args5.maxSeconds ?? MAX_VIDEO_INGEST_SECONDS);
68412
+ if (tooLong) {
68413
+ return { ok: false, error: { code: "VALIDATION_ERROR", message: tooLong } };
68414
+ }
68415
+ const { filePath, tier, reducedQuality } = await runYtDlp({
68416
+ url: args5.url,
68417
+ audioOnly: false,
68418
+ workDir,
68419
+ vimeoCookie,
68420
+ timeoutMs: args5.timeoutMs ?? downloadTimeoutMs(seconds),
68421
+ maxHeight: args5.maxHeight
68422
+ });
68423
+ return {
68424
+ ok: true,
68425
+ data: {
68426
+ file: await placeInto(outDir, filePath, fileStem(probe.info)),
68427
+ durationSeconds: seconds,
68428
+ egress: tier,
68429
+ ...reducedQuality ? { reducedQuality: true } : {}
68430
+ }
68431
+ };
68432
+ } catch (err) {
68433
+ const message = err instanceof YtDlpError ? `${err.message}: ${err.stderrTail}`.trim() : err instanceof Error ? err.message : String(err);
68434
+ return { ok: false, error: { code: "DOWNLOAD_FAILED", message } };
68435
+ } finally {
68436
+ await rm9(workDir, { recursive: true, force: true }).catch(() => {
68437
+ });
68438
+ }
68439
+ }
68440
+ function fileStem(info) {
68441
+ const parts = [info.extractor_key, info.id].filter((part) => typeof part === "string" && part !== "");
68442
+ const stem = parts.join("-").toLowerCase().replace(/[^a-z0-9._-]+/g, "_").replace(/^[._-]+/, "");
68443
+ return stem === "" ? "video" : stem;
68444
+ }
68445
+ async function placeInto(outDir, source, stem) {
68446
+ for (let n = 1; ; n++) {
68447
+ const target2 = path47.join(outDir, `${stem}${n === 1 ? "" : `-${n}`}${path47.extname(source)}`);
68448
+ try {
68449
+ await copyFile2(source, target2, constants.COPYFILE_EXCL);
68450
+ return target2;
68451
+ } catch (err) {
68452
+ if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
68453
+ }
68454
+ }
68455
+ }
68456
+
68457
+ // src/commands/videos/get.ts
68458
+ import { defineCommand as defineCommand236 } from "citty";
68459
+ registerSchema({
68460
+ command: "videos.get",
68461
+ description: "Get a single video by ID",
68462
+ args: {
68463
+ id: { type: "string", description: "Video ID", required: true }
68464
+ }
68465
+ });
68466
+ var getCommand7 = defineCommand236({
68467
+ meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
68468
+ args: {
68469
+ id: { type: "positional", description: "Video ID", required: false },
68470
+ "video-id": { type: "string", description: "Video ID (alternative to positional)", required: false },
68471
+ output: { type: "string", description: "Output format: json|files|md", required: false, default: "json" },
68472
+ fields: { type: "string", description: "Comma-separated field names to include", required: false },
68473
+ full: { type: "boolean", description: "Include full metadata", required: false, default: false }
68474
+ },
68475
+ run: async ({ args: args5 }) => {
68476
+ try {
68477
+ const id = args5.id || args5["video-id"];
68478
+ if (!id) {
68479
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Video ID is required" } });
68480
+ process.exit(1);
68481
+ }
68482
+ validateConvexId(id);
68483
+ const data = await apiGet("/api/videos/get", { id });
68484
+ writeOutput(
68485
+ { ok: true, data },
68486
+ args5.output || "json",
68487
+ args5.fields ? args5.fields.split(",") : void 0,
68488
+ args5.full,
68489
+ videoNormalizer
68490
+ );
68491
+ } catch (err) {
68492
+ if (err instanceof ApiError) {
68493
+ writeJson({ ok: false, error: { code: err.code, message: err.message } });
68494
+ process.exit(1);
68495
+ }
68496
+ writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
68497
+ process.exit(1);
68498
+ }
68499
+ }
68500
+ });
68501
+
68502
+ // src/commands/videos/group.ts
68503
+ import { defineCommand as defineCommand237 } from "citty";
68504
+ registerSchema({
68505
+ command: "videos.group",
68506
+ description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
68507
+ args: {
68508
+ id: { type: "string", description: "A video or image id \u2014 returns that asset's whole set", required: false },
68509
+ "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
68510
+ }
68511
+ });
68512
+ var groupCommand2 = defineCommand237({
68513
+ meta: {
68514
+ name: "group",
68515
+ 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>"
68516
+ },
68517
+ args: {
68518
+ id: { type: "positional", description: "A video or image id from the set", required: false },
68519
+ "group-key": { type: "string", description: "The set key, if you already have it", required: false }
68520
+ },
68521
+ run: async ({ args: args5 }) => {
68522
+ await runMediaGroupLookup({
68523
+ id: args5.id,
68524
+ groupKey: args5["group-key"],
68525
+ path: "/api/videos/group",
68526
+ idLabel: "a video or image id"
68527
+ });
68528
+ }
68529
+ });
68530
+
68531
+ // src/commands/videos/ingest.ts
68532
+ import { mkdtemp as mkdtemp4, rm as rm10, stat as stat11 } from "fs/promises";
68533
+ import { tmpdir as tmpdir5 } from "os";
68534
+ import path48 from "path";
68535
+ import { defineCommand as defineCommand238 } from "citty";
68536
+
68537
+ // src/lib/streamUpload.ts
68538
+ import { createHash as createHash2 } from "crypto";
68539
+ import { createReadStream } from "fs";
68540
+ import { Readable } from "stream";
68541
+ import { pipeline } from "stream/promises";
68542
+ async function hashFile(filePath) {
68543
+ const hash = createHash2("sha256");
68544
+ await pipeline(createReadStream(filePath), hash);
68545
+ return hash.digest("hex");
68546
+ }
68547
+ async function putFileStreamed(args5) {
68548
+ const body = Readable.toWeb(createReadStream(args5.filePath));
68549
+ return await fetch(args5.putUrl, {
68550
+ method: "PUT",
68551
+ body,
68552
+ headers: {
68553
+ "Content-Type": args5.contentType,
68554
+ "Content-Length": String(args5.sizeBytes)
68555
+ },
68556
+ duplex: "half"
68557
+ });
68558
+ }
68559
+
68191
68560
  // src/commands/videos/ingest.ts
68192
68561
  registerSchema({
68193
68562
  command: "videos.ingest",
@@ -68216,7 +68585,7 @@ registerSchema({
68216
68585
  "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
68217
68586
  }
68218
68587
  });
68219
- var ingestCommand2 = defineCommand231({
68588
+ var ingestCommand2 = defineCommand238({
68220
68589
  meta: {
68221
68590
  name: "ingest",
68222
68591
  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"
@@ -68402,7 +68771,7 @@ function ingestUrl(args5) {
68402
68771
  }
68403
68772
  async function downloadThenIngest(args5, country) {
68404
68773
  const vimeoCookie = captureVimeoCookie();
68405
- const workDir = await mkdtemp3(path47.join(tmpdir4(), "videos-ingest-"));
68774
+ const workDir = await mkdtemp4(path48.join(tmpdir5(), "videos-ingest-"));
68406
68775
  try {
68407
68776
  const probe = await probeYtDlp({ url: args5.url, country, vimeoCookie, cookieDir: workDir });
68408
68777
  if (isAudioOnly(probe.info)) {
@@ -68459,7 +68828,7 @@ async function downloadThenIngest(args5, country) {
68459
68828
  ...reducedQuality ? { reducedQuality: true } : {}
68460
68829
  };
68461
68830
  } finally {
68462
- await rm9(workDir, { recursive: true, force: true }).catch(() => {
68831
+ await rm10(workDir, { recursive: true, force: true }).catch(() => {
68463
68832
  });
68464
68833
  }
68465
68834
  }
@@ -68478,7 +68847,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
68478
68847
  }
68479
68848
 
68480
68849
  // src/commands/videos/search.ts
68481
- import { defineCommand as defineCommand232 } from "citty";
68850
+ import { defineCommand as defineCommand239 } from "citty";
68482
68851
  registerSchema({
68483
68852
  command: "videos.search",
68484
68853
  description: "Search videos by text query. Only returns ready videos.",
@@ -68488,7 +68857,7 @@ registerSchema({
68488
68857
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
68489
68858
  }
68490
68859
  });
68491
- var searchCommand4 = defineCommand232({
68860
+ var searchCommand4 = defineCommand239({
68492
68861
  meta: {
68493
68862
  name: "search",
68494
68863
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -68540,7 +68909,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
68540
68909
  // src/commands/videos/upload.ts
68541
68910
  import { readFile as readFile33, stat as stat12 } from "fs/promises";
68542
68911
  import { basename as basename3, extname as extname4 } from "path";
68543
- import { defineCommand as defineCommand233 } from "citty";
68912
+ import { defineCommand as defineCommand240 } from "citty";
68544
68913
  var MIME_MAP = {
68545
68914
  ".mp4": "video/mp4",
68546
68915
  ".mov": "video/quicktime",
@@ -68582,7 +68951,7 @@ function detectContentType(filePath) {
68582
68951
  function isRemoteUrl3(value) {
68583
68952
  return /^https?:\/\//i.test(value);
68584
68953
  }
68585
- var uploadCommand2 = defineCommand233({
68954
+ var uploadCommand2 = defineCommand240({
68586
68955
  meta: {
68587
68956
  name: "upload",
68588
68957
  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"
@@ -68666,10 +69035,10 @@ var uploadCommand2 = defineCommand233({
68666
69035
  });
68667
69036
 
68668
69037
  // src/commands/videos/index.ts
68669
- var videosCommand = defineCommand234({
69038
+ var videosCommand = defineCommand241({
68670
69039
  meta: {
68671
69040
  name: "videos",
68672
- description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, describe, delete, tags.
69041
+ description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, fetch, describe, delete, tags.
68673
69042
 
68674
69043
  Examples:
68675
69044
  baker videos search "product demo" --limit 5
@@ -68677,6 +69046,7 @@ Examples:
68677
69046
  baker videos get <video-id>
68678
69047
  baker videos upload ./demo.mp4
68679
69048
  baker videos ingest https://www.youtube.com/watch?v=abc123
69049
+ baker videos fetch https://www.youtube.com/watch?v=abc123 --out ./clips
68680
69050
  baker videos describe <video-id> --description "Customer testimonial, 30s"
68681
69051
  baker videos delete <video-id> --dry-run
68682
69052
  baker videos tags
@@ -68688,6 +69058,7 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
68688
69058
  search: searchCommand4,
68689
69059
  upload: uploadCommand2,
68690
69060
  ingest: ingestCommand2,
69061
+ fetch: fetchCommand2,
68691
69062
  describe: describeCommand2,
68692
69063
  delete: deleteCommand4,
68693
69064
  tags: tagsCommand6
@@ -68695,10 +69066,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
68695
69066
  });
68696
69067
 
68697
69068
  // src/commands/winning-ads/index.ts
68698
- import { defineCommand as defineCommand247 } from "citty";
69069
+ import { defineCommand as defineCommand254 } from "citty";
68699
69070
 
68700
69071
  // src/commands/winning-ads/advertisers.ts
68701
- import { defineCommand as defineCommand235 } from "citty";
69072
+ import { defineCommand as defineCommand242 } from "citty";
68702
69073
 
68703
69074
  // src/commands/winning-ads/shared.ts
68704
69075
  function splitList3(value) {
@@ -68707,7 +69078,7 @@ function splitList3(value) {
68707
69078
  }
68708
69079
  return value.split(",").map((v) => v.trim()).filter(Boolean);
68709
69080
  }
68710
- function reportError2(err) {
69081
+ function reportError3(err) {
68711
69082
  if (err instanceof ApiError) {
68712
69083
  writeJson({ ok: false, error: { code: err.code, message: err.message } });
68713
69084
  process.exit(1);
@@ -68751,7 +69122,7 @@ function advertiserNormalizer(record, full) {
68751
69122
  last_synced_at: record.last_synced_at ?? null
68752
69123
  };
68753
69124
  }
68754
- var advertisersCommand2 = defineCommand235({
69125
+ var advertisersCommand2 = defineCommand242({
68755
69126
  meta: {
68756
69127
  name: "advertisers",
68757
69128
  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'
@@ -68803,13 +69174,13 @@ var advertisersCommand2 = defineCommand235({
68803
69174
  advertiserNormalizer
68804
69175
  );
68805
69176
  } catch (err) {
68806
- reportError2(err);
69177
+ reportError3(err);
68807
69178
  }
68808
69179
  }
68809
69180
  });
68810
69181
 
68811
69182
  // src/commands/winning-ads/brief.ts
68812
- import { defineCommand as defineCommand236 } from "citty";
69183
+ import { defineCommand as defineCommand243 } from "citty";
68813
69184
  registerSchema({
68814
69185
  command: "winning-ads.brief",
68815
69186
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -68855,7 +69226,7 @@ function parseDna(raw) {
68855
69226
  }
68856
69227
  return parsed;
68857
69228
  }
68858
- var briefCommand = defineCommand236({
69229
+ var briefCommand = defineCommand243({
68859
69230
  meta: {
68860
69231
  name: "brief",
68861
69232
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -68885,13 +69256,13 @@ var briefCommand = defineCommand236({
68885
69256
  const data = await apiPost("/api/ad-library/brief", body);
68886
69257
  writeJson({ ok: true, data });
68887
69258
  } catch (err) {
68888
- reportError2(err);
69259
+ reportError3(err);
68889
69260
  }
68890
69261
  }
68891
69262
  });
68892
69263
 
68893
69264
  // src/commands/winning-ads/content.ts
68894
- import { defineCommand as defineCommand237 } from "citty";
69265
+ import { defineCommand as defineCommand244 } from "citty";
68895
69266
  registerSchema({
68896
69267
  command: "winning-ads.content",
68897
69268
  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. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
@@ -68904,7 +69275,7 @@ registerSchema({
68904
69275
  }
68905
69276
  }
68906
69277
  });
68907
- var contentCommand = defineCommand237({
69278
+ var contentCommand = defineCommand244({
68908
69279
  meta: {
68909
69280
  name: "content",
68910
69281
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -68947,13 +69318,13 @@ var contentCommand = defineCommand237({
68947
69318
  adContentNormalizer
68948
69319
  );
68949
69320
  } catch (err) {
68950
- reportError2(err);
69321
+ reportError3(err);
68951
69322
  }
68952
69323
  }
68953
69324
  });
68954
69325
 
68955
69326
  // src/commands/winning-ads/feed.ts
68956
- import { defineCommand as defineCommand238 } from "citty";
69327
+ import { defineCommand as defineCommand245 } from "citty";
68957
69328
  function buildFeedParams(input) {
68958
69329
  const params = {};
68959
69330
  const advertiser = splitList3(input.advertiser);
@@ -69005,7 +69376,7 @@ registerSchema({
69005
69376
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
69006
69377
  }
69007
69378
  });
69008
- var feedCommand2 = defineCommand238({
69379
+ var feedCommand2 = defineCommand245({
69009
69380
  meta: {
69010
69381
  name: "feed",
69011
69382
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -69084,13 +69455,13 @@ var feedCommand2 = defineCommand238({
69084
69455
  `);
69085
69456
  }
69086
69457
  } catch (err) {
69087
- reportError2(err);
69458
+ reportError3(err);
69088
69459
  }
69089
69460
  }
69090
69461
  });
69091
69462
 
69092
69463
  // src/commands/winning-ads/follow.ts
69093
- import { defineCommand as defineCommand239 } from "citty";
69464
+ import { defineCommand as defineCommand246 } from "citty";
69094
69465
  var PLATFORMS = adLibraryPlatformSchema.options;
69095
69466
  registerSchema({
69096
69467
  command: "winning-ads.follow",
@@ -69105,7 +69476,7 @@ registerSchema({
69105
69476
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
69106
69477
  }
69107
69478
  });
69108
- var followCommand = defineCommand239({
69479
+ var followCommand = defineCommand246({
69109
69480
  meta: {
69110
69481
  name: "follow",
69111
69482
  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'
@@ -69146,13 +69517,13 @@ var followCommand = defineCommand239({
69146
69517
  }
69147
69518
  writeJson({ ok: true, data, hints: hints2 });
69148
69519
  } catch (err) {
69149
- reportError2(err);
69520
+ reportError3(err);
69150
69521
  }
69151
69522
  }
69152
69523
  });
69153
69524
 
69154
69525
  // src/commands/winning-ads/follow-competitors.ts
69155
- import { defineCommand as defineCommand240 } from "citty";
69526
+ import { defineCommand as defineCommand247 } from "citty";
69156
69527
  var PLATFORMS2 = adLibraryPlatformSchema.options;
69157
69528
  var BATCH_TIMEOUT_MS = 3e5;
69158
69529
  function buildFollowBatchBody(input) {
@@ -69185,7 +69556,7 @@ registerSchema({
69185
69556
  }
69186
69557
  }
69187
69558
  });
69188
- var followCompetitorsCommand = defineCommand240({
69559
+ var followCompetitorsCommand = defineCommand247({
69189
69560
  meta: {
69190
69561
  name: "follow-competitors",
69191
69562
  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"'
@@ -69254,13 +69625,13 @@ var followCompetitorsCommand = defineCommand240({
69254
69625
  }
69255
69626
  writeJson({ ok: true, data, hints: hints2.length > 0 ? hints2 : void 0 });
69256
69627
  } catch (err) {
69257
- reportError2(err);
69628
+ reportError3(err);
69258
69629
  }
69259
69630
  }
69260
69631
  });
69261
69632
 
69262
69633
  // src/commands/winning-ads/following.ts
69263
- import { defineCommand as defineCommand241 } from "citty";
69634
+ import { defineCommand as defineCommand248 } from "citty";
69264
69635
  registerSchema({
69265
69636
  command: "winning-ads.following",
69266
69637
  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.",
@@ -69314,7 +69685,7 @@ function followingNormalizer(record, full) {
69314
69685
  platforms: Array.isArray(record.platforms) ? record.platforms : []
69315
69686
  };
69316
69687
  }
69317
- var followingCommand = defineCommand241({
69688
+ var followingCommand = defineCommand248({
69318
69689
  meta: {
69319
69690
  name: "following",
69320
69691
  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"
@@ -69344,13 +69715,13 @@ var followingCommand = defineCommand241({
69344
69715
  followingNormalizer
69345
69716
  );
69346
69717
  } catch (err) {
69347
- reportError2(err);
69718
+ reportError3(err);
69348
69719
  }
69349
69720
  }
69350
69721
  });
69351
69722
 
69352
69723
  // src/commands/winning-ads/patterns.ts
69353
- import { defineCommand as defineCommand242 } from "citty";
69724
+ import { defineCommand as defineCommand249 } from "citty";
69354
69725
  registerSchema({
69355
69726
  command: "winning-ads.patterns",
69356
69727
  description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
@@ -69389,7 +69760,7 @@ function discriminatorRow(record) {
69389
69760
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
69390
69761
  };
69391
69762
  }
69392
- var patternsCommand = defineCommand242({
69763
+ var patternsCommand = defineCommand249({
69393
69764
  meta: {
69394
69765
  name: "patterns",
69395
69766
  description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
@@ -69439,13 +69810,13 @@ var patternsCommand = defineCommand242({
69439
69810
  (record) => discriminatorRow(record)
69440
69811
  );
69441
69812
  } catch (err) {
69442
- reportError2(err);
69813
+ reportError3(err);
69443
69814
  }
69444
69815
  }
69445
69816
  });
69446
69817
 
69447
69818
  // src/commands/winning-ads/search.ts
69448
- import { defineCommand as defineCommand243 } from "citty";
69819
+ import { defineCommand as defineCommand250 } from "citty";
69449
69820
  registerSchema({
69450
69821
  command: "winning-ads.search",
69451
69822
  description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
@@ -69581,9 +69952,9 @@ function reportSearchError(err) {
69581
69952
  writeJson({ ok: false, error: { code: err.code, message: err.message, fix } });
69582
69953
  process.exit(1);
69583
69954
  }
69584
- reportError2(err);
69955
+ reportError3(err);
69585
69956
  }
69586
- var searchCommand5 = defineCommand243({
69957
+ var searchCommand5 = defineCommand250({
69587
69958
  meta: {
69588
69959
  name: "search",
69589
69960
  description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
@@ -69703,7 +70074,7 @@ var searchCommand5 = defineCommand243({
69703
70074
  });
69704
70075
 
69705
70076
  // src/commands/winning-ads/seeds.ts
69706
- import { defineCommand as defineCommand244 } from "citty";
70077
+ import { defineCommand as defineCommand251 } from "citty";
69707
70078
  function leanRow(r) {
69708
70079
  return {
69709
70080
  key: r.key,
@@ -69731,7 +70102,7 @@ function makeSeedCommand(opts) {
69731
70102
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
69732
70103
  }
69733
70104
  });
69734
- return defineCommand244({
70105
+ return defineCommand251({
69735
70106
  meta: { name: opts.name, description: opts.description },
69736
70107
  args: {
69737
70108
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -69758,7 +70129,7 @@ function makeSeedCommand(opts) {
69758
70129
  }
69759
70130
  writeOutput({ ok: true, data: projected }, output);
69760
70131
  } catch (err) {
69761
- reportError2(err);
70132
+ reportError3(err);
69762
70133
  }
69763
70134
  }
69764
70135
  });
@@ -69780,7 +70151,7 @@ var formatsCommand = makeSeedCommand({
69780
70151
  });
69781
70152
 
69782
70153
  // src/commands/winning-ads/unfollow.ts
69783
- import { defineCommand as defineCommand245 } from "citty";
70154
+ import { defineCommand as defineCommand252 } from "citty";
69784
70155
  registerSchema({
69785
70156
  command: "winning-ads.unfollow",
69786
70157
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -69788,7 +70159,7 @@ registerSchema({
69788
70159
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
69789
70160
  }
69790
70161
  });
69791
- var unfollowCommand = defineCommand245({
70162
+ var unfollowCommand = defineCommand252({
69792
70163
  meta: {
69793
70164
  name: "unfollow",
69794
70165
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -69803,13 +70174,13 @@ var unfollowCommand = defineCommand245({
69803
70174
  });
69804
70175
  writeJson({ ok: true, data });
69805
70176
  } catch (err) {
69806
- reportError2(err);
70177
+ reportError3(err);
69807
70178
  }
69808
70179
  }
69809
70180
  });
69810
70181
 
69811
70182
  // src/commands/winning-ads/winners.ts
69812
- import { defineCommand as defineCommand246 } from "citty";
70183
+ import { defineCommand as defineCommand253 } from "citty";
69813
70184
  registerSchema({
69814
70185
  command: "winning-ads.winners",
69815
70186
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -69819,7 +70190,7 @@ registerSchema({
69819
70190
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
69820
70191
  }
69821
70192
  });
69822
- var winnersCommand = defineCommand246({
70193
+ var winnersCommand = defineCommand253({
69823
70194
  meta: {
69824
70195
  name: "winners",
69825
70196
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -69863,13 +70234,13 @@ var winnersCommand = defineCommand246({
69863
70234
  winningAdNormalizer
69864
70235
  );
69865
70236
  } catch (err) {
69866
- reportError2(err);
70237
+ reportError3(err);
69867
70238
  }
69868
70239
  }
69869
70240
  });
69870
70241
 
69871
70242
  // src/commands/winning-ads/index.ts
69872
- var winningAdsCommand = defineCommand247({
70243
+ var winningAdsCommand = defineCommand254({
69873
70244
  meta: {
69874
70245
  name: "winning-ads",
69875
70246
  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.
@@ -69941,7 +70312,7 @@ function getCliVersion() {
69941
70312
  }
69942
70313
 
69943
70314
  // src/cli.ts
69944
- var main = defineCommand248({
70315
+ var main = defineCommand255({
69945
70316
  meta: {
69946
70317
  name: "baker",
69947
70318
  version: getCliVersion(),
@@ -69981,6 +70352,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
69981
70352
  history: historyCommand2,
69982
70353
  hubspot: hubspotCommand,
69983
70354
  "winning-ads": winningAdsCommand,
70355
+ "media-sources": mediaSourcesCommand,
69984
70356
  mcp: mcpCommand,
69985
70357
  schema: schemaCommand2
69986
70358
  }