@koda-sl/baker-cli 0.268.0-dev.1f1c09c80 → 0.270.1-dev.1f1c09c80
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 +8 -0
- package/dist/cli.js +316 -237
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9786,11 +9786,11 @@ function rawTextEntries(value) {
|
|
|
9786
9786
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
9787
9787
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
9788
9788
|
}
|
|
9789
|
-
function rawFileEntries(
|
|
9790
|
-
if (typeof
|
|
9789
|
+
function rawFileEntries(path41) {
|
|
9790
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
9791
9791
|
return [];
|
|
9792
9792
|
}
|
|
9793
|
-
return readFileSync2(
|
|
9793
|
+
return readFileSync2(path41, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
9794
9794
|
}
|
|
9795
9795
|
function keywordEntries(args) {
|
|
9796
9796
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -9813,19 +9813,19 @@ function keywordEntries(args) {
|
|
|
9813
9813
|
}
|
|
9814
9814
|
return entries;
|
|
9815
9815
|
}
|
|
9816
|
-
function loadJsonFileArg(
|
|
9817
|
-
if (typeof
|
|
9816
|
+
function loadJsonFileArg(path41) {
|
|
9817
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
9818
9818
|
return {};
|
|
9819
9819
|
}
|
|
9820
9820
|
try {
|
|
9821
|
-
const parsed = JSON.parse(readFileSync2(
|
|
9821
|
+
const parsed = JSON.parse(readFileSync2(path41, "utf8"));
|
|
9822
9822
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9823
|
-
failWriteValidation(`${
|
|
9823
|
+
failWriteValidation(`${path41} must contain a JSON object`);
|
|
9824
9824
|
}
|
|
9825
9825
|
return parsed;
|
|
9826
9826
|
} catch (err) {
|
|
9827
9827
|
if (err instanceof SyntaxError) {
|
|
9828
|
-
failWriteValidation(`${
|
|
9828
|
+
failWriteValidation(`${path41} is not valid JSON: ${err.message}`);
|
|
9829
9829
|
}
|
|
9830
9830
|
throw err;
|
|
9831
9831
|
}
|
|
@@ -9955,10 +9955,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
|
|
|
9955
9955
|
async function stageTarget(kind, customerId, target, hints) {
|
|
9956
9956
|
await stageGoogleOp({ kind, customerId, target }, hints);
|
|
9957
9957
|
}
|
|
9958
|
-
async function draftAction(
|
|
9958
|
+
async function draftAction(path41, body, chat) {
|
|
9959
9959
|
try {
|
|
9960
9960
|
const chatId = resolveChatId(chat);
|
|
9961
|
-
const response = await apiPost(
|
|
9961
|
+
const response = await apiPost(path41, { chatId, ...body });
|
|
9962
9962
|
writeJsonEnvelope(response);
|
|
9963
9963
|
} catch (err) {
|
|
9964
9964
|
handleGoogleError(err);
|
|
@@ -15172,19 +15172,19 @@ function failWriteValidation2(message) {
|
|
|
15172
15172
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
15173
15173
|
process.exit(1);
|
|
15174
15174
|
}
|
|
15175
|
-
function loadJsonFileArg2(
|
|
15176
|
-
if (typeof
|
|
15175
|
+
function loadJsonFileArg2(path41) {
|
|
15176
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15177
15177
|
return {};
|
|
15178
15178
|
}
|
|
15179
15179
|
try {
|
|
15180
|
-
const parsed = JSON.parse(readFileSync4(
|
|
15180
|
+
const parsed = JSON.parse(readFileSync4(path41, "utf8"));
|
|
15181
15181
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15182
|
-
failWriteValidation2(`${
|
|
15182
|
+
failWriteValidation2(`${path41} must contain a JSON object`);
|
|
15183
15183
|
}
|
|
15184
15184
|
return parsed;
|
|
15185
15185
|
} catch (err) {
|
|
15186
15186
|
if (err instanceof SyntaxError) {
|
|
15187
|
-
failWriteValidation2(`${
|
|
15187
|
+
failWriteValidation2(`${path41} is not valid JSON: ${err.message}`);
|
|
15188
15188
|
}
|
|
15189
15189
|
throw err;
|
|
15190
15190
|
}
|
|
@@ -15269,15 +15269,15 @@ function parseLocaleFlag(value) {
|
|
|
15269
15269
|
}
|
|
15270
15270
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
15271
15271
|
}
|
|
15272
|
-
function loadTargetingFileArg(
|
|
15273
|
-
if (typeof
|
|
15272
|
+
function loadTargetingFileArg(path41) {
|
|
15273
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15274
15274
|
return void 0;
|
|
15275
15275
|
}
|
|
15276
|
-
const parsed = loadJsonFileArg2(
|
|
15276
|
+
const parsed = loadJsonFileArg2(path41);
|
|
15277
15277
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
15278
15278
|
if (!criteria.include) {
|
|
15279
15279
|
failWriteValidation2(
|
|
15280
|
-
`${
|
|
15280
|
+
`${path41} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
15281
15281
|
);
|
|
15282
15282
|
}
|
|
15283
15283
|
return criteria;
|
|
@@ -15312,14 +15312,14 @@ function parseCsvLine(line) {
|
|
|
15312
15312
|
cells.push(current);
|
|
15313
15313
|
return cells.map((cell2) => cell2.trim());
|
|
15314
15314
|
}
|
|
15315
|
-
function parseListFileArg(
|
|
15316
|
-
if (typeof
|
|
15315
|
+
function parseListFileArg(path41, maxRows) {
|
|
15316
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15317
15317
|
return void 0;
|
|
15318
15318
|
}
|
|
15319
|
-
const raw = readFileSync4(
|
|
15319
|
+
const raw = readFileSync4(path41, "utf8");
|
|
15320
15320
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
15321
15321
|
if (lines.length < 2) {
|
|
15322
|
-
failWriteValidation2(`${
|
|
15322
|
+
failWriteValidation2(`${path41} needs a header row and at least one data row`);
|
|
15323
15323
|
}
|
|
15324
15324
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
15325
15325
|
const rows = [];
|
|
@@ -15338,7 +15338,7 @@ function parseListFileArg(path40, maxRows) {
|
|
|
15338
15338
|
}
|
|
15339
15339
|
}
|
|
15340
15340
|
if (rows.length > maxRows) {
|
|
15341
|
-
failWriteValidation2(`${
|
|
15341
|
+
failWriteValidation2(`${path41} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
15342
15342
|
}
|
|
15343
15343
|
return { columns, rows };
|
|
15344
15344
|
}
|
|
@@ -15434,11 +15434,11 @@ function readPositionals(args) {
|
|
|
15434
15434
|
function splitIdList(raw) {
|
|
15435
15435
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
15436
15436
|
}
|
|
15437
|
-
function idsFileEntries(
|
|
15438
|
-
if (typeof
|
|
15437
|
+
function idsFileEntries(path41) {
|
|
15438
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15439
15439
|
return [];
|
|
15440
15440
|
}
|
|
15441
|
-
return readFileSync4(
|
|
15441
|
+
return readFileSync4(path41, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
15442
15442
|
}
|
|
15443
15443
|
function requireTargets(args, entity) {
|
|
15444
15444
|
const positionals = readPositionals(args);
|
|
@@ -18059,9 +18059,9 @@ function compactRow(row) {
|
|
|
18059
18059
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
18060
18060
|
};
|
|
18061
18061
|
}
|
|
18062
|
-
function readPath(row,
|
|
18062
|
+
function readPath(row, path41) {
|
|
18063
18063
|
let current = row;
|
|
18064
|
-
for (const segment of
|
|
18064
|
+
for (const segment of path41.split(".")) {
|
|
18065
18065
|
const record = asRecord2(current);
|
|
18066
18066
|
if (!record) return void 0;
|
|
18067
18067
|
current = record[segment];
|
|
@@ -18071,10 +18071,10 @@ function readPath(row, path40) {
|
|
|
18071
18071
|
function projectFields(rows, paths) {
|
|
18072
18072
|
return rows.map((row) => {
|
|
18073
18073
|
const projected = {};
|
|
18074
|
-
for (const
|
|
18075
|
-
const value = readPath(row,
|
|
18074
|
+
for (const path41 of paths) {
|
|
18075
|
+
const value = readPath(row, path41);
|
|
18076
18076
|
if (value !== void 0) {
|
|
18077
|
-
projected[
|
|
18077
|
+
projected[path41] = value;
|
|
18078
18078
|
}
|
|
18079
18079
|
}
|
|
18080
18080
|
return projected;
|
|
@@ -19374,11 +19374,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
|
|
|
19374
19374
|
function currencyMinimums2(currencyCode) {
|
|
19375
19375
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
19376
19376
|
}
|
|
19377
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
19377
|
+
function validateDailyBudgetFloor(money, ctx, path41) {
|
|
19378
19378
|
if (money?.currencyCode) {
|
|
19379
19379
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
19380
19380
|
if (Number(money.amount) < min) {
|
|
19381
|
-
ctx.addIssue({ code: "custom", path:
|
|
19381
|
+
ctx.addIssue({ code: "custom", path: path41, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
19382
19382
|
}
|
|
19383
19383
|
}
|
|
19384
19384
|
}
|
|
@@ -20047,19 +20047,19 @@ function failWriteValidation3(message) {
|
|
|
20047
20047
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
20048
20048
|
process.exit(1);
|
|
20049
20049
|
}
|
|
20050
|
-
function loadJsonFileArg3(
|
|
20051
|
-
if (typeof
|
|
20050
|
+
function loadJsonFileArg3(path41) {
|
|
20051
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
20052
20052
|
return {};
|
|
20053
20053
|
}
|
|
20054
20054
|
try {
|
|
20055
|
-
const parsed = JSON.parse(readFileSync8(
|
|
20055
|
+
const parsed = JSON.parse(readFileSync8(path41, "utf8"));
|
|
20056
20056
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
20057
|
-
failWriteValidation3(`${
|
|
20057
|
+
failWriteValidation3(`${path41} must contain a JSON object`);
|
|
20058
20058
|
}
|
|
20059
20059
|
return parsed;
|
|
20060
20060
|
} catch (err) {
|
|
20061
20061
|
if (err instanceof SyntaxError) {
|
|
20062
|
-
failWriteValidation3(`${
|
|
20062
|
+
failWriteValidation3(`${path41} is not valid JSON: ${err.message}`);
|
|
20063
20063
|
}
|
|
20064
20064
|
throw err;
|
|
20065
20065
|
}
|
|
@@ -24452,8 +24452,8 @@ import { defineCommand as defineCommand94 } from "citty";
|
|
|
24452
24452
|
import { defineCommand as defineCommand89 } from "citty";
|
|
24453
24453
|
|
|
24454
24454
|
// src/commands/avatars/casting.ts
|
|
24455
|
-
var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate` and `baker
|
|
24456
|
-
var VERBATIM_RULE = "When you do write the description yourself \u2014 a canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
|
|
24455
|
+
var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate`, `baker studio animate` and `baker canvas scaffold-ad`. Do NOT hand-roll it by pasting `subjectDescription` into the prompt or by passing the sheet through `--reference` \u2014 both render something that merely resembles them. `--avatar` is also the ONLY thing that carries their pinned VOICE and how they speak into a clip; without it the video model invents a different voice, and no later step can put theirs back.";
|
|
24456
|
+
var VERBATIM_RULE = "When you do write the description yourself \u2014 a hand-built canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
|
|
24457
24457
|
var VIDEO_ROUTING = "In video, a photoreal presenter renders on `google/veo-3.1` (or `google/veo-3.1-fast`), never `bytedance/seedance-2.0` \u2014 it refuses photoreal human faces, AI-generated ones included. Scaffolding a video creative: `baker canvas scaffold-video \u2026 --real-face`.";
|
|
24458
24458
|
function castingHints(avatar) {
|
|
24459
24459
|
if (avatar.status === "generating") {
|
|
@@ -24668,11 +24668,11 @@ function unwrap(response) {
|
|
|
24668
24668
|
}
|
|
24669
24669
|
return response.data;
|
|
24670
24670
|
}
|
|
24671
|
-
async function readAvatars(
|
|
24672
|
-
return unwrap(await apiGet(
|
|
24671
|
+
async function readAvatars(path41, params) {
|
|
24672
|
+
return unwrap(await apiGet(path41, params));
|
|
24673
24673
|
}
|
|
24674
|
-
async function writeAvatars(
|
|
24675
|
-
return unwrap(await apiPost(
|
|
24674
|
+
async function writeAvatars(path41, body) {
|
|
24675
|
+
return unwrap(await apiPost(path41, body));
|
|
24676
24676
|
}
|
|
24677
24677
|
|
|
24678
24678
|
// src/commands/avatars/create.ts
|
|
@@ -25516,12 +25516,12 @@ function missingFontFiles(urls, available) {
|
|
|
25516
25516
|
function planFontAdoption(sources, families) {
|
|
25517
25517
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
25518
25518
|
const byFamily = /* @__PURE__ */ new Map();
|
|
25519
|
-
for (const { path:
|
|
25520
|
-
const dir = posix.dirname(
|
|
25519
|
+
for (const { path: path41, source } of sources) {
|
|
25520
|
+
const dir = posix.dirname(path41);
|
|
25521
25521
|
for (const face of declaredFontFaces(source)) {
|
|
25522
25522
|
if (!wanted.has(face.family)) continue;
|
|
25523
25523
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
25524
|
-
perFile.set(
|
|
25524
|
+
perFile.set(path41, [...perFile.get(path41) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
25525
25525
|
byFamily.set(face.family, perFile);
|
|
25526
25526
|
}
|
|
25527
25527
|
}
|
|
@@ -30515,10 +30515,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
|
|
|
30515
30515
|
return runs.slice(0, Math.max(0, runs.length - keep));
|
|
30516
30516
|
}
|
|
30517
30517
|
async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
30518
|
-
const { readdir:
|
|
30518
|
+
const { readdir: readdir11 } = await import("fs/promises");
|
|
30519
30519
|
let entries;
|
|
30520
30520
|
try {
|
|
30521
|
-
entries = await
|
|
30521
|
+
entries = await readdir11(outputsDir);
|
|
30522
30522
|
} catch {
|
|
30523
30523
|
return;
|
|
30524
30524
|
}
|
|
@@ -32436,8 +32436,8 @@ var scaffoldStaticAdCommand = defineCommand103({
|
|
|
32436
32436
|
});
|
|
32437
32437
|
|
|
32438
32438
|
// src/commands/canvas/scaffold-ad.ts
|
|
32439
|
-
import { copyFile, cp, mkdir as mkdir7, readFile as
|
|
32440
|
-
import
|
|
32439
|
+
import { copyFile, cp, mkdir as mkdir7, readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
|
|
32440
|
+
import path24 from "path";
|
|
32441
32441
|
import { defineCommand as defineCommand104 } from "citty";
|
|
32442
32442
|
|
|
32443
32443
|
// src/engine/scaffold/ad-spec.ts
|
|
@@ -32805,20 +32805,24 @@ function adBrandOverlayCss() {
|
|
|
32805
32805
|
" viewBox is wider than it is tall rendered clipped \u2014 the first real ad drew",
|
|
32806
32806
|
" `greenlea`. Constraining the height and letting the width follow keeps any",
|
|
32807
32807
|
" aspect ratio whole, and the max-width stops a very wide mark spanning the frame. */",
|
|
32808
|
-
" /*
|
|
32809
|
-
"
|
|
32810
|
-
"
|
|
32811
|
-
"
|
|
32812
|
-
"
|
|
32813
|
-
"
|
|
32814
|
-
"
|
|
32808
|
+
" /* A LIGHT panel behind the mark, always. A brand mark is drawn to sit on white",
|
|
32809
|
+
" \u2014 this one is #1D5D47 on transparent \u2014 so a dark backing hid it on the corner",
|
|
32810
|
+
" and the brand-coloured closing plate erased it completely: the end card came",
|
|
32811
|
+
" out as an empty green frame with a single leaf floating in it. A near-white",
|
|
32812
|
+
" panel is the one background every mark in a brand kit is designed against. */",
|
|
32813
|
+
" .brand-mark { position: absolute; top: 90px; left: 56px; height: 52px; width: auto;",
|
|
32814
|
+
" max-width: 420px; object-fit: contain; padding: 14px 22px; border-radius: 18px;",
|
|
32815
|
+
" background: rgba(255,255,255,0.94);",
|
|
32816
|
+
" filter: drop-shadow(0 3px 10px rgba(0,0,0,0.35)); }",
|
|
32815
32817
|
" .endcard { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);",
|
|
32816
32818
|
" display: flex; flex-direction: column; align-items: center; gap: 56px;",
|
|
32817
32819
|
" width: 100%; max-width: 1080px; padding: 0 60px; text-align: center; }",
|
|
32818
32820
|
" /* Sized to FILL, not merely bounded. Given only max-* constraints an SVG",
|
|
32819
32821
|
" renders at its own intrinsic size, and a small viewBox drew a closing mark",
|
|
32820
32822
|
" a few dozen pixels tall in the middle of a 1080px frame. */",
|
|
32821
|
-
" .endcard-logo { width: 62%; height: auto; max-height:
|
|
32823
|
+
" .endcard-logo { width: 62%; height: auto; max-height: 300px; object-fit: contain;",
|
|
32824
|
+
" padding: 46px 56px; border-radius: 40px; background: rgba(255,255,255,0.96);",
|
|
32825
|
+
" box-shadow: 0 10px 40px rgba(0,0,0,0.28); }",
|
|
32822
32826
|
" /* Capped and wrapping, not sized to its content: measured in a real browser,",
|
|
32823
32827
|
" `GET YOUR FREE QUOTE` renders 948px wide against a 1080px canvas, so the next",
|
|
32824
32828
|
" slightly longer call to action runs off the frame. Two lines beat an edge. */",
|
|
@@ -32841,20 +32845,69 @@ function injectAdBrandOverlay(indexHtml, overlayHtml) {
|
|
|
32841
32845
|
${withCss.slice(rootClose)}`;
|
|
32842
32846
|
}
|
|
32843
32847
|
|
|
32848
|
+
// src/engine/scaffold/ad-brand-source.ts
|
|
32849
|
+
import { readdir as readdir7, readFile as readFile15 } from "fs/promises";
|
|
32850
|
+
import path22 from "path";
|
|
32851
|
+
var BRAND_DIR = "src/brand";
|
|
32852
|
+
function paletteFromBrandDoc(markdown) {
|
|
32853
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32854
|
+
const out = [];
|
|
32855
|
+
for (const match of markdown.matchAll(/#([0-9a-fA-F]{6})\b/g)) {
|
|
32856
|
+
const hex = `#${match[1].toLowerCase()}`;
|
|
32857
|
+
if (seen.has(hex)) continue;
|
|
32858
|
+
seen.add(hex);
|
|
32859
|
+
const r = Number.parseInt(hex.slice(1, 3), 16);
|
|
32860
|
+
const g = Number.parseInt(hex.slice(3, 5), 16);
|
|
32861
|
+
const b = Number.parseInt(hex.slice(5, 7), 16);
|
|
32862
|
+
const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
32863
|
+
if (luma > 236 || luma < 14) continue;
|
|
32864
|
+
out.push(hex);
|
|
32865
|
+
}
|
|
32866
|
+
return out;
|
|
32867
|
+
}
|
|
32868
|
+
function pickBrandMark(files) {
|
|
32869
|
+
const usable = files.filter((f) => /\.(svg|png|webp)$/i.test(f));
|
|
32870
|
+
if (usable.length === 0) return null;
|
|
32871
|
+
const score = (f) => {
|
|
32872
|
+
const name = f.toLowerCase();
|
|
32873
|
+
let s = 0;
|
|
32874
|
+
if (name.endsWith(".svg")) s += 4;
|
|
32875
|
+
if (/\b(logo|wordmark|lockup)\b/.test(name)) s += 2;
|
|
32876
|
+
if (/\b(mono|white|black|inverse|icon|favicon|mark-only|isotipo)\b/.test(name)) s -= 3;
|
|
32877
|
+
return s;
|
|
32878
|
+
};
|
|
32879
|
+
return [...usable].sort((a, b) => score(b) - score(a) || a.localeCompare(b))[0] ?? null;
|
|
32880
|
+
}
|
|
32881
|
+
async function readBrandFromWorkspace(root = ".") {
|
|
32882
|
+
const found = {};
|
|
32883
|
+
const doc = await readFile15(path22.join(root, BRAND_DIR, "BRAND.md"), "utf-8").catch(() => null);
|
|
32884
|
+
if (doc) {
|
|
32885
|
+
const palette = paletteFromBrandDoc(doc);
|
|
32886
|
+
if (palette.length > 0) found.palette = palette;
|
|
32887
|
+
}
|
|
32888
|
+
const dir = path22.join(root, BRAND_DIR, "logos");
|
|
32889
|
+
const files = await readdir7(dir).catch(() => null);
|
|
32890
|
+
if (files) {
|
|
32891
|
+
const mark = pickBrandMark(files);
|
|
32892
|
+
if (mark) found.logo = path22.join(BRAND_DIR, "logos", mark);
|
|
32893
|
+
}
|
|
32894
|
+
return found;
|
|
32895
|
+
}
|
|
32896
|
+
|
|
32844
32897
|
// src/commands/canvas/composition-path.ts
|
|
32845
32898
|
import { existsSync as existsSync4 } from "fs";
|
|
32846
|
-
import
|
|
32899
|
+
import path23 from "path";
|
|
32847
32900
|
function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
|
|
32848
|
-
const rel =
|
|
32901
|
+
const rel = path23.join("canvas", name);
|
|
32849
32902
|
let dir = startDir;
|
|
32850
32903
|
for (let i = 0; i < maxDepth; i++) {
|
|
32851
|
-
const candidate =
|
|
32852
|
-
if (exists(
|
|
32853
|
-
const parent =
|
|
32904
|
+
const candidate = path23.join(dir, rel);
|
|
32905
|
+
if (exists(path23.join(candidate, "meta.json"))) return candidate;
|
|
32906
|
+
const parent = path23.dirname(dir);
|
|
32854
32907
|
if (parent === dir) break;
|
|
32855
32908
|
dir = parent;
|
|
32856
32909
|
}
|
|
32857
|
-
return
|
|
32910
|
+
return path23.resolve(startDir, "../../../", rel);
|
|
32858
32911
|
}
|
|
32859
32912
|
|
|
32860
32913
|
// src/commands/canvas/scaffold-ad.ts
|
|
@@ -32870,6 +32923,11 @@ registerSchema({
|
|
|
32870
32923
|
required: true,
|
|
32871
32924
|
description: 'Path to the ad spec JSON. Shape: { format?, market?, brand?, cast?, end_card?, voice?, music?, beats: [{ say, show, on_camera?, cast? }] }. Fill `brand` from src/brand/BRAND.md \u2014 `palette` (its hex tokens, most important first) and `logo` (the repo path to the mark) are what make the ad look like the client rather than like stock. `market` is where the ad is SET; omitted, it is inferred from the voice language, and getting it wrong is what fills a Spanish ad with British houses. `say` is ONE clause ending in its own punctuation \u2014 it becomes a caption card verbatim, so two sentences in one beat produce a card holding both. `show` is the shot brief for that line. Set `on_camera` ONLY when that beat\'s subject talks to camera. `cast` is WHO the ad is about, and an ad with people in it needs one: `{ "avatar": "marta" }` names a cast avatar (`baker avatars list`) and every beat is grounded on that avatar\'s identity sheet with its subject description copied verbatim \u2014 the same face here as in the rest of the company\'s work. Without it each beat invents its own stranger, which is how one 28-second ad came back with five different men playing one customer. `{ "description": "..." }` is the fallback when there is no avatar, and it scaffolds a canvas that asks you to drop a photo before it can run \u2014 so prefer the avatar. A beat sets `cast: false` for a shot they are not in. `end_card` is on by default whenever `brand` is set: the last beat becomes a flat brand plate with the mark and a call to action drawn over it. `{ "cta": "..." }` sets the button copy, `false` keeps the footage.'
|
|
32872
32925
|
},
|
|
32926
|
+
avatar: {
|
|
32927
|
+
type: "string",
|
|
32928
|
+
required: false,
|
|
32929
|
+
description: "Cast this avatar as the ad's recurring person \u2014 the same flag, the same word and the same meaning as on `baker studio generate` and `baker studio animate`. Every beat they appear in is grounded on their identity sheet and their subject description is copied verbatim, so it is one face throughout and the same face as the rest of the company's work. Overrides `cast.avatar` in the spec. `baker avatars list` shows who there is."
|
|
32930
|
+
},
|
|
32873
32931
|
slug: {
|
|
32874
32932
|
type: "string",
|
|
32875
32933
|
required: false,
|
|
@@ -32889,6 +32947,7 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32889
32947
|
},
|
|
32890
32948
|
args: {
|
|
32891
32949
|
spec: { type: "string", required: true, description: "Path to the ad spec JSON" },
|
|
32950
|
+
avatar: { type: "string", required: false, description: "Cast this avatar as the ad's recurring person \u2014 the same flag, the same word and the same meaning as on `baker studio generate` and `baker studio animate`. Every beat they appear in is grounded on their identity sheet and their subject description is copied verbatim, so it is one face throughout and the same face as the rest of the company's work. Overrides `cast.avatar` in the spec. `baker avatars list` shows who there is." },
|
|
32892
32951
|
slug: {
|
|
32893
32952
|
type: "string",
|
|
32894
32953
|
required: false,
|
|
@@ -32900,7 +32959,7 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32900
32959
|
const specPath = args.spec;
|
|
32901
32960
|
let parsed;
|
|
32902
32961
|
try {
|
|
32903
|
-
parsed = JSON.parse(await
|
|
32962
|
+
parsed = JSON.parse(await readFile16(specPath, "utf-8"));
|
|
32904
32963
|
} catch (e) {
|
|
32905
32964
|
writeJson({
|
|
32906
32965
|
ok: false,
|
|
@@ -32926,7 +32985,11 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32926
32985
|
process.exit(1);
|
|
32927
32986
|
return;
|
|
32928
32987
|
}
|
|
32929
|
-
const
|
|
32988
|
+
const flagAvatar = args.avatar?.trim();
|
|
32989
|
+
if (flagAvatar) {
|
|
32990
|
+
spec.data.cast = { ...spec.data.cast, avatar: flagAvatar };
|
|
32991
|
+
}
|
|
32992
|
+
const handle = flagAvatar || spec.data.cast?.avatar?.trim();
|
|
32930
32993
|
let avatar = null;
|
|
32931
32994
|
let avatarError = null;
|
|
32932
32995
|
if (handle) {
|
|
@@ -32946,27 +33009,37 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32946
33009
|
avatarError = `Could not read avatar \`${handle}\`: ${e instanceof Error ? e.message : String(e)}. \`baker avatars list\` shows who there is.`;
|
|
32947
33010
|
}
|
|
32948
33011
|
}
|
|
33012
|
+
const fromWorkspace = await readBrandFromWorkspace();
|
|
33013
|
+
const filledLogo = !spec.data.brand?.logo && fromWorkspace.logo ? fromWorkspace.logo : void 0;
|
|
33014
|
+
const filledPalette = !spec.data.brand?.palette?.length && fromWorkspace.palette?.length ? fromWorkspace.palette : void 0;
|
|
33015
|
+
if (filledLogo || filledPalette) {
|
|
33016
|
+
spec.data.brand = {
|
|
33017
|
+
...spec.data.brand,
|
|
33018
|
+
...filledLogo ? { logo: filledLogo } : {},
|
|
33019
|
+
...filledPalette ? { palette: filledPalette } : {}
|
|
33020
|
+
};
|
|
33021
|
+
}
|
|
32949
33022
|
const blueprint = adSpecToBlueprint(spec.data);
|
|
32950
|
-
const slug = args.slug ??
|
|
32951
|
-
const outPath = args.out ?? (args.slug ?
|
|
32952
|
-
const outDir =
|
|
33023
|
+
const slug = args.slug ?? path24.basename(specPath).replace(/\.[^.]+$/, "");
|
|
33024
|
+
const outPath = args.out ?? (args.slug ? path24.join("src/creatives", slug, `${slug}.canvas.json`) : path24.join(path24.dirname(specPath), `${slug}.canvas.json`));
|
|
33025
|
+
const outDir = path24.dirname(outPath);
|
|
32953
33026
|
await mkdir7(outDir, { recursive: true });
|
|
32954
|
-
const compositionDest =
|
|
32955
|
-
const captionsDest =
|
|
33027
|
+
const compositionDest = path24.join(outDir, "video-overlay-composition");
|
|
33028
|
+
const captionsDest = path24.join(outDir, "tiktok-captions-composition");
|
|
32956
33029
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
32957
33030
|
await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
|
|
32958
|
-
const blueprintPath =
|
|
32959
|
-
const blueprintStylePath =
|
|
33031
|
+
const blueprintPath = path24.join(outDir, "prompt.json");
|
|
33032
|
+
const blueprintStylePath = path24.join(outDir, "prompt.style.json");
|
|
32960
33033
|
await writeSceneFiles(outDir, blueprint);
|
|
32961
33034
|
await writeFile9(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
|
|
32962
33035
|
const logoPath = spec.data.brand?.logo?.trim();
|
|
32963
33036
|
const opts = {
|
|
32964
33037
|
imageModel: AD_IMAGE_MODEL,
|
|
32965
33038
|
videoModel: DEFAULT_VIDEO_GENERATE_MODEL,
|
|
32966
|
-
overlayCompositionPath:
|
|
32967
|
-
captionsCompositionPath:
|
|
32968
|
-
blueprintPath:
|
|
32969
|
-
blueprintStylePath:
|
|
33039
|
+
overlayCompositionPath: path24.relative(outDir, compositionDest),
|
|
33040
|
+
captionsCompositionPath: path24.relative(outDir, captionsDest),
|
|
33041
|
+
blueprintPath: path24.relative(outDir, blueprintPath),
|
|
33042
|
+
blueprintStylePath: path24.relative(outDir, blueprintStylePath),
|
|
32970
33043
|
aspect: spec.data.format.aspect_ratio,
|
|
32971
33044
|
resolution: spec.data.format.resolution,
|
|
32972
33045
|
// A beat is one clause and one card, so the cap has to clear a whole clause.
|
|
@@ -32980,12 +33053,12 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32980
33053
|
};
|
|
32981
33054
|
const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
|
|
32982
33055
|
const renderNode3 = canvas.nodes.find((n) => n.type === "hyperframe_render");
|
|
32983
|
-
const renderedDir = renderNode3 ?
|
|
33056
|
+
const renderedDir = renderNode3 ? path24.join(outDir, String(renderNode3.params?.composition ?? "")) : null;
|
|
32984
33057
|
let staged = null;
|
|
32985
33058
|
if (logoPath && renderedDir) {
|
|
32986
|
-
const ext =
|
|
33059
|
+
const ext = path24.extname(logoPath) || ".png";
|
|
32987
33060
|
try {
|
|
32988
|
-
await copyFile(logoPath,
|
|
33061
|
+
await copyFile(logoPath, path24.join(renderedDir, `brand-mark${ext}`));
|
|
32989
33062
|
staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
|
|
32990
33063
|
} catch {
|
|
32991
33064
|
}
|
|
@@ -33000,8 +33073,8 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
33000
33073
|
accent: firstHex(spec.data.brand?.palette)
|
|
33001
33074
|
}) : "";
|
|
33002
33075
|
if (overlayHtml && renderedDir) {
|
|
33003
|
-
const indexPath =
|
|
33004
|
-
const html = await
|
|
33076
|
+
const indexPath = path24.join(renderedDir, "index.html");
|
|
33077
|
+
const html = await readFile16(indexPath, "utf-8");
|
|
33005
33078
|
await writeFile9(indexPath, injectAdBrandOverlay(html, overlayHtml), "utf-8");
|
|
33006
33079
|
}
|
|
33007
33080
|
await writeFile9(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
@@ -33015,6 +33088,12 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
33015
33088
|
...logoPath && !staged ? [`Could not read the logo at \`${logoPath}\` \u2014 the ad has no mark on screen. Give the path as it appears in src/brand/BRAND.md, relative to the workspace root.`] : [],
|
|
33016
33089
|
...staged ? ["The brand mark is drawn by the composition \u2014 top-left throughout, and large on the closing card. It is never generated as a picture, so it cannot come back garbled."] : [],
|
|
33017
33090
|
...endCardWanted(spec.data) ? ["The last beat closes on a flat brand plate with the call to action over it, instead of one more photograph."] : [],
|
|
33091
|
+
...spec.data.brand?.logo && !filledLogo && !spec.data.brand.logo.startsWith(`${BRAND_DIR}/`) ? [
|
|
33092
|
+
`The mark at \`${spec.data.brand.logo}\` is not in \`${BRAND_DIR}/logos/\`. If you drew it for this ad, it is not the client's logo and the ad is going out wearing something you invented \u2014 put the real one in the brand folder (\`/brand-build\`) and re-run. A hand-drawn wordmark also tends to overflow its own viewBox, which renders as a clipped word.`
|
|
33093
|
+
] : [],
|
|
33094
|
+
...filledLogo || filledPalette ? [
|
|
33095
|
+
`The spec left the brand empty, so it was read from the workspace: ${[filledLogo ? `logo \`${filledLogo}\`` : null, filledPalette ? `palette ${filledPalette.slice(0, 3).join(", ")}` : null].filter(Boolean).join(" and ")}. Pass them yourself when the ad needs a different mark or colour.`
|
|
33096
|
+
] : [],
|
|
33018
33097
|
...avatarError ? [avatarError] : [],
|
|
33019
33098
|
...avatar ? [
|
|
33020
33099
|
`Every beat the customer appears in is grounded on \`${handle}\`'s identity sheet, and their subject description was copied into the frames verbatim \u2014 so it is the same face throughout and the same face as the rest of this company's work.`
|
|
@@ -33044,9 +33123,9 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
33044
33123
|
});
|
|
33045
33124
|
|
|
33046
33125
|
// src/commands/canvas/scaffold-video.ts
|
|
33047
|
-
import { access as access2, cp as cp2, mkdir as mkdir8, readFile as
|
|
33126
|
+
import { access as access2, cp as cp2, mkdir as mkdir8, readFile as readFile19, rm as rm7, writeFile as writeFile10 } from "fs/promises";
|
|
33048
33127
|
import { tmpdir as tmpdir3 } from "os";
|
|
33049
|
-
import
|
|
33128
|
+
import path26 from "path";
|
|
33050
33129
|
import { defineCommand as defineCommand105 } from "citty";
|
|
33051
33130
|
|
|
33052
33131
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -33086,7 +33165,7 @@ function routeVideoModel(input) {
|
|
|
33086
33165
|
|
|
33087
33166
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
33088
33167
|
import { execFile as execFile3 } from "child_process";
|
|
33089
|
-
import { mkdtemp as mkdtemp2, readdir as
|
|
33168
|
+
import { mkdtemp as mkdtemp2, readdir as readdir8, readFile as readFile17, rm as rm6 } from "fs/promises";
|
|
33090
33169
|
import { tmpdir as tmpdir2 } from "os";
|
|
33091
33170
|
import { join as join2 } from "path";
|
|
33092
33171
|
import { promisify as promisify3 } from "util";
|
|
@@ -33160,9 +33239,9 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
33160
33239
|
],
|
|
33161
33240
|
{ encoding: "utf-8", maxBuffer: 32 * 1024 * 1024, timeout: timeoutMs }
|
|
33162
33241
|
);
|
|
33163
|
-
const csvName = (await
|
|
33242
|
+
const csvName = (await readdir8(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
33164
33243
|
if (!csvName) return [];
|
|
33165
|
-
return parsePySceneDetectCsvCuts(await
|
|
33244
|
+
return parsePySceneDetectCsvCuts(await readFile17(join2(outDir, csvName), "utf-8"));
|
|
33166
33245
|
} finally {
|
|
33167
33246
|
await rm6(outDir, { recursive: true, force: true });
|
|
33168
33247
|
}
|
|
@@ -33186,8 +33265,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
33186
33265
|
}
|
|
33187
33266
|
|
|
33188
33267
|
// src/commands/canvas/gitignore.ts
|
|
33189
|
-
import { appendFile, readFile as
|
|
33190
|
-
import
|
|
33268
|
+
import { appendFile, readFile as readFile18 } from "fs/promises";
|
|
33269
|
+
import path25 from "path";
|
|
33191
33270
|
function missingGitignoreEntries(existing, entries) {
|
|
33192
33271
|
const present2 = new Set(
|
|
33193
33272
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -33195,10 +33274,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
33195
33274
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
33196
33275
|
}
|
|
33197
33276
|
async function ensureGitignore(dir, entries) {
|
|
33198
|
-
const file =
|
|
33277
|
+
const file = path25.join(dir, ".gitignore");
|
|
33199
33278
|
let existing;
|
|
33200
33279
|
try {
|
|
33201
|
-
existing = await
|
|
33280
|
+
existing = await readFile18(file, "utf8");
|
|
33202
33281
|
} catch {
|
|
33203
33282
|
return;
|
|
33204
33283
|
}
|
|
@@ -33237,7 +33316,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
|
|
|
33237
33316
|
For each kept element return: { "type": one of person|animal|product|logo|badge|location, "label": a short UPPER_SNAKE_CASE name (e.g. HERO, CREATOR_SKEPTIC, INSURANCE_CARD, LOGO), "description": a concrete reusable description to source/shoot the real asset \u2014 for a person/animal give a NEUTRAL castable role (e.g. "hero pet-owner, woman in her 30s" or "a small beagle"), NOT the original individual's literal face/identity: we RECAST with a FRESH person/animal, so never tell the agent to reuse the original. "expression": a living subject's typical expression or null, "cast_id": the global.cast id if it maps to one else null, "same_as": the label of another element this is the SAME individual as (different wardrobe/persona) else null, "scenes": the 0-based indices of ONLY the scenes where the element is ACTUALLY VISIBLE ON SCREEN \u2014 judged from that scene's start_frame_prompt / end_frame_prompt subjects and its action_detail, NOT from who is merely speaking. A narrator heard over b-roll is NOT present in that b-roll scene; a dog-running cutaway does NOT contain the couch creator just because she talks across it. Do NOT pad the list \u2014 an element wrongly listed in a scene makes the reproduction render the wrong subject there (e.g. the creator appearing in a pure-dog b-roll). When in doubt, leave a scene OUT. Output ONLY the JSON object.`;
|
|
33238
33317
|
async function loadAssetText2(ref, label) {
|
|
33239
33318
|
const r = ref;
|
|
33240
|
-
if (typeof r?.path === "string") return
|
|
33319
|
+
if (typeof r?.path === "string") return readFile19(r.path, "utf8");
|
|
33241
33320
|
if (typeof r?.url === "string") {
|
|
33242
33321
|
const res = await fetch(r.url);
|
|
33243
33322
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -33256,7 +33335,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
33256
33335
|
async function stageCaptions(outDir, transcript) {
|
|
33257
33336
|
const text2 = transcript?.trim();
|
|
33258
33337
|
if (!text2 || text2 === "[]") return {};
|
|
33259
|
-
const compositionPath =
|
|
33338
|
+
const compositionPath = path26.join(outDir, "tiktok-captions-composition");
|
|
33260
33339
|
await cp2(SHIPPED_CAPTIONS_DIR2, compositionPath, { recursive: true });
|
|
33261
33340
|
return { compositionPath };
|
|
33262
33341
|
}
|
|
@@ -33274,11 +33353,11 @@ function patchCompositionHtml(html, dims) {
|
|
|
33274
33353
|
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`);
|
|
33275
33354
|
}
|
|
33276
33355
|
async function stampCompositionDims(compositionDir, dims) {
|
|
33277
|
-
const metaPath =
|
|
33278
|
-
const rawMeta = await
|
|
33356
|
+
const metaPath = path26.join(compositionDir, "meta.json");
|
|
33357
|
+
const rawMeta = await readFile19(metaPath, "utf8");
|
|
33279
33358
|
await writeFile10(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
33280
|
-
const htmlPath =
|
|
33281
|
-
const rawHtml = await
|
|
33359
|
+
const htmlPath = path26.join(compositionDir, "index.html");
|
|
33360
|
+
const rawHtml = await readFile19(htmlPath, "utf8");
|
|
33282
33361
|
await writeFile10(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
33283
33362
|
}
|
|
33284
33363
|
function parseElements2(raw) {
|
|
@@ -33326,7 +33405,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
33326
33405
|
"video/x-matroska": ".mkv"
|
|
33327
33406
|
};
|
|
33328
33407
|
function referenceVideoExt(url, contentType) {
|
|
33329
|
-
const fromPath =
|
|
33408
|
+
const fromPath = path26.extname(new URL(url).pathname).toLowerCase();
|
|
33330
33409
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
33331
33410
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
33332
33411
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -33352,7 +33431,7 @@ function videoDefinitionDescription(blueprint) {
|
|
|
33352
33431
|
return typeof product === "string" && product.trim() ? product.trim() : void 0;
|
|
33353
33432
|
}
|
|
33354
33433
|
async function materializeReferenceVideo(fileArg2) {
|
|
33355
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
33434
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path26.resolve(fileArg2);
|
|
33356
33435
|
let bytes;
|
|
33357
33436
|
let contentType;
|
|
33358
33437
|
try {
|
|
@@ -33364,7 +33443,7 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
33364
33443
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
33365
33444
|
}
|
|
33366
33445
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
33367
|
-
const dest =
|
|
33446
|
+
const dest = path26.join(
|
|
33368
33447
|
tmpdir3(),
|
|
33369
33448
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
33370
33449
|
);
|
|
@@ -33584,11 +33663,11 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33584
33663
|
} catch (e) {
|
|
33585
33664
|
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
33586
33665
|
}
|
|
33587
|
-
const base =
|
|
33588
|
-
const outPath = args.out ?
|
|
33589
|
-
const outDir =
|
|
33590
|
-
const blueprintPath =
|
|
33591
|
-
const blueprintStylePath =
|
|
33666
|
+
const base = path26.basename(videoPath, path26.extname(videoPath));
|
|
33667
|
+
const outPath = args.out ? path26.resolve(String(args.out)) : slug ? path26.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path26.join(path26.dirname(videoPath), `${base}.video.canvas.json`);
|
|
33668
|
+
const outDir = path26.dirname(outPath);
|
|
33669
|
+
const blueprintPath = path26.join(outDir, "prompt.json");
|
|
33670
|
+
const blueprintStylePath = path26.join(outDir, "prompt.style.json");
|
|
33592
33671
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
33593
33672
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
33594
33673
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -33633,12 +33712,12 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33633
33712
|
`
|
|
33634
33713
|
);
|
|
33635
33714
|
}
|
|
33636
|
-
const compositionDest =
|
|
33715
|
+
const compositionDest = path26.join(outDir, "video-overlay-composition");
|
|
33637
33716
|
await cp2(SHIPPED_COMPOSITION_DIR2, compositionDest, { recursive: true });
|
|
33638
33717
|
await stampCompositionDims(compositionDest, outDims);
|
|
33639
|
-
const indexPath =
|
|
33718
|
+
const indexPath = path26.join(compositionDest, "index.html");
|
|
33640
33719
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
|
|
33641
|
-
const indexHtml = await
|
|
33720
|
+
const indexHtml = await readFile19(indexPath, "utf8");
|
|
33642
33721
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
33643
33722
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
33644
33723
|
fail4(
|
|
@@ -33652,10 +33731,10 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33652
33731
|
const opts = {
|
|
33653
33732
|
imageModel,
|
|
33654
33733
|
videoModel,
|
|
33655
|
-
overlayCompositionPath:
|
|
33656
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
33657
|
-
blueprintPath:
|
|
33658
|
-
blueprintStylePath:
|
|
33734
|
+
overlayCompositionPath: path26.relative(outDir, compositionDest),
|
|
33735
|
+
captionsCompositionPath: captions.compositionPath ? path26.relative(outDir, captions.compositionPath) : void 0,
|
|
33736
|
+
blueprintPath: path26.relative(outDir, blueprintPath),
|
|
33737
|
+
blueprintStylePath: path26.relative(outDir, blueprintStylePath),
|
|
33659
33738
|
frames,
|
|
33660
33739
|
ambient: Boolean(args.ambient),
|
|
33661
33740
|
seamDedup: resolveSeamDedup(args["seam-dedup"]),
|
|
@@ -33683,7 +33762,7 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33683
33762
|
await writeFile10(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
33684
33763
|
`, "utf8");
|
|
33685
33764
|
await writeFile10(
|
|
33686
|
-
|
|
33765
|
+
path26.join(outDir, REBUILD_FILE),
|
|
33687
33766
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
33688
33767
|
`,
|
|
33689
33768
|
"utf8"
|
|
@@ -33708,7 +33787,7 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33708
33787
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
33709
33788
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
33710
33789
|
if (slug) {
|
|
33711
|
-
const definitionPath =
|
|
33790
|
+
const definitionPath = path26.join(outDir, "_definition.md");
|
|
33712
33791
|
if (!await fileExists2(definitionPath)) {
|
|
33713
33792
|
await writeFile10(
|
|
33714
33793
|
definitionPath,
|
|
@@ -33763,7 +33842,7 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33763
33842
|
graph: canvas.metadata?.video?.graph_stats
|
|
33764
33843
|
},
|
|
33765
33844
|
checklist: {
|
|
33766
|
-
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${
|
|
33845
|
+
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${path26.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path26.basename(blueprintStylePath)}, the projection each frame's target_blueprint reads) \u2014 so your scene edits reach the render automatically. Never hand-edit the inlined node prompts in the canvas or the derived ${path26.basename(blueprintStylePath)}; both are regenerated.`,
|
|
33767
33846
|
recurring_elements_to_supply: report.elements,
|
|
33768
33847
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
33769
33848
|
scene: d.scene,
|
|
@@ -33800,8 +33879,8 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33800
33879
|
});
|
|
33801
33880
|
|
|
33802
33881
|
// src/commands/canvas/set-prompt.ts
|
|
33803
|
-
import { readFile as
|
|
33804
|
-
import
|
|
33882
|
+
import { readFile as readFile20, writeFile as writeFile11 } from "fs/promises";
|
|
33883
|
+
import path27 from "path";
|
|
33805
33884
|
import { defineCommand as defineCommand106 } from "citty";
|
|
33806
33885
|
function setNodePrompt(canvas, nodeId, text2) {
|
|
33807
33886
|
const nodes = canvas?.nodes;
|
|
@@ -33829,17 +33908,17 @@ var setPromptCommand = defineCommand106({
|
|
|
33829
33908
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
33830
33909
|
},
|
|
33831
33910
|
async run({ args }) {
|
|
33832
|
-
const filePath =
|
|
33911
|
+
const filePath = path27.resolve(String(args.file));
|
|
33833
33912
|
let canvas;
|
|
33834
33913
|
try {
|
|
33835
|
-
canvas = JSON.parse(await
|
|
33914
|
+
canvas = JSON.parse(await readFile20(filePath, "utf8"));
|
|
33836
33915
|
} catch (e) {
|
|
33837
33916
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
|
|
33838
33917
|
`);
|
|
33839
33918
|
process.exit(2);
|
|
33840
33919
|
}
|
|
33841
33920
|
let text2;
|
|
33842
|
-
if (args["text-file"]) text2 = await
|
|
33921
|
+
if (args["text-file"]) text2 = await readFile20(path27.resolve(String(args["text-file"])), "utf8");
|
|
33843
33922
|
else if (args.text !== void 0) text2 = String(args.text);
|
|
33844
33923
|
else {
|
|
33845
33924
|
process.stderr.write(
|
|
@@ -33860,7 +33939,7 @@ var setPromptCommand = defineCommand106({
|
|
|
33860
33939
|
process.exit(2);
|
|
33861
33940
|
return;
|
|
33862
33941
|
}
|
|
33863
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
33942
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path27.dirname(filePath)), defaultRegistry());
|
|
33864
33943
|
if (!validation.ok) {
|
|
33865
33944
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
33866
33945
|
`);
|
|
@@ -33875,8 +33954,8 @@ var setPromptCommand = defineCommand106({
|
|
|
33875
33954
|
});
|
|
33876
33955
|
|
|
33877
33956
|
// src/commands/canvas/validate.ts
|
|
33878
|
-
import { readFile as
|
|
33879
|
-
import
|
|
33957
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
33958
|
+
import path28 from "path";
|
|
33880
33959
|
import { defineCommand as defineCommand107 } from "citty";
|
|
33881
33960
|
var validateCommand = defineCommand107({
|
|
33882
33961
|
meta: {
|
|
@@ -33885,8 +33964,8 @@ var validateCommand = defineCommand107({
|
|
|
33885
33964
|
},
|
|
33886
33965
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
33887
33966
|
async run({ args }) {
|
|
33888
|
-
const filePath =
|
|
33889
|
-
const raw = await
|
|
33967
|
+
const filePath = path28.resolve(String(args.file));
|
|
33968
|
+
const raw = await readFile21(filePath, "utf8");
|
|
33890
33969
|
let parsed;
|
|
33891
33970
|
try {
|
|
33892
33971
|
parsed = JSON.parse(raw);
|
|
@@ -33898,7 +33977,7 @@ var validateCommand = defineCommand107({
|
|
|
33898
33977
|
}
|
|
33899
33978
|
const healed = await healAbsoluteCanvasPaths(filePath, parsed);
|
|
33900
33979
|
parsed = healed.canvas;
|
|
33901
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
33980
|
+
parsed = resolveRelativeCanvasPaths(parsed, path28.dirname(filePath));
|
|
33902
33981
|
let styleProjection = "not_applicable";
|
|
33903
33982
|
try {
|
|
33904
33983
|
styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
@@ -34324,7 +34403,7 @@ import { defineCommand as defineCommand112 } from "citty";
|
|
|
34324
34403
|
import { defineCommand as defineCommand111 } from "citty";
|
|
34325
34404
|
|
|
34326
34405
|
// src/commands/images/api.ts
|
|
34327
|
-
import { readFile as
|
|
34406
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
34328
34407
|
import { basename, extname } from "path";
|
|
34329
34408
|
var imageProcessingTimeoutMs = 18e4;
|
|
34330
34409
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -34338,7 +34417,7 @@ var mimeMap = {
|
|
|
34338
34417
|
".avif": "image/avif"
|
|
34339
34418
|
};
|
|
34340
34419
|
var defaultImageApiDeps = {
|
|
34341
|
-
readFile:
|
|
34420
|
+
readFile: readFile22,
|
|
34342
34421
|
post: apiPost,
|
|
34343
34422
|
get: apiGet,
|
|
34344
34423
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -34600,12 +34679,12 @@ function collectSideEffects(tree) {
|
|
|
34600
34679
|
);
|
|
34601
34680
|
}
|
|
34602
34681
|
function readFlowTree(slug) {
|
|
34603
|
-
const
|
|
34604
|
-
if (!existsSync5(
|
|
34682
|
+
const path41 = join3(flowsDir(), slug, "_data.json");
|
|
34683
|
+
if (!existsSync5(path41)) {
|
|
34605
34684
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
34606
34685
|
}
|
|
34607
34686
|
try {
|
|
34608
|
-
return JSON.parse(readFileSync9(
|
|
34687
|
+
return JSON.parse(readFileSync9(path41, "utf-8"));
|
|
34609
34688
|
} catch (error) {
|
|
34610
34689
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
34611
34690
|
}
|
|
@@ -34997,10 +35076,10 @@ function parseValueExpression(raw) {
|
|
|
34997
35076
|
return parts.map(parsePart);
|
|
34998
35077
|
}
|
|
34999
35078
|
function trackingFieldIds() {
|
|
35000
|
-
const
|
|
35001
|
-
if (!existsSync6(
|
|
35079
|
+
const path41 = join4(flowsDir(), "..", "tracking.ts");
|
|
35080
|
+
if (!existsSync6(path41)) return null;
|
|
35002
35081
|
try {
|
|
35003
|
-
const source = readFileSync10(
|
|
35082
|
+
const source = readFileSync10(path41, "utf-8");
|
|
35004
35083
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
35005
35084
|
if (!block2) return null;
|
|
35006
35085
|
const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -35552,13 +35631,13 @@ function specsFromFile(parsed) {
|
|
|
35552
35631
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
35553
35632
|
});
|
|
35554
35633
|
}
|
|
35555
|
-
function readSpecFile(
|
|
35634
|
+
function readSpecFile(path41) {
|
|
35556
35635
|
let raw;
|
|
35557
35636
|
try {
|
|
35558
|
-
raw =
|
|
35637
|
+
raw = path41 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path41, "utf-8");
|
|
35559
35638
|
} catch (error) {
|
|
35560
35639
|
refuse(
|
|
35561
|
-
`Could not read ${
|
|
35640
|
+
`Could not read ${path41 === "-" ? "the mapping from stdin" : `"${path41}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
35562
35641
|
);
|
|
35563
35642
|
}
|
|
35564
35643
|
let parsed;
|
|
@@ -35566,7 +35645,7 @@ function readSpecFile(path40) {
|
|
|
35566
35645
|
parsed = JSON.parse(raw);
|
|
35567
35646
|
} catch (error) {
|
|
35568
35647
|
refuse(
|
|
35569
|
-
`${
|
|
35648
|
+
`${path41 === "-" ? "stdin" : `"${path41}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
35570
35649
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
35571
35650
|
);
|
|
35572
35651
|
}
|
|
@@ -35820,18 +35899,18 @@ var ARRAY_FIELDS = [
|
|
|
35820
35899
|
"tagIds"
|
|
35821
35900
|
];
|
|
35822
35901
|
var ARRAY_OWNERS = ["", "body"];
|
|
35823
|
-
function dropUnsetOptionals(sideEffect,
|
|
35902
|
+
function dropUnsetOptionals(sideEffect, path41) {
|
|
35824
35903
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
35825
35904
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
35826
35905
|
delete sideEffect[key];
|
|
35827
|
-
return [{ path:
|
|
35906
|
+
return [{ path: path41, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
35828
35907
|
});
|
|
35829
35908
|
}
|
|
35830
|
-
function fillNulledArrays(target, prefix,
|
|
35909
|
+
function fillNulledArrays(target, prefix, path41) {
|
|
35831
35910
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
35832
35911
|
if (!(key in target) || target[key] !== null) return [];
|
|
35833
35912
|
target[key] = [];
|
|
35834
|
-
return [{ path:
|
|
35913
|
+
return [{ path: path41, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
35835
35914
|
});
|
|
35836
35915
|
}
|
|
35837
35916
|
function sideEffectsOf(node) {
|
|
@@ -35841,13 +35920,13 @@ function sideEffectsOf(node) {
|
|
|
35841
35920
|
);
|
|
35842
35921
|
}
|
|
35843
35922
|
function normalizeSideEffect(sideEffect, where) {
|
|
35844
|
-
const
|
|
35923
|
+
const path41 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
35845
35924
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
35846
35925
|
const target = owner ? sideEffect[owner] : sideEffect;
|
|
35847
35926
|
if (!target || typeof target !== "object") return [];
|
|
35848
|
-
return fillNulledArrays(target, owner ? `${owner}.` : "",
|
|
35927
|
+
return fillNulledArrays(target, owner ? `${owner}.` : "", path41);
|
|
35849
35928
|
});
|
|
35850
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
35929
|
+
return [...dropUnsetOptionals(sideEffect, path41), ...arrays];
|
|
35851
35930
|
}
|
|
35852
35931
|
function normalizeFlowTree(tree) {
|
|
35853
35932
|
const changes = [];
|
|
@@ -36385,10 +36464,10 @@ async function stageOps(ops) {
|
|
|
36385
36464
|
handleError2(err);
|
|
36386
36465
|
}
|
|
36387
36466
|
}
|
|
36388
|
-
async function draftAction2(
|
|
36467
|
+
async function draftAction2(path41, body, chat) {
|
|
36389
36468
|
const chatId = resolveChatId(chat);
|
|
36390
36469
|
try {
|
|
36391
|
-
const data = await apiPost(
|
|
36470
|
+
const data = await apiPost(path41, { chatId, ...body });
|
|
36392
36471
|
writeJsonEnvelope({ ok: true, data });
|
|
36393
36472
|
return data;
|
|
36394
36473
|
} catch (err) {
|
|
@@ -38785,7 +38864,7 @@ function cropSprite(input, region) {
|
|
|
38785
38864
|
|
|
38786
38865
|
// src/lib/image/io.ts
|
|
38787
38866
|
import { randomBytes } from "crypto";
|
|
38788
|
-
import { glob as fsGlob, readFile as
|
|
38867
|
+
import { glob as fsGlob, readFile as readFile23, rename, stat as stat4, writeFile as writeFile12 } from "fs/promises";
|
|
38789
38868
|
import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
|
|
38790
38869
|
var REMOTE_RE = /^https?:\/\//i;
|
|
38791
38870
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -38818,11 +38897,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
38818
38897
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
38819
38898
|
return buffer;
|
|
38820
38899
|
}
|
|
38821
|
-
return
|
|
38900
|
+
return readFile23(pathOrUrl);
|
|
38822
38901
|
}
|
|
38823
|
-
async function isDirectory(
|
|
38902
|
+
async function isDirectory(path41) {
|
|
38824
38903
|
try {
|
|
38825
|
-
const s = await stat4(
|
|
38904
|
+
const s = await stat4(path41);
|
|
38826
38905
|
return s.isDirectory();
|
|
38827
38906
|
} catch {
|
|
38828
38907
|
return false;
|
|
@@ -39124,13 +39203,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
39124
39203
|
}
|
|
39125
39204
|
function disambiguate(paths) {
|
|
39126
39205
|
const taken = /* @__PURE__ */ new Set();
|
|
39127
|
-
return paths.map((
|
|
39128
|
-
if (!taken.has(
|
|
39129
|
-
taken.add(
|
|
39130
|
-
return
|
|
39206
|
+
return paths.map((path41) => {
|
|
39207
|
+
if (!taken.has(path41)) {
|
|
39208
|
+
taken.add(path41);
|
|
39209
|
+
return path41;
|
|
39131
39210
|
}
|
|
39132
|
-
const ext = extname3(
|
|
39133
|
-
const stem =
|
|
39211
|
+
const ext = extname3(path41);
|
|
39212
|
+
const stem = path41.slice(0, path41.length - ext.length);
|
|
39134
39213
|
let n = 2;
|
|
39135
39214
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
39136
39215
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -39257,10 +39336,10 @@ async function runDownloads(plan) {
|
|
|
39257
39336
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
39258
39337
|
const downloaded = [];
|
|
39259
39338
|
for (const [index, item] of fetched.entries()) {
|
|
39260
|
-
const
|
|
39339
|
+
const path41 = paths[index] ?? item.path;
|
|
39261
39340
|
try {
|
|
39262
|
-
await atomicWrite(
|
|
39263
|
-
downloaded.push({ input: item.input, output:
|
|
39341
|
+
await atomicWrite(path41, item.buffer);
|
|
39342
|
+
downloaded.push({ input: item.input, output: path41, bytes: item.buffer.length, contentType: item.contentType });
|
|
39264
39343
|
} catch (err) {
|
|
39265
39344
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
39266
39345
|
}
|
|
@@ -42219,8 +42298,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
42219
42298
|
import { defineCommand as defineCommand167 } from "citty";
|
|
42220
42299
|
|
|
42221
42300
|
// src/commands/landing/critique.ts
|
|
42222
|
-
import { readdir as
|
|
42223
|
-
import
|
|
42301
|
+
import { readdir as readdir10, stat as stat6 } from "fs/promises";
|
|
42302
|
+
import path31 from "path";
|
|
42224
42303
|
import { defineCommand as defineCommand157 } from "citty";
|
|
42225
42304
|
|
|
42226
42305
|
// src/engine/landing/lib/constants.ts
|
|
@@ -43188,13 +43267,13 @@ function describeCounts(findings) {
|
|
|
43188
43267
|
|
|
43189
43268
|
// src/commands/landing/snapshot.ts
|
|
43190
43269
|
import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
|
|
43191
|
-
import
|
|
43270
|
+
import path29 from "path";
|
|
43192
43271
|
var CRITIC_VERSION = "2";
|
|
43193
43272
|
function critiqueCacheDir(projectRoot) {
|
|
43194
|
-
return
|
|
43273
|
+
return path29.join(projectRoot, ".cache", "landing-critique");
|
|
43195
43274
|
}
|
|
43196
43275
|
function snapshotPath(projectRoot, slug) {
|
|
43197
|
-
return
|
|
43276
|
+
return path29.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
43198
43277
|
}
|
|
43199
43278
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
43200
43279
|
await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
|
|
@@ -43206,21 +43285,21 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
|
43206
43285
|
}
|
|
43207
43286
|
|
|
43208
43287
|
// src/commands/landing/source-version.ts
|
|
43209
|
-
import { readdir as
|
|
43210
|
-
import
|
|
43288
|
+
import { readdir as readdir9, readFile as readFile24, stat as stat5 } from "fs/promises";
|
|
43289
|
+
import path30 from "path";
|
|
43211
43290
|
async function landingSourceRelPaths(landingDir) {
|
|
43212
43291
|
const rel = [];
|
|
43213
|
-
if (await isFile(
|
|
43214
|
-
const componentsDir =
|
|
43292
|
+
if (await isFile(path30.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
43293
|
+
const componentsDir = path30.join(landingDir, "_components");
|
|
43215
43294
|
for (const abs of await walkAstro(componentsDir)) {
|
|
43216
|
-
rel.push(
|
|
43295
|
+
rel.push(path30.relative(landingDir, abs).split(path30.sep).join("/"));
|
|
43217
43296
|
}
|
|
43218
43297
|
return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
43219
43298
|
}
|
|
43220
43299
|
async function readLandingSources(landingDir) {
|
|
43221
43300
|
const rel = await landingSourceRelPaths(landingDir);
|
|
43222
43301
|
const out = [];
|
|
43223
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
43302
|
+
for (const r of rel) out.push({ path: r, text: await readFile24(path30.join(landingDir, r), "utf8") });
|
|
43224
43303
|
return out;
|
|
43225
43304
|
}
|
|
43226
43305
|
async function computeLandingSourceSha(landingDir) {
|
|
@@ -43229,7 +43308,7 @@ async function computeLandingSourceSha(landingDir) {
|
|
|
43229
43308
|
for (const r of rel) {
|
|
43230
43309
|
let bytes;
|
|
43231
43310
|
try {
|
|
43232
|
-
bytes = await
|
|
43311
|
+
bytes = await readFile24(path30.join(landingDir, r));
|
|
43233
43312
|
} catch {
|
|
43234
43313
|
bytes = Buffer.alloc(0);
|
|
43235
43314
|
}
|
|
@@ -43247,13 +43326,13 @@ async function isFile(p) {
|
|
|
43247
43326
|
async function walkAstro(dir) {
|
|
43248
43327
|
let entries;
|
|
43249
43328
|
try {
|
|
43250
|
-
entries = await
|
|
43329
|
+
entries = await readdir9(dir, { withFileTypes: true });
|
|
43251
43330
|
} catch {
|
|
43252
43331
|
return [];
|
|
43253
43332
|
}
|
|
43254
43333
|
const out = [];
|
|
43255
43334
|
for (const entry of entries) {
|
|
43256
|
-
const abs =
|
|
43335
|
+
const abs = path30.join(dir, entry.name);
|
|
43257
43336
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
43258
43337
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
43259
43338
|
}
|
|
@@ -43314,7 +43393,7 @@ var critiqueCommand2 = defineCommand157({
|
|
|
43314
43393
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
43315
43394
|
);
|
|
43316
43395
|
}
|
|
43317
|
-
if (!await isDir(
|
|
43396
|
+
if (!await isDir(path31.resolve(projectRoot, "src", "pages", slug))) {
|
|
43318
43397
|
fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
43319
43398
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
43320
43399
|
});
|
|
@@ -43353,7 +43432,7 @@ var critiqueCommand2 = defineCommand157({
|
|
|
43353
43432
|
}
|
|
43354
43433
|
});
|
|
43355
43434
|
async function critiqueOne(projectRoot, slug, brand) {
|
|
43356
|
-
const landingDir =
|
|
43435
|
+
const landingDir = path31.resolve(projectRoot, "src", "pages", slug);
|
|
43357
43436
|
const [sources, sourceSha] = await Promise.all([readLandingSources(landingDir), computeLandingSourceSha(landingDir)]);
|
|
43358
43437
|
const report = critiqueLanding({ slug, sources, brand });
|
|
43359
43438
|
let snapshotFailed = false;
|
|
@@ -43373,7 +43452,7 @@ async function critiqueOne(projectRoot, slug, brand) {
|
|
|
43373
43452
|
}
|
|
43374
43453
|
async function listLandingSlugs(projectRoot) {
|
|
43375
43454
|
try {
|
|
43376
|
-
const entries = await
|
|
43455
|
+
const entries = await readdir10(path31.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
43377
43456
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
43378
43457
|
} catch {
|
|
43379
43458
|
return [];
|
|
@@ -43525,7 +43604,7 @@ var addCommand = defineCommand158({
|
|
|
43525
43604
|
|
|
43526
43605
|
// src/commands/landing/inspiration/code.ts
|
|
43527
43606
|
import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
|
|
43528
|
-
import
|
|
43607
|
+
import path32 from "path";
|
|
43529
43608
|
import { defineCommand as defineCommand159 } from "citty";
|
|
43530
43609
|
registerSchema({
|
|
43531
43610
|
command: "landing.inspiration.code",
|
|
@@ -43548,9 +43627,9 @@ var codeCommand = defineCommand159({
|
|
|
43548
43627
|
try {
|
|
43549
43628
|
const id = args.id;
|
|
43550
43629
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
43551
|
-
const dir =
|
|
43630
|
+
const dir = path32.join(process.cwd(), ".baker", "inspiration", id);
|
|
43552
43631
|
await mkdir10(dir, { recursive: true });
|
|
43553
|
-
const file =
|
|
43632
|
+
const file = path32.join(dir, "section.html");
|
|
43554
43633
|
await writeFile14(file, data.html);
|
|
43555
43634
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
43556
43635
|
const fidelity = fidelityHint(data.fidelity);
|
|
@@ -43560,7 +43639,7 @@ var codeCommand = defineCommand159({
|
|
|
43560
43639
|
ok: true,
|
|
43561
43640
|
data: {
|
|
43562
43641
|
id,
|
|
43563
|
-
file:
|
|
43642
|
+
file: path32.relative(process.cwd(), file),
|
|
43564
43643
|
bytes: data.html.length,
|
|
43565
43644
|
fidelity: data.fidelity,
|
|
43566
43645
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -43992,7 +44071,7 @@ function classifyCaptureFailure(error) {
|
|
|
43992
44071
|
|
|
43993
44072
|
// src/engine/landing-library/run.ts
|
|
43994
44073
|
import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
|
|
43995
|
-
import
|
|
44074
|
+
import path34 from "path";
|
|
43996
44075
|
|
|
43997
44076
|
// ../proxy/src/preflight.ts
|
|
43998
44077
|
import http from "http";
|
|
@@ -45408,9 +45487,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
45408
45487
|
|
|
45409
45488
|
// src/engine/landing-library/report.ts
|
|
45410
45489
|
import { writeFile as writeFile15 } from "fs/promises";
|
|
45411
|
-
import
|
|
45490
|
+
import path33 from "path";
|
|
45412
45491
|
async function writeCaptureReport(manifest, outDir) {
|
|
45413
|
-
const file =
|
|
45492
|
+
const file = path33.join(outDir, "report.html");
|
|
45414
45493
|
await writeFile15(file, renderReport(manifest));
|
|
45415
45494
|
return file;
|
|
45416
45495
|
}
|
|
@@ -45589,32 +45668,32 @@ async function reproducePage(args) {
|
|
|
45589
45668
|
const { browser, page, outDir, pageUrl, livePageShot } = args;
|
|
45590
45669
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
45591
45670
|
if (!built) return { bundle: null, fidelity: null };
|
|
45592
|
-
await writeFile16(
|
|
45671
|
+
await writeFile16(path34.join(outDir, "page.html"), built.html);
|
|
45593
45672
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
45594
45673
|
wholePage: true,
|
|
45595
45674
|
timeoutMs: 6e4
|
|
45596
45675
|
});
|
|
45597
45676
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
45598
|
-
await writeFile16(
|
|
45677
|
+
await writeFile16(path34.join(outDir, "page-rendered.png"), rendered);
|
|
45599
45678
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
45600
45679
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
45601
45680
|
}
|
|
45602
45681
|
async function captureOneSection(args) {
|
|
45603
45682
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
|
|
45604
|
-
const dir =
|
|
45683
|
+
const dir = path34.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
45605
45684
|
await mkdir11(dir, { recursive: true });
|
|
45606
45685
|
const desktop = await captureSection(page, candidate);
|
|
45607
|
-
if (desktop) await writeFile16(
|
|
45686
|
+
if (desktop) await writeFile16(path34.join(dir, "desktop.png"), desktop);
|
|
45608
45687
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
45609
45688
|
const motion = await collectMotion(page, candidate.selector);
|
|
45610
45689
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
45611
45690
|
let fidelity = null;
|
|
45612
45691
|
let fidelityNote;
|
|
45613
45692
|
if (built) {
|
|
45614
|
-
await writeFile16(
|
|
45693
|
+
await writeFile16(path34.join(dir, "section.html"), built.html);
|
|
45615
45694
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
45616
45695
|
if (rendered && desktop) {
|
|
45617
|
-
await writeFile16(
|
|
45696
|
+
await writeFile16(path34.join(dir, "section-rendered.png"), rendered);
|
|
45618
45697
|
const result = await scoreFidelity(desktop, rendered);
|
|
45619
45698
|
fidelity = result.score;
|
|
45620
45699
|
fidelityNote = result.note;
|
|
@@ -45622,9 +45701,9 @@ async function captureOneSection(args) {
|
|
|
45622
45701
|
}
|
|
45623
45702
|
return {
|
|
45624
45703
|
...candidate,
|
|
45625
|
-
desktopShot: desktop ?
|
|
45704
|
+
desktopShot: desktop ? path34.relative(outDir, path34.join(dir, "desktop.png")) : null,
|
|
45626
45705
|
mobileShot: null,
|
|
45627
|
-
bundle: built ?
|
|
45706
|
+
bundle: built ? path34.relative(outDir, path34.join(dir, "section.html")) : null,
|
|
45628
45707
|
fidelity,
|
|
45629
45708
|
...fidelityNote ? { fidelityNote } : {},
|
|
45630
45709
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -45643,9 +45722,9 @@ async function captureMobileShots(args) {
|
|
|
45643
45722
|
for (const section of sections) {
|
|
45644
45723
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
45645
45724
|
if (!shot) continue;
|
|
45646
|
-
const file =
|
|
45725
|
+
const file = path34.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
|
|
45647
45726
|
await writeFile16(file, shot);
|
|
45648
|
-
section.mobileShot =
|
|
45727
|
+
section.mobileShot = path34.relative(outDir, file);
|
|
45649
45728
|
}
|
|
45650
45729
|
} finally {
|
|
45651
45730
|
await mobile.context.close();
|
|
@@ -45660,10 +45739,10 @@ async function captureMotionTakes(args) {
|
|
|
45660
45739
|
const filmOne = async (section) => {
|
|
45661
45740
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
45662
45741
|
if (!take) return;
|
|
45663
|
-
const dir =
|
|
45664
|
-
const file =
|
|
45742
|
+
const dir = path34.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
45743
|
+
const file = path34.join(dir, "motion-filmstrip.png");
|
|
45665
45744
|
await writeFile16(file, take.filmstrip);
|
|
45666
|
-
section.motionFilmstrip =
|
|
45745
|
+
section.motionFilmstrip = path34.relative(outDir, file);
|
|
45667
45746
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
45668
45747
|
};
|
|
45669
45748
|
const queue = [...moving];
|
|
@@ -45720,7 +45799,7 @@ async function captureAlternateViews(args) {
|
|
|
45720
45799
|
async function reproduceWholePage(args) {
|
|
45721
45800
|
const { browser, page, outDir, pageUrl, withCode, log } = args;
|
|
45722
45801
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
45723
|
-
if (fullPage) await writeFile16(
|
|
45802
|
+
if (fullPage) await writeFile16(path34.join(outDir, "full-page.png"), fullPage);
|
|
45724
45803
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
45725
45804
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
45726
45805
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -45791,7 +45870,7 @@ async function openViaLadder(args) {
|
|
|
45791
45870
|
async function scrapeLanding(options) {
|
|
45792
45871
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
45793
45872
|
const log = options.onProgress ?? (() => void 0);
|
|
45794
|
-
const sectionsDir =
|
|
45873
|
+
const sectionsDir = path34.join(options.outDir, "sections");
|
|
45795
45874
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
45796
45875
|
if (nonPublic) {
|
|
45797
45876
|
throw new BlockedPageError({
|
|
@@ -45853,7 +45932,7 @@ async function scrapeLanding(options) {
|
|
|
45853
45932
|
security: prepared.security,
|
|
45854
45933
|
captureTier: tier
|
|
45855
45934
|
};
|
|
45856
|
-
await writeFile16(
|
|
45935
|
+
await writeFile16(path34.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
45857
45936
|
`);
|
|
45858
45937
|
if (options.report !== false) {
|
|
45859
45938
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -45868,28 +45947,28 @@ async function scrapeLanding(options) {
|
|
|
45868
45947
|
|
|
45869
45948
|
// src/commands/landing/inspiration/captureOut.ts
|
|
45870
45949
|
import { existsSync as existsSync9 } from "fs";
|
|
45871
|
-
import
|
|
45950
|
+
import path35 from "path";
|
|
45872
45951
|
var SCRATCH_DIR = ".baker";
|
|
45873
45952
|
function isWithin(parent, target) {
|
|
45874
|
-
const relative =
|
|
45875
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
45953
|
+
const relative = path35.relative(parent, target);
|
|
45954
|
+
return relative === "" || !relative.startsWith("..") && !path35.isAbsolute(relative);
|
|
45876
45955
|
}
|
|
45877
45956
|
function findRepoRoot(from) {
|
|
45878
|
-
let dir =
|
|
45957
|
+
let dir = path35.resolve(from);
|
|
45879
45958
|
for (; ; ) {
|
|
45880
|
-
if (existsSync9(
|
|
45881
|
-
const parent =
|
|
45959
|
+
if (existsSync9(path35.join(dir, ".git"))) return dir;
|
|
45960
|
+
const parent = path35.dirname(dir);
|
|
45882
45961
|
if (parent === dir) return null;
|
|
45883
45962
|
dir = parent;
|
|
45884
45963
|
}
|
|
45885
45964
|
}
|
|
45886
45965
|
function checkCaptureOut(out, options) {
|
|
45887
45966
|
const { cwd, repoRoot } = options;
|
|
45888
|
-
const resolved =
|
|
45967
|
+
const resolved = path35.resolve(cwd, out);
|
|
45889
45968
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
45890
|
-
const scratch =
|
|
45969
|
+
const scratch = path35.join(repoRoot, SCRATCH_DIR);
|
|
45891
45970
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
45892
|
-
const suggestion =
|
|
45971
|
+
const suggestion = path35.posix.join(SCRATCH_DIR, "teardowns", path35.basename(resolved) || "capture");
|
|
45893
45972
|
return {
|
|
45894
45973
|
ok: false,
|
|
45895
45974
|
error: {
|
|
@@ -46063,12 +46142,12 @@ var scrapeCommand = defineCommand162({
|
|
|
46063
46142
|
});
|
|
46064
46143
|
|
|
46065
46144
|
// src/commands/landing/inspiration/search.ts
|
|
46066
|
-
import
|
|
46145
|
+
import path37 from "path";
|
|
46067
46146
|
import { defineCommand as defineCommand163 } from "citty";
|
|
46068
46147
|
|
|
46069
46148
|
// src/commands/landing/inspiration/shot.ts
|
|
46070
46149
|
import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
|
|
46071
|
-
import
|
|
46150
|
+
import path36 from "path";
|
|
46072
46151
|
import sharp6 from "sharp";
|
|
46073
46152
|
var READABLE_SHOT = {
|
|
46074
46153
|
maxWidth: 1440,
|
|
@@ -46094,9 +46173,9 @@ async function downloadReadableShot(url, file) {
|
|
|
46094
46173
|
const response = await fetch(url);
|
|
46095
46174
|
if (!response.ok) return null;
|
|
46096
46175
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
46097
|
-
await mkdir12(
|
|
46176
|
+
await mkdir12(path36.dirname(file), { recursive: true });
|
|
46098
46177
|
await writeFile17(file, shot);
|
|
46099
|
-
return
|
|
46178
|
+
return path36.relative(process.cwd(), file);
|
|
46100
46179
|
} catch {
|
|
46101
46180
|
return null;
|
|
46102
46181
|
}
|
|
@@ -46188,13 +46267,13 @@ function buildSearchBody(args) {
|
|
|
46188
46267
|
return body;
|
|
46189
46268
|
}
|
|
46190
46269
|
async function downloadShots(results) {
|
|
46191
|
-
const dir =
|
|
46270
|
+
const dir = path37.join(process.cwd(), ".baker", "inspiration");
|
|
46192
46271
|
const saved = /* @__PURE__ */ new Map();
|
|
46193
46272
|
await Promise.all(
|
|
46194
46273
|
results.map(async (result) => {
|
|
46195
46274
|
const file = await downloadReadableShot(
|
|
46196
46275
|
result.desktopShotUrl,
|
|
46197
|
-
|
|
46276
|
+
path37.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
46198
46277
|
);
|
|
46199
46278
|
if (file) saved.set(result.id, file);
|
|
46200
46279
|
})
|
|
@@ -46422,7 +46501,7 @@ var sequencesCommand = defineCommand164({
|
|
|
46422
46501
|
});
|
|
46423
46502
|
|
|
46424
46503
|
// src/commands/landing/inspiration/view.ts
|
|
46425
|
-
import
|
|
46504
|
+
import path38 from "path";
|
|
46426
46505
|
import { defineCommand as defineCommand165 } from "citty";
|
|
46427
46506
|
registerSchema({
|
|
46428
46507
|
command: "landing.inspiration.view",
|
|
@@ -46455,12 +46534,12 @@ var viewCommand2 = defineCommand165({
|
|
|
46455
46534
|
const id = args.id;
|
|
46456
46535
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
46457
46536
|
const section = data.section;
|
|
46458
|
-
const dir =
|
|
46537
|
+
const dir = path38.join(process.cwd(), ".baker", "inspiration", id);
|
|
46459
46538
|
const ext = READABLE_SHOT.extension;
|
|
46460
46539
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
46461
|
-
downloadReadableShot(section.desktopShotUrl,
|
|
46462
|
-
downloadReadableShot(section.mobileShotUrl,
|
|
46463
|
-
downloadReadableShot(section.motionFilmstripUrl,
|
|
46540
|
+
downloadReadableShot(section.desktopShotUrl, path38.join(dir, `desktop.${ext}`)),
|
|
46541
|
+
downloadReadableShot(section.mobileShotUrl, path38.join(dir, `mobile.${ext}`)),
|
|
46542
|
+
downloadReadableShot(section.motionFilmstripUrl, path38.join(dir, `motion-filmstrip.${ext}`))
|
|
46464
46543
|
]);
|
|
46465
46544
|
const full = args.full;
|
|
46466
46545
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -48309,8 +48388,8 @@ var listCommand15 = defineCommand184({
|
|
|
48309
48388
|
});
|
|
48310
48389
|
|
|
48311
48390
|
// src/commands/scheduled-actions/templates.ts
|
|
48312
|
-
import { readFile as
|
|
48313
|
-
import
|
|
48391
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
48392
|
+
import path39 from "path";
|
|
48314
48393
|
import { defineCommand as defineCommand185 } from "citty";
|
|
48315
48394
|
registerSchema({
|
|
48316
48395
|
command: "scheduled-actions.templates",
|
|
@@ -48413,7 +48492,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
48413
48492
|
}
|
|
48414
48493
|
if (save.length > 0) {
|
|
48415
48494
|
const briefFile = flag("brief-file");
|
|
48416
|
-
const brief = briefFile.length > 0 ? await
|
|
48495
|
+
const brief = briefFile.length > 0 ? await readFile25(path39.resolve(briefFile), "utf8") : flag("brief");
|
|
48417
48496
|
if (brief.trim().length === 0) {
|
|
48418
48497
|
failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
48419
48498
|
}
|
|
@@ -48884,7 +48963,7 @@ function parseImageRefs(spec) {
|
|
|
48884
48963
|
}
|
|
48885
48964
|
var defaultDeps = {
|
|
48886
48965
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
48887
|
-
upload: (
|
|
48966
|
+
upload: (path41) => uploadLocalImage({ file: path41, contentType: detectImageContentType(path41), source: "uploaded" })
|
|
48888
48967
|
};
|
|
48889
48968
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
48890
48969
|
const refs = parseImageRefs(spec);
|
|
@@ -48904,10 +48983,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
48904
48983
|
}
|
|
48905
48984
|
return { imageIds, added };
|
|
48906
48985
|
}
|
|
48907
|
-
function uploadFailure(
|
|
48986
|
+
function uploadFailure(path41) {
|
|
48908
48987
|
return (error) => {
|
|
48909
48988
|
if (error instanceof ApiError) throw error;
|
|
48910
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
48989
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path41}" as an image.`);
|
|
48911
48990
|
};
|
|
48912
48991
|
}
|
|
48913
48992
|
|
|
@@ -49979,10 +50058,10 @@ async function stageOp4(op) {
|
|
|
49979
50058
|
handleError5(err);
|
|
49980
50059
|
}
|
|
49981
50060
|
}
|
|
49982
|
-
async function draftAction3(
|
|
50061
|
+
async function draftAction3(path41, body, chat) {
|
|
49983
50062
|
const chatId = resolveChatId(chat);
|
|
49984
50063
|
try {
|
|
49985
|
-
const data = await apiPost(
|
|
50064
|
+
const data = await apiPost(path41, { chatId, ...body });
|
|
49986
50065
|
writeJsonEnvelope({ ok: true, data });
|
|
49987
50066
|
return data;
|
|
49988
50067
|
} catch (err) {
|
|
@@ -51089,7 +51168,7 @@ var groupCommand2 = defineCommand210({
|
|
|
51089
51168
|
// src/commands/videos/ingest.ts
|
|
51090
51169
|
import { mkdtemp as mkdtemp3, rm as rm8, stat as stat7 } from "fs/promises";
|
|
51091
51170
|
import { tmpdir as tmpdir4 } from "os";
|
|
51092
|
-
import
|
|
51171
|
+
import path40 from "path";
|
|
51093
51172
|
import { defineCommand as defineCommand211 } from "citty";
|
|
51094
51173
|
|
|
51095
51174
|
// src/lib/streamUpload.ts
|
|
@@ -51440,7 +51519,7 @@ function ingestUrl(args) {
|
|
|
51440
51519
|
}
|
|
51441
51520
|
async function downloadThenIngest(args, country) {
|
|
51442
51521
|
const vimeoCookie = captureVimeoCookie();
|
|
51443
|
-
const workDir = await mkdtemp3(
|
|
51522
|
+
const workDir = await mkdtemp3(path40.join(tmpdir4(), "videos-ingest-"));
|
|
51444
51523
|
try {
|
|
51445
51524
|
const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
|
|
51446
51525
|
if (isAudioOnly(probe.info)) {
|
|
@@ -51576,7 +51655,7 @@ var searchCommand4 = defineCommand212({
|
|
|
51576
51655
|
var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
51577
51656
|
|
|
51578
51657
|
// src/commands/videos/upload.ts
|
|
51579
|
-
import { readFile as
|
|
51658
|
+
import { readFile as readFile26, stat as stat8 } from "fs/promises";
|
|
51580
51659
|
import { basename as basename3, extname as extname4 } from "path";
|
|
51581
51660
|
import { defineCommand as defineCommand213 } from "citty";
|
|
51582
51661
|
var MIME_MAP = {
|
|
@@ -51673,7 +51752,7 @@ var uploadCommand2 = defineCommand213({
|
|
|
51673
51752
|
originalFilename,
|
|
51674
51753
|
descriptionContext
|
|
51675
51754
|
});
|
|
51676
|
-
const fileBuffer = await
|
|
51755
|
+
const fileBuffer = await readFile26(filePath);
|
|
51677
51756
|
const uploadResponse = await fetch(uploadUrl, {
|
|
51678
51757
|
method: "PUT",
|
|
51679
51758
|
headers: { "Content-Type": contentType },
|
|
@@ -53060,7 +53139,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
|
|
|
53060
53139
|
};
|
|
53061
53140
|
}
|
|
53062
53141
|
function commandPathOf(root, argv) {
|
|
53063
|
-
const
|
|
53142
|
+
const path41 = [];
|
|
53064
53143
|
let command = root;
|
|
53065
53144
|
for (const token of argv) {
|
|
53066
53145
|
if (token === "--" || token.startsWith("-")) {
|
|
@@ -53071,10 +53150,10 @@ function commandPathOf(root, argv) {
|
|
|
53071
53150
|
if (next === void 0 || typeof next !== "object") {
|
|
53072
53151
|
break;
|
|
53073
53152
|
}
|
|
53074
|
-
|
|
53153
|
+
path41.push(token);
|
|
53075
53154
|
command = next;
|
|
53076
53155
|
}
|
|
53077
|
-
return
|
|
53156
|
+
return path41.join(" ");
|
|
53078
53157
|
}
|
|
53079
53158
|
function refuseUnknownFlags(root, argv) {
|
|
53080
53159
|
const unknown = findUnknownFlags(root, argv);
|