@koda-sl/baker-cli 0.80.0 → 0.82.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 CHANGED
@@ -3263,6 +3263,8 @@ Reverse-engineer a video into a replication-grade blueprint: scene boundaries, t
3263
3263
 
3264
3264
  Over-length runs fail with a message that includes ready-to-use suggested windows, so the loop is self-correcting.
3265
3265
 
3266
+ > **Async execution.** Full-mode `video_deconstruct` is a multi-minute job, so the backend runs it as a durable background workflow rather than over a single long-lived HTTP request: the exec call returns immediately and the CLI transparently polls the job to completion. This is invisible to callers — the node's inputs, params, and outputs are unchanged — but it means a stalled provider can no longer drop the connection and cause a whole-node retry storm. (`mode:"index"` stays synchronous.)
3267
+
3266
3268
  **Inputs**
3267
3269
 
3268
3270
  | Slot | Kind | Required | Accepted MIMEs |
@@ -3631,6 +3633,8 @@ It then scaffolds the full pipeline: per scene, two **static-ad-grade frames** (
3631
3633
 
3632
3634
  **Overlays are agent-painted HTML, not props.** The clips are concatenated, then the `video-overlay` composition (copied next to the canvas) composites the overlay layer. The scaffold **bakes the reference's overlays into that composition's `index.html` as real, editable HTML** (each overlay is a plain element with its text, a `.pos-*` position class, and `data-start`/`data-dur` timing); a tiny generic runtime only shows/hides each element at its timestamp (with an optional `data-anim` entrance). It makes **no styling decisions** — bars, tickers, colors, fonts, and a real logo `<img>` you drop into the dir all live in the HTML/CSS you edit. Floating elements (logo bugs) are seeded as commented `<img>` stubs so an un-edited render stays clean. Drop `brand-bold.otf`/`brand-regular.otf` for on-brand type.
3633
3635
 
3636
+ **Re-craft the script — the hook is the #1 decision.** A reproduction is *inspiration* from a proven ad, not a clone: its structure (hook → body → CTA) carries the persuasion, and the hook is *targeting*, so a competitor's hook often does **not** transfer. `metadata.todo.script_recraft` tags each scene with its `narrative_role` (from the deconstruct, else inferred) and carries the original line **flagged** so it is never shipped as-is — and the per-scene `recraft` instruction is **role-aware**: the **hook** scene's entry carries the diagnose → decide (keep/adapt/rebuild) → criteria (statement not question, benefit by ~2s, first frame legible **sound-off** in ~1s, no bait-and-switch) inline and routes to the skill's `references/hook-craft.md`. A dedicated top-level **`metadata.todo.hook`** key foregrounds it as the highest-leverage beat, mapped onto scene-0's artifacts (`s0_start` first frame, scene-0 overlay text, `s0_clip` line, micro-hook, hook-ramp).
3637
+
3634
3638
  The emitted canvas is validated (`validateCanvasDeep`) before it's written, so it always runs. It also carries a **`metadata.video`** timing plan that `baker canvas validate` proves **statically, before any billed render**: no two voiceover turns overlap, the audio length ≈ the video length, and every single-on-camera-speaker scene is a native talking head (its clip carries `generate_audio` and is wired to an `audio_voice_convert` node). The full editable checklist is embedded as **`metadata.todo`** (with a step-by-step guide in `metadata.description`). stdout returns `{ ok, canvas_path, prompt_path, models, stats, checklist }`.
3635
3639
 
3636
3640
  ```bash
@@ -770,13 +770,48 @@ function shouldRetry(err) {
770
770
  }
771
771
 
772
772
  // src/engine/client/backend-client.ts
773
+ function isAsyncJob(res) {
774
+ return typeof res.job_id === "string";
775
+ }
776
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
777
+ var JOB_POLL_INTERVAL_MS = 3e3;
778
+ var JOB_POLL_MAX_MS = 20 * 60 * 1e3;
773
779
  var BackendClient = class {
774
780
  http;
775
781
  constructor(opts) {
776
782
  this.http = new HttpClient(opts);
777
783
  }
778
- exec(req, signal) {
779
- return this.http.postJson("/api/canvas/nodes/exec", req, signal);
784
+ async exec(req, signal) {
785
+ const res = await this.http.postJson("/api/canvas/nodes/exec", req, signal);
786
+ if (isAsyncJob(res)) {
787
+ return await this.pollJob(res.job_id, signal);
788
+ }
789
+ return res;
790
+ }
791
+ async pollJob(jobId, signal) {
792
+ const deadline = Date.now() + JOB_POLL_MAX_MS;
793
+ const path16 = `/api/canvas/jobs/${encodeURIComponent(jobId)}`;
794
+ while (true) {
795
+ if (signal?.aborted) {
796
+ throw new BackendHttpError({ kind: "network", cause: signal.reason ?? new Error("aborted") });
797
+ }
798
+ const job = await this.http.getJson(path16, signal);
799
+ if (job.status === "completed") return job.result;
800
+ if (job.status === "failed") {
801
+ throw new BackendHttpError({
802
+ kind: "provider",
803
+ status: job.error.status ?? 502,
804
+ provider: job.error.provider,
805
+ code: job.error.code ?? "provider_error",
806
+ message: job.error.message ?? "deconstruct failed",
807
+ retryable: job.error.retryable ?? false
808
+ });
809
+ }
810
+ if (Date.now() > deadline) {
811
+ throw new BackendHttpError({ kind: "timeout", message: `job ${jobId} did not finish in time` });
812
+ }
813
+ await sleep(JOB_POLL_INTERVAL_MS);
814
+ }
780
815
  }
781
816
  presignAssetUpload(sha256, mime, signal) {
782
817
  return this.http.postJson(
@@ -5970,4 +6005,4 @@ export {
5970
6005
  defaultRegistry,
5971
6006
  createEngineFromEnv
5972
6007
  };
5973
- //# sourceMappingURL=chunk-NBNUNCY7.js.map
6008
+ //# sourceMappingURL=chunk-KIL2ZJST.js.map