@koda-sl/baker-cli 0.369.0 → 0.371.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -55,6 +55,7 @@ import {
55
55
  promptMaxLength,
56
56
  requireCredentialsFromEnv,
57
57
  resolveConcurrency,
58
+ runFailureCode,
58
59
  runYtDlp,
59
60
  sha256Hex,
60
61
  spineInputFlags,
@@ -69,7 +70,7 @@ import {
69
70
  validateCanvasDeep,
70
71
  withoutCastName,
71
72
  ytDlpBlockSignal
72
- } from "./chunk-AI4HWQVN.js";
73
+ } from "./chunk-3NIBJ3GL.js";
73
74
  import {
74
75
  csvOrJson,
75
76
  daysAgoIso,
@@ -12051,6 +12052,41 @@ function adCopyHints(payload) {
12051
12052
  return tells.slice(0, MAX_HINTS).map(hintFor);
12052
12053
  }
12053
12054
 
12055
+ // src/commands/ads/google/copy-list-usage.ts
12056
+ 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"'`;
12057
+ var COPY_LIST_FLAG = {
12058
+ headlines: "--headlines",
12059
+ descriptions: "--descriptions",
12060
+ longHeadlines: "--long-headlines"
12061
+ };
12062
+ function valueAt(root, path48) {
12063
+ let node = root;
12064
+ for (const key of path48) {
12065
+ if (node === null || typeof node !== "object") return void 0;
12066
+ node = node[key];
12067
+ }
12068
+ return node;
12069
+ }
12070
+ function countRefusal(op, issue) {
12071
+ if (issue.code !== "too_big" && issue.code !== "too_small" || issue.origin !== "array") return void 0;
12072
+ const flag = COPY_LIST_FLAG[String(issue.path.at(-1))];
12073
+ if (!flag) return void 0;
12074
+ const value = valueAt(op, issue.path);
12075
+ const count2 = Array.isArray(value) ? value.length : void 0;
12076
+ return issue.code === "too_big" ? { flag, count: count2, bound: `at most ${issue.maximum}`, tooMany: true } : { flag, count: count2, bound: `at least ${issue.minimum}`, tooMany: false };
12077
+ }
12078
+ function copyListCountFix(op, issues) {
12079
+ const refusals = issues.map((issue) => countRefusal(op, issue));
12080
+ if (refusals.length === 0 || refusals.some((refusal) => refusal === void 0)) return void 0;
12081
+ const counted = refusals.filter((refusal) => refusal !== void 0);
12082
+ const limits = counted.map(({ flag, count: count2, bound }) => `${flag} has ${count2 ?? "the wrong number of"} items; this ad takes ${bound}.`).join(" ");
12083
+ const commaCause = counted.some((refusal) => refusal.tooMany) ? ` If you meant fewer items, the copy was cut at its commas: a flag given once is read as a comma-separated list, so an item with a comma in it ("Fast, free quotes", a decimal like "4,5/5") becomes two. To keep a comma, ${AD_COPY_LIST_USAGE} \u2014 or put the content in --file as JSON.` : "";
12084
+ return {
12085
+ action: "fix_copy_list_count",
12086
+ explanation: `${limits}${commaCause} ${commaCause ? "Otherwise add" : "Add"} or drop items to fit the limit, then stage again; the field names are right.`
12087
+ };
12088
+ }
12089
+
12054
12090
  // src/commands/ads/google/draft-status.ts
12055
12091
  var OPERATION_LABELS = {
12056
12092
  create: "Creating",
@@ -12371,7 +12407,7 @@ async function stageGoogleOp(raw, hints2) {
12371
12407
  code: "VALIDATION_ERROR",
12372
12408
  message: fields.map((f) => f.path ? `${f.path}: ${f.message}` : f.message).join("; "),
12373
12409
  fields,
12374
- fix: capabilityFix("google-ads")
12410
+ fix: copyListCountFix(raw, preflight.error.issues) ?? capabilityFix("google-ads")
12375
12411
  }
12376
12412
  });
12377
12413
  process.exit(1);
@@ -12407,7 +12443,7 @@ async function stageGoogleOps(rawOps, hints2) {
12407
12443
  code: "VALIDATION_ERROR",
12408
12444
  message: fields.map((f) => `${f.path}: ${f.message}`).join("; "),
12409
12445
  fields,
12410
- fix: capabilityFix("google-ads")
12446
+ fix: copyListCountFix(raw, preflight.error.issues) ?? capabilityFix("google-ads")
12411
12447
  }
12412
12448
  });
12413
12449
  process.exit(1);
@@ -17858,7 +17894,6 @@ import { defineCommand as defineCommand31 } from "citty";
17858
17894
 
17859
17895
  // src/commands/ads/google/ad-copy-flags.ts
17860
17896
  var AD_COPY_LIST_FLAGS = ["headlines", "descriptions", "long-headlines"];
17861
- 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"'`;
17862
17897
  function skipBlanks(value, at) {
17863
17898
  let i = at;
17864
17899
  while (value[i] === " " || value[i] === " ") i++;
@@ -42434,14 +42469,14 @@ ${describeRewrites(healed.rewrites)}
42434
42469
  const failures = e.failures.map((f) => ({ node_id: f.nodeId, message: describeFailureReason(f.reason) }));
42435
42470
  if (poster) await poster.flush(failedPayload(e.message));
42436
42471
  process.stderr.write(
42437
- `${JSON.stringify({ ok: false, error: { code: "runtime", message: e.message, failures } }, null, 2)}
42472
+ `${JSON.stringify({ ok: false, error: { code: runFailureCode(e), message: e.message, failures } }, null, 2)}
42438
42473
  `
42439
42474
  );
42440
42475
  process.exit(1);
42441
42476
  }
42442
42477
  const msg = e instanceof Error ? e.message : String(e);
42443
42478
  if (poster) await poster.flush(failedPayload(msg));
42444
- process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "runtime", message: msg } }, null, 2)}
42479
+ process.stderr.write(`${JSON.stringify({ ok: false, error: { code: runFailureCode(e), message: msg } }, null, 2)}
42445
42480
  `);
42446
42481
  process.exit(1);
42447
42482
  }
@@ -43278,6 +43313,15 @@ function resolvePlacements(placementsFlag, definitionPlatform) {
43278
43313
  message: `--placements "${flag}" is not a valid placement set. Use one of: ${AD_FORMAT_PLATFORMS.join(", ")}, or "none" for a single base ad.`
43279
43314
  };
43280
43315
  }
43316
+ async function validateScaffoldedStaticAd(canvas, canvasDir) {
43317
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(canvas, canvasDir), defaultRegistry());
43318
+ if (validation.ok) return { blocking: [], hints: [], estimatedCredits: validation.estimatedCredits };
43319
+ const overCap = validation.issues.filter((i) => i.code === "PROMPT_OVER_MODEL_LIMIT");
43320
+ return {
43321
+ blocking: validation.issues.filter((i) => i.code !== "PROMPT_OVER_MODEL_LIMIT"),
43322
+ hints: overCap.map((i) => `${i.code}: ${i.message} Trim prompt.json, then \`baker canvas validate\`.`)
43323
+ };
43324
+ }
43281
43325
  function regionCount(layout) {
43282
43326
  const regions = layout?.regions;
43283
43327
  return Array.isArray(regions) ? regions.length : 0;
@@ -43469,10 +43513,10 @@ var scaffoldStaticAdCommand = defineCommand114({
43469
43513
  } catch (e) {
43470
43514
  return fail3("scaffold", e instanceof Error ? e.message : String(e));
43471
43515
  }
43472
- const validation = await validateCanvasDeep(canvas, defaultRegistry());
43473
- if (!validation.ok) {
43516
+ const validation = await validateScaffoldedStaticAd(canvas, path21.dirname(outPath));
43517
+ if (validation.blocking.length > 0) {
43474
43518
  process.stderr.write(
43475
- `${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
43519
+ `${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.blocking } }, null, 2)}
43476
43520
  `
43477
43521
  );
43478
43522
  process.exit(2);
@@ -43506,6 +43550,12 @@ var scaffoldStaticAdCommand = defineCommand114({
43506
43550
  sourceSha: await computeSourceSha(outPath)
43507
43551
  });
43508
43552
  }
43553
+ const hints2 = [
43554
+ ...uncast.length ? [
43555
+ `NOT CAST: ${uncast.map((h) => `\`${h}\``).join(", ")} \u2014 the element selection found fewer people than avatars passed, so ${uncast.length === 1 ? "this avatar plays" : "these avatars play"} nobody in this ad. Tell the user who is cast and who is not.`
43556
+ ] : [],
43557
+ ...validation.hints
43558
+ ];
43509
43559
  process.stdout.write(
43510
43560
  `${JSON.stringify(
43511
43561
  {
@@ -43515,11 +43565,7 @@ var scaffoldStaticAdCommand = defineCommand114({
43515
43565
  definition_path: definitionPath ?? void 0,
43516
43566
  source_reference: durableSourceUrl ?? void 0,
43517
43567
  output: canvas.output,
43518
- ...uncast.length ? {
43519
- hints: [
43520
- `NOT CAST: ${uncast.map((h) => `\`${h}\``).join(", ")} \u2014 the element selection found fewer people than avatars passed, so ${uncast.length === 1 ? "this avatar plays" : "these avatars play"} nobody in this ad. Tell the user who is cast and who is not.`
43521
- ]
43522
- } : {},
43568
+ ...hints2.length ? { hints: hints2 } : {},
43523
43569
  models: { describe: describeModel, select: selectModel, layout: layoutModel, gen: opts.genModel },
43524
43570
  aspect_ratio: report.aspect_ratio,
43525
43571
  placements: report.placements,
@@ -45804,7 +45850,7 @@ import { defineCommand as defineCommand118 } from "citty";
45804
45850
  var validateCommand = defineCommand118({
45805
45851
  meta: {
45806
45852
  name: "validate",
45807
- description: "Validate a canvas JSON file (no execution). Includes a per-node cost preview and runs each node's deep validators (composition meta checks for hyperframe_render/_snapshot)."
45853
+ description: "Validate a canvas JSON file (no execution). Includes a per-node cost preview and runs each node's deep validators (composition meta checks for hyperframe_render/_snapshot), and measures each generator prompt with its {{slots}} filled against the model's character cap (PROMPT_OVER_MODEL_LIMIT)."
45808
45854
  },
45809
45855
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
45810
45856
  async run({ args: args5 }) {