@koda-sl/baker-cli 0.404.0 → 0.406.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-YOF4XTUW.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 [
@@ -72093,10 +72148,6 @@ var runCommand = defineCommand131({
72093
72148
  type: "string",
72094
72149
  description: "Alias for --concurrency: independent clips (and every other same-layer node) already fan out in parallel up to this bound"
72095
72150
  },
72096
- "max-credits": {
72097
- type: "string",
72098
- description: "Credit ceiling for this run (env BAKER_CANVAS_MAX_CREDITS). Aborts before billing if the estimate exceeds it, and at layer boundaries once actual spend does; completed nodes stay cached so a capped-up retry loses nothing."
72099
- },
72100
72151
  // citty consumes any `--no-<flag>` as a negation of `<flag>`, so the
72101
72152
  // opt-out spelling `--no-record` requires the flag to be named `record`
72102
72153
  // (a literal "no-record" arg would never receive a value).
@@ -72133,22 +72184,10 @@ var runCommand = defineCommand131({
72133
72184
  concurrency: (args8.concurrency ?? args8.parallel) !== void 0 ? String(args8.concurrency ?? args8.parallel) : void 0,
72134
72185
  keepRuns: args8["keep-runs"] !== void 0 ? Number(args8["keep-runs"]) : void 0,
72135
72186
  remoteCache: args8["remote-cache"] !== void 0 ? String(args8["remote-cache"]) !== "off" : void 0,
72136
- record: args8.record !== false,
72137
- maxCredits: resolveMaxCredits(
72138
- args8["max-credits"] !== void 0 ? String(args8["max-credits"]) : void 0,
72139
- process.env.BAKER_CANVAS_MAX_CREDITS
72140
- )
72187
+ record: args8.record !== false
72141
72188
  });
72142
72189
  }
72143
72190
  });
72144
- function resolveMaxCredits(...candidates) {
72145
- for (const candidate of candidates) {
72146
- if (candidate === void 0 || candidate === "") continue;
72147
- const n = Number(candidate);
72148
- if (Number.isFinite(n) && n >= 0) return n;
72149
- }
72150
- return void 0;
72151
- }
72152
72191
  function stopBeforeKinds(until) {
72153
72192
  if (until?.trim() === "frames") return /* @__PURE__ */ new Set(["video_generate"]);
72154
72193
  return null;
@@ -72278,7 +72317,7 @@ ${describeRewrites(prepared.rewrites)}
72278
72317
  );
72279
72318
  process.exit(2);
72280
72319
  }
72281
- const round4 = creativeSlug && !resumed && isStaticAdCanvas(parsed) ? await spendRender(canvasRoot, creativeSlug, optionalChatId() ?? null) : null;
72320
+ const round4 = creativeSlug && isStaticAdCanvas(parsed) ? resumed ? await resumedRender(canvasRoot, creativeSlug, optionalChatId() ?? null, runId) : await spendRender(canvasRoot, creativeSlug, optionalChatId() ?? null, runId) : null;
72282
72321
  if (round4 && !round4.allowed && creativeSlug) {
72283
72322
  process.stderr.write(`${JSON.stringify(renderBudgetRefusal(creativeSlug), null, 2)}
72284
72323
  `);
@@ -72353,7 +72392,7 @@ ${describeRewrites(prepared.rewrites)}
72353
72392
  run_id: runId,
72354
72393
  signal: abort.signal,
72355
72394
  cache_policy: policy,
72356
- max_credits: opts.maxCredits,
72395
+ ...round4?.kind === "redraw" ? { billed_from_cache_only: true } : {},
72357
72396
  ...stopBeforeKinds(opts.until) ? { stop_before_kinds: stopBeforeKinds(opts.until) } : {},
72358
72397
  concurrency: resolveConcurrency(opts.concurrency, process.env.BAKER_CANVAS_CONCURRENCY),
72359
72398
  regenerate,
@@ -72380,6 +72419,13 @@ ${describeRewrites(prepared.rewrites)}
72380
72419
  await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
72381
72420
  `));
72382
72421
  }
72422
+ const settled = round4?.allowed && creativeSlug ? await settleSpentRender(canvasRoot, creativeSlug, optionalChatId() ?? null, round4.kind, result.stats.total_credits > 0) : null;
72423
+ const roundAfter = round4 ? {
72424
+ renders: settled?.renders ?? round4.renders,
72425
+ of: RENDERS_PER_ROUND,
72426
+ redraws: settled?.redraws ?? round4.redraws,
72427
+ redrawsOf: REDRAWS_PER_ROUND
72428
+ } : null;
72383
72429
  const reviewable = videoPathFromOutput(result.output);
72384
72430
  const reviewUsage = [];
72385
72431
  const review = reviewable ? await reviewRenderedVideo({
@@ -72394,7 +72440,7 @@ ${describeRewrites(prepared.rewrites)}
72394
72440
  output: result.output,
72395
72441
  outputsByNode: result.outputs_by_node,
72396
72442
  onSpend: (usage) => reviewUsage.push(usage),
72397
- round: round4 ? { renders: round4.renders, of: RENDERS_PER_ROUND } : null
72443
+ round: roundAfter
72398
72444
  });
72399
72445
  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
72446
  `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,16 +72521,17 @@ ${describeRewrites(prepared.rewrites)}
72475
72521
  );
72476
72522
  process.exit(0);
72477
72523
  }
72478
- if (e instanceof RunAbortedError && e.reason === "cost_cap") {
72524
+ if (e instanceof RunAbortedError && e.reason === "cache_only" && creativeSlug) {
72479
72525
  await clearMarker();
72526
+ await refundRender(canvasRoot, creativeSlug, optionalChatId() ?? null, "redraw");
72480
72527
  if (poster) await poster.flush(failedPayload(e.message));
72481
- process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "cost_cap", message: e.message } }, null, 2)}
72528
+ process.stderr.write(`${JSON.stringify(redrawNeedsPicturesRefusal(creativeSlug), null, 2)}
72482
72529
  `);
72483
- process.exit(1);
72530
+ process.exit(2);
72484
72531
  }
72485
72532
  await clearMarker();
72486
72533
  if (round4?.allowed && creativeSlug && failedOnlyOnTransientErrors(e)) {
72487
- await refundRender(canvasRoot, creativeSlug, optionalChatId() ?? null);
72534
+ await refundRender(canvasRoot, creativeSlug, optionalChatId() ?? null, round4.kind);
72488
72535
  }
72489
72536
  if (e instanceof ValidationError) {
72490
72537
  process.stderr.write(