@koda-sl/baker-cli 0.200.0 → 0.201.0-dev.0a9adf6f7
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 +38 -15
- package/dist/{chunk-5MPIOGRO.js → chunk-CAWFJ5X3.js} +265 -215
- package/dist/chunk-CAWFJ5X3.js.map +1 -0
- package/dist/cli.js +542 -368
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-5MPIOGRO.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
SEEDANCE_PROFILE,
|
|
19
19
|
VIDEO_GENERATE_MODELS,
|
|
20
20
|
ValidationError,
|
|
21
|
+
YtDlpError,
|
|
21
22
|
clipParamRecipe,
|
|
22
23
|
clipProfileFor,
|
|
23
24
|
collectAssetRefLikes,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
promptMaxLength,
|
|
42
43
|
requireCredentialsFromEnv,
|
|
43
44
|
resolveConcurrency,
|
|
45
|
+
runYtDlp,
|
|
44
46
|
sha256Hex,
|
|
45
47
|
spineInputFlags,
|
|
46
48
|
spineInputOps,
|
|
@@ -49,8 +51,9 @@ import {
|
|
|
49
51
|
supportsReferenceToVideo,
|
|
50
52
|
toModelSafeImage,
|
|
51
53
|
ulid,
|
|
52
|
-
validateCanvasDeep
|
|
53
|
-
|
|
54
|
+
validateCanvasDeep,
|
|
55
|
+
ytDlpBlockSignal
|
|
56
|
+
} from "./chunk-CAWFJ5X3.js";
|
|
54
57
|
import {
|
|
55
58
|
csvOrJson,
|
|
56
59
|
daysAgoIso,
|
|
@@ -99,7 +102,7 @@ import {
|
|
|
99
102
|
} from "./chunk-6NZG2TCM.js";
|
|
100
103
|
|
|
101
104
|
// src/cli.ts
|
|
102
|
-
import { defineCommand as
|
|
105
|
+
import { defineCommand as defineCommand217, runMain } from "citty";
|
|
103
106
|
|
|
104
107
|
// src/commands/actions/index.ts
|
|
105
108
|
import { defineCommand as defineCommand18 } from "citty";
|
|
@@ -5686,6 +5689,7 @@ var testimonialsOutscraperWebhookResponseSchema = z18.object({
|
|
|
5686
5689
|
// ../api/src/videos.ts
|
|
5687
5690
|
import { z as z19 } from "zod";
|
|
5688
5691
|
var videoStatusSchema = z19.enum(["uploading", "uploaded", "processing", "ready", "error"]);
|
|
5692
|
+
var videoSourceSchema = z19.enum(["uploaded", "instagram", "url", "youtube"]);
|
|
5689
5693
|
var videoTranscriptSegmentSchema = z19.object({
|
|
5690
5694
|
text: z19.string(),
|
|
5691
5695
|
startSecond: z19.number(),
|
|
@@ -5786,6 +5790,19 @@ var videosUploadRequestSchema = z19.object({
|
|
|
5786
5790
|
descriptionContext: z19.string().optional()
|
|
5787
5791
|
});
|
|
5788
5792
|
var videosUploadResponseSchema = z19.object({ uploadUrl: z19.string(), videoId: z19.string() });
|
|
5793
|
+
var videosIngestRequestSchema = z19.object({
|
|
5794
|
+
// Must be a direct media file Mux can pull (mp4/mov/webm/…). A page URL —
|
|
5795
|
+
// a YouTube watch link, a TikTok post — is not one: the CLI resolves those to
|
|
5796
|
+
// a media file before calling this route.
|
|
5797
|
+
url: z19.string().url(),
|
|
5798
|
+
source: videoSourceSchema,
|
|
5799
|
+
externalId: z19.string().optional(),
|
|
5800
|
+
externalUrl: z19.string().optional()
|
|
5801
|
+
});
|
|
5802
|
+
var videosIngestResponseSchema = z19.object({
|
|
5803
|
+
videoId: z19.string(),
|
|
5804
|
+
deduped: z19.boolean()
|
|
5805
|
+
});
|
|
5789
5806
|
var videosDeleteRequestSchema = z19.object({ id: z19.string().min(1, "Missing video ID") });
|
|
5790
5807
|
var videosDeleteResponseSchema = z19.object({ ok: z19.literal(true) });
|
|
5791
5808
|
|
|
@@ -20664,99 +20681,14 @@ async function probeDuration(filePath) {
|
|
|
20664
20681
|
}
|
|
20665
20682
|
|
|
20666
20683
|
// src/commands/canvas/rerun.ts
|
|
20667
|
-
import
|
|
20684
|
+
import path14 from "path";
|
|
20668
20685
|
import { defineCommand as defineCommand96 } from "citty";
|
|
20669
20686
|
|
|
20670
20687
|
// src/commands/canvas/run.ts
|
|
20671
20688
|
import { readFile as readFile10 } from "fs/promises";
|
|
20672
|
-
import
|
|
20689
|
+
import path13 from "path";
|
|
20673
20690
|
import { defineCommand as defineCommand95 } from "citty";
|
|
20674
20691
|
|
|
20675
|
-
// src/commands/canvas/normalize-paths.ts
|
|
20676
|
-
import { existsSync as existsSync3, realpathSync } from "fs";
|
|
20677
|
-
import { writeFile as writeFile2 } from "fs/promises";
|
|
20678
|
-
import path4 from "path";
|
|
20679
|
-
function findWorkspaceRoot(startDir, exists = existsSync3, maxDepth = 12) {
|
|
20680
|
-
let dir = path4.resolve(startDir);
|
|
20681
|
-
for (let i = 0; i < maxDepth; i++) {
|
|
20682
|
-
if (exists(path4.join(dir, "package.json"))) return dir;
|
|
20683
|
-
const parent = path4.dirname(dir);
|
|
20684
|
-
if (parent === dir) break;
|
|
20685
|
-
dir = parent;
|
|
20686
|
-
}
|
|
20687
|
-
return null;
|
|
20688
|
-
}
|
|
20689
|
-
function canonicalize(target) {
|
|
20690
|
-
const abs = path4.resolve(target);
|
|
20691
|
-
let dir = abs;
|
|
20692
|
-
for (; ; ) {
|
|
20693
|
-
try {
|
|
20694
|
-
const real = realpathSync(dir);
|
|
20695
|
-
return dir === abs ? real : path4.join(real, path4.relative(dir, abs));
|
|
20696
|
-
} catch {
|
|
20697
|
-
const parent = path4.dirname(dir);
|
|
20698
|
-
if (parent === dir) return abs;
|
|
20699
|
-
dir = parent;
|
|
20700
|
-
}
|
|
20701
|
-
}
|
|
20702
|
-
}
|
|
20703
|
-
function isInside(root, target) {
|
|
20704
|
-
const rel = path4.relative(root, target);
|
|
20705
|
-
return rel !== "" && !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
20706
|
-
}
|
|
20707
|
-
function toCanvasRelative(canvasDir, target) {
|
|
20708
|
-
return path4.relative(canvasDir, target).split(path4.sep).join("/");
|
|
20709
|
-
}
|
|
20710
|
-
var RUNTIME_WORKSPACE_ROOT = "/home/user/repo";
|
|
20711
|
-
function rewriteTarget(value, workspaceRoot) {
|
|
20712
|
-
if (typeof value !== "string" || value.length === 0) return null;
|
|
20713
|
-
if (value.includes("[TODO") || looksLikeHttpUrl(value) || !path4.isAbsolute(value)) return null;
|
|
20714
|
-
const target = canonicalize(value);
|
|
20715
|
-
if (isInside(workspaceRoot, target)) return target;
|
|
20716
|
-
const fromRuntime = path4.relative(RUNTIME_WORKSPACE_ROOT, path4.normalize(value));
|
|
20717
|
-
if (fromRuntime !== "" && !fromRuntime.startsWith("..") && !path4.isAbsolute(fromRuntime)) {
|
|
20718
|
-
return path4.join(workspaceRoot, fromRuntime);
|
|
20719
|
-
}
|
|
20720
|
-
return null;
|
|
20721
|
-
}
|
|
20722
|
-
function normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot) {
|
|
20723
|
-
if (!canvas || typeof canvas !== "object") return { canvas, rewrites: [] };
|
|
20724
|
-
const c = canvas;
|
|
20725
|
-
if (!Array.isArray(c.nodes)) return { canvas, rewrites: [] };
|
|
20726
|
-
const root = canonicalize(workspaceRoot);
|
|
20727
|
-
const dir = canonicalize(canvasDir);
|
|
20728
|
-
const rewrites = [];
|
|
20729
|
-
const nodes = c.nodes.map((node) => {
|
|
20730
|
-
if (!node || typeof node !== "object") return node;
|
|
20731
|
-
const n = node;
|
|
20732
|
-
const params = n.params;
|
|
20733
|
-
if (!params || typeof params !== "object") return node;
|
|
20734
|
-
const nodeId = typeof n.id === "string" ? n.id : "?";
|
|
20735
|
-
const key = n.type === "ingest" && params.source === "path" ? "path" : n.type === "hyperframe_render" ? "composition" : null;
|
|
20736
|
-
if (!key) return node;
|
|
20737
|
-
const target = rewriteTarget(params[key], root);
|
|
20738
|
-
if (target === null) return node;
|
|
20739
|
-
const to = toCanvasRelative(dir, target);
|
|
20740
|
-
rewrites.push({ nodeId, param: key, from: params[key], to });
|
|
20741
|
-
return { ...node, params: { ...params, [key]: to } };
|
|
20742
|
-
});
|
|
20743
|
-
return rewrites.length === 0 ? { canvas, rewrites } : { canvas: { ...canvas, nodes }, rewrites };
|
|
20744
|
-
}
|
|
20745
|
-
async function healAbsoluteCanvasPaths(filePath, canvas) {
|
|
20746
|
-
const canvasDir = path4.dirname(filePath);
|
|
20747
|
-
const workspaceRoot = findWorkspaceRoot(canvasDir);
|
|
20748
|
-
if (!workspaceRoot) return { canvas, rewrites: [], text: null };
|
|
20749
|
-
const normalized = normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot);
|
|
20750
|
-
if (normalized.rewrites.length === 0) return { ...normalized, text: null };
|
|
20751
|
-
const text2 = `${JSON.stringify(normalized.canvas, null, 2)}
|
|
20752
|
-
`;
|
|
20753
|
-
await writeFile2(filePath, text2, "utf8");
|
|
20754
|
-
return { ...normalized, text: text2 };
|
|
20755
|
-
}
|
|
20756
|
-
function describeRewrites(rewrites) {
|
|
20757
|
-
return rewrites.map((r) => ` ${r.nodeId}.${r.param}: ${r.from} \u2192 ${r.to}`).join("\n");
|
|
20758
|
-
}
|
|
20759
|
-
|
|
20760
20692
|
// src/commands/canvas/placeholders.ts
|
|
20761
20693
|
function unsuppliedPlaceholderAssets(canvas) {
|
|
20762
20694
|
const nodes = canvas?.nodes;
|
|
@@ -20774,7 +20706,7 @@ function unsuppliedPlaceholderAssets(canvas) {
|
|
|
20774
20706
|
}
|
|
20775
20707
|
|
|
20776
20708
|
// src/commands/canvas/resolve-paths.ts
|
|
20777
|
-
import
|
|
20709
|
+
import path4 from "path";
|
|
20778
20710
|
function resolveRelativeCanvasPaths(canvas, baseDir) {
|
|
20779
20711
|
if (!canvas || typeof canvas !== "object") return canvas;
|
|
20780
20712
|
const c = canvas;
|
|
@@ -20787,24 +20719,24 @@ function resolveNode(node, baseDir) {
|
|
|
20787
20719
|
const params = n.params;
|
|
20788
20720
|
if (!params || typeof params !== "object") return node;
|
|
20789
20721
|
if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
|
|
20790
|
-
return { ...node, params: { ...params, path:
|
|
20722
|
+
return { ...node, params: { ...params, path: path4.resolve(baseDir, params.path) } };
|
|
20791
20723
|
}
|
|
20792
20724
|
if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
|
|
20793
|
-
return { ...node, params: { ...params, composition:
|
|
20725
|
+
return { ...node, params: { ...params, composition: path4.resolve(baseDir, params.composition) } };
|
|
20794
20726
|
}
|
|
20795
20727
|
return node;
|
|
20796
20728
|
}
|
|
20797
20729
|
function isResolvableRelative(value) {
|
|
20798
|
-
return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !
|
|
20730
|
+
return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !path4.isAbsolute(value);
|
|
20799
20731
|
}
|
|
20800
20732
|
|
|
20801
20733
|
// src/commands/canvas/source-version.ts
|
|
20802
20734
|
import { readFile as readFile5 } from "fs/promises";
|
|
20803
|
-
import
|
|
20735
|
+
import path6 from "path";
|
|
20804
20736
|
|
|
20805
20737
|
// src/commands/canvas/scene-files.ts
|
|
20806
|
-
import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as
|
|
20807
|
-
import
|
|
20738
|
+
import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
20739
|
+
import path5 from "path";
|
|
20808
20740
|
var SCENES_DIR = "scenes";
|
|
20809
20741
|
var GLOBAL_PROMPT_FILE = "prompt.json";
|
|
20810
20742
|
var REBUILD_FILE = "prompt.rebuild.json";
|
|
@@ -20821,19 +20753,19 @@ function splitBlueprint(blueprint) {
|
|
|
20821
20753
|
}
|
|
20822
20754
|
async function writeSceneFiles(outDir, blueprint) {
|
|
20823
20755
|
const { global, scenes } = splitBlueprint(blueprint);
|
|
20824
|
-
await
|
|
20756
|
+
await writeFile2(path5.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
|
|
20825
20757
|
`, "utf8");
|
|
20826
|
-
const scenesDir =
|
|
20758
|
+
const scenesDir = path5.join(outDir, SCENES_DIR);
|
|
20827
20759
|
await mkdir2(scenesDir, { recursive: true });
|
|
20828
20760
|
const written = /* @__PURE__ */ new Set();
|
|
20829
20761
|
for (let i = 0; i < scenes.length; i++) {
|
|
20830
20762
|
const name = sceneFileName(i, scenes.length);
|
|
20831
20763
|
written.add(name);
|
|
20832
|
-
await
|
|
20764
|
+
await writeFile2(path5.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
|
|
20833
20765
|
`, "utf8");
|
|
20834
20766
|
}
|
|
20835
20767
|
for (const name of await listSceneFileNames(scenesDir)) {
|
|
20836
|
-
if (!written.has(name)) await rm(
|
|
20768
|
+
if (!written.has(name)) await rm(path5.join(scenesDir, name), { force: true });
|
|
20837
20769
|
}
|
|
20838
20770
|
}
|
|
20839
20771
|
async function listSceneFileNames(scenesDir) {
|
|
@@ -20846,13 +20778,13 @@ async function listSceneFileNames(scenesDir) {
|
|
|
20846
20778
|
return entries.filter((n) => /^s\d+\.json$/.test(n)).sort(bySceneIndex);
|
|
20847
20779
|
}
|
|
20848
20780
|
async function listSceneFiles(creativeDir) {
|
|
20849
|
-
const scenesDir =
|
|
20850
|
-
return (await listSceneFileNames(scenesDir)).map((n) =>
|
|
20781
|
+
const scenesDir = path5.join(creativeDir, SCENES_DIR);
|
|
20782
|
+
return (await listSceneFileNames(scenesDir)).map((n) => path5.join(scenesDir, n));
|
|
20851
20783
|
}
|
|
20852
20784
|
async function reassembleBlueprint(creativeDir) {
|
|
20853
20785
|
const files = await listSceneFiles(creativeDir);
|
|
20854
20786
|
if (files.length === 0) return null;
|
|
20855
|
-
const globalRaw = await readFile4(
|
|
20787
|
+
const globalRaw = await readFile4(path5.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
|
|
20856
20788
|
const global = JSON.parse(globalRaw);
|
|
20857
20789
|
const scenes = [];
|
|
20858
20790
|
for (const file of files) {
|
|
@@ -20874,8 +20806,8 @@ async function computeSourceSha(canvasPath) {
|
|
|
20874
20806
|
} catch {
|
|
20875
20807
|
return void 0;
|
|
20876
20808
|
}
|
|
20877
|
-
const canvasDir =
|
|
20878
|
-
const promptPath =
|
|
20809
|
+
const canvasDir = path6.dirname(canvasPath);
|
|
20810
|
+
const promptPath = path6.join(canvasDir, "prompt.json");
|
|
20879
20811
|
let promptBytes;
|
|
20880
20812
|
try {
|
|
20881
20813
|
promptBytes = await readFile5(promptPath);
|
|
@@ -20902,7 +20834,7 @@ async function computeSourceSha(canvasPath) {
|
|
|
20902
20834
|
|
|
20903
20835
|
// src/commands/canvas/scene-projection.ts
|
|
20904
20836
|
import { readFile as readFile6 } from "fs/promises";
|
|
20905
|
-
import
|
|
20837
|
+
import path7 from "path";
|
|
20906
20838
|
|
|
20907
20839
|
// src/engine/scaffold/video.ts
|
|
20908
20840
|
import { toCardinal as nwAr } from "n2words/ar-SA";
|
|
@@ -24188,12 +24120,12 @@ function nonPromptParamsDiverge(live = {}, rebuilt = {}) {
|
|
|
24188
24120
|
return false;
|
|
24189
24121
|
}
|
|
24190
24122
|
async function syncSceneNodeParams(canvas, canvasPath, log) {
|
|
24191
|
-
const creativeDir =
|
|
24123
|
+
const creativeDir = path7.dirname(canvasPath);
|
|
24192
24124
|
const blueprint = await reassembleBlueprint(creativeDir);
|
|
24193
24125
|
if (!blueprint) return "not_applicable";
|
|
24194
24126
|
let rebuildRaw;
|
|
24195
24127
|
try {
|
|
24196
|
-
rebuildRaw = await readFile6(
|
|
24128
|
+
rebuildRaw = await readFile6(path7.join(creativeDir, REBUILD_FILE), "utf8");
|
|
24197
24129
|
} catch {
|
|
24198
24130
|
return "not_applicable";
|
|
24199
24131
|
}
|
|
@@ -24234,7 +24166,7 @@ async function syncSceneNodeParams(canvas, canvasPath, log) {
|
|
|
24234
24166
|
}
|
|
24235
24167
|
|
|
24236
24168
|
// src/commands/canvas/style-projection.ts
|
|
24237
|
-
import { readFile as readFile7, writeFile as
|
|
24169
|
+
import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
|
|
24238
24170
|
function findBlueprintProjection(canvas) {
|
|
24239
24171
|
if (!canvas || typeof canvas !== "object") return null;
|
|
24240
24172
|
const nodes = canvas.nodes;
|
|
@@ -24265,7 +24197,7 @@ async function syncStyleProjection(canvas, log) {
|
|
|
24265
24197
|
const rendered = renderStyleProjection(await readFile7(pair.promptPath, "utf8"));
|
|
24266
24198
|
const current = await readFile7(pair.stylePath, "utf8").catch(() => null);
|
|
24267
24199
|
if (current === rendered) return "up_to_date";
|
|
24268
|
-
await
|
|
24200
|
+
await writeFile3(pair.stylePath, rendered, "utf8");
|
|
24269
24201
|
log(
|
|
24270
24202
|
"[style] prompt.style.json regenerated from prompt.json \u2014 every frame's shared ad spec changed; affected image frames will re-bill on the next run"
|
|
24271
24203
|
);
|
|
@@ -24325,13 +24257,13 @@ ${body}` : header || body || compactJson(record);
|
|
|
24325
24257
|
}
|
|
24326
24258
|
|
|
24327
24259
|
// src/commands/canvas/run-record.ts
|
|
24328
|
-
import
|
|
24260
|
+
import path8 from "path";
|
|
24329
24261
|
var MAX_RUN_NODES = 200;
|
|
24330
24262
|
var MAX_OUTPUTS_PER_NODE = 10;
|
|
24331
24263
|
var MAX_FINAL_OUTPUTS = 10;
|
|
24332
24264
|
var MAX_CREATIVE_SLUG_LENGTH = 100;
|
|
24333
24265
|
function creativeSlugFromCanvasPath(filePath) {
|
|
24334
|
-
const normalized = filePath.split(
|
|
24266
|
+
const normalized = filePath.split(path8.sep).join("/");
|
|
24335
24267
|
const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
|
|
24336
24268
|
const slug = match?.[1] ?? null;
|
|
24337
24269
|
return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
|
|
@@ -24619,7 +24551,7 @@ var RunRecordPoster = class {
|
|
|
24619
24551
|
|
|
24620
24552
|
// src/commands/canvas/run-retention.ts
|
|
24621
24553
|
import { rm as rm2 } from "fs/promises";
|
|
24622
|
-
import
|
|
24554
|
+
import path9 from "path";
|
|
24623
24555
|
function runDirsToPrune(entries, keep, currentRunId) {
|
|
24624
24556
|
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
24625
24557
|
if (keep <= 0) return runs;
|
|
@@ -24636,7 +24568,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
24636
24568
|
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
24637
24569
|
if (toPrune.length === 0) return;
|
|
24638
24570
|
for (const dir of toPrune) {
|
|
24639
|
-
await rm2(
|
|
24571
|
+
await rm2(path9.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
24640
24572
|
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
24641
24573
|
);
|
|
24642
24574
|
}
|
|
@@ -24644,13 +24576,13 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
24644
24576
|
}
|
|
24645
24577
|
|
|
24646
24578
|
// src/commands/canvas/dirty-marker.ts
|
|
24647
|
-
import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as
|
|
24648
|
-
import
|
|
24579
|
+
import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as writeFile4 } from "fs/promises";
|
|
24580
|
+
import path10 from "path";
|
|
24649
24581
|
function creativeDirtyDir(base) {
|
|
24650
|
-
return base ??
|
|
24582
|
+
return base ?? path10.resolve("canvas", ".dirty");
|
|
24651
24583
|
}
|
|
24652
24584
|
function dirtyMarkerFile(slug, base) {
|
|
24653
|
-
return
|
|
24585
|
+
return path10.join(creativeDirtyDir(base), `${slug}.json`);
|
|
24654
24586
|
}
|
|
24655
24587
|
async function clearCreativeDirty(slug, base) {
|
|
24656
24588
|
try {
|
|
@@ -24660,18 +24592,18 @@ async function clearCreativeDirty(slug, base) {
|
|
|
24660
24592
|
}
|
|
24661
24593
|
|
|
24662
24594
|
// src/commands/canvas/run-resume.ts
|
|
24663
|
-
import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as
|
|
24664
|
-
import
|
|
24595
|
+
import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as writeFile5 } from "fs/promises";
|
|
24596
|
+
import path11 from "path";
|
|
24665
24597
|
function markerKey(canvasPath) {
|
|
24666
24598
|
const slug = creativeSlugFromCanvasPath(canvasPath);
|
|
24667
|
-
const identity = slug ??
|
|
24599
|
+
const identity = slug ?? path11.relative(process.cwd(), path11.resolve(canvasPath));
|
|
24668
24600
|
return sha256Hex(Buffer.from(identity)).slice(0, 32);
|
|
24669
24601
|
}
|
|
24670
24602
|
function legacyMarkerKey(canvasPath) {
|
|
24671
|
-
return sha256Hex(Buffer.from(
|
|
24603
|
+
return sha256Hex(Buffer.from(path11.resolve(canvasPath))).slice(0, 32);
|
|
24672
24604
|
}
|
|
24673
24605
|
function markerFile(outputsDir, key) {
|
|
24674
|
-
return
|
|
24606
|
+
return path11.join(outputsDir, ".inflight", `${key}.json`);
|
|
24675
24607
|
}
|
|
24676
24608
|
var REMOTE_ADOPT_STALE_MS = 12e4;
|
|
24677
24609
|
function classifyRemoteRun(run, now) {
|
|
@@ -24718,8 +24650,8 @@ async function readMarkerRunId(outputsDir, canvasPath) {
|
|
|
24718
24650
|
async function markRunInFlight(outputsDir, canvasPath, runId) {
|
|
24719
24651
|
try {
|
|
24720
24652
|
const file = markerFile(outputsDir, markerKey(canvasPath));
|
|
24721
|
-
await mkdir4(
|
|
24722
|
-
await
|
|
24653
|
+
await mkdir4(path11.dirname(file), { recursive: true });
|
|
24654
|
+
await writeFile5(file, JSON.stringify({ runId, canvasPath: path11.resolve(canvasPath), startedAt: Date.now() }));
|
|
24723
24655
|
} catch {
|
|
24724
24656
|
}
|
|
24725
24657
|
}
|
|
@@ -24733,8 +24665,8 @@ async function clearRunMarker(outputsDir, canvasPath) {
|
|
|
24733
24665
|
}
|
|
24734
24666
|
|
|
24735
24667
|
// src/commands/canvas/run-snapshot.ts
|
|
24736
|
-
import { mkdir as mkdir5, readdir as readdir4, readFile as readFile9, stat as stat2, writeFile as
|
|
24737
|
-
import
|
|
24668
|
+
import { mkdir as mkdir5, readdir as readdir4, readFile as readFile9, stat as stat2, writeFile as writeFile6 } from "fs/promises";
|
|
24669
|
+
import path12 from "path";
|
|
24738
24670
|
var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
|
|
24739
24671
|
var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
|
|
24740
24672
|
var EXT_TO_MIME = {
|
|
@@ -24761,15 +24693,15 @@ var EXT_TO_MIME = {
|
|
|
24761
24693
|
woff2: "font/woff2"
|
|
24762
24694
|
};
|
|
24763
24695
|
function mimeForFile(filePath) {
|
|
24764
|
-
const ext =
|
|
24696
|
+
const ext = path12.extname(filePath).slice(1).toLowerCase();
|
|
24765
24697
|
return EXT_TO_MIME[ext] ?? "application/octet-stream";
|
|
24766
24698
|
}
|
|
24767
24699
|
function toPosix(p) {
|
|
24768
|
-
return p.split(
|
|
24700
|
+
return p.split(path12.sep).join("/");
|
|
24769
24701
|
}
|
|
24770
|
-
function
|
|
24771
|
-
const rel =
|
|
24772
|
-
return !rel.startsWith("..") && !
|
|
24702
|
+
function isInside(dir, target) {
|
|
24703
|
+
const rel = path12.relative(dir, target);
|
|
24704
|
+
return !rel.startsWith("..") && !path12.isAbsolute(rel);
|
|
24773
24705
|
}
|
|
24774
24706
|
function localPathRefsFromCanvas(parsed) {
|
|
24775
24707
|
const nodes = parsed?.nodes;
|
|
@@ -24794,24 +24726,24 @@ function isSnapshotablePath(value) {
|
|
|
24794
24726
|
async function sourceRefsToSnapshot(canvasDir, parsed) {
|
|
24795
24727
|
const refs = new Set(localPathRefsFromCanvas(parsed));
|
|
24796
24728
|
for (const sceneFile of await listSceneFiles(canvasDir)) {
|
|
24797
|
-
refs.add(toPosix(
|
|
24729
|
+
refs.add(toPosix(path12.relative(canvasDir, sceneFile)));
|
|
24798
24730
|
}
|
|
24799
24731
|
refs.add(REBUILD_FILE);
|
|
24800
24732
|
return [...refs];
|
|
24801
24733
|
}
|
|
24802
24734
|
async function uploadRunSnapshot(client, opts) {
|
|
24803
24735
|
try {
|
|
24804
|
-
const canvasDir =
|
|
24736
|
+
const canvasDir = path12.dirname(opts.canvasPath);
|
|
24805
24737
|
const put = (bytes, mime) => putContentAddressed(client, bytes, mime, opts.signal);
|
|
24806
24738
|
const canvasBytes = Buffer.from(opts.raw);
|
|
24807
24739
|
const canvasUpload = await put(canvasBytes, "application/json");
|
|
24808
24740
|
const files = [];
|
|
24809
24741
|
const skipped = [];
|
|
24810
24742
|
for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
|
|
24811
|
-
const abs =
|
|
24812
|
-
if (!
|
|
24743
|
+
const abs = path12.isAbsolute(refPath) ? refPath : path12.resolve(canvasDir, refPath);
|
|
24744
|
+
if (!isInside(canvasDir, abs)) {
|
|
24813
24745
|
skipped.push({
|
|
24814
|
-
path: toPosix(
|
|
24746
|
+
path: toPosix(path12.relative(canvasDir, abs)),
|
|
24815
24747
|
reason: "outside the creative folder \u2014 read from the workspace on rerun"
|
|
24816
24748
|
});
|
|
24817
24749
|
continue;
|
|
@@ -24820,12 +24752,12 @@ async function uploadRunSnapshot(client, opts) {
|
|
|
24820
24752
|
try {
|
|
24821
24753
|
st = await stat2(abs);
|
|
24822
24754
|
} catch {
|
|
24823
|
-
skipped.push({ path: toPosix(
|
|
24755
|
+
skipped.push({ path: toPosix(path12.relative(canvasDir, abs)), reason: "missing" });
|
|
24824
24756
|
continue;
|
|
24825
24757
|
}
|
|
24826
24758
|
const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
|
|
24827
24759
|
for (const file of fileList) {
|
|
24828
|
-
const rel = toPosix(
|
|
24760
|
+
const rel = toPosix(path12.relative(canvasDir, file));
|
|
24829
24761
|
const size = (await stat2(file)).size;
|
|
24830
24762
|
if (size > MAX_SNAPSHOT_FILE_BYTES) {
|
|
24831
24763
|
skipped.push({ path: rel, reason: `too large (${size} bytes)` });
|
|
@@ -24840,7 +24772,7 @@ async function uploadRunSnapshot(client, opts) {
|
|
|
24840
24772
|
schema: SNAPSHOT_SCHEMA,
|
|
24841
24773
|
creativeSlug: opts.creativeSlug,
|
|
24842
24774
|
canvasSha: canvasUpload.sha256,
|
|
24843
|
-
canvas: { path:
|
|
24775
|
+
canvas: { path: path12.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
|
|
24844
24776
|
files,
|
|
24845
24777
|
skipped: skipped.length > 0 ? skipped : void 0
|
|
24846
24778
|
};
|
|
@@ -24867,7 +24799,7 @@ async function putContentAddressed(client, bytes, mime, signal) {
|
|
|
24867
24799
|
}
|
|
24868
24800
|
async function listFilesRecursive(dir) {
|
|
24869
24801
|
const entries = await readdir4(dir, { recursive: true, withFileTypes: true });
|
|
24870
|
-
return entries.filter((d) => d.isFile()).map((d) =>
|
|
24802
|
+
return entries.filter((d) => d.isFile()).map((d) => path12.join(d.parentPath, d.name));
|
|
24871
24803
|
}
|
|
24872
24804
|
var SnapshotConflictError = class extends Error {
|
|
24873
24805
|
conflicts;
|
|
@@ -24879,16 +24811,16 @@ var SnapshotConflictError = class extends Error {
|
|
|
24879
24811
|
};
|
|
24880
24812
|
async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
24881
24813
|
const entries = [manifest.canvas, ...manifest.files];
|
|
24882
|
-
const resolvedTarget =
|
|
24814
|
+
const resolvedTarget = path12.resolve(targetDir);
|
|
24883
24815
|
const planned = [];
|
|
24884
24816
|
const conflicts = [];
|
|
24885
24817
|
const upToDate = [];
|
|
24886
24818
|
for (const entry of entries) {
|
|
24887
|
-
if (
|
|
24819
|
+
if (path12.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
|
|
24888
24820
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
24889
24821
|
}
|
|
24890
|
-
const target =
|
|
24891
|
-
if (target !== resolvedTarget && !target.startsWith(resolvedTarget +
|
|
24822
|
+
const target = path12.resolve(resolvedTarget, entry.path);
|
|
24823
|
+
if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path12.sep)) {
|
|
24892
24824
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
24893
24825
|
}
|
|
24894
24826
|
const existing = await readFile9(target).catch(() => null);
|
|
@@ -24913,11 +24845,11 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
|
24913
24845
|
if (sha256Hex(bytes) !== entry.sha256) {
|
|
24914
24846
|
throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
|
|
24915
24847
|
}
|
|
24916
|
-
await mkdir5(
|
|
24917
|
-
await
|
|
24848
|
+
await mkdir5(path12.dirname(target), { recursive: true });
|
|
24849
|
+
await writeFile6(target, bytes);
|
|
24918
24850
|
restored.push(entry.path);
|
|
24919
24851
|
}
|
|
24920
|
-
return { canvasPath:
|
|
24852
|
+
return { canvasPath: path12.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
|
|
24921
24853
|
}
|
|
24922
24854
|
|
|
24923
24855
|
// src/commands/canvas/run.ts
|
|
@@ -24997,7 +24929,7 @@ function resolveMaxCredits(...candidates) {
|
|
|
24997
24929
|
return void 0;
|
|
24998
24930
|
}
|
|
24999
24931
|
async function executeCanvasRun(opts) {
|
|
25000
|
-
const filePath =
|
|
24932
|
+
const filePath = path13.resolve(opts.file);
|
|
25001
24933
|
const raw = await readFile10(filePath, "utf8");
|
|
25002
24934
|
let parsed;
|
|
25003
24935
|
try {
|
|
@@ -25010,17 +24942,7 @@ async function executeCanvasRun(opts) {
|
|
|
25010
24942
|
}
|
|
25011
24943
|
const attemptedSlug = creativeSlugFromCanvasPath(filePath);
|
|
25012
24944
|
if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
|
|
25013
|
-
|
|
25014
|
-
parsed = healed.canvas;
|
|
25015
|
-
const canvasText = healed.text ?? raw;
|
|
25016
|
-
if (healed.rewrites.length > 0) {
|
|
25017
|
-
process.stderr.write(
|
|
25018
|
-
`[canvas] rewrote ${healed.rewrites.length} absolute asset path(s) to canvas-relative \u2014 they resolve only in this workspace:
|
|
25019
|
-
${describeRewrites(healed.rewrites)}
|
|
25020
|
-
`
|
|
25021
|
-
);
|
|
25022
|
-
}
|
|
25023
|
-
parsed = resolveRelativeCanvasPaths(parsed, path14.dirname(filePath));
|
|
24945
|
+
parsed = resolveRelativeCanvasPaths(parsed, path13.dirname(filePath));
|
|
25024
24946
|
try {
|
|
25025
24947
|
await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
25026
24948
|
`));
|
|
@@ -25123,10 +25045,10 @@ ${describeRewrites(healed.rewrites)}
|
|
|
25123
25045
|
process.stdout.write(`[warn] ${w.code}: ${w.message}
|
|
25124
25046
|
`);
|
|
25125
25047
|
}
|
|
25126
|
-
const canvasSha = sha256Hex(Buffer.from(
|
|
25048
|
+
const canvasSha = sha256Hex(Buffer.from(raw));
|
|
25127
25049
|
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
25128
25050
|
const client = opts.record === false ? null : buildBackendClient();
|
|
25129
|
-
const outputsDir = opts.outputsDir ?
|
|
25051
|
+
const outputsDir = opts.outputsDir ? path13.resolve(opts.outputsDir) : path13.resolve("canvas");
|
|
25130
25052
|
const { runId, resumed, source, concurrentRunId } = await resolveRunId({
|
|
25131
25053
|
explicitRunId: opts.runId,
|
|
25132
25054
|
fresh: opts.fresh === true,
|
|
@@ -25157,10 +25079,10 @@ ${describeRewrites(healed.rewrites)}
|
|
|
25157
25079
|
});
|
|
25158
25080
|
}
|
|
25159
25081
|
await markRunInFlight(outputsDir, filePath, runId);
|
|
25160
|
-
const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw
|
|
25082
|
+
const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw, creativeSlug, parsed }) ?? void 0 : void 0;
|
|
25161
25083
|
const recordMeta = {
|
|
25162
25084
|
creativeSlug,
|
|
25163
|
-
canvasPath:
|
|
25085
|
+
canvasPath: path13.relative(process.cwd(), filePath) || void 0,
|
|
25164
25086
|
canvasSha,
|
|
25165
25087
|
// The fingerprint the dashboard compares against the current source to flag
|
|
25166
25088
|
// "edited since last render". Computed from the on-disk canvas.json +
|
|
@@ -25347,7 +25269,7 @@ var rerunCommand = defineCommand96({
|
|
|
25347
25269
|
if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
|
|
25348
25270
|
fail2("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
|
|
25349
25271
|
}
|
|
25350
|
-
const targetDir =
|
|
25272
|
+
const targetDir = path14.resolve("src", "creatives", slug);
|
|
25351
25273
|
let restoredCanvasPath;
|
|
25352
25274
|
try {
|
|
25353
25275
|
const restore = await restoreRunSnapshot(manifest, targetDir, { force: args["force-remote"] === true });
|
|
@@ -25396,8 +25318,8 @@ async function fetchManifest(url) {
|
|
|
25396
25318
|
}
|
|
25397
25319
|
|
|
25398
25320
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
25399
|
-
import { access, mkdir as mkdir6, readFile as readFile12, writeFile as
|
|
25400
|
-
import
|
|
25321
|
+
import { access, mkdir as mkdir6, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
|
|
25322
|
+
import path18 from "path";
|
|
25401
25323
|
import { defineCommand as defineCommand98 } from "citty";
|
|
25402
25324
|
|
|
25403
25325
|
// src/engine/scaffold/staticAd.ts
|
|
@@ -25679,7 +25601,7 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
25679
25601
|
}
|
|
25680
25602
|
|
|
25681
25603
|
// src/commands/canvas/creative-definition.ts
|
|
25682
|
-
import
|
|
25604
|
+
import path15 from "path";
|
|
25683
25605
|
var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
|
|
25684
25606
|
var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
|
|
25685
25607
|
function titleFromSlug(slug) {
|
|
@@ -25727,16 +25649,16 @@ function buildCreativeDefinition(input) {
|
|
|
25727
25649
|
}
|
|
25728
25650
|
|
|
25729
25651
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
25730
|
-
import
|
|
25652
|
+
import path16 from "path";
|
|
25731
25653
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
25732
25654
|
const file = rawFile.trim();
|
|
25733
25655
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
25734
|
-
const imageSource = imageIsUrl ? file :
|
|
25735
|
-
const outPath = out ?
|
|
25736
|
-
const blueprintPath =
|
|
25737
|
-
const creativeDir = slug ?
|
|
25738
|
-
const definitionPath = creativeDir ?
|
|
25739
|
-
const referencesDir = creativeDir ?
|
|
25656
|
+
const imageSource = imageIsUrl ? file : path16.resolve(cwd, file);
|
|
25657
|
+
const outPath = out ? path16.resolve(cwd, out) : slug ? path16.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path16.join(cwd, "static-ad.canvas.json") : path16.join(path16.dirname(imageSource), "static-ad.canvas.json");
|
|
25658
|
+
const blueprintPath = path16.join(path16.dirname(outPath), "prompt.json");
|
|
25659
|
+
const creativeDir = slug ? path16.dirname(outPath) : null;
|
|
25660
|
+
const definitionPath = creativeDir ? path16.join(creativeDir, "_definition.md") : null;
|
|
25661
|
+
const referencesDir = creativeDir ? path16.join(creativeDir, "references") : null;
|
|
25740
25662
|
return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
|
|
25741
25663
|
}
|
|
25742
25664
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -25747,7 +25669,7 @@ function isValidScaffoldSlug(slug) {
|
|
|
25747
25669
|
|
|
25748
25670
|
// src/commands/canvas/sync-definition.ts
|
|
25749
25671
|
import { readdir as readdir5, readFile as readFile11, stat as stat3 } from "fs/promises";
|
|
25750
|
-
import
|
|
25672
|
+
import path17 from "path";
|
|
25751
25673
|
import { defineCommand as defineCommand97 } from "citty";
|
|
25752
25674
|
|
|
25753
25675
|
// src/commands/canvas/definition-graph.ts
|
|
@@ -25840,15 +25762,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
25840
25762
|
}
|
|
25841
25763
|
}
|
|
25842
25764
|
async function resolveCanvasPath(inputPath) {
|
|
25843
|
-
const resolved =
|
|
25765
|
+
const resolved = path17.resolve(inputPath);
|
|
25844
25766
|
let dir = resolved;
|
|
25845
25767
|
try {
|
|
25846
25768
|
if ((await stat3(resolved)).isFile()) {
|
|
25847
25769
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
25848
|
-
dir =
|
|
25770
|
+
dir = path17.dirname(resolved);
|
|
25849
25771
|
}
|
|
25850
25772
|
} catch {
|
|
25851
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
25773
|
+
dir = resolved.endsWith(".canvas.json") ? path17.dirname(resolved) : resolved;
|
|
25852
25774
|
}
|
|
25853
25775
|
let entries;
|
|
25854
25776
|
try {
|
|
@@ -25859,7 +25781,7 @@ async function resolveCanvasPath(inputPath) {
|
|
|
25859
25781
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
25860
25782
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
25861
25783
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
25862
|
-
return chosen ?
|
|
25784
|
+
return chosen ? path17.join(dir, chosen) : null;
|
|
25863
25785
|
}
|
|
25864
25786
|
var syncDefinitionCommand = defineCommand97({
|
|
25865
25787
|
meta: {
|
|
@@ -26140,7 +26062,7 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26140
26062
|
process.cwd(),
|
|
26141
26063
|
slug
|
|
26142
26064
|
);
|
|
26143
|
-
await mkdir6(
|
|
26065
|
+
await mkdir6(path18.dirname(outPath), { recursive: true });
|
|
26144
26066
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
|
|
26145
26067
|
let durableSourceUrl;
|
|
26146
26068
|
if (referencesDir) {
|
|
@@ -26166,7 +26088,7 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26166
26088
|
if (layout && annotated && typeof annotated === "object") {
|
|
26167
26089
|
annotated.layout = layout;
|
|
26168
26090
|
}
|
|
26169
|
-
await
|
|
26091
|
+
await writeFile7(blueprintPath, `${JSON.stringify(annotated, null, 2)}
|
|
26170
26092
|
`, "utf8");
|
|
26171
26093
|
let canvasImagePath = imageSource;
|
|
26172
26094
|
let canvasImageIsUrl = imageIsUrl;
|
|
@@ -26211,10 +26133,10 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26211
26133
|
);
|
|
26212
26134
|
process.exit(2);
|
|
26213
26135
|
}
|
|
26214
|
-
await
|
|
26136
|
+
await writeFile7(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
26215
26137
|
`, "utf8");
|
|
26216
26138
|
if (definitionPath && !await fileExists(definitionPath)) {
|
|
26217
|
-
await
|
|
26139
|
+
await writeFile7(
|
|
26218
26140
|
definitionPath,
|
|
26219
26141
|
buildCreativeDefinition({
|
|
26220
26142
|
title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
|
|
@@ -26260,7 +26182,7 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26260
26182
|
run_estimated_credits: validation.estimatedCredits
|
|
26261
26183
|
},
|
|
26262
26184
|
checklist: {
|
|
26263
|
-
edit_prompt: `Edit ${
|
|
26185
|
+
edit_prompt: `Edit ${path18.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.`,
|
|
26264
26186
|
assets_to_supply: report.elements,
|
|
26265
26187
|
font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path (the describe pass recorded the ad's typefaces under `fonts` in prompt.json \u2014 match those). The font is wired into the render as a TYPE SPECIMEN reference so generated text takes the brand letterforms. Delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
|
|
26266
26188
|
actor_sheets: report.actor_sheets.length > 0 ? `Each living hero (${report.actor_sheets.join(", ")}) is fused into a generated multi-view reference sheet (image_reference_sheet) that the render grounds on \u2014 so drop ONE clean photo at that hero's ingest and the sheet builds the consistent turnaround. Pass --skip-actor-sheets to ground on the lone photo instead.` : "none (no person/animal heroes detected, or --skip-actor-sheets)",
|
|
@@ -26277,9 +26199,9 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26277
26199
|
});
|
|
26278
26200
|
|
|
26279
26201
|
// src/commands/canvas/scaffold-video.ts
|
|
26280
|
-
import { access as access2, cp, mkdir as mkdir7, readFile as readFile15, rm as rm6, writeFile as
|
|
26202
|
+
import { access as access2, cp, mkdir as mkdir7, readFile as readFile15, rm as rm6, writeFile as writeFile8 } from "fs/promises";
|
|
26281
26203
|
import { tmpdir as tmpdir2 } from "os";
|
|
26282
|
-
import
|
|
26204
|
+
import path21 from "path";
|
|
26283
26205
|
import { defineCommand as defineCommand99 } from "citty";
|
|
26284
26206
|
|
|
26285
26207
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -26419,24 +26341,24 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
26419
26341
|
}
|
|
26420
26342
|
|
|
26421
26343
|
// src/commands/canvas/composition-path.ts
|
|
26422
|
-
import { existsSync as
|
|
26423
|
-
import
|
|
26424
|
-
function resolveShippedCanvasDir(name, startDir, exists =
|
|
26425
|
-
const rel =
|
|
26344
|
+
import { existsSync as existsSync3 } from "fs";
|
|
26345
|
+
import path19 from "path";
|
|
26346
|
+
function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
|
|
26347
|
+
const rel = path19.join("canvas", name);
|
|
26426
26348
|
let dir = startDir;
|
|
26427
26349
|
for (let i = 0; i < maxDepth; i++) {
|
|
26428
|
-
const candidate =
|
|
26429
|
-
if (exists(
|
|
26430
|
-
const parent =
|
|
26350
|
+
const candidate = path19.join(dir, rel);
|
|
26351
|
+
if (exists(path19.join(candidate, "meta.json"))) return candidate;
|
|
26352
|
+
const parent = path19.dirname(dir);
|
|
26431
26353
|
if (parent === dir) break;
|
|
26432
26354
|
dir = parent;
|
|
26433
26355
|
}
|
|
26434
|
-
return
|
|
26356
|
+
return path19.resolve(startDir, "../../../", rel);
|
|
26435
26357
|
}
|
|
26436
26358
|
|
|
26437
26359
|
// src/commands/canvas/gitignore.ts
|
|
26438
26360
|
import { appendFile, readFile as readFile14 } from "fs/promises";
|
|
26439
|
-
import
|
|
26361
|
+
import path20 from "path";
|
|
26440
26362
|
function missingGitignoreEntries(existing, entries) {
|
|
26441
26363
|
const present2 = new Set(
|
|
26442
26364
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -26444,7 +26366,7 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
26444
26366
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
26445
26367
|
}
|
|
26446
26368
|
async function ensureGitignore(dir, entries) {
|
|
26447
|
-
const file =
|
|
26369
|
+
const file = path20.join(dir, ".gitignore");
|
|
26448
26370
|
let existing;
|
|
26449
26371
|
try {
|
|
26450
26372
|
existing = await readFile14(file, "utf8");
|
|
@@ -26505,7 +26427,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
26505
26427
|
async function stageCaptions(outDir, transcript) {
|
|
26506
26428
|
const text2 = transcript?.trim();
|
|
26507
26429
|
if (!text2 || text2 === "[]") return {};
|
|
26508
|
-
const compositionPath =
|
|
26430
|
+
const compositionPath = path21.join(outDir, "tiktok-captions-composition");
|
|
26509
26431
|
await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
26510
26432
|
return { compositionPath };
|
|
26511
26433
|
}
|
|
@@ -26523,12 +26445,12 @@ function patchCompositionHtml(html, dims) {
|
|
|
26523
26445
|
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`);
|
|
26524
26446
|
}
|
|
26525
26447
|
async function stampCompositionDims(compositionDir, dims) {
|
|
26526
|
-
const metaPath =
|
|
26448
|
+
const metaPath = path21.join(compositionDir, "meta.json");
|
|
26527
26449
|
const rawMeta = await readFile15(metaPath, "utf8");
|
|
26528
|
-
await
|
|
26529
|
-
const htmlPath =
|
|
26450
|
+
await writeFile8(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
26451
|
+
const htmlPath = path21.join(compositionDir, "index.html");
|
|
26530
26452
|
const rawHtml = await readFile15(htmlPath, "utf8");
|
|
26531
|
-
await
|
|
26453
|
+
await writeFile8(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
26532
26454
|
}
|
|
26533
26455
|
function parseElements2(raw) {
|
|
26534
26456
|
const parsed = JSON.parse(raw);
|
|
@@ -26575,7 +26497,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
26575
26497
|
"video/x-matroska": ".mkv"
|
|
26576
26498
|
};
|
|
26577
26499
|
function referenceVideoExt(url, contentType) {
|
|
26578
|
-
const fromPath =
|
|
26500
|
+
const fromPath = path21.extname(new URL(url).pathname).toLowerCase();
|
|
26579
26501
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
26580
26502
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
26581
26503
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -26601,7 +26523,7 @@ function videoDefinitionDescription(blueprint) {
|
|
|
26601
26523
|
return typeof product === "string" && product.trim() ? product.trim() : void 0;
|
|
26602
26524
|
}
|
|
26603
26525
|
async function materializeReferenceVideo(fileArg2) {
|
|
26604
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
26526
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path21.resolve(fileArg2);
|
|
26605
26527
|
let bytes;
|
|
26606
26528
|
let contentType;
|
|
26607
26529
|
try {
|
|
@@ -26613,11 +26535,11 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
26613
26535
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
26614
26536
|
}
|
|
26615
26537
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
26616
|
-
const dest =
|
|
26538
|
+
const dest = path21.join(
|
|
26617
26539
|
tmpdir2(),
|
|
26618
26540
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
26619
26541
|
);
|
|
26620
|
-
await
|
|
26542
|
+
await writeFile8(dest, bytes);
|
|
26621
26543
|
return dest;
|
|
26622
26544
|
}
|
|
26623
26545
|
function resolveSeamDedup(raw) {
|
|
@@ -26833,11 +26755,11 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26833
26755
|
} catch (e) {
|
|
26834
26756
|
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
26835
26757
|
}
|
|
26836
|
-
const base =
|
|
26837
|
-
const outPath = args.out ?
|
|
26838
|
-
const outDir =
|
|
26839
|
-
const blueprintPath =
|
|
26840
|
-
const blueprintStylePath =
|
|
26758
|
+
const base = path21.basename(videoPath, path21.extname(videoPath));
|
|
26759
|
+
const outPath = args.out ? path21.resolve(String(args.out)) : slug ? path21.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path21.join(path21.dirname(videoPath), `${base}.video.canvas.json`);
|
|
26760
|
+
const outDir = path21.dirname(outPath);
|
|
26761
|
+
const blueprintPath = path21.join(outDir, "prompt.json");
|
|
26762
|
+
const blueprintStylePath = path21.join(outDir, "prompt.style.json");
|
|
26841
26763
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
26842
26764
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
26843
26765
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -26864,7 +26786,7 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26864
26786
|
await mkdir7(outDir, { recursive: true });
|
|
26865
26787
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
26866
26788
|
await writeSceneFiles(outDir, annotated);
|
|
26867
|
-
await
|
|
26789
|
+
await writeFile8(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
|
|
26868
26790
|
let aspect;
|
|
26869
26791
|
try {
|
|
26870
26792
|
aspect = resolveAspect(
|
|
@@ -26882,10 +26804,10 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26882
26804
|
`
|
|
26883
26805
|
);
|
|
26884
26806
|
}
|
|
26885
|
-
const compositionDest =
|
|
26807
|
+
const compositionDest = path21.join(outDir, "video-overlay-composition");
|
|
26886
26808
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
26887
26809
|
await stampCompositionDims(compositionDest, outDims);
|
|
26888
|
-
const indexPath =
|
|
26810
|
+
const indexPath = path21.join(compositionDest, "index.html");
|
|
26889
26811
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
|
|
26890
26812
|
const indexHtml = await readFile15(indexPath, "utf8");
|
|
26891
26813
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
@@ -26895,16 +26817,16 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26895
26817
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
26896
26818
|
);
|
|
26897
26819
|
}
|
|
26898
|
-
await
|
|
26820
|
+
await writeFile8(indexPath, injected, "utf8");
|
|
26899
26821
|
const captions = await stageCaptions(outDir, transcript);
|
|
26900
26822
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
26901
26823
|
const opts = {
|
|
26902
26824
|
imageModel,
|
|
26903
26825
|
videoModel,
|
|
26904
|
-
overlayCompositionPath:
|
|
26905
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
26906
|
-
blueprintPath:
|
|
26907
|
-
blueprintStylePath:
|
|
26826
|
+
overlayCompositionPath: path21.relative(outDir, compositionDest),
|
|
26827
|
+
captionsCompositionPath: captions.compositionPath ? path21.relative(outDir, captions.compositionPath) : void 0,
|
|
26828
|
+
blueprintPath: path21.relative(outDir, blueprintPath),
|
|
26829
|
+
blueprintStylePath: path21.relative(outDir, blueprintStylePath),
|
|
26908
26830
|
frames,
|
|
26909
26831
|
ambient: Boolean(args.ambient),
|
|
26910
26832
|
seamDedup: resolveSeamDedup(args["seam-dedup"]),
|
|
@@ -26929,10 +26851,10 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26929
26851
|
todo.blocking_validation_issues = validation.issues;
|
|
26930
26852
|
meta.todo = todo;
|
|
26931
26853
|
}
|
|
26932
|
-
await
|
|
26854
|
+
await writeFile8(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
26933
26855
|
`, "utf8");
|
|
26934
|
-
await
|
|
26935
|
-
|
|
26856
|
+
await writeFile8(
|
|
26857
|
+
path21.join(outDir, REBUILD_FILE),
|
|
26936
26858
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
26937
26859
|
`,
|
|
26938
26860
|
"utf8"
|
|
@@ -26957,9 +26879,9 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26957
26879
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
26958
26880
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
26959
26881
|
if (slug) {
|
|
26960
|
-
const definitionPath =
|
|
26882
|
+
const definitionPath = path21.join(outDir, "_definition.md");
|
|
26961
26883
|
if (!await fileExists2(definitionPath)) {
|
|
26962
|
-
await
|
|
26884
|
+
await writeFile8(
|
|
26963
26885
|
definitionPath,
|
|
26964
26886
|
buildCreativeDefinition({
|
|
26965
26887
|
title: titleFromSlug(slug),
|
|
@@ -27012,7 +26934,7 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
27012
26934
|
graph: canvas.metadata?.video?.graph_stats
|
|
27013
26935
|
},
|
|
27014
26936
|
checklist: {
|
|
27015
|
-
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 \`${
|
|
26937
|
+
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 \`${path21.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path21.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 ${path21.basename(blueprintStylePath)}; both are regenerated.`,
|
|
27016
26938
|
recurring_elements_to_supply: report.elements,
|
|
27017
26939
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
27018
26940
|
scene: d.scene,
|
|
@@ -27049,8 +26971,8 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
27049
26971
|
});
|
|
27050
26972
|
|
|
27051
26973
|
// src/commands/canvas/set-prompt.ts
|
|
27052
|
-
import { readFile as readFile16, writeFile as
|
|
27053
|
-
import
|
|
26974
|
+
import { readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
|
|
26975
|
+
import path22 from "path";
|
|
27054
26976
|
import { defineCommand as defineCommand100 } from "citty";
|
|
27055
26977
|
function setNodePrompt(canvas, nodeId, text2) {
|
|
27056
26978
|
const nodes = canvas?.nodes;
|
|
@@ -27078,7 +27000,7 @@ var setPromptCommand = defineCommand100({
|
|
|
27078
27000
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
27079
27001
|
},
|
|
27080
27002
|
async run({ args }) {
|
|
27081
|
-
const filePath =
|
|
27003
|
+
const filePath = path22.resolve(String(args.file));
|
|
27082
27004
|
let canvas;
|
|
27083
27005
|
try {
|
|
27084
27006
|
canvas = JSON.parse(await readFile16(filePath, "utf8"));
|
|
@@ -27088,7 +27010,7 @@ var setPromptCommand = defineCommand100({
|
|
|
27088
27010
|
process.exit(2);
|
|
27089
27011
|
}
|
|
27090
27012
|
let text2;
|
|
27091
|
-
if (args["text-file"]) text2 = await readFile16(
|
|
27013
|
+
if (args["text-file"]) text2 = await readFile16(path22.resolve(String(args["text-file"])), "utf8");
|
|
27092
27014
|
else if (args.text !== void 0) text2 = String(args.text);
|
|
27093
27015
|
else {
|
|
27094
27016
|
process.stderr.write(
|
|
@@ -27109,14 +27031,14 @@ var setPromptCommand = defineCommand100({
|
|
|
27109
27031
|
process.exit(2);
|
|
27110
27032
|
return;
|
|
27111
27033
|
}
|
|
27112
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
27034
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path22.dirname(filePath)), defaultRegistry());
|
|
27113
27035
|
if (!validation.ok) {
|
|
27114
27036
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
27115
27037
|
`);
|
|
27116
27038
|
process.exit(2);
|
|
27117
27039
|
return;
|
|
27118
27040
|
}
|
|
27119
|
-
await
|
|
27041
|
+
await writeFile9(filePath, `${JSON.stringify(updated, null, 2)}
|
|
27120
27042
|
`, "utf8");
|
|
27121
27043
|
process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text2.length }, null, 2)}
|
|
27122
27044
|
`);
|
|
@@ -27125,7 +27047,7 @@ var setPromptCommand = defineCommand100({
|
|
|
27125
27047
|
|
|
27126
27048
|
// src/commands/canvas/validate.ts
|
|
27127
27049
|
import { readFile as readFile17 } from "fs/promises";
|
|
27128
|
-
import
|
|
27050
|
+
import path23 from "path";
|
|
27129
27051
|
import { defineCommand as defineCommand101 } from "citty";
|
|
27130
27052
|
var validateCommand = defineCommand101({
|
|
27131
27053
|
meta: {
|
|
@@ -27134,7 +27056,7 @@ var validateCommand = defineCommand101({
|
|
|
27134
27056
|
},
|
|
27135
27057
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
27136
27058
|
async run({ args }) {
|
|
27137
|
-
const filePath =
|
|
27059
|
+
const filePath = path23.resolve(String(args.file));
|
|
27138
27060
|
const raw = await readFile17(filePath, "utf8");
|
|
27139
27061
|
let parsed;
|
|
27140
27062
|
try {
|
|
@@ -27145,9 +27067,7 @@ var validateCommand = defineCommand101({
|
|
|
27145
27067
|
`);
|
|
27146
27068
|
process.exit(2);
|
|
27147
27069
|
}
|
|
27148
|
-
|
|
27149
|
-
parsed = healed.canvas;
|
|
27150
|
-
parsed = resolveRelativeCanvasPaths(parsed, path24.dirname(filePath));
|
|
27070
|
+
parsed = resolveRelativeCanvasPaths(parsed, path23.dirname(filePath));
|
|
27151
27071
|
let styleProjection = "not_applicable";
|
|
27152
27072
|
try {
|
|
27153
27073
|
styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
@@ -27188,7 +27108,6 @@ var validateCommand = defineCommand101({
|
|
|
27188
27108
|
cost_preview: result.perNodeCredits ?? [],
|
|
27189
27109
|
style_projection: styleProjection,
|
|
27190
27110
|
scene_projection: sceneProjection,
|
|
27191
|
-
normalized_paths: healed.rewrites,
|
|
27192
27111
|
warnings: result.warnings ?? []
|
|
27193
27112
|
},
|
|
27194
27113
|
null,
|
|
@@ -27793,14 +27712,14 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
27793
27712
|
import { defineCommand as defineCommand107 } from "citty";
|
|
27794
27713
|
|
|
27795
27714
|
// src/commands/flows/shared.ts
|
|
27796
|
-
import { existsSync as
|
|
27715
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
|
|
27797
27716
|
import { join as join3 } from "path";
|
|
27798
27717
|
var FLOWS_DIR = "src/lib/flow-engine/flows";
|
|
27799
27718
|
function flowsDir() {
|
|
27800
27719
|
let dir = process.cwd();
|
|
27801
27720
|
for (let i = 0; i < 6; i++) {
|
|
27802
27721
|
const candidate = join3(dir, FLOWS_DIR);
|
|
27803
|
-
if (
|
|
27722
|
+
if (existsSync4(candidate)) {
|
|
27804
27723
|
return candidate;
|
|
27805
27724
|
}
|
|
27806
27725
|
const parent = join3(dir, "..");
|
|
@@ -27817,14 +27736,14 @@ function failLocal(message) {
|
|
|
27817
27736
|
}
|
|
27818
27737
|
function listFlowSlugs() {
|
|
27819
27738
|
const dir = flowsDir();
|
|
27820
|
-
if (!
|
|
27739
|
+
if (!existsSync4(dir)) {
|
|
27821
27740
|
return [];
|
|
27822
27741
|
}
|
|
27823
27742
|
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
|
|
27824
27743
|
}
|
|
27825
27744
|
function readFlowTree(slug) {
|
|
27826
27745
|
const path36 = join3(flowsDir(), slug, "_data.json");
|
|
27827
|
-
if (!
|
|
27746
|
+
if (!existsSync4(path36)) {
|
|
27828
27747
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
27829
27748
|
}
|
|
27830
27749
|
try {
|
|
@@ -28490,7 +28409,7 @@ Examples:
|
|
|
28490
28409
|
});
|
|
28491
28410
|
|
|
28492
28411
|
// src/commands/ga4/query.ts
|
|
28493
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
28412
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
|
|
28494
28413
|
import { resolve as resolve2 } from "path";
|
|
28495
28414
|
import { defineCommand as defineCommand112 } from "citty";
|
|
28496
28415
|
|
|
@@ -28564,7 +28483,7 @@ function writeRowsToFile2(filePath, rows, append) {
|
|
|
28564
28483
|
const fields = extractFields2(rows);
|
|
28565
28484
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
28566
28485
|
if (ext === "csv") {
|
|
28567
|
-
if (!append || !
|
|
28486
|
+
if (!append || !existsSync5(filePath)) {
|
|
28568
28487
|
writeFileSync3(filePath, `${toCsvRow(fields)}
|
|
28569
28488
|
`, "utf-8");
|
|
28570
28489
|
}
|
|
@@ -28575,12 +28494,12 @@ function writeRowsToFile2(filePath, rows, append) {
|
|
|
28575
28494
|
const lines = rows.map((row) => JSON.stringify(row));
|
|
28576
28495
|
const content = `${lines.join("\n")}
|
|
28577
28496
|
`;
|
|
28578
|
-
if (append &&
|
|
28497
|
+
if (append && existsSync5(filePath)) {
|
|
28579
28498
|
appendFileSync2(filePath, content, "utf-8");
|
|
28580
28499
|
} else {
|
|
28581
28500
|
writeFileSync3(filePath, content, "utf-8");
|
|
28582
28501
|
}
|
|
28583
|
-
} else if (append &&
|
|
28502
|
+
} else if (append && existsSync5(filePath)) {
|
|
28584
28503
|
const existing = JSON.parse(readFileSync11(filePath, "utf-8"));
|
|
28585
28504
|
writeFileSync3(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
|
|
28586
28505
|
} else {
|
|
@@ -29104,7 +29023,7 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
|
|
|
29104
29023
|
import { defineCommand as defineCommand118 } from "citty";
|
|
29105
29024
|
|
|
29106
29025
|
// src/commands/gsc/query.ts
|
|
29107
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
29026
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
|
|
29108
29027
|
import { resolve as resolve3 } from "path";
|
|
29109
29028
|
import { defineCommand as defineCommand115 } from "citty";
|
|
29110
29029
|
|
|
@@ -29236,7 +29155,7 @@ function writeRowsToFile3(filePath, rows, append) {
|
|
|
29236
29155
|
const fields = extractFields3(rows);
|
|
29237
29156
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
29238
29157
|
if (ext === "csv") {
|
|
29239
|
-
if (!append || !
|
|
29158
|
+
if (!append || !existsSync6(filePath)) {
|
|
29240
29159
|
writeFileSync4(filePath, `${toCsvRow(fields)}
|
|
29241
29160
|
`, "utf-8");
|
|
29242
29161
|
}
|
|
@@ -29246,12 +29165,12 @@ function writeRowsToFile3(filePath, rows, append) {
|
|
|
29246
29165
|
} else if (ext === "jsonl") {
|
|
29247
29166
|
const content = `${rows.map((row) => JSON.stringify(row)).join("\n")}
|
|
29248
29167
|
`;
|
|
29249
|
-
if (append &&
|
|
29168
|
+
if (append && existsSync6(filePath)) {
|
|
29250
29169
|
appendFileSync3(filePath, content, "utf-8");
|
|
29251
29170
|
} else {
|
|
29252
29171
|
writeFileSync4(filePath, content, "utf-8");
|
|
29253
29172
|
}
|
|
29254
|
-
} else if (append &&
|
|
29173
|
+
} else if (append && existsSync6(filePath)) {
|
|
29255
29174
|
const existing = JSON.parse(readFileSync12(filePath, "utf-8"));
|
|
29256
29175
|
writeFileSync4(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
|
|
29257
29176
|
} else {
|
|
@@ -30230,7 +30149,7 @@ function cropSprite(input, region) {
|
|
|
30230
30149
|
|
|
30231
30150
|
// src/lib/image/io.ts
|
|
30232
30151
|
import { randomBytes } from "crypto";
|
|
30233
|
-
import { glob as fsGlob, readFile as readFile19, rename, stat as stat4, writeFile as
|
|
30152
|
+
import { glob as fsGlob, readFile as readFile19, rename, stat as stat4, writeFile as writeFile10 } from "fs/promises";
|
|
30234
30153
|
import { dirname as dirname2, extname as extname2, join as join4, resolve as resolve4 } from "path";
|
|
30235
30154
|
var REMOTE_RE = /^https?:\/\//i;
|
|
30236
30155
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -30286,7 +30205,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
30286
30205
|
const absolute = resolve4(targetPath);
|
|
30287
30206
|
const dir = dirname2(absolute);
|
|
30288
30207
|
const tmp = join4(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
30289
|
-
await
|
|
30208
|
+
await writeFile10(tmp, data);
|
|
30290
30209
|
await rename(tmp, absolute);
|
|
30291
30210
|
}
|
|
30292
30211
|
|
|
@@ -33627,12 +33546,12 @@ import { defineCommand as defineCommand157 } from "citty";
|
|
|
33627
33546
|
|
|
33628
33547
|
// src/commands/landing/critique.ts
|
|
33629
33548
|
import { readdir as readdir8, stat as stat6 } from "fs/promises";
|
|
33630
|
-
import
|
|
33549
|
+
import path28 from "path";
|
|
33631
33550
|
import { defineCommand as defineCommand147 } from "citty";
|
|
33632
33551
|
|
|
33633
33552
|
// src/engine/landing/lib/brand-tokens.ts
|
|
33634
33553
|
import { readFile as readFile20 } from "fs/promises";
|
|
33635
|
-
import
|
|
33554
|
+
import path24 from "path";
|
|
33636
33555
|
|
|
33637
33556
|
// src/engine/landing/lib/color.ts
|
|
33638
33557
|
var NEUTRAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
@@ -33809,7 +33728,7 @@ function brandTokensFromCanonical(tokens) {
|
|
|
33809
33728
|
return { fonts, colors, hasTokens: fonts.size > 0 || colors.length > 0 };
|
|
33810
33729
|
}
|
|
33811
33730
|
async function loadBrandTokens(projectRoot) {
|
|
33812
|
-
const tokensRaw = await safeRead(
|
|
33731
|
+
const tokensRaw = await safeRead(path24.join(projectRoot, "src", "brand", "tokens.json"));
|
|
33813
33732
|
if (tokensRaw) {
|
|
33814
33733
|
try {
|
|
33815
33734
|
const canonical2 = brandTokensSchema.safeParse(JSON.parse(tokensRaw));
|
|
@@ -33817,8 +33736,8 @@ async function loadBrandTokens(projectRoot) {
|
|
|
33817
33736
|
} catch {
|
|
33818
33737
|
}
|
|
33819
33738
|
}
|
|
33820
|
-
const globalCss = await safeRead(
|
|
33821
|
-
const brandMd = await safeRead(
|
|
33739
|
+
const globalCss = await safeRead(path24.join(projectRoot, "src", "styles", "global.css"));
|
|
33740
|
+
const brandMd = await safeRead(path24.join(projectRoot, "src", "brand", "BRAND.md"));
|
|
33822
33741
|
if (!globalCss && !brandMd) return EMPTY;
|
|
33823
33742
|
return parseBrandTokens(globalCss, brandMd);
|
|
33824
33743
|
}
|
|
@@ -34879,13 +34798,13 @@ function describeCounts(findings) {
|
|
|
34879
34798
|
}
|
|
34880
34799
|
|
|
34881
34800
|
// src/engine/landing/lib/referenceStore.ts
|
|
34882
|
-
import { mkdir as mkdir8, readFile as readFile21, writeFile as
|
|
34883
|
-
import
|
|
34801
|
+
import { mkdir as mkdir8, readFile as readFile21, writeFile as writeFile11 } from "fs/promises";
|
|
34802
|
+
import path25 from "path";
|
|
34884
34803
|
var REFERENCES_FILE = ".cache/inspiration-refs.json";
|
|
34885
34804
|
var REFERENCE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
34886
34805
|
async function readReferences(projectRoot) {
|
|
34887
34806
|
try {
|
|
34888
|
-
const raw = await readFile21(
|
|
34807
|
+
const raw = await readFile21(path25.join(projectRoot, REFERENCES_FILE), "utf8");
|
|
34889
34808
|
const parsed = JSON.parse(raw);
|
|
34890
34809
|
if (!Array.isArray(parsed)) return [];
|
|
34891
34810
|
const cutoff = Date.now() - REFERENCE_TTL_MS;
|
|
@@ -34909,9 +34828,9 @@ async function recordReferences(projectRoot, references) {
|
|
|
34909
34828
|
const existing = await readReferences(projectRoot);
|
|
34910
34829
|
const replaced = new Set(references.map((reference) => reference.sectionId));
|
|
34911
34830
|
const merged = [...existing.filter((entry) => !replaced.has(entry.sectionId)), ...references];
|
|
34912
|
-
const file =
|
|
34913
|
-
await mkdir8(
|
|
34914
|
-
await
|
|
34831
|
+
const file = path25.join(projectRoot, REFERENCES_FILE);
|
|
34832
|
+
await mkdir8(path25.dirname(file), { recursive: true });
|
|
34833
|
+
await writeFile11(file, `${JSON.stringify(merged, null, 2)}
|
|
34915
34834
|
`);
|
|
34916
34835
|
return true;
|
|
34917
34836
|
} catch {
|
|
@@ -34920,40 +34839,40 @@ async function recordReferences(projectRoot, references) {
|
|
|
34920
34839
|
}
|
|
34921
34840
|
|
|
34922
34841
|
// src/commands/landing/snapshot.ts
|
|
34923
|
-
import { mkdir as mkdir9, rename as rename2, writeFile as
|
|
34924
|
-
import
|
|
34842
|
+
import { mkdir as mkdir9, rename as rename2, writeFile as writeFile12 } from "fs/promises";
|
|
34843
|
+
import path26 from "path";
|
|
34925
34844
|
var CRITIC_VERSION = "2";
|
|
34926
34845
|
function critiqueCacheDir(projectRoot) {
|
|
34927
|
-
return
|
|
34846
|
+
return path26.join(projectRoot, ".cache", "landing-critique");
|
|
34928
34847
|
}
|
|
34929
34848
|
function snapshotPath(projectRoot, slug) {
|
|
34930
|
-
return
|
|
34849
|
+
return path26.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
34931
34850
|
}
|
|
34932
34851
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
34933
34852
|
await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
|
|
34934
34853
|
const dest = snapshotPath(projectRoot, snapshot.slug);
|
|
34935
34854
|
const tmp = `${dest}.tmp`;
|
|
34936
|
-
await
|
|
34855
|
+
await writeFile12(tmp, `${JSON.stringify(snapshot, null, 2)}
|
|
34937
34856
|
`, "utf8");
|
|
34938
34857
|
await rename2(tmp, dest);
|
|
34939
34858
|
}
|
|
34940
34859
|
|
|
34941
34860
|
// src/commands/landing/source-version.ts
|
|
34942
34861
|
import { readdir as readdir7, readFile as readFile22, stat as stat5 } from "fs/promises";
|
|
34943
|
-
import
|
|
34862
|
+
import path27 from "path";
|
|
34944
34863
|
async function landingSourceRelPaths(landingDir) {
|
|
34945
34864
|
const rel = [];
|
|
34946
|
-
if (await isFile(
|
|
34947
|
-
const componentsDir =
|
|
34865
|
+
if (await isFile(path27.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
34866
|
+
const componentsDir = path27.join(landingDir, "_components");
|
|
34948
34867
|
for (const abs of await walkAstro(componentsDir)) {
|
|
34949
|
-
rel.push(
|
|
34868
|
+
rel.push(path27.relative(landingDir, abs).split(path27.sep).join("/"));
|
|
34950
34869
|
}
|
|
34951
34870
|
return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
34952
34871
|
}
|
|
34953
34872
|
async function readLandingSources(landingDir) {
|
|
34954
34873
|
const rel = await landingSourceRelPaths(landingDir);
|
|
34955
34874
|
const out = [];
|
|
34956
|
-
for (const r of rel) out.push({ path: r, text: await readFile22(
|
|
34875
|
+
for (const r of rel) out.push({ path: r, text: await readFile22(path27.join(landingDir, r), "utf8") });
|
|
34957
34876
|
return out;
|
|
34958
34877
|
}
|
|
34959
34878
|
async function computeLandingSourceSha(landingDir) {
|
|
@@ -34962,7 +34881,7 @@ async function computeLandingSourceSha(landingDir) {
|
|
|
34962
34881
|
for (const r of rel) {
|
|
34963
34882
|
let bytes;
|
|
34964
34883
|
try {
|
|
34965
|
-
bytes = await readFile22(
|
|
34884
|
+
bytes = await readFile22(path27.join(landingDir, r));
|
|
34966
34885
|
} catch {
|
|
34967
34886
|
bytes = Buffer.alloc(0);
|
|
34968
34887
|
}
|
|
@@ -34986,7 +34905,7 @@ async function walkAstro(dir) {
|
|
|
34986
34905
|
}
|
|
34987
34906
|
const out = [];
|
|
34988
34907
|
for (const entry of entries) {
|
|
34989
|
-
const abs =
|
|
34908
|
+
const abs = path27.join(dir, entry.name);
|
|
34990
34909
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
34991
34910
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
34992
34911
|
}
|
|
@@ -35047,7 +34966,7 @@ var critiqueCommand2 = defineCommand147({
|
|
|
35047
34966
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
35048
34967
|
);
|
|
35049
34968
|
}
|
|
35050
|
-
if (!await isDir(
|
|
34969
|
+
if (!await isDir(path28.resolve(projectRoot, "src", "pages", slug))) {
|
|
35051
34970
|
fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
35052
34971
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
35053
34972
|
});
|
|
@@ -35086,7 +35005,7 @@ var critiqueCommand2 = defineCommand147({
|
|
|
35086
35005
|
}
|
|
35087
35006
|
});
|
|
35088
35007
|
async function critiqueOne(projectRoot, slug, brand, references) {
|
|
35089
|
-
const landingDir =
|
|
35008
|
+
const landingDir = path28.resolve(projectRoot, "src", "pages", slug);
|
|
35090
35009
|
const [sources, sourceSha] = await Promise.all([readLandingSources(landingDir), computeLandingSourceSha(landingDir)]);
|
|
35091
35010
|
const report = critiqueLanding({ slug, sources, brand, references });
|
|
35092
35011
|
let snapshotFailed = false;
|
|
@@ -35106,7 +35025,7 @@ async function critiqueOne(projectRoot, slug, brand, references) {
|
|
|
35106
35025
|
}
|
|
35107
35026
|
async function listLandingSlugs(projectRoot) {
|
|
35108
35027
|
try {
|
|
35109
|
-
const entries = await readdir8(
|
|
35028
|
+
const entries = await readdir8(path28.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
35110
35029
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
35111
35030
|
} catch {
|
|
35112
35031
|
return [];
|
|
@@ -35257,8 +35176,8 @@ var addCommand = defineCommand148({
|
|
|
35257
35176
|
});
|
|
35258
35177
|
|
|
35259
35178
|
// src/commands/landing/inspiration/code.ts
|
|
35260
|
-
import { mkdir as mkdir10, writeFile as
|
|
35261
|
-
import
|
|
35179
|
+
import { mkdir as mkdir10, writeFile as writeFile13 } from "fs/promises";
|
|
35180
|
+
import path29 from "path";
|
|
35262
35181
|
import { defineCommand as defineCommand149 } from "citty";
|
|
35263
35182
|
registerSchema({
|
|
35264
35183
|
command: "landing.inspiration.code",
|
|
@@ -35281,10 +35200,10 @@ var codeCommand = defineCommand149({
|
|
|
35281
35200
|
try {
|
|
35282
35201
|
const id = args.id;
|
|
35283
35202
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
35284
|
-
const dir =
|
|
35203
|
+
const dir = path29.join(process.cwd(), ".baker", "inspiration", id);
|
|
35285
35204
|
await mkdir10(dir, { recursive: true });
|
|
35286
|
-
const file =
|
|
35287
|
-
await
|
|
35205
|
+
const file = path29.join(dir, "section.html");
|
|
35206
|
+
await writeFile13(file, data.html);
|
|
35288
35207
|
const recorded = await recordReference(process.cwd(), {
|
|
35289
35208
|
sectionId: id,
|
|
35290
35209
|
sourceUrl: data.sourceUrl,
|
|
@@ -35304,7 +35223,7 @@ var codeCommand = defineCommand149({
|
|
|
35304
35223
|
ok: true,
|
|
35305
35224
|
data: {
|
|
35306
35225
|
id,
|
|
35307
|
-
file:
|
|
35226
|
+
file: path29.relative(process.cwd(), file),
|
|
35308
35227
|
bytes: data.html.length,
|
|
35309
35228
|
fidelity: data.fidelity,
|
|
35310
35229
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -35560,7 +35479,7 @@ var pageCommand = defineCommand151({
|
|
|
35560
35479
|
|
|
35561
35480
|
// src/commands/landing/inspiration/scrape.ts
|
|
35562
35481
|
import { readFile as readFile23 } from "fs/promises";
|
|
35563
|
-
import
|
|
35482
|
+
import path32 from "path";
|
|
35564
35483
|
import { defineCommand as defineCommand152 } from "citty";
|
|
35565
35484
|
|
|
35566
35485
|
// src/engine/landing/lib/capturedReferences.ts
|
|
@@ -35752,8 +35671,8 @@ function classifyCaptureFailure(error) {
|
|
|
35752
35671
|
}
|
|
35753
35672
|
|
|
35754
35673
|
// src/engine/landing-library/run.ts
|
|
35755
|
-
import { mkdir as mkdir11, writeFile as
|
|
35756
|
-
import
|
|
35674
|
+
import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
|
|
35675
|
+
import path31 from "path";
|
|
35757
35676
|
|
|
35758
35677
|
// ../proxy/src/preflight.ts
|
|
35759
35678
|
import http from "http";
|
|
@@ -37168,11 +37087,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
37168
37087
|
}
|
|
37169
37088
|
|
|
37170
37089
|
// src/engine/landing-library/report.ts
|
|
37171
|
-
import { writeFile as
|
|
37172
|
-
import
|
|
37090
|
+
import { writeFile as writeFile14 } from "fs/promises";
|
|
37091
|
+
import path30 from "path";
|
|
37173
37092
|
async function writeCaptureReport(manifest, outDir) {
|
|
37174
|
-
const file =
|
|
37175
|
-
await
|
|
37093
|
+
const file = path30.join(outDir, "report.html");
|
|
37094
|
+
await writeFile14(file, renderReport(manifest));
|
|
37176
37095
|
return file;
|
|
37177
37096
|
}
|
|
37178
37097
|
function escapeHtml3(value) {
|
|
@@ -37350,32 +37269,32 @@ async function reproducePage(args) {
|
|
|
37350
37269
|
const { browser, page, outDir, pageUrl, livePageShot } = args;
|
|
37351
37270
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
37352
37271
|
if (!built) return { bundle: null, fidelity: null };
|
|
37353
|
-
await
|
|
37272
|
+
await writeFile15(path31.join(outDir, "page.html"), built.html);
|
|
37354
37273
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
37355
37274
|
wholePage: true,
|
|
37356
37275
|
timeoutMs: 6e4
|
|
37357
37276
|
});
|
|
37358
37277
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
37359
|
-
await
|
|
37278
|
+
await writeFile15(path31.join(outDir, "page-rendered.png"), rendered);
|
|
37360
37279
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
37361
37280
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
37362
37281
|
}
|
|
37363
37282
|
async function captureOneSection(args) {
|
|
37364
37283
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
|
|
37365
|
-
const dir =
|
|
37284
|
+
const dir = path31.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
37366
37285
|
await mkdir11(dir, { recursive: true });
|
|
37367
37286
|
const desktop = await captureSection(page, candidate);
|
|
37368
|
-
if (desktop) await
|
|
37287
|
+
if (desktop) await writeFile15(path31.join(dir, "desktop.png"), desktop);
|
|
37369
37288
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
37370
37289
|
const motion = await collectMotion(page, candidate.selector);
|
|
37371
37290
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
37372
37291
|
let fidelity = null;
|
|
37373
37292
|
let fidelityNote;
|
|
37374
37293
|
if (built) {
|
|
37375
|
-
await
|
|
37294
|
+
await writeFile15(path31.join(dir, "section.html"), built.html);
|
|
37376
37295
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
37377
37296
|
if (rendered && desktop) {
|
|
37378
|
-
await
|
|
37297
|
+
await writeFile15(path31.join(dir, "section-rendered.png"), rendered);
|
|
37379
37298
|
const result = await scoreFidelity(desktop, rendered);
|
|
37380
37299
|
fidelity = result.score;
|
|
37381
37300
|
fidelityNote = result.note;
|
|
@@ -37383,9 +37302,9 @@ async function captureOneSection(args) {
|
|
|
37383
37302
|
}
|
|
37384
37303
|
return {
|
|
37385
37304
|
...candidate,
|
|
37386
|
-
desktopShot: desktop ?
|
|
37305
|
+
desktopShot: desktop ? path31.relative(outDir, path31.join(dir, "desktop.png")) : null,
|
|
37387
37306
|
mobileShot: null,
|
|
37388
|
-
bundle: built ?
|
|
37307
|
+
bundle: built ? path31.relative(outDir, path31.join(dir, "section.html")) : null,
|
|
37389
37308
|
fidelity,
|
|
37390
37309
|
...fidelityNote ? { fidelityNote } : {},
|
|
37391
37310
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -37404,9 +37323,9 @@ async function captureMobileShots(args) {
|
|
|
37404
37323
|
for (const section of sections) {
|
|
37405
37324
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
37406
37325
|
if (!shot) continue;
|
|
37407
|
-
const file =
|
|
37408
|
-
await
|
|
37409
|
-
section.mobileShot =
|
|
37326
|
+
const file = path31.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
|
|
37327
|
+
await writeFile15(file, shot);
|
|
37328
|
+
section.mobileShot = path31.relative(outDir, file);
|
|
37410
37329
|
}
|
|
37411
37330
|
} finally {
|
|
37412
37331
|
await mobile.context.close();
|
|
@@ -37421,10 +37340,10 @@ async function captureMotionTakes(args) {
|
|
|
37421
37340
|
const filmOne = async (section) => {
|
|
37422
37341
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
37423
37342
|
if (!take) return;
|
|
37424
|
-
const dir =
|
|
37425
|
-
const file =
|
|
37426
|
-
await
|
|
37427
|
-
section.motionFilmstrip =
|
|
37343
|
+
const dir = path31.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
37344
|
+
const file = path31.join(dir, "motion-filmstrip.png");
|
|
37345
|
+
await writeFile15(file, take.filmstrip);
|
|
37346
|
+
section.motionFilmstrip = path31.relative(outDir, file);
|
|
37428
37347
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
37429
37348
|
};
|
|
37430
37349
|
const queue = [...moving];
|
|
@@ -37481,7 +37400,7 @@ async function captureAlternateViews(args) {
|
|
|
37481
37400
|
async function reproduceWholePage(args) {
|
|
37482
37401
|
const { browser, page, outDir, pageUrl, withCode, log } = args;
|
|
37483
37402
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
37484
|
-
if (fullPage) await
|
|
37403
|
+
if (fullPage) await writeFile15(path31.join(outDir, "full-page.png"), fullPage);
|
|
37485
37404
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
37486
37405
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
37487
37406
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -37552,7 +37471,7 @@ async function openViaLadder(args) {
|
|
|
37552
37471
|
async function scrapeLanding(options) {
|
|
37553
37472
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
37554
37473
|
const log = options.onProgress ?? (() => void 0);
|
|
37555
|
-
const sectionsDir =
|
|
37474
|
+
const sectionsDir = path31.join(options.outDir, "sections");
|
|
37556
37475
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
37557
37476
|
if (nonPublic) {
|
|
37558
37477
|
throw new BlockedPageError({
|
|
@@ -37614,7 +37533,7 @@ async function scrapeLanding(options) {
|
|
|
37614
37533
|
security: prepared.security,
|
|
37615
37534
|
captureTier: tier
|
|
37616
37535
|
};
|
|
37617
|
-
await
|
|
37536
|
+
await writeFile15(path31.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
37618
37537
|
`);
|
|
37619
37538
|
if (options.report !== false) {
|
|
37620
37539
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -37641,7 +37560,7 @@ async function recordCapture(manifest, outDir) {
|
|
|
37641
37560
|
for (const section of manifest.sections) {
|
|
37642
37561
|
if (!section.bundle) continue;
|
|
37643
37562
|
try {
|
|
37644
|
-
const markup = await readFile23(
|
|
37563
|
+
const markup = await readFile23(path32.join(outDir, section.bundle), "utf8");
|
|
37645
37564
|
const copyStrings = capturedCopyStrings(markup);
|
|
37646
37565
|
if (copyStrings.length === 0) continue;
|
|
37647
37566
|
references.push({
|
|
@@ -37803,8 +37722,8 @@ var scrapeCommand = defineCommand152({
|
|
|
37803
37722
|
});
|
|
37804
37723
|
|
|
37805
37724
|
// src/commands/landing/inspiration/search.ts
|
|
37806
|
-
import { mkdir as mkdir12, writeFile as
|
|
37807
|
-
import
|
|
37725
|
+
import { mkdir as mkdir12, writeFile as writeFile16 } from "fs/promises";
|
|
37726
|
+
import path33 from "path";
|
|
37808
37727
|
import { defineCommand as defineCommand153 } from "citty";
|
|
37809
37728
|
registerSchema({
|
|
37810
37729
|
command: "landing.inspiration.search",
|
|
@@ -37891,7 +37810,7 @@ function buildSearchBody(args) {
|
|
|
37891
37810
|
return body;
|
|
37892
37811
|
}
|
|
37893
37812
|
async function downloadShots(results) {
|
|
37894
|
-
const dir =
|
|
37813
|
+
const dir = path33.join(process.cwd(), ".baker", "inspiration");
|
|
37895
37814
|
await mkdir12(dir, { recursive: true });
|
|
37896
37815
|
const saved = /* @__PURE__ */ new Map();
|
|
37897
37816
|
await Promise.all(
|
|
@@ -37900,9 +37819,9 @@ async function downloadShots(results) {
|
|
|
37900
37819
|
try {
|
|
37901
37820
|
const response = await fetch(result.desktopShotUrl);
|
|
37902
37821
|
if (!response.ok) return;
|
|
37903
|
-
const file =
|
|
37904
|
-
await
|
|
37905
|
-
saved.set(result.id,
|
|
37822
|
+
const file = path33.join(dir, `${result.id}.png`);
|
|
37823
|
+
await writeFile16(file, Buffer.from(await response.arrayBuffer()));
|
|
37824
|
+
saved.set(result.id, path33.relative(process.cwd(), file));
|
|
37906
37825
|
} catch {
|
|
37907
37826
|
}
|
|
37908
37827
|
})
|
|
@@ -38130,8 +38049,8 @@ var sequencesCommand = defineCommand154({
|
|
|
38130
38049
|
});
|
|
38131
38050
|
|
|
38132
38051
|
// src/commands/landing/inspiration/view.ts
|
|
38133
|
-
import { mkdir as mkdir13, writeFile as
|
|
38134
|
-
import
|
|
38052
|
+
import { mkdir as mkdir13, writeFile as writeFile17 } from "fs/promises";
|
|
38053
|
+
import path34 from "path";
|
|
38135
38054
|
import { defineCommand as defineCommand155 } from "citty";
|
|
38136
38055
|
registerSchema({
|
|
38137
38056
|
command: "landing.inspiration.view",
|
|
@@ -38150,9 +38069,9 @@ async function download(url, file) {
|
|
|
38150
38069
|
try {
|
|
38151
38070
|
const response = await fetch(url);
|
|
38152
38071
|
if (!response.ok) return null;
|
|
38153
|
-
await mkdir13(
|
|
38154
|
-
await
|
|
38155
|
-
return
|
|
38072
|
+
await mkdir13(path34.dirname(file), { recursive: true });
|
|
38073
|
+
await writeFile17(file, Buffer.from(await response.arrayBuffer()));
|
|
38074
|
+
return path34.relative(process.cwd(), file);
|
|
38156
38075
|
} catch {
|
|
38157
38076
|
return null;
|
|
38158
38077
|
}
|
|
@@ -38176,11 +38095,11 @@ var viewCommand2 = defineCommand155({
|
|
|
38176
38095
|
const id = args.id;
|
|
38177
38096
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
38178
38097
|
const section = data.section;
|
|
38179
|
-
const dir =
|
|
38098
|
+
const dir = path34.join(process.cwd(), ".baker", "inspiration", id);
|
|
38180
38099
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
38181
|
-
download(section.desktopShotUrl,
|
|
38182
|
-
download(section.mobileShotUrl,
|
|
38183
|
-
download(section.motionFilmstripUrl,
|
|
38100
|
+
download(section.desktopShotUrl, path34.join(dir, "desktop.png")),
|
|
38101
|
+
download(section.mobileShotUrl, path34.join(dir, "mobile.png")),
|
|
38102
|
+
download(section.motionFilmstripUrl, path34.join(dir, "motion-filmstrip.png"))
|
|
38184
38103
|
]);
|
|
38185
38104
|
const full = args.full;
|
|
38186
38105
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -42390,7 +42309,7 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
42390
42309
|
});
|
|
42391
42310
|
|
|
42392
42311
|
// src/commands/videos/index.ts
|
|
42393
|
-
import { defineCommand as
|
|
42312
|
+
import { defineCommand as defineCommand203 } from "citty";
|
|
42394
42313
|
|
|
42395
42314
|
// src/commands/videos/delete.ts
|
|
42396
42315
|
import { defineCommand as defineCommand197 } from "citty";
|
|
@@ -42521,8 +42440,245 @@ var groupCommand2 = defineCommand199({
|
|
|
42521
42440
|
}
|
|
42522
42441
|
});
|
|
42523
42442
|
|
|
42524
|
-
// src/commands/videos/
|
|
42443
|
+
// src/commands/videos/ingest.ts
|
|
42444
|
+
import { mkdtemp as mkdtemp2, readFile as readFile24, rm as rm7, stat as stat7 } from "fs/promises";
|
|
42445
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
42446
|
+
import path35 from "path";
|
|
42525
42447
|
import { defineCommand as defineCommand200 } from "citty";
|
|
42448
|
+
|
|
42449
|
+
// src/commands/videos/ingestUrl.ts
|
|
42450
|
+
var MAX_VIDEO_INGEST_BYTES = 2 * 1024 * 1024 * 1024;
|
|
42451
|
+
var INGEST_MAX_HEIGHT = 1080;
|
|
42452
|
+
var DIRECT_MEDIA_EXTENSIONS = [".mp4", ".mov", ".webm", ".m4v", ".mkv", ".avi"];
|
|
42453
|
+
var YOUTUBE_HOSTS = ["youtube.com", "youtu.be", "m.youtube.com", "www.youtube.com"];
|
|
42454
|
+
function isDirectMediaUrl(rawUrl) {
|
|
42455
|
+
const pathname = safePathname(rawUrl);
|
|
42456
|
+
return DIRECT_MEDIA_EXTENSIONS.some((ext) => pathname.endsWith(ext));
|
|
42457
|
+
}
|
|
42458
|
+
function resolveIngestSource(explicit, rawUrl) {
|
|
42459
|
+
if (explicit) return explicit;
|
|
42460
|
+
return isYouTubeUrl(rawUrl) ? "youtube" : "url";
|
|
42461
|
+
}
|
|
42462
|
+
function isYouTubeUrl(rawUrl) {
|
|
42463
|
+
let host;
|
|
42464
|
+
try {
|
|
42465
|
+
host = new URL(rawUrl).hostname.toLowerCase();
|
|
42466
|
+
} catch {
|
|
42467
|
+
return false;
|
|
42468
|
+
}
|
|
42469
|
+
return YOUTUBE_HOSTS.includes(host);
|
|
42470
|
+
}
|
|
42471
|
+
var TOOLING_OR_BLOCK_SIGNATURES = [
|
|
42472
|
+
"requested format is not available",
|
|
42473
|
+
"only images are available",
|
|
42474
|
+
"nsig",
|
|
42475
|
+
"sign in to confirm",
|
|
42476
|
+
// bot check — distinct from "Private video. Sign in if…"
|
|
42477
|
+
"unable to download api page",
|
|
42478
|
+
"http error 403"
|
|
42479
|
+
];
|
|
42480
|
+
function isToolingOrBlockFailure(detail) {
|
|
42481
|
+
const haystack = detail.toLowerCase();
|
|
42482
|
+
return TOOLING_OR_BLOCK_SIGNATURES.some((signature) => haystack.includes(signature));
|
|
42483
|
+
}
|
|
42484
|
+
var NOT_ABOUT_THE_URL = ["UNAUTHORIZED", "FORBIDDEN", "RATE_LIMITED", "INTERNAL", "VALIDATION_ERROR", "NOT_FOUND"];
|
|
42485
|
+
function worthDownloadingInstead(errorCode) {
|
|
42486
|
+
return !NOT_ABOUT_THE_URL.includes(errorCode);
|
|
42487
|
+
}
|
|
42488
|
+
function safePathname(rawUrl) {
|
|
42489
|
+
try {
|
|
42490
|
+
return new URL(rawUrl).pathname.toLowerCase();
|
|
42491
|
+
} catch {
|
|
42492
|
+
return "";
|
|
42493
|
+
}
|
|
42494
|
+
}
|
|
42495
|
+
|
|
42496
|
+
// src/commands/videos/ingest.ts
|
|
42497
|
+
registerSchema({
|
|
42498
|
+
command: "videos.ingest",
|
|
42499
|
+
description: "Add a video to the library from a URL \u2014 a direct file, or a YouTube/TikTok/Vimeo page.",
|
|
42500
|
+
args: {
|
|
42501
|
+
url: { type: "string", description: "Video URL (direct file or a watch/post page)", required: true },
|
|
42502
|
+
source: { type: "string", description: "Provenance: url | youtube", required: false },
|
|
42503
|
+
"external-id": { type: "string", description: "Provider asset id (deduped on)", required: false },
|
|
42504
|
+
"external-url": { type: "string", description: "Canonical page URL", required: false },
|
|
42505
|
+
download: {
|
|
42506
|
+
type: "boolean",
|
|
42507
|
+
description: "Force the download path instead of letting Baker fetch the URL directly",
|
|
42508
|
+
required: false,
|
|
42509
|
+
default: false
|
|
42510
|
+
},
|
|
42511
|
+
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
42512
|
+
}
|
|
42513
|
+
});
|
|
42514
|
+
var ingestCommand2 = defineCommand200({
|
|
42515
|
+
meta: {
|
|
42516
|
+
name: "ingest",
|
|
42517
|
+
description: "Add a video to the library from a URL. A direct file URL is handed straight to Baker, which fetches it. A page URL (YouTube, TikTok, Vimeo, Instagram) is downloaded here first, then uploaded \u2014 and a direct URL that Baker cannot fetch falls back to that same path automatically.\n\nExample: baker videos ingest https://www.youtube.com/watch?v=abc123"
|
|
42518
|
+
},
|
|
42519
|
+
args: {
|
|
42520
|
+
url: { type: "positional", description: "Video URL", required: false },
|
|
42521
|
+
source: { type: "string", description: "Provenance: url | youtube", required: false },
|
|
42522
|
+
"external-id": { type: "string", description: "Provider asset id", required: false },
|
|
42523
|
+
"external-url": { type: "string", description: "Canonical page URL", required: false },
|
|
42524
|
+
download: { type: "boolean", description: "Force the download path", required: false, default: false },
|
|
42525
|
+
"dry-run": { type: "boolean", description: "Preview without executing", required: false, default: false }
|
|
42526
|
+
},
|
|
42527
|
+
run: ({ args }) => runVideoIngest({
|
|
42528
|
+
url: args.url,
|
|
42529
|
+
source: args.source,
|
|
42530
|
+
externalId: args["external-id"],
|
|
42531
|
+
externalUrl: args["external-url"],
|
|
42532
|
+
forceDownload: args.download === true,
|
|
42533
|
+
dryRun: args["dry-run"] === true
|
|
42534
|
+
})
|
|
42535
|
+
});
|
|
42536
|
+
async function runVideoIngest(opts) {
|
|
42537
|
+
const url = opts.url;
|
|
42538
|
+
if (!url) {
|
|
42539
|
+
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "URL is required" } });
|
|
42540
|
+
process.exit(1);
|
|
42541
|
+
}
|
|
42542
|
+
const direct = !opts.forceDownload && isDirectMediaUrl(url);
|
|
42543
|
+
const args = {
|
|
42544
|
+
url,
|
|
42545
|
+
source: resolveIngestSource(opts.source, url),
|
|
42546
|
+
externalId: opts.externalId,
|
|
42547
|
+
externalUrl: opts.externalUrl ?? (direct ? void 0 : url)
|
|
42548
|
+
};
|
|
42549
|
+
const validation = videosIngestRequestSchema.safeParse({ ...args, url });
|
|
42550
|
+
if (!validation.success) {
|
|
42551
|
+
const problem = validation.error.issues[0];
|
|
42552
|
+
writeJson({
|
|
42553
|
+
ok: false,
|
|
42554
|
+
error: {
|
|
42555
|
+
code: "VALIDATION_ERROR",
|
|
42556
|
+
message: problem ? `${problem.path.join(".") || "request"}: ${problem.message}` : "Invalid request"
|
|
42557
|
+
}
|
|
42558
|
+
});
|
|
42559
|
+
process.exit(1);
|
|
42560
|
+
}
|
|
42561
|
+
if (opts.dryRun) {
|
|
42562
|
+
writeJson({
|
|
42563
|
+
ok: true,
|
|
42564
|
+
dryRun: true,
|
|
42565
|
+
operation: "videos.ingest",
|
|
42566
|
+
params: {
|
|
42567
|
+
url,
|
|
42568
|
+
source: args.source,
|
|
42569
|
+
externalId: args.externalId ?? null,
|
|
42570
|
+
externalUrl: args.externalUrl ?? null,
|
|
42571
|
+
direct
|
|
42572
|
+
}
|
|
42573
|
+
});
|
|
42574
|
+
return;
|
|
42575
|
+
}
|
|
42576
|
+
try {
|
|
42577
|
+
writeJson({ ok: true, data: await ingestByRoute(args, direct) });
|
|
42578
|
+
} catch (err) {
|
|
42579
|
+
reportIngestFailure(err);
|
|
42580
|
+
}
|
|
42581
|
+
}
|
|
42582
|
+
async function ingestByRoute(args, direct) {
|
|
42583
|
+
if (!direct) {
|
|
42584
|
+
return { ...await downloadThenIngest(args), via: "download" };
|
|
42585
|
+
}
|
|
42586
|
+
try {
|
|
42587
|
+
return { ...await ingestUrl(args), via: "direct" };
|
|
42588
|
+
} catch (err) {
|
|
42589
|
+
if (!(err instanceof ApiError) || !worthDownloadingInstead(err.code)) throw err;
|
|
42590
|
+
return { ...await downloadThenIngest(args), via: "download", fallbackFrom: err.message };
|
|
42591
|
+
}
|
|
42592
|
+
}
|
|
42593
|
+
function reportIngestFailure(err) {
|
|
42594
|
+
if (err instanceof ApiError) {
|
|
42595
|
+
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
42596
|
+
process.exit(1);
|
|
42597
|
+
}
|
|
42598
|
+
if (err instanceof YtDlpError) {
|
|
42599
|
+
const detail = `${err.stderrTail} ${err.message}`;
|
|
42600
|
+
if (isProxyFailure(ytDlpBlockSignal(detail))) {
|
|
42601
|
+
writeJson({
|
|
42602
|
+
ok: false,
|
|
42603
|
+
error: {
|
|
42604
|
+
code: "INGEST_FAILED",
|
|
42605
|
+
message: "Couldn't download that video: our connection to the internet was refused.",
|
|
42606
|
+
fix: "This is Baker's egress proxy, not the link \u2014 its credentials look wrong or expired. The link is probably fine. Report it; retrying won't help until the proxy is fixed."
|
|
42607
|
+
}
|
|
42608
|
+
});
|
|
42609
|
+
process.exit(1);
|
|
42610
|
+
}
|
|
42611
|
+
const toolingOrBlock = isToolingOrBlockFailure(detail);
|
|
42612
|
+
writeJson({
|
|
42613
|
+
ok: false,
|
|
42614
|
+
error: {
|
|
42615
|
+
code: "INGEST_FAILED",
|
|
42616
|
+
message: `Couldn't download that video. ${err.stderrTail || err.message}`,
|
|
42617
|
+
fix: toolingOrBlock ? "The site wouldn't hand over the video \u2014 usually the downloader being out of date against a site that changed, or the request being blocked. Retrying won't help, and the link is probably fine. Report it so the tool can be updated; meanwhile download the file and use `baker videos upload <file>`." : "Check the link is public and playable. Private, age-restricted, or region-locked videos can't be downloaded \u2014 download the file yourself and use `baker videos upload <file>`."
|
|
42618
|
+
}
|
|
42619
|
+
});
|
|
42620
|
+
process.exit(1);
|
|
42621
|
+
}
|
|
42622
|
+
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
42623
|
+
process.exit(1);
|
|
42624
|
+
}
|
|
42625
|
+
function ingestUrl(args) {
|
|
42626
|
+
const body = {
|
|
42627
|
+
url: args.url,
|
|
42628
|
+
source: args.source
|
|
42629
|
+
};
|
|
42630
|
+
if (args.externalId) body.externalId = args.externalId;
|
|
42631
|
+
if (args.externalUrl) body.externalUrl = args.externalUrl;
|
|
42632
|
+
return apiPost("/api/videos/ingest", body);
|
|
42633
|
+
}
|
|
42634
|
+
async function downloadThenIngest(args) {
|
|
42635
|
+
const workDir = await mkdtemp2(path35.join(tmpdir3(), "videos-ingest-"));
|
|
42636
|
+
try {
|
|
42637
|
+
const { filePath, info, tier } = await runYtDlp({
|
|
42638
|
+
url: args.url,
|
|
42639
|
+
audioOnly: false,
|
|
42640
|
+
workDir,
|
|
42641
|
+
maxHeight: INGEST_MAX_HEIGHT
|
|
42642
|
+
});
|
|
42643
|
+
const stats = await stat7(filePath);
|
|
42644
|
+
if (stats.size > MAX_VIDEO_INGEST_BYTES) {
|
|
42645
|
+
throw new ApiError(
|
|
42646
|
+
"VALIDATION_ERROR",
|
|
42647
|
+
`That video is ${Math.round(stats.size / 1e6)} MB, over the ${Math.round(MAX_VIDEO_INGEST_BYTES / 1e6)} MB limit for this route.`
|
|
42648
|
+
);
|
|
42649
|
+
}
|
|
42650
|
+
const bytes = await readFile24(filePath);
|
|
42651
|
+
const publicUrl = await uploadToAssetStore(bytes);
|
|
42652
|
+
const externalId = args.externalId ?? (typeof info.id === "string" ? `${args.source}:${info.id}` : void 0);
|
|
42653
|
+
return {
|
|
42654
|
+
...await ingestUrl({ ...args, url: publicUrl, externalId, externalUrl: args.externalUrl ?? args.url }),
|
|
42655
|
+
egress: tier
|
|
42656
|
+
};
|
|
42657
|
+
} finally {
|
|
42658
|
+
await rm7(workDir, { recursive: true, force: true }).catch(() => {
|
|
42659
|
+
});
|
|
42660
|
+
}
|
|
42661
|
+
}
|
|
42662
|
+
async function uploadToAssetStore(bytes) {
|
|
42663
|
+
const sha256 = sha256Hex(bytes);
|
|
42664
|
+
const { putUrl, publicUrl } = await apiPost("/api/canvas/assets/presign", {
|
|
42665
|
+
sha256,
|
|
42666
|
+
mime: "video/mp4",
|
|
42667
|
+
purpose: "source"
|
|
42668
|
+
});
|
|
42669
|
+
const putRes = await fetch(putUrl, {
|
|
42670
|
+
method: "PUT",
|
|
42671
|
+
body: new Uint8Array(bytes),
|
|
42672
|
+
headers: { "Content-Type": "video/mp4" }
|
|
42673
|
+
});
|
|
42674
|
+
if (!putRes.ok) {
|
|
42675
|
+
throw new ApiError("INTERNAL_ERROR", `Storing the downloaded video failed: HTTP ${putRes.status}`);
|
|
42676
|
+
}
|
|
42677
|
+
return publicUrl;
|
|
42678
|
+
}
|
|
42679
|
+
|
|
42680
|
+
// src/commands/videos/search.ts
|
|
42681
|
+
import { defineCommand as defineCommand201 } from "citty";
|
|
42526
42682
|
registerSchema({
|
|
42527
42683
|
command: "videos.search",
|
|
42528
42684
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -42532,7 +42688,7 @@ registerSchema({
|
|
|
42532
42688
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
42533
42689
|
}
|
|
42534
42690
|
});
|
|
42535
|
-
var searchCommand4 =
|
|
42691
|
+
var searchCommand4 = defineCommand201({
|
|
42536
42692
|
meta: {
|
|
42537
42693
|
name: "search",
|
|
42538
42694
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -42582,9 +42738,9 @@ var searchCommand4 = defineCommand200({
|
|
|
42582
42738
|
var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
42583
42739
|
|
|
42584
42740
|
// src/commands/videos/upload.ts
|
|
42585
|
-
import { readFile as
|
|
42741
|
+
import { readFile as readFile25, stat as stat8 } from "fs/promises";
|
|
42586
42742
|
import { basename as basename3, extname as extname4 } from "path";
|
|
42587
|
-
import { defineCommand as
|
|
42743
|
+
import { defineCommand as defineCommand202 } from "citty";
|
|
42588
42744
|
var MIME_MAP = {
|
|
42589
42745
|
".mp4": "video/mp4",
|
|
42590
42746
|
".mov": "video/quicktime",
|
|
@@ -42594,9 +42750,9 @@ var MIME_MAP = {
|
|
|
42594
42750
|
};
|
|
42595
42751
|
registerSchema({
|
|
42596
42752
|
command: "videos.upload",
|
|
42597
|
-
description: "Upload a video
|
|
42753
|
+
description: "Upload a video to Baker \u2014 a local file (via Mux direct upload) or a remote http(s) URL.",
|
|
42598
42754
|
args: {
|
|
42599
|
-
file: { type: "string", description: "
|
|
42755
|
+
file: { type: "string", description: "Local video file path, or a remote http(s) URL", required: true },
|
|
42600
42756
|
"content-type": {
|
|
42601
42757
|
type: "string",
|
|
42602
42758
|
description: "MIME type (auto-detected from extension if omitted)",
|
|
@@ -42623,33 +42779,49 @@ function detectContentType(filePath) {
|
|
|
42623
42779
|
}
|
|
42624
42780
|
return mime;
|
|
42625
42781
|
}
|
|
42626
|
-
|
|
42782
|
+
function isRemoteUrl3(value) {
|
|
42783
|
+
return /^https?:\/\//i.test(value);
|
|
42784
|
+
}
|
|
42785
|
+
var uploadCommand2 = defineCommand202({
|
|
42627
42786
|
meta: {
|
|
42628
42787
|
name: "upload",
|
|
42629
|
-
description: "Upload a video
|
|
42788
|
+
description: "Upload a video to Baker \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: auto-detects content type and uploads via Mux direct upload.\nRemote: hands off to `videos ingest` (direct fetch, or download-then-upload for a YouTube/TikTok/Vimeo page).\n\nExamples:\n baker videos upload ./demo.mp4\n baker videos upload https://www.youtube.com/watch?v=abc123"
|
|
42630
42789
|
},
|
|
42631
42790
|
args: {
|
|
42632
|
-
file: { type: "positional", description: "
|
|
42633
|
-
"content-type": { type: "string", description: "MIME type (auto-detected
|
|
42791
|
+
file: { type: "positional", description: "Local video file path or remote http(s) URL", required: false },
|
|
42792
|
+
"content-type": { type: "string", description: "MIME type (local only \u2014 auto-detected)", required: false },
|
|
42634
42793
|
context: {
|
|
42635
42794
|
type: "string",
|
|
42636
42795
|
description: "What this clip is and what it is for \u2014 who is on camera, which campaign, how it was shot. Start here: the analysis reads it, so it is what makes the clip findable later.",
|
|
42637
42796
|
required: false
|
|
42638
42797
|
},
|
|
42798
|
+
source: { type: "string", description: "Provenance (URL mode only): url | youtube", required: false },
|
|
42799
|
+
"external-id": { type: "string", description: "Provider asset id (URL mode only)", required: false },
|
|
42800
|
+
"external-url": { type: "string", description: "Canonical page URL (URL mode only)", required: false },
|
|
42639
42801
|
"dry-run": { type: "boolean", description: "Preview without executing", required: false, default: false }
|
|
42640
42802
|
},
|
|
42641
42803
|
run: async ({ args }) => {
|
|
42642
42804
|
try {
|
|
42643
42805
|
const filePath = args.file;
|
|
42644
42806
|
if (!filePath) {
|
|
42645
|
-
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path is required" } });
|
|
42807
|
+
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path or URL is required" } });
|
|
42646
42808
|
process.exit(1);
|
|
42647
42809
|
}
|
|
42810
|
+
if (isRemoteUrl3(filePath)) {
|
|
42811
|
+
await runVideoIngest({
|
|
42812
|
+
url: filePath,
|
|
42813
|
+
source: args.source,
|
|
42814
|
+
externalId: args["external-id"],
|
|
42815
|
+
externalUrl: args["external-url"],
|
|
42816
|
+
dryRun: args["dry-run"] === true
|
|
42817
|
+
});
|
|
42818
|
+
return;
|
|
42819
|
+
}
|
|
42648
42820
|
const contentType = args["content-type"] || detectContentType(filePath);
|
|
42649
42821
|
const originalFilename = basename3(filePath);
|
|
42650
42822
|
const descriptionContext = args.context;
|
|
42651
42823
|
if (args["dry-run"]) {
|
|
42652
|
-
const fileStats = await
|
|
42824
|
+
const fileStats = await stat8(filePath);
|
|
42653
42825
|
writeJson({
|
|
42654
42826
|
ok: true,
|
|
42655
42827
|
dryRun: true,
|
|
@@ -42662,7 +42834,7 @@ var uploadCommand2 = defineCommand201({
|
|
|
42662
42834
|
originalFilename,
|
|
42663
42835
|
descriptionContext
|
|
42664
42836
|
});
|
|
42665
|
-
const fileBuffer = await
|
|
42837
|
+
const fileBuffer = await readFile25(filePath);
|
|
42666
42838
|
const uploadResponse = await fetch(uploadUrl, {
|
|
42667
42839
|
method: "PUT",
|
|
42668
42840
|
headers: { "Content-Type": contentType },
|
|
@@ -42693,16 +42865,17 @@ var uploadCommand2 = defineCommand201({
|
|
|
42693
42865
|
});
|
|
42694
42866
|
|
|
42695
42867
|
// src/commands/videos/index.ts
|
|
42696
|
-
var videosCommand =
|
|
42868
|
+
var videosCommand = defineCommand203({
|
|
42697
42869
|
meta: {
|
|
42698
42870
|
name: "videos",
|
|
42699
|
-
description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
|
|
42871
|
+
description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
|
|
42700
42872
|
|
|
42701
42873
|
Examples:
|
|
42702
42874
|
baker videos search "product demo" --limit 5
|
|
42703
42875
|
baker videos search "tutorial" --tags explainer
|
|
42704
42876
|
baker videos get <video-id>
|
|
42705
42877
|
baker videos upload ./demo.mp4
|
|
42878
|
+
baker videos ingest https://www.youtube.com/watch?v=abc123
|
|
42706
42879
|
baker videos delete <video-id> --dry-run
|
|
42707
42880
|
baker videos tags
|
|
42708
42881
|
Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
@@ -42712,16 +42885,17 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
42712
42885
|
group: groupCommand2,
|
|
42713
42886
|
search: searchCommand4,
|
|
42714
42887
|
upload: uploadCommand2,
|
|
42888
|
+
ingest: ingestCommand2,
|
|
42715
42889
|
delete: deleteCommand3,
|
|
42716
42890
|
tags: tagsCommand5
|
|
42717
42891
|
}
|
|
42718
42892
|
});
|
|
42719
42893
|
|
|
42720
42894
|
// src/commands/winning-ads/index.ts
|
|
42721
|
-
import { defineCommand as
|
|
42895
|
+
import { defineCommand as defineCommand216 } from "citty";
|
|
42722
42896
|
|
|
42723
42897
|
// src/commands/winning-ads/advertisers.ts
|
|
42724
|
-
import { defineCommand as
|
|
42898
|
+
import { defineCommand as defineCommand204 } from "citty";
|
|
42725
42899
|
|
|
42726
42900
|
// src/commands/winning-ads/shared.ts
|
|
42727
42901
|
function splitList2(value) {
|
|
@@ -42774,7 +42948,7 @@ function advertiserNormalizer(record, full) {
|
|
|
42774
42948
|
last_synced_at: record.last_synced_at ?? null
|
|
42775
42949
|
};
|
|
42776
42950
|
}
|
|
42777
|
-
var advertisersCommand2 =
|
|
42951
|
+
var advertisersCommand2 = defineCommand204({
|
|
42778
42952
|
meta: {
|
|
42779
42953
|
name: "advertisers",
|
|
42780
42954
|
description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
|
|
@@ -42832,7 +43006,7 @@ var advertisersCommand2 = defineCommand203({
|
|
|
42832
43006
|
});
|
|
42833
43007
|
|
|
42834
43008
|
// src/commands/winning-ads/brief.ts
|
|
42835
|
-
import { defineCommand as
|
|
43009
|
+
import { defineCommand as defineCommand205 } from "citty";
|
|
42836
43010
|
registerSchema({
|
|
42837
43011
|
command: "winning-ads.brief",
|
|
42838
43012
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -42878,7 +43052,7 @@ function parseDna(raw) {
|
|
|
42878
43052
|
}
|
|
42879
43053
|
return parsed;
|
|
42880
43054
|
}
|
|
42881
|
-
var briefCommand =
|
|
43055
|
+
var briefCommand = defineCommand205({
|
|
42882
43056
|
meta: {
|
|
42883
43057
|
name: "brief",
|
|
42884
43058
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -42914,7 +43088,7 @@ var briefCommand = defineCommand204({
|
|
|
42914
43088
|
});
|
|
42915
43089
|
|
|
42916
43090
|
// src/commands/winning-ads/content.ts
|
|
42917
|
-
import { defineCommand as
|
|
43091
|
+
import { defineCommand as defineCommand206 } from "citty";
|
|
42918
43092
|
registerSchema({
|
|
42919
43093
|
command: "winning-ads.content",
|
|
42920
43094
|
description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
|
|
@@ -42927,7 +43101,7 @@ registerSchema({
|
|
|
42927
43101
|
}
|
|
42928
43102
|
}
|
|
42929
43103
|
});
|
|
42930
|
-
var contentCommand =
|
|
43104
|
+
var contentCommand = defineCommand206({
|
|
42931
43105
|
meta: {
|
|
42932
43106
|
name: "content",
|
|
42933
43107
|
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -42976,7 +43150,7 @@ var contentCommand = defineCommand205({
|
|
|
42976
43150
|
});
|
|
42977
43151
|
|
|
42978
43152
|
// src/commands/winning-ads/feed.ts
|
|
42979
|
-
import { defineCommand as
|
|
43153
|
+
import { defineCommand as defineCommand207 } from "citty";
|
|
42980
43154
|
function buildFeedParams(input) {
|
|
42981
43155
|
const params = {};
|
|
42982
43156
|
const advertiser = splitList2(input.advertiser);
|
|
@@ -43028,7 +43202,7 @@ registerSchema({
|
|
|
43028
43202
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
43029
43203
|
}
|
|
43030
43204
|
});
|
|
43031
|
-
var feedCommand =
|
|
43205
|
+
var feedCommand = defineCommand207({
|
|
43032
43206
|
meta: {
|
|
43033
43207
|
name: "feed",
|
|
43034
43208
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -43113,7 +43287,7 @@ var feedCommand = defineCommand206({
|
|
|
43113
43287
|
});
|
|
43114
43288
|
|
|
43115
43289
|
// src/commands/winning-ads/follow.ts
|
|
43116
|
-
import { defineCommand as
|
|
43290
|
+
import { defineCommand as defineCommand208 } from "citty";
|
|
43117
43291
|
var PLATFORMS = ["meta", "linkedin"];
|
|
43118
43292
|
registerSchema({
|
|
43119
43293
|
command: "winning-ads.follow",
|
|
@@ -43128,7 +43302,7 @@ registerSchema({
|
|
|
43128
43302
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
43129
43303
|
}
|
|
43130
43304
|
});
|
|
43131
|
-
var followCommand =
|
|
43305
|
+
var followCommand = defineCommand208({
|
|
43132
43306
|
meta: {
|
|
43133
43307
|
name: "follow",
|
|
43134
43308
|
description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
|
|
@@ -43175,7 +43349,7 @@ var followCommand = defineCommand207({
|
|
|
43175
43349
|
});
|
|
43176
43350
|
|
|
43177
43351
|
// src/commands/winning-ads/follow-competitors.ts
|
|
43178
|
-
import { defineCommand as
|
|
43352
|
+
import { defineCommand as defineCommand209 } from "citty";
|
|
43179
43353
|
var PLATFORMS2 = ["meta", "linkedin"];
|
|
43180
43354
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
43181
43355
|
function buildFollowBatchBody(input) {
|
|
@@ -43208,7 +43382,7 @@ registerSchema({
|
|
|
43208
43382
|
}
|
|
43209
43383
|
}
|
|
43210
43384
|
});
|
|
43211
|
-
var followCompetitorsCommand =
|
|
43385
|
+
var followCompetitorsCommand = defineCommand209({
|
|
43212
43386
|
meta: {
|
|
43213
43387
|
name: "follow-competitors",
|
|
43214
43388
|
description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
|
|
@@ -43283,7 +43457,7 @@ var followCompetitorsCommand = defineCommand208({
|
|
|
43283
43457
|
});
|
|
43284
43458
|
|
|
43285
43459
|
// src/commands/winning-ads/following.ts
|
|
43286
|
-
import { defineCommand as
|
|
43460
|
+
import { defineCommand as defineCommand210 } from "citty";
|
|
43287
43461
|
registerSchema({
|
|
43288
43462
|
command: "winning-ads.following",
|
|
43289
43463
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
|
|
@@ -43316,7 +43490,7 @@ function followingNormalizer(record, full) {
|
|
|
43316
43490
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
43317
43491
|
};
|
|
43318
43492
|
}
|
|
43319
|
-
var followingCommand =
|
|
43493
|
+
var followingCommand = defineCommand210({
|
|
43320
43494
|
meta: {
|
|
43321
43495
|
name: "following",
|
|
43322
43496
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
|
|
@@ -43351,7 +43525,7 @@ var followingCommand = defineCommand209({
|
|
|
43351
43525
|
});
|
|
43352
43526
|
|
|
43353
43527
|
// src/commands/winning-ads/patterns.ts
|
|
43354
|
-
import { defineCommand as
|
|
43528
|
+
import { defineCommand as defineCommand211 } from "citty";
|
|
43355
43529
|
registerSchema({
|
|
43356
43530
|
command: "winning-ads.patterns",
|
|
43357
43531
|
description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
|
|
@@ -43390,7 +43564,7 @@ function discriminatorRow(record) {
|
|
|
43390
43564
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
43391
43565
|
};
|
|
43392
43566
|
}
|
|
43393
|
-
var patternsCommand =
|
|
43567
|
+
var patternsCommand = defineCommand211({
|
|
43394
43568
|
meta: {
|
|
43395
43569
|
name: "patterns",
|
|
43396
43570
|
description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
|
|
@@ -43446,7 +43620,7 @@ var patternsCommand = defineCommand210({
|
|
|
43446
43620
|
});
|
|
43447
43621
|
|
|
43448
43622
|
// src/commands/winning-ads/search.ts
|
|
43449
|
-
import { defineCommand as
|
|
43623
|
+
import { defineCommand as defineCommand212 } from "citty";
|
|
43450
43624
|
registerSchema({
|
|
43451
43625
|
command: "winning-ads.search",
|
|
43452
43626
|
description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
|
|
@@ -43554,7 +43728,7 @@ function buildSearchBody2(args) {
|
|
|
43554
43728
|
}
|
|
43555
43729
|
return body;
|
|
43556
43730
|
}
|
|
43557
|
-
var searchCommand5 =
|
|
43731
|
+
var searchCommand5 = defineCommand212({
|
|
43558
43732
|
meta: {
|
|
43559
43733
|
name: "search",
|
|
43560
43734
|
description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
|
|
@@ -43669,7 +43843,7 @@ var searchCommand5 = defineCommand211({
|
|
|
43669
43843
|
});
|
|
43670
43844
|
|
|
43671
43845
|
// src/commands/winning-ads/seeds.ts
|
|
43672
|
-
import { defineCommand as
|
|
43846
|
+
import { defineCommand as defineCommand213 } from "citty";
|
|
43673
43847
|
function leanRow(r) {
|
|
43674
43848
|
return {
|
|
43675
43849
|
key: r.key,
|
|
@@ -43697,7 +43871,7 @@ function makeSeedCommand(opts) {
|
|
|
43697
43871
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
43698
43872
|
}
|
|
43699
43873
|
});
|
|
43700
|
-
return
|
|
43874
|
+
return defineCommand213({
|
|
43701
43875
|
meta: { name: opts.name, description: opts.description },
|
|
43702
43876
|
args: {
|
|
43703
43877
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -43746,7 +43920,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
43746
43920
|
});
|
|
43747
43921
|
|
|
43748
43922
|
// src/commands/winning-ads/unfollow.ts
|
|
43749
|
-
import { defineCommand as
|
|
43923
|
+
import { defineCommand as defineCommand214 } from "citty";
|
|
43750
43924
|
registerSchema({
|
|
43751
43925
|
command: "winning-ads.unfollow",
|
|
43752
43926
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -43754,7 +43928,7 @@ registerSchema({
|
|
|
43754
43928
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
43755
43929
|
}
|
|
43756
43930
|
});
|
|
43757
|
-
var unfollowCommand =
|
|
43931
|
+
var unfollowCommand = defineCommand214({
|
|
43758
43932
|
meta: {
|
|
43759
43933
|
name: "unfollow",
|
|
43760
43934
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -43775,7 +43949,7 @@ var unfollowCommand = defineCommand213({
|
|
|
43775
43949
|
});
|
|
43776
43950
|
|
|
43777
43951
|
// src/commands/winning-ads/winners.ts
|
|
43778
|
-
import { defineCommand as
|
|
43952
|
+
import { defineCommand as defineCommand215 } from "citty";
|
|
43779
43953
|
registerSchema({
|
|
43780
43954
|
command: "winning-ads.winners",
|
|
43781
43955
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -43785,7 +43959,7 @@ registerSchema({
|
|
|
43785
43959
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
43786
43960
|
}
|
|
43787
43961
|
});
|
|
43788
|
-
var winnersCommand =
|
|
43962
|
+
var winnersCommand = defineCommand215({
|
|
43789
43963
|
meta: {
|
|
43790
43964
|
name: "winners",
|
|
43791
43965
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -43835,7 +44009,7 @@ var winnersCommand = defineCommand214({
|
|
|
43835
44009
|
});
|
|
43836
44010
|
|
|
43837
44011
|
// src/commands/winning-ads/index.ts
|
|
43838
|
-
var winningAdsCommand =
|
|
44012
|
+
var winningAdsCommand = defineCommand216({
|
|
43839
44013
|
meta: {
|
|
43840
44014
|
name: "winning-ads",
|
|
43841
44015
|
description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
|
|
@@ -43907,7 +44081,7 @@ function getCliVersion() {
|
|
|
43907
44081
|
}
|
|
43908
44082
|
|
|
43909
44083
|
// src/cli.ts
|
|
43910
|
-
var main =
|
|
44084
|
+
var main = defineCommand217({
|
|
43911
44085
|
meta: {
|
|
43912
44086
|
name: "baker",
|
|
43913
44087
|
version: getCliVersion(),
|