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