@koda-sl/baker-cli 0.98.0-dev.62be5b016 → 0.98.1-dev.662146ce
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 +12 -0
- package/canvas/tiktok-captions-composition/index.html +7 -2
- package/canvas/video-overlay-composition/index.html +18 -25
- package/dist/{chunk-IKMDQQ4M.js → chunk-26K7V346.js} +130 -26
- package/dist/chunk-26K7V346.js.map +1 -0
- package/dist/cli.js +736 -472
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.js +1 -1
- package/package.json +5 -3
- package/dist/chunk-IKMDQQ4M.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
AssetRef,
|
|
4
3
|
ELEVENLABS_MAX_MUSIC_LENGTH_MS,
|
|
5
4
|
IMAGE_GENERATE_MODELS,
|
|
6
5
|
MODEL_REGISTRY,
|
|
@@ -8,13 +7,12 @@ import {
|
|
|
8
7
|
ValidationError,
|
|
9
8
|
createEngineFromEnv,
|
|
10
9
|
defaultRegistry,
|
|
11
|
-
extForMime,
|
|
12
10
|
generateCatalog,
|
|
13
11
|
validateCanvasDeep
|
|
14
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-26K7V346.js";
|
|
15
13
|
|
|
16
14
|
// src/cli.ts
|
|
17
|
-
import { defineCommand as
|
|
15
|
+
import { defineCommand as defineCommand151, runMain } from "citty";
|
|
18
16
|
|
|
19
17
|
// src/commands/actions/index.ts
|
|
20
18
|
import { defineCommand as defineCommand17 } from "citty";
|
|
@@ -149,9 +147,9 @@ async function handleResponse(response) {
|
|
|
149
147
|
throw new ApiError("INTERNAL_ERROR", "Failed to parse API response as JSON");
|
|
150
148
|
}
|
|
151
149
|
}
|
|
152
|
-
async function apiGet(
|
|
150
|
+
async function apiGet(path11, params) {
|
|
153
151
|
const env = getEnv();
|
|
154
|
-
const url = new URL(
|
|
152
|
+
const url = new URL(path11, env.BAKER_API_URL);
|
|
155
153
|
if (params) {
|
|
156
154
|
const clean = sanitizeParams(params);
|
|
157
155
|
for (const [key, value] of Object.entries(clean)) {
|
|
@@ -176,12 +174,12 @@ async function apiGet(path8, params) {
|
|
|
176
174
|
}
|
|
177
175
|
return handleResponse(response);
|
|
178
176
|
}
|
|
179
|
-
async function apiPost(
|
|
177
|
+
async function apiPost(path11, body, opts) {
|
|
180
178
|
const env = getEnv();
|
|
181
179
|
const timeoutMs = opts?.timeoutMs ?? 6e4;
|
|
182
180
|
let response;
|
|
183
181
|
try {
|
|
184
|
-
response = await fetchWithRateLimitRetry(new URL(
|
|
182
|
+
response = await fetchWithRateLimitRetry(new URL(path11, env.BAKER_API_URL).toString(), {
|
|
185
183
|
method: "POST",
|
|
186
184
|
headers: {
|
|
187
185
|
Authorization: `Bearer ${env.BAKER_API_KEY}`,
|
|
@@ -1329,31 +1327,31 @@ function cachePath(category, key) {
|
|
|
1329
1327
|
return join(dir, `${hashKey(key)}.json`);
|
|
1330
1328
|
}
|
|
1331
1329
|
function cacheGet(category, key) {
|
|
1332
|
-
const
|
|
1333
|
-
if (!existsSync(
|
|
1330
|
+
const path11 = cachePath(category, key);
|
|
1331
|
+
if (!existsSync(path11)) {
|
|
1334
1332
|
return null;
|
|
1335
1333
|
}
|
|
1336
1334
|
try {
|
|
1337
|
-
const raw = readFileSync(
|
|
1335
|
+
const raw = readFileSync(path11, "utf-8");
|
|
1338
1336
|
const entry = JSON.parse(raw);
|
|
1339
1337
|
if (entry.expiresAt < Date.now()) {
|
|
1340
|
-
rmSync(
|
|
1338
|
+
rmSync(path11, { force: true });
|
|
1341
1339
|
return null;
|
|
1342
1340
|
}
|
|
1343
1341
|
return entry;
|
|
1344
1342
|
} catch {
|
|
1345
|
-
rmSync(
|
|
1343
|
+
rmSync(path11, { force: true });
|
|
1346
1344
|
return null;
|
|
1347
1345
|
}
|
|
1348
1346
|
}
|
|
1349
1347
|
function cacheSet(category, key, data, ttlMs, fields) {
|
|
1350
|
-
const
|
|
1348
|
+
const path11 = cachePath(category, key);
|
|
1351
1349
|
const entry = {
|
|
1352
1350
|
expiresAt: Date.now() + ttlMs,
|
|
1353
1351
|
data,
|
|
1354
1352
|
fields
|
|
1355
1353
|
};
|
|
1356
|
-
writeFileSync(
|
|
1354
|
+
writeFileSync(path11, JSON.stringify(entry), "utf-8");
|
|
1357
1355
|
}
|
|
1358
1356
|
var HOUR = 60 * 60 * 1e3;
|
|
1359
1357
|
var MINUTE = 60 * 1e3;
|
|
@@ -8063,232 +8061,14 @@ var catalogCommand = defineCommand78({
|
|
|
8063
8061
|
}
|
|
8064
8062
|
});
|
|
8065
8063
|
|
|
8066
|
-
// src/commands/canvas/gallery.ts
|
|
8067
|
-
import { readdir, readFile } from "fs/promises";
|
|
8068
|
-
import path from "path";
|
|
8069
|
-
import { defineCommand as defineCommand79 } from "citty";
|
|
8070
|
-
|
|
8071
|
-
// src/engine/gallery/descriptor.ts
|
|
8072
|
-
var KNOWN_RATIOS = [
|
|
8073
|
-
["9:16", 9 / 16],
|
|
8074
|
-
["4:5", 4 / 5],
|
|
8075
|
-
["1:1", 1],
|
|
8076
|
-
["1.91:1", 1.91],
|
|
8077
|
-
["16:9", 16 / 9],
|
|
8078
|
-
["4:1", 4]
|
|
8079
|
-
];
|
|
8080
|
-
var RATIO_TOLERANCE = 0.06;
|
|
8081
|
-
function aspectLabel(width, height) {
|
|
8082
|
-
if (!width || !height) {
|
|
8083
|
-
return "other";
|
|
8084
|
-
}
|
|
8085
|
-
const ratio = width / height;
|
|
8086
|
-
let best = "other";
|
|
8087
|
-
let bestErr = Number.POSITIVE_INFINITY;
|
|
8088
|
-
for (const [label, value] of KNOWN_RATIOS) {
|
|
8089
|
-
const err = Math.abs(ratio - value) / value;
|
|
8090
|
-
if (err < bestErr) {
|
|
8091
|
-
bestErr = err;
|
|
8092
|
-
best = label;
|
|
8093
|
-
}
|
|
8094
|
-
}
|
|
8095
|
-
return bestErr <= RATIO_TOLERANCE ? best : `${width}x${height}`;
|
|
8096
|
-
}
|
|
8097
|
-
function visualRef(value) {
|
|
8098
|
-
const parsed = AssetRef.safeParse(value);
|
|
8099
|
-
if (!parsed.success) {
|
|
8100
|
-
return null;
|
|
8101
|
-
}
|
|
8102
|
-
if (parsed.data.kind !== "image" && parsed.data.kind !== "video") {
|
|
8103
|
-
return null;
|
|
8104
|
-
}
|
|
8105
|
-
return parsed.data;
|
|
8106
|
-
}
|
|
8107
|
-
function deliverableFor(ref, stem, resolveLocal) {
|
|
8108
|
-
const width = "width" in ref ? ref.width : void 0;
|
|
8109
|
-
const height = "height" in ref ? ref.height : void 0;
|
|
8110
|
-
return {
|
|
8111
|
-
kind: ref.kind,
|
|
8112
|
-
format: aspectLabel(width, height),
|
|
8113
|
-
// Remote-node outputs already carry a public R2 url; local composites are
|
|
8114
|
-
// resolved against the mounted run dir's public base.
|
|
8115
|
-
url: ref.url ?? resolveLocal(`${stem}.${extForMime(ref.mime)}`),
|
|
8116
|
-
width,
|
|
8117
|
-
height,
|
|
8118
|
-
label: stem
|
|
8119
|
-
};
|
|
8120
|
-
}
|
|
8121
|
-
function deliverablesFromOutput(output, resolveLocal) {
|
|
8122
|
-
if (Array.isArray(output)) {
|
|
8123
|
-
const out = [];
|
|
8124
|
-
output.forEach((entry, i) => {
|
|
8125
|
-
const ref2 = visualRef(entry);
|
|
8126
|
-
if (ref2) {
|
|
8127
|
-
out.push(deliverableFor(ref2, `_final__${i}`, resolveLocal));
|
|
8128
|
-
}
|
|
8129
|
-
});
|
|
8130
|
-
return out;
|
|
8131
|
-
}
|
|
8132
|
-
const ref = visualRef(output);
|
|
8133
|
-
return ref ? [deliverableFor(ref, "_final", resolveLocal)] : [];
|
|
8134
|
-
}
|
|
8135
|
-
function buildGeneration(runId, manifest, resolveLocal) {
|
|
8136
|
-
const m = manifest ?? {};
|
|
8137
|
-
const credits = typeof m.stats?.total_credits === "number" ? m.stats.total_credits : 0;
|
|
8138
|
-
return {
|
|
8139
|
-
runId,
|
|
8140
|
-
createdAt: typeof m.completed_at === "number" ? m.completed_at : 0,
|
|
8141
|
-
credits,
|
|
8142
|
-
deliverables: deliverablesFromOutput(m.output, resolveLocal)
|
|
8143
|
-
};
|
|
8144
|
-
}
|
|
8145
|
-
function buildGalleryDescriptor(input) {
|
|
8146
|
-
const generations = [...input.generations].sort((a, b) => b.createdAt - a.createdAt);
|
|
8147
|
-
return {
|
|
8148
|
-
slug: input.slug,
|
|
8149
|
-
title: input.definition.title,
|
|
8150
|
-
platform: input.definition.platform,
|
|
8151
|
-
status: input.definition.status,
|
|
8152
|
-
reference: input.definition.reference,
|
|
8153
|
-
selectedRun: input.definition.selectedRun,
|
|
8154
|
-
generations
|
|
8155
|
-
};
|
|
8156
|
-
}
|
|
8157
|
-
function titleFromSlug(slug) {
|
|
8158
|
-
return slug.split(/[-_/]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
8159
|
-
}
|
|
8160
|
-
function stripQuotes(raw) {
|
|
8161
|
-
const trimmed = raw.trim();
|
|
8162
|
-
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
8163
|
-
return trimmed.slice(1, -1);
|
|
8164
|
-
}
|
|
8165
|
-
return trimmed;
|
|
8166
|
-
}
|
|
8167
|
-
function parseInlineList(raw) {
|
|
8168
|
-
return raw.slice(1, -1).split(",").map((item) => stripQuotes(item)).filter(Boolean);
|
|
8169
|
-
}
|
|
8170
|
-
function parseFrontmatter(markdown) {
|
|
8171
|
-
const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
8172
|
-
const block = match?.[1];
|
|
8173
|
-
if (!block) {
|
|
8174
|
-
return {};
|
|
8175
|
-
}
|
|
8176
|
-
const out = {};
|
|
8177
|
-
let listKey = null;
|
|
8178
|
-
for (const line of block.split(/\r?\n/)) {
|
|
8179
|
-
const item = line.match(/^\s+-\s+(.*)$/)?.[1];
|
|
8180
|
-
if (listKey && item !== void 0) {
|
|
8181
|
-
out[listKey].push(stripQuotes(item));
|
|
8182
|
-
continue;
|
|
8183
|
-
}
|
|
8184
|
-
const kv = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
|
|
8185
|
-
if (!kv?.[1]) {
|
|
8186
|
-
continue;
|
|
8187
|
-
}
|
|
8188
|
-
listKey = null;
|
|
8189
|
-
const key = kv[1];
|
|
8190
|
-
const value = (kv[2] ?? "").trim();
|
|
8191
|
-
if (value === "") {
|
|
8192
|
-
out[key] = [];
|
|
8193
|
-
listKey = key;
|
|
8194
|
-
} else if (value.startsWith("[") && value.endsWith("]")) {
|
|
8195
|
-
out[key] = parseInlineList(value);
|
|
8196
|
-
} else {
|
|
8197
|
-
out[key] = stripQuotes(value);
|
|
8198
|
-
}
|
|
8199
|
-
}
|
|
8200
|
-
return out;
|
|
8201
|
-
}
|
|
8202
|
-
function asString(value) {
|
|
8203
|
-
if (typeof value === "string" && value.length > 0) {
|
|
8204
|
-
return value;
|
|
8205
|
-
}
|
|
8206
|
-
return void 0;
|
|
8207
|
-
}
|
|
8208
|
-
function asList(value) {
|
|
8209
|
-
if (Array.isArray(value)) {
|
|
8210
|
-
return value;
|
|
8211
|
-
}
|
|
8212
|
-
return typeof value === "string" && value.length > 0 ? [value] : [];
|
|
8213
|
-
}
|
|
8214
|
-
function parseCreativeDefinition(markdown, slug) {
|
|
8215
|
-
const fm = parseFrontmatter(markdown);
|
|
8216
|
-
return {
|
|
8217
|
-
title: asString(fm.title) ?? titleFromSlug(slug),
|
|
8218
|
-
platform: asList(fm.platform),
|
|
8219
|
-
formats: asList(fm.formats),
|
|
8220
|
-
status: asString(fm.status) ?? "draft",
|
|
8221
|
-
reference: asString(fm.reference),
|
|
8222
|
-
selectedRun: asString(fm.selected_run)
|
|
8223
|
-
};
|
|
8224
|
-
}
|
|
8225
|
-
|
|
8226
|
-
// src/commands/canvas/gallery.ts
|
|
8227
|
-
async function readJson(file) {
|
|
8228
|
-
try {
|
|
8229
|
-
return JSON.parse(await readFile(file, "utf8"));
|
|
8230
|
-
} catch {
|
|
8231
|
-
return null;
|
|
8232
|
-
}
|
|
8233
|
-
}
|
|
8234
|
-
async function listRunDirs(runsDir) {
|
|
8235
|
-
try {
|
|
8236
|
-
const entries = await readdir(runsDir, { withFileTypes: true });
|
|
8237
|
-
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
8238
|
-
} catch {
|
|
8239
|
-
return [];
|
|
8240
|
-
}
|
|
8241
|
-
}
|
|
8242
|
-
var galleryCommand = defineCommand79({
|
|
8243
|
-
meta: {
|
|
8244
|
-
name: "gallery",
|
|
8245
|
-
description: "Read a creative's _definition.md + every persisted run manifest and emit the gallery descriptor (JSON) the dashboard renders."
|
|
8246
|
-
},
|
|
8247
|
-
args: {
|
|
8248
|
-
dir: { type: "positional", required: true, description: "Creative folder, e.g. src/creatives/<slug>" },
|
|
8249
|
-
"workspace-dir": { type: "string", description: "R2-mounted workspace root (default ./.creatives-workspace)" },
|
|
8250
|
-
"public-url": { type: "string", description: "R2 public base (default $R2_PUBLIC_URL)" },
|
|
8251
|
-
"company-id": { type: "string", description: "Company id for the R2 prefix (default $BAKER_COMPANY_ID)" }
|
|
8252
|
-
},
|
|
8253
|
-
async run({ args }) {
|
|
8254
|
-
const creativeDir = path.resolve(String(args.dir));
|
|
8255
|
-
const slug = path.basename(creativeDir);
|
|
8256
|
-
const workspaceDir = path.resolve(String(args["workspace-dir"] ?? ".creatives-workspace"));
|
|
8257
|
-
const runsDir = path.join(workspaceDir, slug, "runs");
|
|
8258
|
-
const runtimeEnv = process.env;
|
|
8259
|
-
const publicUrl = (args["public-url"] ?? runtimeEnv.R2_PUBLIC_URL ?? "").replace(/\/+$/, "");
|
|
8260
|
-
const companyId = String(args["company-id"] ?? runtimeEnv.BAKER_COMPANY_ID ?? "");
|
|
8261
|
-
const definitionPath = path.join(creativeDir, "_definition.md");
|
|
8262
|
-
let definitionMd = "";
|
|
8263
|
-
try {
|
|
8264
|
-
definitionMd = await readFile(definitionPath, "utf8");
|
|
8265
|
-
} catch {
|
|
8266
|
-
}
|
|
8267
|
-
const definition = parseCreativeDefinition(definitionMd, slug);
|
|
8268
|
-
const generations = [];
|
|
8269
|
-
for (const runId of await listRunDirs(runsDir)) {
|
|
8270
|
-
const manifest = await readJson(path.join(runsDir, runId, "manifest.json"));
|
|
8271
|
-
if (!manifest) {
|
|
8272
|
-
continue;
|
|
8273
|
-
}
|
|
8274
|
-
const runDir = path.join(runsDir, runId);
|
|
8275
|
-
const resolveLocal = (filename) => publicUrl && companyId ? `${publicUrl}/creatives/${companyId}/${slug}/runs/${runId}/${filename}` : path.join(runDir, filename);
|
|
8276
|
-
generations.push(buildGeneration(runId, manifest, resolveLocal));
|
|
8277
|
-
}
|
|
8278
|
-
const descriptor = buildGalleryDescriptor({ slug, definition, generations });
|
|
8279
|
-
process.stdout.write(`${JSON.stringify({ ok: true, descriptor }, null, 2)}
|
|
8280
|
-
`);
|
|
8281
|
-
}
|
|
8282
|
-
});
|
|
8283
|
-
|
|
8284
8064
|
// src/commands/canvas/inspect.ts
|
|
8285
8065
|
import { execFile } from "child_process";
|
|
8286
|
-
import { readdir
|
|
8287
|
-
import
|
|
8066
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
8067
|
+
import path from "path";
|
|
8288
8068
|
import { promisify } from "util";
|
|
8289
|
-
import { defineCommand as
|
|
8069
|
+
import { defineCommand as defineCommand79 } from "citty";
|
|
8290
8070
|
var execFileAsync = promisify(execFile);
|
|
8291
|
-
var inspectCommand =
|
|
8071
|
+
var inspectCommand = defineCommand79({
|
|
8292
8072
|
meta: {
|
|
8293
8073
|
name: "inspect",
|
|
8294
8074
|
description: "Dump a one-page summary of a canvas run: per-node duration + cache status, list of output files in the run dir, and optionally three thumbnail frames per video output. Pass either a run_id (resolved against --outputs-dir) or an absolute run directory."
|
|
@@ -8302,7 +8082,7 @@ var inspectCommand = defineCommand80({
|
|
|
8302
8082
|
}
|
|
8303
8083
|
},
|
|
8304
8084
|
async run({ args }) {
|
|
8305
|
-
const outputsDir =
|
|
8085
|
+
const outputsDir = path.resolve(String(args["outputs-dir"] ?? "canvas"));
|
|
8306
8086
|
const runArg = String(args.run);
|
|
8307
8087
|
const runDir = await resolveRunDir(runArg, outputsDir);
|
|
8308
8088
|
const manifest = await loadManifest(runDir);
|
|
@@ -8314,7 +8094,7 @@ var inspectCommand = defineCommand80({
|
|
|
8314
8094
|
}
|
|
8315
8095
|
const summary = {
|
|
8316
8096
|
ok: true,
|
|
8317
|
-
run_id: manifest.run_id ??
|
|
8097
|
+
run_id: manifest.run_id ?? path.basename(runDir),
|
|
8318
8098
|
run_dir: runDir,
|
|
8319
8099
|
stats: manifest.stats ?? null,
|
|
8320
8100
|
output: manifest.output ?? null,
|
|
@@ -8327,20 +8107,20 @@ var inspectCommand = defineCommand80({
|
|
|
8327
8107
|
}
|
|
8328
8108
|
});
|
|
8329
8109
|
async function resolveRunDir(run, outputsDir) {
|
|
8330
|
-
if (
|
|
8110
|
+
if (path.isAbsolute(run)) {
|
|
8331
8111
|
const s2 = await stat(run).catch(() => null);
|
|
8332
8112
|
if (s2?.isDirectory()) return run;
|
|
8333
8113
|
throw new Error(`inspect: ${run} is not a directory`);
|
|
8334
8114
|
}
|
|
8335
|
-
const candidate =
|
|
8115
|
+
const candidate = path.join(outputsDir, run);
|
|
8336
8116
|
const s = await stat(candidate).catch(() => null);
|
|
8337
8117
|
if (s?.isDirectory()) return candidate;
|
|
8338
8118
|
throw new Error(`inspect: no run directory at ${candidate}`);
|
|
8339
8119
|
}
|
|
8340
8120
|
async function loadManifest(runDir) {
|
|
8341
|
-
const manifestPath =
|
|
8121
|
+
const manifestPath = path.join(runDir, "manifest.json");
|
|
8342
8122
|
try {
|
|
8343
|
-
const raw = await
|
|
8123
|
+
const raw = await readFile(manifestPath, "utf-8");
|
|
8344
8124
|
return JSON.parse(raw);
|
|
8345
8125
|
} catch {
|
|
8346
8126
|
return {};
|
|
@@ -8348,9 +8128,9 @@ async function loadManifest(runDir) {
|
|
|
8348
8128
|
}
|
|
8349
8129
|
async function listRunFiles(runDir) {
|
|
8350
8130
|
const out = [];
|
|
8351
|
-
const names = await
|
|
8131
|
+
const names = await readdir(runDir);
|
|
8352
8132
|
for (const name of names) {
|
|
8353
|
-
const abs =
|
|
8133
|
+
const abs = path.join(runDir, name);
|
|
8354
8134
|
const s = await stat(abs).catch(() => null);
|
|
8355
8135
|
if (!s?.isFile()) continue;
|
|
8356
8136
|
out.push({ name, path: abs, size: s.size });
|
|
@@ -8395,9 +8175,9 @@ async function probeDuration(filePath) {
|
|
|
8395
8175
|
}
|
|
8396
8176
|
|
|
8397
8177
|
// src/commands/canvas/run.ts
|
|
8398
|
-
import { readFile as
|
|
8399
|
-
import
|
|
8400
|
-
import { defineCommand as
|
|
8178
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
8179
|
+
import path4 from "path";
|
|
8180
|
+
import { defineCommand as defineCommand80 } from "citty";
|
|
8401
8181
|
|
|
8402
8182
|
// src/commands/canvas/placeholders.ts
|
|
8403
8183
|
function unsuppliedPlaceholderAssets(canvas) {
|
|
@@ -8415,19 +8195,74 @@ function unsuppliedPlaceholderAssets(canvas) {
|
|
|
8415
8195
|
return out;
|
|
8416
8196
|
}
|
|
8417
8197
|
|
|
8198
|
+
// src/commands/canvas/resolve-paths.ts
|
|
8199
|
+
import path2 from "path";
|
|
8200
|
+
function resolveRelativeCanvasPaths(canvas, baseDir) {
|
|
8201
|
+
if (!canvas || typeof canvas !== "object") return canvas;
|
|
8202
|
+
const c = canvas;
|
|
8203
|
+
if (!Array.isArray(c.nodes)) return canvas;
|
|
8204
|
+
return { ...canvas, nodes: c.nodes.map((n) => resolveNode(n, baseDir)) };
|
|
8205
|
+
}
|
|
8206
|
+
function resolveNode(node, baseDir) {
|
|
8207
|
+
if (!node || typeof node !== "object") return node;
|
|
8208
|
+
const n = node;
|
|
8209
|
+
const params = n.params;
|
|
8210
|
+
if (!params || typeof params !== "object") return node;
|
|
8211
|
+
if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
|
|
8212
|
+
return { ...node, params: { ...params, path: path2.resolve(baseDir, params.path) } };
|
|
8213
|
+
}
|
|
8214
|
+
if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
|
|
8215
|
+
return { ...node, params: { ...params, composition: path2.resolve(baseDir, params.composition) } };
|
|
8216
|
+
}
|
|
8217
|
+
return node;
|
|
8218
|
+
}
|
|
8219
|
+
function isResolvableRelative(value) {
|
|
8220
|
+
return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path2.isAbsolute(value);
|
|
8221
|
+
}
|
|
8222
|
+
|
|
8223
|
+
// src/commands/canvas/run-retention.ts
|
|
8224
|
+
import { rm } from "fs/promises";
|
|
8225
|
+
import path3 from "path";
|
|
8226
|
+
function runDirsToPrune(entries, keep, currentRunId) {
|
|
8227
|
+
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
8228
|
+
if (keep <= 0) return runs;
|
|
8229
|
+
return runs.slice(0, Math.max(0, runs.length - keep));
|
|
8230
|
+
}
|
|
8231
|
+
async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
8232
|
+
const { readdir: readdir3 } = await import("fs/promises");
|
|
8233
|
+
let entries;
|
|
8234
|
+
try {
|
|
8235
|
+
entries = await readdir3(outputsDir);
|
|
8236
|
+
} catch {
|
|
8237
|
+
return;
|
|
8238
|
+
}
|
|
8239
|
+
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
8240
|
+
if (toPrune.length === 0) return;
|
|
8241
|
+
for (const dir of toPrune) {
|
|
8242
|
+
await rm(path3.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
8243
|
+
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
8244
|
+
);
|
|
8245
|
+
}
|
|
8246
|
+
log(`[prune ] removed ${toPrune.length} old run dir(s), kept the ${keep} newest`);
|
|
8247
|
+
}
|
|
8248
|
+
|
|
8418
8249
|
// src/commands/canvas/run.ts
|
|
8419
|
-
var runCommand =
|
|
8250
|
+
var runCommand = defineCommand80({
|
|
8420
8251
|
meta: { name: "run", description: "Validate and execute a canvas JSON file." },
|
|
8421
8252
|
args: {
|
|
8422
8253
|
file: { type: "positional", required: true, description: "Path to canvas JSON" },
|
|
8423
8254
|
"cache-dir": { type: "string", description: "Cache root (default ./canvas/.cache)" },
|
|
8424
8255
|
"outputs-dir": { type: "string", description: "Per-run outputs root (default ./canvas)" },
|
|
8425
8256
|
"run-id": { type: "string", description: "Override run id" },
|
|
8426
|
-
"cache-policy": { type: "string", description: "read_write | bypass | read_only" }
|
|
8257
|
+
"cache-policy": { type: "string", description: "read_write | bypass | read_only" },
|
|
8258
|
+
"keep-runs": {
|
|
8259
|
+
type: "string",
|
|
8260
|
+
description: "After the run, prune old r_* run dirs, keeping the N newest (off by default)"
|
|
8261
|
+
}
|
|
8427
8262
|
},
|
|
8428
8263
|
async run({ args }) {
|
|
8429
|
-
const filePath =
|
|
8430
|
-
const raw = await
|
|
8264
|
+
const filePath = path4.resolve(String(args.file));
|
|
8265
|
+
const raw = await readFile2(filePath, "utf8");
|
|
8431
8266
|
let parsed;
|
|
8432
8267
|
try {
|
|
8433
8268
|
parsed = JSON.parse(raw);
|
|
@@ -8437,6 +8272,7 @@ var runCommand = defineCommand81({
|
|
|
8437
8272
|
`);
|
|
8438
8273
|
process.exit(2);
|
|
8439
8274
|
}
|
|
8275
|
+
parsed = resolveRelativeCanvasPaths(parsed, path4.dirname(filePath));
|
|
8440
8276
|
const pending = unsuppliedPlaceholderAssets(parsed);
|
|
8441
8277
|
if (pending.length > 0) {
|
|
8442
8278
|
process.stderr.write(
|
|
@@ -8468,6 +8304,12 @@ var runCommand = defineCommand81({
|
|
|
8468
8304
|
run_id: args["run-id"] ? String(args["run-id"]) : void 0,
|
|
8469
8305
|
cache_policy: policy
|
|
8470
8306
|
});
|
|
8307
|
+
const keepRuns = args["keep-runs"] !== void 0 ? Number(args["keep-runs"]) : void 0;
|
|
8308
|
+
if (keepRuns !== void 0 && Number.isFinite(keepRuns)) {
|
|
8309
|
+
const outputsDir = args["outputs-dir"] ? path4.resolve(String(args["outputs-dir"])) : path4.resolve("canvas");
|
|
8310
|
+
await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
|
|
8311
|
+
`));
|
|
8312
|
+
}
|
|
8471
8313
|
process.stdout.write(
|
|
8472
8314
|
`${JSON.stringify(
|
|
8473
8315
|
{
|
|
@@ -8499,9 +8341,9 @@ var runCommand = defineCommand81({
|
|
|
8499
8341
|
});
|
|
8500
8342
|
|
|
8501
8343
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
8502
|
-
import { readFile as
|
|
8503
|
-
import
|
|
8504
|
-
import { defineCommand as
|
|
8344
|
+
import { readFile as readFile3, writeFile } from "fs/promises";
|
|
8345
|
+
import path5 from "path";
|
|
8346
|
+
import { defineCommand as defineCommand81 } from "citty";
|
|
8505
8347
|
|
|
8506
8348
|
// src/engine/scaffold/staticAd.ts
|
|
8507
8349
|
import { z as z2 } from "zod";
|
|
@@ -8714,7 +8556,7 @@ var SELECT_SYSTEM = 'You identify the MAIN, identity-critical visual elements of
|
|
|
8714
8556
|
var SELECT_PROMPT = 'AD BLUEPRINT (from image_describe):\n{{blueprint}}\n\nFrom this blueprint, list ONLY the elements that are prominent, important, and identity-bearing \u2014 the ones a reproduction must ground in a real asset:\n- the brand logo/wordmark (from brands_logos with function_in_image = advertiser_brand) -> type "logo"\n- trust/rating/certification/app-store/review badges (brands_logos with function_in_image = trust_badge | review_platform | certification_or_seal | app_store_badge | payment_method) -> type "badge"\n- a showcased/hero product or package (a foreground entry in subjects that the ad is selling) -> type "product"\n- a foreground person whose identity matters (from people) -> type "person"\n- a foreground animal/character with a specific expression (from subjects) -> type "animal"\n\nDROP background extras, decorative props, generic scenery, and anything small or incidental. Keep at most ~6. If there are none, return an empty list.\n\nFor each kept element return: { "type": one of logo|product|person|animal|badge, "label": a short UPPER_SNAKE_CASE name (e.g. LOGO, PRODUCT, HERO_DOG, TRUSTPILOT), "description": a concrete reusable description to source/shoot the real asset (include the exact expression for a living subject), "expression": the facial expression for a living subject or null, "reason": why it is identity-critical, "locator": the blueprint entry this element came from as { "collection": one of "subjects" | "people" | "brands_logos", "index": its 0-based position in that array } (people -> people; logos/badges -> brands_logos; products/animals/objects -> subjects). Output ONLY the JSON object.';
|
|
8715
8557
|
async function loadAssetText(ref, label) {
|
|
8716
8558
|
const r = ref;
|
|
8717
|
-
if (typeof r?.path === "string") return
|
|
8559
|
+
if (typeof r?.path === "string") return readFile3(r.path, "utf8");
|
|
8718
8560
|
if (typeof r?.url === "string") {
|
|
8719
8561
|
const res = await fetch(r.url);
|
|
8720
8562
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -8820,7 +8662,7 @@ async function runVisionPasses(canvas) {
|
|
|
8820
8662
|
return fail("read_outputs", e instanceof Error ? e.message : String(e));
|
|
8821
8663
|
}
|
|
8822
8664
|
}
|
|
8823
|
-
var scaffoldStaticAdCommand =
|
|
8665
|
+
var scaffoldStaticAdCommand = defineCommand81({
|
|
8824
8666
|
meta: {
|
|
8825
8667
|
name: "scaffold-static-ad",
|
|
8826
8668
|
description: "Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes \u2014 image_describe (the blueprint, baked to prompt.json as the editable 'prompt'), an AI selection of the image's MAIN identity elements, and a structured global-layout pass (the column/row grid with per-region bounds and text sizes) \u2014 then scaffolds a canvas that wires one [TODO] ingest slot per element (logo/product/subject/badge + brand font) into image_generate. Edit prompt.json and drop the real assets, then `baker canvas run` it."
|
|
@@ -8837,10 +8679,10 @@ var scaffoldStaticAdCommand = defineCommand82({
|
|
|
8837
8679
|
"skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" }
|
|
8838
8680
|
},
|
|
8839
8681
|
async run({ args }) {
|
|
8840
|
-
const imagePath =
|
|
8841
|
-
const outPath = args.out ?
|
|
8842
|
-
const outDir =
|
|
8843
|
-
const blueprintPath =
|
|
8682
|
+
const imagePath = path5.resolve(String(args.file));
|
|
8683
|
+
const outPath = args.out ? path5.resolve(String(args.out)) : path5.join(path5.dirname(imagePath), "static-ad.canvas.json");
|
|
8684
|
+
const outDir = path5.dirname(outPath);
|
|
8685
|
+
const blueprintPath = path5.join(outDir, "prompt.json");
|
|
8844
8686
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
|
|
8845
8687
|
const describeCanvas = buildDescribeCanvas(
|
|
8846
8688
|
imagePath,
|
|
@@ -8897,7 +8739,7 @@ var scaffoldStaticAdCommand = defineCommand82({
|
|
|
8897
8739
|
run_estimated_credits: validation.estimatedCredits
|
|
8898
8740
|
},
|
|
8899
8741
|
checklist: {
|
|
8900
|
-
edit_prompt: `Edit ${
|
|
8742
|
+
edit_prompt: `Edit ${path5.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.`,
|
|
8901
8743
|
assets_to_supply: report.elements,
|
|
8902
8744
|
font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path, or delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
|
|
8903
8745
|
note: "Replace every [TODO] ingest path with a real file, then `baker canvas validate` and `baker canvas run`. Running generates a billed image \u2014 it is not free."
|
|
@@ -8913,12 +8755,12 @@ var scaffoldStaticAdCommand = defineCommand82({
|
|
|
8913
8755
|
|
|
8914
8756
|
// src/commands/canvas/scaffold-video.ts
|
|
8915
8757
|
import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
|
|
8916
|
-
import
|
|
8917
|
-
import { defineCommand as
|
|
8758
|
+
import path8 from "path";
|
|
8759
|
+
import { defineCommand as defineCommand82 } from "citty";
|
|
8918
8760
|
|
|
8919
8761
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
8920
8762
|
import { execFile as execFile2 } from "child_process";
|
|
8921
|
-
import { mkdtemp, readdir as
|
|
8763
|
+
import { mkdtemp, readdir as readdir2, readFile as readFile4, rm as rm2 } from "fs/promises";
|
|
8922
8764
|
import { tmpdir } from "os";
|
|
8923
8765
|
import { join as join2 } from "path";
|
|
8924
8766
|
import { promisify as promisify2 } from "util";
|
|
@@ -8982,11 +8824,11 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
8982
8824
|
],
|
|
8983
8825
|
{ encoding: "utf-8", maxBuffer: 32 * 1024 * 1024, timeout: timeoutMs }
|
|
8984
8826
|
);
|
|
8985
|
-
const csvName = (await
|
|
8827
|
+
const csvName = (await readdir2(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
8986
8828
|
if (!csvName) return [];
|
|
8987
|
-
return parsePySceneDetectCsvCuts(await
|
|
8829
|
+
return parsePySceneDetectCsvCuts(await readFile4(join2(outDir, csvName), "utf-8"));
|
|
8988
8830
|
} finally {
|
|
8989
|
-
await
|
|
8831
|
+
await rm2(outDir, { recursive: true, force: true });
|
|
8990
8832
|
}
|
|
8991
8833
|
}
|
|
8992
8834
|
async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
@@ -9242,6 +9084,13 @@ function stillHoldArgs(durationS, dims) {
|
|
|
9242
9084
|
`scale=${dims.w}:${dims.h}:force_original_aspect_ratio=increase,crop=${dims.w}:${dims.h},setsar=1,format=yuv420p`,
|
|
9243
9085
|
"-c:v",
|
|
9244
9086
|
"libx264",
|
|
9087
|
+
// Near-visually-lossless re-encode. libx264 DEFAULTS (crf 23, preset medium)
|
|
9088
|
+
// roughly halve the source bitrate and add banding on smooth surfaces; the
|
|
9089
|
+
// spine concats these by stream-copy, so any loss here ships to the final cut.
|
|
9090
|
+
"-crf",
|
|
9091
|
+
"18",
|
|
9092
|
+
"-preset",
|
|
9093
|
+
"slow",
|
|
9245
9094
|
"-pix_fmt",
|
|
9246
9095
|
"yuv420p",
|
|
9247
9096
|
"{{out.video}}"
|
|
@@ -9257,6 +9106,13 @@ function trimArgs(durationS, offsetS = 0) {
|
|
|
9257
9106
|
"-an",
|
|
9258
9107
|
"-c:v",
|
|
9259
9108
|
"libx264",
|
|
9109
|
+
// Preserve the seedance source quality through the trim. libx264 DEFAULTS
|
|
9110
|
+
// (crf 23) halve the bitrate (measured 9.57→4.40 Mbps) and band on motion;
|
|
9111
|
+
// the spine stream-copies the result, so the loss is permanent without this.
|
|
9112
|
+
"-crf",
|
|
9113
|
+
"18",
|
|
9114
|
+
"-preset",
|
|
9115
|
+
"slow",
|
|
9260
9116
|
"-pix_fmt",
|
|
9261
9117
|
"yuv420p",
|
|
9262
9118
|
"{{out.video}}"
|
|
@@ -9323,6 +9179,13 @@ var Scene = z3.object({
|
|
|
9323
9179
|
// The scene's role in the ad's persuasion arc (DECON-supplied); drives the
|
|
9324
9180
|
// script re-craft checklist. Inferred from position when absent.
|
|
9325
9181
|
narrative_role: z3.string().optional(),
|
|
9182
|
+
// DECON-supplied on the HOOK scene: the engineered physical/emotional state that
|
|
9183
|
+
// makes the first frame stop the scroll (sweaty/breathless/urgent …). Injected
|
|
9184
|
+
// into the hook's start-frame description so the generator renders that state,
|
|
9185
|
+
// not a calm influencer (CCA-11).
|
|
9186
|
+
hook_mechanic: z3.object({ mechanic: z3.string().optional(), why_it_stops_scroll: z3.string().optional() }).loose().optional(),
|
|
9187
|
+
// DECON-supplied per-scene location (so a gym hook isn't flattened to "home").
|
|
9188
|
+
scene_setting: z3.string().optional(),
|
|
9326
9189
|
// How this scene cuts to the next (DECON-supplied). A recognized non-cut type
|
|
9327
9190
|
// (fade/whip/zoom/dissolve/swipe) is reproduced as an ffmpeg xfade at the
|
|
9328
9191
|
// boundary; cut/match_cut/none/other stay hard cuts. The last scene's value is
|
|
@@ -9370,10 +9233,21 @@ var VideoBlueprint = z3.object({
|
|
|
9370
9233
|
mode: z3.string().optional(),
|
|
9371
9234
|
voice_description: z3.string().optional(),
|
|
9372
9235
|
persona: z3.string().optional()
|
|
9373
|
-
}).loose().optional()
|
|
9236
|
+
}).loose().optional(),
|
|
9237
|
+
// Visual palette — read only to colour a clean brand-card/CTA plate (the
|
|
9238
|
+
// first hex is the dominant brand colour); never to drive frame generation.
|
|
9239
|
+
style: z3.object({ palette: z3.array(z3.object({ hex: z3.string().optional() }).loose()).optional() }).loose().optional()
|
|
9374
9240
|
}).loose().optional(),
|
|
9375
9241
|
scenes: z3.array(Scene).min(1)
|
|
9376
9242
|
}).loose();
|
|
9243
|
+
function injectHookPhysicality(blueprint) {
|
|
9244
|
+
for (const scene of blueprint.scenes) {
|
|
9245
|
+
const why = scene.hook_mechanic?.why_it_stops_scroll?.trim();
|
|
9246
|
+
const prompt = scene.start_frame_prompt?.trim();
|
|
9247
|
+
if (!why || !prompt || prompt.includes(why)) continue;
|
|
9248
|
+
scene.start_frame_prompt = `${prompt} The subject's physical state IS the scroll-stopper \u2014 render it explicitly, not a calm pose: ${why}.`;
|
|
9249
|
+
}
|
|
9250
|
+
}
|
|
9377
9251
|
var AppearsItem = z3.union([z3.number(), z3.object({ scene: z3.number(), edge: z3.string().optional() }).loose()]);
|
|
9378
9252
|
var RecurringElement = z3.object({
|
|
9379
9253
|
// person | animal | product | logo | badge | other
|
|
@@ -9399,7 +9273,8 @@ function sanitizeId2(raw, fallback) {
|
|
|
9399
9273
|
return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
|
|
9400
9274
|
}
|
|
9401
9275
|
function labelFor2(el, used) {
|
|
9402
|
-
const
|
|
9276
|
+
const raw = el.type?.toLowerCase() === "logo" ? "BRAND_LOGO" : el.label ?? el.type ?? "ELEMENT";
|
|
9277
|
+
const base = raw.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "ELEMENT";
|
|
9403
9278
|
let label = base;
|
|
9404
9279
|
let n = 2;
|
|
9405
9280
|
while (used.has(label)) label = `${base}_${n++}`;
|
|
@@ -9566,6 +9441,8 @@ function buildElementSheets(slots, nodes) {
|
|
|
9566
9441
|
if (slot.sameAs) continue;
|
|
9567
9442
|
if (slot.presence.size < 1) continue;
|
|
9568
9443
|
const sheetId = `${slot.id}_sheet`;
|
|
9444
|
+
const slotType = slot.type.toLowerCase();
|
|
9445
|
+
const isCast = slotType === "person" || slotType === "animal";
|
|
9569
9446
|
nodes.push({
|
|
9570
9447
|
id: sheetId,
|
|
9571
9448
|
type: "image_reference_sheet",
|
|
@@ -9578,7 +9455,14 @@ function buildElementSheets(slots, nodes) {
|
|
|
9578
9455
|
// 4K: the sheet packs up to 8 cells (angles + tight face/detail close-ups), and
|
|
9579
9456
|
// it's the ONE reference every frame grounds on — per-cell sharpness here
|
|
9580
9457
|
// propagates to every clip, so it's worth the highest tier on this single asset.
|
|
9581
|
-
image_size: "4K"
|
|
9458
|
+
image_size: "4K",
|
|
9459
|
+
// The sheet is the look that propagates to EVERY grounded frame, so a glossy
|
|
9460
|
+
// studio turnaround makes the whole UGC ad read as "produced" (the #1 AI tell).
|
|
9461
|
+
// Force a flat, real, front-camera look on the cast sheet so the actor stays
|
|
9462
|
+
// authentic, not an airbrushed influencer (CCA-02).
|
|
9463
|
+
...isCast ? {
|
|
9464
|
+
style: "authentic UGC look: flat, even, natural front-camera lighting \u2014 no studio key/rim light, no seamless backdrop, no shallow depth of field; real skin texture and pores, no airbrushing or beauty retouch; true-to-life everyday styling"
|
|
9465
|
+
} : {}
|
|
9582
9466
|
}
|
|
9583
9467
|
});
|
|
9584
9468
|
slot.ref = `$ref:${sheetId}.sheet`;
|
|
@@ -9686,8 +9570,7 @@ function buildFrameRef(edge, url, framePrompt, present, ctx, nodes) {
|
|
|
9686
9570
|
const t = s.type.toLowerCase();
|
|
9687
9571
|
return t === "person" || t === "animal";
|
|
9688
9572
|
});
|
|
9689
|
-
const
|
|
9690
|
-
const useOriginalAnchor = Boolean(url) && (castSlots.length === 0 || castIdentityLocked);
|
|
9573
|
+
const useOriginalAnchor = Boolean(url) && castSlots.length === 0;
|
|
9691
9574
|
const hasOriginal = useOriginalAnchor;
|
|
9692
9575
|
const originalRef = useOriginalAnchor && url ? ingestFrameRef(url, edge, ctx, nodes) : void 0;
|
|
9693
9576
|
const reference = [...present.map((s) => s.ref), ...originalRef ? [originalRef] : []];
|
|
@@ -9898,6 +9781,39 @@ function isUiOnlyComposite(regions) {
|
|
|
9898
9781
|
const ui = regions.filter(regionIsUiSurface).length;
|
|
9899
9782
|
return ui >= 1 && regions.length - ui <= 1;
|
|
9900
9783
|
}
|
|
9784
|
+
function sceneIsFullScreenUi(scene, present) {
|
|
9785
|
+
if (scene.narrative_role?.trim() === "cta") return false;
|
|
9786
|
+
const hasCast = present.some((s) => {
|
|
9787
|
+
const t = s.type.toLowerCase();
|
|
9788
|
+
return t === "person" || t === "animal";
|
|
9789
|
+
});
|
|
9790
|
+
if (hasCast) return false;
|
|
9791
|
+
const hay = `${scene.summary ?? ""} ${scene.start_frame_prompt ?? ""} ${scene.end_frame_prompt ?? ""} ${scene.action_detail ?? ""}`;
|
|
9792
|
+
return UI_SURFACE_RE.test(hay);
|
|
9793
|
+
}
|
|
9794
|
+
function screenStillArgs(durationS, dims) {
|
|
9795
|
+
return [
|
|
9796
|
+
"-loop",
|
|
9797
|
+
"1",
|
|
9798
|
+
"-i",
|
|
9799
|
+
"{{in.frame}}",
|
|
9800
|
+
"-t",
|
|
9801
|
+
durationS.toFixed(3),
|
|
9802
|
+
"-r",
|
|
9803
|
+
"30",
|
|
9804
|
+
"-vf",
|
|
9805
|
+
`scale=${dims.w}:${dims.h}:force_original_aspect_ratio=decrease,pad=${dims.w}:${dims.h}:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1,format=yuv420p`,
|
|
9806
|
+
"-c:v",
|
|
9807
|
+
"libx264",
|
|
9808
|
+
"-crf",
|
|
9809
|
+
"18",
|
|
9810
|
+
"-preset",
|
|
9811
|
+
"slow",
|
|
9812
|
+
"-pix_fmt",
|
|
9813
|
+
"yuv420p",
|
|
9814
|
+
"{{out.video}}"
|
|
9815
|
+
];
|
|
9816
|
+
}
|
|
9901
9817
|
function layeredComposition(scene) {
|
|
9902
9818
|
const comp = scene.composition;
|
|
9903
9819
|
const layout = (comp?.layout ?? "").toLowerCase();
|
|
@@ -10068,6 +9984,77 @@ function emitFlashHold(i, scene, slots, ctx, lengths, out, ar, nodes, clips) {
|
|
|
10068
9984
|
});
|
|
10069
9985
|
clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
|
|
10070
9986
|
}
|
|
9987
|
+
function emitScreenScene(i, scene, lengths, out, ar, nodes, clips) {
|
|
9988
|
+
const label = commentSafe((scene.summary || scene.start_frame_prompt || "the app screen").slice(0, 120));
|
|
9989
|
+
const refId = `s${i}_screen_ref`;
|
|
9990
|
+
nodes.push({
|
|
9991
|
+
id: refId,
|
|
9992
|
+
type: "ingest",
|
|
9993
|
+
params: {
|
|
9994
|
+
source: "path",
|
|
9995
|
+
path: `[TODO: supply the REAL screen for "${label}" \u2014 NEVER AI-generate a UI. Capture a clean, text-free screenshot with \`baker images screenshot https://<brand-domain>/<path>\` (image-library skill); spoken/overlay text rides the overlay layer, not the screenshot]`,
|
|
9996
|
+
expect: "image"
|
|
9997
|
+
}
|
|
9998
|
+
});
|
|
9999
|
+
nodes.push({
|
|
10000
|
+
id: `s${i}_clip`,
|
|
10001
|
+
type: "ffmpeg",
|
|
10002
|
+
inputs: { frame: `$ref:${refId}.asset` },
|
|
10003
|
+
params: {
|
|
10004
|
+
args: screenStillArgs(lengths.trimTarget, canvasDims(ar)),
|
|
10005
|
+
outputs: { video: { kind: "video", ext: "mp4" } }
|
|
10006
|
+
}
|
|
10007
|
+
});
|
|
10008
|
+
clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
|
|
10009
|
+
}
|
|
10010
|
+
var BRAND_CARD_RE = /\b(?:solid|plain|flat|brand|logo|wordmark|end[- ]?card|cta card|title card|colou?r background|background colou?r)\b/i;
|
|
10011
|
+
function sceneIsBrandCard(scene, present, isCta) {
|
|
10012
|
+
if (!isCta) return false;
|
|
10013
|
+
const hasCast = present.some((s) => {
|
|
10014
|
+
const t = s.type.toLowerCase();
|
|
10015
|
+
return t === "person" || t === "animal";
|
|
10016
|
+
});
|
|
10017
|
+
if (hasCast) return false;
|
|
10018
|
+
const hay = `${scene.summary ?? ""} ${scene.start_frame_prompt ?? ""} ${scene.end_frame_prompt ?? ""}`;
|
|
10019
|
+
return BRAND_CARD_RE.test(hay);
|
|
10020
|
+
}
|
|
10021
|
+
var HEX6_RE = /^#?[0-9a-fA-F]{6}$/;
|
|
10022
|
+
function brandPlateColor(blueprint) {
|
|
10023
|
+
const palette = blueprint.global?.style?.palette;
|
|
10024
|
+
const hex = palette?.map((p) => p?.hex).find((h) => typeof h === "string" && HEX6_RE.test(h));
|
|
10025
|
+
return hex ? `0x${hex.replace(/^#/, "").toUpperCase()}` : "0x000000";
|
|
10026
|
+
}
|
|
10027
|
+
function colorPlateArgs(durationS, dims, color) {
|
|
10028
|
+
return [
|
|
10029
|
+
"-f",
|
|
10030
|
+
"lavfi",
|
|
10031
|
+
"-i",
|
|
10032
|
+
`color=c=${color}:s=${dims.w}x${dims.h}:r=30`,
|
|
10033
|
+
"-t",
|
|
10034
|
+
durationS.toFixed(3),
|
|
10035
|
+
"-c:v",
|
|
10036
|
+
"libx264",
|
|
10037
|
+
"-crf",
|
|
10038
|
+
"18",
|
|
10039
|
+
"-preset",
|
|
10040
|
+
"slow",
|
|
10041
|
+
"-pix_fmt",
|
|
10042
|
+
"yuv420p",
|
|
10043
|
+
"{{out.video}}"
|
|
10044
|
+
];
|
|
10045
|
+
}
|
|
10046
|
+
function emitBrandCardScene(i, lengths, out, ar, color, nodes, clips) {
|
|
10047
|
+
nodes.push({
|
|
10048
|
+
id: `s${i}_clip`,
|
|
10049
|
+
type: "ffmpeg",
|
|
10050
|
+
inputs: {},
|
|
10051
|
+
params: {
|
|
10052
|
+
args: colorPlateArgs(lengths.trimTarget, canvasDims(ar), color),
|
|
10053
|
+
outputs: { video: { kind: "video", ext: "mp4" } }
|
|
10054
|
+
}
|
|
10055
|
+
});
|
|
10056
|
+
clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
|
|
10057
|
+
}
|
|
10071
10058
|
function musicArcDigest(blueprint) {
|
|
10072
10059
|
const roles = blueprint.scenes.map((s) => s.narrative_role).filter((r) => Boolean(r));
|
|
10073
10060
|
const arc = roles.length > 0 ? roles.join(" \u2192 ") : "";
|
|
@@ -10187,7 +10174,7 @@ function makePresenterPresent(slots, canonical, opts = {}) {
|
|
|
10187
10174
|
const solePerson = !opts.strict && personSlots.length === 1 ? personSlots[0].presence : null;
|
|
10188
10175
|
return (speaker, sceneIndex) => {
|
|
10189
10176
|
const presence = bySpeaker.get(speaker) ?? solePerson;
|
|
10190
|
-
if (!presence) return opts.strict
|
|
10177
|
+
if (!presence) return !opts.strict;
|
|
10191
10178
|
return presence.has(sceneIndex);
|
|
10192
10179
|
};
|
|
10193
10180
|
}
|
|
@@ -10552,6 +10539,15 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
|
|
|
10552
10539
|
shootMode: mode,
|
|
10553
10540
|
ingestCache: env.ingestCache
|
|
10554
10541
|
};
|
|
10542
|
+
if (!env.reuse && sceneIsFullScreenUi(scene, present)) {
|
|
10543
|
+
emitScreenScene(i, scene, lengths, lengths.out, env.ar, nodes, out.clips);
|
|
10544
|
+
return void 0;
|
|
10545
|
+
}
|
|
10546
|
+
const isCta = scene.narrative_role?.trim() === "cta" || isLast;
|
|
10547
|
+
if (!env.reuse && sceneIsBrandCard(scene, present, isCta)) {
|
|
10548
|
+
emitBrandCardScene(i, lengths, lengths.out, env.ar, brandPlateColor(env.blueprint), nodes, out.clips);
|
|
10549
|
+
return void 0;
|
|
10550
|
+
}
|
|
10555
10551
|
if (!ambientBroll && lengths.dur <= FLASH_HOLD_MAX_S) {
|
|
10556
10552
|
emitFlashHold(i, scene, env.slots, ctx, lengths, lengths.out, env.ar, nodes, out.clips);
|
|
10557
10553
|
return void 0;
|
|
@@ -10817,7 +10813,7 @@ function overlayElement(ov, at, dur) {
|
|
|
10817
10813
|
const normAnim = normalizeAnim(ov.animation);
|
|
10818
10814
|
const anim = normAnim ? ` data-anim="${normAnim}"` : "";
|
|
10819
10815
|
const detail = ov.animation_detail ? ` data-anim-detail="${escapeHtml(ov.animation_detail)}"` : "";
|
|
10820
|
-
return `<div class="ov ${positionClass(ov.position)}" data-start="${at}" data-dur="${dur}"${role}${anim}${detail}>${escapeHtml(ov.text.trim())}</div>`;
|
|
10816
|
+
return `<div class="ov clip ${positionClass(ov.position)}" data-start="${at}" data-dur="${dur}"${role}${anim}${detail}>${escapeHtml(ov.text.trim())}</div>`;
|
|
10821
10817
|
}
|
|
10822
10818
|
var RICH_OVERLAY_RE = /notif|tweet|\bx post\b|post\b|comment|message|chat|bubble|card|review|rating|stat|counter|toast|popup/;
|
|
10823
10819
|
function sourceHint(fe) {
|
|
@@ -10847,7 +10843,7 @@ function floatingStub(fe, sceneStart) {
|
|
|
10847
10843
|
const slug = (fe.kind ?? "element").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "element";
|
|
10848
10844
|
return [
|
|
10849
10845
|
`<!-- ${kind}: ${label} @ ${at}s for ${dur}s (${positionClass(fe.position)}). Source a real asset: ${hint} \u2014 drop it in this dir and uncomment:`,
|
|
10850
|
-
`<img class="ov ${positionClass(fe.position)}" src="your-${slug}.png" data-start="${at}" data-dur="${dur}" alt="" /> -->`
|
|
10846
|
+
`<img class="ov clip ${positionClass(fe.position)}" src="your-${slug}.png" data-start="${at}" data-dur="${dur}" alt="" /> -->`
|
|
10851
10847
|
].join("\n");
|
|
10852
10848
|
}
|
|
10853
10849
|
function uiPipStub(scene) {
|
|
@@ -10867,7 +10863,7 @@ function uiPipStub(scene) {
|
|
|
10867
10863
|
" \u2014 OR hand-build a brand-accurate HTML screen; then frame it in a phone mockup:",
|
|
10868
10864
|
" npx hyperframes add phone-scroll (writes compositions/phone-scroll.html)",
|
|
10869
10865
|
" drop the screenshot as screenshot.png in this dir and nest it as a PIP clip:",
|
|
10870
|
-
` <div data-composition-src="compositions/phone-scroll.html" data-start="${at}" data-duration="${dur}" data-track-index="2" data-width="1080" data-height="1920"></div> -->`
|
|
10866
|
+
` <div class="clip" data-composition-src="compositions/phone-scroll.html" data-start="${at}" data-duration="${dur}" data-track-index="2" data-width="1080" data-height="1920"></div> -->`
|
|
10871
10867
|
].join("\n");
|
|
10872
10868
|
}
|
|
10873
10869
|
function buildOverlayHtml(input) {
|
|
@@ -10963,6 +10959,7 @@ function buildSpine(clips, nodes) {
|
|
|
10963
10959
|
}
|
|
10964
10960
|
function scaffoldVideoCanvas(input, elementsInput, opts) {
|
|
10965
10961
|
const blueprint = VideoBlueprint.parse(input);
|
|
10962
|
+
injectHookPhysicality(blueprint);
|
|
10966
10963
|
const elements = RecurringElements.parse(elementsInput);
|
|
10967
10964
|
const nodes = [];
|
|
10968
10965
|
nodes.push({
|
|
@@ -11374,18 +11371,44 @@ function videoReport(input, elementsInput) {
|
|
|
11374
11371
|
|
|
11375
11372
|
// src/commands/canvas/composition-path.ts
|
|
11376
11373
|
import { existsSync as existsSync3 } from "fs";
|
|
11377
|
-
import
|
|
11374
|
+
import path6 from "path";
|
|
11378
11375
|
function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
|
|
11379
|
-
const rel =
|
|
11376
|
+
const rel = path6.join("canvas", name);
|
|
11380
11377
|
let dir = startDir;
|
|
11381
11378
|
for (let i = 0; i < maxDepth; i++) {
|
|
11382
|
-
const candidate =
|
|
11383
|
-
if (exists(
|
|
11384
|
-
const parent =
|
|
11379
|
+
const candidate = path6.join(dir, rel);
|
|
11380
|
+
if (exists(path6.join(candidate, "meta.json"))) return candidate;
|
|
11381
|
+
const parent = path6.dirname(dir);
|
|
11385
11382
|
if (parent === dir) break;
|
|
11386
11383
|
dir = parent;
|
|
11387
11384
|
}
|
|
11388
|
-
return
|
|
11385
|
+
return path6.resolve(startDir, "../../../", rel);
|
|
11386
|
+
}
|
|
11387
|
+
|
|
11388
|
+
// src/commands/canvas/gitignore.ts
|
|
11389
|
+
import { appendFile, readFile as readFile5 } from "fs/promises";
|
|
11390
|
+
import path7 from "path";
|
|
11391
|
+
function missingGitignoreEntries(existing, entries) {
|
|
11392
|
+
const present = new Set(
|
|
11393
|
+
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
11394
|
+
);
|
|
11395
|
+
return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
|
|
11396
|
+
}
|
|
11397
|
+
async function ensureGitignore(dir, entries) {
|
|
11398
|
+
const file = path7.join(dir, ".gitignore");
|
|
11399
|
+
let existing;
|
|
11400
|
+
try {
|
|
11401
|
+
existing = await readFile5(file, "utf8");
|
|
11402
|
+
} catch {
|
|
11403
|
+
return;
|
|
11404
|
+
}
|
|
11405
|
+
const missing = missingGitignoreEntries(existing, entries);
|
|
11406
|
+
if (missing.length === 0) return;
|
|
11407
|
+
const prefix = existing.endsWith("\n") || existing.length === 0 ? "" : "\n";
|
|
11408
|
+
await appendFile(file, `${prefix}
|
|
11409
|
+
# Baker canvas (engine cache + scaffold working files)
|
|
11410
|
+
${missing.join("\n")}
|
|
11411
|
+
`);
|
|
11389
11412
|
}
|
|
11390
11413
|
|
|
11391
11414
|
// src/commands/canvas/scaffold-video.ts
|
|
@@ -11433,7 +11456,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
11433
11456
|
async function stageCaptions(outDir, transcript) {
|
|
11434
11457
|
const text = transcript?.trim();
|
|
11435
11458
|
if (!text || text === "[]") return {};
|
|
11436
|
-
const compositionPath =
|
|
11459
|
+
const compositionPath = path8.join(outDir, "tiktok-captions-composition");
|
|
11437
11460
|
await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
11438
11461
|
return { compositionPath };
|
|
11439
11462
|
}
|
|
@@ -11565,7 +11588,7 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
|
|
|
11565
11588
|
return fail2("deconstruct", e instanceof Error ? e.message : String(e));
|
|
11566
11589
|
}
|
|
11567
11590
|
}
|
|
11568
|
-
var scaffoldVideoCommand =
|
|
11591
|
+
var scaffoldVideoCommand = defineCommand82({
|
|
11569
11592
|
meta: {
|
|
11570
11593
|
name: "scaffold-video",
|
|
11571
11594
|
description: "Turn a reference video into a runnable reproduction canvas in one command. Runs billed passes \u2014 video_deconstruct (the full scene-by-scene blueprint + transcript, baked to prompt.json as the editable 'prompt') and an AI selection of the video's RECURRING identity elements (person/animal/product/logo) \u2014 then scaffolds a pipeline where every scene boundary is a static-ad-grade frame (the blueprint as target_blueprint, a reference legend, the real frame as anchor) and each recurring element gets ONE shared [TODO] ingest slot wired into every frame it appears in. The clips feed Seedance an ultra-detailed motion brief (action, camera, dialogue, transcript). Edit prompt.json, drop the real source images, then `baker canvas run`."
|
|
@@ -11595,11 +11618,11 @@ var scaffoldVideoCommand = defineCommand83({
|
|
|
11595
11618
|
}
|
|
11596
11619
|
},
|
|
11597
11620
|
async run({ args }) {
|
|
11598
|
-
const videoPath =
|
|
11599
|
-
const base =
|
|
11600
|
-
const outPath = args.out ?
|
|
11601
|
-
const outDir =
|
|
11602
|
-
const blueprintPath =
|
|
11621
|
+
const videoPath = path8.resolve(String(args.file));
|
|
11622
|
+
const base = path8.basename(videoPath, path8.extname(videoPath));
|
|
11623
|
+
const outPath = args.out ? path8.resolve(String(args.out)) : path8.join(path8.dirname(videoPath), `${base}.video.canvas.json`);
|
|
11624
|
+
const outDir = path8.dirname(outPath);
|
|
11625
|
+
const blueprintPath = path8.join(outDir, "prompt.json");
|
|
11603
11626
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
11604
11627
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
11605
11628
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -11622,9 +11645,9 @@ var scaffoldVideoCommand = defineCommand83({
|
|
|
11622
11645
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
11623
11646
|
await writeFile2(blueprintPath, `${JSON.stringify(annotated, null, 2)}
|
|
11624
11647
|
`, "utf8");
|
|
11625
|
-
const compositionDest =
|
|
11648
|
+
const compositionDest = path8.join(outDir, "video-overlay-composition");
|
|
11626
11649
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
11627
|
-
const indexPath =
|
|
11650
|
+
const indexPath = path8.join(compositionDest, "index.html");
|
|
11628
11651
|
const overlayHtml = buildOverlayHtml(blueprint);
|
|
11629
11652
|
const indexHtml = await readFile6(indexPath, "utf8");
|
|
11630
11653
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
@@ -11639,9 +11662,9 @@ var scaffoldVideoCommand = defineCommand83({
|
|
|
11639
11662
|
const opts = {
|
|
11640
11663
|
imageModel,
|
|
11641
11664
|
videoModel,
|
|
11642
|
-
overlayCompositionPath: compositionDest,
|
|
11643
|
-
captionsCompositionPath: captions.compositionPath,
|
|
11644
|
-
blueprintPath,
|
|
11665
|
+
overlayCompositionPath: path8.relative(outDir, compositionDest),
|
|
11666
|
+
captionsCompositionPath: captions.compositionPath ? path8.relative(outDir, captions.compositionPath) : void 0,
|
|
11667
|
+
blueprintPath: path8.relative(outDir, blueprintPath),
|
|
11645
11668
|
frames,
|
|
11646
11669
|
ambient: Boolean(args.ambient),
|
|
11647
11670
|
...args.resolution ? { resolution: String(args.resolution) } : {}
|
|
@@ -11654,7 +11677,7 @@ var scaffoldVideoCommand = defineCommand83({
|
|
|
11654
11677
|
} catch (e) {
|
|
11655
11678
|
return fail2("scaffold", e instanceof Error ? e.message : String(e));
|
|
11656
11679
|
}
|
|
11657
|
-
const validation = await validateCanvasDeep(canvas, defaultRegistry());
|
|
11680
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(canvas, outDir), defaultRegistry());
|
|
11658
11681
|
if (!validation.ok) {
|
|
11659
11682
|
process.stderr.write(
|
|
11660
11683
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
@@ -11664,6 +11687,7 @@ var scaffoldVideoCommand = defineCommand83({
|
|
|
11664
11687
|
}
|
|
11665
11688
|
await writeFile2(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
11666
11689
|
`, "utf8");
|
|
11690
|
+
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
11667
11691
|
process.stdout.write(
|
|
11668
11692
|
`${JSON.stringify(
|
|
11669
11693
|
{
|
|
@@ -11681,7 +11705,7 @@ var scaffoldVideoCommand = defineCommand83({
|
|
|
11681
11705
|
run_estimated_credits: validation.estimatedCredits
|
|
11682
11706
|
},
|
|
11683
11707
|
checklist: {
|
|
11684
|
-
edit_prompt: `Edit ${
|
|
11708
|
+
edit_prompt: `Edit ${path8.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
|
|
11685
11709
|
recurring_elements_to_supply: report.elements,
|
|
11686
11710
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
11687
11711
|
scene: d.scene,
|
|
@@ -11706,9 +11730,84 @@ var scaffoldVideoCommand = defineCommand83({
|
|
|
11706
11730
|
}
|
|
11707
11731
|
});
|
|
11708
11732
|
|
|
11733
|
+
// src/commands/canvas/set-prompt.ts
|
|
11734
|
+
import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
|
|
11735
|
+
import path9 from "path";
|
|
11736
|
+
import { defineCommand as defineCommand83 } from "citty";
|
|
11737
|
+
function setNodePrompt(canvas, nodeId, text) {
|
|
11738
|
+
const nodes = canvas?.nodes;
|
|
11739
|
+
if (!Array.isArray(nodes)) throw new Error("canvas has no nodes array");
|
|
11740
|
+
const idx = nodes.findIndex((n) => n?.id === nodeId);
|
|
11741
|
+
if (idx < 0) {
|
|
11742
|
+
const ids = nodes.map((n) => n?.id).filter((id) => typeof id === "string");
|
|
11743
|
+
throw new Error(`node "${nodeId}" not found. Known nodes: ${ids.join(", ")}`);
|
|
11744
|
+
}
|
|
11745
|
+
const node = nodes[idx];
|
|
11746
|
+
const newNode = { ...node, params: { ...node.params ?? {}, prompt: text } };
|
|
11747
|
+
const newNodes = [...nodes];
|
|
11748
|
+
newNodes[idx] = newNode;
|
|
11749
|
+
return { ...canvas, nodes: newNodes };
|
|
11750
|
+
}
|
|
11751
|
+
var setPromptCommand = defineCommand83({
|
|
11752
|
+
meta: {
|
|
11753
|
+
name: "set-prompt",
|
|
11754
|
+
description: "Safely set a node's params.prompt (a frame description, motion prompt, etc.) without hand-editing the JSON. Prefer --text-file for multi-line/accented copy \u2014 it preserves UTF-8 exactly, unlike shell-quoted jq."
|
|
11755
|
+
},
|
|
11756
|
+
args: {
|
|
11757
|
+
file: { type: "positional", required: true, description: "Path to canvas JSON" },
|
|
11758
|
+
node: { type: "positional", required: true, description: "Node id to edit (e.g. s0_start)" },
|
|
11759
|
+
text: { type: "string", description: "New prompt text (inline)" },
|
|
11760
|
+
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
11761
|
+
},
|
|
11762
|
+
async run({ args }) {
|
|
11763
|
+
const filePath = path9.resolve(String(args.file));
|
|
11764
|
+
let canvas;
|
|
11765
|
+
try {
|
|
11766
|
+
canvas = JSON.parse(await readFile7(filePath, "utf8"));
|
|
11767
|
+
} catch (e) {
|
|
11768
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
|
|
11769
|
+
`);
|
|
11770
|
+
process.exit(2);
|
|
11771
|
+
}
|
|
11772
|
+
let text;
|
|
11773
|
+
if (args["text-file"]) text = await readFile7(path9.resolve(String(args["text-file"])), "utf8");
|
|
11774
|
+
else if (args.text !== void 0) text = String(args.text);
|
|
11775
|
+
else {
|
|
11776
|
+
process.stderr.write(
|
|
11777
|
+
`${JSON.stringify({ ok: false, error: { code: "no_text", message: "pass --text or --text-file" } }, null, 2)}
|
|
11778
|
+
`
|
|
11779
|
+
);
|
|
11780
|
+
process.exit(2);
|
|
11781
|
+
return;
|
|
11782
|
+
}
|
|
11783
|
+
let updated;
|
|
11784
|
+
try {
|
|
11785
|
+
updated = setNodePrompt(canvas, String(args.node), text);
|
|
11786
|
+
} catch (e) {
|
|
11787
|
+
process.stderr.write(
|
|
11788
|
+
`${JSON.stringify({ ok: false, error: { code: "node_not_found", message: String(e.message) } }, null, 2)}
|
|
11789
|
+
`
|
|
11790
|
+
);
|
|
11791
|
+
process.exit(2);
|
|
11792
|
+
return;
|
|
11793
|
+
}
|
|
11794
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path9.dirname(filePath)), defaultRegistry());
|
|
11795
|
+
if (!validation.ok) {
|
|
11796
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
11797
|
+
`);
|
|
11798
|
+
process.exit(2);
|
|
11799
|
+
return;
|
|
11800
|
+
}
|
|
11801
|
+
await writeFile3(filePath, `${JSON.stringify(updated, null, 2)}
|
|
11802
|
+
`, "utf8");
|
|
11803
|
+
process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text.length }, null, 2)}
|
|
11804
|
+
`);
|
|
11805
|
+
}
|
|
11806
|
+
});
|
|
11807
|
+
|
|
11709
11808
|
// src/commands/canvas/validate.ts
|
|
11710
|
-
import { readFile as
|
|
11711
|
-
import
|
|
11809
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
11810
|
+
import path10 from "path";
|
|
11712
11811
|
import { defineCommand as defineCommand84 } from "citty";
|
|
11713
11812
|
var validateCommand = defineCommand84({
|
|
11714
11813
|
meta: {
|
|
@@ -11717,8 +11816,8 @@ var validateCommand = defineCommand84({
|
|
|
11717
11816
|
},
|
|
11718
11817
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
11719
11818
|
async run({ args }) {
|
|
11720
|
-
const filePath =
|
|
11721
|
-
const raw = await
|
|
11819
|
+
const filePath = path10.resolve(String(args.file));
|
|
11820
|
+
const raw = await readFile8(filePath, "utf8");
|
|
11722
11821
|
let parsed;
|
|
11723
11822
|
try {
|
|
11724
11823
|
parsed = JSON.parse(raw);
|
|
@@ -11728,6 +11827,7 @@ var validateCommand = defineCommand84({
|
|
|
11728
11827
|
`);
|
|
11729
11828
|
process.exit(2);
|
|
11730
11829
|
}
|
|
11830
|
+
parsed = resolveRelativeCanvasPaths(parsed, path10.dirname(filePath));
|
|
11731
11831
|
const result = await validateCanvasDeep(parsed, defaultRegistry());
|
|
11732
11832
|
if (!result.ok) {
|
|
11733
11833
|
process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
|
|
@@ -11764,7 +11864,6 @@ Subcommands:
|
|
|
11764
11864
|
baker canvas run <file.json> \u2014 execute the canvas, write outputs to ./canvas/<run_id>/
|
|
11765
11865
|
baker canvas catalog \u2014 print the agent-facing node + composition catalog (JSON Schema)
|
|
11766
11866
|
baker canvas inspect <run_id> \u2014 one-page summary of a completed run
|
|
11767
|
-
baker canvas gallery <dir> \u2014 read a creative folder's _definition.md + run manifests into the dashboard gallery descriptor (JSON)
|
|
11768
11867
|
baker canvas scaffold-video <video> \u2014 turn a reference video into a runnable reproduction canvas (deconstruct + recurring-element detection)
|
|
11769
11868
|
baker canvas scaffold-static-ad <image> \u2014 turn a source image into a runnable static-ad canvas (describe + element detection)`
|
|
11770
11869
|
},
|
|
@@ -11773,17 +11872,200 @@ Subcommands:
|
|
|
11773
11872
|
validate: validateCommand,
|
|
11774
11873
|
catalog: catalogCommand,
|
|
11775
11874
|
inspect: inspectCommand,
|
|
11776
|
-
gallery: galleryCommand,
|
|
11777
11875
|
"scaffold-video": scaffoldVideoCommand,
|
|
11778
|
-
"scaffold-static-ad": scaffoldStaticAdCommand
|
|
11876
|
+
"scaffold-static-ad": scaffoldStaticAdCommand,
|
|
11877
|
+
"set-prompt": setPromptCommand
|
|
11878
|
+
}
|
|
11879
|
+
});
|
|
11880
|
+
|
|
11881
|
+
// src/commands/creatives/index.ts
|
|
11882
|
+
import { defineCommand as defineCommand87 } from "citty";
|
|
11883
|
+
|
|
11884
|
+
// src/commands/creatives/publish.ts
|
|
11885
|
+
import { defineCommand as defineCommand86 } from "citty";
|
|
11886
|
+
|
|
11887
|
+
// src/commands/images/api.ts
|
|
11888
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
11889
|
+
import { extname } from "path";
|
|
11890
|
+
var imageProcessingTimeoutMs = 18e4;
|
|
11891
|
+
var imageReadyPollIntervalMs = 2e3;
|
|
11892
|
+
var mimeMap = {
|
|
11893
|
+
".png": "image/png",
|
|
11894
|
+
".jpg": "image/jpeg",
|
|
11895
|
+
".jpeg": "image/jpeg",
|
|
11896
|
+
".gif": "image/gif",
|
|
11897
|
+
".webp": "image/webp",
|
|
11898
|
+
".svg": "image/svg+xml",
|
|
11899
|
+
".avif": "image/avif"
|
|
11900
|
+
};
|
|
11901
|
+
var defaultImageApiDeps = {
|
|
11902
|
+
readFile: readFile9,
|
|
11903
|
+
post: apiPost,
|
|
11904
|
+
get: apiGet,
|
|
11905
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
11906
|
+
};
|
|
11907
|
+
function detectImageContentType(filePath, opts = {}) {
|
|
11908
|
+
const ext = extname(filePath).toLowerCase();
|
|
11909
|
+
const contentType = mimeMap[ext];
|
|
11910
|
+
if (!contentType || opts.allowedContentTypes && !opts.allowedContentTypes.includes(contentType)) {
|
|
11911
|
+
throw new ApiError(
|
|
11912
|
+
"VALIDATION_ERROR",
|
|
11913
|
+
opts.unsupportedMessage ?? `Cannot detect content type for extension "${ext}". Use --content-type.`
|
|
11914
|
+
);
|
|
11915
|
+
}
|
|
11916
|
+
return contentType;
|
|
11917
|
+
}
|
|
11918
|
+
async function uploadLocalImage(args, deps = defaultImageApiDeps) {
|
|
11919
|
+
const fileBuffer = await deps.readFile(args.file);
|
|
11920
|
+
const body = {
|
|
11921
|
+
base64: fileBuffer.toString("base64"),
|
|
11922
|
+
contentType: args.contentType
|
|
11923
|
+
};
|
|
11924
|
+
if (args.source) body.source = args.source;
|
|
11925
|
+
if (args.descriptionContext) body.descriptionContext = args.descriptionContext;
|
|
11926
|
+
return deps.post("/api/images/upload", body, { timeoutMs: imageProcessingTimeoutMs });
|
|
11927
|
+
}
|
|
11928
|
+
function getImage(deps, imageId) {
|
|
11929
|
+
return deps.get("/api/images/get", { id: imageId });
|
|
11930
|
+
}
|
|
11931
|
+
function updateImageTags(deps, args) {
|
|
11932
|
+
return deps.post("/api/images/tag", args);
|
|
11933
|
+
}
|
|
11934
|
+
async function waitForReadyImage(deps, imageId, opts = {}) {
|
|
11935
|
+
const timeoutMs = opts.timeoutMs ?? imageProcessingTimeoutMs;
|
|
11936
|
+
const pollIntervalMs = opts.pollIntervalMs ?? imageReadyPollIntervalMs;
|
|
11937
|
+
const deadline = Date.now() + timeoutMs;
|
|
11938
|
+
let lastStatus = "unknown";
|
|
11939
|
+
while (Date.now() <= deadline) {
|
|
11940
|
+
const image = await getImage(deps, imageId);
|
|
11941
|
+
lastStatus = image.status ?? "unknown";
|
|
11942
|
+
if (image.status === "ready") {
|
|
11943
|
+
return image;
|
|
11944
|
+
}
|
|
11945
|
+
if (image.status === "error") {
|
|
11946
|
+
throw new ApiError("IMAGE_PROCESSING_ERROR", "Image processing failed");
|
|
11947
|
+
}
|
|
11948
|
+
await deps.sleep(pollIntervalMs);
|
|
11949
|
+
}
|
|
11950
|
+
throw new ApiError("TIMEOUT", `Image was not ready before timeout; last status: ${lastStatus}`);
|
|
11951
|
+
}
|
|
11952
|
+
|
|
11953
|
+
// src/commands/creatives/publish.ts
|
|
11954
|
+
var creativeTag = "creative";
|
|
11955
|
+
var creativeContentTypes = ["image/png", "image/jpeg", "image/webp"];
|
|
11956
|
+
registerSchema({
|
|
11957
|
+
command: "creatives.publish",
|
|
11958
|
+
description: "Publish a final static creative image to Baker Images, apply the official creative tag, and return an image reference.",
|
|
11959
|
+
args: {
|
|
11960
|
+
file: { type: "string", description: "Local PNG/JPG/WebP creative image path", required: true },
|
|
11961
|
+
title: { type: "string", description: "Human title for the creative output", required: true },
|
|
11962
|
+
context: { type: "string", description: "Optional describe context for the image row", required: false }
|
|
11963
|
+
}
|
|
11964
|
+
});
|
|
11965
|
+
function detectCreativeContentType(filePath) {
|
|
11966
|
+
return detectImageContentType(filePath, {
|
|
11967
|
+
allowedContentTypes: creativeContentTypes,
|
|
11968
|
+
unsupportedMessage: "Unsupported creative image extension. Use PNG, JPG, or WebP."
|
|
11969
|
+
});
|
|
11970
|
+
}
|
|
11971
|
+
function imageToCreativeReference(image, title) {
|
|
11972
|
+
if (!image.imageUrl) {
|
|
11973
|
+
throw new ApiError("IMAGE_PROCESSING_ERROR", "Published image is missing imageUrl");
|
|
11974
|
+
}
|
|
11975
|
+
return {
|
|
11976
|
+
type: "image",
|
|
11977
|
+
slug: image._id,
|
|
11978
|
+
title,
|
|
11979
|
+
tags: image.tags?.includes(creativeTag) ? image.tags : [...image.tags ?? [], creativeTag],
|
|
11980
|
+
imageUrl: image.imageUrl,
|
|
11981
|
+
thumbnailUrl: image.thumbnailUrl ?? image.imageUrl,
|
|
11982
|
+
storageKey: image.storageKey,
|
|
11983
|
+
width: image.width,
|
|
11984
|
+
height: image.height,
|
|
11985
|
+
aspectRatio: image.aspectRatio,
|
|
11986
|
+
source: image.source
|
|
11987
|
+
};
|
|
11988
|
+
}
|
|
11989
|
+
async function publishCreative(args, deps = defaultImageApiDeps) {
|
|
11990
|
+
const title = args.title.trim();
|
|
11991
|
+
if (!title) {
|
|
11992
|
+
throw new ApiError("VALIDATION_ERROR", "--title is required");
|
|
11993
|
+
}
|
|
11994
|
+
const contentType = detectCreativeContentType(args.file);
|
|
11995
|
+
const upload = await uploadLocalImage(
|
|
11996
|
+
{
|
|
11997
|
+
file: args.file,
|
|
11998
|
+
contentType,
|
|
11999
|
+
source: "ai_generated",
|
|
12000
|
+
descriptionContext: args.context ?? `Static ad creative: ${title}`
|
|
12001
|
+
},
|
|
12002
|
+
deps
|
|
12003
|
+
);
|
|
12004
|
+
const readyImage = await waitForReadyImage(deps, upload.imageId, { timeoutMs: imageProcessingTimeoutMs });
|
|
12005
|
+
await updateImageTags(deps, {
|
|
12006
|
+
imageIds: [upload.imageId],
|
|
12007
|
+
addTags: [creativeTag],
|
|
12008
|
+
removeTags: []
|
|
12009
|
+
});
|
|
12010
|
+
const taggedImage = await getImage(deps, upload.imageId);
|
|
12011
|
+
return { imageId: upload.imageId, reference: imageToCreativeReference({ ...readyImage, ...taggedImage }, title) };
|
|
12012
|
+
}
|
|
12013
|
+
var publishCommand = defineCommand86({
|
|
12014
|
+
meta: {
|
|
12015
|
+
name: "publish",
|
|
12016
|
+
description: "Publish a final static creative image to Baker Images, deterministically tag it as creative, and print the image reference JSON."
|
|
12017
|
+
},
|
|
12018
|
+
args: {
|
|
12019
|
+
file: { type: "positional", description: "Local PNG/JPG/WebP creative image path", required: false },
|
|
12020
|
+
title: { type: "string", description: "Human title for the creative output", required: false },
|
|
12021
|
+
context: { type: "string", description: "Optional describe context for the image row", required: false }
|
|
12022
|
+
},
|
|
12023
|
+
run: async ({ args }) => {
|
|
12024
|
+
try {
|
|
12025
|
+
const file = args.file;
|
|
12026
|
+
const title = args.title;
|
|
12027
|
+
if (!file) {
|
|
12028
|
+
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Image path is required" } });
|
|
12029
|
+
process.exit(1);
|
|
12030
|
+
}
|
|
12031
|
+
if (!title) {
|
|
12032
|
+
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "--title is required" } });
|
|
12033
|
+
process.exit(1);
|
|
12034
|
+
}
|
|
12035
|
+
const data = await publishCreative({ file, title, context: args.context });
|
|
12036
|
+
writeJson({ ok: true, data });
|
|
12037
|
+
} catch (err) {
|
|
12038
|
+
if (err instanceof ApiError) {
|
|
12039
|
+
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
12040
|
+
process.exit(1);
|
|
12041
|
+
}
|
|
12042
|
+
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
12043
|
+
process.exit(1);
|
|
12044
|
+
}
|
|
12045
|
+
}
|
|
12046
|
+
});
|
|
12047
|
+
|
|
12048
|
+
// src/commands/creatives/index.ts
|
|
12049
|
+
var creativesCommand3 = defineCommand87({
|
|
12050
|
+
meta: {
|
|
12051
|
+
name: "creatives",
|
|
12052
|
+
description: `Publish static ad creatives as first-class Baker outputs.
|
|
12053
|
+
|
|
12054
|
+
Static creative handoff:
|
|
12055
|
+
baker creatives publish ./canvas/run/final.png --title "Spring Offer Static Ad"
|
|
12056
|
+
|
|
12057
|
+
Publishing uploads the image to the Company image library, applies the official creative tag, and returns an image reference for chat previews.`
|
|
12058
|
+
},
|
|
12059
|
+
subCommands: {
|
|
12060
|
+
publish: publishCommand
|
|
11779
12061
|
}
|
|
11780
12062
|
});
|
|
11781
12063
|
|
|
11782
12064
|
// src/commands/ga4/index.ts
|
|
11783
|
-
import { defineCommand as
|
|
12065
|
+
import { defineCommand as defineCommand91 } from "citty";
|
|
11784
12066
|
|
|
11785
12067
|
// src/commands/ga4/audit.ts
|
|
11786
|
-
import { defineCommand as
|
|
12068
|
+
import { defineCommand as defineCommand88 } from "citty";
|
|
11787
12069
|
|
|
11788
12070
|
// src/commands/ga4/resolve.ts
|
|
11789
12071
|
async function fetchProperties(useCache = true) {
|
|
@@ -11846,7 +12128,7 @@ registerSchema({
|
|
|
11846
12128
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
11847
12129
|
}
|
|
11848
12130
|
});
|
|
11849
|
-
var auditCommand2 =
|
|
12131
|
+
var auditCommand2 = defineCommand88({
|
|
11850
12132
|
meta: {
|
|
11851
12133
|
name: "audit",
|
|
11852
12134
|
description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
|
|
@@ -11898,7 +12180,7 @@ Examples:
|
|
|
11898
12180
|
});
|
|
11899
12181
|
|
|
11900
12182
|
// src/commands/ga4/properties.ts
|
|
11901
|
-
import { defineCommand as
|
|
12183
|
+
import { defineCommand as defineCommand89 } from "citty";
|
|
11902
12184
|
registerSchema({
|
|
11903
12185
|
command: "ga4.properties",
|
|
11904
12186
|
description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
|
|
@@ -11906,7 +12188,7 @@ registerSchema({
|
|
|
11906
12188
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
11907
12189
|
}
|
|
11908
12190
|
});
|
|
11909
|
-
var propertiesCommand =
|
|
12191
|
+
var propertiesCommand = defineCommand89({
|
|
11910
12192
|
meta: {
|
|
11911
12193
|
name: "properties",
|
|
11912
12194
|
description: `List accessible GA4 properties.
|
|
@@ -11956,7 +12238,7 @@ Examples:
|
|
|
11956
12238
|
// src/commands/ga4/query.ts
|
|
11957
12239
|
import { appendFileSync as appendFileSync2, existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
11958
12240
|
import { resolve as resolve2 } from "path";
|
|
11959
|
-
import { defineCommand as
|
|
12241
|
+
import { defineCommand as defineCommand90 } from "citty";
|
|
11960
12242
|
|
|
11961
12243
|
// src/commands/ga4/presets.ts
|
|
11962
12244
|
var GA4_PRESETS = [
|
|
@@ -12088,7 +12370,7 @@ function handleError(err) {
|
|
|
12088
12370
|
});
|
|
12089
12371
|
process.exit(1);
|
|
12090
12372
|
}
|
|
12091
|
-
var queryCommand2 =
|
|
12373
|
+
var queryCommand2 = defineCommand90({
|
|
12092
12374
|
meta: {
|
|
12093
12375
|
name: "query",
|
|
12094
12376
|
description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
|
|
@@ -12159,7 +12441,7 @@ Free-form (escape hatch):
|
|
|
12159
12441
|
});
|
|
12160
12442
|
|
|
12161
12443
|
// src/commands/ga4/index.ts
|
|
12162
|
-
var ga4Command =
|
|
12444
|
+
var ga4Command = defineCommand91({
|
|
12163
12445
|
meta: {
|
|
12164
12446
|
name: "ga4",
|
|
12165
12447
|
description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
|
|
@@ -12182,12 +12464,12 @@ Examples:
|
|
|
12182
12464
|
});
|
|
12183
12465
|
|
|
12184
12466
|
// src/commands/gsc/index.ts
|
|
12185
|
-
import { defineCommand as
|
|
12467
|
+
import { defineCommand as defineCommand95 } from "citty";
|
|
12186
12468
|
|
|
12187
12469
|
// src/commands/gsc/query.ts
|
|
12188
12470
|
import { appendFileSync as appendFileSync3, existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
12189
12471
|
import { resolve as resolve3 } from "path";
|
|
12190
|
-
import { defineCommand as
|
|
12472
|
+
import { defineCommand as defineCommand92 } from "citty";
|
|
12191
12473
|
|
|
12192
12474
|
// src/commands/gsc/presets.ts
|
|
12193
12475
|
var GSC_PRESETS = [
|
|
@@ -12375,7 +12657,7 @@ function handleError2(err) {
|
|
|
12375
12657
|
});
|
|
12376
12658
|
process.exit(1);
|
|
12377
12659
|
}
|
|
12378
|
-
var queryCommand3 =
|
|
12660
|
+
var queryCommand3 = defineCommand92({
|
|
12379
12661
|
meta: {
|
|
12380
12662
|
name: "query",
|
|
12381
12663
|
description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
|
|
@@ -12453,7 +12735,7 @@ Free-form (escape hatch):
|
|
|
12453
12735
|
});
|
|
12454
12736
|
|
|
12455
12737
|
// src/commands/gsc/sitemaps.ts
|
|
12456
|
-
import { defineCommand as
|
|
12738
|
+
import { defineCommand as defineCommand93 } from "citty";
|
|
12457
12739
|
registerSchema({
|
|
12458
12740
|
command: "gsc.sitemaps",
|
|
12459
12741
|
description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
|
|
@@ -12462,7 +12744,7 @@ registerSchema({
|
|
|
12462
12744
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
12463
12745
|
}
|
|
12464
12746
|
});
|
|
12465
|
-
var sitemapsCommand =
|
|
12747
|
+
var sitemapsCommand = defineCommand93({
|
|
12466
12748
|
meta: {
|
|
12467
12749
|
name: "sitemaps",
|
|
12468
12750
|
description: `List sitemaps for a site. Check health and errors.
|
|
@@ -12512,7 +12794,7 @@ Examples:
|
|
|
12512
12794
|
});
|
|
12513
12795
|
|
|
12514
12796
|
// src/commands/gsc/sites.ts
|
|
12515
|
-
import { defineCommand as
|
|
12797
|
+
import { defineCommand as defineCommand94 } from "citty";
|
|
12516
12798
|
registerSchema({
|
|
12517
12799
|
command: "gsc.sites",
|
|
12518
12800
|
description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
|
|
@@ -12520,7 +12802,7 @@ registerSchema({
|
|
|
12520
12802
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
12521
12803
|
}
|
|
12522
12804
|
});
|
|
12523
|
-
var sitesCommand =
|
|
12805
|
+
var sitesCommand = defineCommand94({
|
|
12524
12806
|
meta: {
|
|
12525
12807
|
name: "sites",
|
|
12526
12808
|
description: `List verified Search Console sites.
|
|
@@ -12568,7 +12850,7 @@ Examples:
|
|
|
12568
12850
|
});
|
|
12569
12851
|
|
|
12570
12852
|
// src/commands/gsc/index.ts
|
|
12571
|
-
var gscCommand =
|
|
12853
|
+
var gscCommand = defineCommand95({
|
|
12572
12854
|
meta: {
|
|
12573
12855
|
name: "gsc",
|
|
12574
12856
|
description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
|
|
@@ -12591,10 +12873,10 @@ Examples:
|
|
|
12591
12873
|
});
|
|
12592
12874
|
|
|
12593
12875
|
// src/commands/images/index.ts
|
|
12594
|
-
import { defineCommand as
|
|
12876
|
+
import { defineCommand as defineCommand119 } from "citty";
|
|
12595
12877
|
|
|
12596
12878
|
// src/commands/images/crop.ts
|
|
12597
|
-
import { defineCommand as
|
|
12879
|
+
import { defineCommand as defineCommand96 } from "citty";
|
|
12598
12880
|
|
|
12599
12881
|
// src/lib/image/crop-sprite.ts
|
|
12600
12882
|
import sharp from "sharp";
|
|
@@ -12609,8 +12891,8 @@ function cropSprite(input, region) {
|
|
|
12609
12891
|
|
|
12610
12892
|
// src/lib/image/io.ts
|
|
12611
12893
|
import { randomBytes } from "crypto";
|
|
12612
|
-
import { glob as fsGlob, readFile as
|
|
12613
|
-
import { dirname, extname, join as join3, resolve as resolve4 } from "path";
|
|
12894
|
+
import { glob as fsGlob, readFile as readFile10, rename, stat as stat2, writeFile as writeFile4 } from "fs/promises";
|
|
12895
|
+
import { dirname, extname as extname2, join as join3, resolve as resolve4 } from "path";
|
|
12614
12896
|
var REMOTE_RE = /^https?:\/\//i;
|
|
12615
12897
|
var GLOB_RE = /[*?[\]{}]/;
|
|
12616
12898
|
function isRemoteUrl(value) {
|
|
@@ -12645,18 +12927,18 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
12645
12927
|
}
|
|
12646
12928
|
return Buffer.from(await response.arrayBuffer());
|
|
12647
12929
|
}
|
|
12648
|
-
return
|
|
12930
|
+
return readFile10(pathOrUrl);
|
|
12649
12931
|
}
|
|
12650
|
-
async function isDirectory(
|
|
12932
|
+
async function isDirectory(path11) {
|
|
12651
12933
|
try {
|
|
12652
|
-
const s = await stat2(
|
|
12934
|
+
const s = await stat2(path11);
|
|
12653
12935
|
return s.isDirectory();
|
|
12654
12936
|
} catch {
|
|
12655
12937
|
return false;
|
|
12656
12938
|
}
|
|
12657
12939
|
}
|
|
12658
12940
|
async function resolveOutputPath(inputPath, outputArg, options) {
|
|
12659
|
-
const base = options.newExtension ? inputPath.slice(0, -
|
|
12941
|
+
const base = options.newExtension ? inputPath.slice(0, -extname2(inputPath).length) + options.newExtension : inputPath;
|
|
12660
12942
|
if (!outputArg) return base;
|
|
12661
12943
|
if (options.multipleInputs || await isDirectory(outputArg)) {
|
|
12662
12944
|
const filename = base.split("/").pop() ?? "out.png";
|
|
@@ -12668,7 +12950,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
12668
12950
|
const absolute = resolve4(targetPath);
|
|
12669
12951
|
const dir = dirname(absolute);
|
|
12670
12952
|
const tmp = join3(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
12671
|
-
await
|
|
12953
|
+
await writeFile4(tmp, data);
|
|
12672
12954
|
await rename(tmp, absolute);
|
|
12673
12955
|
}
|
|
12674
12956
|
|
|
@@ -12719,7 +13001,7 @@ function emitError2(err) {
|
|
|
12719
13001
|
}
|
|
12720
13002
|
process.exit(1);
|
|
12721
13003
|
}
|
|
12722
|
-
var cropCommand =
|
|
13004
|
+
var cropCommand = defineCommand96({
|
|
12723
13005
|
meta: {
|
|
12724
13006
|
name: "crop",
|
|
12725
13007
|
description: "Crop a rectangular region from an image.\n\nExample: baker images crop sprite.png --x 0 --y 0 --width 64 --height 64 --output icon.png"
|
|
@@ -12755,7 +13037,7 @@ var cropCommand = defineCommand94({
|
|
|
12755
13037
|
});
|
|
12756
13038
|
|
|
12757
13039
|
// src/commands/images/delete.ts
|
|
12758
|
-
import { defineCommand as
|
|
13040
|
+
import { defineCommand as defineCommand97 } from "citty";
|
|
12759
13041
|
registerSchema({
|
|
12760
13042
|
command: "images.delete",
|
|
12761
13043
|
description: "Delete an image by ID",
|
|
@@ -12769,7 +13051,7 @@ registerSchema({
|
|
|
12769
13051
|
}
|
|
12770
13052
|
}
|
|
12771
13053
|
});
|
|
12772
|
-
var deleteCommand =
|
|
13054
|
+
var deleteCommand = defineCommand97({
|
|
12773
13055
|
meta: {
|
|
12774
13056
|
name: "delete",
|
|
12775
13057
|
description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
|
|
@@ -12810,7 +13092,7 @@ var deleteCommand = defineCommand95({
|
|
|
12810
13092
|
});
|
|
12811
13093
|
|
|
12812
13094
|
// src/commands/images/dimensions.ts
|
|
12813
|
-
import { defineCommand as
|
|
13095
|
+
import { defineCommand as defineCommand98 } from "citty";
|
|
12814
13096
|
|
|
12815
13097
|
// src/lib/image/dimensions.ts
|
|
12816
13098
|
import { imageSize } from "image-size";
|
|
@@ -12833,7 +13115,7 @@ registerSchema({
|
|
|
12833
13115
|
target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
|
|
12834
13116
|
}
|
|
12835
13117
|
});
|
|
12836
|
-
var dimensionsCommand =
|
|
13118
|
+
var dimensionsCommand = defineCommand98({
|
|
12837
13119
|
meta: {
|
|
12838
13120
|
name: "dimensions",
|
|
12839
13121
|
description: "Read image dimensions without decoding the full file.\n\nExample: baker images dimensions ./logo.png\nExample: baker images dimensions https://acme.com/hero.png"
|
|
@@ -12877,7 +13159,7 @@ var dimensionsCommand = defineCommand96({
|
|
|
12877
13159
|
});
|
|
12878
13160
|
|
|
12879
13161
|
// src/commands/images/extract.ts
|
|
12880
|
-
import { defineCommand as
|
|
13162
|
+
import { defineCommand as defineCommand99 } from "citty";
|
|
12881
13163
|
registerSchema({
|
|
12882
13164
|
command: "images.extract",
|
|
12883
13165
|
description: "Extract images from a URL via Firecrawl (formats: images).",
|
|
@@ -12893,7 +13175,7 @@ registerSchema({
|
|
|
12893
13175
|
}
|
|
12894
13176
|
}
|
|
12895
13177
|
});
|
|
12896
|
-
var extractCommand =
|
|
13178
|
+
var extractCommand = defineCommand99({
|
|
12897
13179
|
meta: {
|
|
12898
13180
|
name: "extract",
|
|
12899
13181
|
description: "Pull every image from a single URL via Firecrawl. ~$0.001/scrape. Cap auto-ingest at 20.\n\nExample: baker images extract https://stripe.com --auto-ingest 5"
|
|
@@ -12931,7 +13213,7 @@ var extractCommand = defineCommand97({
|
|
|
12931
13213
|
});
|
|
12932
13214
|
|
|
12933
13215
|
// src/commands/images/find.ts
|
|
12934
|
-
import { defineCommand as
|
|
13216
|
+
import { defineCommand as defineCommand100 } from "citty";
|
|
12935
13217
|
registerSchema({
|
|
12936
13218
|
command: "images.find",
|
|
12937
13219
|
description: "Fanout image search: library first, then opted-in external providers.",
|
|
@@ -12963,7 +13245,7 @@ registerSchema({
|
|
|
12963
13245
|
}
|
|
12964
13246
|
}
|
|
12965
13247
|
});
|
|
12966
|
-
var findCommand =
|
|
13248
|
+
var findCommand = defineCommand100({
|
|
12967
13249
|
meta: {
|
|
12968
13250
|
name: "find",
|
|
12969
13251
|
description: "Library-first fanout image search. Opt in to providers with --sources. `--fallback` short-circuits to externals only when library is thin. With --auto-ingest, ingested external hits return Baker-owned URLs.\n\nExample: baker images find 'office' --sources library,magnific --limit 20"
|
|
@@ -13009,8 +13291,8 @@ var findCommand = defineCommand98({
|
|
|
13009
13291
|
});
|
|
13010
13292
|
|
|
13011
13293
|
// src/commands/images/generate.ts
|
|
13012
|
-
import { readFile as
|
|
13013
|
-
import { defineCommand as
|
|
13294
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
13295
|
+
import { defineCommand as defineCommand101 } from "citty";
|
|
13014
13296
|
import sharp2 from "sharp";
|
|
13015
13297
|
var GENERATE_TIMEOUT_MS = 18e4;
|
|
13016
13298
|
var REFERENCE_MAX_EDGE = 1536;
|
|
@@ -13092,7 +13374,7 @@ async function resolveReferences(spec) {
|
|
|
13092
13374
|
}
|
|
13093
13375
|
let raw;
|
|
13094
13376
|
try {
|
|
13095
|
-
raw = await
|
|
13377
|
+
raw = await readFile11(entry);
|
|
13096
13378
|
} catch {
|
|
13097
13379
|
throw new ApiError("VALIDATION_ERROR", `Reference file not found: ${entry}`);
|
|
13098
13380
|
}
|
|
@@ -13106,7 +13388,7 @@ async function resolveReferences(spec) {
|
|
|
13106
13388
|
}
|
|
13107
13389
|
return out;
|
|
13108
13390
|
}
|
|
13109
|
-
var generateCommand =
|
|
13391
|
+
var generateCommand = defineCommand101({
|
|
13110
13392
|
meta: {
|
|
13111
13393
|
name: "generate",
|
|
13112
13394
|
description: "Generate an image with AI and store it in the library (cost-tracked per request via OpenRouter usage). Models mirror the canvas: openai/gpt-5.4-image-2 (default \u2014 photoreal, cleanest text, best for ad/landing reproduction), google/gemini-3-pro-image-preview (Nano Banana Pro), google/gemini-3.5-flash & google/gemini-3.1-flash-image-preview (fast, extreme aspect ratios), recraft/recraft-v4.1-pro-vector (vector/SVG-style with palette control). The result is auto-ingested (describe + embed), so the next `baker images library` query finds it. Pass --reference with image URLs and/or local file paths (Pinterest, stock, brand assets, sandbox files) to ground generation in reality.\n\nExamples:\n baker images generate 'a friendly golden retriever sitting in a bright modern living room' --aspect-ratio 16:9\n baker images generate 'hero shot of a matte black water bottle on marble' --model google/gemini-3-pro-image-preview --image-size 2K\n baker images generate 'lifestyle photo matching this mood' --reference 'https://\u2026/ref1.jpg,https://\u2026/ref2.jpg'\n baker images generate 'put this product on a marble countertop, soft daylight' --reference './src/brand/logos/product.png,./refs/kitchen-mood.jpg'\n baker images generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
|
|
@@ -13158,7 +13440,7 @@ var generateCommand = defineCommand99({
|
|
|
13158
13440
|
});
|
|
13159
13441
|
|
|
13160
13442
|
// src/commands/images/get.ts
|
|
13161
|
-
import { defineCommand as
|
|
13443
|
+
import { defineCommand as defineCommand102 } from "citty";
|
|
13162
13444
|
registerSchema({
|
|
13163
13445
|
command: "images.get",
|
|
13164
13446
|
description: "Get a single image by ID",
|
|
@@ -13166,7 +13448,7 @@ registerSchema({
|
|
|
13166
13448
|
id: { type: "string", description: "Image ID", required: true }
|
|
13167
13449
|
}
|
|
13168
13450
|
});
|
|
13169
|
-
var getCommand2 =
|
|
13451
|
+
var getCommand2 = defineCommand102({
|
|
13170
13452
|
meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
|
|
13171
13453
|
args: {
|
|
13172
13454
|
id: { type: "positional", description: "Image ID", required: false },
|
|
@@ -13202,7 +13484,7 @@ var getCommand2 = defineCommand100({
|
|
|
13202
13484
|
});
|
|
13203
13485
|
|
|
13204
13486
|
// src/commands/images/gif.ts
|
|
13205
|
-
import { defineCommand as
|
|
13487
|
+
import { defineCommand as defineCommand103 } from "citty";
|
|
13206
13488
|
registerSchema({
|
|
13207
13489
|
command: "images.gif",
|
|
13208
13490
|
description: "Search Giphy for GIFs / reaction memes (paid social creative).",
|
|
@@ -13234,7 +13516,7 @@ registerSchema({
|
|
|
13234
13516
|
}
|
|
13235
13517
|
}
|
|
13236
13518
|
});
|
|
13237
|
-
var gifCommand =
|
|
13519
|
+
var gifCommand = defineCommand103({
|
|
13238
13520
|
meta: {
|
|
13239
13521
|
name: "gif",
|
|
13240
13522
|
description: "Search Giphy for GIFs / reaction memes \u2014 built for paid-social creative (Meta, TikTok, LinkedIn, X). Free API. Each hit carries WebP + GIF + MP4 URLs in providerMeta so you can pick the right format per platform.\n\nExample: baker images gif 'this is fine' --limit 10\nExample: baker images gif 'office reaction' --rating pg --auto-ingest 2\nExample: baker images gif --trending --limit 25"
|
|
@@ -13281,7 +13563,7 @@ var gifCommand = defineCommand101({
|
|
|
13281
13563
|
});
|
|
13282
13564
|
|
|
13283
13565
|
// src/commands/images/google.ts
|
|
13284
|
-
import { defineCommand as
|
|
13566
|
+
import { defineCommand as defineCommand104 } from "citty";
|
|
13285
13567
|
registerSchema({
|
|
13286
13568
|
command: "images.google",
|
|
13287
13569
|
description: "Google Images search via the official Custom Search JSON API. Unverified source \u2014 inspect before placing.",
|
|
@@ -13317,7 +13599,7 @@ registerSchema({
|
|
|
13317
13599
|
}
|
|
13318
13600
|
}
|
|
13319
13601
|
});
|
|
13320
|
-
var googleCommand2 =
|
|
13602
|
+
var googleCommand2 = defineCommand104({
|
|
13321
13603
|
meta: {
|
|
13322
13604
|
name: "google",
|
|
13323
13605
|
description: "Google Images via the official Custom Search JSON API ($0.005/query, free 100/day). \u26A0 Source unverified \u2014 watermarks, low-res, mislabeled results are common. Use as last resort. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExample: baker images google 'industrial workshop' --type photo --size large --limit 20"
|
|
@@ -13365,7 +13647,7 @@ var googleCommand2 = defineCommand102({
|
|
|
13365
13647
|
});
|
|
13366
13648
|
|
|
13367
13649
|
// src/commands/images/icon.ts
|
|
13368
|
-
import { defineCommand as
|
|
13650
|
+
import { defineCommand as defineCommand105 } from "citty";
|
|
13369
13651
|
registerSchema({
|
|
13370
13652
|
command: "images.icon",
|
|
13371
13653
|
description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
|
|
@@ -13391,7 +13673,7 @@ registerSchema({
|
|
|
13391
13673
|
}
|
|
13392
13674
|
}
|
|
13393
13675
|
});
|
|
13394
|
-
var iconCommand =
|
|
13676
|
+
var iconCommand = defineCommand105({
|
|
13395
13677
|
meta: {
|
|
13396
13678
|
name: "icon",
|
|
13397
13679
|
description: "Icon via Iconify (simple-icons, logos, lucide, devicon, heroicons, tabler, phosphor, material-symbols, \u2026). Free CDN, no API key.\n\nExample: baker images icon react --set devicon\nExample: baker images icon lucide:check --color '#0a0a0a'"
|
|
@@ -13431,7 +13713,7 @@ var iconCommand = defineCommand103({
|
|
|
13431
13713
|
});
|
|
13432
13714
|
|
|
13433
13715
|
// src/commands/images/ingest.ts
|
|
13434
|
-
import { defineCommand as
|
|
13716
|
+
import { defineCommand as defineCommand106 } from "citty";
|
|
13435
13717
|
registerSchema({
|
|
13436
13718
|
command: "images.ingest",
|
|
13437
13719
|
description: "Ingest a remote image URL into the library (full describe + embed).",
|
|
@@ -13443,7 +13725,7 @@ registerSchema({
|
|
|
13443
13725
|
context: { type: "string", description: "Description context hint", required: false }
|
|
13444
13726
|
}
|
|
13445
13727
|
});
|
|
13446
|
-
var ingestCommand =
|
|
13728
|
+
var ingestCommand = defineCommand106({
|
|
13447
13729
|
meta: {
|
|
13448
13730
|
name: "ingest",
|
|
13449
13731
|
description: "Download a remote URL and store it in the library. Hash-deduped on bytes + externalId.\n\nExample: baker images ingest https://img.freepik.com/free-photo/xyz.jpg --source magnific --external-id 12345"
|
|
@@ -13485,7 +13767,7 @@ var ingestCommand = defineCommand104({
|
|
|
13485
13767
|
});
|
|
13486
13768
|
|
|
13487
13769
|
// src/commands/images/library.ts
|
|
13488
|
-
import { defineCommand as
|
|
13770
|
+
import { defineCommand as defineCommand107 } from "citty";
|
|
13489
13771
|
registerSchema({
|
|
13490
13772
|
command: "images.library",
|
|
13491
13773
|
description: "Search the company image library. Returns only ready images.",
|
|
@@ -13511,7 +13793,7 @@ registerSchema({
|
|
|
13511
13793
|
}
|
|
13512
13794
|
}
|
|
13513
13795
|
});
|
|
13514
|
-
var libraryCommand =
|
|
13796
|
+
var libraryCommand = defineCommand107({
|
|
13515
13797
|
meta: {
|
|
13516
13798
|
name: "library",
|
|
13517
13799
|
description: "Search the company image library (hybrid BM25 + vector + Cohere rerank). Use this BEFORE any external provider.\n\nExample: baker images library 'hero banner' --aspect-ratio 16:9 --source magnific"
|
|
@@ -13568,7 +13850,7 @@ var libraryCommand = defineCommand105({
|
|
|
13568
13850
|
});
|
|
13569
13851
|
|
|
13570
13852
|
// src/commands/images/logo.ts
|
|
13571
|
-
import { defineCommand as
|
|
13853
|
+
import { defineCommand as defineCommand108 } from "citty";
|
|
13572
13854
|
registerSchema({
|
|
13573
13855
|
command: "images.logo",
|
|
13574
13856
|
description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
|
|
@@ -13593,7 +13875,7 @@ registerSchema({
|
|
|
13593
13875
|
}
|
|
13594
13876
|
}
|
|
13595
13877
|
});
|
|
13596
|
-
var logoCommand =
|
|
13878
|
+
var logoCommand = defineCommand108({
|
|
13597
13879
|
meta: {
|
|
13598
13880
|
name: "logo",
|
|
13599
13881
|
description: "Brand logo via Brandfetch CDN. Returns up to 5 variants (icon, light/dark logo, light/dark symbol). Auto-ingests the first variant.\n\nExample: baker images logo stripe.com --variant logo"
|
|
@@ -13631,7 +13913,7 @@ var logoCommand = defineCommand106({
|
|
|
13631
13913
|
});
|
|
13632
13914
|
|
|
13633
13915
|
// src/commands/images/normalize.ts
|
|
13634
|
-
import { defineCommand as
|
|
13916
|
+
import { defineCommand as defineCommand109 } from "citty";
|
|
13635
13917
|
|
|
13636
13918
|
// src/lib/image/color-changer.ts
|
|
13637
13919
|
import quantize from "quantize";
|
|
@@ -14363,7 +14645,7 @@ function coerceRawArgs(args) {
|
|
|
14363
14645
|
"dry-run": bool(args["dry-run"])
|
|
14364
14646
|
};
|
|
14365
14647
|
}
|
|
14366
|
-
var normalizeCommand =
|
|
14648
|
+
var normalizeCommand = defineCommand109({
|
|
14367
14649
|
meta: {
|
|
14368
14650
|
name: "normalize",
|
|
14369
14651
|
description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
|
|
@@ -14418,7 +14700,7 @@ Examples:
|
|
|
14418
14700
|
});
|
|
14419
14701
|
|
|
14420
14702
|
// src/commands/images/pinterest.ts
|
|
14421
|
-
import { defineCommand as
|
|
14703
|
+
import { defineCommand as defineCommand110 } from "citty";
|
|
14422
14704
|
registerSchema({
|
|
14423
14705
|
command: "images.pinterest",
|
|
14424
14706
|
description: "Pinterest image search via ScrapeCreators. Reference-grade real-world photography, product styling, interiors, fashion, food, and aesthetic mood boards. Inspect before placing \u2014 Pinterest is unverified, trademark-bearing web content.",
|
|
@@ -14438,7 +14720,7 @@ registerSchema({
|
|
|
14438
14720
|
}
|
|
14439
14721
|
}
|
|
14440
14722
|
});
|
|
14441
|
-
var pinterestCommand =
|
|
14723
|
+
var pinterestCommand = defineCommand110({
|
|
14442
14724
|
meta: {
|
|
14443
14725
|
name: "pinterest",
|
|
14444
14726
|
description: "Pinterest image search via ScrapeCreators ($0.00188/request). Best for photo-realistic reference imagery \u2014 lifestyle, interiors, fashion, food, product styling, and mood boards to brief AI generation against. \u26A0 Unverified, trademark-bearing web content \u2014 inspect and respect rights before placing on a customer page. Browse first; auto-ingest only the pins you commit to.\n\nExamples:\n baker images pinterest 'scandinavian living room'\n baker images pinterest 'minimalist skincare product photography' --limit 20\n baker images pinterest 'cozy coffee shop interior' --auto-ingest 2 --context 'Mood reference for hero photography'"
|
|
@@ -14478,7 +14760,7 @@ var pinterestCommand = defineCommand108({
|
|
|
14478
14760
|
});
|
|
14479
14761
|
|
|
14480
14762
|
// src/commands/images/screenshot.ts
|
|
14481
|
-
import { defineCommand as
|
|
14763
|
+
import { defineCommand as defineCommand111 } from "citty";
|
|
14482
14764
|
registerSchema({
|
|
14483
14765
|
command: "images.screenshot",
|
|
14484
14766
|
description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
|
|
@@ -14494,7 +14776,7 @@ registerSchema({
|
|
|
14494
14776
|
}
|
|
14495
14777
|
}
|
|
14496
14778
|
});
|
|
14497
|
-
var screenshotCommand =
|
|
14779
|
+
var screenshotCommand = defineCommand111({
|
|
14498
14780
|
meta: {
|
|
14499
14781
|
name: "screenshot",
|
|
14500
14782
|
description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
|
|
@@ -14544,7 +14826,7 @@ var screenshotCommand = defineCommand109({
|
|
|
14544
14826
|
});
|
|
14545
14827
|
|
|
14546
14828
|
// src/commands/images/search.ts
|
|
14547
|
-
import { defineCommand as
|
|
14829
|
+
import { defineCommand as defineCommand112 } from "citty";
|
|
14548
14830
|
registerSchema({
|
|
14549
14831
|
command: "images.search",
|
|
14550
14832
|
description: "Search images by text query. Only returns ready images.",
|
|
@@ -14560,7 +14842,7 @@ registerSchema({
|
|
|
14560
14842
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
14561
14843
|
}
|
|
14562
14844
|
});
|
|
14563
|
-
var searchCommand =
|
|
14845
|
+
var searchCommand = defineCommand112({
|
|
14564
14846
|
meta: {
|
|
14565
14847
|
name: "search",
|
|
14566
14848
|
description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
|
|
@@ -14620,7 +14902,7 @@ var searchCommand = defineCommand110({
|
|
|
14620
14902
|
});
|
|
14621
14903
|
|
|
14622
14904
|
// src/commands/images/sticker.ts
|
|
14623
|
-
import { defineCommand as
|
|
14905
|
+
import { defineCommand as defineCommand113 } from "citty";
|
|
14624
14906
|
registerSchema({
|
|
14625
14907
|
command: "images.sticker",
|
|
14626
14908
|
description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
|
|
@@ -14652,7 +14934,7 @@ registerSchema({
|
|
|
14652
14934
|
}
|
|
14653
14935
|
}
|
|
14654
14936
|
});
|
|
14655
|
-
var stickerCommand =
|
|
14937
|
+
var stickerCommand = defineCommand113({
|
|
14656
14938
|
meta: {
|
|
14657
14939
|
name: "sticker",
|
|
14658
14940
|
description: "Search Giphy's sticker corpus \u2014 transparent-background WebPs / GIFs ideal for overlaying on ad creative (Meta, TikTok, Stories). Same Giphy free API as `baker images gif`; results carry WebP + GIF + MP4 URLs in providerMeta.\n\nExample: baker images sticker 'thumbs up' --limit 10\nExample: baker images sticker celebration --rating g --auto-ingest 3\nExample: baker images sticker --trending --limit 25"
|
|
@@ -14699,7 +14981,7 @@ var stickerCommand = defineCommand111({
|
|
|
14699
14981
|
});
|
|
14700
14982
|
|
|
14701
14983
|
// src/commands/images/stock.ts
|
|
14702
|
-
import { defineCommand as
|
|
14984
|
+
import { defineCommand as defineCommand114 } from "citty";
|
|
14703
14985
|
registerSchema({
|
|
14704
14986
|
command: "images.stock",
|
|
14705
14987
|
description: "Stock photo, vector illustration, icon-set, and PSD search via Magnific (Freepik's developer API).",
|
|
@@ -14757,7 +15039,7 @@ registerSchema({
|
|
|
14757
15039
|
}
|
|
14758
15040
|
}
|
|
14759
15041
|
});
|
|
14760
|
-
var stockCommand =
|
|
15042
|
+
var stockCommand = defineCommand114({
|
|
14761
15043
|
meta: {
|
|
14762
15044
|
name: "stock",
|
|
14763
15045
|
description: "Stock search via Magnific \u2014 Freepik's developer API (~250M assets: photos, vectors, illustrations, icons, PSDs). $0.002/req. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExamples:\n baker images stock 'minimalist office'\n baker images stock 'flat office workers' --type vector\n baker images stock 'hero photo of a kitchen' --type photo --orientation landscape --ai exclude\n baker images stock 'brand pattern' --color '#0a0a0a' --license freemium --auto-ingest 2"
|
|
@@ -14813,7 +15095,7 @@ var stockCommand = defineCommand112({
|
|
|
14813
15095
|
});
|
|
14814
15096
|
|
|
14815
15097
|
// src/lib/tags-command.ts
|
|
14816
|
-
import { defineCommand as
|
|
15098
|
+
import { defineCommand as defineCommand115 } from "citty";
|
|
14817
15099
|
function makeTagsCommand(command, label, endpoint) {
|
|
14818
15100
|
registerSchema({
|
|
14819
15101
|
command: `${command}.tags`,
|
|
@@ -14822,7 +15104,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
14822
15104
|
output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
|
|
14823
15105
|
}
|
|
14824
15106
|
});
|
|
14825
|
-
return
|
|
15107
|
+
return defineCommand115({
|
|
14826
15108
|
meta: {
|
|
14827
15109
|
name: "tags",
|
|
14828
15110
|
description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
|
|
@@ -14858,18 +15140,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
14858
15140
|
var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
|
|
14859
15141
|
|
|
14860
15142
|
// src/commands/images/upload.ts
|
|
14861
|
-
import {
|
|
14862
|
-
import { extname as extname2 } from "path";
|
|
14863
|
-
import { defineCommand as defineCommand114 } from "citty";
|
|
14864
|
-
var MIME_MAP = {
|
|
14865
|
-
".png": "image/png",
|
|
14866
|
-
".jpg": "image/jpeg",
|
|
14867
|
-
".jpeg": "image/jpeg",
|
|
14868
|
-
".gif": "image/gif",
|
|
14869
|
-
".webp": "image/webp",
|
|
14870
|
-
".svg": "image/svg+xml",
|
|
14871
|
-
".avif": "image/avif"
|
|
14872
|
-
};
|
|
15143
|
+
import { defineCommand as defineCommand116 } from "citty";
|
|
14873
15144
|
registerSchema({
|
|
14874
15145
|
command: "images.upload",
|
|
14875
15146
|
description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
|
|
@@ -14907,15 +15178,7 @@ registerSchema({
|
|
|
14907
15178
|
function isRemoteUrl2(value) {
|
|
14908
15179
|
return /^https?:\/\//i.test(value);
|
|
14909
15180
|
}
|
|
14910
|
-
|
|
14911
|
-
const ext = extname2(filePath).toLowerCase();
|
|
14912
|
-
const mime = MIME_MAP[ext];
|
|
14913
|
-
if (!mime) {
|
|
14914
|
-
throw new ApiError("VALIDATION_ERROR", `Cannot detect content type for extension "${ext}". Use --content-type.`);
|
|
14915
|
-
}
|
|
14916
|
-
return mime;
|
|
14917
|
-
}
|
|
14918
|
-
var uploadCommand = defineCommand114({
|
|
15181
|
+
var uploadCommand = defineCommand116({
|
|
14919
15182
|
meta: {
|
|
14920
15183
|
name: "upload",
|
|
14921
15184
|
description: "Upload an image to the library \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: reads bytes, sends to /api/images/upload, content-type auto-detected from extension.\nRemote: dispatches to /api/images/ingest with hash-dedup on bytes + externalId.\n\nExamples:\n baker images upload ./logo.png --source uploaded\n baker images upload ./cert.png --context 'ISO 27001 badge \u2014 enterprise tier'\n baker images upload https://acme.com/hero.png --source firecrawl --context 'Acme competitor pricing hero'"
|
|
@@ -14983,7 +15246,7 @@ async function uploadRemote(target, args) {
|
|
|
14983
15246
|
writeJson({ ok: true, data });
|
|
14984
15247
|
}
|
|
14985
15248
|
async function uploadLocal(target, args) {
|
|
14986
|
-
const contentType = args["content-type"] ||
|
|
15249
|
+
const contentType = args["content-type"] || detectImageContentType(target);
|
|
14987
15250
|
if (args["dry-run"]) {
|
|
14988
15251
|
writeJson({
|
|
14989
15252
|
ok: true,
|
|
@@ -14998,17 +15261,17 @@ async function uploadLocal(target, args) {
|
|
|
14998
15261
|
});
|
|
14999
15262
|
return;
|
|
15000
15263
|
}
|
|
15001
|
-
const
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15264
|
+
const data = await uploadLocalImage({
|
|
15265
|
+
file: target,
|
|
15266
|
+
contentType,
|
|
15267
|
+
source: args.source,
|
|
15268
|
+
descriptionContext: args.context
|
|
15269
|
+
});
|
|
15007
15270
|
writeJson({ ok: true, data });
|
|
15008
15271
|
}
|
|
15009
15272
|
|
|
15010
15273
|
// src/commands/images/upscale.ts
|
|
15011
|
-
import { defineCommand as
|
|
15274
|
+
import { defineCommand as defineCommand117 } from "citty";
|
|
15012
15275
|
registerSchema({
|
|
15013
15276
|
command: "images.upscale",
|
|
15014
15277
|
description: "Upscale a library image via the backend (Replicate, cost-tracked). Waits for completion by default. The image must be status 'ready' and raster (not SVG/AVIF).",
|
|
@@ -15023,7 +15286,7 @@ registerSchema({
|
|
|
15023
15286
|
}
|
|
15024
15287
|
});
|
|
15025
15288
|
var POLL_INTERVAL_MS3 = 1500;
|
|
15026
|
-
var upscaleCommand =
|
|
15289
|
+
var upscaleCommand = defineCommand117({
|
|
15027
15290
|
meta: {
|
|
15028
15291
|
name: "upscale",
|
|
15029
15292
|
description: "Upscale a library image via the Convex backend (Replicate, cost-tracked at $0.05/image). Waits for completion by default.\n\nExample: baker images upscale j571abc123def\nExample: baker images upscale j571abc123def --max-wait 0 # fire-and-forget"
|
|
@@ -15078,7 +15341,7 @@ var upscaleCommand = defineCommand115({
|
|
|
15078
15341
|
});
|
|
15079
15342
|
|
|
15080
15343
|
// src/commands/images/use.ts
|
|
15081
|
-
import { defineCommand as
|
|
15344
|
+
import { defineCommand as defineCommand118 } from "citty";
|
|
15082
15345
|
registerSchema({
|
|
15083
15346
|
command: "images.use",
|
|
15084
15347
|
description: "Ingest a URL and wait for the library record to be ready.",
|
|
@@ -15094,7 +15357,7 @@ registerSchema({
|
|
|
15094
15357
|
}
|
|
15095
15358
|
});
|
|
15096
15359
|
var POLL_INTERVAL_MS4 = 1500;
|
|
15097
|
-
var useCommand =
|
|
15360
|
+
var useCommand = defineCommand118({
|
|
15098
15361
|
meta: {
|
|
15099
15362
|
name: "use",
|
|
15100
15363
|
description: "Sugar over `ingest`: download \u2192 store \u2192 wait until describe + embed complete \u2192 return ready library record.\n\nExample: baker images use https://cdn.example.com/hero.png --source uploaded"
|
|
@@ -15140,7 +15403,7 @@ var useCommand = defineCommand116({
|
|
|
15140
15403
|
});
|
|
15141
15404
|
|
|
15142
15405
|
// src/commands/images/index.ts
|
|
15143
|
-
var imagesCommand =
|
|
15406
|
+
var imagesCommand = defineCommand119({
|
|
15144
15407
|
meta: {
|
|
15145
15408
|
name: "images",
|
|
15146
15409
|
description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
|
|
@@ -15210,10 +15473,10 @@ Paid transforms (run on the Convex backend, cost-tracked):
|
|
|
15210
15473
|
});
|
|
15211
15474
|
|
|
15212
15475
|
// src/commands/research/index.ts
|
|
15213
|
-
import { defineCommand as
|
|
15476
|
+
import { defineCommand as defineCommand130 } from "citty";
|
|
15214
15477
|
|
|
15215
15478
|
// src/commands/research/advertisers.ts
|
|
15216
|
-
import { defineCommand as
|
|
15479
|
+
import { defineCommand as defineCommand120 } from "citty";
|
|
15217
15480
|
|
|
15218
15481
|
// src/commands/research/output.ts
|
|
15219
15482
|
var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
|
|
@@ -15326,7 +15589,7 @@ var FIELDS3 = {
|
|
|
15326
15589
|
etv: "Estimated traffic value (USD)",
|
|
15327
15590
|
visibility: "SERP visibility score (0-1)"
|
|
15328
15591
|
};
|
|
15329
|
-
var advertisersCommand =
|
|
15592
|
+
var advertisersCommand = defineCommand120({
|
|
15330
15593
|
meta: {
|
|
15331
15594
|
name: "advertisers",
|
|
15332
15595
|
description: `Find domains competing for a keyword in Google SERPs.
|
|
@@ -15373,7 +15636,7 @@ Examples:
|
|
|
15373
15636
|
});
|
|
15374
15637
|
|
|
15375
15638
|
// src/commands/research/autocomplete.ts
|
|
15376
|
-
import { defineCommand as
|
|
15639
|
+
import { defineCommand as defineCommand121 } from "citty";
|
|
15377
15640
|
registerSchema({
|
|
15378
15641
|
command: "research.autocomplete",
|
|
15379
15642
|
description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
@@ -15396,7 +15659,7 @@ registerSchema({
|
|
|
15396
15659
|
var FIELDS4 = {
|
|
15397
15660
|
suggestion: "Autocomplete suggestion from Google"
|
|
15398
15661
|
};
|
|
15399
|
-
var autocompleteCommand =
|
|
15662
|
+
var autocompleteCommand = defineCommand121({
|
|
15400
15663
|
meta: {
|
|
15401
15664
|
name: "autocomplete",
|
|
15402
15665
|
description: `Get Google Autocomplete suggestions for keyword expansion.
|
|
@@ -15442,7 +15705,7 @@ Examples:
|
|
|
15442
15705
|
});
|
|
15443
15706
|
|
|
15444
15707
|
// src/commands/research/countries.ts
|
|
15445
|
-
import { defineCommand as
|
|
15708
|
+
import { defineCommand as defineCommand122 } from "citty";
|
|
15446
15709
|
registerSchema({
|
|
15447
15710
|
command: "research.countries",
|
|
15448
15711
|
description: "List all supported country codes for --location flag in research commands.",
|
|
@@ -15499,7 +15762,7 @@ var FIELDS5 = {
|
|
|
15499
15762
|
code: "Country code to pass as --location",
|
|
15500
15763
|
name: "Country name"
|
|
15501
15764
|
};
|
|
15502
|
-
var countriesCommand =
|
|
15765
|
+
var countriesCommand = defineCommand122({
|
|
15503
15766
|
meta: {
|
|
15504
15767
|
name: "countries",
|
|
15505
15768
|
description: "List all supported country codes for --location flag."
|
|
@@ -15510,7 +15773,7 @@ var countriesCommand = defineCommand120({
|
|
|
15510
15773
|
});
|
|
15511
15774
|
|
|
15512
15775
|
// src/commands/research/intent.ts
|
|
15513
|
-
import { defineCommand as
|
|
15776
|
+
import { defineCommand as defineCommand123 } from "citty";
|
|
15514
15777
|
registerSchema({
|
|
15515
15778
|
command: "research.intent",
|
|
15516
15779
|
description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. IMPORTANT: If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
|
|
@@ -15533,7 +15796,7 @@ var FIELDS6 = {
|
|
|
15533
15796
|
intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
|
|
15534
15797
|
probability: "Confidence score 0.0-1.0"
|
|
15535
15798
|
};
|
|
15536
|
-
var intentCommand =
|
|
15799
|
+
var intentCommand = defineCommand123({
|
|
15537
15800
|
meta: {
|
|
15538
15801
|
name: "intent",
|
|
15539
15802
|
description: `Classify Google Search intent for keywords. Returns intent type and confidence.
|
|
@@ -15581,7 +15844,7 @@ Examples:
|
|
|
15581
15844
|
});
|
|
15582
15845
|
|
|
15583
15846
|
// src/commands/research/keyword-gap.ts
|
|
15584
|
-
import { defineCommand as
|
|
15847
|
+
import { defineCommand as defineCommand124 } from "citty";
|
|
15585
15848
|
registerSchema({
|
|
15586
15849
|
command: "research.keyword-gap",
|
|
15587
15850
|
description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
@@ -15610,7 +15873,7 @@ var FIELDS7 = {
|
|
|
15610
15873
|
cpc: "Cost per click USD",
|
|
15611
15874
|
their_position: "Competitor's ranking position"
|
|
15612
15875
|
};
|
|
15613
|
-
var keywordGapCommand =
|
|
15876
|
+
var keywordGapCommand = defineCommand124({
|
|
15614
15877
|
meta: {
|
|
15615
15878
|
name: "keyword-gap",
|
|
15616
15879
|
description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
|
|
@@ -15684,7 +15947,7 @@ Examples:
|
|
|
15684
15947
|
});
|
|
15685
15948
|
|
|
15686
15949
|
// src/commands/research/keywords-for-site.ts
|
|
15687
|
-
import { defineCommand as
|
|
15950
|
+
import { defineCommand as defineCommand125 } from "citty";
|
|
15688
15951
|
registerSchema({
|
|
15689
15952
|
command: "research.keywords-for-site",
|
|
15690
15953
|
description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
@@ -15717,7 +15980,7 @@ var FIELDS8 = {
|
|
|
15717
15980
|
competition: "LOW, MEDIUM, or HIGH",
|
|
15718
15981
|
competition_index: "Competition score 0-100"
|
|
15719
15982
|
};
|
|
15720
|
-
var keywordsForSiteCommand =
|
|
15983
|
+
var keywordsForSiteCommand = defineCommand125({
|
|
15721
15984
|
meta: {
|
|
15722
15985
|
name: "keywords-for-site",
|
|
15723
15986
|
description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
|
|
@@ -15770,7 +16033,7 @@ Examples:
|
|
|
15770
16033
|
});
|
|
15771
16034
|
|
|
15772
16035
|
// src/commands/research/languages.ts
|
|
15773
|
-
import { defineCommand as
|
|
16036
|
+
import { defineCommand as defineCommand126 } from "citty";
|
|
15774
16037
|
registerSchema({
|
|
15775
16038
|
command: "research.languages",
|
|
15776
16039
|
description: "List all supported language codes for --language flag in research commands.",
|
|
@@ -15800,7 +16063,7 @@ var FIELDS9 = {
|
|
|
15800
16063
|
code: "Language code to pass as --language",
|
|
15801
16064
|
name: "Language name (also accepted by --language)"
|
|
15802
16065
|
};
|
|
15803
|
-
var languagesCommand2 =
|
|
16066
|
+
var languagesCommand2 = defineCommand126({
|
|
15804
16067
|
meta: {
|
|
15805
16068
|
name: "languages",
|
|
15806
16069
|
description: "List all supported language codes for --language flag."
|
|
@@ -15811,7 +16074,7 @@ var languagesCommand2 = defineCommand124({
|
|
|
15811
16074
|
});
|
|
15812
16075
|
|
|
15813
16076
|
// src/commands/research/lighthouse.ts
|
|
15814
|
-
import { defineCommand as
|
|
16077
|
+
import { defineCommand as defineCommand127 } from "citty";
|
|
15815
16078
|
registerSchema({
|
|
15816
16079
|
command: "research.lighthouse",
|
|
15817
16080
|
description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
|
|
@@ -15830,7 +16093,7 @@ var FIELDS10 = {
|
|
|
15830
16093
|
speed_index_ms: "Speed Index in ms (good: < 3400)",
|
|
15831
16094
|
interactive_ms: "Time to Interactive in ms (good: < 3800)"
|
|
15832
16095
|
};
|
|
15833
|
-
var lighthouseCommand =
|
|
16096
|
+
var lighthouseCommand = defineCommand127({
|
|
15834
16097
|
meta: {
|
|
15835
16098
|
name: "lighthouse",
|
|
15836
16099
|
description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
|
|
@@ -15868,7 +16131,7 @@ Examples:
|
|
|
15868
16131
|
});
|
|
15869
16132
|
|
|
15870
16133
|
// src/commands/research/relevant-pages.ts
|
|
15871
|
-
import { defineCommand as
|
|
16134
|
+
import { defineCommand as defineCommand128 } from "citty";
|
|
15872
16135
|
registerSchema({
|
|
15873
16136
|
command: "research.relevant-pages",
|
|
15874
16137
|
description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
@@ -15894,7 +16157,7 @@ var FIELDS11 = {
|
|
|
15894
16157
|
keywords: "Total organic keywords the page ranks for",
|
|
15895
16158
|
top_10: "Keywords in positions 1-10"
|
|
15896
16159
|
};
|
|
15897
|
-
var relevantPagesCommand =
|
|
16160
|
+
var relevantPagesCommand = defineCommand128({
|
|
15898
16161
|
meta: {
|
|
15899
16162
|
name: "relevant-pages",
|
|
15900
16163
|
description: `Get the top pages of a competitor domain with traffic data.
|
|
@@ -15940,7 +16203,7 @@ Examples:
|
|
|
15940
16203
|
});
|
|
15941
16204
|
|
|
15942
16205
|
// src/commands/research/web.ts
|
|
15943
|
-
import { defineCommand as
|
|
16206
|
+
import { defineCommand as defineCommand129 } from "citty";
|
|
15944
16207
|
registerSchema({
|
|
15945
16208
|
command: "research.web",
|
|
15946
16209
|
description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
|
|
@@ -15991,7 +16254,7 @@ async function runDeepResearch(question) {
|
|
|
15991
16254
|
}
|
|
15992
16255
|
throw new Error("Deep research timed out");
|
|
15993
16256
|
}
|
|
15994
|
-
var webCommand =
|
|
16257
|
+
var webCommand = defineCommand129({
|
|
15995
16258
|
meta: {
|
|
15996
16259
|
name: "web",
|
|
15997
16260
|
description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
|
|
@@ -16051,7 +16314,7 @@ Examples:
|
|
|
16051
16314
|
});
|
|
16052
16315
|
|
|
16053
16316
|
// src/commands/research/index.ts
|
|
16054
|
-
var researchCommand =
|
|
16317
|
+
var researchCommand = defineCommand130({
|
|
16055
16318
|
meta: {
|
|
16056
16319
|
name: "research",
|
|
16057
16320
|
description: `Competitive intelligence and AI-powered research commands.
|
|
@@ -16091,10 +16354,10 @@ Examples:
|
|
|
16091
16354
|
});
|
|
16092
16355
|
|
|
16093
16356
|
// src/commands/scheduled-actions/index.ts
|
|
16094
|
-
import { defineCommand as
|
|
16357
|
+
import { defineCommand as defineCommand137 } from "citty";
|
|
16095
16358
|
|
|
16096
16359
|
// src/commands/scheduled-actions/create.ts
|
|
16097
|
-
import { defineCommand as
|
|
16360
|
+
import { defineCommand as defineCommand131 } from "citty";
|
|
16098
16361
|
|
|
16099
16362
|
// src/commands/scheduled-actions/shared.ts
|
|
16100
16363
|
var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
|
|
@@ -16199,7 +16462,7 @@ registerSchema({
|
|
|
16199
16462
|
prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
|
|
16200
16463
|
}
|
|
16201
16464
|
});
|
|
16202
|
-
var createCommand2 =
|
|
16465
|
+
var createCommand2 = defineCommand131({
|
|
16203
16466
|
meta: {
|
|
16204
16467
|
name: "create",
|
|
16205
16468
|
description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
|
|
@@ -16247,7 +16510,7 @@ var createCommand2 = defineCommand129({
|
|
|
16247
16510
|
});
|
|
16248
16511
|
|
|
16249
16512
|
// src/commands/scheduled-actions/delete.ts
|
|
16250
|
-
import { defineCommand as
|
|
16513
|
+
import { defineCommand as defineCommand132 } from "citty";
|
|
16251
16514
|
registerSchema({
|
|
16252
16515
|
command: "scheduled-actions.delete",
|
|
16253
16516
|
description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
|
|
@@ -16255,7 +16518,7 @@ registerSchema({
|
|
|
16255
16518
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
16256
16519
|
}
|
|
16257
16520
|
});
|
|
16258
|
-
var deleteCommand2 =
|
|
16521
|
+
var deleteCommand2 = defineCommand132({
|
|
16259
16522
|
meta: {
|
|
16260
16523
|
name: "delete",
|
|
16261
16524
|
description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
|
|
@@ -16284,7 +16547,7 @@ var deleteCommand2 = defineCommand130({
|
|
|
16284
16547
|
});
|
|
16285
16548
|
|
|
16286
16549
|
// src/commands/scheduled-actions/get.ts
|
|
16287
|
-
import { defineCommand as
|
|
16550
|
+
import { defineCommand as defineCommand133 } from "citty";
|
|
16288
16551
|
registerSchema({
|
|
16289
16552
|
command: "scheduled-actions.get",
|
|
16290
16553
|
description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
|
|
@@ -16292,7 +16555,7 @@ registerSchema({
|
|
|
16292
16555
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
16293
16556
|
}
|
|
16294
16557
|
});
|
|
16295
|
-
var getCommand3 =
|
|
16558
|
+
var getCommand3 = defineCommand133({
|
|
16296
16559
|
meta: {
|
|
16297
16560
|
name: "get",
|
|
16298
16561
|
description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
|
|
@@ -16329,13 +16592,13 @@ var getCommand3 = defineCommand131({
|
|
|
16329
16592
|
});
|
|
16330
16593
|
|
|
16331
16594
|
// src/commands/scheduled-actions/list.ts
|
|
16332
|
-
import { defineCommand as
|
|
16595
|
+
import { defineCommand as defineCommand134 } from "citty";
|
|
16333
16596
|
registerSchema({
|
|
16334
16597
|
command: "scheduled-actions.list",
|
|
16335
16598
|
description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
|
|
16336
16599
|
args: {}
|
|
16337
16600
|
});
|
|
16338
|
-
var listCommand3 =
|
|
16601
|
+
var listCommand3 = defineCommand134({
|
|
16339
16602
|
meta: {
|
|
16340
16603
|
name: "list",
|
|
16341
16604
|
description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
|
|
@@ -16356,7 +16619,7 @@ var listCommand3 = defineCommand132({
|
|
|
16356
16619
|
});
|
|
16357
16620
|
|
|
16358
16621
|
// src/commands/scheduled-actions/trigger.ts
|
|
16359
|
-
import { defineCommand as
|
|
16622
|
+
import { defineCommand as defineCommand135 } from "citty";
|
|
16360
16623
|
registerSchema({
|
|
16361
16624
|
command: "scheduled-actions.trigger",
|
|
16362
16625
|
description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
|
|
@@ -16364,7 +16627,7 @@ registerSchema({
|
|
|
16364
16627
|
id: { type: "string", description: "Published scheduled action ID", required: true }
|
|
16365
16628
|
}
|
|
16366
16629
|
});
|
|
16367
|
-
var triggerCommand =
|
|
16630
|
+
var triggerCommand = defineCommand135({
|
|
16368
16631
|
meta: {
|
|
16369
16632
|
name: "trigger",
|
|
16370
16633
|
description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
|
|
@@ -16401,7 +16664,7 @@ var triggerCommand = defineCommand133({
|
|
|
16401
16664
|
});
|
|
16402
16665
|
|
|
16403
16666
|
// src/commands/scheduled-actions/update.ts
|
|
16404
|
-
import { defineCommand as
|
|
16667
|
+
import { defineCommand as defineCommand136 } from "citty";
|
|
16405
16668
|
registerSchema({
|
|
16406
16669
|
command: "scheduled-actions.update",
|
|
16407
16670
|
description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
|
|
@@ -16426,7 +16689,7 @@ registerSchema({
|
|
|
16426
16689
|
prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
|
|
16427
16690
|
}
|
|
16428
16691
|
});
|
|
16429
|
-
var updateCommand2 =
|
|
16692
|
+
var updateCommand2 = defineCommand136({
|
|
16430
16693
|
meta: {
|
|
16431
16694
|
name: "update",
|
|
16432
16695
|
description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
|
|
@@ -16496,7 +16759,7 @@ var updateCommand2 = defineCommand134({
|
|
|
16496
16759
|
});
|
|
16497
16760
|
|
|
16498
16761
|
// src/commands/scheduled-actions/index.ts
|
|
16499
|
-
var scheduledActionsCommand =
|
|
16762
|
+
var scheduledActionsCommand = defineCommand137({
|
|
16500
16763
|
meta: {
|
|
16501
16764
|
name: "scheduled-actions",
|
|
16502
16765
|
description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
|
|
@@ -16522,8 +16785,8 @@ Examples:
|
|
|
16522
16785
|
});
|
|
16523
16786
|
|
|
16524
16787
|
// src/commands/schema.ts
|
|
16525
|
-
import { defineCommand as
|
|
16526
|
-
var schemaCommand =
|
|
16788
|
+
import { defineCommand as defineCommand138 } from "citty";
|
|
16789
|
+
var schemaCommand = defineCommand138({
|
|
16527
16790
|
meta: {
|
|
16528
16791
|
name: "schema",
|
|
16529
16792
|
description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
|
|
@@ -16559,10 +16822,10 @@ var schemaCommand = defineCommand136({
|
|
|
16559
16822
|
});
|
|
16560
16823
|
|
|
16561
16824
|
// src/commands/testimonials/index.ts
|
|
16562
|
-
import { defineCommand as
|
|
16825
|
+
import { defineCommand as defineCommand142 } from "citty";
|
|
16563
16826
|
|
|
16564
16827
|
// src/commands/testimonials/get.ts
|
|
16565
|
-
import { defineCommand as
|
|
16828
|
+
import { defineCommand as defineCommand139 } from "citty";
|
|
16566
16829
|
registerSchema({
|
|
16567
16830
|
command: "testimonials.get",
|
|
16568
16831
|
description: "Get a single testimonial by ID",
|
|
@@ -16570,7 +16833,7 @@ registerSchema({
|
|
|
16570
16833
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
16571
16834
|
}
|
|
16572
16835
|
});
|
|
16573
|
-
var getCommand4 =
|
|
16836
|
+
var getCommand4 = defineCommand139({
|
|
16574
16837
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
16575
16838
|
args: {
|
|
16576
16839
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -16607,7 +16870,7 @@ var getCommand4 = defineCommand137({
|
|
|
16607
16870
|
});
|
|
16608
16871
|
|
|
16609
16872
|
// src/commands/testimonials/list.ts
|
|
16610
|
-
import { defineCommand as
|
|
16873
|
+
import { defineCommand as defineCommand140 } from "citty";
|
|
16611
16874
|
registerSchema({
|
|
16612
16875
|
command: "testimonials.list",
|
|
16613
16876
|
description: "List testimonials with optional filters.",
|
|
@@ -16637,7 +16900,7 @@ registerSchema({
|
|
|
16637
16900
|
limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
|
|
16638
16901
|
}
|
|
16639
16902
|
});
|
|
16640
|
-
var listCommand4 =
|
|
16903
|
+
var listCommand4 = defineCommand140({
|
|
16641
16904
|
meta: {
|
|
16642
16905
|
name: "list",
|
|
16643
16906
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -16686,7 +16949,7 @@ var listCommand4 = defineCommand138({
|
|
|
16686
16949
|
});
|
|
16687
16950
|
|
|
16688
16951
|
// src/commands/testimonials/search.ts
|
|
16689
|
-
import { defineCommand as
|
|
16952
|
+
import { defineCommand as defineCommand141 } from "citty";
|
|
16690
16953
|
registerSchema({
|
|
16691
16954
|
command: "testimonials.search",
|
|
16692
16955
|
description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
|
|
@@ -16717,7 +16980,7 @@ registerSchema({
|
|
|
16717
16980
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
16718
16981
|
}
|
|
16719
16982
|
});
|
|
16720
|
-
var searchCommand2 =
|
|
16983
|
+
var searchCommand2 = defineCommand141({
|
|
16721
16984
|
meta: {
|
|
16722
16985
|
name: "search",
|
|
16723
16986
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -16791,7 +17054,7 @@ var searchCommand2 = defineCommand139({
|
|
|
16791
17054
|
var tagsCommand3 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
16792
17055
|
|
|
16793
17056
|
// src/commands/testimonials/index.ts
|
|
16794
|
-
var testimonialsCommand =
|
|
17057
|
+
var testimonialsCommand = defineCommand142({
|
|
16795
17058
|
meta: {
|
|
16796
17059
|
name: "testimonials",
|
|
16797
17060
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -16812,10 +17075,10 @@ Examples:
|
|
|
16812
17075
|
});
|
|
16813
17076
|
|
|
16814
17077
|
// src/commands/videos/index.ts
|
|
16815
|
-
import { defineCommand as
|
|
17078
|
+
import { defineCommand as defineCommand147 } from "citty";
|
|
16816
17079
|
|
|
16817
17080
|
// src/commands/videos/delete.ts
|
|
16818
|
-
import { defineCommand as
|
|
17081
|
+
import { defineCommand as defineCommand143 } from "citty";
|
|
16819
17082
|
registerSchema({
|
|
16820
17083
|
command: "videos.delete",
|
|
16821
17084
|
description: "Delete a video by ID",
|
|
@@ -16829,7 +17092,7 @@ registerSchema({
|
|
|
16829
17092
|
}
|
|
16830
17093
|
}
|
|
16831
17094
|
});
|
|
16832
|
-
var deleteCommand3 =
|
|
17095
|
+
var deleteCommand3 = defineCommand143({
|
|
16833
17096
|
meta: {
|
|
16834
17097
|
name: "delete",
|
|
16835
17098
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -16870,7 +17133,7 @@ var deleteCommand3 = defineCommand141({
|
|
|
16870
17133
|
});
|
|
16871
17134
|
|
|
16872
17135
|
// src/commands/videos/get.ts
|
|
16873
|
-
import { defineCommand as
|
|
17136
|
+
import { defineCommand as defineCommand144 } from "citty";
|
|
16874
17137
|
registerSchema({
|
|
16875
17138
|
command: "videos.get",
|
|
16876
17139
|
description: "Get a single video by ID",
|
|
@@ -16878,7 +17141,7 @@ registerSchema({
|
|
|
16878
17141
|
id: { type: "string", description: "Video ID", required: true }
|
|
16879
17142
|
}
|
|
16880
17143
|
});
|
|
16881
|
-
var getCommand5 =
|
|
17144
|
+
var getCommand5 = defineCommand144({
|
|
16882
17145
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
16883
17146
|
args: {
|
|
16884
17147
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -16915,7 +17178,7 @@ var getCommand5 = defineCommand142({
|
|
|
16915
17178
|
});
|
|
16916
17179
|
|
|
16917
17180
|
// src/commands/videos/search.ts
|
|
16918
|
-
import { defineCommand as
|
|
17181
|
+
import { defineCommand as defineCommand145 } from "citty";
|
|
16919
17182
|
registerSchema({
|
|
16920
17183
|
command: "videos.search",
|
|
16921
17184
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -16925,7 +17188,7 @@ registerSchema({
|
|
|
16925
17188
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
16926
17189
|
}
|
|
16927
17190
|
});
|
|
16928
|
-
var searchCommand3 =
|
|
17191
|
+
var searchCommand3 = defineCommand145({
|
|
16929
17192
|
meta: {
|
|
16930
17193
|
name: "search",
|
|
16931
17194
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -16975,10 +17238,10 @@ var searchCommand3 = defineCommand143({
|
|
|
16975
17238
|
var tagsCommand4 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
16976
17239
|
|
|
16977
17240
|
// src/commands/videos/upload.ts
|
|
16978
|
-
import { readFile as
|
|
17241
|
+
import { readFile as readFile12, stat as stat3 } from "fs/promises";
|
|
16979
17242
|
import { extname as extname3 } from "path";
|
|
16980
|
-
import { defineCommand as
|
|
16981
|
-
var
|
|
17243
|
+
import { defineCommand as defineCommand146 } from "citty";
|
|
17244
|
+
var MIME_MAP = {
|
|
16982
17245
|
".mp4": "video/mp4",
|
|
16983
17246
|
".mov": "video/quicktime",
|
|
16984
17247
|
".webm": "video/webm",
|
|
@@ -17003,15 +17266,15 @@ registerSchema({
|
|
|
17003
17266
|
}
|
|
17004
17267
|
}
|
|
17005
17268
|
});
|
|
17006
|
-
function
|
|
17269
|
+
function detectContentType(filePath) {
|
|
17007
17270
|
const ext = extname3(filePath).toLowerCase();
|
|
17008
|
-
const mime =
|
|
17271
|
+
const mime = MIME_MAP[ext];
|
|
17009
17272
|
if (!mime) {
|
|
17010
17273
|
throw new ApiError("VALIDATION_ERROR", `Cannot detect content type for extension "${ext}". Use --content-type.`);
|
|
17011
17274
|
}
|
|
17012
17275
|
return mime;
|
|
17013
17276
|
}
|
|
17014
|
-
var uploadCommand2 =
|
|
17277
|
+
var uploadCommand2 = defineCommand146({
|
|
17015
17278
|
meta: {
|
|
17016
17279
|
name: "upload",
|
|
17017
17280
|
description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
|
|
@@ -17028,7 +17291,7 @@ var uploadCommand2 = defineCommand144({
|
|
|
17028
17291
|
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path is required" } });
|
|
17029
17292
|
process.exit(1);
|
|
17030
17293
|
}
|
|
17031
|
-
const contentType = args["content-type"] ||
|
|
17294
|
+
const contentType = args["content-type"] || detectContentType(filePath);
|
|
17032
17295
|
if (args["dry-run"]) {
|
|
17033
17296
|
const fileStats = await stat3(filePath);
|
|
17034
17297
|
writeJson({
|
|
@@ -17040,7 +17303,7 @@ var uploadCommand2 = defineCommand144({
|
|
|
17040
17303
|
return;
|
|
17041
17304
|
}
|
|
17042
17305
|
const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
|
|
17043
|
-
const fileBuffer = await
|
|
17306
|
+
const fileBuffer = await readFile12(filePath);
|
|
17044
17307
|
const uploadResponse = await fetch(uploadUrl, {
|
|
17045
17308
|
method: "PUT",
|
|
17046
17309
|
headers: { "Content-Type": contentType },
|
|
@@ -17065,7 +17328,7 @@ var uploadCommand2 = defineCommand144({
|
|
|
17065
17328
|
});
|
|
17066
17329
|
|
|
17067
17330
|
// src/commands/videos/index.ts
|
|
17068
|
-
var videosCommand =
|
|
17331
|
+
var videosCommand = defineCommand147({
|
|
17069
17332
|
meta: {
|
|
17070
17333
|
name: "videos",
|
|
17071
17334
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
|
|
@@ -17088,10 +17351,10 @@ Examples:
|
|
|
17088
17351
|
});
|
|
17089
17352
|
|
|
17090
17353
|
// src/commands/winning-ads/index.ts
|
|
17091
|
-
import { defineCommand as
|
|
17354
|
+
import { defineCommand as defineCommand150 } from "citty";
|
|
17092
17355
|
|
|
17093
17356
|
// src/commands/winning-ads/advertisers.ts
|
|
17094
|
-
import { defineCommand as
|
|
17357
|
+
import { defineCommand as defineCommand148 } from "citty";
|
|
17095
17358
|
registerSchema({
|
|
17096
17359
|
command: "winning-ads.advertisers",
|
|
17097
17360
|
description: "Resolve a brand name to advertiser_id(s) in the ad-dna corpus \u2014 to find your OWN advertiser (to --exclude-advertiser) or a competitor (to --advertiser-id).",
|
|
@@ -17104,7 +17367,7 @@ registerSchema({
|
|
|
17104
17367
|
function identity(record) {
|
|
17105
17368
|
return record;
|
|
17106
17369
|
}
|
|
17107
|
-
var advertisersCommand2 =
|
|
17370
|
+
var advertisersCommand2 = defineCommand148({
|
|
17108
17371
|
meta: {
|
|
17109
17372
|
name: "advertisers",
|
|
17110
17373
|
description: 'Resolve a brand name to advertiser_id(s). Use it to find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id. Example: baker winning-ads advertisers "Deel" --output md'
|
|
@@ -17155,7 +17418,7 @@ var advertisersCommand2 = defineCommand146({
|
|
|
17155
17418
|
});
|
|
17156
17419
|
|
|
17157
17420
|
// src/commands/winning-ads/search.ts
|
|
17158
|
-
import { defineCommand as
|
|
17421
|
+
import { defineCommand as defineCommand149 } from "citty";
|
|
17159
17422
|
registerSchema({
|
|
17160
17423
|
command: "winning-ads.search",
|
|
17161
17424
|
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.",
|
|
@@ -17263,7 +17526,7 @@ function buildSearchBody(args) {
|
|
|
17263
17526
|
}
|
|
17264
17527
|
return body;
|
|
17265
17528
|
}
|
|
17266
|
-
var searchCommand4 =
|
|
17529
|
+
var searchCommand4 = defineCommand149({
|
|
17267
17530
|
meta: {
|
|
17268
17531
|
name: "search",
|
|
17269
17532
|
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"
|
|
@@ -17375,7 +17638,7 @@ var searchCommand4 = defineCommand147({
|
|
|
17375
17638
|
});
|
|
17376
17639
|
|
|
17377
17640
|
// src/commands/winning-ads/index.ts
|
|
17378
|
-
var winningAdsCommand =
|
|
17641
|
+
var winningAdsCommand = defineCommand150({
|
|
17379
17642
|
meta: {
|
|
17380
17643
|
name: "winning-ads",
|
|
17381
17644
|
description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
|
|
@@ -17415,7 +17678,7 @@ function getCliVersion() {
|
|
|
17415
17678
|
}
|
|
17416
17679
|
|
|
17417
17680
|
// src/cli.ts
|
|
17418
|
-
var main =
|
|
17681
|
+
var main = defineCommand151({
|
|
17419
17682
|
meta: {
|
|
17420
17683
|
name: "baker",
|
|
17421
17684
|
version: getCliVersion(),
|
|
@@ -17434,6 +17697,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
17434
17697
|
ga4: ga4Command,
|
|
17435
17698
|
gsc: gscCommand,
|
|
17436
17699
|
research: researchCommand,
|
|
17700
|
+
creatives: creativesCommand3,
|
|
17437
17701
|
images: imagesCommand,
|
|
17438
17702
|
videos: videosCommand,
|
|
17439
17703
|
testimonials: testimonialsCommand,
|