@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/README.md +1 -1
- package/dist/{chunk-KE6GXLDE.js → chunk-DGZ5DLXK.js} +55 -15
- package/dist/{chunk-KE6GXLDE.js.map → chunk-DGZ5DLXK.js.map} +1 -1
- package/dist/cli.js +93 -22
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.d.ts +23 -0
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5775,7 +5775,7 @@ It then scaffolds a canvas that draws **only what `prompt.json` holds**; no elem
|
|
|
5775
5775
|
|
|
5776
5776
|
**Placement formats.** The canvas delivers the platform's full placement set, each format at its exact size, collected under `final` with the format as its label. The set is derived from `--platform` (`meta` → 4:5 + 9:16, `google` → 1:1 + 4:5 + 16:9, `linkedin` → 1:1, `tiktok` → 9:16, `youtube` → 16:9); override it with `--placements <preset>` or `--placements none` for a single format. The report's `aspect_ratio` is the ratio the plates really render at (the model's nearest to the lead format), and `format_plan` says how each format is made.
|
|
5777
5777
|
|
|
5778
|
-
**After a run** the CLI reads the finished ad back on what makes a static win, not a checklist of elements: every delivered image's pixel shape against its format, and one vision pass that reads each word against the copy `prompt.json` holds and checks one dominant idea, clipping, artifacts, the brand with the logo covered and the concept's mechanisms, with every format shown a second time at phone-feed thumbnail size (240px wide, same call): a lead message unreadable at that size is blocking. A missing logo, CTA or copy is never a finding; a logo that is drawn and broken is. stdout carries `verdict` (`ready` | `ready_with_notes` | `needs_fixes` | `not_reviewed`), `judge` (the per-plate scores) and hints that end with what the run made; the findings are stored on the run as its `review`. `needs_fixes` means the ad is not done even though every node completed; its hints name the cheapest fix (overlapping or unreadable copy is fixed with a smaller `size`, a `highlight` or `sticker` treatment or shorter words, which re-draw the formats from the cached plates). **Render budget:** a static ad gets
|
|
5778
|
+
**After a run** the CLI reads the finished ad back on what makes a static win, not a checklist of elements: every delivered image's pixel shape against its format, and one vision pass that reads each word against the copy `prompt.json` holds and checks one dominant idea, clipping, artifacts, the brand with the logo covered and the concept's mechanisms, with every format shown a second time at phone-feed thumbnail size (240px wide, same call): a lead message unreadable at that size is blocking. A missing logo, CTA or copy is never a finding; a logo that is drawn and broken is. stdout carries `verdict` (`ready` | `ready_with_notes` | `needs_fixes` | `not_reviewed`), `judge` (the per-plate scores) and hints that end with what the run made; the findings are stored on the run as its `review`. `needs_fixes` means the ad is not done even though every node completed; its hints name the cheapest fix (overlapping or unreadable copy is fixed with a smaller `size`, a `highlight` or `sticker` treatment or shorter words, which re-draw the formats from the cached plates). **Render budget:** a static ad gets, per creative in a chat before the person reacts, 3 renders that paint pictures and then 4 re-draws from those pictures; a render that paints nothing (every billed node from the cache) counts as a re-draw. With the pictures spent a re-draw runs only from the cache (the engine's `billed_from_cache_only`), and one that would paint exits 2 with `RENDER_BUDGET` before billing; with both spent every render exits 2 with `RENDER_BUDGET` without rendering, and `baker creatives feedback record` or `baker creatives select` on that creative opens a new round. Resuming an interrupted run does not count again, and a run that died only on an outage (timeout, network, a provider that says retry) gives its render back.
|
|
5779
5779
|
|
|
5780
5780
|
The logo is never a slot to fill: use the company's committed logo in `src/brand/logos/` (per `BRAND.md`), never `baker images logo <domain>`, whose Brandfetch CDN often serves an outdated mark.
|
|
5781
5781
|
|
|
@@ -6223,20 +6223,7 @@ var Engine = class {
|
|
|
6223
6223
|
const layers = topologicalLayers(graph);
|
|
6224
6224
|
const limit = resolveConcurrency(opts.concurrency);
|
|
6225
6225
|
for (const layer of layers) {
|
|
6226
|
-
|
|
6227
|
-
if (opts.max_credits !== void 0 && counters.totalCredits > opts.max_credits) {
|
|
6228
|
-
throw new RunAbortedError(
|
|
6229
|
-
"cost_cap",
|
|
6230
|
-
`spent ${counters.totalCredits} credits, over the ${opts.max_credits}-credit cap \u2014 completed nodes are cached; raise --max-credits to continue where this stopped`
|
|
6231
|
-
);
|
|
6232
|
-
}
|
|
6233
|
-
const held = heldByCheckpoint(canvas, layer, opts.stop_before_kinds);
|
|
6234
|
-
if (held.length > 0) {
|
|
6235
|
-
throw new RunAbortedError(
|
|
6236
|
-
"checkpoint",
|
|
6237
|
-
`stopped before ${held.join(", ")} \u2014 everything up to here is done and cached, so continuing this run id pays for none of it again`
|
|
6238
|
-
);
|
|
6239
|
-
}
|
|
6226
|
+
await this.assertLayerMayRun(canvas, layer, outputs, runId, opts, counters);
|
|
6240
6227
|
const settled = await mapWithConcurrency(layer, limit, (nodeId) => {
|
|
6241
6228
|
if (opts.signal?.aborted) {
|
|
6242
6229
|
return Promise.reject(new RunAbortedError("signal", "run aborted before node dispatch"));
|
|
@@ -6363,6 +6350,59 @@ var Engine = class {
|
|
|
6363
6350
|
this.logOutputWarnings(node.id, outputsObj);
|
|
6364
6351
|
return { cached: false, credits, durationMs: elapsed };
|
|
6365
6352
|
}
|
|
6353
|
+
/**
|
|
6354
|
+
* Every reason a layer must not dispatch, checked before any of its nodes starts, so a stop
|
|
6355
|
+
* never leaves a layer half billed: a signal, the cost cap, a checkpoint, and a billed node
|
|
6356
|
+
* the cache cannot serve when the run is cache-only.
|
|
6357
|
+
*/
|
|
6358
|
+
async assertLayerMayRun(canvas, layer, outputs, runId, opts, counters) {
|
|
6359
|
+
if (opts.signal?.aborted) throw new RunAbortedError("signal", "run aborted before layer dispatch");
|
|
6360
|
+
if (opts.max_credits !== void 0 && counters.totalCredits > opts.max_credits) {
|
|
6361
|
+
throw new RunAbortedError(
|
|
6362
|
+
"cost_cap",
|
|
6363
|
+
`spent ${counters.totalCredits} credits, over the ${opts.max_credits}-credit cap \u2014 completed nodes are cached; raise --max-credits to continue where this stopped`
|
|
6364
|
+
);
|
|
6365
|
+
}
|
|
6366
|
+
const held = heldByCheckpoint(canvas, layer, opts.stop_before_kinds);
|
|
6367
|
+
if (held.length > 0) {
|
|
6368
|
+
throw new RunAbortedError(
|
|
6369
|
+
"checkpoint",
|
|
6370
|
+
`stopped before ${held.join(", ")} \u2014 everything up to here is done and cached, so continuing this run id pays for none of it again`
|
|
6371
|
+
);
|
|
6372
|
+
}
|
|
6373
|
+
if (opts.billed_from_cache_only) {
|
|
6374
|
+
const fresh = await this.billedMisses(canvas, layer, outputs, runId, opts);
|
|
6375
|
+
if (fresh.length > 0) {
|
|
6376
|
+
throw new RunAbortedError(
|
|
6377
|
+
"cache_only",
|
|
6378
|
+
`${fresh.join(", ")} would be rendered fresh and billed \u2014 nothing in this layer ran`
|
|
6379
|
+
);
|
|
6380
|
+
}
|
|
6381
|
+
}
|
|
6382
|
+
}
|
|
6383
|
+
/**
|
|
6384
|
+
* The billed nodes of a layer that would miss the cache, found before any of them runs:
|
|
6385
|
+
* each is prepared exactly as `executeOne` would (same inputs, params and cache key).
|
|
6386
|
+
*
|
|
6387
|
+
* `executeOne` prepares each node again when it runs, so a cache-only layer hydrates its
|
|
6388
|
+
* text slots and hashes its `cacheKeyExtras` twice. Deliberate: a re-draw's layers are
|
|
6389
|
+
* small, and threading this preparation into `executeOne` would couple the check to the
|
|
6390
|
+
* run path, where a stale preparation would bill the wrong key.
|
|
6391
|
+
*/
|
|
6392
|
+
async billedMisses(canvas, layer, outputs, runId, opts) {
|
|
6393
|
+
const misses = [];
|
|
6394
|
+
for (const nodeId of layer) {
|
|
6395
|
+
const node = canvas.nodes.find((n) => n.id === nodeId);
|
|
6396
|
+
const def = node ? this.registry.get(node.type) : void 0;
|
|
6397
|
+
if (!node || !def) continue;
|
|
6398
|
+
const token = resolveRegenerateToken(node, opts.regenerate, runId);
|
|
6399
|
+
const prepared = await prepareForExecution(node, outputs, def, canvas.cache_salt, token, this.assets);
|
|
6400
|
+
if (estimateNodeCredits2(def, prepared.renderedParams) <= 0) continue;
|
|
6401
|
+
const hit = opts.cache_policy === "bypass" ? null : await this.cache.get(prepared.cacheKey);
|
|
6402
|
+
if (!hit) misses.push(nodeId);
|
|
6403
|
+
}
|
|
6404
|
+
return misses;
|
|
6405
|
+
}
|
|
6366
6406
|
/** A cache hit replayed as this node's result: its outputs written, and free. */
|
|
6367
6407
|
async replayCached(node, cacheKey, outputs, writer) {
|
|
6368
6408
|
const cacheT0 = Date.now();
|
|
@@ -11668,4 +11708,4 @@ export {
|
|
|
11668
11708
|
defaultRegistry,
|
|
11669
11709
|
createEngineFromEnv
|
|
11670
11710
|
};
|
|
11671
|
-
//# sourceMappingURL=chunk-
|
|
11711
|
+
//# sourceMappingURL=chunk-DGZ5DLXK.js.map
|