@koda-sl/baker-cli 0.306.3-dev.bb28562ac → 0.306.6

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
@@ -67,7 +67,7 @@ import {
67
67
  validateCanvasDeep,
68
68
  withoutCastName,
69
69
  ytDlpBlockSignal
70
- } from "./chunk-3TEVIAAV.js";
70
+ } from "./chunk-WONWYE7B.js";
71
71
  import {
72
72
  csvOrJson,
73
73
  daysAgoIso,
@@ -76,7 +76,7 @@ import {
76
76
  resolveAccountIdArg,
77
77
  resolveEffectiveStatus,
78
78
  todayIso
79
- } from "./chunk-4YF56EGK.js";
79
+ } from "./chunk-KMQHIAYG.js";
80
80
  import {
81
81
  buildQueryCacheKey,
82
82
  cacheGet,
@@ -92,32 +92,32 @@ import {
92
92
  writeAdsJson,
93
93
  writeAdsOutput,
94
94
  writeJsonEnvelope
95
- } from "./chunk-A2VZOTCV.js";
95
+ } from "./chunk-AEU5X443.js";
96
96
  import {
97
97
  ApiError,
98
98
  apiGet,
99
99
  apiGetWithHeaders,
100
100
  apiPost,
101
101
  validateConvexId
102
- } from "./chunk-KDTHRRAC.js";
102
+ } from "./chunk-TTIGSGXE.js";
103
103
  import {
104
104
  installStreamTaps,
105
105
  logInvocation
106
- } from "./chunk-2IUN3S3E.js";
106
+ } from "./chunk-FOWT5AS4.js";
107
107
  import {
108
108
  captureBudgetMs,
109
109
  captureProxyCredentials,
110
110
  captureVimeoCookie,
111
- frameVisionKey,
112
111
  getEnv,
113
- hasFrameVisionKey,
112
+ hasOpenRouterKey,
114
113
  isProxyFailure,
114
+ openRouterKey,
115
115
  plannedRoutes,
116
116
  refuseNonPublicUrl,
117
117
  requireChatId,
118
118
  resolveChatId,
119
119
  shouldEscalate
120
- } from "./chunk-WFWU3CHS.js";
120
+ } from "./chunk-UIQJZGBG.js";
121
121
 
122
122
  // src/cli.ts
123
123
  import { defineCommand as defineCommand240, runMain } from "citty";
@@ -23377,10 +23377,10 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
23377
23377
  }
23378
23378
  },
23379
23379
  run: async ({ args }) => {
23380
- const { apiPost: apiPost2 } = await import("./client-6KQHCXS2.js");
23381
- const { requireChatId: requireChatId2 } = await import("./env-C7YXL6C6.js");
23382
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-RBM32FKJ.js");
23383
- const { handleMetaError: handleMetaError2 } = await import("./shared-WJIJTWST.js");
23380
+ const { apiPost: apiPost2 } = await import("./client-QFJHI3FX.js");
23381
+ const { requireChatId: requireChatId2 } = await import("./env-PJSBLG56.js");
23382
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-PAE5S7RI.js");
23383
+ const { handleMetaError: handleMetaError2 } = await import("./shared-J6GGQDIL.js");
23384
23384
  try {
23385
23385
  const accountId = bareAccountId2(args);
23386
23386
  const urlTags = typeof args["url-tags"] === "string" ? args["url-tags"] : void 0;
@@ -23521,10 +23521,10 @@ function duplicateCommand2(entity, label) {
23521
23521
  replace: { type: "boolean", description: "Pause the original once the copy publishes" }
23522
23522
  },
23523
23523
  run: async ({ args }) => {
23524
- const { apiPost: apiPost2 } = await import("./client-6KQHCXS2.js");
23525
- const { requireChatId: requireChatId2 } = await import("./env-C7YXL6C6.js");
23526
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-RBM32FKJ.js");
23527
- const { handleMetaError: handleMetaError2 } = await import("./shared-WJIJTWST.js");
23524
+ const { apiPost: apiPost2 } = await import("./client-QFJHI3FX.js");
23525
+ const { requireChatId: requireChatId2 } = await import("./env-PJSBLG56.js");
23526
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-PAE5S7RI.js");
23527
+ const { handleMetaError: handleMetaError2 } = await import("./shared-J6GGQDIL.js");
23528
23528
  try {
23529
23529
  const accountId = bareAccountId2(args);
23530
23530
  const chatId = requireChatId2();
@@ -30402,7 +30402,7 @@ function rosterHints(roster, statusFilter) {
30402
30402
  `@${mine[0]} was made in this conversation${mine.length > 1 ? ` (with ${mine.slice(1).map((h) => `@${h}`).join(", ")})` : ""} \u2014 that is the avatar this chat is about, and the user chose its face. Use it. The others belong to earlier conversations: only reach for one if the user names it, and never ask which to use when the answer is the one you just cast.`
30403
30403
  ] : [];
30404
30404
  hints2.push(
30405
- "Casting one of these? `baker avatars get <handle>` returns the subject description you copy into the prompt verbatim plus the identity sheet URL for `--reference`. The roster leaves the description out on purpose \u2014 it must never be read half-way."
30405
+ "Casting one of these? Cast them with `--avatar <handle>` on `baker studio generate` / `baker studio animate` \u2014 that flag carries their face AND their accent. `baker avatars get <handle>` returns the written profile behind it; the roster leaves the description out on purpose, because it must never be read half-way."
30406
30406
  );
30407
30407
  const building = roster.filter((avatar) => avatar.status === "generating").map((avatar) => avatar.handle);
30408
30408
  if (building.length > 0) {
@@ -36920,17 +36920,20 @@ import { tmpdir } from "os";
36920
36920
  import path15 from "path";
36921
36921
  import { promisify as promisify2 } from "util";
36922
36922
  import { z as z35 } from "zod";
36923
- var RENDER_REVIEW_MODEL = "gemini-pro-latest";
36923
+ var RENDER_REVIEW_MODEL = "~google/gemini-pro-latest";
36924
36924
  var RENDER_REVIEW_FRAMES = 12;
36925
36925
  var RENDER_REVIEW_WIDTH = 1024;
36926
- var RENDER_WATCH_MODEL = "gemini-3.7-flash";
36926
+ var RENDER_WATCH_MODEL = "~google/gemini-flash-latest";
36927
36927
  function usageFrom(model, body) {
36928
- const meta = body?.usageMetadata;
36929
- if (!meta) return null;
36930
- const inputTokens = meta.promptTokenCount ?? 0;
36931
- const outputTokens = meta.candidatesTokenCount ?? 0;
36932
- if (inputTokens === 0 && outputTokens === 0) return null;
36933
- return { model, inputTokens, outputTokens };
36928
+ const usage = body?.usage;
36929
+ if (!usage) return null;
36930
+ const positive = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
36931
+ return {
36932
+ model,
36933
+ inputTokens: usage.prompt_tokens ?? 0,
36934
+ outputTokens: usage.completion_tokens ?? 0,
36935
+ costUsd: positive(usage.cost) ?? positive(usage.cost_details?.upstream_inference_cost) ?? 0
36936
+ };
36934
36937
  }
36935
36938
  var WATCH_MAX_BYTES = 60 * 1024 * 1024;
36936
36939
  function sceneAt(windows, atS) {
@@ -37233,7 +37236,7 @@ function classifyAudio(volumedetect, hasAudioStream) {
37233
37236
  }
37234
37237
  return findings;
37235
37238
  }
37236
- var RENDER_REVIEW_UNAVAILABLE = "The render was NOT reviewed \u2014 no frame-vision key in this environment. Watch it yourself before sending it on.";
37239
+ var RENDER_REVIEW_UNAVAILABLE = "The render was NOT reviewed \u2014 no model gateway key in this environment. Watch it yourself before sending it on.";
37237
37240
  var REVIEW_CONCURRENCY = 3;
37238
37241
  async function mapWithLimit(items, limit, fn) {
37239
37242
  const out = new Array(items.length);
@@ -37276,64 +37279,63 @@ async function videoDuration(video) {
37276
37279
  return null;
37277
37280
  }
37278
37281
  }
37279
- async function askVideo(video, prompt, spent) {
37280
- const key = frameVisionKey();
37282
+ async function askGateway(model, parts, timeoutMs, spent) {
37283
+ const key = openRouterKey();
37281
37284
  if (!key) return null;
37282
37285
  try {
37283
- const bytes = await readFile11(video);
37284
- if (bytes.byteLength > WATCH_MAX_BYTES) return null;
37285
- const response = await fetch(
37286
- `https://generativelanguage.googleapis.com/v1beta/models/${RENDER_WATCH_MODEL}:generateContent`,
37287
- {
37288
- method: "POST",
37289
- headers: { "content-type": "application/json", "x-goog-api-key": key },
37290
- body: JSON.stringify({
37291
- contents: [
37292
- {
37293
- parts: [{ text: prompt }, { inline_data: { mime_type: "video/mp4", data: bytes.toString("base64") } }]
37294
- }
37295
- ]
37296
- }),
37297
- signal: AbortSignal.timeout(6e5)
37298
- }
37299
- );
37286
+ const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
37287
+ method: "POST",
37288
+ headers: { "content-type": "application/json", authorization: `Bearer ${key}` },
37289
+ body: JSON.stringify({
37290
+ model,
37291
+ messages: [{ role: "user", content: parts }],
37292
+ usage: { include: true }
37293
+ }),
37294
+ signal: AbortSignal.timeout(timeoutMs)
37295
+ });
37300
37296
  if (!response.ok) return null;
37301
37297
  const body = await response.json();
37302
- const usage = usageFrom(RENDER_WATCH_MODEL, body);
37298
+ const usage = usageFrom(model, body);
37303
37299
  if (usage) spent(usage);
37304
- const text2 = body.candidates?.[0]?.content?.parts?.map((p) => p.text ?? "").join("");
37300
+ const text2 = body.choices?.[0]?.message?.content;
37305
37301
  return text2 ? parseReviewJson(text2) : null;
37306
37302
  } catch {
37307
37303
  return null;
37308
37304
  }
37309
37305
  }
37310
- async function ask(images, prompt, spent) {
37311
- const key = frameVisionKey();
37312
- if (!key) return null;
37313
- const parts = [{ text: prompt }];
37314
- for (const b64 of images) parts.push({ inline_data: { mime_type: "image/jpeg", data: b64 } });
37306
+ async function askVideo(video, prompt, spent) {
37307
+ let bytes;
37315
37308
  try {
37316
- const response = await fetch(
37317
- `https://generativelanguage.googleapis.com/v1beta/models/${RENDER_REVIEW_MODEL}:generateContent`,
37318
- {
37319
- method: "POST",
37320
- headers: { "content-type": "application/json", "x-goog-api-key": key },
37321
- body: JSON.stringify({ contents: [{ parts }] }),
37322
- signal: AbortSignal.timeout(18e4)
37323
- }
37324
- );
37325
- if (!response.ok) return null;
37326
- const body = await response.json();
37327
- const usage = usageFrom(RENDER_REVIEW_MODEL, body);
37328
- if (usage) spent(usage);
37329
- const text2 = body.candidates?.[0]?.content?.parts?.map((p) => p.text ?? "").join("");
37330
- return text2 ? parseReviewJson(text2) : null;
37309
+ bytes = await readFile11(video);
37331
37310
  } catch {
37332
37311
  return null;
37333
37312
  }
37313
+ if (bytes.byteLength > WATCH_MAX_BYTES) return null;
37314
+ return await askGateway(
37315
+ RENDER_WATCH_MODEL,
37316
+ [
37317
+ { type: "text", text: prompt },
37318
+ { type: "video_url", video_url: { url: `data:video/mp4;base64,${bytes.toString("base64")}` } }
37319
+ ],
37320
+ 6e5,
37321
+ spent
37322
+ );
37323
+ }
37324
+ async function ask(images, prompt, spent) {
37325
+ return await askGateway(
37326
+ RENDER_REVIEW_MODEL,
37327
+ [
37328
+ { type: "text", text: prompt },
37329
+ ...images.map(
37330
+ (b64) => ({ type: "image_url", image_url: { url: `data:image/jpeg;base64,${b64}` } })
37331
+ )
37332
+ ],
37333
+ 18e4,
37334
+ spent
37335
+ );
37334
37336
  }
37335
37337
  async function reviewRenderedVideo(opts) {
37336
- if (!hasFrameVisionKey()) return null;
37338
+ if (!hasOpenRouterKey()) return null;
37337
37339
  const duration = await videoDuration(opts.video);
37338
37340
  if (duration === null) return null;
37339
37341
  const spent = (usage) => opts.onSpend?.(usage);
@@ -37815,6 +37817,12 @@ ${describeRewrites(healed.rewrites)}
37815
37817
  }
37816
37818
  if (e instanceof RunAbortedError && e.reason === "checkpoint") {
37817
37819
  await clearRunMarker(outputsDir, filePath);
37820
+ if (poster) {
37821
+ await poster.flush({
37822
+ ...failedPayload(`Stopped before the clips, waiting on a decision. Resume with --run-id ${runId}.`),
37823
+ interrupted: true
37824
+ });
37825
+ }
37818
37826
  const frames = frameUrls;
37819
37827
  process.stdout.write(
37820
37828
  `${JSON.stringify(
@@ -39335,6 +39343,13 @@ function beatToCamera(spec, beat, isClosingCard) {
39335
39343
  if (beat.on_camera !== void 0) return beat.on_camera;
39336
39344
  return Boolean(spec.cast) && beat.cast !== false;
39337
39345
  }
39346
+ function beatsIntroducingSpeakerOffCamera(spec) {
39347
+ const closesOnCard = endCardWanted(spec);
39348
+ const last = spec.beats.length - 1;
39349
+ return spec.beats.flatMap(
39350
+ (beat, i) => lineIntroducesSpeaker(beat.say) && !beatToCamera(spec, beat, closesOnCard && i === last) ? [i] : []
39351
+ );
39352
+ }
39338
39353
  function wantsNativeSpeech(spec, closesOnCard) {
39339
39354
  if (!spec.cast || spec.voiceover === false) return false;
39340
39355
  const last = spec.beats.length - 1;
@@ -39453,7 +39468,7 @@ function beatsTooShortToPlay(spec) {
39453
39468
  }
39454
39469
 
39455
39470
  // src/engine/scaffold/ad-brand-overlay.ts
39456
- var markFallback = (name) => name.trim() ? ` onerror="this.replaceWith(Object.assign(document.createElement('span'),{className:this.className+' brand-wordmark',textContent:${JSON.stringify(name.trim())}}))"` : "";
39471
+ var markFallback = (name) => name.trim() ? ` onerror="this.replaceWith(Object.assign(document.createElement('span'),{className:this.className+' brand-wordmark',textContent:${escapeHtml2(JSON.stringify(name.trim()))}}))"` : "";
39457
39472
  var escapeHtml2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
39458
39473
  function firstHex(palette) {
39459
39474
  const hex = (palette ?? []).find((h) => /^#?[0-9a-fA-F]{6}$/.test(h.trim()));
@@ -39822,7 +39837,12 @@ var scaffoldAdCommand = defineCommand111({
39822
39837
  error: {
39823
39838
  code: "VALIDATION_ERROR",
39824
39839
  message: "This ad names a recurring place but gives no picture of it. A location sheet is fused from a real image; with nothing to fuse there is no sheet, and every shot invents the room again.",
39825
- fix: `Make the plate once, then pass it: \`baker images generate "${spec.data.set.description}" --model openai/gpt-image-2\`, then put its url in the spec as \`"set": { "description": "\u2026", "image": "<url>" }\`. Drop \`set\` entirely if the ad does not keep returning to one place.`
39840
+ // Names `studio generate`, which is the command that exists — `baker images`
39841
+ // finds and ingests pictures, it does not make them, so the line this used to
39842
+ // print could not be run. No `--model` either: the lane already defaults to the
39843
+ // curated image model, and the one named here was the legacy id the picker now
39844
+ // offers to nobody.
39845
+ fix: `Make the plate once, then pass it: \`baker studio generate "${spec.data.set.description}"\`, then put its url in the spec as \`"set": { "description": "\u2026", "image": "<url>" }\`. Drop \`set\` entirely if the ad does not keep returning to one place.`
39826
39846
  }
39827
39847
  });
39828
39848
  process.exit(1);
@@ -39884,14 +39904,15 @@ var scaffoldAdCommand = defineCommand111({
39884
39904
  ...filledPalette ? { palette: filledPalette } : {}
39885
39905
  };
39886
39906
  }
39887
- const introducesOffCamera = spec.data.beats.map((beat, i) => ({ i, beat })).filter(({ beat }) => beat.on_camera !== true && lineIntroducesSpeaker(beat.say));
39888
- if (introducesOffCamera.length > 0) {
39889
- const first = introducesOffCamera[0];
39907
+ const introducesOffCamera = beatsIntroducingSpeakerOffCamera(spec.data);
39908
+ const firstIntro = introducesOffCamera[0];
39909
+ if (firstIntro !== void 0) {
39910
+ const line = (spec.data.beats[firstIntro]?.say ?? "").trim();
39890
39911
  writeJson({
39891
39912
  ok: false,
39892
39913
  error: {
39893
39914
  code: "VALIDATION_ERROR",
39894
- message: `Beat ${(first?.i ?? 0) + 1} says "${first?.beat.say.trim()}" \u2014 the speaker introducing themselves \u2014 but nobody is on camera saying it. A voice from off screen introducing the person standing in shot reads as a mistake, the same way a moving mouth under someone else's voice does.`,
39915
+ message: `Beat ${firstIntro + 1} says "${line}" \u2014 the speaker introducing themselves \u2014 but nobody is on camera saying it. A voice from off screen introducing the person standing in shot reads as a mistake, the same way a moving mouth under someone else's voice does.`,
39895
39916
  fix: 'Either set `on_camera: true` on that beat so they say it themselves \u2014 which needs the avatar to have their own accent \u2014 or rewrite the line as narration about them rather than by them: "Marta instal\xF3 placas en nueve d\xEDas", not "Hola, soy Marta".'
39896
39917
  }
39897
39918
  });