@koda-sl/baker-cli 0.113.0-dev.78e242dda → 0.113.2

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
@@ -653,47 +653,6 @@ baker ads google library search-competitors "crm software" --location uk
653
653
 
654
654
  ---
655
655
 
656
- ### Staged writes (`baker ads google budgets|campaigns|...`)
657
-
658
- Write commands **never touch the Google Ads API at stage time**. Each command stages a create/update/pause/resume/remove op against the current chat's draft (`BAKER_CHAT_ID`); the dashboard shows it as a pending "Google Ads" change, and the whole draft applies as one atomic `GoogleAdsService.Mutate` when the chat is published. Feature-flagged per company (`companies.googleAdsWriteEnabled`) — off by default = a fully simulated publish with zero real API calls.
659
-
660
- Reference not-yet-created resources with `g_temp_*` refs returned by earlier `create` commands (they map to Google's temporary negative-ID resource names inside one atomic batch).
661
-
662
- ```bash
663
- # Build a Search campaign bottom-up, chaining temp refs
664
- baker ads google budgets create --customer-id 1234567890 --name "Search budget" --amount 50
665
- # → { ref: "g_temp_ab12", ... }
666
- baker ads google campaigns create --customer-id 1234567890 --name "Brand — Search" \
667
- --channel-type SEARCH --budget-ref g_temp_ab12 --bidding-strategy MANUAL_CPC
668
- baker ads google ad-groups create --customer-id 1234567890 --name "Brand terms" --campaign-ref g_temp_<campaign>
669
- baker ads google keywords add --customer-id 1234567890 --ad-group-ref g_temp_<adgroup> --text "brand name" --match-type EXACT
670
- # Shared negative keyword list → attach to the campaign
671
- baker ads google keyword-lists create --customer-id 1234567890 --name "Brand exclusions"
672
- baker ads google keyword-lists add --customer-id 1234567890 --list-ref g_temp_<list> --text "free" --match-type BROAD
673
- baker ads google keyword-lists attach --customer-id 1234567890 --campaign-ref g_temp_<campaign> --list-ref g_temp_<list>
674
-
675
- # Batch keywords — one command stages the whole set as ONE request (up to 500)
676
- baker ads google keywords add --customer-id 1234567890 --ad-group-ref g_temp_<adgroup> \
677
- --text "brand name, brand shop:PHRASE, brand store" --match-type EXACT
678
- baker ads google keyword-lists add --customer-id 1234567890 --list-ref <list-id> --file negatives.txt
679
- # negatives.txt: one keyword[:MATCH_TYPE] per line; blank lines and "# comments" are skipped
680
- # Responsive search ad
681
- baker ads google ads create --customer-id 1234567890 --ad-group-ref g_temp_<adgroup> \
682
- --headlines "Fast Widgets,Buy Online,Free Shipping" --descriptions "Best widgets around.,Ships tomorrow." \
683
- --final-url https://example.com
684
-
685
- # Review / undo staged changes
686
- baker ads google draft list
687
- baker ads google draft remove g_temp_ab12 # cascades to dependents
688
- baker ads google draft clear
689
- ```
690
-
691
- Command groups: `budgets`, `campaigns`, `ad-groups`, `keywords` (add/update/remove), `negative-keywords`, `keyword-lists`, `ads`, `assets`, `audiences`, `conversions`, `bidding-strategies`, `labels`, `campaign-criteria`, and `draft`. Amounts are in major currency units (converted to micros). Money/bids: `--amount`, `--cpc-bid`, `--target-cpa` take major units; `--target-roas` a ratio. Less-common ops accept a `--file <payload.json>` (flags override file keys). Updates target a resource name or bare id as the positional argument; a target that names an op staged earlier **amends it in place**.
692
-
693
- **Batch keyword adds** — `keywords add`, `negative-keywords add`, and `keyword-lists add` take a whole batch in one command: comma-separate `--text` entries and/or pass `--file <list.txt>` (one keyword per line). A `:EXACT`/`:PHRASE`/`:BROAD` suffix per entry overrides the `--match-type` default. Batches stage all-or-nothing as one request (limit 500); each keyword still lands as its own draft op, so it stays individually removable/amendable.
694
-
695
- ---
696
-
697
656
  ### Caching
698
657
 
699
658
  Google Ads data is cached at two levels:
@@ -2541,7 +2500,11 @@ baker mcp remove --name weather
2541
2500
  ```
2542
2501
 
2543
2502
  - `--url` must be HTTPS and public; loopback / private / cloud-metadata hosts are rejected.
2544
- - `--scope company` (default) shares with every chat in the company; `--scope org` needs an admin API key; `--scope user` binds to the current message sender (requires running inside a chat turn — the bridge supplies the sender via `BAKER_ACTING_USER_ID`).
2503
+ - Scope is two independent choices audience (just you / everyone) × level (this company / whole org):
2504
+ - `--scope company` (default) — everyone in this company.
2505
+ - `--scope org` — everyone across the org; needs an admin API key.
2506
+ - `--scope user` — only you, in this company; binds to the current message sender (requires a chat turn — the bridge supplies the sender via `BAKER_ACTING_USER_ID`).
2507
+ - `--scope user_org` — only you, across every company in the org; also binds to the sender (no admin needed — it only affects your own turns).
2545
2508
  - Repeat `--header "Key: Value"` for multiple auth headers. Header values are stored server-side and ride the MCP transport — `list` returns only the header keys, never the values.
2546
2509
 
2547
2510
  ---
@@ -3809,6 +3772,9 @@ Validate, then execute the graph. Blocks until done. Logs one line per node. Ret
3809
3772
  | `--outputs-dir <path>` | `./canvas` | Root for per-run output directories. |
3810
3773
  | `--run-id <id>` | auto ULID | Override the generated run id. |
3811
3774
  | `--cache-policy <policy>` | `read_write` | `read_write`, `bypass`, or `read_only`. |
3775
+ | `--concurrency <n>` | `5` (or `BAKER_CANVAS_CONCURRENCY`) | Max nodes executing at once within a layer. |
3776
+
3777
+ **Failures don't abandon sibling work.** Nodes in a layer run under the concurrency cap and every one **settles** — a failed clip no longer kills its in-flight siblings, whose finished results still land in the content-addressed cache. One failure re-throws as-is; several are reported together (each failed node named). Re-running `baker canvas run` resumes from the cache and re-executes **only** the failed nodes and their descendants — never hand-orchestrate per-node renders. Long `video_generate` clips execute as **backend jobs** (the CLI polls; a CDN/proxy timeout can no longer kill a generation mid-flight).
3812
3778
 
3813
3779
  #### `baker canvas scaffold-video <video> [flags]`
3814
3780
 
@@ -3825,6 +3791,8 @@ It then scaffolds the full pipeline like an **editing timeline**: each clip gets
3825
3791
 
3826
3792
  **Composited scenes (split-screen / picture-in-picture / keyed presenter).** Real ads aren't always one full-frame shot — a frame can be **persistently divided** (b-roll on top, a presenter talking on the bottom) or **layer a presenter** over background footage (boxed in a corner, or green-screen keyed). The deconstruct now reports this per scene as `scene.composition` (`layout: split_screen | pip | keyed_overlay`, with one `region` per stream — each its own clean-plate frame + motion brief, the talking-head region flagged `is_presenter`). The scaffold reproduces a composited scene by building **one clip per region** (`s<i>_r0_*`, `s<i>_r1_*`, …) and compositing them with ffmpeg: a split-screen `vstack`/`hstack` (stack direction read from the region **panels**, so a top/bottom split always stacks vertically), or a picture-in-picture `overlay` of the presenter inset at its corner. A **keyed** presenter is first cut to transparency by `video_background_remove` (`s<i>_key`), then overlaid. The presenter region carries the native lip-synced voice; b-roll/render panels stay silent. To change a layout, edit `composition` in `prompt.json` and re-scaffold, or hand-edit the `s<i>_composite` ffmpeg args. Plain full-frame scenes (the default) are unaffected.
3827
3793
 
3794
+ **True output aspect, even when the video model can't generate it.** The scaffold resolves TWO ratios: the ad's **output** ratio (the source video's detected `aspect_ratio`, or **`--aspect`**, e.g. `4:5` → a 1080×1350 canvas) and the **generation** ratio — the nearest ratio the video model supports (Seedance has no 4:5, so clips + conditioning keyframes generate at `3:4`). When they differ the scaffold warns loudly, records `metadata.todo.aspect_remap`, normalizes **every clip** onto the output canvas (scale-to-fill, center-crop — keep subjects centered), sizes every ffmpeg plate/still/composite to the output dims, and stamps the copied overlay/caption compositions' `meta.json` to match. Nothing is silently dropped: an unknown ratio snaps to the nearest known one and stays explicit on every node.
3795
+
3828
3796
  **Montage flashes held as stills.** A rapid-cut beat shorter than ~2s with no spoken line is a **flash** — Seedance's shortest clip is 4s, so generating one (then trimming away most of it) burns credits for motion no viewer perceives. The scaffold instead **holds one keyframe as a still** for the scene length (a cheap ffmpeg loop, no billed `video_generate`), same look at a fraction of the cost. Talking/ambient beats keep a real clip (they need motion + native audio).
3829
3797
 
3830
3798
  **The phrase model (voice cut at pauses, not at visual cuts).** The voice is grouped into **phrases** — runs of continuous speech with no real pause, which may span several visual scenes. A phrase is voiced ONCE (so a sentence the deconstruct split at a visual cut never breaks mid-word): if the speaker is **shown** anywhere in the phrase it's a single Seedance clip (`s<anchor>_clip`, native lip-sync + audio) re-voiced to the brand voice; if the speaker is **never shown** it's one ElevenLabs `tts` read. The picture is then assembled **scene by scene**: a scene that shows the speaker **slices its window** out of the phrase clip (`s<i>_seg`, an ffmpeg `-ss`/`-t` cut — video and audio come from the *same* clip, so lip-sync holds), and a **b-roll cutaway** gets its own silent clip while the phrase's voice plays underneath. "Shown" is decided by the **presenter element's per-scene presence**, not just who's speaking — a scene where a cast member narrates over b-roll (their element absent) is treated as a cutaway, so the talking head never appears where the original cut away. A presenter run longer than the **gateway-safe ~10s clip ceiling splits at a scene boundary** into contiguous takes (each its own clip + convert), so a sliced window never reads past its clip. (Seedance's *API* max is 15s, but the generation gateway frequently times out — **HTTP 524** — before it can deliver a clip longer than ~10s, so the scaffold never asks for one that long; 10s is a Seedance-allowed duration, so the split clip still snaps cleanly.) A b-roll cutaway *inside* a phrase lands at an **approximate** time (Seedance exposes no word timing) — nudge the scene boundary if it's off its beat.
@@ -3849,7 +3817,7 @@ It then scaffolds the full pipeline like an **editing timeline**: each clip gets
3849
3817
 
3850
3818
  **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).
3851
3819
 
3852
- 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 }`.
3820
+ 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, 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), **no re-crafted line physically overruns its clip** (`VIDEO_SPEECH_OVERRUN` — est. speech > ~1.6× the clip duration fails validate, since Seedance crams or dies on it), and **every clip agrees on one aspect ratio** (`VIDEO_ASPECT_MISMATCH`). 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 }`.
3853
3821
 
3854
3822
  ```bash
3855
3823
  baker canvas scaffold-video ./reference-ad.mp4 --focus "competitor UGC ad for <brand>"
@@ -66,7 +66,10 @@
66
66
  data-height="1920"
67
67
  >
68
68
  <video id="bg" class="clip" src="background.mp4" data-start="0" data-duration="{{duration}}" muted></video>
69
- <div class="caption-container" id="captions"></div>
69
+ <!-- Captions intentionally paint over the full-bleed background video —
70
+ tell the pre-render occlusion check that's by design (it otherwise
71
+ flags any text sitting over an opaque full-canvas element). -->
72
+ <div class="caption-container" id="captions" data-layout-allow-occlusion></div>
70
73
  </div>
71
74
 
72
75
  <script>
@@ -101,7 +101,10 @@
101
101
  <body>
102
102
  <div id="root" data-composition-id="main" data-start="0" data-duration="{{duration}}" data-width="1080" data-height="1920">
103
103
  <video id="bg" class="clip" src="background.mp4" data-start="0" data-duration="{{duration}}" muted></video>
104
- <div id="overlay-root">
104
+ <!-- Overlays intentionally paint over the full-bleed background video —
105
+ tell the pre-render occlusion check that's by design (it otherwise
106
+ flags any text sitting over an opaque full-canvas element). -->
107
+ <div id="overlay-root" data-layout-allow-occlusion>
105
108
  <!--OVERLAYS-->
106
109
 
107
110
  <!-- NESTED BLOCK SLOT (optional): for an ANIMATED graphic (lower-third,
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  mod
25
25
  ));
26
26
 
27
- // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js
27
+ // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
28
28
  var require_safe_stable_stringify = __commonJS({
29
- "../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js"(exports, module) {
29
+ "../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
30
30
  "use strict";
31
31
  var { hasOwnProperty } = Object.prototype;
32
32
  var stringify = configure2();
@@ -873,6 +873,18 @@ var NodeExecutionError = class extends CanvasError {
873
873
  this.cause = cause;
874
874
  }
875
875
  };
876
+ var LayerExecutionError = class extends CanvasError {
877
+ failures;
878
+ constructor(failures) {
879
+ const ids = failures.map((f) => f.nodeId).join(", ");
880
+ super(`${failures.length} nodes failed in one layer: ${ids}`);
881
+ this.name = "LayerExecutionError";
882
+ this.failures = failures;
883
+ }
884
+ };
885
+ function describeFailureReason(reason) {
886
+ return reason instanceof Error ? reason.message : String(reason);
887
+ }
876
888
  function describeCause(c) {
877
889
  switch (c.kind) {
878
890
  case "local":
@@ -890,7 +902,7 @@ function describeCause(c) {
890
902
  }
891
903
  }
892
904
 
893
- // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/esm/wrapper.js
905
+ // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
894
906
  var import__ = __toESM(require_safe_stable_stringify(), 1);
895
907
  var configure = import__.default.configure;
896
908
  var wrapper_default = import__.default;
@@ -1488,6 +1500,34 @@ function validateValue(key, value, schema, model) {
1488
1500
  }
1489
1501
  }
1490
1502
 
1503
+ // src/engine/lib/concurrency.ts
1504
+ var DEFAULT_CONCURRENCY = 5;
1505
+ function resolveConcurrency(...candidates) {
1506
+ for (const candidate of candidates) {
1507
+ if (candidate === void 0 || candidate === "") continue;
1508
+ const n = typeof candidate === "number" ? candidate : Number(candidate);
1509
+ if (Number.isFinite(n) && n >= 1) return Math.floor(n);
1510
+ }
1511
+ return DEFAULT_CONCURRENCY;
1512
+ }
1513
+ async function mapWithConcurrency(items, limit, fn) {
1514
+ const results = new Array(items.length);
1515
+ let next = 0;
1516
+ const worker = async () => {
1517
+ while (next < items.length) {
1518
+ const index = next++;
1519
+ try {
1520
+ results[index] = { status: "fulfilled", value: await fn(items[index], index) };
1521
+ } catch (reason) {
1522
+ results[index] = { status: "rejected", reason };
1523
+ }
1524
+ }
1525
+ };
1526
+ const workers = Array.from({ length: Math.min(limit, items.length) }, worker);
1527
+ await Promise.all(workers);
1528
+ return results;
1529
+ }
1530
+
1491
1531
  // src/engine/lib/ulid.ts
1492
1532
  import { randomBytes } from "crypto";
1493
1533
  var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
@@ -2094,9 +2134,18 @@ var STAGE_CODES = {
2094
2134
  OUTPUT: "OUTPUT_REF_INVALID",
2095
2135
  VO_OVERLAP: "VIDEO_VO_OVERLAP",
2096
2136
  AUDIO_DURATION: "VIDEO_AUDIO_DURATION",
2097
- LIPSYNC_MISSING: "VIDEO_LIPSYNC_MISSING"
2137
+ LIPSYNC_MISSING: "VIDEO_LIPSYNC_MISSING",
2138
+ SPEECH_OVERRUN: "VIDEO_SPEECH_OVERRUN",
2139
+ ASPECT_MISMATCH: "VIDEO_ASPECT_MISMATCH"
2098
2140
  };
2099
2141
  var VIDEO_TIME_SLACK_S = 0.75;
2142
+ var SPEECH_WORDS_PER_SECOND = 2.5;
2143
+ var SPEECH_OVERRUN_RATIO = 1.6;
2144
+ function nativeDialogueOf(prompt) {
2145
+ if (typeof prompt !== "string") return void 0;
2146
+ const m = prompt.match(/Dialogue: "(.*)"/);
2147
+ return m?.[1]?.trim() || void 0;
2148
+ }
2100
2149
  function validateCanvas(input, registry) {
2101
2150
  const issues = [];
2102
2151
  const shape = CanvasSchema.safeParse(input);
@@ -2453,6 +2502,44 @@ function checkVideoInvariants(ctx) {
2453
2502
  });
2454
2503
  }
2455
2504
  }
2505
+ checkSpeechOverrun(ctx, meta.talking_scenes);
2506
+ checkAspectConsistency(ctx);
2507
+ }
2508
+ function speechOverrunOf(node) {
2509
+ const params = node.params;
2510
+ if (params?.generate_audio !== true) return null;
2511
+ const line = nativeDialogueOf(params.prompt);
2512
+ const duration = typeof params.duration === "number" ? params.duration : void 0;
2513
+ if (!line || !duration) return null;
2514
+ const estSpeechS = line.split(/\s+/).filter(Boolean).length / SPEECH_WORDS_PER_SECOND;
2515
+ return estSpeechS > duration * SPEECH_OVERRUN_RATIO ? { estSpeechS, duration } : null;
2516
+ }
2517
+ function checkSpeechOverrun(ctx, talkingScenes) {
2518
+ for (const entry of talkingScenes) {
2519
+ if (typeof entry !== "object" || !("voice_convert_node" in entry)) continue;
2520
+ const nativeClipRe = new RegExp(`^s${entry.scene}(_r\\d+)?_clip$`);
2521
+ for (const n of ctx.canvas.nodes) {
2522
+ if (!nativeClipRe.test(n.id) || n.type !== "video_generate") continue;
2523
+ const overrun = speechOverrunOf(n);
2524
+ if (!overrun) continue;
2525
+ ctx.issues.push({
2526
+ path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
2527
+ code: STAGE_CODES.SPEECH_OVERRUN,
2528
+ message: `"${n.id}" asks Seedance to speak ~${Math.round(overrun.estSpeechS * 10) / 10}s of dialogue inside a ${overrun.duration}s clip \u2014 the line cannot fit (>${SPEECH_OVERRUN_RATIO}\xD7 the clip). Shorten the line, split the scene, or lengthen the clip duration`
2529
+ });
2530
+ }
2531
+ }
2532
+ }
2533
+ function checkAspectConsistency(ctx) {
2534
+ const clips = ctx.canvas.nodes.filter((n) => n.type === "video_generate");
2535
+ if (clips.length < 2) return;
2536
+ const ratios = new Set(clips.map((n) => String(n.params?.aspect_ratio ?? "(unset)")));
2537
+ if (ratios.size <= 1) return;
2538
+ ctx.issues.push({
2539
+ path: "nodes",
2540
+ code: STAGE_CODES.ASPECT_MISMATCH,
2541
+ message: `video_generate nodes disagree on aspect_ratio (${[...ratios].sort().join(", ")}) \u2014 the spine's segments would render at different shapes and the composite silently crops. Pin every clip to the same aspect_ratio`
2542
+ });
2456
2543
  }
2457
2544
  function checkOutputRef(ctx) {
2458
2545
  const out = ctx.canvas.output;
@@ -2628,8 +2715,11 @@ var Engine = class {
2628
2715
  }
2629
2716
  async runLayers(canvas, outputs, runId, writer, opts, counters, nodeRuns) {
2630
2717
  const layers = topologicalLayers(this.pruneToOutput(canvas, buildGraph(canvas)));
2718
+ const limit = resolveConcurrency(opts.concurrency);
2631
2719
  for (const layer of layers) {
2632
- const tasks = layer.map(
2720
+ const settled = await mapWithConcurrency(
2721
+ layer,
2722
+ limit,
2633
2723
  (nodeId) => this.executeOne(canvas, nodeId, outputs, runId, writer, opts).then((r) => {
2634
2724
  if (r.cached) counters.cachedNodes++;
2635
2725
  counters.totalCredits += r.credits;
@@ -2645,7 +2735,13 @@ var Engine = class {
2645
2735
  }
2646
2736
  })
2647
2737
  );
2648
- await Promise.all(tasks);
2738
+ const failures = [];
2739
+ settled.forEach((result, i) => {
2740
+ const nodeId = layer[i];
2741
+ if (result.status === "rejected" && nodeId) failures.push({ nodeId, reason: result.reason });
2742
+ });
2743
+ if (failures.length === 1 && failures[0]) throw failures[0].reason;
2744
+ if (failures.length > 1) throw new LayerExecutionError(failures);
2649
2745
  }
2650
2746
  }
2651
2747
  /**
@@ -6238,10 +6334,13 @@ function createEngineFromEnv(opts = {}) {
6238
6334
  }
6239
6335
 
6240
6336
  export {
6337
+ LayerExecutionError,
6338
+ describeFailureReason,
6241
6339
  SEEDANCE_DURATIONS,
6242
6340
  ELEVENLABS_MAX_MUSIC_LENGTH_MS,
6243
6341
  IMAGE_GENERATE_MODELS,
6244
6342
  MODEL_REGISTRY,
6343
+ resolveConcurrency,
6245
6344
  BackendClient2 as BackendClient,
6246
6345
  Engine2 as Engine,
6247
6346
  LocalAssetStore2 as LocalAssetStore,
@@ -6252,4 +6351,4 @@ export {
6252
6351
  defaultRegistry,
6253
6352
  createEngineFromEnv
6254
6353
  };
6255
- //# sourceMappingURL=chunk-26K7V346.js.map
6354
+ //# sourceMappingURL=chunk-7K2YAWUT.js.map