@koda-sl/baker-cli 0.423.0 → 0.424.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,13 +2,13 @@ import {
2
2
  handleConnectionError,
3
3
  needsConnectionFix,
4
4
  writeAdsJson
5
- } from "./chunk-3X2EMLBQ.js";
5
+ } from "./chunk-RFZYRG2Q.js";
6
6
  import {
7
7
  ApiError
8
- } from "./chunk-G5L5FX2B.js";
8
+ } from "./chunk-Q6F5UNOU.js";
9
9
  import {
10
10
  getEnv
11
- } from "./chunk-5JVAP5DC.js";
11
+ } from "./chunk-FQOKJYKN.js";
12
12
 
13
13
  // src/commands/ads/meta/shared.ts
14
14
  var DAY_MS = 864e5;
@@ -133,4 +133,4 @@ export {
133
133
  resolveEffectiveStatus,
134
134
  statusFilterHints
135
135
  };
136
- //# sourceMappingURL=chunk-XNFV6BQU.js.map
136
+ //# sourceMappingURL=chunk-PHLFNYGQ.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  debugLogHttp,
3
3
  readBodyForLog
4
- } from "./chunk-S36N4SOX.js";
4
+ } from "./chunk-GCTR4MDE.js";
5
5
  import {
6
6
  getEnv
7
- } from "./chunk-5JVAP5DC.js";
7
+ } from "./chunk-FQOKJYKN.js";
8
8
 
9
9
  // src/client.ts
10
10
  var MAX_RATE_LIMIT_RETRIES = 3;
@@ -208,4 +208,4 @@ export {
208
208
  apiGet,
209
209
  apiPost
210
210
  };
211
- //# sourceMappingURL=chunk-G5L5FX2B.js.map
211
+ //# sourceMappingURL=chunk-Q6F5UNOU.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ApiError,
3
3
  apiGet
4
- } from "./chunk-G5L5FX2B.js";
4
+ } from "./chunk-Q6F5UNOU.js";
5
5
  import {
6
6
  getEnv
7
- } from "./chunk-5JVAP5DC.js";
7
+ } from "./chunk-FQOKJYKN.js";
8
8
 
9
9
  // src/error-handler.ts
10
10
  var REQUEST_CONNECTION_PLATFORM = {
@@ -298,4 +298,4 @@ export {
298
298
  writeAdsOutput,
299
299
  resolveCustomerId
300
300
  };
301
- //# sourceMappingURL=chunk-3X2EMLBQ.js.map
301
+ //# sourceMappingURL=chunk-RFZYRG2Q.js.map
package/dist/cli.js CHANGED
@@ -249,6 +249,7 @@ import {
249
249
  spellNumber,
250
250
  splitScriptFor,
251
251
  spokenWordsIn,
252
+ staticFormatPatternSchema,
252
253
  staticMasterFormat,
253
254
  staticRenderNodes,
254
255
  staticVerdict,
@@ -284,7 +285,7 @@ import {
284
285
  wordCount,
285
286
  xDraftOpInputSchema,
286
287
  ytDlpBlockSignal
287
- } from "./chunk-SWYBHYB5.js";
288
+ } from "./chunk-IR4ZOEZV.js";
288
289
  import {
289
290
  NAME_CONTAINS_ARG,
290
291
  csvOrJson,
@@ -295,7 +296,7 @@ import {
295
296
  resolveEffectiveStatus,
296
297
  statusFilterHints,
297
298
  todayIso
298
- } from "./chunk-XNFV6BQU.js";
299
+ } from "./chunk-PHLFNYGQ.js";
299
300
  import {
300
301
  buildQueryCacheKey,
301
302
  cacheGet,
@@ -311,32 +312,35 @@ import {
311
312
  writeAdsJson,
312
313
  writeAdsOutput,
313
314
  writeJsonEnvelope
314
- } from "./chunk-3X2EMLBQ.js";
315
+ } from "./chunk-RFZYRG2Q.js";
315
316
  import {
316
317
  ApiError,
317
318
  apiGet,
318
319
  apiGetWithHeaders,
319
320
  apiPost,
320
321
  validateConvexId
321
- } from "./chunk-G5L5FX2B.js";
322
+ } from "./chunk-Q6F5UNOU.js";
322
323
  import {
323
324
  installExitRecord,
324
325
  installStreamTaps,
325
326
  logInvocation
326
- } from "./chunk-S36N4SOX.js";
327
+ } from "./chunk-GCTR4MDE.js";
327
328
  import {
328
329
  __commonJS,
329
330
  __esm,
330
331
  __glob,
331
332
  __toESM,
332
333
  adContentNormalizer,
334
+ applyFieldMask,
333
335
  captureBudgetMs,
334
336
  captureProxyCredentials,
335
337
  captureVimeoCookie,
336
338
  commandPathOf,
337
339
  compactImage,
338
340
  compactVideo,
341
+ compactWinningAd,
339
342
  findSchema,
343
+ fullWinningAd,
340
344
  getEnv,
341
345
  installFieldMask,
342
346
  isProxyFailure,
@@ -358,7 +362,7 @@ import {
358
362
  winningAdNormalizer,
359
363
  writeJson,
360
364
  writeOutput
361
- } from "./chunk-5JVAP5DC.js";
365
+ } from "./chunk-FQOKJYKN.js";
362
366
 
363
367
  // ../../node_modules/.pnpm/i18n-iso-countries@7.14.0/node_modules/i18n-iso-countries/langs/af.json
364
368
  var require_af = __commonJS({
@@ -45234,10 +45238,10 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
45234
45238
  }
45235
45239
  },
45236
45240
  run: async ({ args: args8 }) => {
45237
- const { apiPost: apiPost2 } = await import("./client-PIKJZFRA.js");
45238
- const { requireChatId: requireChatId2 } = await import("./env-6UAIAY4M.js");
45239
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-CFDBWWYJ.js");
45240
- const { handleMetaError: handleMetaError2 } = await import("./shared-3O5RKQYZ.js");
45241
+ const { apiPost: apiPost2 } = await import("./client-GS7T7ETG.js");
45242
+ const { requireChatId: requireChatId2 } = await import("./env-SL22S6CM.js");
45243
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-OFDKEPPJ.js");
45244
+ const { handleMetaError: handleMetaError2 } = await import("./shared-XR54KFMH.js");
45241
45245
  try {
45242
45246
  const accountId = bareAccountId2(args8);
45243
45247
  const urlTags = typeof args8["url-tags"] === "string" ? args8["url-tags"] : void 0;
@@ -45388,10 +45392,10 @@ function duplicateCommand2(entity, label) {
45388
45392
  replace: { type: "boolean", description: "Pause the original once the copy publishes" }
45389
45393
  },
45390
45394
  run: async ({ args: args8 }) => {
45391
- const { apiPost: apiPost2 } = await import("./client-PIKJZFRA.js");
45392
- const { requireChatId: requireChatId2 } = await import("./env-6UAIAY4M.js");
45393
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-CFDBWWYJ.js");
45394
- const { handleMetaError: handleMetaError2 } = await import("./shared-3O5RKQYZ.js");
45395
+ const { apiPost: apiPost2 } = await import("./client-GS7T7ETG.js");
45396
+ const { requireChatId: requireChatId2 } = await import("./env-SL22S6CM.js");
45397
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-OFDKEPPJ.js");
45398
+ const { handleMetaError: handleMetaError2 } = await import("./shared-XR54KFMH.js");
45395
45399
  try {
45396
45400
  const accountId = bareAccountId2(args8);
45397
45401
  const chatId = requireChatId2();
@@ -94660,9 +94664,8 @@ registerSchema({
94660
94664
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false },
94661
94665
  "per-advertiser": {
94662
94666
  type: "number",
94663
- description: "Max winners kept per advertiser 0-50 (0 = no cap, default 5)",
94664
- required: false,
94665
- default: 5
94667
+ description: "Spread per advertiser by default: each brand's best 3 first, the rest after, none dropped. Set 1-50 to keep at most N per brand, 0 for rank order",
94668
+ required: false
94666
94669
  },
94667
94670
  limit: {
94668
94671
  type: "number",
@@ -94677,7 +94680,7 @@ registerSchema({
94677
94680
  var feedCommand2 = defineCommand295({
94678
94681
  meta: {
94679
94682
  name: "feed",
94680
- description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
94683
+ description: "Winners across every brand you follow, each brand's best first. Example: baker winning-ads feed --output md"
94681
94684
  },
94682
94685
  args: {
94683
94686
  advertiser: {
@@ -94688,7 +94691,7 @@ var feedCommand2 = defineCommand295({
94688
94691
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false },
94689
94692
  "per-advertiser": {
94690
94693
  type: "string",
94691
- description: "Max winners per advertiser 0-50 (0 = no cap, default 5)",
94694
+ description: "Spread per advertiser by default: each brand's best 3 first, the rest after, none dropped. Set 1-50 to keep at most N per brand, 0 for rank order",
94692
94695
  required: false
94693
94696
  },
94694
94697
  limit: { type: "string", description: "Total winners 1-200 (default 40)", required: false },
@@ -95089,6 +95092,8 @@ var refreshCommand = defineCommand299({
95089
95092
 
95090
95093
  // src/commands/winning-ads/search.ts
95091
95094
  import { defineCommand as defineCommand300 } from "citty";
95095
+ var FORMAT_PATTERN_DESCRIPTION = `Comma list of static containers, the best proven ads in them from any category (\`winning-ads formats\` ranks them for a segment): ${staticFormatPatternSchema.options.join(", ")}`;
95096
+ var LANGUAGE_DESCRIPTION = "Comma list of ISO 639-1 codes (es,en) for the language the ad's copy and voiceover are in; where it ran is --country";
95092
95097
  registerSchema({
95093
95098
  command: "winning-ads.search",
95094
95099
  description: "Search the ad-dna corpus of scored winning ads, the company's followed brands first. Returns a lean shortlist (advertiser, summary, scores, days_active/active/winner_category as proof, media_url, visual/container/mechanism, and who the ad was made for: audience_temperature/funnel_stage/advertiser_size/presumes_brand_knowledge, when known) to pick a reference to reproduce. Each search is a paid reranker + judge call: one strong search, at most one adjusted retry. A value outside a closed list (--platform, --format, --winner-category, --awareness, --audience-temperature, --funnel-stage, --advertiser-size, --format-pattern, --sort) is refused with error.fix.validValues.",
@@ -95108,9 +95113,8 @@ registerSchema({
95108
95113
  limit: { type: "number", description: "Max results 1-100 (default 10)", required: false, default: 10 },
95109
95114
  "max-per-advertiser": {
95110
95115
  type: "number",
95111
- description: "Cap results per advertiser 0-50 (default 3; 0 = no cap), also when the company follows one brand",
95112
- required: false,
95113
- default: 3
95116
+ description: "Spread per advertiser by default: each brand's best 3 first, the rest after the other brands, none dropped. Set 1-50 to drop past N (1 = one reference per brand), 0 for rank order",
95117
+ required: false
95114
95118
  },
95115
95119
  "min-relevance": { type: "number", description: "Relevance floor 0-1; trims weak matches", required: false },
95116
95120
  sort: {
@@ -95150,10 +95154,10 @@ registerSchema({
95150
95154
  },
95151
95155
  country: {
95152
95156
  type: "string",
95153
- description: "Comma list of ISO country codes (US,GB) \u2014 an unknown code is refused with the codes that have ads",
95157
+ description: "Comma list of ISO country codes (US,GB): ads known to run there, plus ads with no country data (every Meta ad) in that country's main language. An unknown code is refused with the codes that have ads",
95154
95158
  required: false
95155
95159
  },
95156
- language: { type: "string", description: "Comma list of language codes", required: false },
95160
+ language: { type: "string", description: LANGUAGE_DESCRIPTION, required: false },
95157
95161
  "advertiser-id": {
95158
95162
  type: "string",
95159
95163
  description: "Restrict to these advertiser ids (browse one brand's winners)",
@@ -95181,7 +95185,7 @@ registerSchema({
95181
95185
  },
95182
95186
  "format-pattern": {
95183
95187
  type: "string",
95184
- description: "Comma list of static containers (from `winning-ads formats`, e.g. comparison_table,before_after_split) \u2014 the best proven ads in that container, whatever they sell",
95188
+ description: FORMAT_PATTERN_DESCRIPTION,
95185
95189
  required: false
95186
95190
  },
95187
95191
  diverse: {
@@ -95236,7 +95240,7 @@ function buildSearchBody2(args8) {
95236
95240
  hardFilters.presumes_brand_knowledge = false;
95237
95241
  }
95238
95242
  setList(hardFilters, "country", args8.country);
95239
- setList(hardFilters, "language", args8.language);
95243
+ setList(hardFilters, "language", args8.language?.toLowerCase());
95240
95244
  setList(hardFilters, "advertiser_ids", args8.advertiserId);
95241
95245
  setList(hardFilters, "exclude_advertiser_ids", args8.excludeAdvertiser);
95242
95246
  setList(hardFilters, "hook_archetype_seeds", args8.hookArchetype);
@@ -95258,6 +95262,10 @@ function searchHints2(data) {
95258
95262
  hints2.push(
95259
95263
  "No followed brand has an ad on this brief. Re-run with --scope followed-first to top up from the whole library."
95260
95264
  );
95265
+ } else if ((data.results?.length ?? 0) === 0) {
95266
+ hints2.push(
95267
+ "No reference matched the brief and every filter. Search once more with ONE change: drop the narrowest filter (--country, --audience-temperature or one --awareness stage) or shorten the brief to the product and who it is for. For a container your concept needs, --format-pattern <container> with no query returns the best proven ads in it from any category."
95268
+ );
95261
95269
  }
95262
95270
  const adapted = (data.results ?? []).filter(
95263
95271
  (r) => r?.baker_uses?.ours === true
@@ -95287,8 +95295,17 @@ function unknownCountryFix(message) {
95287
95295
  validCountries
95288
95296
  };
95289
95297
  }
95298
+ function searchErrorFix(code, message) {
95299
+ if (code === "UNAVAILABLE") {
95300
+ return {
95301
+ action: "retry_narrower_once",
95302
+ explanation: "The library did not answer this search in time. Retry once with a shorter brief, a lower --limit or fewer filters. If it fails again, continue without a reference and say so; do not stop the creative work."
95303
+ };
95304
+ }
95305
+ return unknownCountryFix(message);
95306
+ }
95290
95307
  function reportSearchError(err) {
95291
- const fix = err instanceof ApiError ? unknownCountryFix(err.message) : null;
95308
+ const fix = err instanceof ApiError ? searchErrorFix(err.code, err.message) : null;
95292
95309
  if (err instanceof ApiError && fix) {
95293
95310
  writeJson({ ok: false, error: { code: err.code, message: err.message, fix } });
95294
95311
  process.exit(1);
@@ -95364,12 +95381,38 @@ function searchRejection(args8, body) {
95364
95381
  }
95365
95382
  return schemaRejection(body);
95366
95383
  }
95384
+ var EVERY_SIGNAL = {
95385
+ dna: {
95386
+ visual: "-",
95387
+ container: "-",
95388
+ mechanism: "-",
95389
+ audience_temperature: "-",
95390
+ funnel_stage: "-",
95391
+ advertiser_size: "-",
95392
+ presumes_brand_knowledge: true,
95393
+ brand_knowledge_reason: "-"
95394
+ },
95395
+ baker_uses: { companies: 1, ours: true, approved: 1, rejected: 1 },
95396
+ source: "followed"
95397
+ };
95398
+ var COMPACT_FIELDS = new Set(Object.keys(compactWinningAd(EVERY_SIGNAL)));
95399
+ var ALL_FIELDS = Object.keys(fullWinningAd(EVERY_SIGNAL));
95400
+ function searchFields(requested, full) {
95401
+ if (!requested) return { fields: void 0, full };
95402
+ const fields = requested.map((f) => f.trim()).filter(Boolean);
95403
+ const invalid = fields.filter((f) => !ALL_FIELDS.includes(f));
95404
+ if (invalid.length > 0) return { invalid, valid: ALL_FIELDS };
95405
+ return { fields, full: full || fields.some((f) => !COMPACT_FIELDS.has(f)) };
95406
+ }
95367
95407
  function writeSearchResult(data, opts) {
95368
95408
  const rawResults = Array.isArray(data?.results) ? data.results : [];
95369
95409
  const hints2 = searchHints2(data ?? {});
95370
95410
  const withHints2 = hints2.length > 0 ? { hints: hints2 } : {};
95371
95411
  if (opts.output === "json") {
95372
- const results = rawResults.map((r) => winningAdNormalizer(r, opts.full));
95412
+ const results = rawResults.map((r) => {
95413
+ const row2 = winningAdNormalizer(r, opts.full);
95414
+ return opts.fields ? applyFieldMask(row2, opts.fields) : row2;
95415
+ });
95373
95416
  writeJson({
95374
95417
  ok: true,
95375
95418
  data: {
@@ -95409,7 +95452,7 @@ var searchCommand6 = defineCommand300({
95409
95452
  },
95410
95453
  "max-per-advertiser": {
95411
95454
  type: "string",
95412
- description: "Cap results per advertiser 0-50 (default 3; 0 = no cap), also when the company follows one brand",
95455
+ description: "Spread per advertiser by default: each brand's best 3 first, the rest after the other brands, none dropped. Set 1-50 to drop past N (1 = one reference per brand), 0 for rank order",
95413
95456
  required: false
95414
95457
  },
95415
95458
  "min-relevance": { type: "string", description: "Relevance floor 0-1", required: false },
@@ -95445,10 +95488,10 @@ var searchCommand6 = defineCommand300({
95445
95488
  },
95446
95489
  country: {
95447
95490
  type: "string",
95448
- description: "Comma list of ISO country codes (US,GB) \u2014 an unknown code is refused with the codes that have ads",
95491
+ description: "Comma list of ISO country codes (US,GB): ads known to run there, plus ads with no country data (every Meta ad) in that country's main language. An unknown code is refused with the codes that have ads",
95449
95492
  required: false
95450
95493
  },
95451
- language: { type: "string", description: "Comma list of language codes", required: false },
95494
+ language: { type: "string", description: LANGUAGE_DESCRIPTION, required: false },
95452
95495
  "advertiser-id": { type: "string", description: "Restrict to these advertiser ids", required: false },
95453
95496
  "exclude-advertiser": {
95454
95497
  type: "string",
@@ -95464,7 +95507,7 @@ var searchCommand6 = defineCommand300({
95464
95507
  },
95465
95508
  "format-pattern": {
95466
95509
  type: "string",
95467
- description: "Comma list of static containers (from `winning-ads formats`, e.g. comparison_table) \u2014 the best proven ads in it from any category",
95510
+ description: FORMAT_PATTERN_DESCRIPTION,
95468
95511
  required: false
95469
95512
  },
95470
95513
  diverse: {
@@ -95510,6 +95553,27 @@ var searchCommand6 = defineCommand300({
95510
95553
  formatPattern: args8["format-pattern"],
95511
95554
  diverse: args8.diverse
95512
95555
  };
95556
+ const fieldPlan = searchFields(
95557
+ args8.fields ? args8.fields.split(",") : void 0,
95558
+ args8.full
95559
+ );
95560
+ if ("invalid" in fieldPlan) {
95561
+ writeJson({
95562
+ ok: false,
95563
+ error: {
95564
+ code: "VALIDATION_ERROR",
95565
+ message: `Unknown --fields: ${fieldPlan.invalid.join(", ")}`,
95566
+ fix: {
95567
+ action: "retry_with_valid_values",
95568
+ explanation: "Pass only fields a search row carries.",
95569
+ flag: "--fields",
95570
+ invalidValues: fieldPlan.invalid,
95571
+ validValues: fieldPlan.valid
95572
+ }
95573
+ }
95574
+ });
95575
+ process.exit(1);
95576
+ }
95513
95577
  const body = buildSearchBody2(searchArgs);
95514
95578
  const rejection = searchRejection(searchArgs, body);
95515
95579
  if (rejection) {
@@ -95519,11 +95583,7 @@ var searchCommand6 = defineCommand300({
95519
95583
  const data = await apiPost("/api/ad-library/winners/search", body, {
95520
95584
  timeoutMs: SEARCH_TIMEOUT_MS
95521
95585
  });
95522
- writeSearchResult(data, {
95523
- output: args8.output || "json",
95524
- fields: args8.fields ? args8.fields.split(",") : void 0,
95525
- full: args8.full
95526
- });
95586
+ writeSearchResult(data, { output: args8.output || "json", ...fieldPlan });
95527
95587
  } catch (err) {
95528
95588
  reportSearchError(err);
95529
95589
  }
@@ -95724,7 +95784,7 @@ Examples:
95724
95784
  baker winning-ads advertisers "Acme" --output md
95725
95785
  baker winning-ads follow "deel.com" --platform meta
95726
95786
  baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"
95727
- baker winning-ads feed --per-advertiser 5 --output md
95787
+ baker winning-ads feed --output md
95728
95788
  baker winning-ads feed --advertiser adv_123,adv_456 --platform meta --output md
95729
95789
  baker winning-ads winners adv_123 --top 15 --output md
95730
95790
  baker winning-ads content adg_123 --full --output md