@koda-sl/baker-cli 0.404.0 → 0.405.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
@@ -98,7 +98,7 @@ import {
98
98
  withoutCastName,
99
99
  wordCount,
100
100
  ytDlpBlockSignal
101
- } from "./chunk-KE6GXLDE.js";
101
+ } from "./chunk-DGZ5DLXK.js";
102
102
  import {
103
103
  NAME_CONTAINS_ARG,
104
104
  csvOrJson,
@@ -71092,11 +71092,23 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
71092
71092
  import { mkdir as mkdir7, readFile as readFile20, rm as rm5, writeFile as writeFile12 } from "fs/promises";
71093
71093
  import path23 from "path";
71094
71094
  var RENDERS_PER_ROUND = 3;
71095
+ var REDRAWS_PER_ROUND = 4;
71096
+ var redrawsOf = (ledger) => ledger.redraws ?? 0;
71095
71097
  function claimRender(ledger, chatId) {
71096
71098
  const current = ledger && ledger.chatId === chatId ? ledger : { chatId, renders: 0 };
71097
- if (current.renders >= RENDERS_PER_ROUND) return { allowed: false, renders: current.renders, ledger: current };
71098
- const next = { chatId, renders: current.renders + 1 };
71099
- return { allowed: true, renders: next.renders, ledger: next };
71099
+ if (current.renders < RENDERS_PER_ROUND) {
71100
+ const next = { ...current, renders: current.renders + 1 };
71101
+ return { allowed: true, kind: "pictures", renders: next.renders, redraws: redrawsOf(next), ledger: next };
71102
+ }
71103
+ if (redrawsOf(current) < REDRAWS_PER_ROUND) {
71104
+ const next = { ...current, redraws: redrawsOf(current) + 1 };
71105
+ return { allowed: true, kind: "redraw", renders: next.renders, redraws: redrawsOf(next), ledger: next };
71106
+ }
71107
+ return { allowed: false, kind: "redraw", renders: current.renders, redraws: redrawsOf(current), ledger: current };
71108
+ }
71109
+ function settleRender(ledger, kind, painted) {
71110
+ if (kind !== "pictures" || painted || redrawsOf(ledger) >= REDRAWS_PER_ROUND || ledger.renders <= 0) return ledger;
71111
+ return { ...ledger, renders: ledger.renders - 1, redraws: redrawsOf(ledger) + 1 };
71100
71112
  }
71101
71113
  function ledgerFile(canvasRoot, slug) {
71102
71114
  return path23.join(canvasRoot, ".render-budget", `${slug}.json`);
@@ -71104,28 +71116,55 @@ function ledgerFile(canvasRoot, slug) {
71104
71116
  async function readLedger(canvasRoot, slug) {
71105
71117
  try {
71106
71118
  const raw = JSON.parse(await readFile20(ledgerFile(canvasRoot, slug), "utf8"));
71107
- return typeof raw.renders === "number" ? { chatId: raw.chatId ?? null, renders: raw.renders } : null;
71119
+ if (typeof raw.renders !== "number") return null;
71120
+ const inFlight = raw.inFlight && typeof raw.inFlight.runId === "string" && (raw.inFlight.kind === "pictures" || raw.inFlight.kind === "redraw") ? { inFlight: { runId: raw.inFlight.runId, kind: raw.inFlight.kind } } : {};
71121
+ return {
71122
+ chatId: raw.chatId ?? null,
71123
+ renders: raw.renders,
71124
+ ...typeof raw.redraws === "number" ? { redraws: raw.redraws } : {},
71125
+ ...inFlight
71126
+ };
71108
71127
  } catch {
71109
71128
  return null;
71110
71129
  }
71111
71130
  }
71112
- async function spendRender(canvasRoot, slug, chatId) {
71131
+ async function writeLedger(canvasRoot, slug, ledger) {
71132
+ try {
71133
+ await mkdir7(path23.dirname(ledgerFile(canvasRoot, slug)), { recursive: true });
71134
+ await writeFile12(ledgerFile(canvasRoot, slug), JSON.stringify(ledger));
71135
+ } catch {
71136
+ }
71137
+ }
71138
+ async function spendRender(canvasRoot, slug, chatId, runId) {
71113
71139
  const claim = claimRender(await readLedger(canvasRoot, slug), chatId);
71114
71140
  if (claim.allowed) {
71115
- try {
71116
- await mkdir7(path23.dirname(ledgerFile(canvasRoot, slug)), { recursive: true });
71117
- await writeFile12(ledgerFile(canvasRoot, slug), JSON.stringify(claim.ledger));
71118
- } catch {
71119
- }
71141
+ const { inFlight: _previous, ...ledger } = claim.ledger;
71142
+ await writeLedger(canvasRoot, slug, runId ? { ...ledger, inFlight: { runId, kind: claim.kind } } : ledger);
71120
71143
  }
71121
- return { allowed: claim.allowed, renders: claim.renders };
71144
+ return { allowed: claim.allowed, kind: claim.kind, renders: claim.renders, redraws: claim.redraws };
71122
71145
  }
71123
- async function refundRender(canvasRoot, slug, chatId) {
71146
+ async function resumedRender(canvasRoot, slug, chatId, runId) {
71124
71147
  const ledger = await readLedger(canvasRoot, slug);
71125
- if (!ledger || ledger.chatId !== chatId || ledger.renders <= 0) return;
71126
- await writeFile12(ledgerFile(canvasRoot, slug), JSON.stringify({ chatId, renders: ledger.renders - 1 })).catch(
71127
- () => void 0
71128
- );
71148
+ if (!ledger || ledger.chatId !== chatId || ledger.inFlight?.runId !== runId) return null;
71149
+ return { allowed: true, kind: ledger.inFlight.kind, renders: ledger.renders, redraws: redrawsOf(ledger) };
71150
+ }
71151
+ function landed(ledger) {
71152
+ const { inFlight: _done, ...rest } = ledger;
71153
+ return rest;
71154
+ }
71155
+ async function settleSpentRender(canvasRoot, slug, chatId, kind, painted) {
71156
+ const ledger = await readLedger(canvasRoot, slug);
71157
+ if (!ledger || ledger.chatId !== chatId) return null;
71158
+ const next = landed(settleRender(ledger, kind, painted));
71159
+ await writeLedger(canvasRoot, slug, next);
71160
+ return { renders: next.renders, redraws: redrawsOf(next) };
71161
+ }
71162
+ async function refundRender(canvasRoot, slug, chatId, kind) {
71163
+ const found = await readLedger(canvasRoot, slug);
71164
+ if (!found || found.chatId !== chatId) return;
71165
+ const ledger = landed(found);
71166
+ if (kind === "pictures") await writeLedger(canvasRoot, slug, { ...ledger, renders: Math.max(0, ledger.renders - 1) });
71167
+ else await writeLedger(canvasRoot, slug, { ...ledger, redraws: Math.max(0, redrawsOf(ledger) - 1) });
71129
71168
  }
71130
71169
  async function openRenderRound(canvasRoot, slug) {
71131
71170
  await rm5(ledgerFile(canvasRoot, slug), { force: true }).catch(() => void 0);
@@ -71134,6 +71173,16 @@ async function openRenderRoundInWorkspace(slug) {
71134
71173
  const cwd = process.cwd();
71135
71174
  await openRenderRound(canvasOutputsRoot(path23.join(cwd, "src", "creatives", slug, "canvas.json"), cwd), slug);
71136
71175
  }
71176
+ function redrawNeedsPicturesRefusal(slug) {
71177
+ return {
71178
+ ok: false,
71179
+ error: {
71180
+ code: "RENDER_BUDGET",
71181
+ message: `${slug} has painted this round's ${RENDERS_PER_ROUND} pictures, and this change would paint new ones. Nothing was billed, and the re-draw was not counted.`,
71182
+ fix: "Change only what re-draws from the pictures already made: a line's `size`, a `treatment` of `highlight` or `sticker`, or shorter words. Or stop and show the person the best version, and ask what to change; recording their answer (baker creatives feedback record) opens a new round with new pictures."
71183
+ }
71184
+ };
71185
+ }
71137
71186
  function isStaticAdCanvas(parsed) {
71138
71187
  const nodes = parsed?.nodes;
71139
71188
  return Array.isArray(nodes) && nodes.some((n) => n?.id === "compose_spec" && n?.type === "ingest");
@@ -71143,7 +71192,7 @@ function renderBudgetRefusal(slug) {
71143
71192
  ok: false,
71144
71193
  error: {
71145
71194
  code: "RENDER_BUDGET",
71146
- message: `${slug} has been rendered ${RENDERS_PER_ROUND} times in this chat since the person last reacted to it. It is not rendered again until they do.`,
71195
+ message: `${slug} has used this round's ${RENDERS_PER_ROUND} pictures and ${REDRAWS_PER_ROUND} re-draws in this chat since the person last reacted to it. It is not rendered again until they do.`,
71147
71196
  fix: `Stop fixing. Show the person the best version you have (the run with the fewest blocking problems), say in one line what is still wrong, and ask whether to keep it or what to change. When they answer, record it with baker creatives feedback record --slug ${slug} --run <run_id> (or baker creatives select --slug ${slug} --run <run_id>), which opens a new round of ${RENDERS_PER_ROUND} renders. Working as a subagent: return now with the best run id and what is left.`
71148
71197
  }
71149
71198
  };
@@ -71993,9 +72042,15 @@ async function textOf(ref2) {
71993
72042
  var LAYOUT_FIX = "Overlapping or unreadable copy is a layout fix, not a new picture. In prompt.json make the line smaller (`size` in its layout region's `text`), give the line in `text_content` a `treatment` of `highlight` or `sticker` (a solid fill behind the words), or shorten it (in `text_content` and in its layout region, the same words in both). Those re-draw only the formats from the cached pictures. Moving a region (`x_pct`/`y_pct`) or editing the scene re-paints all three pictures.";
71994
72043
  function verdictHints(verdict, round4) {
71995
72044
  if (verdict === "needs_fixes") {
72045
+ if (round4 && round4.renders >= round4.of && round4.redraws >= round4.redrawsOf) {
72046
+ return [
72047
+ `NOT READY, and this round's ${round4.of} pictures and ${round4.redrawsOf} re-draws for this creative are spent before the person sees it: stop fixing. Show them the best version you have (this one or an earlier run), say in one line what is still wrong, and ask whether to keep it or what to change. Record their answer with baker creatives feedback record; that opens a new round.`
72048
+ ];
72049
+ }
71996
72050
  if (round4 && round4.renders >= round4.of) {
71997
72051
  return [
71998
- `NOT READY, and this was render ${round4.renders} of ${round4.of} for this creative before the person sees it: stop fixing. Show them the best version you have (this one or an earlier run), say in one line what is still wrong, and ask whether to keep it or what to change. Record their answer with baker creatives feedback record; that opens a new round.`
72052
+ `NOT READY, and the ${round4.of} pictures of this round are painted: ${round4.redrawsOf - round4.redraws} free re-draws left, from the pictures already made. Fix only what re-draws from them, the smallest edit, and re-run; a change that needs new pictures (moving a region, editing the scene) is refused until the person reacts.`,
72053
+ LAYOUT_FIX
71999
72054
  ];
72000
72055
  }
72001
72056
  return [
@@ -72278,7 +72333,7 @@ ${describeRewrites(prepared.rewrites)}
72278
72333
  );
72279
72334
  process.exit(2);
72280
72335
  }
72281
- const round4 = creativeSlug && !resumed && isStaticAdCanvas(parsed) ? await spendRender(canvasRoot, creativeSlug, optionalChatId() ?? null) : null;
72336
+ const round4 = creativeSlug && isStaticAdCanvas(parsed) ? resumed ? await resumedRender(canvasRoot, creativeSlug, optionalChatId() ?? null, runId) : await spendRender(canvasRoot, creativeSlug, optionalChatId() ?? null, runId) : null;
72282
72337
  if (round4 && !round4.allowed && creativeSlug) {
72283
72338
  process.stderr.write(`${JSON.stringify(renderBudgetRefusal(creativeSlug), null, 2)}
72284
72339
  `);
@@ -72354,6 +72409,7 @@ ${describeRewrites(prepared.rewrites)}
72354
72409
  signal: abort.signal,
72355
72410
  cache_policy: policy,
72356
72411
  max_credits: opts.maxCredits,
72412
+ ...round4?.kind === "redraw" ? { billed_from_cache_only: true } : {},
72357
72413
  ...stopBeforeKinds(opts.until) ? { stop_before_kinds: stopBeforeKinds(opts.until) } : {},
72358
72414
  concurrency: resolveConcurrency(opts.concurrency, process.env.BAKER_CANVAS_CONCURRENCY),
72359
72415
  regenerate,
@@ -72380,6 +72436,13 @@ ${describeRewrites(prepared.rewrites)}
72380
72436
  await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
72381
72437
  `));
72382
72438
  }
72439
+ const settled = round4?.allowed && creativeSlug ? await settleSpentRender(canvasRoot, creativeSlug, optionalChatId() ?? null, round4.kind, result.stats.total_credits > 0) : null;
72440
+ const roundAfter = round4 ? {
72441
+ renders: settled?.renders ?? round4.renders,
72442
+ of: RENDERS_PER_ROUND,
72443
+ redraws: settled?.redraws ?? round4.redraws,
72444
+ redrawsOf: REDRAWS_PER_ROUND
72445
+ } : null;
72383
72446
  const reviewable = videoPathFromOutput(result.output);
72384
72447
  const reviewUsage = [];
72385
72448
  const review = reviewable ? await reviewRenderedVideo({
@@ -72394,7 +72457,7 @@ ${describeRewrites(prepared.rewrites)}
72394
72457
  output: result.output,
72395
72458
  outputsByNode: result.outputs_by_node,
72396
72459
  onSpend: (usage) => reviewUsage.push(usage),
72397
- round: round4 ? { renders: round4.renders, of: RENDERS_PER_ROUND } : null
72460
+ round: roundAfter
72398
72461
  });
72399
72462
  const hints2 = staticReview ? staticReview.hints : reviewable ? review === null ? [RENDER_REVIEW_UNAVAILABLE] : review.length === 0 ? ["Reviewed the finished video frame by frame \u2014 nothing to report."] : [
72400
72463
  `Reviewed the finished video and found ${review.length} thing${review.length === 1 ? "" : "s"} to fix. Show the user what is wrong before you show them the ad.`,
@@ -72475,6 +72538,14 @@ ${describeRewrites(prepared.rewrites)}
72475
72538
  );
72476
72539
  process.exit(0);
72477
72540
  }
72541
+ if (e instanceof RunAbortedError && e.reason === "cache_only" && creativeSlug) {
72542
+ await clearMarker();
72543
+ await refundRender(canvasRoot, creativeSlug, optionalChatId() ?? null, "redraw");
72544
+ if (poster) await poster.flush(failedPayload(e.message));
72545
+ process.stderr.write(`${JSON.stringify(redrawNeedsPicturesRefusal(creativeSlug), null, 2)}
72546
+ `);
72547
+ process.exit(2);
72548
+ }
72478
72549
  if (e instanceof RunAbortedError && e.reason === "cost_cap") {
72479
72550
  await clearMarker();
72480
72551
  if (poster) await poster.flush(failedPayload(e.message));
@@ -72484,7 +72555,7 @@ ${describeRewrites(prepared.rewrites)}
72484
72555
  }
72485
72556
  await clearMarker();
72486
72557
  if (round4?.allowed && creativeSlug && failedOnlyOnTransientErrors(e)) {
72487
- await refundRender(canvasRoot, creativeSlug, optionalChatId() ?? null);
72558
+ await refundRender(canvasRoot, creativeSlug, optionalChatId() ?? null, round4.kind);
72488
72559
  }
72489
72560
  if (e instanceof ValidationError) {
72490
72561
  process.stderr.write(