@koda-sl/baker-cli 0.382.0 → 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-FGHMWBUD.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 = {
@@ -975,9 +975,6 @@ var OBJECTIVE_CAMPAIGN_TYPES = {
975
975
  ENGAGEMENT: ["SPONSORED_UPDATES", "DYNAMIC"]
976
976
  };
977
977
  var STAGEABLE_CREATE_STATUSES = ["DRAFT", "ACTIVE", "PAUSED"];
978
- var CAMPAIGN_GROUP_CREATE_STATUSES = ["DRAFT", "ACTIVE"];
979
- var CAMPAIGN_GROUP_STATUSES = ["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "DRAFT"];
980
- var POLITICAL_INTENTS = ["NOT_DECLARED", "NOT_POLITICAL", "POLITICAL"];
981
978
  var CREATIVE_INTENDED_STATUSES = ["ACTIVE", "PAUSED", "DRAFT"];
982
979
  var CTA_TYPES = [
983
980
  "APPLY",
@@ -1108,26 +1105,17 @@ var entityStatusSchema = z3.enum(["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "D
1108
1105
  function currencyMinimums(currencyCode) {
1109
1106
  return CURRENCY_MINIMUMS[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM;
1110
1107
  }
1111
- var campaignGroupFieldsSchema = z3.object({
1108
+ var campaignGroupCreateSchema = z3.object({
1112
1109
  name: z3.string().min(1).max(LINKEDIN_LIMITS.campaignGroup.nameMax),
1113
1110
  runSchedule: runScheduleSchema,
1114
1111
  totalBudget: moneySchema.optional(),
1115
- status: z3.enum(CAMPAIGN_GROUP_CREATE_STATUSES).default("DRAFT")
1116
- });
1117
- var campaignGroupCreateSchema = campaignGroupFieldsSchema.superRefine((p, ctx) => {
1118
- if (p.totalBudget && !p.runSchedule.end) {
1119
- ctx.addIssue({
1120
- code: "custom",
1121
- path: ["runSchedule", "end"],
1122
- message: "a group with totalBudget needs runSchedule.end"
1123
- });
1124
- }
1112
+ status: stageableStatusSchema.default("DRAFT")
1125
1113
  });
1126
1114
  var campaignGroupUpdateSchema = z3.object({
1127
1115
  name: z3.string().min(1).max(LINKEDIN_LIMITS.campaignGroup.nameMax).optional(),
1128
1116
  runSchedule: runScheduleSchema.optional(),
1129
1117
  totalBudget: moneySchema.optional(),
1130
- status: z3.enum(CAMPAIGN_GROUP_STATUSES).optional()
1118
+ status: entityStatusSchema.optional()
1131
1119
  }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1132
1120
  var campaignFields = {
1133
1121
  name: z3.string().min(1).max(LINKEDIN_LIMITS.campaign.nameMax),
@@ -1146,7 +1134,6 @@ var campaignFields = {
1146
1134
  connectedTelevisionOnly: z3.boolean().default(false),
1147
1135
  creativeSelection: z3.enum(CREATIVE_SELECTIONS).optional(),
1148
1136
  associatedEntity: z3.string().regex(ORGANIZATION_URN_REGEX).optional(),
1149
- politicalIntent: z3.enum(POLITICAL_INTENTS).default("NOT_DECLARED"),
1150
1137
  status: stageableStatusSchema.default("DRAFT")
1151
1138
  };
1152
1139
  function validateCampaignBudgets(p, ctx, { requireBudget }) {
@@ -1204,7 +1191,6 @@ var campaignUpdateSchema = z3.object({
1204
1191
  offsiteDeliveryEnabled: z3.boolean().optional(),
1205
1192
  connectedTelevisionOnly: z3.boolean().optional(),
1206
1193
  creativeSelection: z3.enum(CREATIVE_SELECTIONS).optional(),
1207
- politicalIntent: z3.enum(POLITICAL_INTENTS).optional(),
1208
1194
  status: entityStatusSchema.optional(),
1209
1195
  // Create-only on LinkedIn — a live campaign can't change these in place.
1210
1196
  // Accepted here only so `draft amend` can carry them into a staged CREATE
@@ -1830,17 +1816,6 @@ var linkedinDraftOpInputSchema = z3.discriminatedUnion("kind", [
1830
1816
  updateOp("leadForm.update", leadFormUpdateSchema)
1831
1817
  ]);
1832
1818
 
1833
- // ../api/src/ads-linkedin/search-statuses.ts
1834
- var LINKEDIN_SEARCH_STATUSES = {
1835
- campaignGroups: ["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "DRAFT", "PENDING_DELETION", "REMOVED"],
1836
- campaigns: ["ACTIVE", "PAUSED", "ARCHIVED", "COMPLETED", "CANCELED", "DRAFT", "PENDING_DELETION", "REMOVED"],
1837
- creatives: ["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "DRAFT", "PENDING_DELETION", "REMOVED"]
1838
- };
1839
- function unsupportedLinkedinSearchStatuses(level, statuses) {
1840
- const accepted = LINKEDIN_SEARCH_STATUSES[level];
1841
- return statuses.filter((status) => !accepted.includes(status));
1842
- }
1843
-
1844
1819
  // ../api/src/ads-linkedin/wire.ts
1845
1820
  import { z as z7 } from "zod";
1846
1821
 
@@ -4559,6 +4534,9 @@ var chatChangeTypeSchema = z13.enum([
4559
4534
  "image",
4560
4535
  "video",
4561
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",
4562
4540
  // A landing page saved to the reference library, keyed on the page.
4563
4541
  "landing-reference",
4564
4542
  // One Studio batch — the takes of one brief, as a set. See convex/lib/validators.ts.
@@ -6423,6 +6401,10 @@ var imageSourceSchema = z20.enum([
6423
6401
  "google_testimonial",
6424
6402
  "trustpilot_testimonial",
6425
6403
  "instagram",
6404
+ "tiktok",
6405
+ "linkedin",
6406
+ "facebook",
6407
+ "google_drive",
6426
6408
  "pexels",
6427
6409
  "pixabay",
6428
6410
  // Retired stock provider — kept so existing library rows stay filterable.
@@ -8233,7 +8215,9 @@ var videoSourceSchema = z30.enum([
8233
8215
  "bluesky",
8234
8216
  "dailymotion",
8235
8217
  "facebook",
8218
+ "google_drive",
8236
8219
  "instagram",
8220
+ "linkedin",
8237
8221
  "loom",
8238
8222
  "newgrounds",
8239
8223
  "ok",
@@ -12127,24 +12111,6 @@ function adCopyHints(payload) {
12127
12111
  return tells.slice(0, MAX_HINTS).map(hintFor);
12128
12112
  }
12129
12113
 
12130
- // src/commands/ads/hints.ts
12131
- function backendHints(envelope) {
12132
- const hints2 = envelope.hints;
12133
- return Array.isArray(hints2) ? hints2.filter((hint) => typeof hint === "string") : [];
12134
- }
12135
- function withHints(envelope, extra) {
12136
- const hints2 = [...backendHints(envelope), ...extra];
12137
- return hints2.length > 0 ? { ...envelope, hints: hints2 } : envelope;
12138
- }
12139
- function hintLines(envelope) {
12140
- return backendHints(envelope).map((hint) => `
12141
- > ${hint}`).join("");
12142
- }
12143
- function splitHints(response) {
12144
- const { hints: hints2, ...data } = response;
12145
- return { data, hints: hints2 ?? [] };
12146
- }
12147
-
12148
12114
  // src/commands/ads/google/copy-list-usage.ts
12149
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"'`;
12150
12116
  var COPY_LIST_FLAG = {
@@ -12152,9 +12118,9 @@ var COPY_LIST_FLAG = {
12152
12118
  descriptions: "--descriptions",
12153
12119
  longHeadlines: "--long-headlines"
12154
12120
  };
12155
- function valueAt(root, path48) {
12121
+ function valueAt(root, path49) {
12156
12122
  let node = root;
12157
- for (const key of path48) {
12123
+ for (const key of path49) {
12158
12124
  if (node === null || typeof node !== "object") return void 0;
12159
12125
  node = node[key];
12160
12126
  }
@@ -12387,11 +12353,11 @@ function parseKeywordEntry(raw, defaultMatch) {
12387
12353
  function rawTextEntries(value, rawArgs) {
12388
12354
  return repeatedValues(rawArgs, "text", value).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
12389
12355
  }
12390
- function rawFileEntries(path48) {
12391
- if (typeof path48 !== "string" || path48.length === 0) {
12356
+ function rawFileEntries(path49) {
12357
+ if (typeof path49 !== "string" || path49.length === 0) {
12392
12358
  return [];
12393
12359
  }
12394
- 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("#"));
12395
12361
  }
12396
12362
  function keywordEntries(args5, rawArgs) {
12397
12363
  const defaultMatch = typeof args5["match-type"] === "string" ? args5["match-type"].toUpperCase() : void 0;
@@ -12414,14 +12380,14 @@ function keywordEntries(args5, rawArgs) {
12414
12380
  }
12415
12381
  return entries;
12416
12382
  }
12417
- function loadJsonFileArg(path48) {
12418
- if (typeof path48 !== "string" || path48.length === 0) {
12383
+ function loadJsonFileArg(path49) {
12384
+ if (typeof path49 !== "string" || path49.length === 0) {
12419
12385
  return {};
12420
12386
  }
12421
- const inline = path48.trimStart().startsWith("{");
12422
- const source = inline ? "inline JSON" : path48;
12387
+ const inline = path49.trimStart().startsWith("{");
12388
+ const source = inline ? "inline JSON" : path49;
12423
12389
  try {
12424
- const parsed = JSON.parse(inline ? path48 : readFileSync2(path48, "utf8"));
12390
+ const parsed = JSON.parse(inline ? path49 : readFileSync2(path49, "utf8"));
12425
12391
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
12426
12392
  failWriteValidation(`${source} must contain a JSON object`);
12427
12393
  }
@@ -12509,7 +12475,7 @@ async function stageGoogleOp(raw, hints2) {
12509
12475
  const chatId = requireChatId();
12510
12476
  const response = await apiPost("/api/ads/google/draft/stage", { chatId, op: preflight.data });
12511
12477
  const allHints = [...noopHints(response.data), ...hints2 ?? [], ...adCopyHints(preflight.data)];
12512
- writeJsonEnvelope(withHints(response, allHints));
12478
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
12513
12479
  } catch (err) {
12514
12480
  handleGoogleError(err);
12515
12481
  }
@@ -12547,7 +12513,7 @@ async function stageGoogleOps(rawOps, hints2) {
12547
12513
  const chatId = requireChatId();
12548
12514
  const response = await apiPost("/api/ads/google/draft/stage-batch", { chatId, ops });
12549
12515
  const allHints = [...skippedHints(response.data.skipped ?? [], ops.length), ...hints2 ?? [], ...adCopyHints(ops)];
12550
- writeJsonEnvelope(withHints(response, allHints));
12516
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
12551
12517
  } catch (err) {
12552
12518
  handleGoogleError(err);
12553
12519
  }
@@ -12558,10 +12524,10 @@ async function stageUpdate(kind, customerId, target2, payload, hints2) {
12558
12524
  async function stageTarget(kind, customerId, target2, hints2) {
12559
12525
  await stageGoogleOp({ kind, customerId, target: target2 }, hints2);
12560
12526
  }
12561
- async function draftAction(path48, body, chat) {
12527
+ async function draftAction(path49, body, chat) {
12562
12528
  try {
12563
12529
  const chatId = resolveChatId(chat);
12564
- const response = await apiPost(path48, { chatId, ...body });
12530
+ const response = await apiPost(path49, { chatId, ...body });
12565
12531
  writeJsonEnvelope(response);
12566
12532
  } catch (err) {
12567
12533
  handleGoogleError(err);
@@ -12575,7 +12541,7 @@ async function draftListAction(json, chat) {
12575
12541
  writeJsonEnvelope(response);
12576
12542
  return;
12577
12543
  }
12578
- process.stdout.write(`${renderDraftStatus(response.data)}${hintLines(response)}
12544
+ process.stdout.write(`${renderDraftStatus(response.data)}
12579
12545
  `);
12580
12546
  } catch (err) {
12581
12547
  handleGoogleError(err);
@@ -14979,14 +14945,14 @@ function mappingRowProblem(row) {
14979
14945
 
14980
14946
  // ../api/src/event-sources/template.ts
14981
14947
  var MENTION = /\{\{\s*([^{}]+?)\s*\}\}/g;
14982
- function mentionOf(path48) {
14983
- return `{{${path48}}}`;
14948
+ function mentionOf(path49) {
14949
+ return `{{${path49}}}`;
14984
14950
  }
14985
14951
  function templateMentions(template) {
14986
14952
  const seen = [];
14987
14953
  for (const match of template.matchAll(MENTION)) {
14988
- const path48 = (match[1] ?? "").trim();
14989
- if (path48 !== "" && !seen.includes(path48)) seen.push(path48);
14954
+ const path49 = (match[1] ?? "").trim();
14955
+ if (path49 !== "" && !seen.includes(path49)) seen.push(path49);
14990
14956
  }
14991
14957
  return seen;
14992
14958
  }
@@ -18341,17 +18307,17 @@ function ignoredColumnHints(columns) {
18341
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.`
18342
18308
  ];
18343
18309
  }
18344
- function readRowsFile(path48) {
18345
- const inline = path48.trimStart().startsWith("[") || path48.trimStart().startsWith("{");
18310
+ function readRowsFile(path49) {
18311
+ const inline = path49.trimStart().startsWith("[") || path49.trimStart().startsWith("{");
18346
18312
  let text2;
18347
18313
  try {
18348
- text2 = inline ? path48 : readFileSync4(path48, "utf8");
18314
+ text2 = inline ? path49 : readFileSync4(path49, "utf8");
18349
18315
  } catch (err) {
18350
- 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)}`);
18351
18317
  }
18352
18318
  const parsed = parseConversionRowsText(text2);
18353
18319
  if (!parsed.ok) {
18354
- failWriteValidation(`${inline ? "the conversions passed inline" : path48}: ${parsed.error}`);
18320
+ failWriteValidation(`${inline ? "the conversions passed inline" : path49}: ${parsed.error}`);
18355
18321
  }
18356
18322
  return { rows: parsed.rows, ignoredColumns: parsed.ignoredColumns };
18357
18323
  }
@@ -20109,6 +20075,10 @@ Full guide: __tooling__/docs/tools/baker/ads-google.md`
20109
20075
  // src/commands/ads/linkedin/index.ts
20110
20076
  import { defineCommand as defineCommand53 } from "citty";
20111
20077
 
20078
+ // src/commands/ads/linkedin/write-shared.ts
20079
+ import { createHash } from "crypto";
20080
+ import { readFileSync as readFileSync5 } from "fs";
20081
+
20112
20082
  // src/commands/ads/linkedin/shared.ts
20113
20083
  var DAY_MS2 = 864e5;
20114
20084
  function handleLinkedinError(err) {
@@ -20201,48 +20171,18 @@ function daysAgoIso2(days) {
20201
20171
  function csvOrJson2(args5) {
20202
20172
  return args5.output ?? "json";
20203
20173
  }
20204
- function statusesArgDescription(level) {
20205
- return `CSV of statuses (${LINKEDIN_SEARCH_STATUSES[level].join(",")})`;
20206
- }
20207
- function statusFilterError(level, statuses) {
20208
- const unsupported = unsupportedLinkedinSearchStatuses(level, statuses);
20209
- if (unsupported.length === 0) {
20210
- return null;
20211
- }
20212
- const accepted = LINKEDIN_SEARCH_STATUSES[level].join(",");
20213
- return {
20214
- ok: false,
20215
- error: {
20216
- code: "INVALID_STATUS",
20217
- message: `LinkedIn does not accept status ${unsupported.join(", ")} for this listing.`,
20218
- fix: {
20219
- action: "retry_with_flag",
20220
- explanation: `Re-run with --statuses from ${accepted}, or --all-statuses to drop the filter.`
20221
- },
20222
- retryable: false
20223
- }
20224
- };
20225
- }
20226
- function resolveStatusFilter(args5, level) {
20174
+ function resolveStatusFilter(args5) {
20227
20175
  if (args5["all-statuses"]) {
20228
20176
  return void 0;
20229
20177
  }
20230
20178
  const explicit = args5.statuses;
20231
- if (!explicit) {
20232
- return ["ACTIVE"];
20233
- }
20234
- const statuses = explicit.split(",").map((s) => s.trim().toUpperCase()).filter(Boolean);
20235
- const error = statusFilterError(level, statuses);
20236
- if (error) {
20237
- writeAdsJson(error);
20238
- process.exit(1);
20179
+ if (explicit) {
20180
+ return explicit.split(",").map((s) => s.trim().toUpperCase()).filter(Boolean);
20239
20181
  }
20240
- return statuses;
20182
+ return ["ACTIVE"];
20241
20183
  }
20242
20184
 
20243
20185
  // src/commands/ads/linkedin/write-shared.ts
20244
- import { createHash } from "crypto";
20245
- import { readFileSync as readFileSync5 } from "fs";
20246
20186
  var TOTAL_BUDGET_HELP = "Lifetime budget \u2014 on something already running it must exceed spend to date, or LinkedIn rejects it";
20247
20187
  function bareAccountId(args5) {
20248
20188
  const raw = resolveAccountIdArg2(args5);
@@ -20252,19 +20192,19 @@ function failWriteValidation2(message) {
20252
20192
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
20253
20193
  process.exit(1);
20254
20194
  }
20255
- function loadJsonFileArg2(path48) {
20256
- if (typeof path48 !== "string" || path48.length === 0) {
20195
+ function loadJsonFileArg2(path49) {
20196
+ if (typeof path49 !== "string" || path49.length === 0) {
20257
20197
  return {};
20258
20198
  }
20259
20199
  try {
20260
- const parsed = JSON.parse(readFileSync5(path48, "utf8"));
20200
+ const parsed = JSON.parse(readFileSync5(path49, "utf8"));
20261
20201
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
20262
- failWriteValidation2(`${path48} must contain a JSON object`);
20202
+ failWriteValidation2(`${path49} must contain a JSON object`);
20263
20203
  }
20264
20204
  return parsed;
20265
20205
  } catch (err) {
20266
20206
  if (err instanceof SyntaxError) {
20267
- failWriteValidation2(`${path48} is not valid JSON: ${err.message}`);
20207
+ failWriteValidation2(`${path49} is not valid JSON: ${err.message}`);
20268
20208
  }
20269
20209
  throw err;
20270
20210
  }
@@ -20349,15 +20289,15 @@ function parseLocaleFlag(value) {
20349
20289
  }
20350
20290
  return { language: match[1], country: match[2].toUpperCase() };
20351
20291
  }
20352
- function loadTargetingFileArg(path48) {
20353
- if (typeof path48 !== "string" || path48.length === 0) {
20292
+ function loadTargetingFileArg(path49) {
20293
+ if (typeof path49 !== "string" || path49.length === 0) {
20354
20294
  return void 0;
20355
20295
  }
20356
- const parsed = loadJsonFileArg2(path48);
20296
+ const parsed = loadJsonFileArg2(path49);
20357
20297
  const criteria = parsed.targetingCriteria ?? parsed;
20358
20298
  if (!criteria.include) {
20359
20299
  failWriteValidation2(
20360
- `${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)`
20361
20301
  );
20362
20302
  }
20363
20303
  return criteria;
@@ -20392,14 +20332,14 @@ function parseCsvLine(line) {
20392
20332
  cells.push(current);
20393
20333
  return cells.map((cell3) => cell3.trim());
20394
20334
  }
20395
- function parseListFileArg(path48, maxRows) {
20396
- if (typeof path48 !== "string" || path48.length === 0) {
20335
+ function parseListFileArg(path49, maxRows) {
20336
+ if (typeof path49 !== "string" || path49.length === 0) {
20397
20337
  return void 0;
20398
20338
  }
20399
- const raw = readFileSync5(path48, "utf8");
20339
+ const raw = readFileSync5(path49, "utf8");
20400
20340
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
20401
20341
  if (lines.length < 2) {
20402
- 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`);
20403
20343
  }
20404
20344
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
20405
20345
  const rows = [];
@@ -20418,7 +20358,7 @@ function parseListFileArg(path48, maxRows) {
20418
20358
  }
20419
20359
  }
20420
20360
  if (rows.length > maxRows) {
20421
- 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.`);
20422
20362
  }
20423
20363
  return { columns, rows };
20424
20364
  }
@@ -20450,7 +20390,7 @@ async function stageOp(raw, hints2) {
20450
20390
  op: preflight.data
20451
20391
  });
20452
20392
  const allHints = [...batchNudge(response.data), ...hints2 ?? [], ...adCopyHints(preflight.data)];
20453
- writeJsonEnvelope(withHints(response, allHints));
20393
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
20454
20394
  } catch (err) {
20455
20395
  handleLinkedinError(err);
20456
20396
  }
@@ -20477,7 +20417,7 @@ async function stageLinkedinOps(rawOps, hints2) {
20477
20417
  const chatId = requireChatId();
20478
20418
  const response = await apiPost("/api/ads/linkedin/draft/stage-batch", { chatId, ops });
20479
20419
  const allHints = [...hints2 ?? [], ...adCopyHints(ops)];
20480
- writeJsonEnvelope(withHints(response, allHints));
20420
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
20481
20421
  } catch (err) {
20482
20422
  handleLinkedinError(err);
20483
20423
  }
@@ -20514,11 +20454,11 @@ function readPositionals(args5) {
20514
20454
  function splitIdList(raw) {
20515
20455
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
20516
20456
  }
20517
- function idsFileEntries(path48) {
20518
- if (typeof path48 !== "string" || path48.length === 0) {
20457
+ function idsFileEntries(path49) {
20458
+ if (typeof path49 !== "string" || path49.length === 0) {
20519
20459
  return [];
20520
20460
  }
20521
- 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);
20522
20462
  }
20523
20463
  function requireTargets(args5, entity) {
20524
20464
  const positionals = readPositionals(args5);
@@ -20569,7 +20509,7 @@ registerSchema({
20569
20509
  "account-id": { type: "string", description: "Account ID", required: false },
20570
20510
  "account-urn": { type: "string", description: "Alias for --account-id", required: false },
20571
20511
  "all-statuses": { type: "boolean", description: "Drop the ACTIVE filter", required: false },
20572
- statuses: { type: "string", description: statusesArgDescription("campaignGroups"), required: false },
20512
+ statuses: { type: "string", description: "CSV of statuses", required: false },
20573
20513
  limit: { type: "string", description: "Max rows (default 500)", required: false },
20574
20514
  "no-cache": NO_CACHE_ARG,
20575
20515
  output: { type: "string", description: "json|csv|jsonl|md", required: false, default: "json" }
@@ -20583,7 +20523,7 @@ registerSchema({
20583
20523
  "account-urn": { type: "string", description: "Alias", required: false },
20584
20524
  "campaign-group-id": { type: "string", description: "Filter by campaign group", required: false },
20585
20525
  "all-statuses": { type: "boolean", description: "Drop the ACTIVE filter", required: false },
20586
- statuses: { type: "string", description: statusesArgDescription("campaigns"), required: false },
20526
+ statuses: { type: "string", description: "CSV of statuses", required: false },
20587
20527
  limit: { type: "string", description: "Max rows", required: false },
20588
20528
  "no-cache": NO_CACHE_ARG,
20589
20529
  output: { type: "string", description: "json|csv|jsonl|md", required: false, default: "json" }
@@ -20597,7 +20537,7 @@ registerSchema({
20597
20537
  "account-urn": { type: "string", description: "Alias", required: false },
20598
20538
  "campaign-id": { type: "string", description: "Filter by campaign", required: false },
20599
20539
  "all-statuses": { type: "boolean", description: "Drop the ACTIVE filter", required: false },
20600
- statuses: { type: "string", description: statusesArgDescription("creatives"), required: false },
20540
+ statuses: { type: "string", description: "CSV of statuses", required: false },
20601
20541
  fields: {
20602
20542
  type: "string",
20603
20543
  description: "CSV of dotted paths to return instead of the compact row, e.g. id,content.textAd.landingPage",
@@ -20870,11 +20810,7 @@ registerSchema({
20870
20810
  description: "3-letter currency code (defaults to the ad account currency)",
20871
20811
  required: false
20872
20812
  },
20873
- status: {
20874
- type: "string",
20875
- description: "DRAFT|ACTIVE (default DRAFT) \u2014 LinkedIn refuses a new group created PAUSED",
20876
- required: false
20877
- },
20813
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)", required: false },
20878
20814
  file: { type: "string", description: "JSON payload file; flags override file keys", required: false }
20879
20815
  }
20880
20816
  });
@@ -20932,16 +20868,7 @@ registerSchema({
20932
20868
  ctv: { type: "string", description: "Connected TV on|off (default off)", required: false },
20933
20869
  "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN", required: false },
20934
20870
  "associated-entity": { type: "string", description: "Organization URN", required: false },
20935
- "political-intent": {
20936
- type: "string",
20937
- 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",
20938
- required: false
20939
- },
20940
- status: {
20941
- type: "string",
20942
- description: "DRAFT|ACTIVE|PAUSED (default DRAFT) \u2014 PAUSED needs a group that is not DRAFT",
20943
- required: false
20944
- },
20871
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)", required: false },
20945
20872
  file: { type: "string", description: "JSON payload file; flags override file keys", required: false }
20946
20873
  }
20947
20874
  });
@@ -20969,11 +20896,6 @@ registerSchema({
20969
20896
  lan: { type: "string", description: "on|off", required: false },
20970
20897
  ctv: { type: "string", description: "on|off", required: false },
20971
20898
  "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN", required: false },
20972
- "political-intent": {
20973
- type: "string",
20974
- 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",
20975
- required: false
20976
- },
20977
20899
  status: { type: "string", description: "ACTIVE|PAUSED|ARCHIVED", required: false },
20978
20900
  file: { type: "string", description: "JSON file with fields to change", required: false }
20979
20901
  }
@@ -21369,8 +21291,7 @@ var campaignGroupsCreateCommand = defineCommand33({
21369
21291
  meta: {
21370
21292
  name: "create",
21371
21293
  description: `Stage a new campaign group. ${STAGED_NOTE}
21372
- A group can't be created PAUSED, and a --total-budget needs an --end.
21373
- 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`
21374
21295
  },
21375
21296
  args: {
21376
21297
  ...accountArgs,
@@ -21379,10 +21300,7 @@ Example: baker ads linkedin campaign-groups create --name "Q3 ABM" --start 2026-
21379
21300
  end: { type: "string", description: "Run schedule end" },
21380
21301
  "total-budget": { type: "string", description: TOTAL_BUDGET_HELP },
21381
21302
  currency: { type: "string", description: "3-letter currency code (defaults to the ad account currency)" },
21382
- status: {
21383
- type: "string",
21384
- description: "DRAFT|ACTIVE (default DRAFT) \u2014 LinkedIn refuses a new group created PAUSED; pause it once it exists"
21385
- },
21303
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)" },
21386
21304
  file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
21387
21305
  },
21388
21306
  run: async ({ args: args5 }) => {
@@ -21448,11 +21366,9 @@ function campaignPayloadFromFlags(args5) {
21448
21366
  connectedTelevisionOnly: parseOnOffFlag(args5.ctv, "--ctv"),
21449
21367
  creativeSelection: args5["creative-selection"] ? String(args5["creative-selection"]).toUpperCase() : void 0,
21450
21368
  associatedEntity: args5["associated-entity"],
21451
- politicalIntent: args5["political-intent"] ? String(args5["political-intent"]).toUpperCase() : void 0,
21452
21369
  status: args5.status ? String(args5.status).toUpperCase() : void 0
21453
21370
  };
21454
21371
  }
21455
- 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."`;
21456
21372
  var campaignFlagArgs = {
21457
21373
  ...accountArgs,
21458
21374
  name: { type: "string", description: "Campaign name (\u2264255 chars)" },
@@ -21473,8 +21389,7 @@ var campaignFlagArgs = {
21473
21389
  ctv: { type: "string", description: "Connected TV on|off" },
21474
21390
  "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN" },
21475
21391
  "associated-entity": { type: "string", description: "Organization URN" },
21476
- "political-intent": { type: "string", description: POLITICAL_INTENT_HELP },
21477
- status: { type: "string", description: "DRAFT|ACTIVE|PAUSED \u2014 PAUSED needs a group that is not DRAFT" },
21392
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED" },
21478
21393
  file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
21479
21394
  };
21480
21395
  var campaignsCreateCommand = defineCommand33({
@@ -22991,7 +22906,7 @@ var listArgs = {
22991
22906
  "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
22992
22907
  "account-urn": { type: "string", description: "Alias for --account-id" },
22993
22908
  "all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
22994
- statuses: { type: "string", description: statusesArgDescription("campaignGroups") },
22909
+ statuses: { type: "string", description: "CSV of statuses (ACTIVE,PAUSED,ARCHIVED,COMPLETED,DRAFT)" },
22995
22910
  limit: { type: "string", description: "Max rows (default: 500)" },
22996
22911
  "no-cache": NO_CACHE_ARG,
22997
22912
  output: { type: "string", description: "json|csv|jsonl|md", default: "json" }
@@ -23006,7 +22921,7 @@ var campaignGroupsListCommand = defineCommand40({
23006
22921
  const accountId = resolveAccountIdArg2(args5);
23007
22922
  try {
23008
22923
  const params = { "account-id": accountId };
23009
- const statuses = resolveStatusFilter(args5, "campaignGroups");
22924
+ const statuses = resolveStatusFilter(args5);
23010
22925
  if (statuses) params.statuses = statuses.join(",");
23011
22926
  if (args5.limit) params.limit = String(args5.limit);
23012
22927
  if (args5["no-cache"]) params["skip-cache"] = "true";
@@ -23057,7 +22972,7 @@ var listArgs2 = {
23057
22972
  "account-urn": { type: "string", description: "Alias for --account-id" },
23058
22973
  "campaign-group-id": { type: "string", description: "Filter by campaign group" },
23059
22974
  "all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
23060
- statuses: { type: "string", description: statusesArgDescription("campaigns") },
22975
+ statuses: { type: "string", description: "CSV of statuses" },
23061
22976
  limit: { type: "string", description: "Max rows (default: 500)" },
23062
22977
  "no-cache": NO_CACHE_ARG,
23063
22978
  output: { type: "string", description: "json|csv|jsonl|md", default: "json" }
@@ -23072,7 +22987,7 @@ var campaignsListCommand = defineCommand41({
23072
22987
  const accountId = resolveAccountIdArg2(args5);
23073
22988
  try {
23074
22989
  const params = { "account-id": accountId };
23075
- const statuses = resolveStatusFilter(args5, "campaigns");
22990
+ const statuses = resolveStatusFilter(args5);
23076
22991
  if (statuses) params.statuses = statuses.join(",");
23077
22992
  if (args5["campaign-group-id"]) params["campaign-group-id"] = String(args5["campaign-group-id"]);
23078
22993
  if (args5.limit) params.limit = String(args5.limit);
@@ -23305,9 +23220,9 @@ import { defineCommand as defineCommand44 } from "citty";
23305
23220
  function asRecord(value) {
23306
23221
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
23307
23222
  }
23308
- function readPath2(row, path48) {
23223
+ function readPath2(row, path49) {
23309
23224
  let current = row;
23310
- for (const segment of path48.split(".")) {
23225
+ for (const segment of path49.split(".")) {
23311
23226
  const record = asRecord(current);
23312
23227
  if (!record) return void 0;
23313
23228
  current = record[segment];
@@ -23317,10 +23232,10 @@ function readPath2(row, path48) {
23317
23232
  function projectFields(rows, paths) {
23318
23233
  return rows.map((row) => {
23319
23234
  const projected = {};
23320
- for (const path48 of paths) {
23321
- const value = readPath2(row, path48);
23235
+ for (const path49 of paths) {
23236
+ const value = readPath2(row, path49);
23322
23237
  if (value !== void 0) {
23323
- projected[path48] = value;
23238
+ projected[path49] = value;
23324
23239
  }
23325
23240
  }
23326
23241
  return projected;
@@ -23333,16 +23248,16 @@ function shapeListRows(rows, args5, compact4) {
23333
23248
  const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
23334
23249
  if (paths.length > 0) {
23335
23250
  const projected = projectFields(raw, paths).map((row, i) => {
23336
- const missing = paths.filter((path48) => row[path48] === void 0);
23251
+ const missing = paths.filter((path49) => row[path49] === void 0);
23337
23252
  const source = rows[i];
23338
23253
  if (missing.length === 0 || source === void 0) return row;
23339
23254
  const compactRow3 = compact4.row(source);
23340
- for (const path48 of missing) {
23341
- 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];
23342
23257
  }
23343
23258
  return row;
23344
23259
  });
23345
- 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));
23346
23261
  if (projected.length === 0 || unmatched.length === 0) return { rows: projected, fields: paths };
23347
23262
  const carried = /* @__PURE__ */ new Set([...compact4.columns, ...raw.flatMap((row) => Object.keys(row))]);
23348
23263
  return {
@@ -23422,7 +23337,7 @@ var listArgs3 = {
23422
23337
  "account-urn": { type: "string", description: "Alias for --account-id" },
23423
23338
  "campaign-id": { type: "string", description: "Filter by campaign ID" },
23424
23339
  "all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
23425
- statuses: { type: "string", description: statusesArgDescription("creatives") },
23340
+ statuses: { type: "string", description: "CSV of intended statuses (ACTIVE,PAUSED,DRAFT,ARCHIVED)" },
23426
23341
  fields: {
23427
23342
  type: "string",
23428
23343
  description: "CSV of dotted paths to return instead of the compact row, e.g. id,content.textAd.landingPage"
@@ -23442,7 +23357,7 @@ var creativesListCommand = defineCommand44({
23442
23357
  const accountId = resolveAccountIdArg2(args5);
23443
23358
  try {
23444
23359
  const params = { "account-id": accountId };
23445
- const statuses = resolveStatusFilter(args5, "creatives");
23360
+ const statuses = resolveStatusFilter(args5);
23446
23361
  if (statuses) params.statuses = statuses.join(",");
23447
23362
  if (args5["campaign-id"]) params["campaign-id"] = String(args5["campaign-id"]);
23448
23363
  if (args5.limit) params.limit = String(args5.limit);
@@ -23749,7 +23664,7 @@ async function listDraft(json, chat) {
23749
23664
  writeJsonEnvelope(response);
23750
23665
  return;
23751
23666
  }
23752
- process.stdout.write(`${renderDraftStatus2(response.data)}${hintLines(response)}
23667
+ process.stdout.write(`${renderDraftStatus2(response.data)}
23753
23668
  `);
23754
23669
  } catch (err) {
23755
23670
  handleLinkedinError(err);
@@ -24712,54 +24627,54 @@ function buildCustomConversionRule(input) {
24712
24627
  function customConversionRuleRefusal(rule) {
24713
24628
  return ruleNodeRefusal(rule, "rule");
24714
24629
  }
24715
- function branchRefusal(key, value, path48) {
24630
+ function branchRefusal(key, value, path49) {
24716
24631
  if (!Array.isArray(value) || value.length === 0) {
24717
- 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`;
24718
24633
  }
24719
24634
  for (const [index, child] of value.entries()) {
24720
- const refusal = ruleNodeRefusal(child, `${path48}.${key}[${index}]`);
24635
+ const refusal = ruleNodeRefusal(child, `${path49}.${key}[${index}]`);
24721
24636
  if (refusal) {
24722
24637
  return refusal;
24723
24638
  }
24724
24639
  }
24725
24640
  return void 0;
24726
24641
  }
24727
- function conditionRefusal(key, value, path48) {
24728
- 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);
24729
24644
  }
24730
- function ruleNodeRefusal(node, path48) {
24645
+ function ruleNodeRefusal(node, path49) {
24731
24646
  if (!isRecord2(node)) {
24732
- 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"}}`;
24733
24648
  }
24734
24649
  const entries = Object.entries(node);
24735
24650
  if (entries.length === 0) {
24736
- 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`;
24737
24652
  }
24738
24653
  if (entries.length > 1) {
24739
- 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]}`;
24740
24655
  }
24741
24656
  for (const [key, value] of entries) {
24742
- const refusal = conditionRefusal(key, value, path48);
24657
+ const refusal = conditionRefusal(key, value, path49);
24743
24658
  if (refusal) {
24744
24659
  return refusal;
24745
24660
  }
24746
24661
  }
24747
24662
  return void 0;
24748
24663
  }
24749
- function leafRefusal(field, condition, path48) {
24664
+ function leafRefusal(field, condition, path49) {
24750
24665
  if (!isRecord2(condition)) {
24751
- 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`;
24752
24667
  }
24753
24668
  const tests = Object.entries(condition);
24754
24669
  if (tests.length === 0) {
24755
- 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(", ")}`;
24756
24671
  }
24757
24672
  if (tests.length > 1) {
24758
- 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]}`;
24759
24674
  }
24760
24675
  for (const [operator] of tests) {
24761
24676
  if (!RULE_OPERATORS[operator]) {
24762
- 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)`;
24763
24678
  }
24764
24679
  }
24765
24680
  return void 0;
@@ -25475,11 +25390,11 @@ var updateStatusSchema = z37.enum(UPDATE_STATUSES);
25475
25390
  function currencyMinimums2(currencyCode) {
25476
25391
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
25477
25392
  }
25478
- function validateDailyBudgetFloor(money, ctx, path48) {
25393
+ function validateDailyBudgetFloor(money, ctx, path49) {
25479
25394
  if (money?.currencyCode) {
25480
25395
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
25481
25396
  if (Number(money.amount) < min) {
25482
- 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` });
25483
25398
  }
25484
25399
  }
25485
25400
  }
@@ -26351,19 +26266,19 @@ function refuseDeleteByUpdate(payload, group) {
26351
26266
  );
26352
26267
  }
26353
26268
  }
26354
- function loadJsonFileArg3(path48) {
26355
- if (typeof path48 !== "string" || path48.length === 0) {
26269
+ function loadJsonFileArg3(path49) {
26270
+ if (typeof path49 !== "string" || path49.length === 0) {
26356
26271
  return {};
26357
26272
  }
26358
26273
  try {
26359
- const parsed = JSON.parse(readFileSync9(path48, "utf8"));
26274
+ const parsed = JSON.parse(readFileSync9(path49, "utf8"));
26360
26275
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
26361
- failWriteValidation3(`${path48} must contain a JSON object`);
26276
+ failWriteValidation3(`${path49} must contain a JSON object`);
26362
26277
  }
26363
26278
  return parsed;
26364
26279
  } catch (err) {
26365
26280
  if (err instanceof SyntaxError) {
26366
- failWriteValidation3(`${path48} is not valid JSON: ${err.message}`);
26281
+ failWriteValidation3(`${path49} is not valid JSON: ${err.message}`);
26367
26282
  }
26368
26283
  throw err;
26369
26284
  }
@@ -26454,7 +26369,7 @@ async function stageOp2(raw) {
26454
26369
  const chatId = requireChatId();
26455
26370
  const response = await apiPost("/api/ads/meta/draft/stage", { chatId, op: preflight.data });
26456
26371
  const copyHints = adCopyHints(preflight.data);
26457
- writeJsonEnvelope(withHints(response, copyHints));
26372
+ writeJsonEnvelope(copyHints.length > 0 ? { ...response, hints: copyHints } : response);
26458
26373
  } catch (err) {
26459
26374
  handleMetaError(err);
26460
26375
  }
@@ -27727,7 +27642,7 @@ async function listDraft2(json, chat) {
27727
27642
  writeJsonEnvelope(response);
27728
27643
  return;
27729
27644
  }
27730
- process.stdout.write(`${renderDraftStatus3(response.data)}${hintLines(response)}
27645
+ process.stdout.write(`${renderDraftStatus3(response.data)}
27731
27646
  `);
27732
27647
  } catch (err) {
27733
27648
  handleMetaError(err);
@@ -32059,12 +31974,12 @@ function flowExists(slug) {
32059
31974
  return existsSync3(join2(flowsDir(), slug, "_data.json"));
32060
31975
  }
32061
31976
  function readFlowTree(slug) {
32062
- const path48 = join2(flowsDir(), slug, "_data.json");
32063
- if (!existsSync3(path48)) {
31977
+ const path49 = join2(flowsDir(), slug, "_data.json");
31978
+ if (!existsSync3(path49)) {
32064
31979
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
32065
31980
  }
32066
31981
  try {
32067
- return JSON.parse(readFileSync11(path48, "utf-8"));
31982
+ return JSON.parse(readFileSync11(path49, "utf-8"));
32068
31983
  } catch (error) {
32069
31984
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
32070
31985
  }
@@ -33024,12 +32939,12 @@ function readPeopleFile(args5) {
33024
32939
  }
33025
32940
  function unreadablePeopleFile(args5) {
33026
32941
  if (!args5["people-file"]) return void 0;
33027
- const path48 = String(args5["people-file"]);
32942
+ const path49 = String(args5["people-file"]);
33028
32943
  try {
33029
- readFileSync12(path48, "utf8");
32944
+ readFileSync12(path49, "utf8");
33030
32945
  return void 0;
33031
32946
  } catch {
33032
- 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.`;
33033
32948
  }
33034
32949
  }
33035
32950
  function peopleLimitError(args5) {
@@ -34001,11 +33916,11 @@ function parseFieldArgs(argv) {
34001
33916
  for (const pair of parseRepeated(argv, "--field")) {
34002
33917
  const eq = pair.indexOf("=");
34003
33918
  const field = eq === -1 ? pair : pair.slice(0, eq);
34004
- const path48 = eq === -1 ? "" : pair.slice(eq + 1).trim();
33919
+ const path49 = eq === -1 ? "" : pair.slice(eq + 1).trim();
34005
33920
  const problem = mappingRowProblem(field);
34006
33921
  if (problem !== null) throw new ConversionArgsError(problem);
34007
- if (path48 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
34008
- mapping[field] = path48;
33922
+ if (path49 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
33923
+ mapping[field] = path49;
34009
33924
  }
34010
33925
  return mapping;
34011
33926
  }
@@ -35607,11 +35522,11 @@ function unwrap(response) {
35607
35522
  }
35608
35523
  return response.data;
35609
35524
  }
35610
- async function readAvatars(path48, params) {
35611
- return unwrap(await apiGet(path48, params));
35525
+ async function readAvatars(path49, params) {
35526
+ return unwrap(await apiGet(path49, params));
35612
35527
  }
35613
- async function writeAvatars(path48, body) {
35614
- return unwrap(await apiPost(path48, body));
35528
+ async function writeAvatars(path49, body) {
35529
+ return unwrap(await apiPost(path49, body));
35615
35530
  }
35616
35531
 
35617
35532
  // src/commands/avatars/create.ts
@@ -36520,12 +36435,12 @@ function missingFontFiles(urls, available) {
36520
36435
  function planFontAdoption(sources, families) {
36521
36436
  const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
36522
36437
  const byFamily = /* @__PURE__ */ new Map();
36523
- for (const { path: path48, source } of sources) {
36524
- const dir = posix.dirname(path48);
36438
+ for (const { path: path49, source } of sources) {
36439
+ const dir = posix.dirname(path49);
36525
36440
  for (const face of declaredFontFaces(source)) {
36526
36441
  if (!wanted.has(face.family)) continue;
36527
36442
  const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
36528
- perFile.set(path48, [...perFile.get(path48) ?? [], rebaseFontFaceSrc(face.block, dir)]);
36443
+ perFile.set(path49, [...perFile.get(path49) ?? [], rebaseFontFaceSrc(face.block, dir)]);
36529
36444
  byFamily.set(face.family, perFile);
36530
36445
  }
36531
36446
  }
@@ -40790,18 +40705,10 @@ function dropUncastVoices(nodes) {
40790
40705
  if (node && node.type === "voice_select" && !referenced.has(node.id)) nodes.splice(i, 1);
40791
40706
  }
40792
40707
  }
40793
- function framesReuseRefusal(blueprint, elements, requested) {
40794
- if (requested !== "reuse") return void 0;
40795
- const showsPeople = (blueprint.global?.cast ?? []).length > 0 || elements.some((e) => e.type.toLowerCase() === "person");
40796
- if (!showsPeople) return void 0;
40797
- 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.";
40798
- }
40799
- function scaffoldVideoCanvas(input, elementsInput, requested) {
40708
+ function scaffoldVideoCanvas(input, elementsInput, opts) {
40800
40709
  const blueprint = VideoBlueprint.parse(input);
40801
40710
  injectHookPhysicality(blueprint);
40802
40711
  const elements = RecurringElements.parse(elementsInput);
40803
- const reuseRefused = framesReuseRefusal(blueprint, elements, requested.frames);
40804
- const opts = reuseRefused ? { ...requested, frames: "generate" } : requested;
40805
40712
  const clock = spineClockFor(blueprint);
40806
40713
  const nodes = [];
40807
40714
  emitBlueprintIngests(opts, nodes);
@@ -40904,7 +40811,6 @@ function scaffoldVideoCanvas(input, elementsInput, requested) {
40904
40811
  }
40905
40812
  const todo = {
40906
40813
  ...buildVideoTodo(videoReport(input, elementsInput), overlays.length, floating.length, opts, blueprint),
40907
- ...reuseRefused ? { frames_reuse_refused: reuseRefused } : {},
40908
40814
  ...aspectRemapTodo(resolveAspect(blueprint.source?.aspect_ratio, opts.aspect, genAspectsFor(opts.videoModel))),
40909
40815
  model_constraints: buildModelConstraints()
40910
40816
  };
@@ -45831,12 +45737,6 @@ function videoDefinitionDescription(blueprint) {
45831
45737
  const product = g.branding?.product;
45832
45738
  return typeof product === "string" && product.trim() ? product.trim() : void 0;
45833
45739
  }
45834
- function framesModeReport(canvas, requested) {
45835
- const todo = canvas.metadata?.todo;
45836
- const mode = typeof todo?.frames_mode === "string" ? todo.frames_mode : requested;
45837
- const refused = typeof todo?.frames_reuse_refused === "string" ? todo.frames_reuse_refused : void 0;
45838
- return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
45839
- }
45840
45740
  async function materializeReferenceVideo(fileArg2) {
45841
45741
  if (!/^https?:\/\//i.test(fileArg2)) return path26.resolve(fileArg2);
45842
45742
  let bytes;
@@ -45994,10 +45894,7 @@ var scaffoldVideoCommand = defineCommand116({
45994
45894
  type: "string",
45995
45895
  description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
45996
45896
  },
45997
- frames: {
45998
- type: "string",
45999
- 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.`
46000
- },
45897
+ frames: { type: "string", description: '"generate" (default, anchored regen) or "reuse" (wire real frames in)' },
46001
45898
  ambient: {
46002
45899
  type: "boolean",
46003
45900
  description: "Give silent b-roll scenes native diegetic ambient mixed deep under the music bed (off by default)"
@@ -46200,7 +46097,6 @@ var scaffoldVideoCommand = defineCommand116({
46200
46097
  error: { code: "validation", issues: validation.issues },
46201
46098
  canvas_path: outPath,
46202
46099
  prompt_path: blueprintPath,
46203
- ...framesModeReport(canvas, frames),
46204
46100
  note: "The canvas WAS written despite the blocking issue(s) \u2014 fix them in place, then `baker canvas validate` before running.",
46205
46101
  ...uncastHint
46206
46102
  },
@@ -46252,7 +46148,7 @@ var scaffoldVideoCommand = defineCommand116({
46252
46148
  composition_dir: compositionDest,
46253
46149
  output: canvas.output,
46254
46150
  ...uncastHint,
46255
- ...framesModeReport(canvas, frames),
46151
+ frames_mode: frames,
46256
46152
  models: {
46257
46153
  deconstruct: deconstructModel,
46258
46154
  select: selectModel,
@@ -49370,10 +49266,10 @@ function parseValueExpression(raw) {
49370
49266
  return parts.map(parsePart);
49371
49267
  }
49372
49268
  function trackingFieldIds() {
49373
- const path48 = join4(flowsDir(), "..", "tracking.ts");
49374
- if (!existsSync6(path48)) return null;
49269
+ const path49 = join4(flowsDir(), "..", "tracking.ts");
49270
+ if (!existsSync6(path49)) return null;
49375
49271
  try {
49376
- const source = readFileSync15(path48, "utf-8");
49272
+ const source = readFileSync15(path49, "utf-8");
49377
49273
  const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
49378
49274
  if (!block2) return null;
49379
49275
  const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
@@ -49947,13 +49843,13 @@ function specsFromFile(parsed) {
49947
49843
  return `${destField}${type}=${entry?.value ?? ""}`;
49948
49844
  });
49949
49845
  }
49950
- function readSpecFile(path48) {
49846
+ function readSpecFile(path49) {
49951
49847
  let raw;
49952
49848
  try {
49953
- raw = path48 === "-" ? readFileSync16(0, "utf-8") : readFileSync16(path48, "utf-8");
49849
+ raw = path49 === "-" ? readFileSync16(0, "utf-8") : readFileSync16(path49, "utf-8");
49954
49850
  } catch (error) {
49955
49851
  refuse(
49956
- `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)}`
49957
49853
  );
49958
49854
  }
49959
49855
  let parsed;
@@ -49961,7 +49857,7 @@ function readSpecFile(path48) {
49961
49857
  parsed = JSON.parse(raw);
49962
49858
  } catch (error) {
49963
49859
  refuse(
49964
- `${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)}`,
49965
49861
  'Expected { "map": { "<destField>": "<value>", \u2026 } }'
49966
49862
  );
49967
49863
  }
@@ -50260,21 +50156,21 @@ var ARRAY_FIELDS = [
50260
50156
  "tagIds"
50261
50157
  ];
50262
50158
  var ARRAY_OWNERS = ["", "body"];
50263
- function dropUnsetOptionals(sideEffect, path48) {
50159
+ function dropUnsetOptionals(sideEffect, path49) {
50264
50160
  return OPTIONAL_STRINGS.flatMap((key) => {
50265
50161
  if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
50266
50162
  delete sideEffect[key];
50267
- 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)` }];
50268
50164
  });
50269
50165
  }
50270
- function fillNulledArrays(target2, prefix, path48) {
50166
+ function fillNulledArrays(target2, prefix, path49) {
50271
50167
  return ARRAY_FIELDS.flatMap((key) => {
50272
50168
  if (!(key in target2) || target2[key] !== null) return [];
50273
50169
  target2[key] = [];
50274
- return [{ path: path48, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
50170
+ return [{ path: path49, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
50275
50171
  });
50276
50172
  }
50277
- function tidyEmailRecipients(sideEffect, path48) {
50173
+ function tidyEmailRecipients(sideEffect, path49) {
50278
50174
  if (sideEffect.type !== "email") return [];
50279
50175
  const config = sideEffect.encryptedConfig;
50280
50176
  if (!config || typeof config !== "object" || Array.isArray(config)) return [];
@@ -50285,7 +50181,7 @@ function tidyEmailRecipients(sideEffect, path48) {
50285
50181
  const tidied = to.join(", ");
50286
50182
  if (tidied === raw) return [];
50287
50183
  config.email = tidied;
50288
- 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" }];
50289
50185
  }
50290
50186
  function sideEffectsOf(node) {
50291
50187
  if (!Array.isArray(node.sideEffects)) return [];
@@ -50294,13 +50190,13 @@ function sideEffectsOf(node) {
50294
50190
  );
50295
50191
  }
50296
50192
  function normalizeSideEffect(sideEffect, where) {
50297
- const path48 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
50193
+ const path49 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
50298
50194
  const arrays = ARRAY_OWNERS.flatMap((owner) => {
50299
50195
  const target2 = owner ? sideEffect[owner] : sideEffect;
50300
50196
  if (!target2 || typeof target2 !== "object") return [];
50301
- return fillNulledArrays(target2, owner ? `${owner}.` : "", path48);
50197
+ return fillNulledArrays(target2, owner ? `${owner}.` : "", path49);
50302
50198
  });
50303
- return [...dropUnsetOptionals(sideEffect, path48), ...arrays, ...tidyEmailRecipients(sideEffect, path48)];
50199
+ return [...dropUnsetOptionals(sideEffect, path49), ...arrays, ...tidyEmailRecipients(sideEffect, path49)];
50304
50200
  }
50305
50201
  function normalizeFlowTree(tree) {
50306
50202
  const changes = [];
@@ -51662,11 +51558,11 @@ var WARNING_HINT = "Read the warnings above before telling the user this is read
51662
51558
  async function stageOp3(op) {
51663
51559
  const chatId = requireChatId();
51664
51560
  try {
51665
- const { data, hints: hints2 } = splitHints(await apiPost("/api/ga4/draft/stage", { chatId, op }));
51561
+ const data = await apiPost("/api/ga4/draft/stage", { chatId, op });
51666
51562
  writeJsonEnvelope({
51667
51563
  ok: true,
51668
51564
  data,
51669
- hints: [...hints2, ...STAGE_HINTS, ...data.warnings.length > 0 ? [WARNING_HINT] : []]
51565
+ hints: [...STAGE_HINTS, ...data.warnings.length > 0 ? [WARNING_HINT] : []]
51670
51566
  });
51671
51567
  } catch (err) {
51672
51568
  handleError5(err);
@@ -51680,23 +51576,21 @@ async function stageOps(ops) {
51680
51576
  }
51681
51577
  const chatId = requireChatId();
51682
51578
  try {
51683
- const { data, hints: hints2 } = splitHints(
51684
- await apiPost("/api/ga4/draft/stage-batch", { chatId, ops })
51685
- );
51579
+ const data = await apiPost("/api/ga4/draft/stage-batch", { chatId, ops });
51686
51580
  writeJsonEnvelope({
51687
51581
  ok: true,
51688
51582
  data,
51689
- 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] : []]
51690
51584
  });
51691
51585
  } catch (err) {
51692
51586
  handleError5(err);
51693
51587
  }
51694
51588
  }
51695
- async function draftAction2(path48, body, chat) {
51589
+ async function draftAction2(path49, body, chat) {
51696
51590
  const chatId = resolveChatId(chat);
51697
51591
  try {
51698
- const { data, hints: hints2 } = splitHints(await apiPost(path48, { chatId, ...body }));
51699
- writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
51592
+ const data = await apiPost(path49, { chatId, ...body });
51593
+ writeJsonEnvelope({ ok: true, data });
51700
51594
  return data;
51701
51595
  } catch (err) {
51702
51596
  handleError5(err);
@@ -51728,13 +51622,12 @@ function renderDraft(response) {
51728
51622
  async function draftList(json, chat) {
51729
51623
  const chatId = resolveChatId(chat);
51730
51624
  try {
51731
- const response = await apiPost("/api/ga4/draft", { chatId });
51732
- const { data, hints: hints2 } = splitHints(response);
51625
+ const data = await apiPost("/api/ga4/draft", { chatId });
51733
51626
  if (json) {
51734
- writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
51627
+ writeJsonEnvelope({ ok: true, data });
51735
51628
  return;
51736
51629
  }
51737
- process.stdout.write(`${renderDraft(data)}${hintLines(response)}
51630
+ process.stdout.write(`${renderDraft(data)}
51738
51631
  `);
51739
51632
  } catch (err) {
51740
51633
  handleError5(err);
@@ -54225,9 +54118,9 @@ async function readImageBuffer(pathOrUrl) {
54225
54118
  }
54226
54119
  return readFile28(pathOrUrl);
54227
54120
  }
54228
- async function isDirectory(path48) {
54121
+ async function isDirectory(path49) {
54229
54122
  try {
54230
- const s = await stat7(path48);
54123
+ const s = await stat7(path49);
54231
54124
  return s.isDirectory();
54232
54125
  } catch {
54233
54126
  return false;
@@ -54655,13 +54548,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
54655
54548
  }
54656
54549
  function disambiguate(paths) {
54657
54550
  const taken = /* @__PURE__ */ new Set();
54658
- return paths.map((path48) => {
54659
- if (!taken.has(path48)) {
54660
- taken.add(path48);
54661
- return path48;
54551
+ return paths.map((path49) => {
54552
+ if (!taken.has(path49)) {
54553
+ taken.add(path49);
54554
+ return path49;
54662
54555
  }
54663
- const ext = extname3(path48);
54664
- const stem = path48.slice(0, path48.length - ext.length);
54556
+ const ext = extname3(path49);
54557
+ const stem = path49.slice(0, path49.length - ext.length);
54665
54558
  let n = 2;
54666
54559
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
54667
54560
  const unique = `${stem}-${n}${ext}`;
@@ -54787,10 +54680,10 @@ async function runDownloads(plan) {
54787
54680
  const paths = disambiguate(fetched.map((item) => item.path));
54788
54681
  const downloaded = [];
54789
54682
  for (const [index, item] of fetched.entries()) {
54790
- const path48 = paths[index] ?? item.path;
54683
+ const path49 = paths[index] ?? item.path;
54791
54684
  try {
54792
- await atomicWrite(path48, item.buffer);
54793
- 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 });
54794
54687
  } catch (err) {
54795
54688
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
54796
54689
  }
@@ -62977,11 +62870,365 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
62977
62870
  }
62978
62871
  });
62979
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
+
62980
63227
  // src/commands/research/index.ts
62981
- import { defineCommand as defineCommand196 } from "citty";
63228
+ import { defineCommand as defineCommand202 } from "citty";
62982
63229
 
62983
63230
  // src/commands/research/advertisers.ts
62984
- import { defineCommand as defineCommand184 } from "citty";
63231
+ import { defineCommand as defineCommand190 } from "citty";
62985
63232
 
62986
63233
  // src/commands/research/hints.ts
62987
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.';
@@ -63183,7 +63430,7 @@ var FIELDS3 = {
63183
63430
  etv: "Estimated traffic value (USD)",
63184
63431
  visibility: "SERP visibility score (0-1)"
63185
63432
  };
63186
- var advertisersCommand = defineCommand184({
63433
+ var advertisersCommand = defineCommand190({
63187
63434
  meta: {
63188
63435
  name: "advertisers",
63189
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).
@@ -63239,7 +63486,7 @@ Examples:
63239
63486
  });
63240
63487
 
63241
63488
  // src/commands/research/autocomplete.ts
63242
- import { defineCommand as defineCommand185 } from "citty";
63489
+ import { defineCommand as defineCommand191 } from "citty";
63243
63490
  registerSchema({
63244
63491
  command: "research.autocomplete",
63245
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).",
@@ -63262,7 +63509,7 @@ registerSchema({
63262
63509
  var FIELDS4 = {
63263
63510
  suggestion: "Autocomplete suggestion from Google"
63264
63511
  };
63265
- var autocompleteCommand = defineCommand185({
63512
+ var autocompleteCommand = defineCommand191({
63266
63513
  meta: {
63267
63514
  name: "autocomplete",
63268
63515
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -63317,7 +63564,7 @@ Examples:
63317
63564
  });
63318
63565
 
63319
63566
  // src/commands/research/countries.ts
63320
- import { defineCommand as defineCommand186 } from "citty";
63567
+ import { defineCommand as defineCommand192 } from "citty";
63321
63568
  registerSchema({
63322
63569
  command: "research.countries",
63323
63570
  description: "List all supported country codes for --location flag in research commands.",
@@ -63374,7 +63621,7 @@ var FIELDS5 = {
63374
63621
  code: "Country code to pass as --location",
63375
63622
  name: "Country name"
63376
63623
  };
63377
- var countriesCommand = defineCommand186({
63624
+ var countriesCommand = defineCommand192({
63378
63625
  meta: {
63379
63626
  name: "countries",
63380
63627
  description: "List all supported country codes for --location flag."
@@ -63385,7 +63632,7 @@ var countriesCommand = defineCommand186({
63385
63632
  });
63386
63633
 
63387
63634
  // src/commands/research/fetch.ts
63388
- import { defineCommand as defineCommand187 } from "citty";
63635
+ import { defineCommand as defineCommand193 } from "citty";
63389
63636
  var CONTENT_PREVIEW_CHARS = 2e4;
63390
63637
  var TIMEOUT_MS = 18e4;
63391
63638
  registerSchema({
@@ -63458,7 +63705,7 @@ function fetchFix(code) {
63458
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."
63459
63706
  };
63460
63707
  }
63461
- var fetchCommand = defineCommand187({
63708
+ var fetchCommand = defineCommand193({
63462
63709
  meta: {
63463
63710
  name: "fetch",
63464
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.
@@ -63535,7 +63782,7 @@ Examples:
63535
63782
  });
63536
63783
 
63537
63784
  // src/commands/research/intent.ts
63538
- import { defineCommand as defineCommand188 } from "citty";
63785
+ import { defineCommand as defineCommand194 } from "citty";
63539
63786
  registerSchema({
63540
63787
  command: "research.intent",
63541
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.",
@@ -63558,7 +63805,7 @@ var FIELDS7 = {
63558
63805
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
63559
63806
  probability: "Confidence score 0.0-1.0"
63560
63807
  };
63561
- var intentCommand = defineCommand188({
63808
+ var intentCommand = defineCommand194({
63562
63809
  meta: {
63563
63810
  name: "intent",
63564
63811
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -63606,7 +63853,7 @@ Examples:
63606
63853
  });
63607
63854
 
63608
63855
  // src/commands/research/keyword-gap.ts
63609
- import { defineCommand as defineCommand189 } from "citty";
63856
+ import { defineCommand as defineCommand195 } from "citty";
63610
63857
  registerSchema({
63611
63858
  command: "research.keyword-gap",
63612
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.",
@@ -63635,7 +63882,7 @@ var FIELDS8 = {
63635
63882
  cpc: "Cost per click USD",
63636
63883
  their_position: "Competitor's ranking position"
63637
63884
  };
63638
- var keywordGapCommand = defineCommand189({
63885
+ var keywordGapCommand = defineCommand195({
63639
63886
  meta: {
63640
63887
  name: "keyword-gap",
63641
63888
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -63710,7 +63957,7 @@ Examples:
63710
63957
  });
63711
63958
 
63712
63959
  // src/commands/research/keyword-metrics.ts
63713
- import { defineCommand as defineCommand190 } from "citty";
63960
+ import { defineCommand as defineCommand196 } from "citty";
63714
63961
 
63715
63962
  // src/commands/research/keyword-metrics-rows.ts
63716
63963
  var KEYWORD_METRICS_SOURCE = "keyword_planner_estimate_via_dataforseo";
@@ -63790,7 +64037,7 @@ registerSchema({
63790
64037
  "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false }
63791
64038
  }
63792
64039
  });
63793
- var keywordMetricsCommand = defineCommand190({
64040
+ var keywordMetricsCommand = defineCommand196({
63794
64041
  meta: {
63795
64042
  name: "keyword-metrics",
63796
64043
  description: `Volume, CPC and competition for keywords you name. No domain required.
@@ -63872,7 +64119,7 @@ Examples:
63872
64119
  });
63873
64120
 
63874
64121
  // src/commands/research/keywords-for-site.ts
63875
- import { defineCommand as defineCommand191 } from "citty";
64122
+ import { defineCommand as defineCommand197 } from "citty";
63876
64123
  registerSchema({
63877
64124
  command: "research.keywords-for-site",
63878
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.",
@@ -63905,7 +64152,7 @@ var FIELDS9 = {
63905
64152
  competition: "LOW, MEDIUM, or HIGH",
63906
64153
  competition_index: "Competition score 0-100"
63907
64154
  };
63908
- var keywordsForSiteCommand = defineCommand191({
64155
+ var keywordsForSiteCommand = defineCommand197({
63909
64156
  meta: {
63910
64157
  name: "keywords-for-site",
63911
64158
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -63967,7 +64214,7 @@ Examples:
63967
64214
  });
63968
64215
 
63969
64216
  // src/commands/research/languages.ts
63970
- import { defineCommand as defineCommand192 } from "citty";
64217
+ import { defineCommand as defineCommand198 } from "citty";
63971
64218
  registerSchema({
63972
64219
  command: "research.languages",
63973
64220
  description: "List all supported language codes for --language flag in research commands.",
@@ -63997,7 +64244,7 @@ var FIELDS10 = {
63997
64244
  code: "Language code to pass as --language",
63998
64245
  name: "Language name (also accepted by --language)"
63999
64246
  };
64000
- var languagesCommand2 = defineCommand192({
64247
+ var languagesCommand2 = defineCommand198({
64001
64248
  meta: {
64002
64249
  name: "languages",
64003
64250
  description: "List all supported language codes for --language flag."
@@ -64008,7 +64255,7 @@ var languagesCommand2 = defineCommand192({
64008
64255
  });
64009
64256
 
64010
64257
  // src/commands/research/lighthouse.ts
64011
- import { defineCommand as defineCommand193 } from "citty";
64258
+ import { defineCommand as defineCommand199 } from "citty";
64012
64259
  registerSchema({
64013
64260
  command: "research.lighthouse",
64014
64261
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -64027,7 +64274,7 @@ var FIELDS11 = {
64027
64274
  speed_index_ms: "Speed Index in ms (good: < 3400)",
64028
64275
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
64029
64276
  };
64030
- var lighthouseCommand = defineCommand193({
64277
+ var lighthouseCommand = defineCommand199({
64031
64278
  meta: {
64032
64279
  name: "lighthouse",
64033
64280
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -64065,7 +64312,7 @@ Examples:
64065
64312
  });
64066
64313
 
64067
64314
  // src/commands/research/relevant-pages.ts
64068
- import { defineCommand as defineCommand194 } from "citty";
64315
+ import { defineCommand as defineCommand200 } from "citty";
64069
64316
  registerSchema({
64070
64317
  command: "research.relevant-pages",
64071
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).",
@@ -64091,7 +64338,7 @@ var FIELDS12 = {
64091
64338
  keywords: "Total organic keywords the page ranks for",
64092
64339
  top_10: "Keywords in positions 1-10"
64093
64340
  };
64094
- var relevantPagesCommand = defineCommand194({
64341
+ var relevantPagesCommand = defineCommand200({
64095
64342
  meta: {
64096
64343
  name: "relevant-pages",
64097
64344
  description: `Get the top pages of a competitor domain with traffic data.
@@ -64138,7 +64385,7 @@ Examples:
64138
64385
  });
64139
64386
 
64140
64387
  // src/commands/research/web.ts
64141
- import { defineCommand as defineCommand195 } from "citty";
64388
+ import { defineCommand as defineCommand201 } from "citty";
64142
64389
  registerSchema({
64143
64390
  command: "research.web",
64144
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).",
@@ -64189,7 +64436,7 @@ async function runDeepResearch(question) {
64189
64436
  }
64190
64437
  throw new Error("Deep research timed out");
64191
64438
  }
64192
- var webCommand = defineCommand195({
64439
+ var webCommand = defineCommand201({
64193
64440
  meta: {
64194
64441
  name: "web",
64195
64442
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -64251,7 +64498,7 @@ Examples:
64251
64498
  });
64252
64499
 
64253
64500
  // src/commands/research/index.ts
64254
- var researchCommand = defineCommand196({
64501
+ var researchCommand = defineCommand202({
64255
64502
  meta: {
64256
64503
  name: "research",
64257
64504
  description: `Competitive intelligence and AI-powered research commands.
@@ -64298,10 +64545,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
64298
64545
  });
64299
64546
 
64300
64547
  // src/commands/scheduled-agents/index.ts
64301
- import { defineCommand as defineCommand207 } from "citty";
64548
+ import { defineCommand as defineCommand213 } from "citty";
64302
64549
 
64303
64550
  // src/commands/scheduled-agents/create.ts
64304
- import { defineCommand as defineCommand197 } from "citty";
64551
+ import { defineCommand as defineCommand203 } from "citty";
64305
64552
 
64306
64553
  // src/commands/scheduled-agents/shared.ts
64307
64554
  var TEMP_SCHEDULED_AGENT_PREFIX = "temp_agent_";
@@ -64668,7 +64915,7 @@ registerSchema({
64668
64915
  }
64669
64916
  }
64670
64917
  });
64671
- var createCommand3 = defineCommand197({
64918
+ var createCommand3 = defineCommand203({
64672
64919
  meta: {
64673
64920
  name: "create",
64674
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'
@@ -64725,7 +64972,7 @@ var createCommand3 = defineCommand197({
64725
64972
  });
64726
64973
 
64727
64974
  // src/commands/scheduled-agents/delete.ts
64728
- import { defineCommand as defineCommand198 } from "citty";
64975
+ import { defineCommand as defineCommand204 } from "citty";
64729
64976
  registerSchema({
64730
64977
  command: "scheduled-agents.delete",
64731
64978
  description: "Stage deletion of a published scheduled agent, or cancel one this chat staged (temp_agent_*).",
@@ -64733,7 +64980,7 @@ registerSchema({
64733
64980
  id: { type: "string", description: "Published scheduled agent ID or temp_agent_* draft ID", required: true }
64734
64981
  }
64735
64982
  });
64736
- var deleteCommand3 = defineCommand198({
64983
+ var deleteCommand3 = defineCommand204({
64737
64984
  meta: {
64738
64985
  name: "delete",
64739
64986
  description: "Stage a scheduled agent deletion. Example: baker scheduled-agents delete <id-or-temp_agent_id>"
@@ -64762,7 +65009,7 @@ var deleteCommand3 = defineCommand198({
64762
65009
  });
64763
65010
 
64764
65011
  // src/commands/scheduled-agents/get.ts
64765
- import { defineCommand as defineCommand199 } from "citty";
65012
+ import { defineCommand as defineCommand205 } from "citty";
64766
65013
  registerSchema({
64767
65014
  command: "scheduled-agents.get",
64768
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.",
@@ -64771,7 +65018,7 @@ registerSchema({
64771
65018
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
64772
65019
  }
64773
65020
  });
64774
- var getCommand4 = defineCommand199({
65021
+ var getCommand4 = defineCommand205({
64775
65022
  meta: {
64776
65023
  name: "get",
64777
65024
  description: "Get a scheduled agent. Example: baker scheduled-agents get <id-or-temp_agent_id>"
@@ -64810,7 +65057,7 @@ var getCommand4 = defineCommand199({
64810
65057
  });
64811
65058
 
64812
65059
  // src/commands/scheduled-agents/list.ts
64813
- import { defineCommand as defineCommand200 } from "citty";
65060
+ import { defineCommand as defineCommand206 } from "citty";
64814
65061
  var FULL_DESCRIPTION = "Return every item and staged change as stored, briefs included (tens of KB). Prefer `get <id>` for one item.";
64815
65062
  registerSchema({
64816
65063
  command: "scheduled-agents.list",
@@ -64820,7 +65067,7 @@ registerSchema({
64820
65067
  full: { type: "boolean", description: FULL_DESCRIPTION, required: false }
64821
65068
  }
64822
65069
  });
64823
- var listCommand17 = defineCommand200({
65070
+ var listCommand18 = defineCommand206({
64824
65071
  meta: {
64825
65072
  name: "list",
64826
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."
@@ -64846,7 +65093,7 @@ var listCommand17 = defineCommand200({
64846
65093
  });
64847
65094
 
64848
65095
  // src/commands/scheduled-agents/run.ts
64849
- import { defineCommand as defineCommand201 } from "citty";
65096
+ import { defineCommand as defineCommand207 } from "citty";
64850
65097
  registerSchema({
64851
65098
  command: "scheduled-agents.run",
64852
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.",
@@ -64854,7 +65101,7 @@ registerSchema({
64854
65101
  id: { type: "string", description: "Published scheduled agent ID", required: true }
64855
65102
  }
64856
65103
  });
64857
- var runCommand2 = defineCommand201({
65104
+ var runCommand2 = defineCommand207({
64858
65105
  meta: {
64859
65106
  name: "run",
64860
65107
  description: "Run a published scheduled agent now. Example: baker scheduled-agents run <id>"
@@ -64886,7 +65133,7 @@ var runCommand2 = defineCommand201({
64886
65133
  });
64887
65134
 
64888
65135
  // src/commands/scheduled-agents/targets.ts
64889
- import { defineCommand as defineCommand202 } from "citty";
65136
+ import { defineCommand as defineCommand208 } from "citty";
64890
65137
  var ARGS2 = {
64891
65138
  trigger: {
64892
65139
  type: "string",
@@ -64920,7 +65167,7 @@ registerSchema({
64920
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.",
64921
65168
  args: ARGS2
64922
65169
  });
64923
- var targetsCommand = defineCommand202({
65170
+ var targetsCommand = defineCommand208({
64924
65171
  meta: {
64925
65172
  name: "targets",
64926
65173
  description: `What a trigger can be pointed at \u2014 so "when a Task is picked up" becomes "when THIS Task is picked up".
@@ -64963,7 +65210,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
64963
65210
  // src/commands/scheduled-agents/templates.ts
64964
65211
  import { readFile as readFile32 } from "fs/promises";
64965
65212
  import path46 from "path";
64966
- import { defineCommand as defineCommand203 } from "citty";
65213
+ import { defineCommand as defineCommand209 } from "citty";
64967
65214
  registerSchema({
64968
65215
  command: "scheduled-agents.templates",
64969
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.",
@@ -65018,7 +65265,7 @@ registerSchema({
65018
65265
  }
65019
65266
  }
65020
65267
  });
65021
- var templatesCommand = defineCommand203({
65268
+ var templatesCommand = defineCommand209({
65022
65269
  meta: {
65023
65270
  name: "templates",
65024
65271
  description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
@@ -65114,7 +65361,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
65114
65361
  });
65115
65362
 
65116
65363
  // src/commands/scheduled-agents/triggers.ts
65117
- import { defineCommand as defineCommand204 } from "citty";
65364
+ import { defineCommand as defineCommand210 } from "citty";
65118
65365
  var ARGS3 = {
65119
65366
  trigger: {
65120
65367
  type: "string",
@@ -65200,7 +65447,7 @@ function shapeTriggerCatalog(response, filter) {
65200
65447
  }
65201
65448
  return { ok: true, data: { groups, reacting: catalog.reacting } };
65202
65449
  }
65203
- var triggersCommand2 = defineCommand204({
65450
+ var triggersCommand2 = defineCommand210({
65204
65451
  meta: {
65205
65452
  name: "triggers",
65206
65453
  description: `Everything that can start a scheduled agent.
@@ -65230,7 +65477,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
65230
65477
  });
65231
65478
 
65232
65479
  // src/commands/scheduled-agents/unstage.ts
65233
- import { defineCommand as defineCommand205 } from "citty";
65480
+ import { defineCommand as defineCommand211 } from "citty";
65234
65481
  registerSchema({
65235
65482
  command: "scheduled-agents.unstage",
65236
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.",
@@ -65256,7 +65503,7 @@ function unstageBody(args5, chatId) {
65256
65503
  }
65257
65504
  return { chatId, ref: args5.ref, index };
65258
65505
  }
65259
- var unstageCommand = defineCommand205({
65506
+ var unstageCommand = defineCommand211({
65260
65507
  meta: {
65261
65508
  name: "unstage",
65262
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"
@@ -65287,7 +65534,7 @@ var unstageCommand = defineCommand205({
65287
65534
  });
65288
65535
 
65289
65536
  // src/commands/scheduled-agents/update.ts
65290
- import { defineCommand as defineCommand206 } from "citty";
65537
+ import { defineCommand as defineCommand212 } from "citty";
65291
65538
  registerSchema({
65292
65539
  command: "scheduled-agents.update",
65293
65540
  description: "Stage an update to a published scheduled agent, or to one this chat staged (temp_agent_*).",
@@ -65314,7 +65561,7 @@ registerSchema({
65314
65561
  enabled: { type: "string", description: "Replacement enabled state", required: false, enum: ["true", "false"] }
65315
65562
  }
65316
65563
  });
65317
- var updateCommand4 = defineCommand206({
65564
+ var updateCommand4 = defineCommand212({
65318
65565
  meta: {
65319
65566
  name: "update",
65320
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"
@@ -65385,7 +65632,7 @@ var updateCommand4 = defineCommand206({
65385
65632
  });
65386
65633
 
65387
65634
  // src/commands/scheduled-agents/index.ts
65388
- var scheduledAgentsCommand = defineCommand207({
65635
+ var scheduledAgentsCommand = defineCommand213({
65389
65636
  meta: {
65390
65637
  name: "scheduled-agents",
65391
65638
  description: `Manage Scheduled agents. Subcommands: list, get, triggers, targets, create, update, delete, unstage, run, templates.
@@ -65414,7 +65661,7 @@ Examples:
65414
65661
  Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
65415
65662
  },
65416
65663
  subCommands: {
65417
- list: listCommand17,
65664
+ list: listCommand18,
65418
65665
  get: getCommand4,
65419
65666
  triggers: triggersCommand2,
65420
65667
  targets: targetsCommand,
@@ -65428,14 +65675,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
65428
65675
  });
65429
65676
 
65430
65677
  // src/commands/schema.ts
65431
- import { defineCommand as defineCommand208 } from "citty";
65678
+ import { defineCommand as defineCommand214 } from "citty";
65432
65679
  function narrowToFamily(commandName, available) {
65433
65680
  const segments = commandName.split(".");
65434
65681
  const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
65435
65682
  const siblings = available.filter((name) => name.startsWith(prefix));
65436
65683
  return siblings.length > 0 ? siblings : available;
65437
65684
  }
65438
- var schemaCommand2 = defineCommand208({
65685
+ var schemaCommand2 = defineCommand214({
65439
65686
  meta: {
65440
65687
  name: "schema",
65441
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)"
@@ -65480,10 +65727,10 @@ var schemaCommand2 = defineCommand208({
65480
65727
  });
65481
65728
 
65482
65729
  // src/commands/studio/index.ts
65483
- import { defineCommand as defineCommand217 } from "citty";
65730
+ import { defineCommand as defineCommand223 } from "citty";
65484
65731
 
65485
65732
  // src/commands/studio/animate.ts
65486
- import { defineCommand as defineCommand209 } from "citty";
65733
+ import { defineCommand as defineCommand215 } from "citty";
65487
65734
 
65488
65735
  // src/commands/studio/spokenPace.ts
65489
65736
  var WORDS_PER_SECOND3 = SPOKEN_WORDS_PER_SECOND;
@@ -65709,7 +65956,7 @@ function parseImageRefs(spec) {
65709
65956
  }
65710
65957
  var defaultDeps = {
65711
65958
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
65712
- upload: (path48) => uploadLocalImage({ file: path48, contentType: detectImageContentType(path48), source: "uploaded" })
65959
+ upload: (path49) => uploadLocalImage({ file: path49, contentType: detectImageContentType(path49), source: "uploaded" })
65713
65960
  };
65714
65961
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
65715
65962
  const refs = parseImageRefs(spec);
@@ -65729,10 +65976,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
65729
65976
  }
65730
65977
  return { imageIds, added };
65731
65978
  }
65732
- function uploadFailure(path48) {
65979
+ function uploadFailure(path49) {
65733
65980
  return (error) => {
65734
65981
  if (error instanceof ApiError) throw error;
65735
- 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.`);
65736
65983
  };
65737
65984
  }
65738
65985
 
@@ -65996,7 +66243,7 @@ async function adByHandHintFor(body) {
65996
66243
  return null;
65997
66244
  }
65998
66245
  }
65999
- var animateCommand = defineCommand209({
66246
+ var animateCommand = defineCommand215({
66000
66247
  meta: {
66001
66248
  name: "animate",
66002
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"
@@ -66121,7 +66368,7 @@ var animateCommand = defineCommand209({
66121
66368
  });
66122
66369
 
66123
66370
  // src/commands/studio/generate.ts
66124
- import { defineCommand as defineCommand210 } from "citty";
66371
+ import { defineCommand as defineCommand216 } from "citty";
66125
66372
  var MODEL_LIST2 = IMAGE_MODEL_IDS;
66126
66373
  var DEFAULT_MAX_WAIT_MS2 = 24e4;
66127
66374
  registerSchema({
@@ -66257,7 +66504,7 @@ function buildGenerateBody(args5, prompt) {
66257
66504
  }
66258
66505
  return body;
66259
66506
  }
66260
- var generateCommand = defineCommand210({
66507
+ var generateCommand = defineCommand216({
66261
66508
  meta: {
66262
66509
  name: "generate",
66263
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]]'"
@@ -66340,7 +66587,7 @@ var generateCommand = defineCommand210({
66340
66587
  });
66341
66588
 
66342
66589
  // src/commands/studio/get.ts
66343
- import { defineCommand as defineCommand211 } from "citty";
66590
+ import { defineCommand as defineCommand217 } from "citty";
66344
66591
  registerSchema({
66345
66592
  command: "studio.get",
66346
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.",
@@ -66349,7 +66596,7 @@ registerSchema({
66349
66596
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
66350
66597
  }
66351
66598
  });
66352
- var getCommand5 = defineCommand211({
66599
+ var getCommand5 = defineCommand217({
66353
66600
  meta: {
66354
66601
  name: "get",
66355
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"
@@ -66378,7 +66625,7 @@ var getCommand5 = defineCommand211({
66378
66625
  });
66379
66626
 
66380
66627
  // src/commands/studio/improve.ts
66381
- import { defineCommand as defineCommand212 } from "citty";
66628
+ import { defineCommand as defineCommand218 } from "citty";
66382
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.";
66383
66630
  registerSchema({
66384
66631
  command: "studio.improve",
@@ -66403,7 +66650,7 @@ registerSchema({
66403
66650
  }
66404
66651
  }
66405
66652
  });
66406
- var improveCommand = defineCommand212({
66653
+ var improveCommand = defineCommand218({
66407
66654
  meta: {
66408
66655
  name: "improve",
66409
66656
  description: `${DESCRIPTION2}
@@ -66447,7 +66694,7 @@ Examples:
66447
66694
  });
66448
66695
 
66449
66696
  // src/commands/studio/keep.ts
66450
- import { defineCommand as defineCommand213 } from "citty";
66697
+ import { defineCommand as defineCommand219 } from "citty";
66451
66698
  registerSchema({
66452
66699
  command: "studio.keep",
66453
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.",
@@ -66457,7 +66704,7 @@ registerSchema({
66457
66704
  undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
66458
66705
  }
66459
66706
  });
66460
- var keepCommand = defineCommand213({
66707
+ var keepCommand = defineCommand219({
66461
66708
  meta: {
66462
66709
  name: "keep",
66463
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"
@@ -66508,7 +66755,7 @@ var keepCommand = defineCommand213({
66508
66755
  });
66509
66756
 
66510
66757
  // src/commands/studio/list.ts
66511
- import { defineCommand as defineCommand214 } from "citty";
66758
+ import { defineCommand as defineCommand220 } from "citty";
66512
66759
  registerSchema({
66513
66760
  command: "studio.list",
66514
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.",
@@ -66519,7 +66766,7 @@ registerSchema({
66519
66766
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
66520
66767
  }
66521
66768
  });
66522
- var listCommand18 = defineCommand214({
66769
+ var listCommand19 = defineCommand220({
66523
66770
  meta: {
66524
66771
  name: "list",
66525
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"
@@ -66553,7 +66800,7 @@ var listCommand18 = defineCommand214({
66553
66800
  });
66554
66801
 
66555
66802
  // src/commands/studio/models.ts
66556
- import { defineCommand as defineCommand215 } from "citty";
66803
+ import { defineCommand as defineCommand221 } from "citty";
66557
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.";
66558
66805
  registerSchema({
66559
66806
  command: "studio.models",
@@ -66640,7 +66887,7 @@ function buildModelCards(kind, model) {
66640
66887
  const selected = model ? ids.filter((id) => id === model) : ids;
66641
66888
  return selected.map((id) => build(id));
66642
66889
  }
66643
- var modelsCommand = defineCommand215({
66890
+ var modelsCommand = defineCommand221({
66644
66891
  meta: {
66645
66892
  name: "models",
66646
66893
  description: `${DESCRIPTION3}
@@ -66687,13 +66934,13 @@ Examples:
66687
66934
  });
66688
66935
 
66689
66936
  // src/commands/studio/skills.ts
66690
- import { defineCommand as defineCommand216 } from "citty";
66937
+ import { defineCommand as defineCommand222 } from "citty";
66691
66938
  registerSchema({
66692
66939
  command: "studio.skills",
66693
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.",
66694
66941
  args: {}
66695
66942
  });
66696
- var skillsCommand = defineCommand216({
66943
+ var skillsCommand = defineCommand222({
66697
66944
  meta: {
66698
66945
  name: "skills",
66699
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"
@@ -66717,7 +66964,7 @@ var skillsCommand = defineCommand216({
66717
66964
  });
66718
66965
 
66719
66966
  // src/commands/studio/index.ts
66720
- var studioCommand = defineCommand217({
66967
+ var studioCommand = defineCommand223({
66721
66968
  meta: {
66722
66969
  name: "studio",
66723
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.
@@ -66751,7 +66998,7 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
66751
66998
  generate: generateCommand,
66752
66999
  animate: animateCommand,
66753
67000
  get: getCommand5,
66754
- list: listCommand18,
67001
+ list: listCommand19,
66755
67002
  keep: keepCommand,
66756
67003
  models: modelsCommand,
66757
67004
  skills: skillsCommand,
@@ -66760,10 +67007,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
66760
67007
  });
66761
67008
 
66762
67009
  // src/commands/tag-manager/index.ts
66763
- import { defineCommand as defineCommand221 } from "citty";
67010
+ import { defineCommand as defineCommand227 } from "citty";
66764
67011
 
66765
67012
  // src/commands/tag-manager/draft.ts
66766
- import { defineCommand as defineCommand218 } from "citty";
67013
+ import { defineCommand as defineCommand224 } from "citty";
66767
67014
 
66768
67015
  // src/commands/tag-manager/shared.ts
66769
67016
  import { readFileSync as readFileSync21 } from "fs";
@@ -66824,19 +67071,17 @@ var STAGE_HINTS2 = [
66824
67071
  async function stageOp4(op) {
66825
67072
  const chatId = requireChatId();
66826
67073
  try {
66827
- const { data, hints: hints2 } = splitHints(
66828
- await apiPost("/api/tag-manager/draft/stage", { chatId, op })
66829
- );
66830
- 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 });
66831
67076
  } catch (err) {
66832
67077
  handleError8(err);
66833
67078
  }
66834
67079
  }
66835
- async function draftAction3(path48, body, chat) {
67080
+ async function draftAction3(path49, body, chat) {
66836
67081
  const chatId = resolveChatId(chat);
66837
67082
  try {
66838
- const { data, hints: hints2 } = splitHints(await apiPost(path48, { chatId, ...body }));
66839
- writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
67083
+ const data = await apiPost(path49, { chatId, ...body });
67084
+ writeJsonEnvelope({ ok: true, data });
66840
67085
  return data;
66841
67086
  } catch (err) {
66842
67087
  handleError8(err);
@@ -66867,13 +67112,12 @@ function renderDraft2(response) {
66867
67112
  async function draftList2(json, chat) {
66868
67113
  const chatId = resolveChatId(chat);
66869
67114
  try {
66870
- const response = await apiPost("/api/tag-manager/draft", { chatId });
66871
- const { data, hints: hints2 } = splitHints(response);
67115
+ const data = await apiPost("/api/tag-manager/draft", { chatId });
66872
67116
  if (json) {
66873
- writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
67117
+ writeJsonEnvelope({ ok: true, data });
66874
67118
  return;
66875
67119
  }
66876
- process.stdout.write(`${renderDraft2(data)}${hintLines(response)}
67120
+ process.stdout.write(`${renderDraft2(data)}
66877
67121
  `);
66878
67122
  } catch (err) {
66879
67123
  handleError8(err);
@@ -66889,13 +67133,13 @@ registerSchema({
66889
67133
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
66890
67134
  }
66891
67135
  });
66892
- var draftCommand5 = defineCommand218({
67136
+ var draftCommand5 = defineCommand224({
66893
67137
  meta: {
66894
67138
  name: "draft",
66895
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."
66896
67140
  },
66897
67141
  subCommands: {
66898
- list: defineCommand218({
67142
+ list: defineCommand224({
66899
67143
  meta: {
66900
67144
  name: "list",
66901
67145
  description: "Review everything staged on this chat (--json for the raw envelope)"
@@ -66908,7 +67152,7 @@ var draftCommand5 = defineCommand218({
66908
67152
  await draftList2(args5.json === true, args5.chat);
66909
67153
  }
66910
67154
  }),
66911
- show: defineCommand218({
67155
+ show: defineCommand224({
66912
67156
  meta: {
66913
67157
  name: "show",
66914
67158
  description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
@@ -66925,7 +67169,7 @@ var draftCommand5 = defineCommand218({
66925
67169
  );
66926
67170
  }
66927
67171
  }),
66928
- amend: defineCommand218({
67172
+ amend: defineCommand224({
66929
67173
  meta: {
66930
67174
  name: "amend",
66931
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."
@@ -66942,7 +67186,7 @@ var draftCommand5 = defineCommand218({
66942
67186
  });
66943
67187
  }
66944
67188
  }),
66945
- remove: defineCommand218({
67189
+ remove: defineCommand224({
66946
67190
  meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
66947
67191
  args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
66948
67192
  run: async ({ args: args5 }) => {
@@ -66951,7 +67195,7 @@ var draftCommand5 = defineCommand218({
66951
67195
  });
66952
67196
  }
66953
67197
  }),
66954
- clear: defineCommand218({
67198
+ clear: defineCommand224({
66955
67199
  meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
66956
67200
  run: async () => {
66957
67201
  await draftAction3("/api/tag-manager/draft/clear", {});
@@ -66961,7 +67205,7 @@ var draftCommand5 = defineCommand218({
66961
67205
  });
66962
67206
 
66963
67207
  // src/commands/tag-manager/read.ts
66964
- import { defineCommand as defineCommand219 } from "citty";
67208
+ import { defineCommand as defineCommand225 } from "citty";
66965
67209
  registerSchema({
66966
67210
  command: "tagManager.containers",
66967
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.",
@@ -67002,7 +67246,7 @@ function containersHints(containers) {
67002
67246
  }))
67003
67247
  });
67004
67248
  }
67005
- var containersCommand = defineCommand219({
67249
+ var containersCommand = defineCommand225({
67006
67250
  meta: {
67007
67251
  name: "containers",
67008
67252
  description: `List Tag Manager containers reachable by this company's connection.
@@ -67019,7 +67263,7 @@ Start here:
67019
67263
  }
67020
67264
  }
67021
67265
  });
67022
- var readCommand = defineCommand219({
67266
+ var readCommand = defineCommand225({
67023
67267
  meta: {
67024
67268
  name: "read",
67025
67269
  description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
@@ -67061,7 +67305,7 @@ Examples:
67061
67305
  });
67062
67306
 
67063
67307
  // src/commands/tag-manager/write-commands.ts
67064
- import { defineCommand as defineCommand220 } from "citty";
67308
+ import { defineCommand as defineCommand226 } from "citty";
67065
67309
  var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
67066
67310
  var ENTITIES = [
67067
67311
  {
@@ -67117,10 +67361,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
67117
67361
  });
67118
67362
  }
67119
67363
  function entityCommand(entity, noun, example) {
67120
- return defineCommand220({
67364
+ return defineCommand226({
67121
67365
  meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
67122
67366
  subCommands: {
67123
- create: defineCommand220({
67367
+ create: defineCommand226({
67124
67368
  meta: {
67125
67369
  name: "create",
67126
67370
  description: `Stage a new ${noun}
@@ -67142,7 +67386,7 @@ Examples:
67142
67386
  });
67143
67387
  }
67144
67388
  }),
67145
- update: defineCommand220({
67389
+ update: defineCommand226({
67146
67390
  meta: {
67147
67391
  name: "update",
67148
67392
  description: `Stage an update to an existing ${noun} (pass its id or path)`
@@ -67162,7 +67406,7 @@ Examples:
67162
67406
  });
67163
67407
  }
67164
67408
  }),
67165
- delete: defineCommand220({
67409
+ delete: defineCommand226({
67166
67410
  meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
67167
67411
  args: {
67168
67412
  id: { type: "positional", description: `${noun} id or path`, required: false },
@@ -67203,7 +67447,7 @@ function builtinTypes(args5) {
67203
67447
  }
67204
67448
  return raw.split(",").map((entry) => entry.trim());
67205
67449
  }
67206
- var builtinCommand = defineCommand220({
67450
+ var builtinCommand = defineCommand226({
67207
67451
  meta: {
67208
67452
  name: "builtin",
67209
67453
  description: `Enable or disable built-in variables
@@ -67213,7 +67457,7 @@ Examples:
67213
67457
  baker tag-manager builtin disable --types formId`
67214
67458
  },
67215
67459
  subCommands: {
67216
- enable: defineCommand220({
67460
+ enable: defineCommand226({
67217
67461
  meta: { name: "enable", description: "Stage enabling built-in variables" },
67218
67462
  args: {
67219
67463
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -67227,7 +67471,7 @@ Examples:
67227
67471
  });
67228
67472
  }
67229
67473
  }),
67230
- disable: defineCommand220({
67474
+ disable: defineCommand226({
67231
67475
  meta: { name: "disable", description: "Stage disabling built-in variables" },
67232
67476
  args: {
67233
67477
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -67245,7 +67489,7 @@ Examples:
67245
67489
  });
67246
67490
 
67247
67491
  // src/commands/tag-manager/index.ts
67248
- var tagManagerCommand = defineCommand221({
67492
+ var tagManagerCommand = defineCommand227({
67249
67493
  meta: {
67250
67494
  name: "tag-manager",
67251
67495
  description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
@@ -67282,7 +67526,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
67282
67526
  });
67283
67527
 
67284
67528
  // src/commands/tags/index.ts
67285
- import { defineCommand as defineCommand222 } from "citty";
67529
+ import { defineCommand as defineCommand228 } from "citty";
67286
67530
 
67287
67531
  // src/commands/tags/shared.ts
67288
67532
  function failApi3(err) {
@@ -67362,7 +67606,7 @@ async function listTags(json) {
67362
67606
  var listArgs12 = {
67363
67607
  json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
67364
67608
  };
67365
- var listCommand19 = defineCommand222({
67609
+ var listCommand20 = defineCommand228({
67366
67610
  meta: {
67367
67611
  name: "list",
67368
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"
@@ -67381,7 +67625,7 @@ async function listDraft4(chat) {
67381
67625
  failApi3(err);
67382
67626
  }
67383
67627
  }
67384
- var draftCommand6 = defineCommand222({
67628
+ var draftCommand6 = defineCommand228({
67385
67629
  meta: {
67386
67630
  name: "draft",
67387
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."
@@ -67391,7 +67635,7 @@ var draftCommand6 = defineCommand222({
67391
67635
  await listDraft4(args5.chat);
67392
67636
  }
67393
67637
  });
67394
- var tagsCommand4 = defineCommand222({
67638
+ var tagsCommand4 = defineCommand228({
67395
67639
  meta: {
67396
67640
  name: "tags",
67397
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.
@@ -67408,7 +67652,7 @@ Examples:
67408
67652
  Full guide: __tooling__/docs/tools/baker/tags.md`
67409
67653
  },
67410
67654
  subCommands: {
67411
- list: listCommand19,
67655
+ list: listCommand20,
67412
67656
  draft: draftCommand6
67413
67657
  },
67414
67658
  // Bare `baker tags` lists the effective tags. `args` stays so citty knows this
@@ -67420,10 +67664,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
67420
67664
  });
67421
67665
 
67422
67666
  // src/commands/testimonials/index.ts
67423
- import { defineCommand as defineCommand226 } from "citty";
67667
+ import { defineCommand as defineCommand232 } from "citty";
67424
67668
 
67425
67669
  // src/commands/testimonials/get.ts
67426
- import { defineCommand as defineCommand223 } from "citty";
67670
+ import { defineCommand as defineCommand229 } from "citty";
67427
67671
  registerSchema({
67428
67672
  command: "testimonials.get",
67429
67673
  description: "Get a single testimonial by ID",
@@ -67431,7 +67675,7 @@ registerSchema({
67431
67675
  id: { type: "string", description: "Testimonial ID", required: true }
67432
67676
  }
67433
67677
  });
67434
- var getCommand6 = defineCommand223({
67678
+ var getCommand6 = defineCommand229({
67435
67679
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
67436
67680
  args: {
67437
67681
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -67468,7 +67712,7 @@ var getCommand6 = defineCommand223({
67468
67712
  });
67469
67713
 
67470
67714
  // src/commands/testimonials/list.ts
67471
- import { defineCommand as defineCommand224 } from "citty";
67715
+ import { defineCommand as defineCommand230 } from "citty";
67472
67716
 
67473
67717
  // src/commands/testimonials/emptyCorpusHints.ts
67474
67718
  function resolveEmptyReason({
@@ -67625,7 +67869,7 @@ function buildListParams(args5) {
67625
67869
  }
67626
67870
  return params;
67627
67871
  }
67628
- var listCommand20 = defineCommand224({
67872
+ var listCommand21 = defineCommand230({
67629
67873
  meta: {
67630
67874
  name: "list",
67631
67875
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -67685,7 +67929,7 @@ var listCommand20 = defineCommand224({
67685
67929
  });
67686
67930
 
67687
67931
  // src/commands/testimonials/search.ts
67688
- import { defineCommand as defineCommand225 } from "citty";
67932
+ import { defineCommand as defineCommand231 } from "citty";
67689
67933
  var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
67690
67934
  function languageBiasHint(results, requestedLanguage) {
67691
67935
  if (requestedLanguage) {
@@ -67764,7 +68008,7 @@ function buildSearchRequest(query, args5) {
67764
68008
  }
67765
68009
  return body;
67766
68010
  }
67767
- var searchCommand3 = defineCommand225({
68011
+ var searchCommand3 = defineCommand231({
67768
68012
  meta: {
67769
68013
  name: "search",
67770
68014
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -67828,7 +68072,7 @@ var searchCommand3 = defineCommand225({
67828
68072
  var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
67829
68073
 
67830
68074
  // src/commands/testimonials/index.ts
67831
- var testimonialsCommand = defineCommand226({
68075
+ var testimonialsCommand = defineCommand232({
67832
68076
  meta: {
67833
68077
  name: "testimonials",
67834
68078
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -67844,16 +68088,16 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
67844
68088
  subCommands: {
67845
68089
  get: getCommand6,
67846
68090
  search: searchCommand3,
67847
- list: listCommand20,
68091
+ list: listCommand21,
67848
68092
  tags: tagsCommand5
67849
68093
  }
67850
68094
  });
67851
68095
 
67852
68096
  // src/commands/videos/index.ts
67853
- import { defineCommand as defineCommand234 } from "citty";
68097
+ import { defineCommand as defineCommand241 } from "citty";
67854
68098
 
67855
68099
  // src/commands/videos/delete.ts
67856
- import { defineCommand as defineCommand227 } from "citty";
68100
+ import { defineCommand as defineCommand233 } from "citty";
67857
68101
  registerSchema({
67858
68102
  command: "videos.delete",
67859
68103
  description: "Delete a video by ID",
@@ -67867,7 +68111,7 @@ registerSchema({
67867
68111
  }
67868
68112
  }
67869
68113
  });
67870
- var deleteCommand4 = defineCommand227({
68114
+ var deleteCommand4 = defineCommand233({
67871
68115
  meta: {
67872
68116
  name: "delete",
67873
68117
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -67908,7 +68152,7 @@ var deleteCommand4 = defineCommand227({
67908
68152
  });
67909
68153
 
67910
68154
  // src/commands/videos/describe.ts
67911
- import { defineCommand as defineCommand228 } from "citty";
68155
+ import { defineCommand as defineCommand234 } from "citty";
67912
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.";
67913
68157
  registerSchema({
67914
68158
  command: "videos.describe",
@@ -67925,7 +68169,7 @@ registerSchema({
67925
68169
  full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
67926
68170
  }
67927
68171
  });
67928
- var describeCommand2 = defineCommand228({
68172
+ var describeCommand2 = defineCommand234({
67929
68173
  meta: {
67930
68174
  name: "describe",
67931
68175
  description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
@@ -67982,108 +68226,12 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
67982
68226
  }
67983
68227
  });
67984
68228
 
67985
- // src/commands/videos/get.ts
67986
- import { defineCommand as defineCommand229 } from "citty";
67987
- registerSchema({
67988
- command: "videos.get",
67989
- description: "Get a single video by ID",
67990
- args: {
67991
- id: { type: "string", description: "Video ID", required: true }
67992
- }
67993
- });
67994
- var getCommand7 = defineCommand229({
67995
- meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
67996
- args: {
67997
- id: { type: "positional", description: "Video ID", required: false },
67998
- "video-id": { type: "string", description: "Video ID (alternative to positional)", required: false },
67999
- output: { type: "string", description: "Output format: json|files|md", required: false, default: "json" },
68000
- fields: { type: "string", description: "Comma-separated field names to include", required: false },
68001
- full: { type: "boolean", description: "Include full metadata", required: false, default: false }
68002
- },
68003
- run: async ({ args: args5 }) => {
68004
- try {
68005
- const id = args5.id || args5["video-id"];
68006
- if (!id) {
68007
- writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Video ID is required" } });
68008
- process.exit(1);
68009
- }
68010
- validateConvexId(id);
68011
- const data = await apiGet("/api/videos/get", { id });
68012
- writeOutput(
68013
- { ok: true, data },
68014
- args5.output || "json",
68015
- args5.fields ? args5.fields.split(",") : void 0,
68016
- args5.full,
68017
- videoNormalizer
68018
- );
68019
- } catch (err) {
68020
- if (err instanceof ApiError) {
68021
- writeJson({ ok: false, error: { code: err.code, message: err.message } });
68022
- process.exit(1);
68023
- }
68024
- writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
68025
- process.exit(1);
68026
- }
68027
- }
68028
- });
68029
-
68030
- // src/commands/videos/group.ts
68031
- import { defineCommand as defineCommand230 } from "citty";
68032
- registerSchema({
68033
- command: "videos.group",
68034
- description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
68035
- args: {
68036
- id: { type: "string", description: "A video or image id \u2014 returns that asset's whole set", required: false },
68037
- "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
68038
- }
68039
- });
68040
- var groupCommand2 = defineCommand230({
68041
- meta: {
68042
- name: "group",
68043
- 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>"
68044
- },
68045
- args: {
68046
- id: { type: "positional", description: "A video or image id from the set", required: false },
68047
- "group-key": { type: "string", description: "The set key, if you already have it", required: false }
68048
- },
68049
- run: async ({ args: args5 }) => {
68050
- await runMediaGroupLookup({
68051
- id: args5.id,
68052
- groupKey: args5["group-key"],
68053
- path: "/api/videos/group",
68054
- idLabel: "a video or image id"
68055
- });
68056
- }
68057
- });
68058
-
68059
- // src/commands/videos/ingest.ts
68060
- 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";
68061
68232
  import { tmpdir as tmpdir4 } from "os";
68062
68233
  import path47 from "path";
68063
- import { defineCommand as defineCommand231 } from "citty";
68064
-
68065
- // src/lib/streamUpload.ts
68066
- import { createHash as createHash2 } from "crypto";
68067
- import { createReadStream } from "fs";
68068
- import { Readable } from "stream";
68069
- import { pipeline } from "stream/promises";
68070
- async function hashFile(filePath) {
68071
- const hash = createHash2("sha256");
68072
- await pipeline(createReadStream(filePath), hash);
68073
- return hash.digest("hex");
68074
- }
68075
- async function putFileStreamed(args5) {
68076
- const body = Readable.toWeb(createReadStream(args5.filePath));
68077
- return await fetch(args5.putUrl, {
68078
- method: "PUT",
68079
- body,
68080
- headers: {
68081
- "Content-Type": args5.contentType,
68082
- "Content-Length": String(args5.sizeBytes)
68083
- },
68084
- duplex: "half"
68085
- });
68086
- }
68234
+ import { defineCommand as defineCommand235 } from "citty";
68087
68235
 
68088
68236
  // src/commands/videos/videoPlatforms.ts
68089
68237
  var PLATFORM_DOMAINS = {
@@ -68092,6 +68240,7 @@ var PLATFORM_DOMAINS = {
68092
68240
  tiktok: ["tiktok.com"],
68093
68241
  twitter: ["twitter.com", "x.com", "vxtwitter.com", "fixvx.com"],
68094
68242
  facebook: ["facebook.com", "fb.watch", "fb.com"],
68243
+ linkedin: ["linkedin.com", "lnkd.in"],
68095
68244
  reddit: ["reddit.com", "redd.it"],
68096
68245
  vimeo: ["vimeo.com"],
68097
68246
  dailymotion: ["dailymotion.com", "dai.ly"],
@@ -68196,6 +68345,218 @@ function safePathname(rawUrl) {
68196
68345
  }
68197
68346
  }
68198
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
+
68199
68560
  // src/commands/videos/ingest.ts
68200
68561
  registerSchema({
68201
68562
  command: "videos.ingest",
@@ -68224,7 +68585,7 @@ registerSchema({
68224
68585
  "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
68225
68586
  }
68226
68587
  });
68227
- var ingestCommand2 = defineCommand231({
68588
+ var ingestCommand2 = defineCommand238({
68228
68589
  meta: {
68229
68590
  name: "ingest",
68230
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"
@@ -68410,7 +68771,7 @@ function ingestUrl(args5) {
68410
68771
  }
68411
68772
  async function downloadThenIngest(args5, country) {
68412
68773
  const vimeoCookie = captureVimeoCookie();
68413
- const workDir = await mkdtemp3(path47.join(tmpdir4(), "videos-ingest-"));
68774
+ const workDir = await mkdtemp4(path48.join(tmpdir5(), "videos-ingest-"));
68414
68775
  try {
68415
68776
  const probe = await probeYtDlp({ url: args5.url, country, vimeoCookie, cookieDir: workDir });
68416
68777
  if (isAudioOnly(probe.info)) {
@@ -68467,7 +68828,7 @@ async function downloadThenIngest(args5, country) {
68467
68828
  ...reducedQuality ? { reducedQuality: true } : {}
68468
68829
  };
68469
68830
  } finally {
68470
- await rm9(workDir, { recursive: true, force: true }).catch(() => {
68831
+ await rm10(workDir, { recursive: true, force: true }).catch(() => {
68471
68832
  });
68472
68833
  }
68473
68834
  }
@@ -68486,7 +68847,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
68486
68847
  }
68487
68848
 
68488
68849
  // src/commands/videos/search.ts
68489
- import { defineCommand as defineCommand232 } from "citty";
68850
+ import { defineCommand as defineCommand239 } from "citty";
68490
68851
  registerSchema({
68491
68852
  command: "videos.search",
68492
68853
  description: "Search videos by text query. Only returns ready videos.",
@@ -68496,7 +68857,7 @@ registerSchema({
68496
68857
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
68497
68858
  }
68498
68859
  });
68499
- var searchCommand4 = defineCommand232({
68860
+ var searchCommand4 = defineCommand239({
68500
68861
  meta: {
68501
68862
  name: "search",
68502
68863
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -68548,7 +68909,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
68548
68909
  // src/commands/videos/upload.ts
68549
68910
  import { readFile as readFile33, stat as stat12 } from "fs/promises";
68550
68911
  import { basename as basename3, extname as extname4 } from "path";
68551
- import { defineCommand as defineCommand233 } from "citty";
68912
+ import { defineCommand as defineCommand240 } from "citty";
68552
68913
  var MIME_MAP = {
68553
68914
  ".mp4": "video/mp4",
68554
68915
  ".mov": "video/quicktime",
@@ -68590,7 +68951,7 @@ function detectContentType(filePath) {
68590
68951
  function isRemoteUrl3(value) {
68591
68952
  return /^https?:\/\//i.test(value);
68592
68953
  }
68593
- var uploadCommand2 = defineCommand233({
68954
+ var uploadCommand2 = defineCommand240({
68594
68955
  meta: {
68595
68956
  name: "upload",
68596
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"
@@ -68674,10 +69035,10 @@ var uploadCommand2 = defineCommand233({
68674
69035
  });
68675
69036
 
68676
69037
  // src/commands/videos/index.ts
68677
- var videosCommand = defineCommand234({
69038
+ var videosCommand = defineCommand241({
68678
69039
  meta: {
68679
69040
  name: "videos",
68680
- 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.
68681
69042
 
68682
69043
  Examples:
68683
69044
  baker videos search "product demo" --limit 5
@@ -68685,6 +69046,7 @@ Examples:
68685
69046
  baker videos get <video-id>
68686
69047
  baker videos upload ./demo.mp4
68687
69048
  baker videos ingest https://www.youtube.com/watch?v=abc123
69049
+ baker videos fetch https://www.youtube.com/watch?v=abc123 --out ./clips
68688
69050
  baker videos describe <video-id> --description "Customer testimonial, 30s"
68689
69051
  baker videos delete <video-id> --dry-run
68690
69052
  baker videos tags
@@ -68696,6 +69058,7 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
68696
69058
  search: searchCommand4,
68697
69059
  upload: uploadCommand2,
68698
69060
  ingest: ingestCommand2,
69061
+ fetch: fetchCommand2,
68699
69062
  describe: describeCommand2,
68700
69063
  delete: deleteCommand4,
68701
69064
  tags: tagsCommand6
@@ -68703,10 +69066,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
68703
69066
  });
68704
69067
 
68705
69068
  // src/commands/winning-ads/index.ts
68706
- import { defineCommand as defineCommand247 } from "citty";
69069
+ import { defineCommand as defineCommand254 } from "citty";
68707
69070
 
68708
69071
  // src/commands/winning-ads/advertisers.ts
68709
- import { defineCommand as defineCommand235 } from "citty";
69072
+ import { defineCommand as defineCommand242 } from "citty";
68710
69073
 
68711
69074
  // src/commands/winning-ads/shared.ts
68712
69075
  function splitList3(value) {
@@ -68715,7 +69078,7 @@ function splitList3(value) {
68715
69078
  }
68716
69079
  return value.split(",").map((v) => v.trim()).filter(Boolean);
68717
69080
  }
68718
- function reportError2(err) {
69081
+ function reportError3(err) {
68719
69082
  if (err instanceof ApiError) {
68720
69083
  writeJson({ ok: false, error: { code: err.code, message: err.message } });
68721
69084
  process.exit(1);
@@ -68759,7 +69122,7 @@ function advertiserNormalizer(record, full) {
68759
69122
  last_synced_at: record.last_synced_at ?? null
68760
69123
  };
68761
69124
  }
68762
- var advertisersCommand2 = defineCommand235({
69125
+ var advertisersCommand2 = defineCommand242({
68763
69126
  meta: {
68764
69127
  name: "advertisers",
68765
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'
@@ -68811,13 +69174,13 @@ var advertisersCommand2 = defineCommand235({
68811
69174
  advertiserNormalizer
68812
69175
  );
68813
69176
  } catch (err) {
68814
- reportError2(err);
69177
+ reportError3(err);
68815
69178
  }
68816
69179
  }
68817
69180
  });
68818
69181
 
68819
69182
  // src/commands/winning-ads/brief.ts
68820
- import { defineCommand as defineCommand236 } from "citty";
69183
+ import { defineCommand as defineCommand243 } from "citty";
68821
69184
  registerSchema({
68822
69185
  command: "winning-ads.brief",
68823
69186
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -68863,7 +69226,7 @@ function parseDna(raw) {
68863
69226
  }
68864
69227
  return parsed;
68865
69228
  }
68866
- var briefCommand = defineCommand236({
69229
+ var briefCommand = defineCommand243({
68867
69230
  meta: {
68868
69231
  name: "brief",
68869
69232
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -68893,13 +69256,13 @@ var briefCommand = defineCommand236({
68893
69256
  const data = await apiPost("/api/ad-library/brief", body);
68894
69257
  writeJson({ ok: true, data });
68895
69258
  } catch (err) {
68896
- reportError2(err);
69259
+ reportError3(err);
68897
69260
  }
68898
69261
  }
68899
69262
  });
68900
69263
 
68901
69264
  // src/commands/winning-ads/content.ts
68902
- import { defineCommand as defineCommand237 } from "citty";
69265
+ import { defineCommand as defineCommand244 } from "citty";
68903
69266
  registerSchema({
68904
69267
  command: "winning-ads.content",
68905
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.",
@@ -68912,7 +69275,7 @@ registerSchema({
68912
69275
  }
68913
69276
  }
68914
69277
  });
68915
- var contentCommand = defineCommand237({
69278
+ var contentCommand = defineCommand244({
68916
69279
  meta: {
68917
69280
  name: "content",
68918
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"
@@ -68955,13 +69318,13 @@ var contentCommand = defineCommand237({
68955
69318
  adContentNormalizer
68956
69319
  );
68957
69320
  } catch (err) {
68958
- reportError2(err);
69321
+ reportError3(err);
68959
69322
  }
68960
69323
  }
68961
69324
  });
68962
69325
 
68963
69326
  // src/commands/winning-ads/feed.ts
68964
- import { defineCommand as defineCommand238 } from "citty";
69327
+ import { defineCommand as defineCommand245 } from "citty";
68965
69328
  function buildFeedParams(input) {
68966
69329
  const params = {};
68967
69330
  const advertiser = splitList3(input.advertiser);
@@ -69013,7 +69376,7 @@ registerSchema({
69013
69376
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
69014
69377
  }
69015
69378
  });
69016
- var feedCommand2 = defineCommand238({
69379
+ var feedCommand2 = defineCommand245({
69017
69380
  meta: {
69018
69381
  name: "feed",
69019
69382
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -69092,13 +69455,13 @@ var feedCommand2 = defineCommand238({
69092
69455
  `);
69093
69456
  }
69094
69457
  } catch (err) {
69095
- reportError2(err);
69458
+ reportError3(err);
69096
69459
  }
69097
69460
  }
69098
69461
  });
69099
69462
 
69100
69463
  // src/commands/winning-ads/follow.ts
69101
- import { defineCommand as defineCommand239 } from "citty";
69464
+ import { defineCommand as defineCommand246 } from "citty";
69102
69465
  var PLATFORMS = adLibraryPlatformSchema.options;
69103
69466
  registerSchema({
69104
69467
  command: "winning-ads.follow",
@@ -69113,7 +69476,7 @@ registerSchema({
69113
69476
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
69114
69477
  }
69115
69478
  });
69116
- var followCommand = defineCommand239({
69479
+ var followCommand = defineCommand246({
69117
69480
  meta: {
69118
69481
  name: "follow",
69119
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'
@@ -69154,13 +69517,13 @@ var followCommand = defineCommand239({
69154
69517
  }
69155
69518
  writeJson({ ok: true, data, hints: hints2 });
69156
69519
  } catch (err) {
69157
- reportError2(err);
69520
+ reportError3(err);
69158
69521
  }
69159
69522
  }
69160
69523
  });
69161
69524
 
69162
69525
  // src/commands/winning-ads/follow-competitors.ts
69163
- import { defineCommand as defineCommand240 } from "citty";
69526
+ import { defineCommand as defineCommand247 } from "citty";
69164
69527
  var PLATFORMS2 = adLibraryPlatformSchema.options;
69165
69528
  var BATCH_TIMEOUT_MS = 3e5;
69166
69529
  function buildFollowBatchBody(input) {
@@ -69193,7 +69556,7 @@ registerSchema({
69193
69556
  }
69194
69557
  }
69195
69558
  });
69196
- var followCompetitorsCommand = defineCommand240({
69559
+ var followCompetitorsCommand = defineCommand247({
69197
69560
  meta: {
69198
69561
  name: "follow-competitors",
69199
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"'
@@ -69262,13 +69625,13 @@ var followCompetitorsCommand = defineCommand240({
69262
69625
  }
69263
69626
  writeJson({ ok: true, data, hints: hints2.length > 0 ? hints2 : void 0 });
69264
69627
  } catch (err) {
69265
- reportError2(err);
69628
+ reportError3(err);
69266
69629
  }
69267
69630
  }
69268
69631
  });
69269
69632
 
69270
69633
  // src/commands/winning-ads/following.ts
69271
- import { defineCommand as defineCommand241 } from "citty";
69634
+ import { defineCommand as defineCommand248 } from "citty";
69272
69635
  registerSchema({
69273
69636
  command: "winning-ads.following",
69274
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.",
@@ -69322,7 +69685,7 @@ function followingNormalizer(record, full) {
69322
69685
  platforms: Array.isArray(record.platforms) ? record.platforms : []
69323
69686
  };
69324
69687
  }
69325
- var followingCommand = defineCommand241({
69688
+ var followingCommand = defineCommand248({
69326
69689
  meta: {
69327
69690
  name: "following",
69328
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"
@@ -69352,13 +69715,13 @@ var followingCommand = defineCommand241({
69352
69715
  followingNormalizer
69353
69716
  );
69354
69717
  } catch (err) {
69355
- reportError2(err);
69718
+ reportError3(err);
69356
69719
  }
69357
69720
  }
69358
69721
  });
69359
69722
 
69360
69723
  // src/commands/winning-ads/patterns.ts
69361
- import { defineCommand as defineCommand242 } from "citty";
69724
+ import { defineCommand as defineCommand249 } from "citty";
69362
69725
  registerSchema({
69363
69726
  command: "winning-ads.patterns",
69364
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.",
@@ -69397,7 +69760,7 @@ function discriminatorRow(record) {
69397
69760
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
69398
69761
  };
69399
69762
  }
69400
- var patternsCommand = defineCommand242({
69763
+ var patternsCommand = defineCommand249({
69401
69764
  meta: {
69402
69765
  name: "patterns",
69403
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"
@@ -69447,13 +69810,13 @@ var patternsCommand = defineCommand242({
69447
69810
  (record) => discriminatorRow(record)
69448
69811
  );
69449
69812
  } catch (err) {
69450
- reportError2(err);
69813
+ reportError3(err);
69451
69814
  }
69452
69815
  }
69453
69816
  });
69454
69817
 
69455
69818
  // src/commands/winning-ads/search.ts
69456
- import { defineCommand as defineCommand243 } from "citty";
69819
+ import { defineCommand as defineCommand250 } from "citty";
69457
69820
  registerSchema({
69458
69821
  command: "winning-ads.search",
69459
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.",
@@ -69589,9 +69952,9 @@ function reportSearchError(err) {
69589
69952
  writeJson({ ok: false, error: { code: err.code, message: err.message, fix } });
69590
69953
  process.exit(1);
69591
69954
  }
69592
- reportError2(err);
69955
+ reportError3(err);
69593
69956
  }
69594
- var searchCommand5 = defineCommand243({
69957
+ var searchCommand5 = defineCommand250({
69595
69958
  meta: {
69596
69959
  name: "search",
69597
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"
@@ -69711,7 +70074,7 @@ var searchCommand5 = defineCommand243({
69711
70074
  });
69712
70075
 
69713
70076
  // src/commands/winning-ads/seeds.ts
69714
- import { defineCommand as defineCommand244 } from "citty";
70077
+ import { defineCommand as defineCommand251 } from "citty";
69715
70078
  function leanRow(r) {
69716
70079
  return {
69717
70080
  key: r.key,
@@ -69739,7 +70102,7 @@ function makeSeedCommand(opts) {
69739
70102
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
69740
70103
  }
69741
70104
  });
69742
- return defineCommand244({
70105
+ return defineCommand251({
69743
70106
  meta: { name: opts.name, description: opts.description },
69744
70107
  args: {
69745
70108
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -69766,7 +70129,7 @@ function makeSeedCommand(opts) {
69766
70129
  }
69767
70130
  writeOutput({ ok: true, data: projected }, output);
69768
70131
  } catch (err) {
69769
- reportError2(err);
70132
+ reportError3(err);
69770
70133
  }
69771
70134
  }
69772
70135
  });
@@ -69788,7 +70151,7 @@ var formatsCommand = makeSeedCommand({
69788
70151
  });
69789
70152
 
69790
70153
  // src/commands/winning-ads/unfollow.ts
69791
- import { defineCommand as defineCommand245 } from "citty";
70154
+ import { defineCommand as defineCommand252 } from "citty";
69792
70155
  registerSchema({
69793
70156
  command: "winning-ads.unfollow",
69794
70157
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -69796,7 +70159,7 @@ registerSchema({
69796
70159
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
69797
70160
  }
69798
70161
  });
69799
- var unfollowCommand = defineCommand245({
70162
+ var unfollowCommand = defineCommand252({
69800
70163
  meta: {
69801
70164
  name: "unfollow",
69802
70165
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -69811,13 +70174,13 @@ var unfollowCommand = defineCommand245({
69811
70174
  });
69812
70175
  writeJson({ ok: true, data });
69813
70176
  } catch (err) {
69814
- reportError2(err);
70177
+ reportError3(err);
69815
70178
  }
69816
70179
  }
69817
70180
  });
69818
70181
 
69819
70182
  // src/commands/winning-ads/winners.ts
69820
- import { defineCommand as defineCommand246 } from "citty";
70183
+ import { defineCommand as defineCommand253 } from "citty";
69821
70184
  registerSchema({
69822
70185
  command: "winning-ads.winners",
69823
70186
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -69827,7 +70190,7 @@ registerSchema({
69827
70190
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
69828
70191
  }
69829
70192
  });
69830
- var winnersCommand = defineCommand246({
70193
+ var winnersCommand = defineCommand253({
69831
70194
  meta: {
69832
70195
  name: "winners",
69833
70196
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -69871,13 +70234,13 @@ var winnersCommand = defineCommand246({
69871
70234
  winningAdNormalizer
69872
70235
  );
69873
70236
  } catch (err) {
69874
- reportError2(err);
70237
+ reportError3(err);
69875
70238
  }
69876
70239
  }
69877
70240
  });
69878
70241
 
69879
70242
  // src/commands/winning-ads/index.ts
69880
- var winningAdsCommand = defineCommand247({
70243
+ var winningAdsCommand = defineCommand254({
69881
70244
  meta: {
69882
70245
  name: "winning-ads",
69883
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.
@@ -69949,7 +70312,7 @@ function getCliVersion() {
69949
70312
  }
69950
70313
 
69951
70314
  // src/cli.ts
69952
- var main = defineCommand248({
70315
+ var main = defineCommand255({
69953
70316
  meta: {
69954
70317
  name: "baker",
69955
70318
  version: getCliVersion(),
@@ -69989,6 +70352,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
69989
70352
  history: historyCommand2,
69990
70353
  hubspot: hubspotCommand,
69991
70354
  "winning-ads": winningAdsCommand,
70355
+ "media-sources": mediaSourcesCommand,
69992
70356
  mcp: mcpCommand,
69993
70357
  schema: schemaCommand2
69994
70358
  }