@koda-sl/baker-cli 0.201.0-dev.0a9adf6f7 → 0.201.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -39
- package/dist/{chunk-CAWFJ5X3.js → chunk-5MPIOGRO.js} +215 -265
- package/dist/chunk-5MPIOGRO.js.map +1 -0
- package/dist/cli.js +464 -618
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-CAWFJ5X3.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,
|
|
@@ -51,9 +49,8 @@ import {
|
|
|
51
49
|
supportsReferenceToVideo,
|
|
52
50
|
toModelSafeImage,
|
|
53
51
|
ulid,
|
|
54
|
-
validateCanvasDeep
|
|
55
|
-
|
|
56
|
-
} from "./chunk-CAWFJ5X3.js";
|
|
52
|
+
validateCanvasDeep
|
|
53
|
+
} from "./chunk-5MPIOGRO.js";
|
|
57
54
|
import {
|
|
58
55
|
csvOrJson,
|
|
59
56
|
daysAgoIso,
|
|
@@ -102,7 +99,7 @@ import {
|
|
|
102
99
|
} from "./chunk-6NZG2TCM.js";
|
|
103
100
|
|
|
104
101
|
// src/cli.ts
|
|
105
|
-
import { defineCommand as
|
|
102
|
+
import { defineCommand as defineCommand216, runMain } from "citty";
|
|
106
103
|
|
|
107
104
|
// src/commands/actions/index.ts
|
|
108
105
|
import { defineCommand as defineCommand18 } from "citty";
|
|
@@ -5689,7 +5686,6 @@ var testimonialsOutscraperWebhookResponseSchema = z18.object({
|
|
|
5689
5686
|
// ../api/src/videos.ts
|
|
5690
5687
|
import { z as z19 } from "zod";
|
|
5691
5688
|
var videoStatusSchema = z19.enum(["uploading", "uploaded", "processing", "ready", "error"]);
|
|
5692
|
-
var videoSourceSchema = z19.enum(["uploaded", "instagram", "url", "youtube"]);
|
|
5693
5689
|
var videoTranscriptSegmentSchema = z19.object({
|
|
5694
5690
|
text: z19.string(),
|
|
5695
5691
|
startSecond: z19.number(),
|
|
@@ -5790,19 +5786,6 @@ var videosUploadRequestSchema = z19.object({
|
|
|
5790
5786
|
descriptionContext: z19.string().optional()
|
|
5791
5787
|
});
|
|
5792
5788
|
var videosUploadResponseSchema = z19.object({ uploadUrl: z19.string(), videoId: z19.string() });
|
|
5793
|
-
var videosIngestRequestSchema = z19.object({
|
|
5794
|
-
// Must be a direct media file Mux can pull (mp4/mov/webm/…). A page URL —
|
|
5795
|
-
// a YouTube watch link, a TikTok post — is not one: the CLI resolves those to
|
|
5796
|
-
// a media file before calling this route.
|
|
5797
|
-
url: z19.string().url(),
|
|
5798
|
-
source: videoSourceSchema,
|
|
5799
|
-
externalId: z19.string().optional(),
|
|
5800
|
-
externalUrl: z19.string().optional()
|
|
5801
|
-
});
|
|
5802
|
-
var videosIngestResponseSchema = z19.object({
|
|
5803
|
-
videoId: z19.string(),
|
|
5804
|
-
deduped: z19.boolean()
|
|
5805
|
-
});
|
|
5806
5789
|
var videosDeleteRequestSchema = z19.object({ id: z19.string().min(1, "Missing video ID") });
|
|
5807
5790
|
var videosDeleteResponseSchema = z19.object({ ok: z19.literal(true) });
|
|
5808
5791
|
|
|
@@ -8763,11 +8746,11 @@ function rawTextEntries(value) {
|
|
|
8763
8746
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
8764
8747
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
8765
8748
|
}
|
|
8766
|
-
function rawFileEntries(
|
|
8767
|
-
if (typeof
|
|
8749
|
+
function rawFileEntries(path37) {
|
|
8750
|
+
if (typeof path37 !== "string" || path37.length === 0) {
|
|
8768
8751
|
return [];
|
|
8769
8752
|
}
|
|
8770
|
-
return readFileSync2(
|
|
8753
|
+
return readFileSync2(path37, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
8771
8754
|
}
|
|
8772
8755
|
function keywordEntries(args) {
|
|
8773
8756
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -8790,19 +8773,19 @@ function keywordEntries(args) {
|
|
|
8790
8773
|
}
|
|
8791
8774
|
return entries;
|
|
8792
8775
|
}
|
|
8793
|
-
function loadJsonFileArg(
|
|
8794
|
-
if (typeof
|
|
8776
|
+
function loadJsonFileArg(path37) {
|
|
8777
|
+
if (typeof path37 !== "string" || path37.length === 0) {
|
|
8795
8778
|
return {};
|
|
8796
8779
|
}
|
|
8797
8780
|
try {
|
|
8798
|
-
const parsed = JSON.parse(readFileSync2(
|
|
8781
|
+
const parsed = JSON.parse(readFileSync2(path37, "utf8"));
|
|
8799
8782
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8800
|
-
failWriteValidation(`${
|
|
8783
|
+
failWriteValidation(`${path37} must contain a JSON object`);
|
|
8801
8784
|
}
|
|
8802
8785
|
return parsed;
|
|
8803
8786
|
} catch (err) {
|
|
8804
8787
|
if (err instanceof SyntaxError) {
|
|
8805
|
-
failWriteValidation(`${
|
|
8788
|
+
failWriteValidation(`${path37} is not valid JSON: ${err.message}`);
|
|
8806
8789
|
}
|
|
8807
8790
|
throw err;
|
|
8808
8791
|
}
|
|
@@ -8932,10 +8915,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
|
|
|
8932
8915
|
async function stageTarget(kind, customerId, target, hints) {
|
|
8933
8916
|
await stageGoogleOp({ kind, customerId, target }, hints);
|
|
8934
8917
|
}
|
|
8935
|
-
async function draftAction(
|
|
8918
|
+
async function draftAction(path37, body, chat) {
|
|
8936
8919
|
try {
|
|
8937
8920
|
const chatId = resolveChatId(chat);
|
|
8938
|
-
const response = await apiPost(
|
|
8921
|
+
const response = await apiPost(path37, { chatId, ...body });
|
|
8939
8922
|
writeJsonEnvelope(response);
|
|
8940
8923
|
} catch (err) {
|
|
8941
8924
|
handleGoogleError(err);
|
|
@@ -11793,19 +11776,19 @@ function failWriteValidation2(message) {
|
|
|
11793
11776
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
11794
11777
|
process.exit(1);
|
|
11795
11778
|
}
|
|
11796
|
-
function loadJsonFileArg2(
|
|
11797
|
-
if (typeof
|
|
11779
|
+
function loadJsonFileArg2(path37) {
|
|
11780
|
+
if (typeof path37 !== "string" || path37.length === 0) {
|
|
11798
11781
|
return {};
|
|
11799
11782
|
}
|
|
11800
11783
|
try {
|
|
11801
|
-
const parsed = JSON.parse(readFileSync4(
|
|
11784
|
+
const parsed = JSON.parse(readFileSync4(path37, "utf8"));
|
|
11802
11785
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
11803
|
-
failWriteValidation2(`${
|
|
11786
|
+
failWriteValidation2(`${path37} must contain a JSON object`);
|
|
11804
11787
|
}
|
|
11805
11788
|
return parsed;
|
|
11806
11789
|
} catch (err) {
|
|
11807
11790
|
if (err instanceof SyntaxError) {
|
|
11808
|
-
failWriteValidation2(`${
|
|
11791
|
+
failWriteValidation2(`${path37} is not valid JSON: ${err.message}`);
|
|
11809
11792
|
}
|
|
11810
11793
|
throw err;
|
|
11811
11794
|
}
|
|
@@ -11890,15 +11873,15 @@ function parseLocaleFlag(value) {
|
|
|
11890
11873
|
}
|
|
11891
11874
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
11892
11875
|
}
|
|
11893
|
-
function loadTargetingFileArg(
|
|
11894
|
-
if (typeof
|
|
11876
|
+
function loadTargetingFileArg(path37) {
|
|
11877
|
+
if (typeof path37 !== "string" || path37.length === 0) {
|
|
11895
11878
|
return void 0;
|
|
11896
11879
|
}
|
|
11897
|
-
const parsed = loadJsonFileArg2(
|
|
11880
|
+
const parsed = loadJsonFileArg2(path37);
|
|
11898
11881
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
11899
11882
|
if (!criteria.include) {
|
|
11900
11883
|
failWriteValidation2(
|
|
11901
|
-
`${
|
|
11884
|
+
`${path37} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
11902
11885
|
);
|
|
11903
11886
|
}
|
|
11904
11887
|
return criteria;
|
|
@@ -11933,14 +11916,14 @@ function parseCsvLine(line) {
|
|
|
11933
11916
|
cells.push(current);
|
|
11934
11917
|
return cells.map((cell) => cell.trim());
|
|
11935
11918
|
}
|
|
11936
|
-
function parseListFileArg(
|
|
11937
|
-
if (typeof
|
|
11919
|
+
function parseListFileArg(path37, maxRows) {
|
|
11920
|
+
if (typeof path37 !== "string" || path37.length === 0) {
|
|
11938
11921
|
return void 0;
|
|
11939
11922
|
}
|
|
11940
|
-
const raw = readFileSync4(
|
|
11923
|
+
const raw = readFileSync4(path37, "utf8");
|
|
11941
11924
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
11942
11925
|
if (lines.length < 2) {
|
|
11943
|
-
failWriteValidation2(`${
|
|
11926
|
+
failWriteValidation2(`${path37} needs a header row and at least one data row`);
|
|
11944
11927
|
}
|
|
11945
11928
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
11946
11929
|
const rows = [];
|
|
@@ -11959,7 +11942,7 @@ function parseListFileArg(path36, maxRows) {
|
|
|
11959
11942
|
}
|
|
11960
11943
|
}
|
|
11961
11944
|
if (rows.length > maxRows) {
|
|
11962
|
-
failWriteValidation2(`${
|
|
11945
|
+
failWriteValidation2(`${path37} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
11963
11946
|
}
|
|
11964
11947
|
return { columns, rows };
|
|
11965
11948
|
}
|
|
@@ -12055,11 +12038,11 @@ function readPositionals(args) {
|
|
|
12055
12038
|
function splitIdList(raw) {
|
|
12056
12039
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
12057
12040
|
}
|
|
12058
|
-
function idsFileEntries(
|
|
12059
|
-
if (typeof
|
|
12041
|
+
function idsFileEntries(path37) {
|
|
12042
|
+
if (typeof path37 !== "string" || path37.length === 0) {
|
|
12060
12043
|
return [];
|
|
12061
12044
|
}
|
|
12062
|
-
return readFileSync4(
|
|
12045
|
+
return readFileSync4(path37, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
12063
12046
|
}
|
|
12064
12047
|
function requireTargets(args, entity) {
|
|
12065
12048
|
const positionals = readPositionals(args);
|
|
@@ -14682,9 +14665,9 @@ function compactRow(row) {
|
|
|
14682
14665
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
14683
14666
|
};
|
|
14684
14667
|
}
|
|
14685
|
-
function readPath(row,
|
|
14668
|
+
function readPath(row, path37) {
|
|
14686
14669
|
let current = row;
|
|
14687
|
-
for (const segment of
|
|
14670
|
+
for (const segment of path37.split(".")) {
|
|
14688
14671
|
const record = asRecord2(current);
|
|
14689
14672
|
if (!record) return void 0;
|
|
14690
14673
|
current = record[segment];
|
|
@@ -14694,10 +14677,10 @@ function readPath(row, path36) {
|
|
|
14694
14677
|
function projectFields(rows, paths) {
|
|
14695
14678
|
return rows.map((row) => {
|
|
14696
14679
|
const projected = {};
|
|
14697
|
-
for (const
|
|
14698
|
-
const value = readPath(row,
|
|
14680
|
+
for (const path37 of paths) {
|
|
14681
|
+
const value = readPath(row, path37);
|
|
14699
14682
|
if (value !== void 0) {
|
|
14700
|
-
projected[
|
|
14683
|
+
projected[path37] = value;
|
|
14701
14684
|
}
|
|
14702
14685
|
}
|
|
14703
14686
|
return projected;
|
|
@@ -15991,11 +15974,11 @@ var updateStatusSchema = z21.enum(UPDATE_STATUSES);
|
|
|
15991
15974
|
function currencyMinimums2(currencyCode) {
|
|
15992
15975
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
15993
15976
|
}
|
|
15994
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
15977
|
+
function validateDailyBudgetFloor(money, ctx, path37) {
|
|
15995
15978
|
if (money?.currencyCode) {
|
|
15996
15979
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
15997
15980
|
if (Number(money.amount) < min) {
|
|
15998
|
-
ctx.addIssue({ code: "custom", path:
|
|
15981
|
+
ctx.addIssue({ code: "custom", path: path37, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
15999
15982
|
}
|
|
16000
15983
|
}
|
|
16001
15984
|
}
|
|
@@ -16657,19 +16640,19 @@ function failWriteValidation3(message) {
|
|
|
16657
16640
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
16658
16641
|
process.exit(1);
|
|
16659
16642
|
}
|
|
16660
|
-
function loadJsonFileArg3(
|
|
16661
|
-
if (typeof
|
|
16643
|
+
function loadJsonFileArg3(path37) {
|
|
16644
|
+
if (typeof path37 !== "string" || path37.length === 0) {
|
|
16662
16645
|
return {};
|
|
16663
16646
|
}
|
|
16664
16647
|
try {
|
|
16665
|
-
const parsed = JSON.parse(readFileSync8(
|
|
16648
|
+
const parsed = JSON.parse(readFileSync8(path37, "utf8"));
|
|
16666
16649
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
16667
|
-
failWriteValidation3(`${
|
|
16650
|
+
failWriteValidation3(`${path37} must contain a JSON object`);
|
|
16668
16651
|
}
|
|
16669
16652
|
return parsed;
|
|
16670
16653
|
} catch (err) {
|
|
16671
16654
|
if (err instanceof SyntaxError) {
|
|
16672
|
-
failWriteValidation3(`${
|
|
16655
|
+
failWriteValidation3(`${path37} is not valid JSON: ${err.message}`);
|
|
16673
16656
|
}
|
|
16674
16657
|
throw err;
|
|
16675
16658
|
}
|
|
@@ -20681,14 +20664,99 @@ async function probeDuration(filePath) {
|
|
|
20681
20664
|
}
|
|
20682
20665
|
|
|
20683
20666
|
// src/commands/canvas/rerun.ts
|
|
20684
|
-
import
|
|
20667
|
+
import path15 from "path";
|
|
20685
20668
|
import { defineCommand as defineCommand96 } from "citty";
|
|
20686
20669
|
|
|
20687
20670
|
// src/commands/canvas/run.ts
|
|
20688
20671
|
import { readFile as readFile10 } from "fs/promises";
|
|
20689
|
-
import
|
|
20672
|
+
import path14 from "path";
|
|
20690
20673
|
import { defineCommand as defineCommand95 } from "citty";
|
|
20691
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
|
+
|
|
20692
20760
|
// src/commands/canvas/placeholders.ts
|
|
20693
20761
|
function unsuppliedPlaceholderAssets(canvas) {
|
|
20694
20762
|
const nodes = canvas?.nodes;
|
|
@@ -20706,7 +20774,7 @@ function unsuppliedPlaceholderAssets(canvas) {
|
|
|
20706
20774
|
}
|
|
20707
20775
|
|
|
20708
20776
|
// src/commands/canvas/resolve-paths.ts
|
|
20709
|
-
import
|
|
20777
|
+
import path5 from "path";
|
|
20710
20778
|
function resolveRelativeCanvasPaths(canvas, baseDir) {
|
|
20711
20779
|
if (!canvas || typeof canvas !== "object") return canvas;
|
|
20712
20780
|
const c = canvas;
|
|
@@ -20719,24 +20787,24 @@ function resolveNode(node, baseDir) {
|
|
|
20719
20787
|
const params = n.params;
|
|
20720
20788
|
if (!params || typeof params !== "object") return node;
|
|
20721
20789
|
if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
|
|
20722
|
-
return { ...node, params: { ...params, path:
|
|
20790
|
+
return { ...node, params: { ...params, path: path5.resolve(baseDir, params.path) } };
|
|
20723
20791
|
}
|
|
20724
20792
|
if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
|
|
20725
|
-
return { ...node, params: { ...params, composition:
|
|
20793
|
+
return { ...node, params: { ...params, composition: path5.resolve(baseDir, params.composition) } };
|
|
20726
20794
|
}
|
|
20727
20795
|
return node;
|
|
20728
20796
|
}
|
|
20729
20797
|
function isResolvableRelative(value) {
|
|
20730
|
-
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);
|
|
20731
20799
|
}
|
|
20732
20800
|
|
|
20733
20801
|
// src/commands/canvas/source-version.ts
|
|
20734
20802
|
import { readFile as readFile5 } from "fs/promises";
|
|
20735
|
-
import
|
|
20803
|
+
import path7 from "path";
|
|
20736
20804
|
|
|
20737
20805
|
// src/commands/canvas/scene-files.ts
|
|
20738
|
-
import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as
|
|
20739
|
-
import
|
|
20806
|
+
import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as writeFile3 } from "fs/promises";
|
|
20807
|
+
import path6 from "path";
|
|
20740
20808
|
var SCENES_DIR = "scenes";
|
|
20741
20809
|
var GLOBAL_PROMPT_FILE = "prompt.json";
|
|
20742
20810
|
var REBUILD_FILE = "prompt.rebuild.json";
|
|
@@ -20753,19 +20821,19 @@ function splitBlueprint(blueprint) {
|
|
|
20753
20821
|
}
|
|
20754
20822
|
async function writeSceneFiles(outDir, blueprint) {
|
|
20755
20823
|
const { global, scenes } = splitBlueprint(blueprint);
|
|
20756
|
-
await
|
|
20824
|
+
await writeFile3(path6.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
|
|
20757
20825
|
`, "utf8");
|
|
20758
|
-
const scenesDir =
|
|
20826
|
+
const scenesDir = path6.join(outDir, SCENES_DIR);
|
|
20759
20827
|
await mkdir2(scenesDir, { recursive: true });
|
|
20760
20828
|
const written = /* @__PURE__ */ new Set();
|
|
20761
20829
|
for (let i = 0; i < scenes.length; i++) {
|
|
20762
20830
|
const name = sceneFileName(i, scenes.length);
|
|
20763
20831
|
written.add(name);
|
|
20764
|
-
await
|
|
20832
|
+
await writeFile3(path6.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
|
|
20765
20833
|
`, "utf8");
|
|
20766
20834
|
}
|
|
20767
20835
|
for (const name of await listSceneFileNames(scenesDir)) {
|
|
20768
|
-
if (!written.has(name)) await rm(
|
|
20836
|
+
if (!written.has(name)) await rm(path6.join(scenesDir, name), { force: true });
|
|
20769
20837
|
}
|
|
20770
20838
|
}
|
|
20771
20839
|
async function listSceneFileNames(scenesDir) {
|
|
@@ -20778,13 +20846,13 @@ async function listSceneFileNames(scenesDir) {
|
|
|
20778
20846
|
return entries.filter((n) => /^s\d+\.json$/.test(n)).sort(bySceneIndex);
|
|
20779
20847
|
}
|
|
20780
20848
|
async function listSceneFiles(creativeDir) {
|
|
20781
|
-
const scenesDir =
|
|
20782
|
-
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));
|
|
20783
20851
|
}
|
|
20784
20852
|
async function reassembleBlueprint(creativeDir) {
|
|
20785
20853
|
const files = await listSceneFiles(creativeDir);
|
|
20786
20854
|
if (files.length === 0) return null;
|
|
20787
|
-
const globalRaw = await readFile4(
|
|
20855
|
+
const globalRaw = await readFile4(path6.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
|
|
20788
20856
|
const global = JSON.parse(globalRaw);
|
|
20789
20857
|
const scenes = [];
|
|
20790
20858
|
for (const file of files) {
|
|
@@ -20806,8 +20874,8 @@ async function computeSourceSha(canvasPath) {
|
|
|
20806
20874
|
} catch {
|
|
20807
20875
|
return void 0;
|
|
20808
20876
|
}
|
|
20809
|
-
const canvasDir =
|
|
20810
|
-
const promptPath =
|
|
20877
|
+
const canvasDir = path7.dirname(canvasPath);
|
|
20878
|
+
const promptPath = path7.join(canvasDir, "prompt.json");
|
|
20811
20879
|
let promptBytes;
|
|
20812
20880
|
try {
|
|
20813
20881
|
promptBytes = await readFile5(promptPath);
|
|
@@ -20834,7 +20902,7 @@ async function computeSourceSha(canvasPath) {
|
|
|
20834
20902
|
|
|
20835
20903
|
// src/commands/canvas/scene-projection.ts
|
|
20836
20904
|
import { readFile as readFile6 } from "fs/promises";
|
|
20837
|
-
import
|
|
20905
|
+
import path8 from "path";
|
|
20838
20906
|
|
|
20839
20907
|
// src/engine/scaffold/video.ts
|
|
20840
20908
|
import { toCardinal as nwAr } from "n2words/ar-SA";
|
|
@@ -24120,12 +24188,12 @@ function nonPromptParamsDiverge(live = {}, rebuilt = {}) {
|
|
|
24120
24188
|
return false;
|
|
24121
24189
|
}
|
|
24122
24190
|
async function syncSceneNodeParams(canvas, canvasPath, log) {
|
|
24123
|
-
const creativeDir =
|
|
24191
|
+
const creativeDir = path8.dirname(canvasPath);
|
|
24124
24192
|
const blueprint = await reassembleBlueprint(creativeDir);
|
|
24125
24193
|
if (!blueprint) return "not_applicable";
|
|
24126
24194
|
let rebuildRaw;
|
|
24127
24195
|
try {
|
|
24128
|
-
rebuildRaw = await readFile6(
|
|
24196
|
+
rebuildRaw = await readFile6(path8.join(creativeDir, REBUILD_FILE), "utf8");
|
|
24129
24197
|
} catch {
|
|
24130
24198
|
return "not_applicable";
|
|
24131
24199
|
}
|
|
@@ -24166,7 +24234,7 @@ async function syncSceneNodeParams(canvas, canvasPath, log) {
|
|
|
24166
24234
|
}
|
|
24167
24235
|
|
|
24168
24236
|
// src/commands/canvas/style-projection.ts
|
|
24169
|
-
import { readFile as readFile7, writeFile as
|
|
24237
|
+
import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
|
|
24170
24238
|
function findBlueprintProjection(canvas) {
|
|
24171
24239
|
if (!canvas || typeof canvas !== "object") return null;
|
|
24172
24240
|
const nodes = canvas.nodes;
|
|
@@ -24197,7 +24265,7 @@ async function syncStyleProjection(canvas, log) {
|
|
|
24197
24265
|
const rendered = renderStyleProjection(await readFile7(pair.promptPath, "utf8"));
|
|
24198
24266
|
const current = await readFile7(pair.stylePath, "utf8").catch(() => null);
|
|
24199
24267
|
if (current === rendered) return "up_to_date";
|
|
24200
|
-
await
|
|
24268
|
+
await writeFile4(pair.stylePath, rendered, "utf8");
|
|
24201
24269
|
log(
|
|
24202
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"
|
|
24203
24271
|
);
|
|
@@ -24257,13 +24325,13 @@ ${body}` : header || body || compactJson(record);
|
|
|
24257
24325
|
}
|
|
24258
24326
|
|
|
24259
24327
|
// src/commands/canvas/run-record.ts
|
|
24260
|
-
import
|
|
24328
|
+
import path9 from "path";
|
|
24261
24329
|
var MAX_RUN_NODES = 200;
|
|
24262
24330
|
var MAX_OUTPUTS_PER_NODE = 10;
|
|
24263
24331
|
var MAX_FINAL_OUTPUTS = 10;
|
|
24264
24332
|
var MAX_CREATIVE_SLUG_LENGTH = 100;
|
|
24265
24333
|
function creativeSlugFromCanvasPath(filePath) {
|
|
24266
|
-
const normalized = filePath.split(
|
|
24334
|
+
const normalized = filePath.split(path9.sep).join("/");
|
|
24267
24335
|
const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
|
|
24268
24336
|
const slug = match?.[1] ?? null;
|
|
24269
24337
|
return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
|
|
@@ -24551,7 +24619,7 @@ var RunRecordPoster = class {
|
|
|
24551
24619
|
|
|
24552
24620
|
// src/commands/canvas/run-retention.ts
|
|
24553
24621
|
import { rm as rm2 } from "fs/promises";
|
|
24554
|
-
import
|
|
24622
|
+
import path10 from "path";
|
|
24555
24623
|
function runDirsToPrune(entries, keep, currentRunId) {
|
|
24556
24624
|
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
24557
24625
|
if (keep <= 0) return runs;
|
|
@@ -24568,7 +24636,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
24568
24636
|
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
24569
24637
|
if (toPrune.length === 0) return;
|
|
24570
24638
|
for (const dir of toPrune) {
|
|
24571
|
-
await rm2(
|
|
24639
|
+
await rm2(path10.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
24572
24640
|
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
24573
24641
|
);
|
|
24574
24642
|
}
|
|
@@ -24576,13 +24644,13 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
24576
24644
|
}
|
|
24577
24645
|
|
|
24578
24646
|
// src/commands/canvas/dirty-marker.ts
|
|
24579
|
-
import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as
|
|
24580
|
-
import
|
|
24647
|
+
import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as writeFile5 } from "fs/promises";
|
|
24648
|
+
import path11 from "path";
|
|
24581
24649
|
function creativeDirtyDir(base) {
|
|
24582
|
-
return base ??
|
|
24650
|
+
return base ?? path11.resolve("canvas", ".dirty");
|
|
24583
24651
|
}
|
|
24584
24652
|
function dirtyMarkerFile(slug, base) {
|
|
24585
|
-
return
|
|
24653
|
+
return path11.join(creativeDirtyDir(base), `${slug}.json`);
|
|
24586
24654
|
}
|
|
24587
24655
|
async function clearCreativeDirty(slug, base) {
|
|
24588
24656
|
try {
|
|
@@ -24592,18 +24660,18 @@ async function clearCreativeDirty(slug, base) {
|
|
|
24592
24660
|
}
|
|
24593
24661
|
|
|
24594
24662
|
// src/commands/canvas/run-resume.ts
|
|
24595
|
-
import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as
|
|
24596
|
-
import
|
|
24663
|
+
import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as writeFile6 } from "fs/promises";
|
|
24664
|
+
import path12 from "path";
|
|
24597
24665
|
function markerKey(canvasPath) {
|
|
24598
24666
|
const slug = creativeSlugFromCanvasPath(canvasPath);
|
|
24599
|
-
const identity = slug ??
|
|
24667
|
+
const identity = slug ?? path12.relative(process.cwd(), path12.resolve(canvasPath));
|
|
24600
24668
|
return sha256Hex(Buffer.from(identity)).slice(0, 32);
|
|
24601
24669
|
}
|
|
24602
24670
|
function legacyMarkerKey(canvasPath) {
|
|
24603
|
-
return sha256Hex(Buffer.from(
|
|
24671
|
+
return sha256Hex(Buffer.from(path12.resolve(canvasPath))).slice(0, 32);
|
|
24604
24672
|
}
|
|
24605
24673
|
function markerFile(outputsDir, key) {
|
|
24606
|
-
return
|
|
24674
|
+
return path12.join(outputsDir, ".inflight", `${key}.json`);
|
|
24607
24675
|
}
|
|
24608
24676
|
var REMOTE_ADOPT_STALE_MS = 12e4;
|
|
24609
24677
|
function classifyRemoteRun(run, now) {
|
|
@@ -24650,8 +24718,8 @@ async function readMarkerRunId(outputsDir, canvasPath) {
|
|
|
24650
24718
|
async function markRunInFlight(outputsDir, canvasPath, runId) {
|
|
24651
24719
|
try {
|
|
24652
24720
|
const file = markerFile(outputsDir, markerKey(canvasPath));
|
|
24653
|
-
await mkdir4(
|
|
24654
|
-
await
|
|
24721
|
+
await mkdir4(path12.dirname(file), { recursive: true });
|
|
24722
|
+
await writeFile6(file, JSON.stringify({ runId, canvasPath: path12.resolve(canvasPath), startedAt: Date.now() }));
|
|
24655
24723
|
} catch {
|
|
24656
24724
|
}
|
|
24657
24725
|
}
|
|
@@ -24665,8 +24733,8 @@ async function clearRunMarker(outputsDir, canvasPath) {
|
|
|
24665
24733
|
}
|
|
24666
24734
|
|
|
24667
24735
|
// src/commands/canvas/run-snapshot.ts
|
|
24668
|
-
import { mkdir as mkdir5, readdir as readdir4, readFile as readFile9, stat as stat2, writeFile as
|
|
24669
|
-
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";
|
|
24670
24738
|
var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
|
|
24671
24739
|
var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
|
|
24672
24740
|
var EXT_TO_MIME = {
|
|
@@ -24693,15 +24761,15 @@ var EXT_TO_MIME = {
|
|
|
24693
24761
|
woff2: "font/woff2"
|
|
24694
24762
|
};
|
|
24695
24763
|
function mimeForFile(filePath) {
|
|
24696
|
-
const ext =
|
|
24764
|
+
const ext = path13.extname(filePath).slice(1).toLowerCase();
|
|
24697
24765
|
return EXT_TO_MIME[ext] ?? "application/octet-stream";
|
|
24698
24766
|
}
|
|
24699
24767
|
function toPosix(p) {
|
|
24700
|
-
return p.split(
|
|
24768
|
+
return p.split(path13.sep).join("/");
|
|
24701
24769
|
}
|
|
24702
|
-
function
|
|
24703
|
-
const rel =
|
|
24704
|
-
return !rel.startsWith("..") && !
|
|
24770
|
+
function isInside2(dir, target) {
|
|
24771
|
+
const rel = path13.relative(dir, target);
|
|
24772
|
+
return !rel.startsWith("..") && !path13.isAbsolute(rel);
|
|
24705
24773
|
}
|
|
24706
24774
|
function localPathRefsFromCanvas(parsed) {
|
|
24707
24775
|
const nodes = parsed?.nodes;
|
|
@@ -24726,24 +24794,24 @@ function isSnapshotablePath(value) {
|
|
|
24726
24794
|
async function sourceRefsToSnapshot(canvasDir, parsed) {
|
|
24727
24795
|
const refs = new Set(localPathRefsFromCanvas(parsed));
|
|
24728
24796
|
for (const sceneFile of await listSceneFiles(canvasDir)) {
|
|
24729
|
-
refs.add(toPosix(
|
|
24797
|
+
refs.add(toPosix(path13.relative(canvasDir, sceneFile)));
|
|
24730
24798
|
}
|
|
24731
24799
|
refs.add(REBUILD_FILE);
|
|
24732
24800
|
return [...refs];
|
|
24733
24801
|
}
|
|
24734
24802
|
async function uploadRunSnapshot(client, opts) {
|
|
24735
24803
|
try {
|
|
24736
|
-
const canvasDir =
|
|
24804
|
+
const canvasDir = path13.dirname(opts.canvasPath);
|
|
24737
24805
|
const put = (bytes, mime) => putContentAddressed(client, bytes, mime, opts.signal);
|
|
24738
24806
|
const canvasBytes = Buffer.from(opts.raw);
|
|
24739
24807
|
const canvasUpload = await put(canvasBytes, "application/json");
|
|
24740
24808
|
const files = [];
|
|
24741
24809
|
const skipped = [];
|
|
24742
24810
|
for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
|
|
24743
|
-
const abs =
|
|
24744
|
-
if (!
|
|
24811
|
+
const abs = path13.isAbsolute(refPath) ? refPath : path13.resolve(canvasDir, refPath);
|
|
24812
|
+
if (!isInside2(canvasDir, abs)) {
|
|
24745
24813
|
skipped.push({
|
|
24746
|
-
path: toPosix(
|
|
24814
|
+
path: toPosix(path13.relative(canvasDir, abs)),
|
|
24747
24815
|
reason: "outside the creative folder \u2014 read from the workspace on rerun"
|
|
24748
24816
|
});
|
|
24749
24817
|
continue;
|
|
@@ -24752,12 +24820,12 @@ async function uploadRunSnapshot(client, opts) {
|
|
|
24752
24820
|
try {
|
|
24753
24821
|
st = await stat2(abs);
|
|
24754
24822
|
} catch {
|
|
24755
|
-
skipped.push({ path: toPosix(
|
|
24823
|
+
skipped.push({ path: toPosix(path13.relative(canvasDir, abs)), reason: "missing" });
|
|
24756
24824
|
continue;
|
|
24757
24825
|
}
|
|
24758
24826
|
const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
|
|
24759
24827
|
for (const file of fileList) {
|
|
24760
|
-
const rel = toPosix(
|
|
24828
|
+
const rel = toPosix(path13.relative(canvasDir, file));
|
|
24761
24829
|
const size = (await stat2(file)).size;
|
|
24762
24830
|
if (size > MAX_SNAPSHOT_FILE_BYTES) {
|
|
24763
24831
|
skipped.push({ path: rel, reason: `too large (${size} bytes)` });
|
|
@@ -24772,7 +24840,7 @@ async function uploadRunSnapshot(client, opts) {
|
|
|
24772
24840
|
schema: SNAPSHOT_SCHEMA,
|
|
24773
24841
|
creativeSlug: opts.creativeSlug,
|
|
24774
24842
|
canvasSha: canvasUpload.sha256,
|
|
24775
|
-
canvas: { path:
|
|
24843
|
+
canvas: { path: path13.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
|
|
24776
24844
|
files,
|
|
24777
24845
|
skipped: skipped.length > 0 ? skipped : void 0
|
|
24778
24846
|
};
|
|
@@ -24799,7 +24867,7 @@ async function putContentAddressed(client, bytes, mime, signal) {
|
|
|
24799
24867
|
}
|
|
24800
24868
|
async function listFilesRecursive(dir) {
|
|
24801
24869
|
const entries = await readdir4(dir, { recursive: true, withFileTypes: true });
|
|
24802
|
-
return entries.filter((d) => d.isFile()).map((d) =>
|
|
24870
|
+
return entries.filter((d) => d.isFile()).map((d) => path13.join(d.parentPath, d.name));
|
|
24803
24871
|
}
|
|
24804
24872
|
var SnapshotConflictError = class extends Error {
|
|
24805
24873
|
conflicts;
|
|
@@ -24811,16 +24879,16 @@ var SnapshotConflictError = class extends Error {
|
|
|
24811
24879
|
};
|
|
24812
24880
|
async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
24813
24881
|
const entries = [manifest.canvas, ...manifest.files];
|
|
24814
|
-
const resolvedTarget =
|
|
24882
|
+
const resolvedTarget = path13.resolve(targetDir);
|
|
24815
24883
|
const planned = [];
|
|
24816
24884
|
const conflicts = [];
|
|
24817
24885
|
const upToDate = [];
|
|
24818
24886
|
for (const entry of entries) {
|
|
24819
|
-
if (
|
|
24887
|
+
if (path13.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
|
|
24820
24888
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
24821
24889
|
}
|
|
24822
|
-
const target =
|
|
24823
|
-
if (target !== resolvedTarget && !target.startsWith(resolvedTarget +
|
|
24890
|
+
const target = path13.resolve(resolvedTarget, entry.path);
|
|
24891
|
+
if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path13.sep)) {
|
|
24824
24892
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
24825
24893
|
}
|
|
24826
24894
|
const existing = await readFile9(target).catch(() => null);
|
|
@@ -24845,11 +24913,11 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
|
24845
24913
|
if (sha256Hex(bytes) !== entry.sha256) {
|
|
24846
24914
|
throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
|
|
24847
24915
|
}
|
|
24848
|
-
await mkdir5(
|
|
24849
|
-
await
|
|
24916
|
+
await mkdir5(path13.dirname(target), { recursive: true });
|
|
24917
|
+
await writeFile7(target, bytes);
|
|
24850
24918
|
restored.push(entry.path);
|
|
24851
24919
|
}
|
|
24852
|
-
return { canvasPath:
|
|
24920
|
+
return { canvasPath: path13.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
|
|
24853
24921
|
}
|
|
24854
24922
|
|
|
24855
24923
|
// src/commands/canvas/run.ts
|
|
@@ -24929,7 +24997,7 @@ function resolveMaxCredits(...candidates) {
|
|
|
24929
24997
|
return void 0;
|
|
24930
24998
|
}
|
|
24931
24999
|
async function executeCanvasRun(opts) {
|
|
24932
|
-
const filePath =
|
|
25000
|
+
const filePath = path14.resolve(opts.file);
|
|
24933
25001
|
const raw = await readFile10(filePath, "utf8");
|
|
24934
25002
|
let parsed;
|
|
24935
25003
|
try {
|
|
@@ -24942,7 +25010,17 @@ async function executeCanvasRun(opts) {
|
|
|
24942
25010
|
}
|
|
24943
25011
|
const attemptedSlug = creativeSlugFromCanvasPath(filePath);
|
|
24944
25012
|
if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
|
|
24945
|
-
|
|
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));
|
|
24946
25024
|
try {
|
|
24947
25025
|
await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
24948
25026
|
`));
|
|
@@ -25045,10 +25123,10 @@ async function executeCanvasRun(opts) {
|
|
|
25045
25123
|
process.stdout.write(`[warn] ${w.code}: ${w.message}
|
|
25046
25124
|
`);
|
|
25047
25125
|
}
|
|
25048
|
-
const canvasSha = sha256Hex(Buffer.from(
|
|
25126
|
+
const canvasSha = sha256Hex(Buffer.from(canvasText));
|
|
25049
25127
|
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
25050
25128
|
const client = opts.record === false ? null : buildBackendClient();
|
|
25051
|
-
const outputsDir = opts.outputsDir ?
|
|
25129
|
+
const outputsDir = opts.outputsDir ? path14.resolve(opts.outputsDir) : path14.resolve("canvas");
|
|
25052
25130
|
const { runId, resumed, source, concurrentRunId } = await resolveRunId({
|
|
25053
25131
|
explicitRunId: opts.runId,
|
|
25054
25132
|
fresh: opts.fresh === true,
|
|
@@ -25079,10 +25157,10 @@ async function executeCanvasRun(opts) {
|
|
|
25079
25157
|
});
|
|
25080
25158
|
}
|
|
25081
25159
|
await markRunInFlight(outputsDir, filePath, runId);
|
|
25082
|
-
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;
|
|
25083
25161
|
const recordMeta = {
|
|
25084
25162
|
creativeSlug,
|
|
25085
|
-
canvasPath:
|
|
25163
|
+
canvasPath: path14.relative(process.cwd(), filePath) || void 0,
|
|
25086
25164
|
canvasSha,
|
|
25087
25165
|
// The fingerprint the dashboard compares against the current source to flag
|
|
25088
25166
|
// "edited since last render". Computed from the on-disk canvas.json +
|
|
@@ -25269,7 +25347,7 @@ var rerunCommand = defineCommand96({
|
|
|
25269
25347
|
if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
|
|
25270
25348
|
fail2("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
|
|
25271
25349
|
}
|
|
25272
|
-
const targetDir =
|
|
25350
|
+
const targetDir = path15.resolve("src", "creatives", slug);
|
|
25273
25351
|
let restoredCanvasPath;
|
|
25274
25352
|
try {
|
|
25275
25353
|
const restore = await restoreRunSnapshot(manifest, targetDir, { force: args["force-remote"] === true });
|
|
@@ -25318,8 +25396,8 @@ async function fetchManifest(url) {
|
|
|
25318
25396
|
}
|
|
25319
25397
|
|
|
25320
25398
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
25321
|
-
import { access, mkdir as mkdir6, readFile as readFile12, writeFile as
|
|
25322
|
-
import
|
|
25399
|
+
import { access, mkdir as mkdir6, readFile as readFile12, writeFile as writeFile8 } from "fs/promises";
|
|
25400
|
+
import path19 from "path";
|
|
25323
25401
|
import { defineCommand as defineCommand98 } from "citty";
|
|
25324
25402
|
|
|
25325
25403
|
// src/engine/scaffold/staticAd.ts
|
|
@@ -25601,7 +25679,7 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
25601
25679
|
}
|
|
25602
25680
|
|
|
25603
25681
|
// src/commands/canvas/creative-definition.ts
|
|
25604
|
-
import
|
|
25682
|
+
import path16 from "path";
|
|
25605
25683
|
var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
|
|
25606
25684
|
var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
|
|
25607
25685
|
function titleFromSlug(slug) {
|
|
@@ -25649,16 +25727,16 @@ function buildCreativeDefinition(input) {
|
|
|
25649
25727
|
}
|
|
25650
25728
|
|
|
25651
25729
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
25652
|
-
import
|
|
25730
|
+
import path17 from "path";
|
|
25653
25731
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
25654
25732
|
const file = rawFile.trim();
|
|
25655
25733
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
25656
|
-
const imageSource = imageIsUrl ? file :
|
|
25657
|
-
const outPath = out ?
|
|
25658
|
-
const blueprintPath =
|
|
25659
|
-
const creativeDir = slug ?
|
|
25660
|
-
const definitionPath = creativeDir ?
|
|
25661
|
-
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;
|
|
25662
25740
|
return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
|
|
25663
25741
|
}
|
|
25664
25742
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -25669,7 +25747,7 @@ function isValidScaffoldSlug(slug) {
|
|
|
25669
25747
|
|
|
25670
25748
|
// src/commands/canvas/sync-definition.ts
|
|
25671
25749
|
import { readdir as readdir5, readFile as readFile11, stat as stat3 } from "fs/promises";
|
|
25672
|
-
import
|
|
25750
|
+
import path18 from "path";
|
|
25673
25751
|
import { defineCommand as defineCommand97 } from "citty";
|
|
25674
25752
|
|
|
25675
25753
|
// src/commands/canvas/definition-graph.ts
|
|
@@ -25762,15 +25840,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
25762
25840
|
}
|
|
25763
25841
|
}
|
|
25764
25842
|
async function resolveCanvasPath(inputPath) {
|
|
25765
|
-
const resolved =
|
|
25843
|
+
const resolved = path18.resolve(inputPath);
|
|
25766
25844
|
let dir = resolved;
|
|
25767
25845
|
try {
|
|
25768
25846
|
if ((await stat3(resolved)).isFile()) {
|
|
25769
25847
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
25770
|
-
dir =
|
|
25848
|
+
dir = path18.dirname(resolved);
|
|
25771
25849
|
}
|
|
25772
25850
|
} catch {
|
|
25773
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
25851
|
+
dir = resolved.endsWith(".canvas.json") ? path18.dirname(resolved) : resolved;
|
|
25774
25852
|
}
|
|
25775
25853
|
let entries;
|
|
25776
25854
|
try {
|
|
@@ -25781,7 +25859,7 @@ async function resolveCanvasPath(inputPath) {
|
|
|
25781
25859
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
25782
25860
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
25783
25861
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
25784
|
-
return chosen ?
|
|
25862
|
+
return chosen ? path18.join(dir, chosen) : null;
|
|
25785
25863
|
}
|
|
25786
25864
|
var syncDefinitionCommand = defineCommand97({
|
|
25787
25865
|
meta: {
|
|
@@ -26062,7 +26140,7 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26062
26140
|
process.cwd(),
|
|
26063
26141
|
slug
|
|
26064
26142
|
);
|
|
26065
|
-
await mkdir6(
|
|
26143
|
+
await mkdir6(path19.dirname(outPath), { recursive: true });
|
|
26066
26144
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
|
|
26067
26145
|
let durableSourceUrl;
|
|
26068
26146
|
if (referencesDir) {
|
|
@@ -26088,7 +26166,7 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26088
26166
|
if (layout && annotated && typeof annotated === "object") {
|
|
26089
26167
|
annotated.layout = layout;
|
|
26090
26168
|
}
|
|
26091
|
-
await
|
|
26169
|
+
await writeFile8(blueprintPath, `${JSON.stringify(annotated, null, 2)}
|
|
26092
26170
|
`, "utf8");
|
|
26093
26171
|
let canvasImagePath = imageSource;
|
|
26094
26172
|
let canvasImageIsUrl = imageIsUrl;
|
|
@@ -26133,10 +26211,10 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26133
26211
|
);
|
|
26134
26212
|
process.exit(2);
|
|
26135
26213
|
}
|
|
26136
|
-
await
|
|
26214
|
+
await writeFile8(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
26137
26215
|
`, "utf8");
|
|
26138
26216
|
if (definitionPath && !await fileExists(definitionPath)) {
|
|
26139
|
-
await
|
|
26217
|
+
await writeFile8(
|
|
26140
26218
|
definitionPath,
|
|
26141
26219
|
buildCreativeDefinition({
|
|
26142
26220
|
title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
|
|
@@ -26182,7 +26260,7 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26182
26260
|
run_estimated_credits: validation.estimatedCredits
|
|
26183
26261
|
},
|
|
26184
26262
|
checklist: {
|
|
26185
|
-
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.`,
|
|
26186
26264
|
assets_to_supply: report.elements,
|
|
26187
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)",
|
|
26188
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)",
|
|
@@ -26199,9 +26277,9 @@ var scaffoldStaticAdCommand = defineCommand98({
|
|
|
26199
26277
|
});
|
|
26200
26278
|
|
|
26201
26279
|
// src/commands/canvas/scaffold-video.ts
|
|
26202
|
-
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";
|
|
26203
26281
|
import { tmpdir as tmpdir2 } from "os";
|
|
26204
|
-
import
|
|
26282
|
+
import path22 from "path";
|
|
26205
26283
|
import { defineCommand as defineCommand99 } from "citty";
|
|
26206
26284
|
|
|
26207
26285
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -26341,24 +26419,24 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
26341
26419
|
}
|
|
26342
26420
|
|
|
26343
26421
|
// src/commands/canvas/composition-path.ts
|
|
26344
|
-
import { existsSync as
|
|
26345
|
-
import
|
|
26346
|
-
function resolveShippedCanvasDir(name, startDir, exists =
|
|
26347
|
-
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);
|
|
26348
26426
|
let dir = startDir;
|
|
26349
26427
|
for (let i = 0; i < maxDepth; i++) {
|
|
26350
|
-
const candidate =
|
|
26351
|
-
if (exists(
|
|
26352
|
-
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);
|
|
26353
26431
|
if (parent === dir) break;
|
|
26354
26432
|
dir = parent;
|
|
26355
26433
|
}
|
|
26356
|
-
return
|
|
26434
|
+
return path20.resolve(startDir, "../../../", rel);
|
|
26357
26435
|
}
|
|
26358
26436
|
|
|
26359
26437
|
// src/commands/canvas/gitignore.ts
|
|
26360
26438
|
import { appendFile, readFile as readFile14 } from "fs/promises";
|
|
26361
|
-
import
|
|
26439
|
+
import path21 from "path";
|
|
26362
26440
|
function missingGitignoreEntries(existing, entries) {
|
|
26363
26441
|
const present2 = new Set(
|
|
26364
26442
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -26366,7 +26444,7 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
26366
26444
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
26367
26445
|
}
|
|
26368
26446
|
async function ensureGitignore(dir, entries) {
|
|
26369
|
-
const file =
|
|
26447
|
+
const file = path21.join(dir, ".gitignore");
|
|
26370
26448
|
let existing;
|
|
26371
26449
|
try {
|
|
26372
26450
|
existing = await readFile14(file, "utf8");
|
|
@@ -26427,7 +26505,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
26427
26505
|
async function stageCaptions(outDir, transcript) {
|
|
26428
26506
|
const text2 = transcript?.trim();
|
|
26429
26507
|
if (!text2 || text2 === "[]") return {};
|
|
26430
|
-
const compositionPath =
|
|
26508
|
+
const compositionPath = path22.join(outDir, "tiktok-captions-composition");
|
|
26431
26509
|
await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
26432
26510
|
return { compositionPath };
|
|
26433
26511
|
}
|
|
@@ -26445,12 +26523,12 @@ function patchCompositionHtml(html, dims) {
|
|
|
26445
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`);
|
|
26446
26524
|
}
|
|
26447
26525
|
async function stampCompositionDims(compositionDir, dims) {
|
|
26448
|
-
const metaPath =
|
|
26526
|
+
const metaPath = path22.join(compositionDir, "meta.json");
|
|
26449
26527
|
const rawMeta = await readFile15(metaPath, "utf8");
|
|
26450
|
-
await
|
|
26451
|
-
const htmlPath =
|
|
26528
|
+
await writeFile9(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
26529
|
+
const htmlPath = path22.join(compositionDir, "index.html");
|
|
26452
26530
|
const rawHtml = await readFile15(htmlPath, "utf8");
|
|
26453
|
-
await
|
|
26531
|
+
await writeFile9(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
26454
26532
|
}
|
|
26455
26533
|
function parseElements2(raw) {
|
|
26456
26534
|
const parsed = JSON.parse(raw);
|
|
@@ -26497,7 +26575,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
26497
26575
|
"video/x-matroska": ".mkv"
|
|
26498
26576
|
};
|
|
26499
26577
|
function referenceVideoExt(url, contentType) {
|
|
26500
|
-
const fromPath =
|
|
26578
|
+
const fromPath = path22.extname(new URL(url).pathname).toLowerCase();
|
|
26501
26579
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
26502
26580
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
26503
26581
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -26523,7 +26601,7 @@ function videoDefinitionDescription(blueprint) {
|
|
|
26523
26601
|
return typeof product === "string" && product.trim() ? product.trim() : void 0;
|
|
26524
26602
|
}
|
|
26525
26603
|
async function materializeReferenceVideo(fileArg2) {
|
|
26526
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
26604
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path22.resolve(fileArg2);
|
|
26527
26605
|
let bytes;
|
|
26528
26606
|
let contentType;
|
|
26529
26607
|
try {
|
|
@@ -26535,11 +26613,11 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
26535
26613
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
26536
26614
|
}
|
|
26537
26615
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
26538
|
-
const dest =
|
|
26616
|
+
const dest = path22.join(
|
|
26539
26617
|
tmpdir2(),
|
|
26540
26618
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
26541
26619
|
);
|
|
26542
|
-
await
|
|
26620
|
+
await writeFile9(dest, bytes);
|
|
26543
26621
|
return dest;
|
|
26544
26622
|
}
|
|
26545
26623
|
function resolveSeamDedup(raw) {
|
|
@@ -26755,11 +26833,11 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26755
26833
|
} catch (e) {
|
|
26756
26834
|
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
26757
26835
|
}
|
|
26758
|
-
const base =
|
|
26759
|
-
const outPath = args.out ?
|
|
26760
|
-
const outDir =
|
|
26761
|
-
const blueprintPath =
|
|
26762
|
-
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");
|
|
26763
26841
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
26764
26842
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
26765
26843
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -26786,7 +26864,7 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26786
26864
|
await mkdir7(outDir, { recursive: true });
|
|
26787
26865
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
26788
26866
|
await writeSceneFiles(outDir, annotated);
|
|
26789
|
-
await
|
|
26867
|
+
await writeFile9(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
|
|
26790
26868
|
let aspect;
|
|
26791
26869
|
try {
|
|
26792
26870
|
aspect = resolveAspect(
|
|
@@ -26804,10 +26882,10 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26804
26882
|
`
|
|
26805
26883
|
);
|
|
26806
26884
|
}
|
|
26807
|
-
const compositionDest =
|
|
26885
|
+
const compositionDest = path22.join(outDir, "video-overlay-composition");
|
|
26808
26886
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
26809
26887
|
await stampCompositionDims(compositionDest, outDims);
|
|
26810
|
-
const indexPath =
|
|
26888
|
+
const indexPath = path22.join(compositionDest, "index.html");
|
|
26811
26889
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
|
|
26812
26890
|
const indexHtml = await readFile15(indexPath, "utf8");
|
|
26813
26891
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
@@ -26817,16 +26895,16 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26817
26895
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
26818
26896
|
);
|
|
26819
26897
|
}
|
|
26820
|
-
await
|
|
26898
|
+
await writeFile9(indexPath, injected, "utf8");
|
|
26821
26899
|
const captions = await stageCaptions(outDir, transcript);
|
|
26822
26900
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
26823
26901
|
const opts = {
|
|
26824
26902
|
imageModel,
|
|
26825
26903
|
videoModel,
|
|
26826
|
-
overlayCompositionPath:
|
|
26827
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
26828
|
-
blueprintPath:
|
|
26829
|
-
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),
|
|
26830
26908
|
frames,
|
|
26831
26909
|
ambient: Boolean(args.ambient),
|
|
26832
26910
|
seamDedup: resolveSeamDedup(args["seam-dedup"]),
|
|
@@ -26851,10 +26929,10 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26851
26929
|
todo.blocking_validation_issues = validation.issues;
|
|
26852
26930
|
meta.todo = todo;
|
|
26853
26931
|
}
|
|
26854
|
-
await
|
|
26932
|
+
await writeFile9(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
26855
26933
|
`, "utf8");
|
|
26856
|
-
await
|
|
26857
|
-
|
|
26934
|
+
await writeFile9(
|
|
26935
|
+
path22.join(outDir, REBUILD_FILE),
|
|
26858
26936
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
26859
26937
|
`,
|
|
26860
26938
|
"utf8"
|
|
@@ -26879,9 +26957,9 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26879
26957
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
26880
26958
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
26881
26959
|
if (slug) {
|
|
26882
|
-
const definitionPath =
|
|
26960
|
+
const definitionPath = path22.join(outDir, "_definition.md");
|
|
26883
26961
|
if (!await fileExists2(definitionPath)) {
|
|
26884
|
-
await
|
|
26962
|
+
await writeFile9(
|
|
26885
26963
|
definitionPath,
|
|
26886
26964
|
buildCreativeDefinition({
|
|
26887
26965
|
title: titleFromSlug(slug),
|
|
@@ -26934,7 +27012,7 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26934
27012
|
graph: canvas.metadata?.video?.graph_stats
|
|
26935
27013
|
},
|
|
26936
27014
|
checklist: {
|
|
26937
|
-
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${
|
|
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.`,
|
|
26938
27016
|
recurring_elements_to_supply: report.elements,
|
|
26939
27017
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
26940
27018
|
scene: d.scene,
|
|
@@ -26971,8 +27049,8 @@ var scaffoldVideoCommand = defineCommand99({
|
|
|
26971
27049
|
});
|
|
26972
27050
|
|
|
26973
27051
|
// src/commands/canvas/set-prompt.ts
|
|
26974
|
-
import { readFile as readFile16, writeFile as
|
|
26975
|
-
import
|
|
27052
|
+
import { readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
|
|
27053
|
+
import path23 from "path";
|
|
26976
27054
|
import { defineCommand as defineCommand100 } from "citty";
|
|
26977
27055
|
function setNodePrompt(canvas, nodeId, text2) {
|
|
26978
27056
|
const nodes = canvas?.nodes;
|
|
@@ -27000,7 +27078,7 @@ var setPromptCommand = defineCommand100({
|
|
|
27000
27078
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
27001
27079
|
},
|
|
27002
27080
|
async run({ args }) {
|
|
27003
|
-
const filePath =
|
|
27081
|
+
const filePath = path23.resolve(String(args.file));
|
|
27004
27082
|
let canvas;
|
|
27005
27083
|
try {
|
|
27006
27084
|
canvas = JSON.parse(await readFile16(filePath, "utf8"));
|
|
@@ -27010,7 +27088,7 @@ var setPromptCommand = defineCommand100({
|
|
|
27010
27088
|
process.exit(2);
|
|
27011
27089
|
}
|
|
27012
27090
|
let text2;
|
|
27013
|
-
if (args["text-file"]) text2 = await readFile16(
|
|
27091
|
+
if (args["text-file"]) text2 = await readFile16(path23.resolve(String(args["text-file"])), "utf8");
|
|
27014
27092
|
else if (args.text !== void 0) text2 = String(args.text);
|
|
27015
27093
|
else {
|
|
27016
27094
|
process.stderr.write(
|
|
@@ -27031,14 +27109,14 @@ var setPromptCommand = defineCommand100({
|
|
|
27031
27109
|
process.exit(2);
|
|
27032
27110
|
return;
|
|
27033
27111
|
}
|
|
27034
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
27112
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path23.dirname(filePath)), defaultRegistry());
|
|
27035
27113
|
if (!validation.ok) {
|
|
27036
27114
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
27037
27115
|
`);
|
|
27038
27116
|
process.exit(2);
|
|
27039
27117
|
return;
|
|
27040
27118
|
}
|
|
27041
|
-
await
|
|
27119
|
+
await writeFile10(filePath, `${JSON.stringify(updated, null, 2)}
|
|
27042
27120
|
`, "utf8");
|
|
27043
27121
|
process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text2.length }, null, 2)}
|
|
27044
27122
|
`);
|
|
@@ -27047,7 +27125,7 @@ var setPromptCommand = defineCommand100({
|
|
|
27047
27125
|
|
|
27048
27126
|
// src/commands/canvas/validate.ts
|
|
27049
27127
|
import { readFile as readFile17 } from "fs/promises";
|
|
27050
|
-
import
|
|
27128
|
+
import path24 from "path";
|
|
27051
27129
|
import { defineCommand as defineCommand101 } from "citty";
|
|
27052
27130
|
var validateCommand = defineCommand101({
|
|
27053
27131
|
meta: {
|
|
@@ -27056,7 +27134,7 @@ var validateCommand = defineCommand101({
|
|
|
27056
27134
|
},
|
|
27057
27135
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
27058
27136
|
async run({ args }) {
|
|
27059
|
-
const filePath =
|
|
27137
|
+
const filePath = path24.resolve(String(args.file));
|
|
27060
27138
|
const raw = await readFile17(filePath, "utf8");
|
|
27061
27139
|
let parsed;
|
|
27062
27140
|
try {
|
|
@@ -27067,7 +27145,9 @@ var validateCommand = defineCommand101({
|
|
|
27067
27145
|
`);
|
|
27068
27146
|
process.exit(2);
|
|
27069
27147
|
}
|
|
27070
|
-
|
|
27148
|
+
const healed = await healAbsoluteCanvasPaths(filePath, parsed);
|
|
27149
|
+
parsed = healed.canvas;
|
|
27150
|
+
parsed = resolveRelativeCanvasPaths(parsed, path24.dirname(filePath));
|
|
27071
27151
|
let styleProjection = "not_applicable";
|
|
27072
27152
|
try {
|
|
27073
27153
|
styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
@@ -27108,6 +27188,7 @@ var validateCommand = defineCommand101({
|
|
|
27108
27188
|
cost_preview: result.perNodeCredits ?? [],
|
|
27109
27189
|
style_projection: styleProjection,
|
|
27110
27190
|
scene_projection: sceneProjection,
|
|
27191
|
+
normalized_paths: healed.rewrites,
|
|
27111
27192
|
warnings: result.warnings ?? []
|
|
27112
27193
|
},
|
|
27113
27194
|
null,
|
|
@@ -27712,14 +27793,14 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
27712
27793
|
import { defineCommand as defineCommand107 } from "citty";
|
|
27713
27794
|
|
|
27714
27795
|
// src/commands/flows/shared.ts
|
|
27715
|
-
import { existsSync as
|
|
27796
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
|
|
27716
27797
|
import { join as join3 } from "path";
|
|
27717
27798
|
var FLOWS_DIR = "src/lib/flow-engine/flows";
|
|
27718
27799
|
function flowsDir() {
|
|
27719
27800
|
let dir = process.cwd();
|
|
27720
27801
|
for (let i = 0; i < 6; i++) {
|
|
27721
27802
|
const candidate = join3(dir, FLOWS_DIR);
|
|
27722
|
-
if (
|
|
27803
|
+
if (existsSync5(candidate)) {
|
|
27723
27804
|
return candidate;
|
|
27724
27805
|
}
|
|
27725
27806
|
const parent = join3(dir, "..");
|
|
@@ -27736,18 +27817,18 @@ function failLocal(message) {
|
|
|
27736
27817
|
}
|
|
27737
27818
|
function listFlowSlugs() {
|
|
27738
27819
|
const dir = flowsDir();
|
|
27739
|
-
if (!
|
|
27820
|
+
if (!existsSync5(dir)) {
|
|
27740
27821
|
return [];
|
|
27741
27822
|
}
|
|
27742
27823
|
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
|
|
27743
27824
|
}
|
|
27744
27825
|
function readFlowTree(slug) {
|
|
27745
|
-
const
|
|
27746
|
-
if (!
|
|
27826
|
+
const path37 = join3(flowsDir(), slug, "_data.json");
|
|
27827
|
+
if (!existsSync5(path37)) {
|
|
27747
27828
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
27748
27829
|
}
|
|
27749
27830
|
try {
|
|
27750
|
-
return JSON.parse(readFileSync9(
|
|
27831
|
+
return JSON.parse(readFileSync9(path37, "utf-8"));
|
|
27751
27832
|
} catch (error) {
|
|
27752
27833
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
27753
27834
|
}
|
|
@@ -28160,10 +28241,10 @@ async function stageOps(ops) {
|
|
|
28160
28241
|
handleError(err);
|
|
28161
28242
|
}
|
|
28162
28243
|
}
|
|
28163
|
-
async function draftAction2(
|
|
28244
|
+
async function draftAction2(path37, body, chat) {
|
|
28164
28245
|
const chatId = resolveChatId(chat);
|
|
28165
28246
|
try {
|
|
28166
|
-
const data = await apiPost(
|
|
28247
|
+
const data = await apiPost(path37, { chatId, ...body });
|
|
28167
28248
|
writeJsonEnvelope({ ok: true, data });
|
|
28168
28249
|
return data;
|
|
28169
28250
|
} catch (err) {
|
|
@@ -28409,7 +28490,7 @@ Examples:
|
|
|
28409
28490
|
});
|
|
28410
28491
|
|
|
28411
28492
|
// src/commands/ga4/query.ts
|
|
28412
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
28493
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
|
|
28413
28494
|
import { resolve as resolve2 } from "path";
|
|
28414
28495
|
import { defineCommand as defineCommand112 } from "citty";
|
|
28415
28496
|
|
|
@@ -28483,7 +28564,7 @@ function writeRowsToFile2(filePath, rows, append) {
|
|
|
28483
28564
|
const fields = extractFields2(rows);
|
|
28484
28565
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
28485
28566
|
if (ext === "csv") {
|
|
28486
|
-
if (!append || !
|
|
28567
|
+
if (!append || !existsSync6(filePath)) {
|
|
28487
28568
|
writeFileSync3(filePath, `${toCsvRow(fields)}
|
|
28488
28569
|
`, "utf-8");
|
|
28489
28570
|
}
|
|
@@ -28494,12 +28575,12 @@ function writeRowsToFile2(filePath, rows, append) {
|
|
|
28494
28575
|
const lines = rows.map((row) => JSON.stringify(row));
|
|
28495
28576
|
const content = `${lines.join("\n")}
|
|
28496
28577
|
`;
|
|
28497
|
-
if (append &&
|
|
28578
|
+
if (append && existsSync6(filePath)) {
|
|
28498
28579
|
appendFileSync2(filePath, content, "utf-8");
|
|
28499
28580
|
} else {
|
|
28500
28581
|
writeFileSync3(filePath, content, "utf-8");
|
|
28501
28582
|
}
|
|
28502
|
-
} else if (append &&
|
|
28583
|
+
} else if (append && existsSync6(filePath)) {
|
|
28503
28584
|
const existing = JSON.parse(readFileSync11(filePath, "utf-8"));
|
|
28504
28585
|
writeFileSync3(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
|
|
28505
28586
|
} else {
|
|
@@ -29023,7 +29104,7 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
|
|
|
29023
29104
|
import { defineCommand as defineCommand118 } from "citty";
|
|
29024
29105
|
|
|
29025
29106
|
// src/commands/gsc/query.ts
|
|
29026
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
29107
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
|
|
29027
29108
|
import { resolve as resolve3 } from "path";
|
|
29028
29109
|
import { defineCommand as defineCommand115 } from "citty";
|
|
29029
29110
|
|
|
@@ -29155,7 +29236,7 @@ function writeRowsToFile3(filePath, rows, append) {
|
|
|
29155
29236
|
const fields = extractFields3(rows);
|
|
29156
29237
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
29157
29238
|
if (ext === "csv") {
|
|
29158
|
-
if (!append || !
|
|
29239
|
+
if (!append || !existsSync7(filePath)) {
|
|
29159
29240
|
writeFileSync4(filePath, `${toCsvRow(fields)}
|
|
29160
29241
|
`, "utf-8");
|
|
29161
29242
|
}
|
|
@@ -29165,12 +29246,12 @@ function writeRowsToFile3(filePath, rows, append) {
|
|
|
29165
29246
|
} else if (ext === "jsonl") {
|
|
29166
29247
|
const content = `${rows.map((row) => JSON.stringify(row)).join("\n")}
|
|
29167
29248
|
`;
|
|
29168
|
-
if (append &&
|
|
29249
|
+
if (append && existsSync7(filePath)) {
|
|
29169
29250
|
appendFileSync3(filePath, content, "utf-8");
|
|
29170
29251
|
} else {
|
|
29171
29252
|
writeFileSync4(filePath, content, "utf-8");
|
|
29172
29253
|
}
|
|
29173
|
-
} else if (append &&
|
|
29254
|
+
} else if (append && existsSync7(filePath)) {
|
|
29174
29255
|
const existing = JSON.parse(readFileSync12(filePath, "utf-8"));
|
|
29175
29256
|
writeFileSync4(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
|
|
29176
29257
|
} else {
|
|
@@ -30149,7 +30230,7 @@ function cropSprite(input, region) {
|
|
|
30149
30230
|
|
|
30150
30231
|
// src/lib/image/io.ts
|
|
30151
30232
|
import { randomBytes } from "crypto";
|
|
30152
|
-
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";
|
|
30153
30234
|
import { dirname as dirname2, extname as extname2, join as join4, resolve as resolve4 } from "path";
|
|
30154
30235
|
var REMOTE_RE = /^https?:\/\//i;
|
|
30155
30236
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -30184,9 +30265,9 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
30184
30265
|
}
|
|
30185
30266
|
return readFile19(pathOrUrl);
|
|
30186
30267
|
}
|
|
30187
|
-
async function isDirectory(
|
|
30268
|
+
async function isDirectory(path37) {
|
|
30188
30269
|
try {
|
|
30189
|
-
const s = await stat4(
|
|
30270
|
+
const s = await stat4(path37);
|
|
30190
30271
|
return s.isDirectory();
|
|
30191
30272
|
} catch {
|
|
30192
30273
|
return false;
|
|
@@ -30205,7 +30286,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
30205
30286
|
const absolute = resolve4(targetPath);
|
|
30206
30287
|
const dir = dirname2(absolute);
|
|
30207
30288
|
const tmp = join4(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
30208
|
-
await
|
|
30289
|
+
await writeFile11(tmp, data);
|
|
30209
30290
|
await rename(tmp, absolute);
|
|
30210
30291
|
}
|
|
30211
30292
|
|
|
@@ -30470,13 +30551,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
30470
30551
|
}
|
|
30471
30552
|
function disambiguate(paths) {
|
|
30472
30553
|
const taken = /* @__PURE__ */ new Set();
|
|
30473
|
-
return paths.map((
|
|
30474
|
-
if (!taken.has(
|
|
30475
|
-
taken.add(
|
|
30476
|
-
return
|
|
30554
|
+
return paths.map((path37) => {
|
|
30555
|
+
if (!taken.has(path37)) {
|
|
30556
|
+
taken.add(path37);
|
|
30557
|
+
return path37;
|
|
30477
30558
|
}
|
|
30478
|
-
const ext = extname3(
|
|
30479
|
-
const stem =
|
|
30559
|
+
const ext = extname3(path37);
|
|
30560
|
+
const stem = path37.slice(0, path37.length - ext.length);
|
|
30480
30561
|
let n = 2;
|
|
30481
30562
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
30482
30563
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -30603,10 +30684,10 @@ async function runDownloads(plan) {
|
|
|
30603
30684
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
30604
30685
|
const downloaded = [];
|
|
30605
30686
|
for (const [index, item] of fetched.entries()) {
|
|
30606
|
-
const
|
|
30687
|
+
const path37 = paths[index] ?? item.path;
|
|
30607
30688
|
try {
|
|
30608
|
-
await atomicWrite(
|
|
30609
|
-
downloaded.push({ input: item.input, output:
|
|
30689
|
+
await atomicWrite(path37, item.buffer);
|
|
30690
|
+
downloaded.push({ input: item.input, output: path37, bytes: item.buffer.length, contentType: item.contentType });
|
|
30610
30691
|
} catch (err) {
|
|
30611
30692
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
30612
30693
|
}
|
|
@@ -33546,12 +33627,12 @@ import { defineCommand as defineCommand157 } from "citty";
|
|
|
33546
33627
|
|
|
33547
33628
|
// src/commands/landing/critique.ts
|
|
33548
33629
|
import { readdir as readdir8, stat as stat6 } from "fs/promises";
|
|
33549
|
-
import
|
|
33630
|
+
import path29 from "path";
|
|
33550
33631
|
import { defineCommand as defineCommand147 } from "citty";
|
|
33551
33632
|
|
|
33552
33633
|
// src/engine/landing/lib/brand-tokens.ts
|
|
33553
33634
|
import { readFile as readFile20 } from "fs/promises";
|
|
33554
|
-
import
|
|
33635
|
+
import path25 from "path";
|
|
33555
33636
|
|
|
33556
33637
|
// src/engine/landing/lib/color.ts
|
|
33557
33638
|
var NEUTRAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
@@ -33728,7 +33809,7 @@ function brandTokensFromCanonical(tokens) {
|
|
|
33728
33809
|
return { fonts, colors, hasTokens: fonts.size > 0 || colors.length > 0 };
|
|
33729
33810
|
}
|
|
33730
33811
|
async function loadBrandTokens(projectRoot) {
|
|
33731
|
-
const tokensRaw = await safeRead(
|
|
33812
|
+
const tokensRaw = await safeRead(path25.join(projectRoot, "src", "brand", "tokens.json"));
|
|
33732
33813
|
if (tokensRaw) {
|
|
33733
33814
|
try {
|
|
33734
33815
|
const canonical2 = brandTokensSchema.safeParse(JSON.parse(tokensRaw));
|
|
@@ -33736,8 +33817,8 @@ async function loadBrandTokens(projectRoot) {
|
|
|
33736
33817
|
} catch {
|
|
33737
33818
|
}
|
|
33738
33819
|
}
|
|
33739
|
-
const globalCss = await safeRead(
|
|
33740
|
-
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"));
|
|
33741
33822
|
if (!globalCss && !brandMd) return EMPTY;
|
|
33742
33823
|
return parseBrandTokens(globalCss, brandMd);
|
|
33743
33824
|
}
|
|
@@ -34798,13 +34879,13 @@ function describeCounts(findings) {
|
|
|
34798
34879
|
}
|
|
34799
34880
|
|
|
34800
34881
|
// src/engine/landing/lib/referenceStore.ts
|
|
34801
|
-
import { mkdir as mkdir8, readFile as readFile21, writeFile as
|
|
34802
|
-
import
|
|
34882
|
+
import { mkdir as mkdir8, readFile as readFile21, writeFile as writeFile12 } from "fs/promises";
|
|
34883
|
+
import path26 from "path";
|
|
34803
34884
|
var REFERENCES_FILE = ".cache/inspiration-refs.json";
|
|
34804
34885
|
var REFERENCE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
34805
34886
|
async function readReferences(projectRoot) {
|
|
34806
34887
|
try {
|
|
34807
|
-
const raw = await readFile21(
|
|
34888
|
+
const raw = await readFile21(path26.join(projectRoot, REFERENCES_FILE), "utf8");
|
|
34808
34889
|
const parsed = JSON.parse(raw);
|
|
34809
34890
|
if (!Array.isArray(parsed)) return [];
|
|
34810
34891
|
const cutoff = Date.now() - REFERENCE_TTL_MS;
|
|
@@ -34828,9 +34909,9 @@ async function recordReferences(projectRoot, references) {
|
|
|
34828
34909
|
const existing = await readReferences(projectRoot);
|
|
34829
34910
|
const replaced = new Set(references.map((reference) => reference.sectionId));
|
|
34830
34911
|
const merged = [...existing.filter((entry) => !replaced.has(entry.sectionId)), ...references];
|
|
34831
|
-
const file =
|
|
34832
|
-
await mkdir8(
|
|
34833
|
-
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)}
|
|
34834
34915
|
`);
|
|
34835
34916
|
return true;
|
|
34836
34917
|
} catch {
|
|
@@ -34839,40 +34920,40 @@ async function recordReferences(projectRoot, references) {
|
|
|
34839
34920
|
}
|
|
34840
34921
|
|
|
34841
34922
|
// src/commands/landing/snapshot.ts
|
|
34842
|
-
import { mkdir as mkdir9, rename as rename2, writeFile as
|
|
34843
|
-
import
|
|
34923
|
+
import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
|
|
34924
|
+
import path27 from "path";
|
|
34844
34925
|
var CRITIC_VERSION = "2";
|
|
34845
34926
|
function critiqueCacheDir(projectRoot) {
|
|
34846
|
-
return
|
|
34927
|
+
return path27.join(projectRoot, ".cache", "landing-critique");
|
|
34847
34928
|
}
|
|
34848
34929
|
function snapshotPath(projectRoot, slug) {
|
|
34849
|
-
return
|
|
34930
|
+
return path27.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
34850
34931
|
}
|
|
34851
34932
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
34852
34933
|
await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
|
|
34853
34934
|
const dest = snapshotPath(projectRoot, snapshot.slug);
|
|
34854
34935
|
const tmp = `${dest}.tmp`;
|
|
34855
|
-
await
|
|
34936
|
+
await writeFile13(tmp, `${JSON.stringify(snapshot, null, 2)}
|
|
34856
34937
|
`, "utf8");
|
|
34857
34938
|
await rename2(tmp, dest);
|
|
34858
34939
|
}
|
|
34859
34940
|
|
|
34860
34941
|
// src/commands/landing/source-version.ts
|
|
34861
34942
|
import { readdir as readdir7, readFile as readFile22, stat as stat5 } from "fs/promises";
|
|
34862
|
-
import
|
|
34943
|
+
import path28 from "path";
|
|
34863
34944
|
async function landingSourceRelPaths(landingDir) {
|
|
34864
34945
|
const rel = [];
|
|
34865
|
-
if (await isFile(
|
|
34866
|
-
const componentsDir =
|
|
34946
|
+
if (await isFile(path28.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
34947
|
+
const componentsDir = path28.join(landingDir, "_components");
|
|
34867
34948
|
for (const abs of await walkAstro(componentsDir)) {
|
|
34868
|
-
rel.push(
|
|
34949
|
+
rel.push(path28.relative(landingDir, abs).split(path28.sep).join("/"));
|
|
34869
34950
|
}
|
|
34870
34951
|
return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
34871
34952
|
}
|
|
34872
34953
|
async function readLandingSources(landingDir) {
|
|
34873
34954
|
const rel = await landingSourceRelPaths(landingDir);
|
|
34874
34955
|
const out = [];
|
|
34875
|
-
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") });
|
|
34876
34957
|
return out;
|
|
34877
34958
|
}
|
|
34878
34959
|
async function computeLandingSourceSha(landingDir) {
|
|
@@ -34881,7 +34962,7 @@ async function computeLandingSourceSha(landingDir) {
|
|
|
34881
34962
|
for (const r of rel) {
|
|
34882
34963
|
let bytes;
|
|
34883
34964
|
try {
|
|
34884
|
-
bytes = await readFile22(
|
|
34965
|
+
bytes = await readFile22(path28.join(landingDir, r));
|
|
34885
34966
|
} catch {
|
|
34886
34967
|
bytes = Buffer.alloc(0);
|
|
34887
34968
|
}
|
|
@@ -34905,7 +34986,7 @@ async function walkAstro(dir) {
|
|
|
34905
34986
|
}
|
|
34906
34987
|
const out = [];
|
|
34907
34988
|
for (const entry of entries) {
|
|
34908
|
-
const abs =
|
|
34989
|
+
const abs = path28.join(dir, entry.name);
|
|
34909
34990
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
34910
34991
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
34911
34992
|
}
|
|
@@ -34966,7 +35047,7 @@ var critiqueCommand2 = defineCommand147({
|
|
|
34966
35047
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
34967
35048
|
);
|
|
34968
35049
|
}
|
|
34969
|
-
if (!await isDir(
|
|
35050
|
+
if (!await isDir(path29.resolve(projectRoot, "src", "pages", slug))) {
|
|
34970
35051
|
fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
34971
35052
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
34972
35053
|
});
|
|
@@ -35005,7 +35086,7 @@ var critiqueCommand2 = defineCommand147({
|
|
|
35005
35086
|
}
|
|
35006
35087
|
});
|
|
35007
35088
|
async function critiqueOne(projectRoot, slug, brand, references) {
|
|
35008
|
-
const landingDir =
|
|
35089
|
+
const landingDir = path29.resolve(projectRoot, "src", "pages", slug);
|
|
35009
35090
|
const [sources, sourceSha] = await Promise.all([readLandingSources(landingDir), computeLandingSourceSha(landingDir)]);
|
|
35010
35091
|
const report = critiqueLanding({ slug, sources, brand, references });
|
|
35011
35092
|
let snapshotFailed = false;
|
|
@@ -35025,7 +35106,7 @@ async function critiqueOne(projectRoot, slug, brand, references) {
|
|
|
35025
35106
|
}
|
|
35026
35107
|
async function listLandingSlugs(projectRoot) {
|
|
35027
35108
|
try {
|
|
35028
|
-
const entries = await readdir8(
|
|
35109
|
+
const entries = await readdir8(path29.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
35029
35110
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
35030
35111
|
} catch {
|
|
35031
35112
|
return [];
|
|
@@ -35176,8 +35257,8 @@ var addCommand = defineCommand148({
|
|
|
35176
35257
|
});
|
|
35177
35258
|
|
|
35178
35259
|
// src/commands/landing/inspiration/code.ts
|
|
35179
|
-
import { mkdir as mkdir10, writeFile as
|
|
35180
|
-
import
|
|
35260
|
+
import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
|
|
35261
|
+
import path30 from "path";
|
|
35181
35262
|
import { defineCommand as defineCommand149 } from "citty";
|
|
35182
35263
|
registerSchema({
|
|
35183
35264
|
command: "landing.inspiration.code",
|
|
@@ -35200,10 +35281,10 @@ var codeCommand = defineCommand149({
|
|
|
35200
35281
|
try {
|
|
35201
35282
|
const id = args.id;
|
|
35202
35283
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
35203
|
-
const dir =
|
|
35284
|
+
const dir = path30.join(process.cwd(), ".baker", "inspiration", id);
|
|
35204
35285
|
await mkdir10(dir, { recursive: true });
|
|
35205
|
-
const file =
|
|
35206
|
-
await
|
|
35286
|
+
const file = path30.join(dir, "section.html");
|
|
35287
|
+
await writeFile14(file, data.html);
|
|
35207
35288
|
const recorded = await recordReference(process.cwd(), {
|
|
35208
35289
|
sectionId: id,
|
|
35209
35290
|
sourceUrl: data.sourceUrl,
|
|
@@ -35223,7 +35304,7 @@ var codeCommand = defineCommand149({
|
|
|
35223
35304
|
ok: true,
|
|
35224
35305
|
data: {
|
|
35225
35306
|
id,
|
|
35226
|
-
file:
|
|
35307
|
+
file: path30.relative(process.cwd(), file),
|
|
35227
35308
|
bytes: data.html.length,
|
|
35228
35309
|
fidelity: data.fidelity,
|
|
35229
35310
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -35479,7 +35560,7 @@ var pageCommand = defineCommand151({
|
|
|
35479
35560
|
|
|
35480
35561
|
// src/commands/landing/inspiration/scrape.ts
|
|
35481
35562
|
import { readFile as readFile23 } from "fs/promises";
|
|
35482
|
-
import
|
|
35563
|
+
import path33 from "path";
|
|
35483
35564
|
import { defineCommand as defineCommand152 } from "citty";
|
|
35484
35565
|
|
|
35485
35566
|
// src/engine/landing/lib/capturedReferences.ts
|
|
@@ -35671,8 +35752,8 @@ function classifyCaptureFailure(error) {
|
|
|
35671
35752
|
}
|
|
35672
35753
|
|
|
35673
35754
|
// src/engine/landing-library/run.ts
|
|
35674
|
-
import { mkdir as mkdir11, writeFile as
|
|
35675
|
-
import
|
|
35755
|
+
import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
|
|
35756
|
+
import path32 from "path";
|
|
35676
35757
|
|
|
35677
35758
|
// ../proxy/src/preflight.ts
|
|
35678
35759
|
import http from "http";
|
|
@@ -37087,11 +37168,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
37087
37168
|
}
|
|
37088
37169
|
|
|
37089
37170
|
// src/engine/landing-library/report.ts
|
|
37090
|
-
import { writeFile as
|
|
37091
|
-
import
|
|
37171
|
+
import { writeFile as writeFile15 } from "fs/promises";
|
|
37172
|
+
import path31 from "path";
|
|
37092
37173
|
async function writeCaptureReport(manifest, outDir) {
|
|
37093
|
-
const file =
|
|
37094
|
-
await
|
|
37174
|
+
const file = path31.join(outDir, "report.html");
|
|
37175
|
+
await writeFile15(file, renderReport(manifest));
|
|
37095
37176
|
return file;
|
|
37096
37177
|
}
|
|
37097
37178
|
function escapeHtml3(value) {
|
|
@@ -37269,32 +37350,32 @@ async function reproducePage(args) {
|
|
|
37269
37350
|
const { browser, page, outDir, pageUrl, livePageShot } = args;
|
|
37270
37351
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
37271
37352
|
if (!built) return { bundle: null, fidelity: null };
|
|
37272
|
-
await
|
|
37353
|
+
await writeFile16(path32.join(outDir, "page.html"), built.html);
|
|
37273
37354
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
37274
37355
|
wholePage: true,
|
|
37275
37356
|
timeoutMs: 6e4
|
|
37276
37357
|
});
|
|
37277
37358
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
37278
|
-
await
|
|
37359
|
+
await writeFile16(path32.join(outDir, "page-rendered.png"), rendered);
|
|
37279
37360
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
37280
37361
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
37281
37362
|
}
|
|
37282
37363
|
async function captureOneSection(args) {
|
|
37283
37364
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
|
|
37284
|
-
const dir =
|
|
37365
|
+
const dir = path32.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
37285
37366
|
await mkdir11(dir, { recursive: true });
|
|
37286
37367
|
const desktop = await captureSection(page, candidate);
|
|
37287
|
-
if (desktop) await
|
|
37368
|
+
if (desktop) await writeFile16(path32.join(dir, "desktop.png"), desktop);
|
|
37288
37369
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
37289
37370
|
const motion = await collectMotion(page, candidate.selector);
|
|
37290
37371
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
37291
37372
|
let fidelity = null;
|
|
37292
37373
|
let fidelityNote;
|
|
37293
37374
|
if (built) {
|
|
37294
|
-
await
|
|
37375
|
+
await writeFile16(path32.join(dir, "section.html"), built.html);
|
|
37295
37376
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
37296
37377
|
if (rendered && desktop) {
|
|
37297
|
-
await
|
|
37378
|
+
await writeFile16(path32.join(dir, "section-rendered.png"), rendered);
|
|
37298
37379
|
const result = await scoreFidelity(desktop, rendered);
|
|
37299
37380
|
fidelity = result.score;
|
|
37300
37381
|
fidelityNote = result.note;
|
|
@@ -37302,9 +37383,9 @@ async function captureOneSection(args) {
|
|
|
37302
37383
|
}
|
|
37303
37384
|
return {
|
|
37304
37385
|
...candidate,
|
|
37305
|
-
desktopShot: desktop ?
|
|
37386
|
+
desktopShot: desktop ? path32.relative(outDir, path32.join(dir, "desktop.png")) : null,
|
|
37306
37387
|
mobileShot: null,
|
|
37307
|
-
bundle: built ?
|
|
37388
|
+
bundle: built ? path32.relative(outDir, path32.join(dir, "section.html")) : null,
|
|
37308
37389
|
fidelity,
|
|
37309
37390
|
...fidelityNote ? { fidelityNote } : {},
|
|
37310
37391
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -37323,9 +37404,9 @@ async function captureMobileShots(args) {
|
|
|
37323
37404
|
for (const section of sections) {
|
|
37324
37405
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
37325
37406
|
if (!shot) continue;
|
|
37326
|
-
const file =
|
|
37327
|
-
await
|
|
37328
|
-
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);
|
|
37329
37410
|
}
|
|
37330
37411
|
} finally {
|
|
37331
37412
|
await mobile.context.close();
|
|
@@ -37340,10 +37421,10 @@ async function captureMotionTakes(args) {
|
|
|
37340
37421
|
const filmOne = async (section) => {
|
|
37341
37422
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
37342
37423
|
if (!take) return;
|
|
37343
|
-
const dir =
|
|
37344
|
-
const file =
|
|
37345
|
-
await
|
|
37346
|
-
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);
|
|
37347
37428
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
37348
37429
|
};
|
|
37349
37430
|
const queue = [...moving];
|
|
@@ -37400,7 +37481,7 @@ async function captureAlternateViews(args) {
|
|
|
37400
37481
|
async function reproduceWholePage(args) {
|
|
37401
37482
|
const { browser, page, outDir, pageUrl, withCode, log } = args;
|
|
37402
37483
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
37403
|
-
if (fullPage) await
|
|
37484
|
+
if (fullPage) await writeFile16(path32.join(outDir, "full-page.png"), fullPage);
|
|
37404
37485
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
37405
37486
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
37406
37487
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -37471,7 +37552,7 @@ async function openViaLadder(args) {
|
|
|
37471
37552
|
async function scrapeLanding(options) {
|
|
37472
37553
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
37473
37554
|
const log = options.onProgress ?? (() => void 0);
|
|
37474
|
-
const sectionsDir =
|
|
37555
|
+
const sectionsDir = path32.join(options.outDir, "sections");
|
|
37475
37556
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
37476
37557
|
if (nonPublic) {
|
|
37477
37558
|
throw new BlockedPageError({
|
|
@@ -37533,7 +37614,7 @@ async function scrapeLanding(options) {
|
|
|
37533
37614
|
security: prepared.security,
|
|
37534
37615
|
captureTier: tier
|
|
37535
37616
|
};
|
|
37536
|
-
await
|
|
37617
|
+
await writeFile16(path32.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
37537
37618
|
`);
|
|
37538
37619
|
if (options.report !== false) {
|
|
37539
37620
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -37560,7 +37641,7 @@ async function recordCapture(manifest, outDir) {
|
|
|
37560
37641
|
for (const section of manifest.sections) {
|
|
37561
37642
|
if (!section.bundle) continue;
|
|
37562
37643
|
try {
|
|
37563
|
-
const markup = await readFile23(
|
|
37644
|
+
const markup = await readFile23(path33.join(outDir, section.bundle), "utf8");
|
|
37564
37645
|
const copyStrings = capturedCopyStrings(markup);
|
|
37565
37646
|
if (copyStrings.length === 0) continue;
|
|
37566
37647
|
references.push({
|
|
@@ -37722,9 +37803,46 @@ var scrapeCommand = defineCommand152({
|
|
|
37722
37803
|
});
|
|
37723
37804
|
|
|
37724
37805
|
// src/commands/landing/inspiration/search.ts
|
|
37725
|
-
import
|
|
37726
|
-
import path33 from "path";
|
|
37806
|
+
import path35 from "path";
|
|
37727
37807
|
import { defineCommand as defineCommand153 } from "citty";
|
|
37808
|
+
|
|
37809
|
+
// src/commands/landing/inspiration/shot.ts
|
|
37810
|
+
import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
|
|
37811
|
+
import path34 from "path";
|
|
37812
|
+
import sharp6 from "sharp";
|
|
37813
|
+
var READABLE_SHOT = {
|
|
37814
|
+
maxWidth: 1440,
|
|
37815
|
+
maxHeight: 4e3,
|
|
37816
|
+
quality: 82,
|
|
37817
|
+
extension: "webp"
|
|
37818
|
+
};
|
|
37819
|
+
async function toReadableShot(original) {
|
|
37820
|
+
try {
|
|
37821
|
+
return await sharp6(original).resize({
|
|
37822
|
+
width: READABLE_SHOT.maxWidth,
|
|
37823
|
+
height: READABLE_SHOT.maxHeight,
|
|
37824
|
+
fit: "inside",
|
|
37825
|
+
withoutEnlargement: true
|
|
37826
|
+
}).webp({ quality: READABLE_SHOT.quality }).toBuffer();
|
|
37827
|
+
} catch {
|
|
37828
|
+
return original;
|
|
37829
|
+
}
|
|
37830
|
+
}
|
|
37831
|
+
async function downloadReadableShot(url, file) {
|
|
37832
|
+
if (!url) return null;
|
|
37833
|
+
try {
|
|
37834
|
+
const response = await fetch(url);
|
|
37835
|
+
if (!response.ok) return null;
|
|
37836
|
+
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
37837
|
+
await mkdir12(path34.dirname(file), { recursive: true });
|
|
37838
|
+
await writeFile17(file, shot);
|
|
37839
|
+
return path34.relative(process.cwd(), file);
|
|
37840
|
+
} catch {
|
|
37841
|
+
return null;
|
|
37842
|
+
}
|
|
37843
|
+
}
|
|
37844
|
+
|
|
37845
|
+
// src/commands/landing/inspiration/search.ts
|
|
37728
37846
|
registerSchema({
|
|
37729
37847
|
command: "landing.inspiration.search",
|
|
37730
37848
|
description: "Search the shared library of real landing-page sections for reference before you design. Start here: baker landing inspiration search 'pricing with a monthly/annual toggle'. Returns the idea behind each section plus a downloaded screenshot you can Read. Defaults to this company's saved sections; pass --scope all for the whole library.",
|
|
@@ -37810,20 +37928,15 @@ function buildSearchBody(args) {
|
|
|
37810
37928
|
return body;
|
|
37811
37929
|
}
|
|
37812
37930
|
async function downloadShots(results) {
|
|
37813
|
-
const dir =
|
|
37814
|
-
await mkdir12(dir, { recursive: true });
|
|
37931
|
+
const dir = path35.join(process.cwd(), ".baker", "inspiration");
|
|
37815
37932
|
const saved = /* @__PURE__ */ new Map();
|
|
37816
37933
|
await Promise.all(
|
|
37817
37934
|
results.map(async (result) => {
|
|
37818
|
-
|
|
37819
|
-
|
|
37820
|
-
|
|
37821
|
-
|
|
37822
|
-
|
|
37823
|
-
await writeFile16(file, Buffer.from(await response.arrayBuffer()));
|
|
37824
|
-
saved.set(result.id, path33.relative(process.cwd(), file));
|
|
37825
|
-
} catch {
|
|
37826
|
-
}
|
|
37935
|
+
const file = await downloadReadableShot(
|
|
37936
|
+
result.desktopShotUrl,
|
|
37937
|
+
path35.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
37938
|
+
);
|
|
37939
|
+
if (file) saved.set(result.id, file);
|
|
37827
37940
|
})
|
|
37828
37941
|
);
|
|
37829
37942
|
return saved;
|
|
@@ -38049,8 +38162,7 @@ var sequencesCommand = defineCommand154({
|
|
|
38049
38162
|
});
|
|
38050
38163
|
|
|
38051
38164
|
// src/commands/landing/inspiration/view.ts
|
|
38052
|
-
import
|
|
38053
|
-
import path34 from "path";
|
|
38165
|
+
import path36 from "path";
|
|
38054
38166
|
import { defineCommand as defineCommand155 } from "citty";
|
|
38055
38167
|
registerSchema({
|
|
38056
38168
|
command: "landing.inspiration.view",
|
|
@@ -38064,18 +38176,6 @@ registerSchema({
|
|
|
38064
38176
|
}
|
|
38065
38177
|
}
|
|
38066
38178
|
});
|
|
38067
|
-
async function download(url, file) {
|
|
38068
|
-
if (!url) return null;
|
|
38069
|
-
try {
|
|
38070
|
-
const response = await fetch(url);
|
|
38071
|
-
if (!response.ok) return null;
|
|
38072
|
-
await mkdir13(path34.dirname(file), { recursive: true });
|
|
38073
|
-
await writeFile17(file, Buffer.from(await response.arrayBuffer()));
|
|
38074
|
-
return path34.relative(process.cwd(), file);
|
|
38075
|
-
} catch {
|
|
38076
|
-
return null;
|
|
38077
|
-
}
|
|
38078
|
-
}
|
|
38079
38179
|
var viewCommand2 = defineCommand155({
|
|
38080
38180
|
meta: {
|
|
38081
38181
|
name: "view",
|
|
@@ -38095,11 +38195,12 @@ var viewCommand2 = defineCommand155({
|
|
|
38095
38195
|
const id = args.id;
|
|
38096
38196
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
38097
38197
|
const section = data.section;
|
|
38098
|
-
const dir =
|
|
38198
|
+
const dir = path36.join(process.cwd(), ".baker", "inspiration", id);
|
|
38199
|
+
const ext = READABLE_SHOT.extension;
|
|
38099
38200
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
38100
|
-
|
|
38101
|
-
|
|
38102
|
-
|
|
38201
|
+
downloadReadableShot(section.desktopShotUrl, path36.join(dir, `desktop.${ext}`)),
|
|
38202
|
+
downloadReadableShot(section.mobileShotUrl, path36.join(dir, `mobile.${ext}`)),
|
|
38203
|
+
downloadReadableShot(section.motionFilmstripUrl, path36.join(dir, `motion-filmstrip.${ext}`))
|
|
38103
38204
|
]);
|
|
38104
38205
|
const full = args.full;
|
|
38105
38206
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -38109,7 +38210,7 @@ var viewCommand2 = defineCommand155({
|
|
|
38109
38210
|
if (fidelity) hints.push(fidelity);
|
|
38110
38211
|
if (filmstrip) {
|
|
38111
38212
|
hints.push(
|
|
38112
|
-
|
|
38213
|
+
`${filmstrip} shows this section animating in, six frames left-to-right then top-to-bottom, with the elapsed time on each. Read it if you want the motion, not just the layout.`
|
|
38113
38214
|
);
|
|
38114
38215
|
}
|
|
38115
38216
|
writeJson({
|
|
@@ -40317,7 +40418,7 @@ function parseImageRefs(spec) {
|
|
|
40317
40418
|
}
|
|
40318
40419
|
var defaultDeps = {
|
|
40319
40420
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
40320
|
-
upload: (
|
|
40421
|
+
upload: (path37) => uploadLocalImage({ file: path37, contentType: detectImageContentType(path37), source: "uploaded" })
|
|
40321
40422
|
};
|
|
40322
40423
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
40323
40424
|
const refs = parseImageRefs(spec);
|
|
@@ -40337,10 +40438,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
40337
40438
|
}
|
|
40338
40439
|
return { imageIds, added };
|
|
40339
40440
|
}
|
|
40340
|
-
function uploadFailure(
|
|
40441
|
+
function uploadFailure(path37) {
|
|
40341
40442
|
return (error) => {
|
|
40342
40443
|
if (error instanceof ApiError) throw error;
|
|
40343
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
40444
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path37}" as an image.`);
|
|
40344
40445
|
};
|
|
40345
40446
|
}
|
|
40346
40447
|
|
|
@@ -41390,10 +41491,10 @@ async function stageOp4(op) {
|
|
|
41390
41491
|
handleError4(err);
|
|
41391
41492
|
}
|
|
41392
41493
|
}
|
|
41393
|
-
async function draftAction3(
|
|
41494
|
+
async function draftAction3(path37, body, chat) {
|
|
41394
41495
|
const chatId = resolveChatId(chat);
|
|
41395
41496
|
try {
|
|
41396
|
-
const data = await apiPost(
|
|
41497
|
+
const data = await apiPost(path37, { chatId, ...body });
|
|
41397
41498
|
writeJsonEnvelope({ ok: true, data });
|
|
41398
41499
|
return data;
|
|
41399
41500
|
} catch (err) {
|
|
@@ -42309,7 +42410,7 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
42309
42410
|
});
|
|
42310
42411
|
|
|
42311
42412
|
// src/commands/videos/index.ts
|
|
42312
|
-
import { defineCommand as
|
|
42413
|
+
import { defineCommand as defineCommand202 } from "citty";
|
|
42313
42414
|
|
|
42314
42415
|
// src/commands/videos/delete.ts
|
|
42315
42416
|
import { defineCommand as defineCommand197 } from "citty";
|
|
@@ -42440,245 +42541,8 @@ var groupCommand2 = defineCommand199({
|
|
|
42440
42541
|
}
|
|
42441
42542
|
});
|
|
42442
42543
|
|
|
42443
|
-
// src/commands/videos/ingest.ts
|
|
42444
|
-
import { mkdtemp as mkdtemp2, readFile as readFile24, rm as rm7, stat as stat7 } from "fs/promises";
|
|
42445
|
-
import { tmpdir as tmpdir3 } from "os";
|
|
42446
|
-
import path35 from "path";
|
|
42447
|
-
import { defineCommand as defineCommand200 } from "citty";
|
|
42448
|
-
|
|
42449
|
-
// src/commands/videos/ingestUrl.ts
|
|
42450
|
-
var MAX_VIDEO_INGEST_BYTES = 2 * 1024 * 1024 * 1024;
|
|
42451
|
-
var INGEST_MAX_HEIGHT = 1080;
|
|
42452
|
-
var DIRECT_MEDIA_EXTENSIONS = [".mp4", ".mov", ".webm", ".m4v", ".mkv", ".avi"];
|
|
42453
|
-
var YOUTUBE_HOSTS = ["youtube.com", "youtu.be", "m.youtube.com", "www.youtube.com"];
|
|
42454
|
-
function isDirectMediaUrl(rawUrl) {
|
|
42455
|
-
const pathname = safePathname(rawUrl);
|
|
42456
|
-
return DIRECT_MEDIA_EXTENSIONS.some((ext) => pathname.endsWith(ext));
|
|
42457
|
-
}
|
|
42458
|
-
function resolveIngestSource(explicit, rawUrl) {
|
|
42459
|
-
if (explicit) return explicit;
|
|
42460
|
-
return isYouTubeUrl(rawUrl) ? "youtube" : "url";
|
|
42461
|
-
}
|
|
42462
|
-
function isYouTubeUrl(rawUrl) {
|
|
42463
|
-
let host;
|
|
42464
|
-
try {
|
|
42465
|
-
host = new URL(rawUrl).hostname.toLowerCase();
|
|
42466
|
-
} catch {
|
|
42467
|
-
return false;
|
|
42468
|
-
}
|
|
42469
|
-
return YOUTUBE_HOSTS.includes(host);
|
|
42470
|
-
}
|
|
42471
|
-
var TOOLING_OR_BLOCK_SIGNATURES = [
|
|
42472
|
-
"requested format is not available",
|
|
42473
|
-
"only images are available",
|
|
42474
|
-
"nsig",
|
|
42475
|
-
"sign in to confirm",
|
|
42476
|
-
// bot check — distinct from "Private video. Sign in if…"
|
|
42477
|
-
"unable to download api page",
|
|
42478
|
-
"http error 403"
|
|
42479
|
-
];
|
|
42480
|
-
function isToolingOrBlockFailure(detail) {
|
|
42481
|
-
const haystack = detail.toLowerCase();
|
|
42482
|
-
return TOOLING_OR_BLOCK_SIGNATURES.some((signature) => haystack.includes(signature));
|
|
42483
|
-
}
|
|
42484
|
-
var NOT_ABOUT_THE_URL = ["UNAUTHORIZED", "FORBIDDEN", "RATE_LIMITED", "INTERNAL", "VALIDATION_ERROR", "NOT_FOUND"];
|
|
42485
|
-
function worthDownloadingInstead(errorCode) {
|
|
42486
|
-
return !NOT_ABOUT_THE_URL.includes(errorCode);
|
|
42487
|
-
}
|
|
42488
|
-
function safePathname(rawUrl) {
|
|
42489
|
-
try {
|
|
42490
|
-
return new URL(rawUrl).pathname.toLowerCase();
|
|
42491
|
-
} catch {
|
|
42492
|
-
return "";
|
|
42493
|
-
}
|
|
42494
|
-
}
|
|
42495
|
-
|
|
42496
|
-
// src/commands/videos/ingest.ts
|
|
42497
|
-
registerSchema({
|
|
42498
|
-
command: "videos.ingest",
|
|
42499
|
-
description: "Add a video to the library from a URL \u2014 a direct file, or a YouTube/TikTok/Vimeo page.",
|
|
42500
|
-
args: {
|
|
42501
|
-
url: { type: "string", description: "Video URL (direct file or a watch/post page)", required: true },
|
|
42502
|
-
source: { type: "string", description: "Provenance: url | youtube", required: false },
|
|
42503
|
-
"external-id": { type: "string", description: "Provider asset id (deduped on)", required: false },
|
|
42504
|
-
"external-url": { type: "string", description: "Canonical page URL", required: false },
|
|
42505
|
-
download: {
|
|
42506
|
-
type: "boolean",
|
|
42507
|
-
description: "Force the download path instead of letting Baker fetch the URL directly",
|
|
42508
|
-
required: false,
|
|
42509
|
-
default: false
|
|
42510
|
-
},
|
|
42511
|
-
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
42512
|
-
}
|
|
42513
|
-
});
|
|
42514
|
-
var ingestCommand2 = defineCommand200({
|
|
42515
|
-
meta: {
|
|
42516
|
-
name: "ingest",
|
|
42517
|
-
description: "Add a video to the library from a URL. A direct file URL is handed straight to Baker, which fetches it. A page URL (YouTube, TikTok, Vimeo, Instagram) is downloaded here first, then uploaded \u2014 and a direct URL that Baker cannot fetch falls back to that same path automatically.\n\nExample: baker videos ingest https://www.youtube.com/watch?v=abc123"
|
|
42518
|
-
},
|
|
42519
|
-
args: {
|
|
42520
|
-
url: { type: "positional", description: "Video URL", required: false },
|
|
42521
|
-
source: { type: "string", description: "Provenance: url | youtube", required: false },
|
|
42522
|
-
"external-id": { type: "string", description: "Provider asset id", required: false },
|
|
42523
|
-
"external-url": { type: "string", description: "Canonical page URL", required: false },
|
|
42524
|
-
download: { type: "boolean", description: "Force the download path", required: false, default: false },
|
|
42525
|
-
"dry-run": { type: "boolean", description: "Preview without executing", required: false, default: false }
|
|
42526
|
-
},
|
|
42527
|
-
run: ({ args }) => runVideoIngest({
|
|
42528
|
-
url: args.url,
|
|
42529
|
-
source: args.source,
|
|
42530
|
-
externalId: args["external-id"],
|
|
42531
|
-
externalUrl: args["external-url"],
|
|
42532
|
-
forceDownload: args.download === true,
|
|
42533
|
-
dryRun: args["dry-run"] === true
|
|
42534
|
-
})
|
|
42535
|
-
});
|
|
42536
|
-
async function runVideoIngest(opts) {
|
|
42537
|
-
const url = opts.url;
|
|
42538
|
-
if (!url) {
|
|
42539
|
-
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "URL is required" } });
|
|
42540
|
-
process.exit(1);
|
|
42541
|
-
}
|
|
42542
|
-
const direct = !opts.forceDownload && isDirectMediaUrl(url);
|
|
42543
|
-
const args = {
|
|
42544
|
-
url,
|
|
42545
|
-
source: resolveIngestSource(opts.source, url),
|
|
42546
|
-
externalId: opts.externalId,
|
|
42547
|
-
externalUrl: opts.externalUrl ?? (direct ? void 0 : url)
|
|
42548
|
-
};
|
|
42549
|
-
const validation = videosIngestRequestSchema.safeParse({ ...args, url });
|
|
42550
|
-
if (!validation.success) {
|
|
42551
|
-
const problem = validation.error.issues[0];
|
|
42552
|
-
writeJson({
|
|
42553
|
-
ok: false,
|
|
42554
|
-
error: {
|
|
42555
|
-
code: "VALIDATION_ERROR",
|
|
42556
|
-
message: problem ? `${problem.path.join(".") || "request"}: ${problem.message}` : "Invalid request"
|
|
42557
|
-
}
|
|
42558
|
-
});
|
|
42559
|
-
process.exit(1);
|
|
42560
|
-
}
|
|
42561
|
-
if (opts.dryRun) {
|
|
42562
|
-
writeJson({
|
|
42563
|
-
ok: true,
|
|
42564
|
-
dryRun: true,
|
|
42565
|
-
operation: "videos.ingest",
|
|
42566
|
-
params: {
|
|
42567
|
-
url,
|
|
42568
|
-
source: args.source,
|
|
42569
|
-
externalId: args.externalId ?? null,
|
|
42570
|
-
externalUrl: args.externalUrl ?? null,
|
|
42571
|
-
direct
|
|
42572
|
-
}
|
|
42573
|
-
});
|
|
42574
|
-
return;
|
|
42575
|
-
}
|
|
42576
|
-
try {
|
|
42577
|
-
writeJson({ ok: true, data: await ingestByRoute(args, direct) });
|
|
42578
|
-
} catch (err) {
|
|
42579
|
-
reportIngestFailure(err);
|
|
42580
|
-
}
|
|
42581
|
-
}
|
|
42582
|
-
async function ingestByRoute(args, direct) {
|
|
42583
|
-
if (!direct) {
|
|
42584
|
-
return { ...await downloadThenIngest(args), via: "download" };
|
|
42585
|
-
}
|
|
42586
|
-
try {
|
|
42587
|
-
return { ...await ingestUrl(args), via: "direct" };
|
|
42588
|
-
} catch (err) {
|
|
42589
|
-
if (!(err instanceof ApiError) || !worthDownloadingInstead(err.code)) throw err;
|
|
42590
|
-
return { ...await downloadThenIngest(args), via: "download", fallbackFrom: err.message };
|
|
42591
|
-
}
|
|
42592
|
-
}
|
|
42593
|
-
function reportIngestFailure(err) {
|
|
42594
|
-
if (err instanceof ApiError) {
|
|
42595
|
-
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
42596
|
-
process.exit(1);
|
|
42597
|
-
}
|
|
42598
|
-
if (err instanceof YtDlpError) {
|
|
42599
|
-
const detail = `${err.stderrTail} ${err.message}`;
|
|
42600
|
-
if (isProxyFailure(ytDlpBlockSignal(detail))) {
|
|
42601
|
-
writeJson({
|
|
42602
|
-
ok: false,
|
|
42603
|
-
error: {
|
|
42604
|
-
code: "INGEST_FAILED",
|
|
42605
|
-
message: "Couldn't download that video: our connection to the internet was refused.",
|
|
42606
|
-
fix: "This is Baker's egress proxy, not the link \u2014 its credentials look wrong or expired. The link is probably fine. Report it; retrying won't help until the proxy is fixed."
|
|
42607
|
-
}
|
|
42608
|
-
});
|
|
42609
|
-
process.exit(1);
|
|
42610
|
-
}
|
|
42611
|
-
const toolingOrBlock = isToolingOrBlockFailure(detail);
|
|
42612
|
-
writeJson({
|
|
42613
|
-
ok: false,
|
|
42614
|
-
error: {
|
|
42615
|
-
code: "INGEST_FAILED",
|
|
42616
|
-
message: `Couldn't download that video. ${err.stderrTail || err.message}`,
|
|
42617
|
-
fix: toolingOrBlock ? "The site wouldn't hand over the video \u2014 usually the downloader being out of date against a site that changed, or the request being blocked. Retrying won't help, and the link is probably fine. Report it so the tool can be updated; meanwhile download the file and use `baker videos upload <file>`." : "Check the link is public and playable. Private, age-restricted, or region-locked videos can't be downloaded \u2014 download the file yourself and use `baker videos upload <file>`."
|
|
42618
|
-
}
|
|
42619
|
-
});
|
|
42620
|
-
process.exit(1);
|
|
42621
|
-
}
|
|
42622
|
-
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
42623
|
-
process.exit(1);
|
|
42624
|
-
}
|
|
42625
|
-
function ingestUrl(args) {
|
|
42626
|
-
const body = {
|
|
42627
|
-
url: args.url,
|
|
42628
|
-
source: args.source
|
|
42629
|
-
};
|
|
42630
|
-
if (args.externalId) body.externalId = args.externalId;
|
|
42631
|
-
if (args.externalUrl) body.externalUrl = args.externalUrl;
|
|
42632
|
-
return apiPost("/api/videos/ingest", body);
|
|
42633
|
-
}
|
|
42634
|
-
async function downloadThenIngest(args) {
|
|
42635
|
-
const workDir = await mkdtemp2(path35.join(tmpdir3(), "videos-ingest-"));
|
|
42636
|
-
try {
|
|
42637
|
-
const { filePath, info, tier } = await runYtDlp({
|
|
42638
|
-
url: args.url,
|
|
42639
|
-
audioOnly: false,
|
|
42640
|
-
workDir,
|
|
42641
|
-
maxHeight: INGEST_MAX_HEIGHT
|
|
42642
|
-
});
|
|
42643
|
-
const stats = await stat7(filePath);
|
|
42644
|
-
if (stats.size > MAX_VIDEO_INGEST_BYTES) {
|
|
42645
|
-
throw new ApiError(
|
|
42646
|
-
"VALIDATION_ERROR",
|
|
42647
|
-
`That video is ${Math.round(stats.size / 1e6)} MB, over the ${Math.round(MAX_VIDEO_INGEST_BYTES / 1e6)} MB limit for this route.`
|
|
42648
|
-
);
|
|
42649
|
-
}
|
|
42650
|
-
const bytes = await readFile24(filePath);
|
|
42651
|
-
const publicUrl = await uploadToAssetStore(bytes);
|
|
42652
|
-
const externalId = args.externalId ?? (typeof info.id === "string" ? `${args.source}:${info.id}` : void 0);
|
|
42653
|
-
return {
|
|
42654
|
-
...await ingestUrl({ ...args, url: publicUrl, externalId, externalUrl: args.externalUrl ?? args.url }),
|
|
42655
|
-
egress: tier
|
|
42656
|
-
};
|
|
42657
|
-
} finally {
|
|
42658
|
-
await rm7(workDir, { recursive: true, force: true }).catch(() => {
|
|
42659
|
-
});
|
|
42660
|
-
}
|
|
42661
|
-
}
|
|
42662
|
-
async function uploadToAssetStore(bytes) {
|
|
42663
|
-
const sha256 = sha256Hex(bytes);
|
|
42664
|
-
const { putUrl, publicUrl } = await apiPost("/api/canvas/assets/presign", {
|
|
42665
|
-
sha256,
|
|
42666
|
-
mime: "video/mp4",
|
|
42667
|
-
purpose: "source"
|
|
42668
|
-
});
|
|
42669
|
-
const putRes = await fetch(putUrl, {
|
|
42670
|
-
method: "PUT",
|
|
42671
|
-
body: new Uint8Array(bytes),
|
|
42672
|
-
headers: { "Content-Type": "video/mp4" }
|
|
42673
|
-
});
|
|
42674
|
-
if (!putRes.ok) {
|
|
42675
|
-
throw new ApiError("INTERNAL_ERROR", `Storing the downloaded video failed: HTTP ${putRes.status}`);
|
|
42676
|
-
}
|
|
42677
|
-
return publicUrl;
|
|
42678
|
-
}
|
|
42679
|
-
|
|
42680
42544
|
// src/commands/videos/search.ts
|
|
42681
|
-
import { defineCommand as
|
|
42545
|
+
import { defineCommand as defineCommand200 } from "citty";
|
|
42682
42546
|
registerSchema({
|
|
42683
42547
|
command: "videos.search",
|
|
42684
42548
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -42688,7 +42552,7 @@ registerSchema({
|
|
|
42688
42552
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
42689
42553
|
}
|
|
42690
42554
|
});
|
|
42691
|
-
var searchCommand4 =
|
|
42555
|
+
var searchCommand4 = defineCommand200({
|
|
42692
42556
|
meta: {
|
|
42693
42557
|
name: "search",
|
|
42694
42558
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -42738,9 +42602,9 @@ var searchCommand4 = defineCommand201({
|
|
|
42738
42602
|
var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
42739
42603
|
|
|
42740
42604
|
// src/commands/videos/upload.ts
|
|
42741
|
-
import { readFile as
|
|
42605
|
+
import { readFile as readFile24, stat as stat7 } from "fs/promises";
|
|
42742
42606
|
import { basename as basename3, extname as extname4 } from "path";
|
|
42743
|
-
import { defineCommand as
|
|
42607
|
+
import { defineCommand as defineCommand201 } from "citty";
|
|
42744
42608
|
var MIME_MAP = {
|
|
42745
42609
|
".mp4": "video/mp4",
|
|
42746
42610
|
".mov": "video/quicktime",
|
|
@@ -42750,9 +42614,9 @@ var MIME_MAP = {
|
|
|
42750
42614
|
};
|
|
42751
42615
|
registerSchema({
|
|
42752
42616
|
command: "videos.upload",
|
|
42753
|
-
description: "Upload a video to Baker
|
|
42617
|
+
description: "Upload a video file to Baker (via Mux direct upload)",
|
|
42754
42618
|
args: {
|
|
42755
|
-
file: { type: "string", description: "
|
|
42619
|
+
file: { type: "string", description: "Path to the video file", required: true },
|
|
42756
42620
|
"content-type": {
|
|
42757
42621
|
type: "string",
|
|
42758
42622
|
description: "MIME type (auto-detected from extension if omitted)",
|
|
@@ -42779,49 +42643,33 @@ function detectContentType(filePath) {
|
|
|
42779
42643
|
}
|
|
42780
42644
|
return mime;
|
|
42781
42645
|
}
|
|
42782
|
-
|
|
42783
|
-
return /^https?:\/\//i.test(value);
|
|
42784
|
-
}
|
|
42785
|
-
var uploadCommand2 = defineCommand202({
|
|
42646
|
+
var uploadCommand2 = defineCommand201({
|
|
42786
42647
|
meta: {
|
|
42787
42648
|
name: "upload",
|
|
42788
|
-
description: "Upload a video to Baker
|
|
42649
|
+
description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
|
|
42789
42650
|
},
|
|
42790
42651
|
args: {
|
|
42791
|
-
file: { type: "positional", description: "
|
|
42792
|
-
"content-type": { type: "string", description: "MIME type (
|
|
42652
|
+
file: { type: "positional", description: "Path to the video file", required: false },
|
|
42653
|
+
"content-type": { type: "string", description: "MIME type (auto-detected if omitted)", required: false },
|
|
42793
42654
|
context: {
|
|
42794
42655
|
type: "string",
|
|
42795
42656
|
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.",
|
|
42796
42657
|
required: false
|
|
42797
42658
|
},
|
|
42798
|
-
source: { type: "string", description: "Provenance (URL mode only): url | youtube", required: false },
|
|
42799
|
-
"external-id": { type: "string", description: "Provider asset id (URL mode only)", required: false },
|
|
42800
|
-
"external-url": { type: "string", description: "Canonical page URL (URL mode only)", required: false },
|
|
42801
42659
|
"dry-run": { type: "boolean", description: "Preview without executing", required: false, default: false }
|
|
42802
42660
|
},
|
|
42803
42661
|
run: async ({ args }) => {
|
|
42804
42662
|
try {
|
|
42805
42663
|
const filePath = args.file;
|
|
42806
42664
|
if (!filePath) {
|
|
42807
|
-
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path
|
|
42665
|
+
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path is required" } });
|
|
42808
42666
|
process.exit(1);
|
|
42809
42667
|
}
|
|
42810
|
-
if (isRemoteUrl3(filePath)) {
|
|
42811
|
-
await runVideoIngest({
|
|
42812
|
-
url: filePath,
|
|
42813
|
-
source: args.source,
|
|
42814
|
-
externalId: args["external-id"],
|
|
42815
|
-
externalUrl: args["external-url"],
|
|
42816
|
-
dryRun: args["dry-run"] === true
|
|
42817
|
-
});
|
|
42818
|
-
return;
|
|
42819
|
-
}
|
|
42820
42668
|
const contentType = args["content-type"] || detectContentType(filePath);
|
|
42821
42669
|
const originalFilename = basename3(filePath);
|
|
42822
42670
|
const descriptionContext = args.context;
|
|
42823
42671
|
if (args["dry-run"]) {
|
|
42824
|
-
const fileStats = await
|
|
42672
|
+
const fileStats = await stat7(filePath);
|
|
42825
42673
|
writeJson({
|
|
42826
42674
|
ok: true,
|
|
42827
42675
|
dryRun: true,
|
|
@@ -42834,7 +42682,7 @@ var uploadCommand2 = defineCommand202({
|
|
|
42834
42682
|
originalFilename,
|
|
42835
42683
|
descriptionContext
|
|
42836
42684
|
});
|
|
42837
|
-
const fileBuffer = await
|
|
42685
|
+
const fileBuffer = await readFile24(filePath);
|
|
42838
42686
|
const uploadResponse = await fetch(uploadUrl, {
|
|
42839
42687
|
method: "PUT",
|
|
42840
42688
|
headers: { "Content-Type": contentType },
|
|
@@ -42865,17 +42713,16 @@ var uploadCommand2 = defineCommand202({
|
|
|
42865
42713
|
});
|
|
42866
42714
|
|
|
42867
42715
|
// src/commands/videos/index.ts
|
|
42868
|
-
var videosCommand =
|
|
42716
|
+
var videosCommand = defineCommand202({
|
|
42869
42717
|
meta: {
|
|
42870
42718
|
name: "videos",
|
|
42871
|
-
description: `Find and manage videos in Baker. Subcommands: search, get, upload,
|
|
42719
|
+
description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
|
|
42872
42720
|
|
|
42873
42721
|
Examples:
|
|
42874
42722
|
baker videos search "product demo" --limit 5
|
|
42875
42723
|
baker videos search "tutorial" --tags explainer
|
|
42876
42724
|
baker videos get <video-id>
|
|
42877
42725
|
baker videos upload ./demo.mp4
|
|
42878
|
-
baker videos ingest https://www.youtube.com/watch?v=abc123
|
|
42879
42726
|
baker videos delete <video-id> --dry-run
|
|
42880
42727
|
baker videos tags
|
|
42881
42728
|
Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
@@ -42885,17 +42732,16 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
42885
42732
|
group: groupCommand2,
|
|
42886
42733
|
search: searchCommand4,
|
|
42887
42734
|
upload: uploadCommand2,
|
|
42888
|
-
ingest: ingestCommand2,
|
|
42889
42735
|
delete: deleteCommand3,
|
|
42890
42736
|
tags: tagsCommand5
|
|
42891
42737
|
}
|
|
42892
42738
|
});
|
|
42893
42739
|
|
|
42894
42740
|
// src/commands/winning-ads/index.ts
|
|
42895
|
-
import { defineCommand as
|
|
42741
|
+
import { defineCommand as defineCommand215 } from "citty";
|
|
42896
42742
|
|
|
42897
42743
|
// src/commands/winning-ads/advertisers.ts
|
|
42898
|
-
import { defineCommand as
|
|
42744
|
+
import { defineCommand as defineCommand203 } from "citty";
|
|
42899
42745
|
|
|
42900
42746
|
// src/commands/winning-ads/shared.ts
|
|
42901
42747
|
function splitList2(value) {
|
|
@@ -42948,7 +42794,7 @@ function advertiserNormalizer(record, full) {
|
|
|
42948
42794
|
last_synced_at: record.last_synced_at ?? null
|
|
42949
42795
|
};
|
|
42950
42796
|
}
|
|
42951
|
-
var advertisersCommand2 =
|
|
42797
|
+
var advertisersCommand2 = defineCommand203({
|
|
42952
42798
|
meta: {
|
|
42953
42799
|
name: "advertisers",
|
|
42954
42800
|
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'
|
|
@@ -43006,7 +42852,7 @@ var advertisersCommand2 = defineCommand204({
|
|
|
43006
42852
|
});
|
|
43007
42853
|
|
|
43008
42854
|
// src/commands/winning-ads/brief.ts
|
|
43009
|
-
import { defineCommand as
|
|
42855
|
+
import { defineCommand as defineCommand204 } from "citty";
|
|
43010
42856
|
registerSchema({
|
|
43011
42857
|
command: "winning-ads.brief",
|
|
43012
42858
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -43052,7 +42898,7 @@ function parseDna(raw) {
|
|
|
43052
42898
|
}
|
|
43053
42899
|
return parsed;
|
|
43054
42900
|
}
|
|
43055
|
-
var briefCommand =
|
|
42901
|
+
var briefCommand = defineCommand204({
|
|
43056
42902
|
meta: {
|
|
43057
42903
|
name: "brief",
|
|
43058
42904
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -43088,7 +42934,7 @@ var briefCommand = defineCommand205({
|
|
|
43088
42934
|
});
|
|
43089
42935
|
|
|
43090
42936
|
// src/commands/winning-ads/content.ts
|
|
43091
|
-
import { defineCommand as
|
|
42937
|
+
import { defineCommand as defineCommand205 } from "citty";
|
|
43092
42938
|
registerSchema({
|
|
43093
42939
|
command: "winning-ads.content",
|
|
43094
42940
|
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.",
|
|
@@ -43101,7 +42947,7 @@ registerSchema({
|
|
|
43101
42947
|
}
|
|
43102
42948
|
}
|
|
43103
42949
|
});
|
|
43104
|
-
var contentCommand =
|
|
42950
|
+
var contentCommand = defineCommand205({
|
|
43105
42951
|
meta: {
|
|
43106
42952
|
name: "content",
|
|
43107
42953
|
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -43150,7 +42996,7 @@ var contentCommand = defineCommand206({
|
|
|
43150
42996
|
});
|
|
43151
42997
|
|
|
43152
42998
|
// src/commands/winning-ads/feed.ts
|
|
43153
|
-
import { defineCommand as
|
|
42999
|
+
import { defineCommand as defineCommand206 } from "citty";
|
|
43154
43000
|
function buildFeedParams(input) {
|
|
43155
43001
|
const params = {};
|
|
43156
43002
|
const advertiser = splitList2(input.advertiser);
|
|
@@ -43202,7 +43048,7 @@ registerSchema({
|
|
|
43202
43048
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
43203
43049
|
}
|
|
43204
43050
|
});
|
|
43205
|
-
var feedCommand =
|
|
43051
|
+
var feedCommand = defineCommand206({
|
|
43206
43052
|
meta: {
|
|
43207
43053
|
name: "feed",
|
|
43208
43054
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -43287,7 +43133,7 @@ var feedCommand = defineCommand207({
|
|
|
43287
43133
|
});
|
|
43288
43134
|
|
|
43289
43135
|
// src/commands/winning-ads/follow.ts
|
|
43290
|
-
import { defineCommand as
|
|
43136
|
+
import { defineCommand as defineCommand207 } from "citty";
|
|
43291
43137
|
var PLATFORMS = ["meta", "linkedin"];
|
|
43292
43138
|
registerSchema({
|
|
43293
43139
|
command: "winning-ads.follow",
|
|
@@ -43302,7 +43148,7 @@ registerSchema({
|
|
|
43302
43148
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
43303
43149
|
}
|
|
43304
43150
|
});
|
|
43305
|
-
var followCommand =
|
|
43151
|
+
var followCommand = defineCommand207({
|
|
43306
43152
|
meta: {
|
|
43307
43153
|
name: "follow",
|
|
43308
43154
|
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'
|
|
@@ -43349,7 +43195,7 @@ var followCommand = defineCommand208({
|
|
|
43349
43195
|
});
|
|
43350
43196
|
|
|
43351
43197
|
// src/commands/winning-ads/follow-competitors.ts
|
|
43352
|
-
import { defineCommand as
|
|
43198
|
+
import { defineCommand as defineCommand208 } from "citty";
|
|
43353
43199
|
var PLATFORMS2 = ["meta", "linkedin"];
|
|
43354
43200
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
43355
43201
|
function buildFollowBatchBody(input) {
|
|
@@ -43382,7 +43228,7 @@ registerSchema({
|
|
|
43382
43228
|
}
|
|
43383
43229
|
}
|
|
43384
43230
|
});
|
|
43385
|
-
var followCompetitorsCommand =
|
|
43231
|
+
var followCompetitorsCommand = defineCommand208({
|
|
43386
43232
|
meta: {
|
|
43387
43233
|
name: "follow-competitors",
|
|
43388
43234
|
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"'
|
|
@@ -43457,7 +43303,7 @@ var followCompetitorsCommand = defineCommand209({
|
|
|
43457
43303
|
});
|
|
43458
43304
|
|
|
43459
43305
|
// src/commands/winning-ads/following.ts
|
|
43460
|
-
import { defineCommand as
|
|
43306
|
+
import { defineCommand as defineCommand209 } from "citty";
|
|
43461
43307
|
registerSchema({
|
|
43462
43308
|
command: "winning-ads.following",
|
|
43463
43309
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
|
|
@@ -43490,7 +43336,7 @@ function followingNormalizer(record, full) {
|
|
|
43490
43336
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
43491
43337
|
};
|
|
43492
43338
|
}
|
|
43493
|
-
var followingCommand =
|
|
43339
|
+
var followingCommand = defineCommand209({
|
|
43494
43340
|
meta: {
|
|
43495
43341
|
name: "following",
|
|
43496
43342
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
|
|
@@ -43525,7 +43371,7 @@ var followingCommand = defineCommand210({
|
|
|
43525
43371
|
});
|
|
43526
43372
|
|
|
43527
43373
|
// src/commands/winning-ads/patterns.ts
|
|
43528
|
-
import { defineCommand as
|
|
43374
|
+
import { defineCommand as defineCommand210 } from "citty";
|
|
43529
43375
|
registerSchema({
|
|
43530
43376
|
command: "winning-ads.patterns",
|
|
43531
43377
|
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.",
|
|
@@ -43564,7 +43410,7 @@ function discriminatorRow(record) {
|
|
|
43564
43410
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
43565
43411
|
};
|
|
43566
43412
|
}
|
|
43567
|
-
var patternsCommand =
|
|
43413
|
+
var patternsCommand = defineCommand210({
|
|
43568
43414
|
meta: {
|
|
43569
43415
|
name: "patterns",
|
|
43570
43416
|
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"
|
|
@@ -43620,7 +43466,7 @@ var patternsCommand = defineCommand211({
|
|
|
43620
43466
|
});
|
|
43621
43467
|
|
|
43622
43468
|
// src/commands/winning-ads/search.ts
|
|
43623
|
-
import { defineCommand as
|
|
43469
|
+
import { defineCommand as defineCommand211 } from "citty";
|
|
43624
43470
|
registerSchema({
|
|
43625
43471
|
command: "winning-ads.search",
|
|
43626
43472
|
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.",
|
|
@@ -43728,7 +43574,7 @@ function buildSearchBody2(args) {
|
|
|
43728
43574
|
}
|
|
43729
43575
|
return body;
|
|
43730
43576
|
}
|
|
43731
|
-
var searchCommand5 =
|
|
43577
|
+
var searchCommand5 = defineCommand211({
|
|
43732
43578
|
meta: {
|
|
43733
43579
|
name: "search",
|
|
43734
43580
|
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"
|
|
@@ -43843,7 +43689,7 @@ var searchCommand5 = defineCommand212({
|
|
|
43843
43689
|
});
|
|
43844
43690
|
|
|
43845
43691
|
// src/commands/winning-ads/seeds.ts
|
|
43846
|
-
import { defineCommand as
|
|
43692
|
+
import { defineCommand as defineCommand212 } from "citty";
|
|
43847
43693
|
function leanRow(r) {
|
|
43848
43694
|
return {
|
|
43849
43695
|
key: r.key,
|
|
@@ -43871,7 +43717,7 @@ function makeSeedCommand(opts) {
|
|
|
43871
43717
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
43872
43718
|
}
|
|
43873
43719
|
});
|
|
43874
|
-
return
|
|
43720
|
+
return defineCommand212({
|
|
43875
43721
|
meta: { name: opts.name, description: opts.description },
|
|
43876
43722
|
args: {
|
|
43877
43723
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -43920,7 +43766,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
43920
43766
|
});
|
|
43921
43767
|
|
|
43922
43768
|
// src/commands/winning-ads/unfollow.ts
|
|
43923
|
-
import { defineCommand as
|
|
43769
|
+
import { defineCommand as defineCommand213 } from "citty";
|
|
43924
43770
|
registerSchema({
|
|
43925
43771
|
command: "winning-ads.unfollow",
|
|
43926
43772
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -43928,7 +43774,7 @@ registerSchema({
|
|
|
43928
43774
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
43929
43775
|
}
|
|
43930
43776
|
});
|
|
43931
|
-
var unfollowCommand =
|
|
43777
|
+
var unfollowCommand = defineCommand213({
|
|
43932
43778
|
meta: {
|
|
43933
43779
|
name: "unfollow",
|
|
43934
43780
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -43949,7 +43795,7 @@ var unfollowCommand = defineCommand214({
|
|
|
43949
43795
|
});
|
|
43950
43796
|
|
|
43951
43797
|
// src/commands/winning-ads/winners.ts
|
|
43952
|
-
import { defineCommand as
|
|
43798
|
+
import { defineCommand as defineCommand214 } from "citty";
|
|
43953
43799
|
registerSchema({
|
|
43954
43800
|
command: "winning-ads.winners",
|
|
43955
43801
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -43959,7 +43805,7 @@ registerSchema({
|
|
|
43959
43805
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
43960
43806
|
}
|
|
43961
43807
|
});
|
|
43962
|
-
var winnersCommand =
|
|
43808
|
+
var winnersCommand = defineCommand214({
|
|
43963
43809
|
meta: {
|
|
43964
43810
|
name: "winners",
|
|
43965
43811
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -44009,7 +43855,7 @@ var winnersCommand = defineCommand215({
|
|
|
44009
43855
|
});
|
|
44010
43856
|
|
|
44011
43857
|
// src/commands/winning-ads/index.ts
|
|
44012
|
-
var winningAdsCommand =
|
|
43858
|
+
var winningAdsCommand = defineCommand215({
|
|
44013
43859
|
meta: {
|
|
44014
43860
|
name: "winning-ads",
|
|
44015
43861
|
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.
|
|
@@ -44081,7 +43927,7 @@ function getCliVersion() {
|
|
|
44081
43927
|
}
|
|
44082
43928
|
|
|
44083
43929
|
// src/cli.ts
|
|
44084
|
-
var main =
|
|
43930
|
+
var main = defineCommand216({
|
|
44085
43931
|
meta: {
|
|
44086
43932
|
name: "baker",
|
|
44087
43933
|
version: getCliVersion(),
|