@koda-sl/baker-cli 0.116.0-dev.3bcc79f9c → 0.116.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
@@ -1,24 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- BackendClient,
4
3
  ELEVENLABS_MAX_MUSIC_LENGTH_MS,
5
4
  IMAGE_GENERATE_MODELS,
6
5
  LayerExecutionError,
7
6
  MODEL_REGISTRY,
8
7
  SEEDANCE_DURATIONS,
9
8
  ValidationError,
10
- collectAssetRefLikes,
11
9
  createEngineFromEnv,
12
10
  defaultRegistry,
13
11
  describeFailureReason,
14
12
  generateCatalog,
15
- isPersistedAssetRef,
16
- requireCredentialsFromEnv,
17
13
  resolveConcurrency,
18
- sha256Hex,
19
- ulid,
20
14
  validateCanvasDeep
21
- } from "./chunk-OCMOQOIJ.js";
15
+ } from "./chunk-ZWMMCLJ4.js";
22
16
 
23
17
  // src/cli.ts
24
18
  import { defineCommand as defineCommand157, runMain } from "citty";
@@ -157,9 +151,9 @@ async function handleResponse(response) {
157
151
  throw new ApiError("INTERNAL_ERROR", "Failed to parse API response as JSON");
158
152
  }
159
153
  }
160
- async function apiGet(path13, params) {
154
+ async function apiGet(path12, params) {
161
155
  const env = getEnv();
162
- const url = new URL(path13, env.BAKER_API_URL);
156
+ const url = new URL(path12, env.BAKER_API_URL);
163
157
  if (params) {
164
158
  const clean = sanitizeParams(params);
165
159
  for (const [key, value] of Object.entries(clean)) {
@@ -184,12 +178,12 @@ async function apiGet(path13, params) {
184
178
  }
185
179
  return handleResponse(response);
186
180
  }
187
- async function apiPost(path13, body, opts) {
181
+ async function apiPost(path12, body, opts) {
188
182
  const env = getEnv();
189
183
  const timeoutMs = opts?.timeoutMs ?? 6e4;
190
184
  let response;
191
185
  try {
192
- response = await fetchWithRateLimitRetry(new URL(path13, env.BAKER_API_URL).toString(), {
186
+ response = await fetchWithRateLimitRetry(new URL(path12, env.BAKER_API_URL).toString(), {
193
187
  method: "POST",
194
188
  headers: {
195
189
  Authorization: `Bearer ${env.BAKER_API_KEY}`,
@@ -994,9 +988,58 @@ var LINKEDIN_LIMITS = {
994
988
  questionsSoftMax: 4,
995
989
  // playbook: >4 fields tanks completion rate
996
990
  customQuestionsMax: 3,
997
- thankYouMessageMax: 300
991
+ customQuestionTextMax: 100,
992
+ choiceOptionsMin: 2,
993
+ choiceOptionsMax: 30,
994
+ choiceOptionTextMax: 100,
995
+ thankYouMessageMax: 300,
996
+ privacyPolicyTextMax: 2e3,
997
+ legalDisclaimerMax: 2e3,
998
+ consentsMax: 5,
999
+ // Campaign Manager caps disclosure checkboxes at 5
1000
+ consentTextMax: 500,
1001
+ hiddenFieldsMax: 20,
1002
+ hiddenFieldNameMax: 100,
1003
+ hiddenFieldValueMax: 2e3
998
1004
  }
999
1005
  };
1006
+ var LEAD_FORM_PREDEFINED_FIELDS = [
1007
+ // Contact
1008
+ "FIRST_NAME",
1009
+ "LAST_NAME",
1010
+ "EMAIL",
1011
+ "PHONE_NUMBER",
1012
+ "CITY",
1013
+ "STATE",
1014
+ "COUNTRY",
1015
+ "ZIP_CODE",
1016
+ "WORK_EMAIL",
1017
+ // Work
1018
+ "JOB_TITLE",
1019
+ "JOB_FUNCTION",
1020
+ "SENIORITY",
1021
+ "YEARS_OF_EXPERIENCE",
1022
+ // Company
1023
+ "COMPANY_NAME",
1024
+ "COMPANY_SIZE",
1025
+ "INDUSTRY",
1026
+ // Education
1027
+ "DEGREE",
1028
+ "FIELD_OF_STUDY",
1029
+ "SCHOOL_NAME",
1030
+ "START_DATE",
1031
+ "GRADUATION_DATE",
1032
+ // Demographic
1033
+ "GENDER"
1034
+ ];
1035
+ var LEAD_FORM_QUESTION_TYPES = ["SINGLE_LINE_TEXT", "MULTIPLE_CHOICE"];
1036
+ var LEAD_FORM_CONFIRMATION_CTAS = [
1037
+ "VIEW_COMPANY_WEBSITE",
1038
+ "DOWNLOAD",
1039
+ "LEARN_MORE",
1040
+ "VIEW_NOW",
1041
+ "TRY_NOW"
1042
+ ];
1000
1043
  var CAMPAIGN_TYPES = ["SPONSORED_UPDATES", "TEXT_AD", "SPONSORED_INMAILS", "DYNAMIC"];
1001
1044
  var COST_TYPES = ["CPC", "CPM", "CPV"];
1002
1045
  var OBJECTIVE_TYPES = [
@@ -1463,21 +1506,101 @@ var conversionUpdateSchema = z3.object({
1463
1506
  attributionType: z3.enum(ATTRIBUTION_TYPES).optional(),
1464
1507
  enabled: z3.boolean().optional()
1465
1508
  }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1466
- var leadFormCreateSchema = z3.object({
1467
- name: z3.string().min(1).max(LINKEDIN_LIMITS.leadForm.nameMax),
1468
- headline: z3.string().min(1).max(LINKEDIN_LIMITS.leadForm.headlineMax),
1469
- description: z3.string().max(LINKEDIN_LIMITS.leadForm.descriptionMax).optional(),
1509
+ var LEAD = LINKEDIN_LIMITS.leadForm;
1510
+ var leadFormQuestionSchema = z3.object({
1511
+ name: z3.string().min(1).max(LEAD.customQuestionTextMax),
1512
+ predefinedField: z3.enum(LEAD_FORM_PREDEFINED_FIELDS).optional(),
1513
+ questionType: z3.enum(LEAD_FORM_QUESTION_TYPES).optional(),
1514
+ options: z3.array(z3.string().min(1).max(LEAD.choiceOptionTextMax)).optional()
1515
+ }).superRefine((q, ctx) => {
1516
+ if (q.predefinedField && q.questionType) {
1517
+ ctx.addIssue({
1518
+ code: "custom",
1519
+ path: ["questionType"],
1520
+ message: "a question is either a predefinedField or a custom questionType, not both"
1521
+ });
1522
+ }
1523
+ if (q.questionType === "MULTIPLE_CHOICE") {
1524
+ const count = q.options?.length ?? 0;
1525
+ if (count < LEAD.choiceOptionsMin || count > LEAD.choiceOptionsMax) {
1526
+ ctx.addIssue({
1527
+ code: "custom",
1528
+ path: ["options"],
1529
+ message: `MULTIPLE_CHOICE needs ${LEAD.choiceOptionsMin}-${LEAD.choiceOptionsMax} options`
1530
+ });
1531
+ }
1532
+ }
1533
+ if (q.questionType !== "MULTIPLE_CHOICE" && q.options?.length) {
1534
+ ctx.addIssue({
1535
+ code: "custom",
1536
+ path: ["options"],
1537
+ message: "options are only valid on MULTIPLE_CHOICE questions"
1538
+ });
1539
+ }
1540
+ });
1541
+ var leadFormConsentSchema = z3.object({
1542
+ text: z3.string().min(1).max(LEAD.consentTextMax),
1543
+ required: z3.boolean().default(false)
1544
+ });
1545
+ var leadFormHiddenFieldSchema = z3.object({
1546
+ name: z3.string().min(1).max(LEAD.hiddenFieldNameMax),
1547
+ value: z3.string().max(LEAD.hiddenFieldValueMax)
1548
+ });
1549
+ var leadFormThankYouSchema = z3.object({
1550
+ message: z3.string().max(LEAD.thankYouMessageMax).optional(),
1551
+ cta: z3.enum(LEAD_FORM_CONFIRMATION_CTAS).optional(),
1552
+ landingUrl: httpsUrlSchema.optional(),
1553
+ /** "Book an appointment" — a Calendly / Chili Piper scheduling link, in place of a landing page. */
1554
+ appointmentUrl: httpsUrlSchema.optional()
1555
+ }).superRefine((t, ctx) => {
1556
+ if (t.landingUrl && t.appointmentUrl) {
1557
+ ctx.addIssue({
1558
+ code: "custom",
1559
+ path: ["appointmentUrl"],
1560
+ message: "link the confirmation to a landingUrl OR an appointmentUrl, not both"
1561
+ });
1562
+ }
1563
+ if (t.cta && !t.landingUrl) {
1564
+ ctx.addIssue({ code: "custom", path: ["cta"], message: "a confirmation cta needs a landingUrl" });
1565
+ }
1566
+ });
1567
+ var leadFormFields = {
1568
+ name: z3.string().min(1).max(LEAD.nameMax),
1569
+ headline: z3.string().min(1).max(LEAD.headlineMax),
1570
+ description: z3.string().max(LEAD.descriptionMax).optional(),
1571
+ /** Form banner. Baker library id (uploaded at publish) or an image URN already on LinkedIn. */
1572
+ formImageId: bakerMediaIdSchema.optional(),
1573
+ formImageUrn: z3.string().regex(IMAGE_URN_REGEX).optional(),
1574
+ /** Form language, e.g. { country: "US", language: "en" }. Defaults to the account locale on LinkedIn. */
1575
+ locale: z3.object({ country: z3.string().length(2), language: z3.string().length(2) }).optional(),
1470
1576
  privacyPolicyUrl: httpsUrlSchema,
1471
- questions: z3.array(z3.object({ name: z3.string().min(1), predefinedField: z3.string().optional() })).min(1).max(LINKEDIN_LIMITS.leadForm.questionsMax),
1472
- thankYou: z3.object({
1473
- message: z3.string().max(LINKEDIN_LIMITS.leadForm.thankYouMessageMax).optional(),
1474
- landingUrl: httpsUrlSchema.optional()
1475
- }).optional(),
1476
- legalDisclaimer: z3.string().optional(),
1577
+ privacyPolicyText: z3.string().max(LEAD.privacyPolicyTextMax).optional(),
1578
+ questions: z3.array(leadFormQuestionSchema).min(1).max(LEAD.questionsMax),
1579
+ consents: z3.array(leadFormConsentSchema).max(LEAD.consentsMax).optional(),
1580
+ hiddenFields: z3.array(leadFormHiddenFieldSchema).max(LEAD.hiddenFieldsMax).optional(),
1581
+ thankYou: leadFormThankYouSchema.optional(),
1582
+ legalDisclaimer: z3.string().max(LEAD.legalDisclaimerMax).optional(),
1477
1583
  raw: z3.record(z3.string(), z3.unknown()).optional()
1584
+ };
1585
+ var leadFormCreateSchema = z3.object(leadFormFields).superRefine((p, ctx) => {
1586
+ if (p.formImageId && p.formImageUrn) {
1587
+ ctx.addIssue({
1588
+ code: "custom",
1589
+ path: ["formImageId"],
1590
+ message: "provide at most one of formImageId (Baker library) or formImageUrn (already on LinkedIn)"
1591
+ });
1592
+ }
1593
+ const customCount = p.questions.filter((q) => q.questionType).length;
1594
+ if (customCount > LEAD.customQuestionsMax) {
1595
+ ctx.addIssue({
1596
+ code: "custom",
1597
+ path: ["questions"],
1598
+ message: `at most ${LEAD.customQuestionsMax} custom questions (LinkedIn limit); the rest must be predefined profile fields`
1599
+ });
1600
+ }
1478
1601
  });
1479
1602
  var leadFormUpdateSchema = z3.object({
1480
- name: leadFormCreateSchema.shape.name.optional(),
1603
+ name: leadFormFields.name.optional(),
1481
1604
  raw: z3.record(z3.string(), z3.unknown()).optional()
1482
1605
  }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1483
1606
  var LINKEDIN_DRAFT_OP_KINDS = [
@@ -2881,31 +3004,31 @@ function cachePath(category, key) {
2881
3004
  return join2(dir, `${hashKey(key)}.json`);
2882
3005
  }
2883
3006
  function cacheGet(category, key) {
2884
- const path13 = cachePath(category, key);
2885
- if (!existsSync2(path13)) {
3007
+ const path12 = cachePath(category, key);
3008
+ if (!existsSync2(path12)) {
2886
3009
  return null;
2887
3010
  }
2888
3011
  try {
2889
- const raw = readFileSync2(path13, "utf-8");
3012
+ const raw = readFileSync2(path12, "utf-8");
2890
3013
  const entry = JSON.parse(raw);
2891
3014
  if (entry.expiresAt < Date.now()) {
2892
- rmSync(path13, { force: true });
3015
+ rmSync(path12, { force: true });
2893
3016
  return null;
2894
3017
  }
2895
3018
  return entry;
2896
3019
  } catch {
2897
- rmSync(path13, { force: true });
3020
+ rmSync(path12, { force: true });
2898
3021
  return null;
2899
3022
  }
2900
3023
  }
2901
3024
  function cacheSet(category, key, data, ttlMs, fields) {
2902
- const path13 = cachePath(category, key);
3025
+ const path12 = cachePath(category, key);
2903
3026
  const entry = {
2904
3027
  expiresAt: Date.now() + ttlMs,
2905
3028
  data,
2906
3029
  fields
2907
3030
  };
2908
- writeFileSync(path13, JSON.stringify(entry), "utf-8");
3031
+ writeFileSync(path12, JSON.stringify(entry), "utf-8");
2909
3032
  }
2910
3033
  var HOUR = 60 * 60 * 1e3;
2911
3034
  var MINUTE = 60 * 1e3;
@@ -4818,11 +4941,11 @@ function rawTextEntries(value) {
4818
4941
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
4819
4942
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
4820
4943
  }
4821
- function rawFileEntries(path13) {
4822
- if (typeof path13 !== "string" || path13.length === 0) {
4944
+ function rawFileEntries(path12) {
4945
+ if (typeof path12 !== "string" || path12.length === 0) {
4823
4946
  return [];
4824
4947
  }
4825
- return readFileSync3(path13, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4948
+ return readFileSync3(path12, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4826
4949
  }
4827
4950
  function keywordEntries(args) {
4828
4951
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -4845,19 +4968,19 @@ function keywordEntries(args) {
4845
4968
  }
4846
4969
  return entries;
4847
4970
  }
4848
- function loadJsonFileArg(path13) {
4849
- if (typeof path13 !== "string" || path13.length === 0) {
4971
+ function loadJsonFileArg(path12) {
4972
+ if (typeof path12 !== "string" || path12.length === 0) {
4850
4973
  return {};
4851
4974
  }
4852
4975
  try {
4853
- const parsed = JSON.parse(readFileSync3(path13, "utf8"));
4976
+ const parsed = JSON.parse(readFileSync3(path12, "utf8"));
4854
4977
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
4855
- failWriteValidation(`${path13} must contain a JSON object`);
4978
+ failWriteValidation(`${path12} must contain a JSON object`);
4856
4979
  }
4857
4980
  return parsed;
4858
4981
  } catch (err) {
4859
4982
  if (err instanceof SyntaxError) {
4860
- failWriteValidation(`${path13} is not valid JSON: ${err.message}`);
4983
+ failWriteValidation(`${path12} is not valid JSON: ${err.message}`);
4861
4984
  }
4862
4985
  throw err;
4863
4986
  }
@@ -4946,10 +5069,10 @@ async function stageUpdate(kind, customerId, target, payload) {
4946
5069
  async function stageTarget(kind, customerId, target) {
4947
5070
  await stageGoogleOp({ kind, customerId, target });
4948
5071
  }
4949
- async function draftAction(path13, body) {
5072
+ async function draftAction(path12, body) {
4950
5073
  try {
4951
5074
  const chatId = requireChatId();
4952
- const response = await apiPost(path13, { chatId, ...body });
5075
+ const response = await apiPost(path12, { chatId, ...body });
4953
5076
  writeJsonEnvelope(response);
4954
5077
  } catch (err) {
4955
5078
  handleGoogleError(err);
@@ -8490,19 +8613,19 @@ function failWriteValidation2(message) {
8490
8613
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
8491
8614
  process.exit(1);
8492
8615
  }
8493
- function loadJsonFileArg2(path13) {
8494
- if (typeof path13 !== "string" || path13.length === 0) {
8616
+ function loadJsonFileArg2(path12) {
8617
+ if (typeof path12 !== "string" || path12.length === 0) {
8495
8618
  return {};
8496
8619
  }
8497
8620
  try {
8498
- const parsed = JSON.parse(readFileSync7(path13, "utf8"));
8621
+ const parsed = JSON.parse(readFileSync7(path12, "utf8"));
8499
8622
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
8500
- failWriteValidation2(`${path13} must contain a JSON object`);
8623
+ failWriteValidation2(`${path12} must contain a JSON object`);
8501
8624
  }
8502
8625
  return parsed;
8503
8626
  } catch (err) {
8504
8627
  if (err instanceof SyntaxError) {
8505
- failWriteValidation2(`${path13} is not valid JSON: ${err.message}`);
8628
+ failWriteValidation2(`${path12} is not valid JSON: ${err.message}`);
8506
8629
  }
8507
8630
  throw err;
8508
8631
  }
@@ -8565,15 +8688,15 @@ function parseLocaleFlag(value) {
8565
8688
  }
8566
8689
  return { language: match[1], country: match[2].toUpperCase() };
8567
8690
  }
8568
- function loadTargetingFileArg(path13) {
8569
- if (typeof path13 !== "string" || path13.length === 0) {
8691
+ function loadTargetingFileArg(path12) {
8692
+ if (typeof path12 !== "string" || path12.length === 0) {
8570
8693
  return void 0;
8571
8694
  }
8572
- const parsed = loadJsonFileArg2(path13);
8695
+ const parsed = loadJsonFileArg2(path12);
8573
8696
  const criteria = parsed.targetingCriteria ?? parsed;
8574
8697
  if (!criteria.include) {
8575
8698
  failWriteValidation2(
8576
- `${path13} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
8699
+ `${path12} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
8577
8700
  );
8578
8701
  }
8579
8702
  return criteria;
@@ -8608,14 +8731,14 @@ function parseCsvLine(line) {
8608
8731
  cells.push(current);
8609
8732
  return cells.map((cell) => cell.trim());
8610
8733
  }
8611
- function parseListFileArg(path13, maxRows) {
8612
- if (typeof path13 !== "string" || path13.length === 0) {
8734
+ function parseListFileArg(path12, maxRows) {
8735
+ if (typeof path12 !== "string" || path12.length === 0) {
8613
8736
  return void 0;
8614
8737
  }
8615
- const raw = readFileSync7(path13, "utf8");
8738
+ const raw = readFileSync7(path12, "utf8");
8616
8739
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
8617
8740
  if (lines.length < 2) {
8618
- failWriteValidation2(`${path13} needs a header row and at least one data row`);
8741
+ failWriteValidation2(`${path12} needs a header row and at least one data row`);
8619
8742
  }
8620
8743
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
8621
8744
  const rows = [];
@@ -8634,7 +8757,7 @@ function parseListFileArg(path13, maxRows) {
8634
8757
  }
8635
8758
  }
8636
8759
  if (rows.length > maxRows) {
8637
- failWriteValidation2(`${path13} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
8760
+ failWriteValidation2(`${path12} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
8638
8761
  }
8639
8762
  return { columns, rows };
8640
8763
  }
@@ -9268,7 +9391,10 @@ var leadFormsCreateCommand = defineCommand38({
9268
9391
  meta: {
9269
9392
  name: "create",
9270
9393
  description: `Stage a new Lead Gen Form from a JSON file. ${STAGED_NOTE}
9271
- The file needs: name, headline (\u226460), privacyPolicyUrl, questions[] (\u226412; playbook: \u22644 for completion). Example: baker ads linkedin lead-forms create --file form.json`
9394
+ Required: name, headline (\u226460), privacyPolicyUrl, questions[] (\u226412; playbook: \u22644 for completion).
9395
+ Each question is a predefined profile field ({ name, predefinedField: "EMAIL" }) or a custom question ({ name, questionType: "MULTIPLE_CHOICE", options: [...] }; \u22643 custom).
9396
+ Best-practice fields the preview will nudge for if missing: 1-3 qualifying questions, consents[] (disclosure checkboxes), thankYou.message + thankYou.landingUrl|appointmentUrl.
9397
+ Also supported: locale, formImageId|formImageUrn, privacyPolicyText, hiddenFields[], legalDisclaimer, thankYou.cta. Example: baker ads linkedin lead-forms create --file form.json`
9272
9398
  },
9273
9399
  args: {
9274
9400
  ...accountArgs,
@@ -12837,7 +12963,7 @@ async function probeDuration(filePath) {
12837
12963
 
12838
12964
  // src/commands/canvas/run.ts
12839
12965
  import { readFile as readFile2 } from "fs/promises";
12840
- import path5 from "path";
12966
+ import path4 from "path";
12841
12967
  import { defineCommand as defineCommand85 } from "citty";
12842
12968
 
12843
12969
  // src/commands/canvas/placeholders.ts
@@ -12881,102 +13007,9 @@ function isResolvableRelative(value) {
12881
13007
  return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path2.isAbsolute(value);
12882
13008
  }
12883
13009
 
12884
- // src/commands/canvas/run-record.ts
12885
- import path3 from "path";
12886
- var MAX_RUN_NODES = 200;
12887
- var MAX_OUTPUTS_PER_NODE = 10;
12888
- var MAX_FINAL_OUTPUTS = 10;
12889
- var MAX_CREATIVE_SLUG_LENGTH = 100;
12890
- function creativeSlugFromCanvasPath(filePath) {
12891
- const normalized = filePath.split(path3.sep).join("/");
12892
- const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
12893
- const slug = match?.[1] ?? null;
12894
- return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
12895
- }
12896
- var OUTPUT_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
12897
- function toRecordOutput(slot, value) {
12898
- const refs = collectAssetRefLikes(value);
12899
- const ref = refs.length === 1 ? refs[0] : null;
12900
- if (!ref || !isPersistedAssetRef(ref)) return null;
12901
- const kind = typeof ref.kind === "string" && OUTPUT_KINDS.has(ref.kind) ? ref.kind : null;
12902
- if (!kind) return null;
12903
- return {
12904
- slot,
12905
- kind,
12906
- sha256: ref.sha256,
12907
- url: ref.url,
12908
- mime: ref.mime,
12909
- width: typeof ref.width === "number" ? ref.width : void 0,
12910
- height: typeof ref.height === "number" ? ref.height : void 0,
12911
- durationMs: typeof ref.duration_ms === "number" ? ref.duration_ms : void 0
12912
- };
12913
- }
12914
- function nodeOutputsToRecord(nodeOutputs) {
12915
- const out = [];
12916
- for (const [slot, value] of Object.entries(nodeOutputs)) {
12917
- if (Array.isArray(value)) {
12918
- value.forEach((item, i) => {
12919
- const rec = toRecordOutput(`${slot}#${i}`, item);
12920
- if (rec) out.push(rec);
12921
- });
12922
- } else {
12923
- const rec = toRecordOutput(slot, value);
12924
- if (rec) out.push(rec);
12925
- }
12926
- }
12927
- return out.slice(0, MAX_OUTPUTS_PER_NODE);
12928
- }
12929
- function finalOutputsToRecord(output) {
12930
- if (Array.isArray(output)) {
12931
- return output.map((item, i) => toRecordOutput(`final#${i}`, item)).filter((rec2) => rec2 !== null).slice(0, MAX_FINAL_OUTPUTS);
12932
- }
12933
- const rec = toRecordOutput("final", output);
12934
- return rec ? [rec] : [];
12935
- }
12936
- function buildRunRecord(result, meta) {
12937
- const nodes = result.node_runs.slice(0, MAX_RUN_NODES).map((run) => ({
12938
- nodeId: run.node_id,
12939
- nodeType: run.node_type,
12940
- cached: run.cached,
12941
- credits: run.credits,
12942
- durationMs: run.duration_ms,
12943
- outputs: nodeOutputsToRecord(result.outputs_by_node[run.node_id] ?? {})
12944
- }));
12945
- const finalOutputs = finalOutputsToRecord(result.output);
12946
- return {
12947
- runId: result.run_id,
12948
- creativeSlug: meta.creativeSlug,
12949
- canvasPath: meta.canvasPath,
12950
- canvasSha: meta.canvasSha,
12951
- chatId: meta.chatId,
12952
- status: "completed",
12953
- stats: {
12954
- totalNodes: result.stats.total_nodes,
12955
- cachedNodes: result.stats.cached_nodes,
12956
- totalCredits: result.stats.total_credits,
12957
- durationMs: result.stats.duration_ms
12958
- },
12959
- nodes,
12960
- finalOutputs: finalOutputs.length > 0 ? finalOutputs : void 0
12961
- };
12962
- }
12963
- function buildFailedRunRecord(runId, errorMessage, meta) {
12964
- return {
12965
- runId,
12966
- creativeSlug: meta.creativeSlug,
12967
- canvasPath: meta.canvasPath,
12968
- canvasSha: meta.canvasSha,
12969
- chatId: meta.chatId,
12970
- status: "failed",
12971
- errorMessage: errorMessage.slice(0, 2e3),
12972
- stats: { totalNodes: 0, cachedNodes: 0, totalCredits: 0, durationMs: 0 },
12973
- nodes: []
12974
- };
12975
- }
12976
-
12977
13010
  // src/commands/canvas/run-retention.ts
12978
13011
  import { rm } from "fs/promises";
12979
- import path4 from "path";
13012
+ import path3 from "path";
12980
13013
  function runDirsToPrune(entries, keep, currentRunId) {
12981
13014
  const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
12982
13015
  if (keep <= 0) return runs;
@@ -12993,7 +13026,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
12993
13026
  const toPrune = runDirsToPrune(entries, keep, currentRunId);
12994
13027
  if (toPrune.length === 0) return;
12995
13028
  for (const dir of toPrune) {
12996
- await rm(path4.join(outputsDir, dir), { recursive: true, force: true }).catch(
13029
+ await rm(path3.join(outputsDir, dir), { recursive: true, force: true }).catch(
12997
13030
  (e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
12998
13031
  );
12999
13032
  }
@@ -13020,22 +13053,10 @@ var runCommand = defineCommand85({
13020
13053
  "keep-runs": {
13021
13054
  type: "string",
13022
13055
  description: "After the run, prune old r_* run dirs, keeping the N newest (off by default)"
13023
- },
13024
- "remote-cache": {
13025
- type: "string",
13026
- description: "on | off \u2014 company-scoped remote cache + durable asset persistence (default on; env BAKER_CANVAS_REMOTE_CACHE)"
13027
- },
13028
- // citty consumes any `--no-<flag>` as a negation of `<flag>`, so the
13029
- // opt-out spelling `--no-record` requires the flag to be named `record`
13030
- // (a literal "no-record" arg would never receive a value).
13031
- record: {
13032
- type: "boolean",
13033
- default: true,
13034
- description: "Post the durable run-history record to Baker (disable with --no-record)"
13035
13056
  }
13036
13057
  },
13037
13058
  async run({ args }) {
13038
- const filePath = path5.resolve(String(args.file));
13059
+ const filePath = path4.resolve(String(args.file));
13039
13060
  const raw = await readFile2(filePath, "utf8");
13040
13061
  let parsed;
13041
13062
  try {
@@ -13046,7 +13067,7 @@ var runCommand = defineCommand85({
13046
13067
  `);
13047
13068
  process.exit(2);
13048
13069
  }
13049
- parsed = resolveRelativeCanvasPaths(parsed, path5.dirname(filePath));
13070
+ parsed = resolveRelativeCanvasPaths(parsed, path4.dirname(filePath));
13050
13071
  const pending = unsuppliedPlaceholderAssets(parsed);
13051
13072
  if (pending.length > 0) {
13052
13073
  process.stderr.write(
@@ -13066,26 +13087,16 @@ var runCommand = defineCommand85({
13066
13087
  );
13067
13088
  process.exit(2);
13068
13089
  }
13069
- const remoteCache = args["remote-cache"] !== void 0 ? String(args["remote-cache"]) !== "off" : void 0;
13070
13090
  const engine = createEngineFromEnv({
13071
13091
  cacheDir: args["cache-dir"] ? String(args["cache-dir"]) : void 0,
13072
13092
  outputsDir: args["outputs-dir"] ? String(args["outputs-dir"]) : void 0,
13073
13093
  log: (line) => process.stdout.write(`${line}
13074
- `),
13075
- remoteCache
13094
+ `)
13076
13095
  });
13077
- const runId = args["run-id"] ? String(args["run-id"]) : `r_${ulid()}`;
13078
- const recordMeta = {
13079
- creativeSlug: creativeSlugFromCanvasPath(filePath) ?? void 0,
13080
- canvasPath: path5.relative(process.cwd(), filePath) || void 0,
13081
- canvasSha: sha256Hex(Buffer.from(raw)),
13082
- chatId: getEnv().BAKER_CHAT_ID || void 0
13083
- };
13084
- const record = args.record === false ? null : buildRecorder();
13085
13096
  try {
13086
13097
  const policy = args["cache-policy"] ?? "read_write";
13087
13098
  const result = await engine.run(parsed, {
13088
- run_id: runId,
13099
+ run_id: args["run-id"] ? String(args["run-id"]) : void 0,
13089
13100
  cache_policy: policy,
13090
13101
  concurrency: resolveConcurrency(
13091
13102
  // --concurrency wins; --parallel is the discoverable alias for the same bound.
@@ -13093,10 +13104,9 @@ var runCommand = defineCommand85({
13093
13104
  process.env.BAKER_CANVAS_CONCURRENCY
13094
13105
  )
13095
13106
  });
13096
- if (record) await record(buildRunRecord(result, recordMeta));
13097
13107
  const keepRuns = args["keep-runs"] !== void 0 ? Number(args["keep-runs"]) : void 0;
13098
13108
  if (keepRuns !== void 0 && Number.isFinite(keepRuns)) {
13099
- const outputsDir = args["outputs-dir"] ? path5.resolve(String(args["outputs-dir"])) : path5.resolve("canvas");
13109
+ const outputsDir = args["outputs-dir"] ? path4.resolve(String(args["outputs-dir"])) : path4.resolve("canvas");
13100
13110
  await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
13101
13111
  `));
13102
13112
  }
@@ -13124,7 +13134,6 @@ var runCommand = defineCommand85({
13124
13134
  }
13125
13135
  if (e instanceof LayerExecutionError) {
13126
13136
  const failures = e.failures.map((f) => ({ node_id: f.nodeId, message: describeFailureReason(f.reason) }));
13127
- if (record) await record(buildFailedRunRecord(runId, e.message, recordMeta));
13128
13137
  process.stderr.write(
13129
13138
  `${JSON.stringify({ ok: false, error: { code: "runtime", message: e.message, failures } }, null, 2)}
13130
13139
  `
@@ -13132,30 +13141,16 @@ var runCommand = defineCommand85({
13132
13141
  process.exit(1);
13133
13142
  }
13134
13143
  const msg = e instanceof Error ? e.message : String(e);
13135
- if (record) await record(buildFailedRunRecord(runId, msg, recordMeta));
13136
13144
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "runtime", message: msg } }, null, 2)}
13137
13145
  `);
13138
13146
  process.exit(1);
13139
13147
  }
13140
13148
  }
13141
13149
  });
13142
- function buildRecorder() {
13143
- return async (payload) => {
13144
- try {
13145
- const creds = requireCredentialsFromEnv();
13146
- const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
13147
- await client.recordRun(payload);
13148
- } catch (e) {
13149
- const msg = e instanceof Error ? e.message : String(e);
13150
- process.stderr.write(`[warn] run record not persisted (${msg})
13151
- `);
13152
- }
13153
- };
13154
- }
13155
13150
 
13156
13151
  // src/commands/canvas/scaffold-static-ad.ts
13157
13152
  import { readFile as readFile3, writeFile } from "fs/promises";
13158
- import path7 from "path";
13153
+ import path6 from "path";
13159
13154
  import { defineCommand as defineCommand86 } from "citty";
13160
13155
 
13161
13156
  // src/engine/scaffold/staticAd.ts
@@ -13342,20 +13337,15 @@ function staticAdReport(input, elementsInput, opts) {
13342
13337
  }
13343
13338
 
13344
13339
  // src/commands/canvas/scaffold-static-ad-paths.ts
13345
- import path6 from "path";
13346
- function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
13340
+ import path5 from "path";
13341
+ function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd()) {
13347
13342
  const file = rawFile.trim();
13348
13343
  const imageIsUrl = /^https?:\/\//i.test(file);
13349
- const imageSource = imageIsUrl ? file : path6.resolve(cwd, file);
13350
- const outPath = out ? path6.resolve(cwd, out) : slug ? path6.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path6.join(cwd, "static-ad.canvas.json") : path6.join(path6.dirname(imageSource), "static-ad.canvas.json");
13351
- const blueprintPath = path6.join(path6.dirname(outPath), "prompt.json");
13344
+ const imageSource = imageIsUrl ? file : path5.resolve(cwd, file);
13345
+ const outPath = out ? path5.resolve(cwd, out) : imageIsUrl ? path5.join(cwd, "static-ad.canvas.json") : path5.join(path5.dirname(imageSource), "static-ad.canvas.json");
13346
+ const blueprintPath = path5.join(path5.dirname(outPath), "prompt.json");
13352
13347
  return { imageIsUrl, imageSource, outPath, blueprintPath };
13353
13348
  }
13354
- var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
13355
- var SCAFFOLD_SLUG_MAX_LENGTH = 100;
13356
- function isValidScaffoldSlug(slug) {
13357
- return slug.length <= SCAFFOLD_SLUG_MAX_LENGTH && SCAFFOLD_SLUG_PATTERN.test(slug);
13358
- }
13359
13349
 
13360
13350
  // src/commands/canvas/scaffold-static-ad.ts
13361
13351
  function resolveModel(kind, preferred) {
@@ -13517,10 +13507,6 @@ var scaffoldStaticAdCommand = defineCommand86({
13517
13507
  file: { type: "positional", required: true, description: "Path or http(s) URL to the source/inspiration image" },
13518
13508
  context: { type: "string", description: "Known provenance (advertiser, category, market) to ground the describe" },
13519
13509
  out: { type: "string", description: "Output canvas path (default <image-dir>/static-ad.canvas.json)" },
13520
- slug: {
13521
- type: "string",
13522
- description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
13523
- },
13524
13510
  "describe-model": { type: "string", description: "Override the image_describe model id" },
13525
13511
  "select-model": { type: "string", description: "Override the text_generate model id for element selection" },
13526
13512
  "layout-model": { type: "string", description: "Override the text_generate model id for the layout pass" },
@@ -13529,19 +13515,9 @@ var scaffoldStaticAdCommand = defineCommand86({
13529
13515
  "skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" }
13530
13516
  },
13531
13517
  async run({ args }) {
13532
- const slug = args.slug ? String(args.slug) : void 0;
13533
- if (slug && !isValidScaffoldSlug(slug)) {
13534
- process.stderr.write(
13535
- `${JSON.stringify({ ok: false, error: { code: "invalid_slug", message: "--slug must be lowercase kebab (a-z, 0-9, hyphens), max 100 chars" } }, null, 2)}
13536
- `
13537
- );
13538
- process.exit(2);
13539
- }
13540
13518
  const { imageIsUrl, imageSource, outPath, blueprintPath } = resolveScaffoldStaticAdPaths(
13541
13519
  String(args.file),
13542
- args.out ? String(args.out) : void 0,
13543
- process.cwd(),
13544
- slug
13520
+ args.out ? String(args.out) : void 0
13545
13521
  );
13546
13522
  const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
13547
13523
  const describeCanvas = buildDescribeCanvas(
@@ -13601,7 +13577,7 @@ var scaffoldStaticAdCommand = defineCommand86({
13601
13577
  run_estimated_credits: validation.estimatedCredits
13602
13578
  },
13603
13579
  checklist: {
13604
- edit_prompt: `Edit ${path7.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
13580
+ edit_prompt: `Edit ${path6.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
13605
13581
  assets_to_supply: report.elements,
13606
13582
  font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path, or delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
13607
13583
  note: "Replace every [TODO] ingest path with a real file, then `baker canvas validate` and `baker canvas run`. Running generates a billed image \u2014 it is not free."
@@ -13617,7 +13593,7 @@ var scaffoldStaticAdCommand = defineCommand86({
13617
13593
 
13618
13594
  // src/commands/canvas/scaffold-video.ts
13619
13595
  import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
13620
- import path10 from "path";
13596
+ import path9 from "path";
13621
13597
  import { defineCommand as defineCommand87 } from "citty";
13622
13598
 
13623
13599
  // src/engine/nodes/local/lib/sceneDetect.ts
@@ -16401,23 +16377,23 @@ function videoReport(input, elementsInput) {
16401
16377
 
16402
16378
  // src/commands/canvas/composition-path.ts
16403
16379
  import { existsSync as existsSync4 } from "fs";
16404
- import path8 from "path";
16380
+ import path7 from "path";
16405
16381
  function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
16406
- const rel = path8.join("canvas", name);
16382
+ const rel = path7.join("canvas", name);
16407
16383
  let dir = startDir;
16408
16384
  for (let i = 0; i < maxDepth; i++) {
16409
- const candidate = path8.join(dir, rel);
16410
- if (exists(path8.join(candidate, "meta.json"))) return candidate;
16411
- const parent = path8.dirname(dir);
16385
+ const candidate = path7.join(dir, rel);
16386
+ if (exists(path7.join(candidate, "meta.json"))) return candidate;
16387
+ const parent = path7.dirname(dir);
16412
16388
  if (parent === dir) break;
16413
16389
  dir = parent;
16414
16390
  }
16415
- return path8.resolve(startDir, "../../../", rel);
16391
+ return path7.resolve(startDir, "../../../", rel);
16416
16392
  }
16417
16393
 
16418
16394
  // src/commands/canvas/gitignore.ts
16419
16395
  import { appendFile, readFile as readFile5 } from "fs/promises";
16420
- import path9 from "path";
16396
+ import path8 from "path";
16421
16397
  function missingGitignoreEntries(existing, entries) {
16422
16398
  const present = new Set(
16423
16399
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -16425,7 +16401,7 @@ function missingGitignoreEntries(existing, entries) {
16425
16401
  return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
16426
16402
  }
16427
16403
  async function ensureGitignore(dir, entries) {
16428
- const file = path9.join(dir, ".gitignore");
16404
+ const file = path8.join(dir, ".gitignore");
16429
16405
  let existing;
16430
16406
  try {
16431
16407
  existing = await readFile5(file, "utf8");
@@ -16486,7 +16462,7 @@ async function loadTranscriptBestEffort(ref) {
16486
16462
  async function stageCaptions(outDir, transcript) {
16487
16463
  const text = transcript?.trim();
16488
16464
  if (!text || text === "[]") return {};
16489
- const compositionPath = path10.join(outDir, "tiktok-captions-composition");
16465
+ const compositionPath = path9.join(outDir, "tiktok-captions-composition");
16490
16466
  await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
16491
16467
  return { compositionPath };
16492
16468
  }
@@ -16504,10 +16480,10 @@ function patchCompositionHtml(html, dims) {
16504
16480
  return html.replace(/(<meta\s+name="viewport"\s+content="width=)\d+(,\s*height=)\d+(")/i, `$1${dims.w}$2${dims.h}$3`).replace(/(width:\s*)\d+(px;\s*height:\s*)\d+(px;)/i, `$1${dims.w}$2${dims.h}$3`).replace(/(data-width=")\d+(")/i, `$1${dims.w}$2`).replace(/(data-height=")\d+(")/i, `$1${dims.h}$2`);
16505
16481
  }
16506
16482
  async function stampCompositionDims(compositionDir, dims) {
16507
- const metaPath = path10.join(compositionDir, "meta.json");
16483
+ const metaPath = path9.join(compositionDir, "meta.json");
16508
16484
  const rawMeta = await readFile6(metaPath, "utf8");
16509
16485
  await writeFile2(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
16510
- const htmlPath = path10.join(compositionDir, "index.html");
16486
+ const htmlPath = path9.join(compositionDir, "index.html");
16511
16487
  const rawHtml = await readFile6(htmlPath, "utf8");
16512
16488
  await writeFile2(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
16513
16489
  }
@@ -16647,10 +16623,6 @@ var scaffoldVideoCommand = defineCommand87({
16647
16623
  args: {
16648
16624
  file: { type: "positional", required: true, description: "Path to the reference video" },
16649
16625
  out: { type: "string", description: "Output canvas path (default <video-dir>/<name>.video.canvas.json)" },
16650
- slug: {
16651
- type: "string",
16652
- description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
16653
- },
16654
16626
  frames: { type: "string", description: '"generate" (default, anchored regen) or "reuse" (wire real frames in)' },
16655
16627
  ambient: {
16656
16628
  type: "boolean",
@@ -16677,19 +16649,11 @@ var scaffoldVideoCommand = defineCommand87({
16677
16649
  }
16678
16650
  },
16679
16651
  async run({ args }) {
16680
- const videoPath = path10.resolve(String(args.file));
16681
- const base = path10.basename(videoPath, path10.extname(videoPath));
16682
- const slug = args.slug ? String(args.slug) : void 0;
16683
- if (slug && !isValidScaffoldSlug(slug)) {
16684
- process.stderr.write(
16685
- `${JSON.stringify({ ok: false, error: { code: "invalid_slug", message: "--slug must be lowercase kebab (a-z, 0-9, hyphens), max 100 chars" } }, null, 2)}
16686
- `
16687
- );
16688
- process.exit(2);
16689
- }
16690
- const outPath = args.out ? path10.resolve(String(args.out)) : slug ? path10.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path10.join(path10.dirname(videoPath), `${base}.video.canvas.json`);
16691
- const outDir = path10.dirname(outPath);
16692
- const blueprintPath = path10.join(outDir, "prompt.json");
16652
+ const videoPath = path9.resolve(String(args.file));
16653
+ const base = path9.basename(videoPath, path9.extname(videoPath));
16654
+ const outPath = args.out ? path9.resolve(String(args.out)) : path9.join(path9.dirname(videoPath), `${base}.video.canvas.json`);
16655
+ const outDir = path9.dirname(outPath);
16656
+ const blueprintPath = path9.join(outDir, "prompt.json");
16693
16657
  const frames = args.frames === "reuse" ? "reuse" : "generate";
16694
16658
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
16695
16659
  if (Number.isFinite(maxScenes)) {
@@ -16729,10 +16693,10 @@ var scaffoldVideoCommand = defineCommand87({
16729
16693
  `
16730
16694
  );
16731
16695
  }
16732
- const compositionDest = path10.join(outDir, "video-overlay-composition");
16696
+ const compositionDest = path9.join(outDir, "video-overlay-composition");
16733
16697
  await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
16734
16698
  await stampCompositionDims(compositionDest, outDims);
16735
- const indexPath = path10.join(compositionDest, "index.html");
16699
+ const indexPath = path9.join(compositionDest, "index.html");
16736
16700
  const overlayHtml = buildOverlayHtml(blueprint);
16737
16701
  const indexHtml = await readFile6(indexPath, "utf8");
16738
16702
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
@@ -16748,9 +16712,9 @@ var scaffoldVideoCommand = defineCommand87({
16748
16712
  const opts = {
16749
16713
  imageModel,
16750
16714
  videoModel,
16751
- overlayCompositionPath: path10.relative(outDir, compositionDest),
16752
- captionsCompositionPath: captions.compositionPath ? path10.relative(outDir, captions.compositionPath) : void 0,
16753
- blueprintPath: path10.relative(outDir, blueprintPath),
16715
+ overlayCompositionPath: path9.relative(outDir, compositionDest),
16716
+ captionsCompositionPath: captions.compositionPath ? path9.relative(outDir, captions.compositionPath) : void 0,
16717
+ blueprintPath: path9.relative(outDir, blueprintPath),
16754
16718
  frames,
16755
16719
  ambient: Boolean(args.ambient),
16756
16720
  ...args.aspect ? { aspect: String(args.aspect) } : {},
@@ -16792,7 +16756,7 @@ var scaffoldVideoCommand = defineCommand87({
16792
16756
  run_estimated_credits: validation.estimatedCredits
16793
16757
  },
16794
16758
  checklist: {
16795
- edit_prompt: `Edit ${path10.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
16759
+ edit_prompt: `Edit ${path9.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
16796
16760
  recurring_elements_to_supply: report.elements,
16797
16761
  voices_to_confirm: report.dialogue.map((d) => ({
16798
16762
  scene: d.scene,
@@ -16819,7 +16783,7 @@ var scaffoldVideoCommand = defineCommand87({
16819
16783
 
16820
16784
  // src/commands/canvas/set-prompt.ts
16821
16785
  import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
16822
- import path11 from "path";
16786
+ import path10 from "path";
16823
16787
  import { defineCommand as defineCommand88 } from "citty";
16824
16788
  function setNodePrompt(canvas, nodeId, text) {
16825
16789
  const nodes = canvas?.nodes;
@@ -16847,7 +16811,7 @@ var setPromptCommand = defineCommand88({
16847
16811
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
16848
16812
  },
16849
16813
  async run({ args }) {
16850
- const filePath = path11.resolve(String(args.file));
16814
+ const filePath = path10.resolve(String(args.file));
16851
16815
  let canvas;
16852
16816
  try {
16853
16817
  canvas = JSON.parse(await readFile7(filePath, "utf8"));
@@ -16857,7 +16821,7 @@ var setPromptCommand = defineCommand88({
16857
16821
  process.exit(2);
16858
16822
  }
16859
16823
  let text;
16860
- if (args["text-file"]) text = await readFile7(path11.resolve(String(args["text-file"])), "utf8");
16824
+ if (args["text-file"]) text = await readFile7(path10.resolve(String(args["text-file"])), "utf8");
16861
16825
  else if (args.text !== void 0) text = String(args.text);
16862
16826
  else {
16863
16827
  process.stderr.write(
@@ -16878,7 +16842,7 @@ var setPromptCommand = defineCommand88({
16878
16842
  process.exit(2);
16879
16843
  return;
16880
16844
  }
16881
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path11.dirname(filePath)), defaultRegistry());
16845
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path10.dirname(filePath)), defaultRegistry());
16882
16846
  if (!validation.ok) {
16883
16847
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
16884
16848
  `);
@@ -16894,7 +16858,7 @@ var setPromptCommand = defineCommand88({
16894
16858
 
16895
16859
  // src/commands/canvas/validate.ts
16896
16860
  import { readFile as readFile8 } from "fs/promises";
16897
- import path12 from "path";
16861
+ import path11 from "path";
16898
16862
  import { defineCommand as defineCommand89 } from "citty";
16899
16863
  var validateCommand = defineCommand89({
16900
16864
  meta: {
@@ -16903,7 +16867,7 @@ var validateCommand = defineCommand89({
16903
16867
  },
16904
16868
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
16905
16869
  async run({ args }) {
16906
- const filePath = path12.resolve(String(args.file));
16870
+ const filePath = path11.resolve(String(args.file));
16907
16871
  const raw = await readFile8(filePath, "utf8");
16908
16872
  let parsed;
16909
16873
  try {
@@ -16914,7 +16878,7 @@ var validateCommand = defineCommand89({
16914
16878
  `);
16915
16879
  process.exit(2);
16916
16880
  }
16917
- parsed = resolveRelativeCanvasPaths(parsed, path12.dirname(filePath));
16881
+ parsed = resolveRelativeCanvasPaths(parsed, path11.dirname(filePath));
16918
16882
  const result = await validateCanvasDeep(parsed, defaultRegistry());
16919
16883
  if (!result.ok) {
16920
16884
  process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
@@ -17051,16 +17015,6 @@ registerSchema({
17051
17015
  type: "string",
17052
17016
  description: "Optional URL of the original reference ad",
17053
17017
  required: false
17054
- },
17055
- slug: {
17056
- type: "string",
17057
- description: "Creative slug (src/creatives/<slug>/) \u2014 attaches the image to that creative's row",
17058
- required: false
17059
- },
17060
- runId: {
17061
- type: "string",
17062
- description: "Canvas run id (r_\u2026) of the approved generation to pin as published",
17063
- required: false
17064
17018
  }
17065
17019
  }
17066
17020
  });
@@ -17070,13 +17024,6 @@ function detectCreativeContentType(filePath) {
17070
17024
  unsupportedMessage: "Unsupported creative image extension. Use PNG, JPG, or WebP."
17071
17025
  });
17072
17026
  }
17073
- function chatIdFromEnv() {
17074
- try {
17075
- return getEnv().BAKER_CHAT_ID || void 0;
17076
- } catch {
17077
- return void 0;
17078
- }
17079
- }
17080
17027
  function parseOptionalUrl(value) {
17081
17028
  if (value === void 0 || value.trim() === "") {
17082
17029
  return void 0;
@@ -17107,11 +17054,7 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
17107
17054
  return publishImageAsCreative(deps, {
17108
17055
  imageId: upload.imageId,
17109
17056
  title,
17110
- sourceReferenceUrl,
17111
- slug: args.slug,
17112
- runId: args.runId,
17113
- // Attribute the publish to the driving chat (injected by the bridge).
17114
- chatId: chatIdFromEnv()
17057
+ sourceReferenceUrl
17115
17058
  });
17116
17059
  }
17117
17060
  var publishCommand = defineCommand91({
@@ -17127,16 +17070,6 @@ var publishCommand = defineCommand91({
17127
17070
  type: "string",
17128
17071
  description: "Optional URL of the original reference ad",
17129
17072
  required: false
17130
- },
17131
- slug: {
17132
- type: "string",
17133
- description: "Creative slug (src/creatives/<slug>/) \u2014 attaches the image to that creative's row",
17134
- required: false
17135
- },
17136
- runId: {
17137
- type: "string",
17138
- description: "Canvas run id (r_\u2026) of the approved generation to pin as published",
17139
- required: false
17140
17073
  }
17141
17074
  },
17142
17075
  run: async ({ args }) => {
@@ -17155,9 +17088,7 @@ var publishCommand = defineCommand91({
17155
17088
  file,
17156
17089
  title,
17157
17090
  context: args.context,
17158
- sourceReferenceUrl: args.sourceReferenceUrl,
17159
- slug: args.slug,
17160
- runId: args.runId
17091
+ sourceReferenceUrl: args.sourceReferenceUrl
17161
17092
  });
17162
17093
  writeJson({ ok: true, data });
17163
17094
  } catch (err) {
@@ -18055,9 +17986,9 @@ async function readImageBuffer(pathOrUrl) {
18055
17986
  }
18056
17987
  return readFile10(pathOrUrl);
18057
17988
  }
18058
- async function isDirectory(path13) {
17989
+ async function isDirectory(path12) {
18059
17990
  try {
18060
- const s = await stat2(path13);
17991
+ const s = await stat2(path12);
18061
17992
  return s.isDirectory();
18062
17993
  } catch {
18063
17994
  return false;