@koda-sl/baker-cli 0.149.0-dev.7b64ca6b5 → 0.150.0-dev.a970fa118
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 +45 -32
- package/dist/{chunk-ZBRVPPJP.js → chunk-JBDSJZBZ.js} +61 -18
- package/dist/chunk-JBDSJZBZ.js.map +1 -0
- package/dist/cli.js +935 -651
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZBRVPPJP.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
imageProfileFor,
|
|
25
25
|
isPersistedAssetRef,
|
|
26
26
|
looksLikeHttpUrl,
|
|
27
|
+
nearestSupportedAspectRatio,
|
|
27
28
|
parseRefExpr,
|
|
28
29
|
platformFormats,
|
|
29
30
|
requireCredentialsFromEnv,
|
|
@@ -31,10 +32,11 @@ import {
|
|
|
31
32
|
sha256Hex,
|
|
32
33
|
spineInputFlags,
|
|
33
34
|
spineInputOps,
|
|
35
|
+
supportsParam,
|
|
34
36
|
toModelSafeImage,
|
|
35
37
|
ulid,
|
|
36
38
|
validateCanvasDeep
|
|
37
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-JBDSJZBZ.js";
|
|
38
40
|
import {
|
|
39
41
|
csvOrJson,
|
|
40
42
|
daysAgoIso,
|
|
@@ -73,7 +75,7 @@ import {
|
|
|
73
75
|
} from "./chunk-RK67WL4O.js";
|
|
74
76
|
|
|
75
77
|
// src/cli.ts
|
|
76
|
-
import { defineCommand as
|
|
78
|
+
import { defineCommand as defineCommand180, runMain } from "citty";
|
|
77
79
|
|
|
78
80
|
// src/commands/actions/index.ts
|
|
79
81
|
import { defineCommand as defineCommand18 } from "citty";
|
|
@@ -2544,13 +2546,6 @@ var imageDocSchema = z8.object({
|
|
|
2544
2546
|
width: z8.number().optional(),
|
|
2545
2547
|
height: z8.number().optional(),
|
|
2546
2548
|
aspectRatio: z8.number().optional(),
|
|
2547
|
-
/** Any non-opaque pixel in the decoded image. */
|
|
2548
|
-
hasAlpha: z8.boolean().optional(),
|
|
2549
|
-
/** Opaque pixels ÷ their bounding-box area, 0..1. Feed with `aspectRatio`
|
|
2550
|
-
* into `classifyLogoShape` to tell a brand wordmark from an app-icon plate
|
|
2551
|
-
* before placing it — the library keeps assets forever, so a bad ingest is
|
|
2552
|
-
* otherwise indistinguishable from a good one months later. */
|
|
2553
|
-
solidity: z8.number().optional(),
|
|
2554
2549
|
dominantColor: z8.string().optional(),
|
|
2555
2550
|
imagePalette: z8.array(z8.string()).optional(),
|
|
2556
2551
|
thumbhashDataUri: z8.string().optional(),
|
|
@@ -2633,13 +2628,6 @@ var imageSearchResultSchema = z8.object({
|
|
|
2633
2628
|
width: z8.number().optional(),
|
|
2634
2629
|
height: z8.number().optional(),
|
|
2635
2630
|
aspectRatio: z8.number().optional(),
|
|
2636
|
-
/** Any non-opaque pixel in the decoded image. */
|
|
2637
|
-
hasAlpha: z8.boolean().optional(),
|
|
2638
|
-
/** Opaque pixels ÷ their bounding-box area, 0..1. Feed with `aspectRatio`
|
|
2639
|
-
* into `classifyLogoShape` to tell a brand wordmark from an app-icon plate
|
|
2640
|
-
* before placing it — the library keeps assets forever, so a bad ingest is
|
|
2641
|
-
* otherwise indistinguishable from a good one months later. */
|
|
2642
|
-
solidity: z8.number().optional(),
|
|
2643
2631
|
dominantColor: z8.string().optional(),
|
|
2644
2632
|
imagePalette: z8.array(z8.string()).optional(),
|
|
2645
2633
|
source: z8.string(),
|
|
@@ -2721,8 +2709,9 @@ var rgbTriple = z8.tuple([
|
|
|
2721
2709
|
z8.number().int().min(0).max(255)
|
|
2722
2710
|
]);
|
|
2723
2711
|
var imageGenerateModelSchema = z8.enum([
|
|
2712
|
+
"openai/gpt-image-2",
|
|
2713
|
+
// Legacy — see the registry entry; kept so pre-switch canvases still run.
|
|
2724
2714
|
"openai/gpt-5.4-image-2",
|
|
2725
|
-
"google/gemini-3.5-flash",
|
|
2726
2715
|
"google/gemini-3.1-flash-image-preview",
|
|
2727
2716
|
"google/gemini-3-pro-image-preview",
|
|
2728
2717
|
"recraft/recraft-v4.1-pro-vector"
|
|
@@ -2763,13 +2752,10 @@ var imagesLogoRequestSchema = z8.object({
|
|
|
2763
2752
|
descriptionContext: z8.string().optional()
|
|
2764
2753
|
});
|
|
2765
2754
|
var imagesLogoResponseSchema = providerHitsResponseSchema();
|
|
2766
|
-
var hexColorSchema = z8.string().transform((value) => value.trim().replace(/^%23/i, "#")).refine((value) => /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value), {
|
|
2767
|
-
message: "Expected a hex color like #D7FFA4"
|
|
2768
|
-
}).transform((value) => value.startsWith("#") ? value : `#${value}`);
|
|
2769
2755
|
var imagesIconRequestSchema = z8.object({
|
|
2770
2756
|
name: z8.string().min(1),
|
|
2771
2757
|
set: z8.string().optional(),
|
|
2772
|
-
color:
|
|
2758
|
+
color: z8.string().optional(),
|
|
2773
2759
|
width: z8.coerce.number().int().positive().optional(),
|
|
2774
2760
|
autoIngest: z8.coerce.number().int().min(0).max(20).optional(),
|
|
2775
2761
|
descriptionContext: z8.string().optional()
|
|
@@ -2819,19 +2805,6 @@ var imagesIngestResponseSchema = z8.object({
|
|
|
2819
2805
|
contentHash: z8.string()
|
|
2820
2806
|
});
|
|
2821
2807
|
|
|
2822
|
-
// ../api/src/logoShape.ts
|
|
2823
|
-
var PLATE_SOLIDITY_THRESHOLD = 0.6;
|
|
2824
|
-
var WORDMARK_MIN_ASPECT = 1.8;
|
|
2825
|
-
function classifyLogoShape({
|
|
2826
|
-
aspectRatio: aspectRatio2,
|
|
2827
|
-
hasAlpha,
|
|
2828
|
-
solidity
|
|
2829
|
-
}) {
|
|
2830
|
-
if (solidity === void 0 || aspectRatio2 === void 0) return "unknown";
|
|
2831
|
-
if (solidity >= PLATE_SOLIDITY_THRESHOLD || hasAlpha === false) return "plated-icon";
|
|
2832
|
-
return aspectRatio2 >= WORDMARK_MIN_ASPECT ? "wordmark" : "symbol";
|
|
2833
|
-
}
|
|
2834
|
-
|
|
2835
2808
|
// ../api/src/tags.ts
|
|
2836
2809
|
import { z as z9 } from "zod";
|
|
2837
2810
|
var TAG_TYPES = [
|
|
@@ -5425,17 +5398,27 @@ var googleDraftStageRequestSchema = z13.object({
|
|
|
5425
5398
|
chatId: z13.string(),
|
|
5426
5399
|
op: googleDraftOpInputSchema
|
|
5427
5400
|
});
|
|
5428
|
-
var googleDraftStageResponseSchema = z13.
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5401
|
+
var googleDraftStageResponseSchema = z13.discriminatedUnion("staged", [
|
|
5402
|
+
z13.object({
|
|
5403
|
+
staged: z13.literal(true),
|
|
5404
|
+
ref: z13.string(),
|
|
5405
|
+
kind: googleDraftOpKindSchema,
|
|
5406
|
+
mode: googleWriteModeSchema,
|
|
5407
|
+
dependsOn: z13.array(z13.string()),
|
|
5408
|
+
summary: z13.string(),
|
|
5409
|
+
warnings: z13.array(z13.string()),
|
|
5410
|
+
/** True when the op amended an already-staged op in place instead of appending a new one. */
|
|
5411
|
+
amended: z13.boolean().optional()
|
|
5412
|
+
}),
|
|
5413
|
+
z13.object({
|
|
5414
|
+
staged: z13.literal(false),
|
|
5415
|
+
noop: z13.literal(true),
|
|
5416
|
+
kind: googleDraftOpKindSchema,
|
|
5417
|
+
mode: googleWriteModeSchema,
|
|
5418
|
+
summary: z13.string(),
|
|
5419
|
+
reason: z13.string()
|
|
5420
|
+
})
|
|
5421
|
+
]);
|
|
5439
5422
|
var googleDraftAmendRequestSchema = z13.object({
|
|
5440
5423
|
chatId: z13.string(),
|
|
5441
5424
|
ref: z13.string(),
|
|
@@ -5462,7 +5445,8 @@ var googleDraftStageBatchResponseSchema = z13.object({
|
|
|
5462
5445
|
summary: z13.string(),
|
|
5463
5446
|
warnings: z13.array(z13.string())
|
|
5464
5447
|
})
|
|
5465
|
-
)
|
|
5448
|
+
),
|
|
5449
|
+
skipped: z13.array(z13.object({ kind: googleDraftOpKindSchema, summary: z13.string(), reason: z13.string() })).optional()
|
|
5466
5450
|
});
|
|
5467
5451
|
var googleDraftOpViewSchema = z13.object({
|
|
5468
5452
|
ref: z13.string(),
|
|
@@ -5702,11 +5686,11 @@ function rawTextEntries(value) {
|
|
|
5702
5686
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
5703
5687
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
5704
5688
|
}
|
|
5705
|
-
function rawFileEntries(
|
|
5706
|
-
if (typeof
|
|
5689
|
+
function rawFileEntries(path28) {
|
|
5690
|
+
if (typeof path28 !== "string" || path28.length === 0) {
|
|
5707
5691
|
return [];
|
|
5708
5692
|
}
|
|
5709
|
-
return readFileSync2(
|
|
5693
|
+
return readFileSync2(path28, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
5710
5694
|
}
|
|
5711
5695
|
function keywordEntries(args) {
|
|
5712
5696
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -5721,7 +5705,7 @@ function keywordEntries(args) {
|
|
|
5721
5705
|
const seen = /* @__PURE__ */ new Set();
|
|
5722
5706
|
for (const item of raw) {
|
|
5723
5707
|
const entry = parseKeywordEntry(item, defaultMatch);
|
|
5724
|
-
const key =
|
|
5708
|
+
const key = JSON.stringify([entry.text, entry.matchType]);
|
|
5725
5709
|
if (!seen.has(key)) {
|
|
5726
5710
|
seen.add(key);
|
|
5727
5711
|
entries.push(entry);
|
|
@@ -5729,19 +5713,19 @@ function keywordEntries(args) {
|
|
|
5729
5713
|
}
|
|
5730
5714
|
return entries;
|
|
5731
5715
|
}
|
|
5732
|
-
function loadJsonFileArg(
|
|
5733
|
-
if (typeof
|
|
5716
|
+
function loadJsonFileArg(path28) {
|
|
5717
|
+
if (typeof path28 !== "string" || path28.length === 0) {
|
|
5734
5718
|
return {};
|
|
5735
5719
|
}
|
|
5736
5720
|
try {
|
|
5737
|
-
const parsed = JSON.parse(readFileSync2(
|
|
5721
|
+
const parsed = JSON.parse(readFileSync2(path28, "utf8"));
|
|
5738
5722
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5739
|
-
failWriteValidation(`${
|
|
5723
|
+
failWriteValidation(`${path28} must contain a JSON object`);
|
|
5740
5724
|
}
|
|
5741
5725
|
return parsed;
|
|
5742
5726
|
} catch (err) {
|
|
5743
5727
|
if (err instanceof SyntaxError) {
|
|
5744
|
-
failWriteValidation(`${
|
|
5728
|
+
failWriteValidation(`${path28} is not valid JSON: ${err.message}`);
|
|
5745
5729
|
}
|
|
5746
5730
|
throw err;
|
|
5747
5731
|
}
|
|
@@ -5788,6 +5772,9 @@ function handleGoogleError(err) {
|
|
|
5788
5772
|
});
|
|
5789
5773
|
process.exit(1);
|
|
5790
5774
|
}
|
|
5775
|
+
function noopHints(data) {
|
|
5776
|
+
return data.staged ? [] : [`NOT STAGED \u2014 ${data.reason}. Nothing to publish for this change; say so rather than reporting it as done.`];
|
|
5777
|
+
}
|
|
5791
5778
|
async function stageGoogleOp(raw, hints) {
|
|
5792
5779
|
const preflight = googleDraftOpInputSchema.safeParse(raw);
|
|
5793
5780
|
if (!preflight.success) {
|
|
@@ -5805,7 +5792,8 @@ async function stageGoogleOp(raw, hints) {
|
|
|
5805
5792
|
try {
|
|
5806
5793
|
const chatId = requireChatId();
|
|
5807
5794
|
const response = await apiPost("/api/ads/google/draft/stage", { chatId, op: preflight.data });
|
|
5808
|
-
|
|
5795
|
+
const allHints = [...noopHints(response.data), ...hints ?? []];
|
|
5796
|
+
writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
|
|
5809
5797
|
} catch (err) {
|
|
5810
5798
|
handleGoogleError(err);
|
|
5811
5799
|
}
|
|
@@ -5841,7 +5829,14 @@ async function stageGoogleOps(rawOps, hints) {
|
|
|
5841
5829
|
try {
|
|
5842
5830
|
const chatId = requireChatId();
|
|
5843
5831
|
const response = await apiPost("/api/ads/google/draft/stage-batch", { chatId, ops });
|
|
5844
|
-
|
|
5832
|
+
const skipped = response.data.skipped ?? [];
|
|
5833
|
+
const allHints = [
|
|
5834
|
+
...skipped.length > 0 ? [
|
|
5835
|
+
`${skipped.length} of ${ops.length} op(s) NOT STAGED \u2014 already in the requested state: ${skipped.map((s) => s.reason).join("; ")}`
|
|
5836
|
+
] : [],
|
|
5837
|
+
...hints ?? []
|
|
5838
|
+
];
|
|
5839
|
+
writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
|
|
5845
5840
|
} catch (err) {
|
|
5846
5841
|
handleGoogleError(err);
|
|
5847
5842
|
}
|
|
@@ -5852,10 +5847,10 @@ async function stageUpdate(kind, customerId, target, payload) {
|
|
|
5852
5847
|
async function stageTarget(kind, customerId, target) {
|
|
5853
5848
|
await stageGoogleOp({ kind, customerId, target });
|
|
5854
5849
|
}
|
|
5855
|
-
async function draftAction(
|
|
5850
|
+
async function draftAction(path28, body) {
|
|
5856
5851
|
try {
|
|
5857
5852
|
const chatId = requireChatId();
|
|
5858
|
-
const response = await apiPost(
|
|
5853
|
+
const response = await apiPost(path28, { chatId, ...body });
|
|
5859
5854
|
writeJsonEnvelope(response);
|
|
5860
5855
|
} catch (err) {
|
|
5861
5856
|
handleGoogleError(err);
|
|
@@ -9438,19 +9433,19 @@ function failWriteValidation2(message) {
|
|
|
9438
9433
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
9439
9434
|
process.exit(1);
|
|
9440
9435
|
}
|
|
9441
|
-
function loadJsonFileArg2(
|
|
9442
|
-
if (typeof
|
|
9436
|
+
function loadJsonFileArg2(path28) {
|
|
9437
|
+
if (typeof path28 !== "string" || path28.length === 0) {
|
|
9443
9438
|
return {};
|
|
9444
9439
|
}
|
|
9445
9440
|
try {
|
|
9446
|
-
const parsed = JSON.parse(readFileSync6(
|
|
9441
|
+
const parsed = JSON.parse(readFileSync6(path28, "utf8"));
|
|
9447
9442
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9448
|
-
failWriteValidation2(`${
|
|
9443
|
+
failWriteValidation2(`${path28} must contain a JSON object`);
|
|
9449
9444
|
}
|
|
9450
9445
|
return parsed;
|
|
9451
9446
|
} catch (err) {
|
|
9452
9447
|
if (err instanceof SyntaxError) {
|
|
9453
|
-
failWriteValidation2(`${
|
|
9448
|
+
failWriteValidation2(`${path28} is not valid JSON: ${err.message}`);
|
|
9454
9449
|
}
|
|
9455
9450
|
throw err;
|
|
9456
9451
|
}
|
|
@@ -9535,15 +9530,15 @@ function parseLocaleFlag(value) {
|
|
|
9535
9530
|
}
|
|
9536
9531
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
9537
9532
|
}
|
|
9538
|
-
function loadTargetingFileArg(
|
|
9539
|
-
if (typeof
|
|
9533
|
+
function loadTargetingFileArg(path28) {
|
|
9534
|
+
if (typeof path28 !== "string" || path28.length === 0) {
|
|
9540
9535
|
return void 0;
|
|
9541
9536
|
}
|
|
9542
|
-
const parsed = loadJsonFileArg2(
|
|
9537
|
+
const parsed = loadJsonFileArg2(path28);
|
|
9543
9538
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
9544
9539
|
if (!criteria.include) {
|
|
9545
9540
|
failWriteValidation2(
|
|
9546
|
-
`${
|
|
9541
|
+
`${path28} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
9547
9542
|
);
|
|
9548
9543
|
}
|
|
9549
9544
|
return criteria;
|
|
@@ -9578,14 +9573,14 @@ function parseCsvLine(line) {
|
|
|
9578
9573
|
cells.push(current);
|
|
9579
9574
|
return cells.map((cell) => cell.trim());
|
|
9580
9575
|
}
|
|
9581
|
-
function parseListFileArg(
|
|
9582
|
-
if (typeof
|
|
9576
|
+
function parseListFileArg(path28, maxRows) {
|
|
9577
|
+
if (typeof path28 !== "string" || path28.length === 0) {
|
|
9583
9578
|
return void 0;
|
|
9584
9579
|
}
|
|
9585
|
-
const raw = readFileSync6(
|
|
9580
|
+
const raw = readFileSync6(path28, "utf8");
|
|
9586
9581
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
9587
9582
|
if (lines.length < 2) {
|
|
9588
|
-
failWriteValidation2(`${
|
|
9583
|
+
failWriteValidation2(`${path28} needs a header row and at least one data row`);
|
|
9589
9584
|
}
|
|
9590
9585
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
9591
9586
|
const rows = [];
|
|
@@ -9604,7 +9599,7 @@ function parseListFileArg(path27, maxRows) {
|
|
|
9604
9599
|
}
|
|
9605
9600
|
}
|
|
9606
9601
|
if (rows.length > maxRows) {
|
|
9607
|
-
failWriteValidation2(`${
|
|
9602
|
+
failWriteValidation2(`${path28} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
9608
9603
|
}
|
|
9609
9604
|
return { columns, rows };
|
|
9610
9605
|
}
|
|
@@ -11704,11 +11699,11 @@ var updateStatusSchema = z14.enum(UPDATE_STATUSES);
|
|
|
11704
11699
|
function currencyMinimums2(currencyCode) {
|
|
11705
11700
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
11706
11701
|
}
|
|
11707
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
11702
|
+
function validateDailyBudgetFloor(money, ctx, path28) {
|
|
11708
11703
|
if (money?.currencyCode) {
|
|
11709
11704
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
11710
11705
|
if (Number(money.amount) < min) {
|
|
11711
|
-
ctx.addIssue({ code: "custom", path:
|
|
11706
|
+
ctx.addIssue({ code: "custom", path: path28, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
11712
11707
|
}
|
|
11713
11708
|
}
|
|
11714
11709
|
}
|
|
@@ -12340,19 +12335,19 @@ function failWriteValidation3(message) {
|
|
|
12340
12335
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
12341
12336
|
process.exit(1);
|
|
12342
12337
|
}
|
|
12343
|
-
function loadJsonFileArg3(
|
|
12344
|
-
if (typeof
|
|
12338
|
+
function loadJsonFileArg3(path28) {
|
|
12339
|
+
if (typeof path28 !== "string" || path28.length === 0) {
|
|
12345
12340
|
return {};
|
|
12346
12341
|
}
|
|
12347
12342
|
try {
|
|
12348
|
-
const parsed = JSON.parse(readFileSync8(
|
|
12343
|
+
const parsed = JSON.parse(readFileSync8(path28, "utf8"));
|
|
12349
12344
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
12350
|
-
failWriteValidation3(`${
|
|
12345
|
+
failWriteValidation3(`${path28} must contain a JSON object`);
|
|
12351
12346
|
}
|
|
12352
12347
|
return parsed;
|
|
12353
12348
|
} catch (err) {
|
|
12354
12349
|
if (err instanceof SyntaxError) {
|
|
12355
|
-
failWriteValidation3(`${
|
|
12350
|
+
failWriteValidation3(`${path28} is not valid JSON: ${err.message}`);
|
|
12356
12351
|
}
|
|
12357
12352
|
throw err;
|
|
12358
12353
|
}
|
|
@@ -15291,12 +15286,327 @@ Full guides: __tooling__/docs/tools/baker/ads-<platform>.md (google|meta|linkedi
|
|
|
15291
15286
|
}
|
|
15292
15287
|
});
|
|
15293
15288
|
|
|
15289
|
+
// src/commands/brand/index.ts
|
|
15290
|
+
import { defineCommand as defineCommand86 } from "citty";
|
|
15291
|
+
|
|
15292
|
+
// src/commands/brand/fonts.ts
|
|
15293
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
15294
|
+
import path from "path";
|
|
15295
|
+
import { defineCommand as defineCommand85 } from "citty";
|
|
15296
|
+
|
|
15297
|
+
// src/engine/brand/fonts.ts
|
|
15298
|
+
var GOOGLE_CSS2 = "https://fonts.googleapis.com/css2";
|
|
15299
|
+
function parseGoogleFontFaces(css) {
|
|
15300
|
+
const faces = [];
|
|
15301
|
+
const re = /\/\*\s*([\w-]+)\s*\*\/\s*@font-face\s*\{([^}]*)\}/g;
|
|
15302
|
+
for (const match of String(css).matchAll(re)) {
|
|
15303
|
+
const subset = match[1] ?? "";
|
|
15304
|
+
const body = match[2] ?? "";
|
|
15305
|
+
const family = body.match(/font-family\s*:\s*['"]?([^'";]+)['"]?\s*;/i)?.[1]?.trim();
|
|
15306
|
+
const url = body.match(/src\s*:\s*url\(\s*['"]?([^'")]+)['"]?\s*\)/i)?.[1]?.trim();
|
|
15307
|
+
const weight = body.match(/font-weight\s*:\s*(\d{3})\s*;/i)?.[1];
|
|
15308
|
+
if (!family || !url || !weight) continue;
|
|
15309
|
+
faces.push({
|
|
15310
|
+
subset,
|
|
15311
|
+
family,
|
|
15312
|
+
style: body.match(/font-style\s*:\s*([\w-]+)\s*;/i)?.[1]?.trim() ?? "normal",
|
|
15313
|
+
weight: Number(weight),
|
|
15314
|
+
url,
|
|
15315
|
+
unicodeRange: body.match(/unicode-range\s*:\s*([^;]+);/i)?.[1]?.trim() ?? ""
|
|
15316
|
+
});
|
|
15317
|
+
}
|
|
15318
|
+
return faces;
|
|
15319
|
+
}
|
|
15320
|
+
function fontFileName(face) {
|
|
15321
|
+
const family = face.family.replace(/[^A-Za-z0-9]/g, "");
|
|
15322
|
+
const style = face.style === "italic" ? "italic" : "";
|
|
15323
|
+
return `${family}-${face.weight}${style}-${face.subset}.woff2`;
|
|
15324
|
+
}
|
|
15325
|
+
function renderFontFaceCss(faces, hrefFor) {
|
|
15326
|
+
return faces.map(
|
|
15327
|
+
(face) => [
|
|
15328
|
+
"@font-face {",
|
|
15329
|
+
` font-family: '${face.family}';`,
|
|
15330
|
+
` font-style: ${face.style};`,
|
|
15331
|
+
` font-weight: ${face.weight};`,
|
|
15332
|
+
" font-display: swap;",
|
|
15333
|
+
` src: url('${hrefFor(face)}') format('woff2');`,
|
|
15334
|
+
...face.unicodeRange ? [` unicode-range: ${face.unicodeRange};`] : [],
|
|
15335
|
+
"}"
|
|
15336
|
+
].join("\n")
|
|
15337
|
+
).join("\n");
|
|
15338
|
+
}
|
|
15339
|
+
function parseWeightAxis(spec) {
|
|
15340
|
+
const axis = spec.match(/wght@([^&]+)/i)?.[1] ?? "";
|
|
15341
|
+
const weights = /* @__PURE__ */ new Set();
|
|
15342
|
+
for (const tuple of axis.split(";")) {
|
|
15343
|
+
const last = tuple.split(",").pop()?.trim() ?? "";
|
|
15344
|
+
const range = last.match(/^(\d{3})\.\.(\d{3})$/);
|
|
15345
|
+
if (range) {
|
|
15346
|
+
for (let w = Number(range[1]); w <= Number(range[2]); w += 100) weights.add(w);
|
|
15347
|
+
} else if (/^\d{3}$/.test(last)) {
|
|
15348
|
+
weights.add(Number(last));
|
|
15349
|
+
}
|
|
15350
|
+
}
|
|
15351
|
+
return [...weights].sort((a, b) => a - b);
|
|
15352
|
+
}
|
|
15353
|
+
function googleFontRequests(css) {
|
|
15354
|
+
const out = [];
|
|
15355
|
+
for (const link of String(css).matchAll(/fonts\.googleapis\.com\/[^\s"')]+/gi)) {
|
|
15356
|
+
for (const param of link[0].matchAll(/[?&]family=([^&]+)/gi)) {
|
|
15357
|
+
const [namePart, ...specParts] = (param[1] ?? "").split(":");
|
|
15358
|
+
const family = decodeURIComponent((namePart ?? "").replaceAll("+", " ")).trim();
|
|
15359
|
+
if (!family) continue;
|
|
15360
|
+
out.push({ family, weights: parseWeightAxis(specParts.join(":")) });
|
|
15361
|
+
}
|
|
15362
|
+
}
|
|
15363
|
+
return out;
|
|
15364
|
+
}
|
|
15365
|
+
function resolveFetchWeights(flag, declared) {
|
|
15366
|
+
if (flag) {
|
|
15367
|
+
return String(flag).split(",").map((w) => Number(w.trim())).filter((w) => Number.isInteger(w) && w >= 100 && w <= 900);
|
|
15368
|
+
}
|
|
15369
|
+
return declared?.length ? declared : [400];
|
|
15370
|
+
}
|
|
15371
|
+
function googleFontCssUrl(family, weights) {
|
|
15372
|
+
const name = family.trim().replace(/\s+/g, "+");
|
|
15373
|
+
const axis = [...new Set(weights)].sort((a, b) => a - b).join(";");
|
|
15374
|
+
const spec = axis ? `${name}:wght@${axis}` : name;
|
|
15375
|
+
return `${GOOGLE_CSS2}?family=${spec}&display=swap`;
|
|
15376
|
+
}
|
|
15377
|
+
|
|
15378
|
+
// src/commands/brand/fonts.ts
|
|
15379
|
+
var GLOBAL_CSS = path.join("src", "styles", "global.css");
|
|
15380
|
+
var FONTS_DIR = path.join("src", "brand", "fonts");
|
|
15381
|
+
var DEFAULT_SUBSETS = "latin";
|
|
15382
|
+
var BROWSER_UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36";
|
|
15383
|
+
registerSchema({
|
|
15384
|
+
command: "brand.fonts.check",
|
|
15385
|
+
description: "Start here when a brand's fonts are 'confirmed' but nobody verified it. Asks Google Fonts what it ACTUALLY serves for every family/weight src/styles/global.css requests, and reports any the brand promises but Google will not serve.",
|
|
15386
|
+
args: {}
|
|
15387
|
+
});
|
|
15388
|
+
registerSchema({
|
|
15389
|
+
command: "brand.fonts.fetch",
|
|
15390
|
+
description: "Download a Google font into src/brand/fonts/ so the brand stops depending on a third party at page load, and print the @font-face block to paste above @theme in src/styles/global.css.",
|
|
15391
|
+
args: {
|
|
15392
|
+
family: { type: "string", description: 'Font family, e.g. "DM Sans"', required: true },
|
|
15393
|
+
weights: {
|
|
15394
|
+
type: "string",
|
|
15395
|
+
description: "Comma-separated weights (default: what global.css requests, else 400)",
|
|
15396
|
+
required: false
|
|
15397
|
+
},
|
|
15398
|
+
subsets: {
|
|
15399
|
+
type: "string",
|
|
15400
|
+
description: `Comma-separated unicode subsets (default: ${DEFAULT_SUBSETS})`,
|
|
15401
|
+
required: false
|
|
15402
|
+
}
|
|
15403
|
+
}
|
|
15404
|
+
});
|
|
15405
|
+
function fail(code, message, fix) {
|
|
15406
|
+
writeJson({ ok: false, error: { code, message, ...fix ? { fix } : {} } });
|
|
15407
|
+
process.exit(2);
|
|
15408
|
+
}
|
|
15409
|
+
async function fetchGoogleCss(url) {
|
|
15410
|
+
try {
|
|
15411
|
+
const res = await fetch(url, { headers: { "User-Agent": BROWSER_UA } });
|
|
15412
|
+
if (!res.ok) return null;
|
|
15413
|
+
return await res.text();
|
|
15414
|
+
} catch {
|
|
15415
|
+
return null;
|
|
15416
|
+
}
|
|
15417
|
+
}
|
|
15418
|
+
async function diagnoseUnserved(family) {
|
|
15419
|
+
const bare = await fetchGoogleCss(googleFontCssUrl(family, []));
|
|
15420
|
+
return bare === null ? "unknown-family" : "unavailable-weight";
|
|
15421
|
+
}
|
|
15422
|
+
async function readGlobalCss() {
|
|
15423
|
+
try {
|
|
15424
|
+
return await readFile(path.resolve(process.cwd(), GLOBAL_CSS), "utf8");
|
|
15425
|
+
} catch {
|
|
15426
|
+
return "";
|
|
15427
|
+
}
|
|
15428
|
+
}
|
|
15429
|
+
var fontsCheckCommand = defineCommand85({
|
|
15430
|
+
meta: {
|
|
15431
|
+
name: "check",
|
|
15432
|
+
description: "Verify the brand's fonts really exist. For every family/weight src/styles/global.css requests from Google Fonts, ask Google what it actually serves and report anything missing \u2014 a weight nobody serves renders as a synthesized fallback and looks off-brand everywhere."
|
|
15433
|
+
},
|
|
15434
|
+
async run() {
|
|
15435
|
+
const css = await readGlobalCss();
|
|
15436
|
+
const requests = googleFontRequests(css);
|
|
15437
|
+
if (requests.length === 0) {
|
|
15438
|
+
writeJson({
|
|
15439
|
+
ok: true,
|
|
15440
|
+
data: { families: [], selfHosted: true },
|
|
15441
|
+
hints: [
|
|
15442
|
+
`No Google Fonts requested in ${GLOBAL_CSS}. If the brand self-hosts, the offline validator already checks those files; nothing to verify against Google.`
|
|
15443
|
+
]
|
|
15444
|
+
});
|
|
15445
|
+
return;
|
|
15446
|
+
}
|
|
15447
|
+
const families = [];
|
|
15448
|
+
const unavailable = [];
|
|
15449
|
+
for (const request of requests) {
|
|
15450
|
+
const url = googleFontCssUrl(request.family, request.weights);
|
|
15451
|
+
const served = await fetchGoogleCss(url);
|
|
15452
|
+
if (served === null) {
|
|
15453
|
+
const cause = await diagnoseUnserved(request.family);
|
|
15454
|
+
unavailable.push(
|
|
15455
|
+
cause === "unknown-family" ? `${request.family} (no such family on Google Fonts \u2014 check the exact spelling)` : `${request.family} ${request.weights.join("/")} (the family exists, these weights do not)`
|
|
15456
|
+
);
|
|
15457
|
+
families.push({
|
|
15458
|
+
family: request.family,
|
|
15459
|
+
requested: request.weights,
|
|
15460
|
+
served: [],
|
|
15461
|
+
missing: request.weights,
|
|
15462
|
+
cause
|
|
15463
|
+
});
|
|
15464
|
+
continue;
|
|
15465
|
+
}
|
|
15466
|
+
const faces = parseGoogleFontFaces(served);
|
|
15467
|
+
const servedWeights = [...new Set(faces.map((f) => f.weight))].sort((a, b) => a - b);
|
|
15468
|
+
const missing = request.weights.filter((w) => !servedWeights.includes(w));
|
|
15469
|
+
if (missing.length > 0) unavailable.push(`${request.family} ${missing.join("/")}`);
|
|
15470
|
+
families.push({
|
|
15471
|
+
family: request.family,
|
|
15472
|
+
requested: request.weights,
|
|
15473
|
+
served: servedWeights,
|
|
15474
|
+
missing,
|
|
15475
|
+
subsets: [...new Set(faces.map((f) => f.subset))]
|
|
15476
|
+
});
|
|
15477
|
+
}
|
|
15478
|
+
if (unavailable.length > 0) {
|
|
15479
|
+
fail("FONT_NOT_SERVED", `Google Fonts does not serve: ${unavailable.join("; ")}`, {
|
|
15480
|
+
action: `Correct the family name or weight in ${GLOBAL_CSS} and src/brand/BRAND.md, or run \`baker brand fonts fetch\` for a weight that does exist.`,
|
|
15481
|
+
explanation: "A requested weight Google does not serve is silently synthesized by the browser, so the page looks subtly wrong with nothing failing."
|
|
15482
|
+
});
|
|
15483
|
+
}
|
|
15484
|
+
writeJson({
|
|
15485
|
+
ok: true,
|
|
15486
|
+
data: { families },
|
|
15487
|
+
hints: [
|
|
15488
|
+
"Every requested family and weight is really served by Google.",
|
|
15489
|
+
`To stop depending on Google at page load, run \`baker brand fonts fetch "<family>"\` to self-host into ${FONTS_DIR}.`
|
|
15490
|
+
]
|
|
15491
|
+
});
|
|
15492
|
+
}
|
|
15493
|
+
});
|
|
15494
|
+
var fontsFetchCommand = defineCommand85({
|
|
15495
|
+
meta: {
|
|
15496
|
+
name: "fetch",
|
|
15497
|
+
description: "Download a Google font into src/brand/fonts/ and print the @font-face block to paste above @theme in src/styles/global.css. Use when a brand font should be self-hosted rather than requested from Google on every page load."
|
|
15498
|
+
},
|
|
15499
|
+
args: {
|
|
15500
|
+
family: { type: "positional", required: true, description: 'Font family, e.g. "DM Sans"' },
|
|
15501
|
+
weights: { type: "string", description: "Comma-separated weights (default: what global.css requests, else 400)" },
|
|
15502
|
+
subsets: { type: "string", description: `Comma-separated unicode subsets (default: ${DEFAULT_SUBSETS})` }
|
|
15503
|
+
},
|
|
15504
|
+
async run({ args }) {
|
|
15505
|
+
const family = String(args.family).trim();
|
|
15506
|
+
if (!family) fail("INVALID_FAMILY", 'Pass a font family, e.g. `baker brand fonts fetch "DM Sans"`.');
|
|
15507
|
+
const css = await readGlobalCss();
|
|
15508
|
+
const declared = googleFontRequests(css).find((r) => r.family.toLowerCase() === family.toLowerCase());
|
|
15509
|
+
const weights = resolveFetchWeights(args.weights, declared?.weights);
|
|
15510
|
+
if (weights.length === 0) {
|
|
15511
|
+
fail("INVALID_WEIGHTS", `"${args.weights}" has no usable weight \u2014 pass whole hundreds, e.g. --weights 400,700.`);
|
|
15512
|
+
}
|
|
15513
|
+
const wanted = new Set(
|
|
15514
|
+
String(args.subsets ?? DEFAULT_SUBSETS).split(",").map((s) => s.trim()).filter(Boolean)
|
|
15515
|
+
);
|
|
15516
|
+
const servedCss = await fetchGoogleCss(googleFontCssUrl(family, weights));
|
|
15517
|
+
if (servedCss === null) {
|
|
15518
|
+
const cause = await diagnoseUnserved(family);
|
|
15519
|
+
fail(
|
|
15520
|
+
"FONT_NOT_SERVED",
|
|
15521
|
+
cause === "unknown-family" ? `Google Fonts has no family called "${family}".` : `"${family}" exists on Google Fonts but not at weight ${weights.join("/")}.`,
|
|
15522
|
+
cause === "unknown-family" ? {
|
|
15523
|
+
action: "Check the exact family name at fonts.google.com, then retry.",
|
|
15524
|
+
explanation: "Family names are case- and space-sensitive in the Google Fonts API."
|
|
15525
|
+
} : {
|
|
15526
|
+
action: `Run \`baker brand fonts check\` to see which weights this family really ships, then retry with --weights set to one of those.`,
|
|
15527
|
+
explanation: "Google rejects the whole request when one requested weight does not exist."
|
|
15528
|
+
}
|
|
15529
|
+
);
|
|
15530
|
+
}
|
|
15531
|
+
const faces = parseGoogleFontFaces(servedCss).filter((f) => wanted.has(f.subset));
|
|
15532
|
+
if (faces.length === 0) {
|
|
15533
|
+
fail("SUBSET_NOT_SERVED", `"${family}" has no ${[...wanted].join("/")} subset.`, {
|
|
15534
|
+
action: `Retry with --subsets set to one Google actually serves for this family.`,
|
|
15535
|
+
explanation: "Google splits each family into unicode subsets; not every family ships every subset."
|
|
15536
|
+
});
|
|
15537
|
+
}
|
|
15538
|
+
const fontsDir = path.resolve(process.cwd(), FONTS_DIR);
|
|
15539
|
+
await mkdir(fontsDir, { recursive: true });
|
|
15540
|
+
const downloaded = [];
|
|
15541
|
+
for (const face of faces) {
|
|
15542
|
+
const res = await fetch(face.url, { headers: { "User-Agent": BROWSER_UA } });
|
|
15543
|
+
if (!res.ok) continue;
|
|
15544
|
+
await writeFile(path.join(fontsDir, fontFileName(face)), Buffer.from(await res.arrayBuffer()));
|
|
15545
|
+
downloaded.push(face);
|
|
15546
|
+
}
|
|
15547
|
+
const written = downloaded.map(fontFileName);
|
|
15548
|
+
if (written.length === 0) {
|
|
15549
|
+
fail("DOWNLOAD_FAILED", `Could not download any file for "${family}".`, {
|
|
15550
|
+
action: "Retry; if it keeps failing, keep the Google @import and note it in BRAND.md.",
|
|
15551
|
+
explanation: "The font stylesheet resolved but the font files themselves did not download."
|
|
15552
|
+
});
|
|
15553
|
+
}
|
|
15554
|
+
const fontFace = renderFontFaceCss(downloaded, (f) => `/${FONTS_DIR}/${fontFileName(f)}`);
|
|
15555
|
+
const downloadedWeights = [...new Set(downloaded.map((f) => f.weight))].sort((a, b) => a - b);
|
|
15556
|
+
const incomplete = weights.filter((w) => !downloadedWeights.includes(w));
|
|
15557
|
+
writeJson({
|
|
15558
|
+
ok: true,
|
|
15559
|
+
data: { family, weights: downloadedWeights, files: written, fontFace },
|
|
15560
|
+
hints: [
|
|
15561
|
+
`Paste the \`fontFace\` block ABOVE the @theme block in ${GLOBAL_CSS}, then remove "${family}" from the Google @import so the page stops fetching it at load.`,
|
|
15562
|
+
`Record the exact weights (${downloadedWeights.join(", ")}) in src/brand/BRAND.md \u2014 a weight listed there but not downloaded renders as a synthesized fallback.`,
|
|
15563
|
+
...incomplete.length > 0 ? [
|
|
15564
|
+
`PARTIAL: ${incomplete.join(", ")} did not download and is NOT in the \`fontFace\` block. Keep "${family}" in the Google @import for those weights, or re-run \`baker brand fonts fetch "${family}" --weights ${incomplete.join(",")}\`.`
|
|
15565
|
+
] : [],
|
|
15566
|
+
"Licensing: Google Fonts are open-licensed and safe to self-host. A client's own commercial face is not \u2014 confirm before re-hosting one."
|
|
15567
|
+
]
|
|
15568
|
+
});
|
|
15569
|
+
}
|
|
15570
|
+
});
|
|
15571
|
+
var fontsCommand = defineCommand85({
|
|
15572
|
+
meta: {
|
|
15573
|
+
name: "fonts",
|
|
15574
|
+
description: `Verify and self-host the brand's typefaces.
|
|
15575
|
+
|
|
15576
|
+
Start here: \`baker brand fonts check\` \u2014 confirms the fonts the brand claims are really served.
|
|
15577
|
+
|
|
15578
|
+
Subcommands:
|
|
15579
|
+
baker brand fonts check \u2014 ask Google what it actually serves for every family/weight global.css requests
|
|
15580
|
+
baker brand fonts fetch <family> \u2014 download a family into src/brand/fonts/ and print its @font-face block`
|
|
15581
|
+
},
|
|
15582
|
+
subCommands: {
|
|
15583
|
+
check: fontsCheckCommand,
|
|
15584
|
+
fetch: fontsFetchCommand
|
|
15585
|
+
}
|
|
15586
|
+
});
|
|
15587
|
+
|
|
15588
|
+
// src/commands/brand/index.ts
|
|
15589
|
+
var brandCommand = defineCommand86({
|
|
15590
|
+
meta: {
|
|
15591
|
+
name: "brand",
|
|
15592
|
+
description: `Brand asset verification for src/brand/.
|
|
15593
|
+
|
|
15594
|
+
Start here: \`baker brand fonts check\` after defining or editing the brand's typography.
|
|
15595
|
+
|
|
15596
|
+
Subcommands:
|
|
15597
|
+
baker brand fonts \u2014 verify the brand's typefaces really load, and self-host them into src/brand/fonts/`
|
|
15598
|
+
},
|
|
15599
|
+
subCommands: {
|
|
15600
|
+
fonts: fontsCommand
|
|
15601
|
+
}
|
|
15602
|
+
});
|
|
15603
|
+
|
|
15294
15604
|
// src/commands/canvas/index.ts
|
|
15295
|
-
import { defineCommand as
|
|
15605
|
+
import { defineCommand as defineCommand97 } from "citty";
|
|
15296
15606
|
|
|
15297
15607
|
// src/commands/canvas/catalog.ts
|
|
15298
|
-
import { defineCommand as
|
|
15299
|
-
var catalogCommand =
|
|
15608
|
+
import { defineCommand as defineCommand87 } from "citty";
|
|
15609
|
+
var catalogCommand = defineCommand87({
|
|
15300
15610
|
meta: {
|
|
15301
15611
|
name: "catalog",
|
|
15302
15612
|
description: "Print the agent-facing node catalog (JSON Schema). Includes every registered node grouped by category."
|
|
@@ -15309,9 +15619,9 @@ var catalogCommand = defineCommand85({
|
|
|
15309
15619
|
});
|
|
15310
15620
|
|
|
15311
15621
|
// src/commands/canvas/critique.ts
|
|
15312
|
-
import { readFile } from "fs/promises";
|
|
15313
|
-
import
|
|
15314
|
-
import { defineCommand as
|
|
15622
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
15623
|
+
import path2 from "path";
|
|
15624
|
+
import { defineCommand as defineCommand88 } from "citty";
|
|
15315
15625
|
|
|
15316
15626
|
// src/engine/scaffold/lib/critique.ts
|
|
15317
15627
|
var VIBE_CUE = /\b(light|lighting|golden|moody|warm|cool|tone|grade|contrast|shadow|glow|rim|backlit|neon|soft)\b/i;
|
|
@@ -15431,15 +15741,15 @@ function critiqueCanvas(canvas) {
|
|
|
15431
15741
|
}
|
|
15432
15742
|
|
|
15433
15743
|
// src/commands/canvas/critique.ts
|
|
15434
|
-
var critiqueCommand =
|
|
15744
|
+
var critiqueCommand = defineCommand88({
|
|
15435
15745
|
meta: {
|
|
15436
15746
|
name: "critique",
|
|
15437
15747
|
description: "Pre-spend creative critic (ADVISORY \u2014 never blocks). Scores a scaffolded creative BEFORE the billed render, so weak spots get fixed for free. A VIDEO creative is scored on hook strength, sound-off first-frame legibility, retention risk, and mechanism clarity; a STATIC ad on baked-text legibility risk, identity grounding, brand-type fidelity, and mechanism clarity. Ground a video hook against `baker winning-ads hooks` for a proven pattern."
|
|
15438
15748
|
},
|
|
15439
15749
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
15440
15750
|
async run({ args }) {
|
|
15441
|
-
const filePath =
|
|
15442
|
-
const raw = await
|
|
15751
|
+
const filePath = path2.resolve(String(args.file));
|
|
15752
|
+
const raw = await readFile2(filePath, "utf8");
|
|
15443
15753
|
let parsed;
|
|
15444
15754
|
try {
|
|
15445
15755
|
parsed = JSON.parse(raw);
|
|
@@ -15470,12 +15780,12 @@ var critiqueCommand = defineCommand86({
|
|
|
15470
15780
|
|
|
15471
15781
|
// src/commands/canvas/inspect.ts
|
|
15472
15782
|
import { execFile } from "child_process";
|
|
15473
|
-
import { readdir, readFile as
|
|
15474
|
-
import
|
|
15783
|
+
import { readdir, readFile as readFile3, stat } from "fs/promises";
|
|
15784
|
+
import path3 from "path";
|
|
15475
15785
|
import { promisify } from "util";
|
|
15476
|
-
import { defineCommand as
|
|
15786
|
+
import { defineCommand as defineCommand89 } from "citty";
|
|
15477
15787
|
var execFileAsync = promisify(execFile);
|
|
15478
|
-
var inspectCommand =
|
|
15788
|
+
var inspectCommand = defineCommand89({
|
|
15479
15789
|
meta: {
|
|
15480
15790
|
name: "inspect",
|
|
15481
15791
|
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."
|
|
@@ -15489,7 +15799,7 @@ var inspectCommand = defineCommand87({
|
|
|
15489
15799
|
}
|
|
15490
15800
|
},
|
|
15491
15801
|
async run({ args }) {
|
|
15492
|
-
const outputsDir =
|
|
15802
|
+
const outputsDir = path3.resolve(String(args["outputs-dir"] ?? "canvas"));
|
|
15493
15803
|
const runArg = String(args.run);
|
|
15494
15804
|
const runDir = await resolveRunDir(runArg, outputsDir);
|
|
15495
15805
|
const manifest = await loadManifest(runDir);
|
|
@@ -15501,7 +15811,7 @@ var inspectCommand = defineCommand87({
|
|
|
15501
15811
|
}
|
|
15502
15812
|
const summary = {
|
|
15503
15813
|
ok: true,
|
|
15504
|
-
run_id: manifest.run_id ??
|
|
15814
|
+
run_id: manifest.run_id ?? path3.basename(runDir),
|
|
15505
15815
|
run_dir: runDir,
|
|
15506
15816
|
stats: manifest.stats ?? null,
|
|
15507
15817
|
output: manifest.output ?? null,
|
|
@@ -15514,20 +15824,20 @@ var inspectCommand = defineCommand87({
|
|
|
15514
15824
|
}
|
|
15515
15825
|
});
|
|
15516
15826
|
async function resolveRunDir(run, outputsDir) {
|
|
15517
|
-
if (
|
|
15827
|
+
if (path3.isAbsolute(run)) {
|
|
15518
15828
|
const s2 = await stat(run).catch(() => null);
|
|
15519
15829
|
if (s2?.isDirectory()) return run;
|
|
15520
15830
|
throw new Error(`inspect: ${run} is not a directory`);
|
|
15521
15831
|
}
|
|
15522
|
-
const candidate =
|
|
15832
|
+
const candidate = path3.join(outputsDir, run);
|
|
15523
15833
|
const s = await stat(candidate).catch(() => null);
|
|
15524
15834
|
if (s?.isDirectory()) return candidate;
|
|
15525
15835
|
throw new Error(`inspect: no run directory at ${candidate}`);
|
|
15526
15836
|
}
|
|
15527
15837
|
async function loadManifest(runDir) {
|
|
15528
|
-
const manifestPath =
|
|
15838
|
+
const manifestPath = path3.join(runDir, "manifest.json");
|
|
15529
15839
|
try {
|
|
15530
|
-
const raw = await
|
|
15840
|
+
const raw = await readFile3(manifestPath, "utf-8");
|
|
15531
15841
|
return JSON.parse(raw);
|
|
15532
15842
|
} catch {
|
|
15533
15843
|
return {};
|
|
@@ -15537,7 +15847,7 @@ async function listRunFiles(runDir) {
|
|
|
15537
15847
|
const out = [];
|
|
15538
15848
|
const names = await readdir(runDir);
|
|
15539
15849
|
for (const name of names) {
|
|
15540
|
-
const abs =
|
|
15850
|
+
const abs = path3.join(runDir, name);
|
|
15541
15851
|
const s = await stat(abs).catch(() => null);
|
|
15542
15852
|
if (!s?.isFile()) continue;
|
|
15543
15853
|
out.push({ name, path: abs, size: s.size });
|
|
@@ -15582,13 +15892,13 @@ async function probeDuration(filePath) {
|
|
|
15582
15892
|
}
|
|
15583
15893
|
|
|
15584
15894
|
// src/commands/canvas/rerun.ts
|
|
15585
|
-
import
|
|
15586
|
-
import { defineCommand as
|
|
15895
|
+
import path14 from "path";
|
|
15896
|
+
import { defineCommand as defineCommand91 } from "citty";
|
|
15587
15897
|
|
|
15588
15898
|
// src/commands/canvas/run.ts
|
|
15589
|
-
import { readFile as
|
|
15590
|
-
import
|
|
15591
|
-
import { defineCommand as
|
|
15899
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
15900
|
+
import path13 from "path";
|
|
15901
|
+
import { defineCommand as defineCommand90 } from "citty";
|
|
15592
15902
|
|
|
15593
15903
|
// src/commands/canvas/placeholders.ts
|
|
15594
15904
|
function unsuppliedPlaceholderAssets(canvas) {
|
|
@@ -15607,7 +15917,7 @@ function unsuppliedPlaceholderAssets(canvas) {
|
|
|
15607
15917
|
}
|
|
15608
15918
|
|
|
15609
15919
|
// src/commands/canvas/resolve-paths.ts
|
|
15610
|
-
import
|
|
15920
|
+
import path4 from "path";
|
|
15611
15921
|
function resolveRelativeCanvasPaths(canvas, baseDir) {
|
|
15612
15922
|
if (!canvas || typeof canvas !== "object") return canvas;
|
|
15613
15923
|
const c = canvas;
|
|
@@ -15620,24 +15930,24 @@ function resolveNode(node, baseDir) {
|
|
|
15620
15930
|
const params = n.params;
|
|
15621
15931
|
if (!params || typeof params !== "object") return node;
|
|
15622
15932
|
if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
|
|
15623
|
-
return { ...node, params: { ...params, path:
|
|
15933
|
+
return { ...node, params: { ...params, path: path4.resolve(baseDir, params.path) } };
|
|
15624
15934
|
}
|
|
15625
15935
|
if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
|
|
15626
|
-
return { ...node, params: { ...params, composition:
|
|
15936
|
+
return { ...node, params: { ...params, composition: path4.resolve(baseDir, params.composition) } };
|
|
15627
15937
|
}
|
|
15628
15938
|
return node;
|
|
15629
15939
|
}
|
|
15630
15940
|
function isResolvableRelative(value) {
|
|
15631
|
-
return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !
|
|
15941
|
+
return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !path4.isAbsolute(value);
|
|
15632
15942
|
}
|
|
15633
15943
|
|
|
15634
15944
|
// src/commands/canvas/source-version.ts
|
|
15635
|
-
import { readFile as
|
|
15636
|
-
import
|
|
15945
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
15946
|
+
import path6 from "path";
|
|
15637
15947
|
|
|
15638
15948
|
// src/commands/canvas/scene-files.ts
|
|
15639
|
-
import { mkdir, readFile as
|
|
15640
|
-
import
|
|
15949
|
+
import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
15950
|
+
import path5 from "path";
|
|
15641
15951
|
var SCENES_DIR = "scenes";
|
|
15642
15952
|
var GLOBAL_PROMPT_FILE = "prompt.json";
|
|
15643
15953
|
var REBUILD_FILE = "prompt.rebuild.json";
|
|
@@ -15654,19 +15964,19 @@ function splitBlueprint(blueprint) {
|
|
|
15654
15964
|
}
|
|
15655
15965
|
async function writeSceneFiles(outDir, blueprint) {
|
|
15656
15966
|
const { global, scenes } = splitBlueprint(blueprint);
|
|
15657
|
-
await
|
|
15967
|
+
await writeFile2(path5.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
|
|
15658
15968
|
`, "utf8");
|
|
15659
|
-
const scenesDir =
|
|
15660
|
-
await
|
|
15969
|
+
const scenesDir = path5.join(outDir, SCENES_DIR);
|
|
15970
|
+
await mkdir2(scenesDir, { recursive: true });
|
|
15661
15971
|
const written = /* @__PURE__ */ new Set();
|
|
15662
15972
|
for (let i = 0; i < scenes.length; i++) {
|
|
15663
15973
|
const name = sceneFileName(i, scenes.length);
|
|
15664
15974
|
written.add(name);
|
|
15665
|
-
await
|
|
15975
|
+
await writeFile2(path5.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
|
|
15666
15976
|
`, "utf8");
|
|
15667
15977
|
}
|
|
15668
15978
|
for (const name of await listSceneFileNames(scenesDir)) {
|
|
15669
|
-
if (!written.has(name)) await rm(
|
|
15979
|
+
if (!written.has(name)) await rm(path5.join(scenesDir, name), { force: true });
|
|
15670
15980
|
}
|
|
15671
15981
|
}
|
|
15672
15982
|
async function listSceneFileNames(scenesDir) {
|
|
@@ -15679,17 +15989,17 @@ async function listSceneFileNames(scenesDir) {
|
|
|
15679
15989
|
return entries.filter((n) => /^s\d+\.json$/.test(n)).sort(bySceneIndex);
|
|
15680
15990
|
}
|
|
15681
15991
|
async function listSceneFiles(creativeDir) {
|
|
15682
|
-
const scenesDir =
|
|
15683
|
-
return (await listSceneFileNames(scenesDir)).map((n) =>
|
|
15992
|
+
const scenesDir = path5.join(creativeDir, SCENES_DIR);
|
|
15993
|
+
return (await listSceneFileNames(scenesDir)).map((n) => path5.join(scenesDir, n));
|
|
15684
15994
|
}
|
|
15685
15995
|
async function reassembleBlueprint(creativeDir) {
|
|
15686
15996
|
const files = await listSceneFiles(creativeDir);
|
|
15687
15997
|
if (files.length === 0) return null;
|
|
15688
|
-
const globalRaw = await
|
|
15998
|
+
const globalRaw = await readFile4(path5.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
|
|
15689
15999
|
const global = JSON.parse(globalRaw);
|
|
15690
16000
|
const scenes = [];
|
|
15691
16001
|
for (const file of files) {
|
|
15692
|
-
scenes.push(JSON.parse(await
|
|
16002
|
+
scenes.push(JSON.parse(await readFile4(file, "utf8")));
|
|
15693
16003
|
}
|
|
15694
16004
|
return { ...global, scenes };
|
|
15695
16005
|
}
|
|
@@ -15703,15 +16013,15 @@ function bySceneIndex(a, b) {
|
|
|
15703
16013
|
async function computeSourceSha(canvasPath) {
|
|
15704
16014
|
let canvasBytes;
|
|
15705
16015
|
try {
|
|
15706
|
-
canvasBytes = await
|
|
16016
|
+
canvasBytes = await readFile5(canvasPath);
|
|
15707
16017
|
} catch {
|
|
15708
16018
|
return void 0;
|
|
15709
16019
|
}
|
|
15710
|
-
const canvasDir =
|
|
15711
|
-
const promptPath =
|
|
16020
|
+
const canvasDir = path6.dirname(canvasPath);
|
|
16021
|
+
const promptPath = path6.join(canvasDir, "prompt.json");
|
|
15712
16022
|
let promptBytes;
|
|
15713
16023
|
try {
|
|
15714
|
-
promptBytes = await
|
|
16024
|
+
promptBytes = await readFile5(promptPath);
|
|
15715
16025
|
} catch {
|
|
15716
16026
|
promptBytes = Buffer.alloc(0);
|
|
15717
16027
|
}
|
|
@@ -15724,7 +16034,7 @@ async function computeSourceSha(canvasPath) {
|
|
|
15724
16034
|
for (const sceneFile of await listSceneFiles(canvasDir)) {
|
|
15725
16035
|
let sceneBytes;
|
|
15726
16036
|
try {
|
|
15727
|
-
sceneBytes = await
|
|
16037
|
+
sceneBytes = await readFile5(sceneFile);
|
|
15728
16038
|
} catch {
|
|
15729
16039
|
sceneBytes = Buffer.alloc(0);
|
|
15730
16040
|
}
|
|
@@ -15734,8 +16044,8 @@ async function computeSourceSha(canvasPath) {
|
|
|
15734
16044
|
}
|
|
15735
16045
|
|
|
15736
16046
|
// src/commands/canvas/scene-projection.ts
|
|
15737
|
-
import { readFile as
|
|
15738
|
-
import
|
|
16047
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
16048
|
+
import path7 from "path";
|
|
15739
16049
|
|
|
15740
16050
|
// src/engine/scaffold/video.ts
|
|
15741
16051
|
import { toCardinal as nwAr } from "n2words/ar-SA";
|
|
@@ -16661,7 +16971,9 @@ function buildFrameRef(edge, url, framePrompt, present2, ctx, nodes) {
|
|
|
16661
16971
|
const imageProfile = imageProfileFor(ctx.imageModel);
|
|
16662
16972
|
const genParams = {
|
|
16663
16973
|
model: ctx.imageModel,
|
|
16664
|
-
|
|
16974
|
+
// gpt-image-2 derives pixel dimensions from the ratio and has no size knob,
|
|
16975
|
+
// so asking for 2K there is an `unknown_param` at validate.
|
|
16976
|
+
...supportsParam("image_generate", ctx.imageModel, "image_size") ? { image_size: "2K" } : {},
|
|
16665
16977
|
// Per-model image defaults (gpt-image: quality=high — OpenRouter forwards it; we do
|
|
16666
16978
|
// NOT send input_fidelity, which gpt-image-2 forces high automatically).
|
|
16667
16979
|
...imageProfile?.paramDefaults ?? {},
|
|
@@ -16676,7 +16988,7 @@ function buildFrameRef(edge, url, framePrompt, present2, ctx, nodes) {
|
|
|
16676
16988
|
ctx.imageModel
|
|
16677
16989
|
)
|
|
16678
16990
|
};
|
|
16679
|
-
if (ctx.genAr) genParams.aspect_ratio = ctx.genAr;
|
|
16991
|
+
if (ctx.genAr) genParams.aspect_ratio = nearestSupportedAspectRatio("image_generate", ctx.imageModel, ctx.genAr);
|
|
16680
16992
|
const genId = `s${ctx.sceneIndex}${tag}_${edge}`;
|
|
16681
16993
|
nodes.push({
|
|
16682
16994
|
id: genId,
|
|
@@ -18992,12 +19304,12 @@ function nonPromptParamsDiverge(live = {}, rebuilt = {}) {
|
|
|
18992
19304
|
return false;
|
|
18993
19305
|
}
|
|
18994
19306
|
async function syncSceneNodeParams(canvas, canvasPath, log) {
|
|
18995
|
-
const creativeDir =
|
|
19307
|
+
const creativeDir = path7.dirname(canvasPath);
|
|
18996
19308
|
const blueprint = await reassembleBlueprint(creativeDir);
|
|
18997
19309
|
if (!blueprint) return "not_applicable";
|
|
18998
19310
|
let rebuildRaw;
|
|
18999
19311
|
try {
|
|
19000
|
-
rebuildRaw = await
|
|
19312
|
+
rebuildRaw = await readFile6(path7.join(creativeDir, REBUILD_FILE), "utf8");
|
|
19001
19313
|
} catch {
|
|
19002
19314
|
return "not_applicable";
|
|
19003
19315
|
}
|
|
@@ -19038,7 +19350,7 @@ async function syncSceneNodeParams(canvas, canvasPath, log) {
|
|
|
19038
19350
|
}
|
|
19039
19351
|
|
|
19040
19352
|
// src/commands/canvas/style-projection.ts
|
|
19041
|
-
import { readFile as
|
|
19353
|
+
import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
|
|
19042
19354
|
function findBlueprintProjection(canvas) {
|
|
19043
19355
|
if (!canvas || typeof canvas !== "object") return null;
|
|
19044
19356
|
const nodes = canvas.nodes;
|
|
@@ -19066,10 +19378,10 @@ function renderStyleProjectionFromValue(blueprint) {
|
|
|
19066
19378
|
async function syncStyleProjection(canvas, log) {
|
|
19067
19379
|
const pair = findBlueprintProjection(canvas);
|
|
19068
19380
|
if (!pair) return "not_applicable";
|
|
19069
|
-
const rendered = renderStyleProjection(await
|
|
19070
|
-
const current = await
|
|
19381
|
+
const rendered = renderStyleProjection(await readFile7(pair.promptPath, "utf8"));
|
|
19382
|
+
const current = await readFile7(pair.stylePath, "utf8").catch(() => null);
|
|
19071
19383
|
if (current === rendered) return "up_to_date";
|
|
19072
|
-
await
|
|
19384
|
+
await writeFile3(pair.stylePath, rendered, "utf8");
|
|
19073
19385
|
log(
|
|
19074
19386
|
"[style] prompt.style.json regenerated from prompt.json \u2014 every frame's shared ad spec changed; affected image frames will re-bill on the next run"
|
|
19075
19387
|
);
|
|
@@ -19129,13 +19441,13 @@ ${body}` : header || body || compactJson(record);
|
|
|
19129
19441
|
}
|
|
19130
19442
|
|
|
19131
19443
|
// src/commands/canvas/run-record.ts
|
|
19132
|
-
import
|
|
19444
|
+
import path8 from "path";
|
|
19133
19445
|
var MAX_RUN_NODES = 200;
|
|
19134
19446
|
var MAX_OUTPUTS_PER_NODE = 10;
|
|
19135
19447
|
var MAX_FINAL_OUTPUTS = 10;
|
|
19136
19448
|
var MAX_CREATIVE_SLUG_LENGTH = 100;
|
|
19137
19449
|
function creativeSlugFromCanvasPath(filePath) {
|
|
19138
|
-
const normalized = filePath.split(
|
|
19450
|
+
const normalized = filePath.split(path8.sep).join("/");
|
|
19139
19451
|
const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
|
|
19140
19452
|
const slug = match?.[1] ?? null;
|
|
19141
19453
|
return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
|
|
@@ -19423,7 +19735,7 @@ var RunRecordPoster = class {
|
|
|
19423
19735
|
|
|
19424
19736
|
// src/commands/canvas/run-retention.ts
|
|
19425
19737
|
import { rm as rm2 } from "fs/promises";
|
|
19426
|
-
import
|
|
19738
|
+
import path9 from "path";
|
|
19427
19739
|
function runDirsToPrune(entries, keep, currentRunId) {
|
|
19428
19740
|
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
19429
19741
|
if (keep <= 0) return runs;
|
|
@@ -19440,7 +19752,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
19440
19752
|
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
19441
19753
|
if (toPrune.length === 0) return;
|
|
19442
19754
|
for (const dir of toPrune) {
|
|
19443
|
-
await rm2(
|
|
19755
|
+
await rm2(path9.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
19444
19756
|
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
19445
19757
|
);
|
|
19446
19758
|
}
|
|
@@ -19448,13 +19760,13 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
19448
19760
|
}
|
|
19449
19761
|
|
|
19450
19762
|
// src/commands/canvas/dirty-marker.ts
|
|
19451
|
-
import { mkdir as
|
|
19452
|
-
import
|
|
19763
|
+
import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as writeFile4 } from "fs/promises";
|
|
19764
|
+
import path10 from "path";
|
|
19453
19765
|
function creativeDirtyDir(base) {
|
|
19454
|
-
return base ??
|
|
19766
|
+
return base ?? path10.resolve("canvas", ".dirty");
|
|
19455
19767
|
}
|
|
19456
19768
|
function dirtyMarkerFile(slug, base) {
|
|
19457
|
-
return
|
|
19769
|
+
return path10.join(creativeDirtyDir(base), `${slug}.json`);
|
|
19458
19770
|
}
|
|
19459
19771
|
async function clearCreativeDirty(slug, base) {
|
|
19460
19772
|
try {
|
|
@@ -19464,18 +19776,18 @@ async function clearCreativeDirty(slug, base) {
|
|
|
19464
19776
|
}
|
|
19465
19777
|
|
|
19466
19778
|
// src/commands/canvas/run-resume.ts
|
|
19467
|
-
import { mkdir as
|
|
19468
|
-
import
|
|
19779
|
+
import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as writeFile5 } from "fs/promises";
|
|
19780
|
+
import path11 from "path";
|
|
19469
19781
|
function markerKey(canvasPath) {
|
|
19470
19782
|
const slug = creativeSlugFromCanvasPath(canvasPath);
|
|
19471
|
-
const identity = slug ??
|
|
19783
|
+
const identity = slug ?? path11.relative(process.cwd(), path11.resolve(canvasPath));
|
|
19472
19784
|
return sha256Hex(Buffer.from(identity)).slice(0, 32);
|
|
19473
19785
|
}
|
|
19474
19786
|
function legacyMarkerKey(canvasPath) {
|
|
19475
|
-
return sha256Hex(Buffer.from(
|
|
19787
|
+
return sha256Hex(Buffer.from(path11.resolve(canvasPath))).slice(0, 32);
|
|
19476
19788
|
}
|
|
19477
19789
|
function markerFile(outputsDir, key) {
|
|
19478
|
-
return
|
|
19790
|
+
return path11.join(outputsDir, ".inflight", `${key}.json`);
|
|
19479
19791
|
}
|
|
19480
19792
|
var REMOTE_ADOPT_STALE_MS = 12e4;
|
|
19481
19793
|
function classifyRemoteRun(run, now) {
|
|
@@ -19511,7 +19823,7 @@ async function resolveRunId(opts) {
|
|
|
19511
19823
|
async function readMarkerRunId(outputsDir, canvasPath) {
|
|
19512
19824
|
for (const key of [markerKey(canvasPath), legacyMarkerKey(canvasPath)]) {
|
|
19513
19825
|
try {
|
|
19514
|
-
const raw = await
|
|
19826
|
+
const raw = await readFile8(markerFile(outputsDir, key), "utf8");
|
|
19515
19827
|
const parsed = JSON.parse(raw);
|
|
19516
19828
|
if (typeof parsed.runId === "string" && parsed.runId.length > 0) return parsed.runId;
|
|
19517
19829
|
} catch {
|
|
@@ -19522,8 +19834,8 @@ async function readMarkerRunId(outputsDir, canvasPath) {
|
|
|
19522
19834
|
async function markRunInFlight(outputsDir, canvasPath, runId) {
|
|
19523
19835
|
try {
|
|
19524
19836
|
const file = markerFile(outputsDir, markerKey(canvasPath));
|
|
19525
|
-
await
|
|
19526
|
-
await
|
|
19837
|
+
await mkdir4(path11.dirname(file), { recursive: true });
|
|
19838
|
+
await writeFile5(file, JSON.stringify({ runId, canvasPath: path11.resolve(canvasPath), startedAt: Date.now() }));
|
|
19527
19839
|
} catch {
|
|
19528
19840
|
}
|
|
19529
19841
|
}
|
|
@@ -19537,8 +19849,8 @@ async function clearRunMarker(outputsDir, canvasPath) {
|
|
|
19537
19849
|
}
|
|
19538
19850
|
|
|
19539
19851
|
// src/commands/canvas/run-snapshot.ts
|
|
19540
|
-
import { mkdir as
|
|
19541
|
-
import
|
|
19852
|
+
import { mkdir as mkdir5, readdir as readdir4, readFile as readFile9, stat as stat2, writeFile as writeFile6 } from "fs/promises";
|
|
19853
|
+
import path12 from "path";
|
|
19542
19854
|
var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
|
|
19543
19855
|
var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
|
|
19544
19856
|
var EXT_TO_MIME = {
|
|
@@ -19565,11 +19877,11 @@ var EXT_TO_MIME = {
|
|
|
19565
19877
|
woff2: "font/woff2"
|
|
19566
19878
|
};
|
|
19567
19879
|
function mimeForFile(filePath) {
|
|
19568
|
-
const ext =
|
|
19880
|
+
const ext = path12.extname(filePath).slice(1).toLowerCase();
|
|
19569
19881
|
return EXT_TO_MIME[ext] ?? "application/octet-stream";
|
|
19570
19882
|
}
|
|
19571
19883
|
function toPosix(p) {
|
|
19572
|
-
return p.split(
|
|
19884
|
+
return p.split(path12.sep).join("/");
|
|
19573
19885
|
}
|
|
19574
19886
|
function localPathRefsFromCanvas(parsed) {
|
|
19575
19887
|
const nodes = parsed?.nodes;
|
|
@@ -19594,37 +19906,37 @@ function isSnapshotablePath(value) {
|
|
|
19594
19906
|
async function sourceRefsToSnapshot(canvasDir, parsed) {
|
|
19595
19907
|
const refs = new Set(localPathRefsFromCanvas(parsed));
|
|
19596
19908
|
for (const sceneFile of await listSceneFiles(canvasDir)) {
|
|
19597
|
-
refs.add(toPosix(
|
|
19909
|
+
refs.add(toPosix(path12.relative(canvasDir, sceneFile)));
|
|
19598
19910
|
}
|
|
19599
19911
|
refs.add(REBUILD_FILE);
|
|
19600
19912
|
return [...refs];
|
|
19601
19913
|
}
|
|
19602
19914
|
async function uploadRunSnapshot(client, opts) {
|
|
19603
19915
|
try {
|
|
19604
|
-
const canvasDir =
|
|
19916
|
+
const canvasDir = path12.dirname(opts.canvasPath);
|
|
19605
19917
|
const put = (bytes, mime) => putContentAddressed(client, bytes, mime, opts.signal);
|
|
19606
19918
|
const canvasBytes = Buffer.from(opts.raw);
|
|
19607
19919
|
const canvasUpload = await put(canvasBytes, "application/json");
|
|
19608
19920
|
const files = [];
|
|
19609
19921
|
const skipped = [];
|
|
19610
19922
|
for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
|
|
19611
|
-
const abs =
|
|
19923
|
+
const abs = path12.isAbsolute(refPath) ? refPath : path12.resolve(canvasDir, refPath);
|
|
19612
19924
|
let st;
|
|
19613
19925
|
try {
|
|
19614
19926
|
st = await stat2(abs);
|
|
19615
19927
|
} catch {
|
|
19616
|
-
skipped.push({ path: toPosix(
|
|
19928
|
+
skipped.push({ path: toPosix(path12.relative(canvasDir, abs)), reason: "missing" });
|
|
19617
19929
|
continue;
|
|
19618
19930
|
}
|
|
19619
19931
|
const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
|
|
19620
19932
|
for (const file of fileList) {
|
|
19621
|
-
const rel = toPosix(
|
|
19933
|
+
const rel = toPosix(path12.relative(canvasDir, file));
|
|
19622
19934
|
const size = (await stat2(file)).size;
|
|
19623
19935
|
if (size > MAX_SNAPSHOT_FILE_BYTES) {
|
|
19624
19936
|
skipped.push({ path: rel, reason: `too large (${size} bytes)` });
|
|
19625
19937
|
continue;
|
|
19626
19938
|
}
|
|
19627
|
-
const bytes = await
|
|
19939
|
+
const bytes = await readFile9(file);
|
|
19628
19940
|
const upload = await put(bytes, mimeForFile(file));
|
|
19629
19941
|
files.push({ path: rel, sha256: upload.sha256, url: upload.url });
|
|
19630
19942
|
}
|
|
@@ -19633,7 +19945,7 @@ async function uploadRunSnapshot(client, opts) {
|
|
|
19633
19945
|
schema: SNAPSHOT_SCHEMA,
|
|
19634
19946
|
creativeSlug: opts.creativeSlug,
|
|
19635
19947
|
canvasSha: canvasUpload.sha256,
|
|
19636
|
-
canvas: { path:
|
|
19948
|
+
canvas: { path: path12.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
|
|
19637
19949
|
files,
|
|
19638
19950
|
skipped: skipped.length > 0 ? skipped : void 0
|
|
19639
19951
|
};
|
|
@@ -19660,7 +19972,7 @@ async function putContentAddressed(client, bytes, mime, signal) {
|
|
|
19660
19972
|
}
|
|
19661
19973
|
async function listFilesRecursive(dir) {
|
|
19662
19974
|
const entries = await readdir4(dir, { recursive: true, withFileTypes: true });
|
|
19663
|
-
return entries.filter((d) => d.isFile()).map((d) =>
|
|
19975
|
+
return entries.filter((d) => d.isFile()).map((d) => path12.join(d.parentPath, d.name));
|
|
19664
19976
|
}
|
|
19665
19977
|
var SnapshotConflictError = class extends Error {
|
|
19666
19978
|
conflicts;
|
|
@@ -19672,19 +19984,19 @@ var SnapshotConflictError = class extends Error {
|
|
|
19672
19984
|
};
|
|
19673
19985
|
async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
19674
19986
|
const entries = [manifest.canvas, ...manifest.files];
|
|
19675
|
-
const resolvedTarget =
|
|
19987
|
+
const resolvedTarget = path12.resolve(targetDir);
|
|
19676
19988
|
const planned = [];
|
|
19677
19989
|
const conflicts = [];
|
|
19678
19990
|
const upToDate = [];
|
|
19679
19991
|
for (const entry of entries) {
|
|
19680
|
-
if (
|
|
19992
|
+
if (path12.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
|
|
19681
19993
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
19682
19994
|
}
|
|
19683
|
-
const target =
|
|
19684
|
-
if (target !== resolvedTarget && !target.startsWith(resolvedTarget +
|
|
19995
|
+
const target = path12.resolve(resolvedTarget, entry.path);
|
|
19996
|
+
if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path12.sep)) {
|
|
19685
19997
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
19686
19998
|
}
|
|
19687
|
-
const existing = await
|
|
19999
|
+
const existing = await readFile9(target).catch(() => null);
|
|
19688
20000
|
if (existing) {
|
|
19689
20001
|
if (sha256Hex(existing) === entry.sha256) {
|
|
19690
20002
|
upToDate.push(entry.path);
|
|
@@ -19706,15 +20018,15 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
|
19706
20018
|
if (sha256Hex(bytes) !== entry.sha256) {
|
|
19707
20019
|
throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
|
|
19708
20020
|
}
|
|
19709
|
-
await
|
|
19710
|
-
await
|
|
20021
|
+
await mkdir5(path12.dirname(target), { recursive: true });
|
|
20022
|
+
await writeFile6(target, bytes);
|
|
19711
20023
|
restored.push(entry.path);
|
|
19712
20024
|
}
|
|
19713
|
-
return { canvasPath:
|
|
20025
|
+
return { canvasPath: path12.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
|
|
19714
20026
|
}
|
|
19715
20027
|
|
|
19716
20028
|
// src/commands/canvas/run.ts
|
|
19717
|
-
var runCommand =
|
|
20029
|
+
var runCommand = defineCommand90({
|
|
19718
20030
|
meta: { name: "run", description: "Validate and execute a canvas JSON file." },
|
|
19719
20031
|
args: {
|
|
19720
20032
|
file: { type: "positional", required: true, description: "Path to canvas JSON" },
|
|
@@ -19790,8 +20102,8 @@ function resolveMaxCredits(...candidates) {
|
|
|
19790
20102
|
return void 0;
|
|
19791
20103
|
}
|
|
19792
20104
|
async function executeCanvasRun(opts) {
|
|
19793
|
-
const filePath =
|
|
19794
|
-
const raw = await
|
|
20105
|
+
const filePath = path13.resolve(opts.file);
|
|
20106
|
+
const raw = await readFile10(filePath, "utf8");
|
|
19795
20107
|
let parsed;
|
|
19796
20108
|
try {
|
|
19797
20109
|
parsed = JSON.parse(raw);
|
|
@@ -19803,7 +20115,7 @@ async function executeCanvasRun(opts) {
|
|
|
19803
20115
|
}
|
|
19804
20116
|
const attemptedSlug = creativeSlugFromCanvasPath(filePath);
|
|
19805
20117
|
if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
|
|
19806
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
20118
|
+
parsed = resolveRelativeCanvasPaths(parsed, path13.dirname(filePath));
|
|
19807
20119
|
try {
|
|
19808
20120
|
await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
19809
20121
|
`));
|
|
@@ -19909,7 +20221,7 @@ async function executeCanvasRun(opts) {
|
|
|
19909
20221
|
const canvasSha = sha256Hex(Buffer.from(raw));
|
|
19910
20222
|
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
19911
20223
|
const client = opts.record === false ? null : buildBackendClient();
|
|
19912
|
-
const outputsDir = opts.outputsDir ?
|
|
20224
|
+
const outputsDir = opts.outputsDir ? path13.resolve(opts.outputsDir) : path13.resolve("canvas");
|
|
19913
20225
|
const { runId, resumed, source, concurrentRunId } = await resolveRunId({
|
|
19914
20226
|
explicitRunId: opts.runId,
|
|
19915
20227
|
fresh: opts.fresh === true,
|
|
@@ -19943,7 +20255,7 @@ async function executeCanvasRun(opts) {
|
|
|
19943
20255
|
const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw, creativeSlug, parsed }) ?? void 0 : void 0;
|
|
19944
20256
|
const recordMeta = {
|
|
19945
20257
|
creativeSlug,
|
|
19946
|
-
canvasPath:
|
|
20258
|
+
canvasPath: path13.relative(process.cwd(), filePath) || void 0,
|
|
19947
20259
|
canvasSha,
|
|
19948
20260
|
// The fingerprint the dashboard compares against the current source to flag
|
|
19949
20261
|
// "edited since last render". Computed from the on-disk canvas.json +
|
|
@@ -20084,7 +20396,7 @@ async function postInitialRunRecord(client, payload) {
|
|
|
20084
20396
|
|
|
20085
20397
|
// src/commands/canvas/rerun.ts
|
|
20086
20398
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
20087
|
-
var rerunCommand =
|
|
20399
|
+
var rerunCommand = defineCommand91({
|
|
20088
20400
|
meta: {
|
|
20089
20401
|
name: "rerun",
|
|
20090
20402
|
description: "Re-run a creative's latest recorded canvas. Restores its definition files from run history first, so it works in a fresh workspace that never had the source chat's files \u2014 an interrupted run resumes (in-flight jobs re-attach), a completed one re-renders from the cache."
|
|
@@ -20113,24 +20425,24 @@ var rerunCommand = defineCommand89({
|
|
|
20113
20425
|
async run({ args }) {
|
|
20114
20426
|
const slug = String(args.slug);
|
|
20115
20427
|
if (!SLUG_PATTERN.test(slug)) {
|
|
20116
|
-
|
|
20428
|
+
fail2("invalid_slug", `"${slug}" is not a valid creative slug`);
|
|
20117
20429
|
}
|
|
20118
20430
|
const client = buildBackendClient();
|
|
20119
20431
|
if (!client) {
|
|
20120
|
-
|
|
20432
|
+
fail2("missing_credentials", "rerun needs backend credentials (BAKER_API_URL / BAKER_API_KEY)");
|
|
20121
20433
|
}
|
|
20122
20434
|
const latest = await client.getLatestSnapshotRun(slug);
|
|
20123
20435
|
if (!latest) {
|
|
20124
|
-
|
|
20436
|
+
fail2(
|
|
20125
20437
|
"no_snapshot_run",
|
|
20126
20438
|
`no rerunnable history for "${slug}" \u2014 the creative was never run by a CLI that records canvas snapshots`
|
|
20127
20439
|
);
|
|
20128
20440
|
}
|
|
20129
20441
|
const manifest = await fetchManifest(latest.canvasSnapshotUrl);
|
|
20130
20442
|
if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
|
|
20131
|
-
|
|
20443
|
+
fail2("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
|
|
20132
20444
|
}
|
|
20133
|
-
const targetDir =
|
|
20445
|
+
const targetDir = path14.resolve("src", "creatives", slug);
|
|
20134
20446
|
let restoredCanvasPath;
|
|
20135
20447
|
try {
|
|
20136
20448
|
const restore = await restoreRunSnapshot(manifest, targetDir, { force: args["force-remote"] === true });
|
|
@@ -20148,7 +20460,7 @@ var rerunCommand = defineCommand89({
|
|
|
20148
20460
|
}
|
|
20149
20461
|
} catch (e) {
|
|
20150
20462
|
if (e instanceof SnapshotConflictError) {
|
|
20151
|
-
|
|
20463
|
+
fail2(
|
|
20152
20464
|
"local_files_differ",
|
|
20153
20465
|
`local files differ from the recorded snapshot: ${e.conflicts.join(", ")}. Re-run with --force-remote to overwrite them, or run the local canvas directly with \`baker canvas run\`.`
|
|
20154
20466
|
);
|
|
@@ -20163,25 +20475,25 @@ var rerunCommand = defineCommand89({
|
|
|
20163
20475
|
});
|
|
20164
20476
|
}
|
|
20165
20477
|
});
|
|
20166
|
-
function
|
|
20478
|
+
function fail2(code, message) {
|
|
20167
20479
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
|
|
20168
20480
|
`);
|
|
20169
20481
|
process.exit(2);
|
|
20170
20482
|
}
|
|
20171
20483
|
async function fetchManifest(url) {
|
|
20172
20484
|
const res = await fetch(url);
|
|
20173
|
-
if (!res.ok)
|
|
20485
|
+
if (!res.ok) fail2("snapshot_unavailable", `snapshot manifest download failed: ${res.status} ${res.statusText}`);
|
|
20174
20486
|
const manifest = await res.json();
|
|
20175
20487
|
if (manifest?.schema !== SNAPSHOT_SCHEMA || typeof manifest.canvas?.path !== "string") {
|
|
20176
|
-
|
|
20488
|
+
fail2("snapshot_invalid", "snapshot manifest has an unexpected shape");
|
|
20177
20489
|
}
|
|
20178
20490
|
return manifest;
|
|
20179
20491
|
}
|
|
20180
20492
|
|
|
20181
20493
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
20182
|
-
import { access, mkdir as
|
|
20183
|
-
import
|
|
20184
|
-
import { defineCommand as
|
|
20494
|
+
import { access, mkdir as mkdir6, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
|
|
20495
|
+
import path18 from "path";
|
|
20496
|
+
import { defineCommand as defineCommand93 } from "citty";
|
|
20185
20497
|
|
|
20186
20498
|
// src/engine/scaffold/staticAd.ts
|
|
20187
20499
|
import { z as z17 } from "zod";
|
|
@@ -20403,8 +20715,13 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
20403
20715
|
inputs: { reference, target_blueprint: "$ref:prompt.asset" },
|
|
20404
20716
|
params: {
|
|
20405
20717
|
model: opts.genModel,
|
|
20406
|
-
|
|
20407
|
-
|
|
20718
|
+
// The hero renders at the closest ratio its model actually accepts; the
|
|
20719
|
+
// placement fan-out below adapts it to the exact platform formats, which
|
|
20720
|
+
// is also how a 4:5 Meta feed ad gets made on a model that has no 4:5.
|
|
20721
|
+
aspect_ratio: nearestSupportedAspectRatio("image_generate", opts.genModel, baseAspectRatio(blueprint, opts)),
|
|
20722
|
+
...supportsParam("image_generate", opts.genModel, "image_size") ? { image_size: "2K" } : {},
|
|
20723
|
+
// Per-model image defaults (gpt-image: quality=high).
|
|
20724
|
+
...imageProfileFor(opts.genModel)?.paramDefaults ?? {},
|
|
20408
20725
|
prompt
|
|
20409
20726
|
}
|
|
20410
20727
|
});
|
|
@@ -20453,7 +20770,7 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
20453
20770
|
}
|
|
20454
20771
|
|
|
20455
20772
|
// src/commands/canvas/creative-definition.ts
|
|
20456
|
-
import
|
|
20773
|
+
import path15 from "path";
|
|
20457
20774
|
var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
|
|
20458
20775
|
var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
|
|
20459
20776
|
function titleFromSlug(slug) {
|
|
@@ -20501,16 +20818,16 @@ function buildCreativeDefinition(input) {
|
|
|
20501
20818
|
}
|
|
20502
20819
|
|
|
20503
20820
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
20504
|
-
import
|
|
20821
|
+
import path16 from "path";
|
|
20505
20822
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
20506
20823
|
const file = rawFile.trim();
|
|
20507
20824
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
20508
|
-
const imageSource = imageIsUrl ? file :
|
|
20509
|
-
const outPath = out ?
|
|
20510
|
-
const blueprintPath =
|
|
20511
|
-
const creativeDir = slug ?
|
|
20512
|
-
const definitionPath = creativeDir ?
|
|
20513
|
-
const referencesDir = creativeDir ?
|
|
20825
|
+
const imageSource = imageIsUrl ? file : path16.resolve(cwd, file);
|
|
20826
|
+
const outPath = out ? path16.resolve(cwd, out) : slug ? path16.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path16.join(cwd, "static-ad.canvas.json") : path16.join(path16.dirname(imageSource), "static-ad.canvas.json");
|
|
20827
|
+
const blueprintPath = path16.join(path16.dirname(outPath), "prompt.json");
|
|
20828
|
+
const creativeDir = slug ? path16.dirname(outPath) : null;
|
|
20829
|
+
const definitionPath = creativeDir ? path16.join(creativeDir, "_definition.md") : null;
|
|
20830
|
+
const referencesDir = creativeDir ? path16.join(creativeDir, "references") : null;
|
|
20514
20831
|
return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
|
|
20515
20832
|
}
|
|
20516
20833
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -20520,9 +20837,9 @@ function isValidScaffoldSlug(slug) {
|
|
|
20520
20837
|
}
|
|
20521
20838
|
|
|
20522
20839
|
// src/commands/canvas/sync-definition.ts
|
|
20523
|
-
import { readdir as readdir5, readFile as
|
|
20524
|
-
import
|
|
20525
|
-
import { defineCommand as
|
|
20840
|
+
import { readdir as readdir5, readFile as readFile11, stat as stat3 } from "fs/promises";
|
|
20841
|
+
import path17 from "path";
|
|
20842
|
+
import { defineCommand as defineCommand92 } from "citty";
|
|
20526
20843
|
|
|
20527
20844
|
// src/commands/canvas/definition-graph.ts
|
|
20528
20845
|
var MAX_NODES = 300;
|
|
@@ -20614,15 +20931,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
20614
20931
|
}
|
|
20615
20932
|
}
|
|
20616
20933
|
async function resolveCanvasPath(inputPath) {
|
|
20617
|
-
const resolved =
|
|
20934
|
+
const resolved = path17.resolve(inputPath);
|
|
20618
20935
|
let dir = resolved;
|
|
20619
20936
|
try {
|
|
20620
20937
|
if ((await stat3(resolved)).isFile()) {
|
|
20621
20938
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
20622
|
-
dir =
|
|
20939
|
+
dir = path17.dirname(resolved);
|
|
20623
20940
|
}
|
|
20624
20941
|
} catch {
|
|
20625
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
20942
|
+
dir = resolved.endsWith(".canvas.json") ? path17.dirname(resolved) : resolved;
|
|
20626
20943
|
}
|
|
20627
20944
|
let entries;
|
|
20628
20945
|
try {
|
|
@@ -20633,9 +20950,9 @@ async function resolveCanvasPath(inputPath) {
|
|
|
20633
20950
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
20634
20951
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
20635
20952
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
20636
|
-
return chosen ?
|
|
20953
|
+
return chosen ? path17.join(dir, chosen) : null;
|
|
20637
20954
|
}
|
|
20638
|
-
var syncDefinitionCommand =
|
|
20955
|
+
var syncDefinitionCommand = defineCommand92({
|
|
20639
20956
|
meta: {
|
|
20640
20957
|
name: "sync-definition",
|
|
20641
20958
|
description: "Push a creative's current node-graph + input thumbnails to the dashboard without rendering. Runs automatically when you edit a creative's canvas.json or prompt.json."
|
|
@@ -20656,7 +20973,7 @@ var syncDefinitionCommand = defineCommand90({
|
|
|
20656
20973
|
if (!slug) return;
|
|
20657
20974
|
let canvas;
|
|
20658
20975
|
try {
|
|
20659
|
-
canvas = JSON.parse(await
|
|
20976
|
+
canvas = JSON.parse(await readFile11(canvasPath, "utf8"));
|
|
20660
20977
|
} catch {
|
|
20661
20978
|
return;
|
|
20662
20979
|
}
|
|
@@ -20681,7 +20998,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
|
|
|
20681
20998
|
if (!res.ok) throw new Error(`failed to download source image (${res.status})`);
|
|
20682
20999
|
bytes = Buffer.from(await res.arrayBuffer());
|
|
20683
21000
|
} else {
|
|
20684
|
-
bytes = await
|
|
21001
|
+
bytes = await readFile12(source);
|
|
20685
21002
|
}
|
|
20686
21003
|
const safe = await toModelSafeImage(bytes);
|
|
20687
21004
|
const sha256 = sha256Hex(safe.bytes);
|
|
@@ -20736,7 +21053,7 @@ DROP background extras, decorative props, generic scenery, and anything small or
|
|
|
20736
21053
|
For 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, and its castable attributes \u2014 breed/species for an animal, apparent age band, apparent origin/ethnicity, and wardrobe/setting for a person \u2014 so it can be recast to fit OUR audience/market), "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.`;
|
|
20737
21054
|
async function loadAssetText(ref, label) {
|
|
20738
21055
|
const r = ref;
|
|
20739
|
-
if (typeof r?.path === "string") return
|
|
21056
|
+
if (typeof r?.path === "string") return readFile12(r.path, "utf8");
|
|
20740
21057
|
if (typeof r?.url === "string") {
|
|
20741
21058
|
const res = await fetch(r.url);
|
|
20742
21059
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -20760,7 +21077,7 @@ function parseLayout(raw) {
|
|
|
20760
21077
|
}
|
|
20761
21078
|
return null;
|
|
20762
21079
|
}
|
|
20763
|
-
function
|
|
21080
|
+
function fail3(code, message) {
|
|
20764
21081
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
|
|
20765
21082
|
`);
|
|
20766
21083
|
process.exit(2);
|
|
@@ -20771,7 +21088,7 @@ function resolveModels(args) {
|
|
|
20771
21088
|
describeModel: pick("describe-model", "image_describe", "~google/gemini-pro-latest"),
|
|
20772
21089
|
selectModel: pick("select-model", "text_generate", "~google/gemini-flash-latest"),
|
|
20773
21090
|
layoutModel: pick("layout-model", "text_generate", "~google/gemini-flash-latest"),
|
|
20774
|
-
genModel: pick("gen-model", "image_generate", "openai/gpt-
|
|
21091
|
+
genModel: pick("gen-model", "image_generate", "openai/gpt-image-2")
|
|
20775
21092
|
};
|
|
20776
21093
|
}
|
|
20777
21094
|
var PLATFORM_PLACEMENTS = {
|
|
@@ -20851,8 +21168,8 @@ async function runVisionPasses(canvas) {
|
|
|
20851
21168
|
outputsByNode = result.outputs_by_node;
|
|
20852
21169
|
creditsSpent = result.stats?.total_credits;
|
|
20853
21170
|
} catch (e) {
|
|
20854
|
-
if (e instanceof ValidationError) return
|
|
20855
|
-
return
|
|
21171
|
+
if (e instanceof ValidationError) return fail3("validation", JSON.stringify(e.issues));
|
|
21172
|
+
return fail3("describe", e instanceof Error ? e.message : String(e));
|
|
20856
21173
|
}
|
|
20857
21174
|
try {
|
|
20858
21175
|
const blueprint = JSON.parse(await loadAssetText(outputsByNode.describe?.description, "describe output"));
|
|
@@ -20860,10 +21177,10 @@ async function runVisionPasses(canvas) {
|
|
|
20860
21177
|
const layout = parseLayout(await loadAssetText(outputsByNode.layout?.text, "layout output"));
|
|
20861
21178
|
return { blueprint, elements, layout, creditsSpent };
|
|
20862
21179
|
} catch (e) {
|
|
20863
|
-
return
|
|
21180
|
+
return fail3("read_outputs", e instanceof Error ? e.message : String(e));
|
|
20864
21181
|
}
|
|
20865
21182
|
}
|
|
20866
|
-
var scaffoldStaticAdCommand =
|
|
21183
|
+
var scaffoldStaticAdCommand = defineCommand93({
|
|
20867
21184
|
meta: {
|
|
20868
21185
|
name: "scaffold-static-ad",
|
|
20869
21186
|
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. By default it also fans the hero out to the platform's placement ratios (via image_aspect_adapt \u2014 the hero ratio is free, the rest are billed AI recompositions); pass --placements none for a single base ad, or a preset to pick the set. Edit prompt.json and drop the real assets, then `baker canvas run` it."
|
|
@@ -20912,7 +21229,7 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20912
21229
|
process.cwd(),
|
|
20913
21230
|
slug
|
|
20914
21231
|
);
|
|
20915
|
-
await
|
|
21232
|
+
await mkdir6(path18.dirname(outPath), { recursive: true });
|
|
20916
21233
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
|
|
20917
21234
|
let durableSourceUrl;
|
|
20918
21235
|
if (referencesDir) {
|
|
@@ -20921,7 +21238,7 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20921
21238
|
try {
|
|
20922
21239
|
durableSourceUrl = await uploadSourceAsReference(imageSource, imageIsUrl, client);
|
|
20923
21240
|
} catch (e) {
|
|
20924
|
-
return
|
|
21241
|
+
return fail3("source_unavailable", e instanceof Error ? e.message : String(e));
|
|
20925
21242
|
}
|
|
20926
21243
|
}
|
|
20927
21244
|
}
|
|
@@ -20938,7 +21255,7 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20938
21255
|
if (layout && annotated && typeof annotated === "object") {
|
|
20939
21256
|
annotated.layout = layout;
|
|
20940
21257
|
}
|
|
20941
|
-
await
|
|
21258
|
+
await writeFile7(blueprintPath, `${JSON.stringify(annotated, null, 2)}
|
|
20942
21259
|
`, "utf8");
|
|
20943
21260
|
let canvasImagePath = imageSource;
|
|
20944
21261
|
let canvasImageIsUrl = imageIsUrl;
|
|
@@ -20955,7 +21272,7 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20955
21272
|
args.placements ? String(args.placements) : void 0,
|
|
20956
21273
|
definitionPlatform
|
|
20957
21274
|
);
|
|
20958
|
-
if (!placementsResult.ok) return
|
|
21275
|
+
if (!placementsResult.ok) return fail3("invalid_placements", placementsResult.message);
|
|
20959
21276
|
const placements = placementsResult.placements;
|
|
20960
21277
|
const opts = {
|
|
20961
21278
|
genModel,
|
|
@@ -20973,7 +21290,7 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20973
21290
|
canvas = scaffoldStaticAd(blueprint, elements, opts);
|
|
20974
21291
|
report = staticAdReport(blueprint, elements, opts);
|
|
20975
21292
|
} catch (e) {
|
|
20976
|
-
return
|
|
21293
|
+
return fail3("scaffold", e instanceof Error ? e.message : String(e));
|
|
20977
21294
|
}
|
|
20978
21295
|
const validation = await validateCanvasDeep(canvas, defaultRegistry());
|
|
20979
21296
|
if (!validation.ok) {
|
|
@@ -20983,10 +21300,10 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20983
21300
|
);
|
|
20984
21301
|
process.exit(2);
|
|
20985
21302
|
}
|
|
20986
|
-
await
|
|
21303
|
+
await writeFile7(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
20987
21304
|
`, "utf8");
|
|
20988
21305
|
if (definitionPath && !await fileExists(definitionPath)) {
|
|
20989
|
-
await
|
|
21306
|
+
await writeFile7(
|
|
20990
21307
|
definitionPath,
|
|
20991
21308
|
buildCreativeDefinition({
|
|
20992
21309
|
title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
|
|
@@ -21032,7 +21349,7 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
21032
21349
|
run_estimated_credits: validation.estimatedCredits
|
|
21033
21350
|
},
|
|
21034
21351
|
checklist: {
|
|
21035
|
-
edit_prompt: `Edit ${
|
|
21352
|
+
edit_prompt: `Edit ${path18.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
|
|
21036
21353
|
assets_to_supply: report.elements,
|
|
21037
21354
|
font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path (the describe pass recorded the ad's typefaces under `fonts` in prompt.json \u2014 match those). The font is wired into the render as a TYPE SPECIMEN reference so generated text takes the brand letterforms. Delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
|
|
21038
21355
|
actor_sheets: report.actor_sheets.length > 0 ? `Each living hero (${report.actor_sheets.join(", ")}) is fused into a generated multi-view reference sheet (image_reference_sheet) that the render grounds on \u2014 so drop ONE clean photo at that hero's ingest and the sheet builds the consistent turnaround. Pass --skip-actor-sheets to ground on the lone photo instead.` : "none (no person/animal heroes detected, or --skip-actor-sheets)",
|
|
@@ -21049,10 +21366,10 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
21049
21366
|
});
|
|
21050
21367
|
|
|
21051
21368
|
// src/commands/canvas/scaffold-video.ts
|
|
21052
|
-
import { access as access2, cp, mkdir as
|
|
21369
|
+
import { access as access2, cp, mkdir as mkdir7, readFile as readFile15, rm as rm6, writeFile as writeFile8 } from "fs/promises";
|
|
21053
21370
|
import { tmpdir as tmpdir2 } from "os";
|
|
21054
|
-
import
|
|
21055
|
-
import { defineCommand as
|
|
21371
|
+
import path21 from "path";
|
|
21372
|
+
import { defineCommand as defineCommand94 } from "citty";
|
|
21056
21373
|
|
|
21057
21374
|
// src/engine/scaffold/lib/model-router.ts
|
|
21058
21375
|
var SEEDANCE = "bytedance/seedance-2.0";
|
|
@@ -21090,7 +21407,7 @@ function routeVideoModel(input) {
|
|
|
21090
21407
|
|
|
21091
21408
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
21092
21409
|
import { execFile as execFile2 } from "child_process";
|
|
21093
|
-
import { mkdtemp, readdir as readdir6, readFile as
|
|
21410
|
+
import { mkdtemp, readdir as readdir6, readFile as readFile13, rm as rm5 } from "fs/promises";
|
|
21094
21411
|
import { tmpdir } from "os";
|
|
21095
21412
|
import { join as join2 } from "path";
|
|
21096
21413
|
import { promisify as promisify2 } from "util";
|
|
@@ -21166,7 +21483,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
21166
21483
|
);
|
|
21167
21484
|
const csvName = (await readdir6(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
21168
21485
|
if (!csvName) return [];
|
|
21169
|
-
return parsePySceneDetectCsvCuts(await
|
|
21486
|
+
return parsePySceneDetectCsvCuts(await readFile13(join2(outDir, csvName), "utf-8"));
|
|
21170
21487
|
} finally {
|
|
21171
21488
|
await rm5(outDir, { recursive: true, force: true });
|
|
21172
21489
|
}
|
|
@@ -21191,23 +21508,23 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
21191
21508
|
|
|
21192
21509
|
// src/commands/canvas/composition-path.ts
|
|
21193
21510
|
import { existsSync as existsSync3 } from "fs";
|
|
21194
|
-
import
|
|
21511
|
+
import path19 from "path";
|
|
21195
21512
|
function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
|
|
21196
|
-
const rel =
|
|
21513
|
+
const rel = path19.join("canvas", name);
|
|
21197
21514
|
let dir = startDir;
|
|
21198
21515
|
for (let i = 0; i < maxDepth; i++) {
|
|
21199
|
-
const candidate =
|
|
21200
|
-
if (exists(
|
|
21201
|
-
const parent =
|
|
21516
|
+
const candidate = path19.join(dir, rel);
|
|
21517
|
+
if (exists(path19.join(candidate, "meta.json"))) return candidate;
|
|
21518
|
+
const parent = path19.dirname(dir);
|
|
21202
21519
|
if (parent === dir) break;
|
|
21203
21520
|
dir = parent;
|
|
21204
21521
|
}
|
|
21205
|
-
return
|
|
21522
|
+
return path19.resolve(startDir, "../../../", rel);
|
|
21206
21523
|
}
|
|
21207
21524
|
|
|
21208
21525
|
// src/commands/canvas/gitignore.ts
|
|
21209
|
-
import { appendFile, readFile as
|
|
21210
|
-
import
|
|
21526
|
+
import { appendFile, readFile as readFile14 } from "fs/promises";
|
|
21527
|
+
import path20 from "path";
|
|
21211
21528
|
function missingGitignoreEntries(existing, entries) {
|
|
21212
21529
|
const present2 = new Set(
|
|
21213
21530
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -21215,10 +21532,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
21215
21532
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
21216
21533
|
}
|
|
21217
21534
|
async function ensureGitignore(dir, entries) {
|
|
21218
|
-
const file =
|
|
21535
|
+
const file = path20.join(dir, ".gitignore");
|
|
21219
21536
|
let existing;
|
|
21220
21537
|
try {
|
|
21221
|
-
existing = await
|
|
21538
|
+
existing = await readFile14(file, "utf8");
|
|
21222
21539
|
} catch {
|
|
21223
21540
|
return;
|
|
21224
21541
|
}
|
|
@@ -21257,7 +21574,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
|
|
|
21257
21574
|
For each kept element return: { "type": one of person|animal|product|logo|badge|location, "label": a short UPPER_SNAKE_CASE name (e.g. HERO, CREATOR_SKEPTIC, INSURANCE_CARD, LOGO), "description": a concrete reusable description to source/shoot the real asset \u2014 for a person/animal give a NEUTRAL castable role (e.g. "hero pet-owner, woman in her 30s" or "a small beagle"), NOT the original individual's literal face/identity: we RECAST with a FRESH person/animal, so never tell the agent to reuse the original. "expression": a living subject's typical expression or null, "cast_id": the global.cast id if it maps to one else null, "same_as": the label of another element this is the SAME individual as (different wardrobe/persona) else null, "scenes": the 0-based indices of ONLY the scenes where the element is ACTUALLY VISIBLE ON SCREEN \u2014 judged from that scene's start_frame_prompt / end_frame_prompt subjects and its action_detail, NOT from who is merely speaking. A narrator heard over b-roll is NOT present in that b-roll scene; a dog-running cutaway does NOT contain the couch creator just because she talks across it. Do NOT pad the list \u2014 an element wrongly listed in a scene makes the reproduction render the wrong subject there (e.g. the creator appearing in a pure-dog b-roll). When in doubt, leave a scene OUT. Output ONLY the JSON object.`;
|
|
21258
21575
|
async function loadAssetText2(ref, label) {
|
|
21259
21576
|
const r = ref;
|
|
21260
|
-
if (typeof r?.path === "string") return
|
|
21577
|
+
if (typeof r?.path === "string") return readFile15(r.path, "utf8");
|
|
21261
21578
|
if (typeof r?.url === "string") {
|
|
21262
21579
|
const res = await fetch(r.url);
|
|
21263
21580
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -21276,7 +21593,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
21276
21593
|
async function stageCaptions(outDir, transcript) {
|
|
21277
21594
|
const text = transcript?.trim();
|
|
21278
21595
|
if (!text || text === "[]") return {};
|
|
21279
|
-
const compositionPath =
|
|
21596
|
+
const compositionPath = path21.join(outDir, "tiktok-captions-composition");
|
|
21280
21597
|
await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
21281
21598
|
return { compositionPath };
|
|
21282
21599
|
}
|
|
@@ -21294,12 +21611,12 @@ function patchCompositionHtml(html, dims) {
|
|
|
21294
21611
|
return html.replace(/(<meta\s+name="viewport"\s+content="width=)\d+(,\s*height=)\d+(")/i, `$1${dims.w}$2${dims.h}$3`).replace(/(width:\s*)\d+(px;\s*height:\s*)\d+(px;)/i, `$1${dims.w}$2${dims.h}$3`).replace(/(data-width=")\d+(")/i, `$1${dims.w}$2`).replace(/(data-height=")\d+(")/i, `$1${dims.h}$2`);
|
|
21295
21612
|
}
|
|
21296
21613
|
async function stampCompositionDims(compositionDir, dims) {
|
|
21297
|
-
const metaPath =
|
|
21298
|
-
const rawMeta = await
|
|
21299
|
-
await
|
|
21300
|
-
const htmlPath =
|
|
21301
|
-
const rawHtml = await
|
|
21302
|
-
await
|
|
21614
|
+
const metaPath = path21.join(compositionDir, "meta.json");
|
|
21615
|
+
const rawMeta = await readFile15(metaPath, "utf8");
|
|
21616
|
+
await writeFile8(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
21617
|
+
const htmlPath = path21.join(compositionDir, "index.html");
|
|
21618
|
+
const rawHtml = await readFile15(htmlPath, "utf8");
|
|
21619
|
+
await writeFile8(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
21303
21620
|
}
|
|
21304
21621
|
function parseElements2(raw) {
|
|
21305
21622
|
const parsed = JSON.parse(raw);
|
|
@@ -21334,7 +21651,7 @@ async function detectShotCutsBestEffort(videoPath, threshold) {
|
|
|
21334
21651
|
return void 0;
|
|
21335
21652
|
}
|
|
21336
21653
|
}
|
|
21337
|
-
function
|
|
21654
|
+
function fail4(code, message) {
|
|
21338
21655
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
|
|
21339
21656
|
`);
|
|
21340
21657
|
process.exit(2);
|
|
@@ -21346,7 +21663,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
21346
21663
|
"video/x-matroska": ".mkv"
|
|
21347
21664
|
};
|
|
21348
21665
|
function referenceVideoExt(url, contentType) {
|
|
21349
|
-
const fromPath =
|
|
21666
|
+
const fromPath = path21.extname(new URL(url).pathname).toLowerCase();
|
|
21350
21667
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
21351
21668
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
21352
21669
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -21372,7 +21689,7 @@ function videoDefinitionDescription(blueprint) {
|
|
|
21372
21689
|
return typeof product === "string" && product.trim() ? product.trim() : void 0;
|
|
21373
21690
|
}
|
|
21374
21691
|
async function materializeReferenceVideo(fileArg2) {
|
|
21375
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
21692
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path21.resolve(fileArg2);
|
|
21376
21693
|
let res;
|
|
21377
21694
|
try {
|
|
21378
21695
|
res = await fetch(fileArg2);
|
|
@@ -21382,11 +21699,11 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
21382
21699
|
if (!res.ok) throw new Error(`failed to download reference video (${res.status} ${res.statusText})`);
|
|
21383
21700
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
21384
21701
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
21385
|
-
const dest =
|
|
21702
|
+
const dest = path21.join(
|
|
21386
21703
|
tmpdir2(),
|
|
21387
21704
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, res.headers.get("content-type"))}`
|
|
21388
21705
|
);
|
|
21389
|
-
await
|
|
21706
|
+
await writeFile8(dest, bytes);
|
|
21390
21707
|
return dest;
|
|
21391
21708
|
}
|
|
21392
21709
|
function resolveSeamDedup(raw) {
|
|
@@ -21413,7 +21730,7 @@ function resolveModels2(args) {
|
|
|
21413
21730
|
// Default to the strongest image model (matches the static-ad scaffold); the
|
|
21414
21731
|
// frame generators need the most faithful text/identity reproduction. Override
|
|
21415
21732
|
// with --image-model for a cheaper/faster pass.
|
|
21416
|
-
imageModel: pick("image-model", "image_generate", "openai/gpt-
|
|
21733
|
+
imageModel: pick("image-model", "image_generate", "openai/gpt-image-2")
|
|
21417
21734
|
};
|
|
21418
21735
|
}
|
|
21419
21736
|
function hasPhotorealCast(elements) {
|
|
@@ -21494,9 +21811,9 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
|
|
|
21494
21811
|
blueprint = JSON.parse(await loadAssetText2(r1.outputs_by_node.deconstruct?.analysis, "deconstruct output"));
|
|
21495
21812
|
transcript = await loadTranscriptBestEffort(r1.outputs_by_node.deconstruct?.transcript);
|
|
21496
21813
|
} catch (e) {
|
|
21497
|
-
if (e instanceof ValidationError) return
|
|
21498
|
-
if (e instanceof SyntaxError) return
|
|
21499
|
-
return
|
|
21814
|
+
if (e instanceof ValidationError) return fail4("validation", JSON.stringify(e.issues));
|
|
21815
|
+
if (e instanceof SyntaxError) return fail4("read_outputs", e.message);
|
|
21816
|
+
return fail4("deconstruct", e instanceof Error ? e.message : String(e));
|
|
21500
21817
|
}
|
|
21501
21818
|
const slimJson = JSON.stringify(slimBlueprintForSelection(blueprint));
|
|
21502
21819
|
try {
|
|
@@ -21505,12 +21822,12 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
|
|
|
21505
21822
|
const elements = parseElements2(await loadAssetText2(r2.outputs_by_node.select?.text, "selection output"));
|
|
21506
21823
|
return { blueprint, elements, transcript, creditsSpent: sawCredits ? credits : void 0 };
|
|
21507
21824
|
} catch (e) {
|
|
21508
|
-
if (e instanceof ValidationError) return
|
|
21509
|
-
if (e instanceof SyntaxError) return
|
|
21510
|
-
return
|
|
21825
|
+
if (e instanceof ValidationError) return fail4("validation", JSON.stringify(e.issues));
|
|
21826
|
+
if (e instanceof SyntaxError) return fail4("read_outputs", e.message);
|
|
21827
|
+
return fail4("deconstruct", e instanceof Error ? e.message : String(e));
|
|
21511
21828
|
}
|
|
21512
21829
|
}
|
|
21513
|
-
var scaffoldVideoCommand =
|
|
21830
|
+
var scaffoldVideoCommand = defineCommand94({
|
|
21514
21831
|
meta: {
|
|
21515
21832
|
name: "scaffold-video",
|
|
21516
21833
|
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 a split, editable blueprint: a global prompt.json plus one small scenes/sNN.json per scene) 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 a scene's scenes/sNN.json (or prompt.json for global cast/palette/brand), drop the real source images, then `baker canvas run`."
|
|
@@ -21591,7 +21908,7 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21591
21908
|
}
|
|
21592
21909
|
const isUrl = /^https?:\/\//i.test(fileArg2);
|
|
21593
21910
|
if (isUrl && !slug && !args.out) {
|
|
21594
|
-
return
|
|
21911
|
+
return fail4(
|
|
21595
21912
|
"missing_output_target",
|
|
21596
21913
|
"When the reference is a URL, pass --slug (writes src/creatives/<slug>/) or --out <path> so the scaffolded canvas has a home in the repo."
|
|
21597
21914
|
);
|
|
@@ -21600,13 +21917,13 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21600
21917
|
try {
|
|
21601
21918
|
videoPath = await materializeReferenceVideo(fileArg2);
|
|
21602
21919
|
} catch (e) {
|
|
21603
|
-
return
|
|
21920
|
+
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
21604
21921
|
}
|
|
21605
|
-
const base =
|
|
21606
|
-
const outPath = args.out ?
|
|
21607
|
-
const outDir =
|
|
21608
|
-
const blueprintPath =
|
|
21609
|
-
const blueprintStylePath =
|
|
21922
|
+
const base = path21.basename(videoPath, path21.extname(videoPath));
|
|
21923
|
+
const outPath = args.out ? path21.resolve(String(args.out)) : slug ? path21.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path21.join(path21.dirname(videoPath), `${base}.video.canvas.json`);
|
|
21924
|
+
const outDir = path21.dirname(outPath);
|
|
21925
|
+
const blueprintPath = path21.join(outDir, "prompt.json");
|
|
21926
|
+
const blueprintStylePath = path21.join(outDir, "prompt.style.json");
|
|
21610
21927
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
21611
21928
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
21612
21929
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -21630,10 +21947,10 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21630
21947
|
process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
|
|
21631
21948
|
`);
|
|
21632
21949
|
}
|
|
21633
|
-
await
|
|
21950
|
+
await mkdir7(outDir, { recursive: true });
|
|
21634
21951
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
21635
21952
|
await writeSceneFiles(outDir, annotated);
|
|
21636
|
-
await
|
|
21953
|
+
await writeFile8(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
|
|
21637
21954
|
let aspect;
|
|
21638
21955
|
try {
|
|
21639
21956
|
aspect = resolveAspect(
|
|
@@ -21642,7 +21959,7 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21642
21959
|
genAspectsFor(videoModel)
|
|
21643
21960
|
);
|
|
21644
21961
|
} catch (e) {
|
|
21645
|
-
return
|
|
21962
|
+
return fail4("aspect", e instanceof Error ? e.message : String(e));
|
|
21646
21963
|
}
|
|
21647
21964
|
const outDims = canvasDims(aspect.outAr);
|
|
21648
21965
|
if (aspect.remapped) {
|
|
@@ -21651,29 +21968,29 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21651
21968
|
`
|
|
21652
21969
|
);
|
|
21653
21970
|
}
|
|
21654
|
-
const compositionDest =
|
|
21971
|
+
const compositionDest = path21.join(outDir, "video-overlay-composition");
|
|
21655
21972
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
21656
21973
|
await stampCompositionDims(compositionDest, outDims);
|
|
21657
|
-
const indexPath =
|
|
21974
|
+
const indexPath = path21.join(compositionDest, "index.html");
|
|
21658
21975
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
|
|
21659
|
-
const indexHtml = await
|
|
21976
|
+
const indexHtml = await readFile15(indexPath, "utf8");
|
|
21660
21977
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
21661
21978
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
21662
|
-
|
|
21979
|
+
fail4(
|
|
21663
21980
|
"composition_marker_missing",
|
|
21664
21981
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
21665
21982
|
);
|
|
21666
21983
|
}
|
|
21667
|
-
await
|
|
21984
|
+
await writeFile8(indexPath, injected, "utf8");
|
|
21668
21985
|
const captions = await stageCaptions(outDir, transcript);
|
|
21669
21986
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
21670
21987
|
const opts = {
|
|
21671
21988
|
imageModel,
|
|
21672
21989
|
videoModel,
|
|
21673
|
-
overlayCompositionPath:
|
|
21674
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
21675
|
-
blueprintPath:
|
|
21676
|
-
blueprintStylePath:
|
|
21990
|
+
overlayCompositionPath: path21.relative(outDir, compositionDest),
|
|
21991
|
+
captionsCompositionPath: captions.compositionPath ? path21.relative(outDir, captions.compositionPath) : void 0,
|
|
21992
|
+
blueprintPath: path21.relative(outDir, blueprintPath),
|
|
21993
|
+
blueprintStylePath: path21.relative(outDir, blueprintStylePath),
|
|
21677
21994
|
frames,
|
|
21678
21995
|
ambient: Boolean(args.ambient),
|
|
21679
21996
|
seamDedup: resolveSeamDedup(args["seam-dedup"]),
|
|
@@ -21686,7 +22003,7 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21686
22003
|
canvas = scaffoldVideoCanvas(blueprint, elements, opts);
|
|
21687
22004
|
report = videoReport(blueprint, elements);
|
|
21688
22005
|
} catch (e) {
|
|
21689
|
-
return
|
|
22006
|
+
return fail4("scaffold", e instanceof Error ? e.message : String(e));
|
|
21690
22007
|
}
|
|
21691
22008
|
if (captions.compositionPath && !canvas.nodes.some((n) => n.id === "captions")) {
|
|
21692
22009
|
await rm6(captions.compositionPath, { recursive: true, force: true });
|
|
@@ -21698,10 +22015,10 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21698
22015
|
todo.blocking_validation_issues = validation.issues;
|
|
21699
22016
|
meta.todo = todo;
|
|
21700
22017
|
}
|
|
21701
|
-
await
|
|
22018
|
+
await writeFile8(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
21702
22019
|
`, "utf8");
|
|
21703
|
-
await
|
|
21704
|
-
|
|
22020
|
+
await writeFile8(
|
|
22021
|
+
path21.join(outDir, REBUILD_FILE),
|
|
21705
22022
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
21706
22023
|
`,
|
|
21707
22024
|
"utf8"
|
|
@@ -21726,9 +22043,9 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21726
22043
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
21727
22044
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
21728
22045
|
if (slug) {
|
|
21729
|
-
const definitionPath =
|
|
22046
|
+
const definitionPath = path21.join(outDir, "_definition.md");
|
|
21730
22047
|
if (!await fileExists2(definitionPath)) {
|
|
21731
|
-
await
|
|
22048
|
+
await writeFile8(
|
|
21732
22049
|
definitionPath,
|
|
21733
22050
|
buildCreativeDefinition({
|
|
21734
22051
|
title: titleFromSlug(slug),
|
|
@@ -21781,7 +22098,7 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21781
22098
|
graph: canvas.metadata?.video?.graph_stats
|
|
21782
22099
|
},
|
|
21783
22100
|
checklist: {
|
|
21784
|
-
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${
|
|
22101
|
+
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${path21.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path21.basename(blueprintStylePath)}, the projection each frame's target_blueprint reads) \u2014 so your scene edits reach the render automatically. Never hand-edit the inlined node prompts in the canvas or the derived ${path21.basename(blueprintStylePath)}; both are regenerated.`,
|
|
21785
22102
|
recurring_elements_to_supply: report.elements,
|
|
21786
22103
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
21787
22104
|
scene: d.scene,
|
|
@@ -21818,9 +22135,9 @@ var scaffoldVideoCommand = defineCommand92({
|
|
|
21818
22135
|
});
|
|
21819
22136
|
|
|
21820
22137
|
// src/commands/canvas/set-prompt.ts
|
|
21821
|
-
import { readFile as
|
|
21822
|
-
import
|
|
21823
|
-
import { defineCommand as
|
|
22138
|
+
import { readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
|
|
22139
|
+
import path22 from "path";
|
|
22140
|
+
import { defineCommand as defineCommand95 } from "citty";
|
|
21824
22141
|
function setNodePrompt(canvas, nodeId, text) {
|
|
21825
22142
|
const nodes = canvas?.nodes;
|
|
21826
22143
|
if (!Array.isArray(nodes)) throw new Error("canvas has no nodes array");
|
|
@@ -21835,7 +22152,7 @@ function setNodePrompt(canvas, nodeId, text) {
|
|
|
21835
22152
|
newNodes[idx] = newNode;
|
|
21836
22153
|
return { ...canvas, nodes: newNodes };
|
|
21837
22154
|
}
|
|
21838
|
-
var setPromptCommand =
|
|
22155
|
+
var setPromptCommand = defineCommand95({
|
|
21839
22156
|
meta: {
|
|
21840
22157
|
name: "set-prompt",
|
|
21841
22158
|
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."
|
|
@@ -21847,17 +22164,17 @@ var setPromptCommand = defineCommand93({
|
|
|
21847
22164
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
21848
22165
|
},
|
|
21849
22166
|
async run({ args }) {
|
|
21850
|
-
const filePath =
|
|
22167
|
+
const filePath = path22.resolve(String(args.file));
|
|
21851
22168
|
let canvas;
|
|
21852
22169
|
try {
|
|
21853
|
-
canvas = JSON.parse(await
|
|
22170
|
+
canvas = JSON.parse(await readFile16(filePath, "utf8"));
|
|
21854
22171
|
} catch (e) {
|
|
21855
22172
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
|
|
21856
22173
|
`);
|
|
21857
22174
|
process.exit(2);
|
|
21858
22175
|
}
|
|
21859
22176
|
let text;
|
|
21860
|
-
if (args["text-file"]) text = await
|
|
22177
|
+
if (args["text-file"]) text = await readFile16(path22.resolve(String(args["text-file"])), "utf8");
|
|
21861
22178
|
else if (args.text !== void 0) text = String(args.text);
|
|
21862
22179
|
else {
|
|
21863
22180
|
process.stderr.write(
|
|
@@ -21878,14 +22195,14 @@ var setPromptCommand = defineCommand93({
|
|
|
21878
22195
|
process.exit(2);
|
|
21879
22196
|
return;
|
|
21880
22197
|
}
|
|
21881
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
22198
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path22.dirname(filePath)), defaultRegistry());
|
|
21882
22199
|
if (!validation.ok) {
|
|
21883
22200
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
21884
22201
|
`);
|
|
21885
22202
|
process.exit(2);
|
|
21886
22203
|
return;
|
|
21887
22204
|
}
|
|
21888
|
-
await
|
|
22205
|
+
await writeFile9(filePath, `${JSON.stringify(updated, null, 2)}
|
|
21889
22206
|
`, "utf8");
|
|
21890
22207
|
process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text.length }, null, 2)}
|
|
21891
22208
|
`);
|
|
@@ -21893,18 +22210,18 @@ var setPromptCommand = defineCommand93({
|
|
|
21893
22210
|
});
|
|
21894
22211
|
|
|
21895
22212
|
// src/commands/canvas/validate.ts
|
|
21896
|
-
import { readFile as
|
|
21897
|
-
import
|
|
21898
|
-
import { defineCommand as
|
|
21899
|
-
var validateCommand =
|
|
22213
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
22214
|
+
import path23 from "path";
|
|
22215
|
+
import { defineCommand as defineCommand96 } from "citty";
|
|
22216
|
+
var validateCommand = defineCommand96({
|
|
21900
22217
|
meta: {
|
|
21901
22218
|
name: "validate",
|
|
21902
22219
|
description: "Validate a canvas JSON file (no execution). Includes a per-node cost preview and runs each node's deep validators (composition meta checks for hyperframe_render/_snapshot)."
|
|
21903
22220
|
},
|
|
21904
22221
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
21905
22222
|
async run({ args }) {
|
|
21906
|
-
const filePath =
|
|
21907
|
-
const raw = await
|
|
22223
|
+
const filePath = path23.resolve(String(args.file));
|
|
22224
|
+
const raw = await readFile17(filePath, "utf8");
|
|
21908
22225
|
let parsed;
|
|
21909
22226
|
try {
|
|
21910
22227
|
parsed = JSON.parse(raw);
|
|
@@ -21914,7 +22231,7 @@ var validateCommand = defineCommand94({
|
|
|
21914
22231
|
`);
|
|
21915
22232
|
process.exit(2);
|
|
21916
22233
|
}
|
|
21917
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
22234
|
+
parsed = resolveRelativeCanvasPaths(parsed, path23.dirname(filePath));
|
|
21918
22235
|
let styleProjection = "not_applicable";
|
|
21919
22236
|
try {
|
|
21920
22237
|
styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
@@ -21966,7 +22283,7 @@ var validateCommand = defineCommand94({
|
|
|
21966
22283
|
});
|
|
21967
22284
|
|
|
21968
22285
|
// src/commands/canvas/index.ts
|
|
21969
|
-
var canvasCommand =
|
|
22286
|
+
var canvasCommand = defineCommand97({
|
|
21970
22287
|
meta: {
|
|
21971
22288
|
name: "canvas",
|
|
21972
22289
|
description: `Run Baker creative canvas JSON files locally. Local nodes execute in-process; remote nodes POST to the Convex backend gateway.
|
|
@@ -22000,7 +22317,7 @@ Full guide: __tooling__/docs/tools/baker/canvas.md`
|
|
|
22000
22317
|
});
|
|
22001
22318
|
|
|
22002
22319
|
// src/commands/chats/index.ts
|
|
22003
|
-
import { defineCommand as
|
|
22320
|
+
import { defineCommand as defineCommand98 } from "citty";
|
|
22004
22321
|
var STATUS_GROUPS = ["active", "archived", "all"];
|
|
22005
22322
|
var REPO_SURFACES = ["knowledge", "company", "brand", "landings", "flows", "creatives"];
|
|
22006
22323
|
function parseBoundedInt(raw, name, min, max) {
|
|
@@ -22022,7 +22339,7 @@ registerSchema({
|
|
|
22022
22339
|
full: { type: "boolean", description: "Include each Session's full change list", required: false, default: false }
|
|
22023
22340
|
}
|
|
22024
22341
|
});
|
|
22025
|
-
var listCommand5 =
|
|
22342
|
+
var listCommand5 = defineCommand98({
|
|
22026
22343
|
meta: { name: "list", description: "List other Sessions on this account, newest first." },
|
|
22027
22344
|
args: {
|
|
22028
22345
|
status: { type: "string", description: "Filter: active|archived|all (default: all)", required: false },
|
|
@@ -22077,7 +22394,7 @@ registerSchema({
|
|
|
22077
22394
|
}
|
|
22078
22395
|
}
|
|
22079
22396
|
});
|
|
22080
|
-
var viewCommand =
|
|
22397
|
+
var viewCommand = defineCommand98({
|
|
22081
22398
|
meta: {
|
|
22082
22399
|
name: "view",
|
|
22083
22400
|
description: "Inspect one Session's full effects \u2014 git content + actions/tags/ads, kickoff, commits."
|
|
@@ -22121,7 +22438,7 @@ registerSchema({
|
|
|
22121
22438
|
}
|
|
22122
22439
|
}
|
|
22123
22440
|
});
|
|
22124
|
-
var transcriptCommand =
|
|
22441
|
+
var transcriptCommand = defineCommand98({
|
|
22125
22442
|
meta: { name: "transcript", description: "Read another Session's conversation." },
|
|
22126
22443
|
args: {
|
|
22127
22444
|
id: { type: "positional", description: "The Session id", required: true },
|
|
@@ -22173,7 +22490,7 @@ registerSchema({
|
|
|
22173
22490
|
}
|
|
22174
22491
|
}
|
|
22175
22492
|
});
|
|
22176
|
-
var diffCommand =
|
|
22493
|
+
var diffCommand = defineCommand98({
|
|
22177
22494
|
meta: { name: "diff", description: "See a published Session's real file-level changes." },
|
|
22178
22495
|
args: {
|
|
22179
22496
|
id: { type: "positional", description: "The Session id", required: true },
|
|
@@ -22217,7 +22534,7 @@ var diffCommand = defineCommand96({
|
|
|
22217
22534
|
}
|
|
22218
22535
|
}
|
|
22219
22536
|
});
|
|
22220
|
-
var chatsCommand =
|
|
22537
|
+
var chatsCommand = defineCommand98({
|
|
22221
22538
|
meta: {
|
|
22222
22539
|
name: "chats",
|
|
22223
22540
|
description: `Inspect other Sessions on this account (read-only): list them, view what they produced, read their conversation, and see their real file changes \u2014 so you can reuse past work for a new goal.
|
|
@@ -22232,13 +22549,13 @@ Full guide: __tooling__/docs/tools/baker/chats.md`
|
|
|
22232
22549
|
});
|
|
22233
22550
|
|
|
22234
22551
|
// src/commands/creatives/index.ts
|
|
22235
|
-
import { defineCommand as
|
|
22552
|
+
import { defineCommand as defineCommand100 } from "citty";
|
|
22236
22553
|
|
|
22237
22554
|
// src/commands/creatives/publish.ts
|
|
22238
|
-
import { defineCommand as
|
|
22555
|
+
import { defineCommand as defineCommand99 } from "citty";
|
|
22239
22556
|
|
|
22240
22557
|
// src/commands/images/api.ts
|
|
22241
|
-
import { readFile as
|
|
22558
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
22242
22559
|
import { extname } from "path";
|
|
22243
22560
|
var imageProcessingTimeoutMs = 18e4;
|
|
22244
22561
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -22252,7 +22569,7 @@ var mimeMap = {
|
|
|
22252
22569
|
".avif": "image/avif"
|
|
22253
22570
|
};
|
|
22254
22571
|
var defaultImageApiDeps = {
|
|
22255
|
-
readFile:
|
|
22572
|
+
readFile: readFile18,
|
|
22256
22573
|
post: apiPost,
|
|
22257
22574
|
get: apiGet,
|
|
22258
22575
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -22379,7 +22696,7 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
|
|
|
22379
22696
|
chatId: chatIdFromEnv()
|
|
22380
22697
|
});
|
|
22381
22698
|
}
|
|
22382
|
-
var publishCommand =
|
|
22699
|
+
var publishCommand = defineCommand99({
|
|
22383
22700
|
meta: {
|
|
22384
22701
|
name: "publish",
|
|
22385
22702
|
description: "Publish a final static creative image to Baker Creatives and print the creative reference JSON."
|
|
@@ -22437,7 +22754,7 @@ var publishCommand = defineCommand97({
|
|
|
22437
22754
|
});
|
|
22438
22755
|
|
|
22439
22756
|
// src/commands/creatives/index.ts
|
|
22440
|
-
var creativesCommand3 =
|
|
22757
|
+
var creativesCommand3 = defineCommand100({
|
|
22441
22758
|
meta: {
|
|
22442
22759
|
name: "creatives",
|
|
22443
22760
|
description: `Publish static ad creatives as first-class Baker outputs.
|
|
@@ -22454,7 +22771,7 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
22454
22771
|
});
|
|
22455
22772
|
|
|
22456
22773
|
// src/commands/flows/index.ts
|
|
22457
|
-
import { defineCommand as
|
|
22774
|
+
import { defineCommand as defineCommand101 } from "citty";
|
|
22458
22775
|
|
|
22459
22776
|
// src/commands/flows/shared.ts
|
|
22460
22777
|
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
|
|
@@ -22487,12 +22804,12 @@ function listFlowSlugs() {
|
|
|
22487
22804
|
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
|
|
22488
22805
|
}
|
|
22489
22806
|
function readFlowTree(slug) {
|
|
22490
|
-
const
|
|
22491
|
-
if (!existsSync4(
|
|
22807
|
+
const path28 = join3(flowsDir(), slug, "_data.json");
|
|
22808
|
+
if (!existsSync4(path28)) {
|
|
22492
22809
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
22493
22810
|
}
|
|
22494
22811
|
try {
|
|
22495
|
-
return JSON.parse(readFileSync9(
|
|
22812
|
+
return JSON.parse(readFileSync9(path28, "utf-8"));
|
|
22496
22813
|
} catch (error) {
|
|
22497
22814
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
22498
22815
|
}
|
|
@@ -22609,7 +22926,7 @@ function displayName(slug) {
|
|
|
22609
22926
|
const name = tree.displayName;
|
|
22610
22927
|
return typeof name === "string" && name.trim() ? name.trim() : slug;
|
|
22611
22928
|
}
|
|
22612
|
-
var listCommand6 =
|
|
22929
|
+
var listCommand6 = defineCommand101({
|
|
22613
22930
|
meta: {
|
|
22614
22931
|
name: "list",
|
|
22615
22932
|
description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
|
|
@@ -22623,7 +22940,7 @@ var listCommand6 = defineCommand99({
|
|
|
22623
22940
|
writeJson({ ok: true, data: { flows } });
|
|
22624
22941
|
}
|
|
22625
22942
|
});
|
|
22626
|
-
var showCommand2 =
|
|
22943
|
+
var showCommand2 = defineCommand101({
|
|
22627
22944
|
meta: {
|
|
22628
22945
|
name: "show",
|
|
22629
22946
|
description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
|
|
@@ -22644,7 +22961,7 @@ var showCommand2 = defineCommand99({
|
|
|
22644
22961
|
writeJson({ ok: true, data: response });
|
|
22645
22962
|
}
|
|
22646
22963
|
});
|
|
22647
|
-
var flowsCommand =
|
|
22964
|
+
var flowsCommand = defineCommand101({
|
|
22648
22965
|
meta: {
|
|
22649
22966
|
name: "flows",
|
|
22650
22967
|
description: `Read this workspace's Forms (flows) and the configuration status of their confidential fields \u2014 connection secrets, OAuth connections, and third-party field definitions (HubSpot, Calendly, HighLevel, SavvyCal).
|
|
@@ -22672,10 +22989,10 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
|
|
|
22672
22989
|
});
|
|
22673
22990
|
|
|
22674
22991
|
// src/commands/ga4/index.ts
|
|
22675
|
-
import { defineCommand as
|
|
22992
|
+
import { defineCommand as defineCommand105 } from "citty";
|
|
22676
22993
|
|
|
22677
22994
|
// src/commands/ga4/audit.ts
|
|
22678
|
-
import { defineCommand as
|
|
22995
|
+
import { defineCommand as defineCommand102 } from "citty";
|
|
22679
22996
|
|
|
22680
22997
|
// src/commands/ga4/resolve.ts
|
|
22681
22998
|
async function fetchProperties(useCache = true) {
|
|
@@ -22738,7 +23055,7 @@ registerSchema({
|
|
|
22738
23055
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
22739
23056
|
}
|
|
22740
23057
|
});
|
|
22741
|
-
var auditCommand2 =
|
|
23058
|
+
var auditCommand2 = defineCommand102({
|
|
22742
23059
|
meta: {
|
|
22743
23060
|
name: "audit",
|
|
22744
23061
|
description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
|
|
@@ -22790,7 +23107,7 @@ Examples:
|
|
|
22790
23107
|
});
|
|
22791
23108
|
|
|
22792
23109
|
// src/commands/ga4/properties.ts
|
|
22793
|
-
import { defineCommand as
|
|
23110
|
+
import { defineCommand as defineCommand103 } from "citty";
|
|
22794
23111
|
registerSchema({
|
|
22795
23112
|
command: "ga4.properties",
|
|
22796
23113
|
description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
|
|
@@ -22798,7 +23115,7 @@ registerSchema({
|
|
|
22798
23115
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
22799
23116
|
}
|
|
22800
23117
|
});
|
|
22801
|
-
var propertiesCommand =
|
|
23118
|
+
var propertiesCommand = defineCommand103({
|
|
22802
23119
|
meta: {
|
|
22803
23120
|
name: "properties",
|
|
22804
23121
|
description: `List accessible GA4 properties.
|
|
@@ -22848,7 +23165,7 @@ Examples:
|
|
|
22848
23165
|
// src/commands/ga4/query.ts
|
|
22849
23166
|
import { appendFileSync as appendFileSync2, existsSync as existsSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
|
|
22850
23167
|
import { resolve as resolve2 } from "path";
|
|
22851
|
-
import { defineCommand as
|
|
23168
|
+
import { defineCommand as defineCommand104 } from "citty";
|
|
22852
23169
|
|
|
22853
23170
|
// src/commands/ga4/presets.ts
|
|
22854
23171
|
var GA4_PRESETS = [
|
|
@@ -22980,7 +23297,7 @@ function handleError(err) {
|
|
|
22980
23297
|
});
|
|
22981
23298
|
process.exit(1);
|
|
22982
23299
|
}
|
|
22983
|
-
var queryCommand2 =
|
|
23300
|
+
var queryCommand2 = defineCommand104({
|
|
22984
23301
|
meta: {
|
|
22985
23302
|
name: "query",
|
|
22986
23303
|
description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
|
|
@@ -23051,7 +23368,7 @@ Free-form (escape hatch):
|
|
|
23051
23368
|
});
|
|
23052
23369
|
|
|
23053
23370
|
// src/commands/ga4/index.ts
|
|
23054
|
-
var ga4Command =
|
|
23371
|
+
var ga4Command = defineCommand105({
|
|
23055
23372
|
meta: {
|
|
23056
23373
|
name: "ga4",
|
|
23057
23374
|
description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
|
|
@@ -23075,12 +23392,12 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
|
|
|
23075
23392
|
});
|
|
23076
23393
|
|
|
23077
23394
|
// src/commands/gsc/index.ts
|
|
23078
|
-
import { defineCommand as
|
|
23395
|
+
import { defineCommand as defineCommand109 } from "citty";
|
|
23079
23396
|
|
|
23080
23397
|
// src/commands/gsc/query.ts
|
|
23081
23398
|
import { appendFileSync as appendFileSync3, existsSync as existsSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
|
|
23082
23399
|
import { resolve as resolve3 } from "path";
|
|
23083
|
-
import { defineCommand as
|
|
23400
|
+
import { defineCommand as defineCommand106 } from "citty";
|
|
23084
23401
|
|
|
23085
23402
|
// src/commands/gsc/presets.ts
|
|
23086
23403
|
var GSC_PRESETS = [
|
|
@@ -23268,7 +23585,7 @@ function handleError2(err) {
|
|
|
23268
23585
|
});
|
|
23269
23586
|
process.exit(1);
|
|
23270
23587
|
}
|
|
23271
|
-
var queryCommand3 =
|
|
23588
|
+
var queryCommand3 = defineCommand106({
|
|
23272
23589
|
meta: {
|
|
23273
23590
|
name: "query",
|
|
23274
23591
|
description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
|
|
@@ -23346,7 +23663,7 @@ Free-form (escape hatch):
|
|
|
23346
23663
|
});
|
|
23347
23664
|
|
|
23348
23665
|
// src/commands/gsc/sitemaps.ts
|
|
23349
|
-
import { defineCommand as
|
|
23666
|
+
import { defineCommand as defineCommand107 } from "citty";
|
|
23350
23667
|
registerSchema({
|
|
23351
23668
|
command: "gsc.sitemaps",
|
|
23352
23669
|
description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
|
|
@@ -23355,7 +23672,7 @@ registerSchema({
|
|
|
23355
23672
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
23356
23673
|
}
|
|
23357
23674
|
});
|
|
23358
|
-
var sitemapsCommand =
|
|
23675
|
+
var sitemapsCommand = defineCommand107({
|
|
23359
23676
|
meta: {
|
|
23360
23677
|
name: "sitemaps",
|
|
23361
23678
|
description: `List sitemaps for a site. Check health and errors.
|
|
@@ -23405,7 +23722,7 @@ Examples:
|
|
|
23405
23722
|
});
|
|
23406
23723
|
|
|
23407
23724
|
// src/commands/gsc/sites.ts
|
|
23408
|
-
import { defineCommand as
|
|
23725
|
+
import { defineCommand as defineCommand108 } from "citty";
|
|
23409
23726
|
registerSchema({
|
|
23410
23727
|
command: "gsc.sites",
|
|
23411
23728
|
description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
|
|
@@ -23413,7 +23730,7 @@ registerSchema({
|
|
|
23413
23730
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
23414
23731
|
}
|
|
23415
23732
|
});
|
|
23416
|
-
var sitesCommand =
|
|
23733
|
+
var sitesCommand = defineCommand108({
|
|
23417
23734
|
meta: {
|
|
23418
23735
|
name: "sites",
|
|
23419
23736
|
description: `List verified Search Console sites.
|
|
@@ -23461,7 +23778,7 @@ Examples:
|
|
|
23461
23778
|
});
|
|
23462
23779
|
|
|
23463
23780
|
// src/commands/gsc/index.ts
|
|
23464
|
-
var gscCommand =
|
|
23781
|
+
var gscCommand = defineCommand109({
|
|
23465
23782
|
meta: {
|
|
23466
23783
|
name: "gsc",
|
|
23467
23784
|
description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
|
|
@@ -23485,7 +23802,7 @@ Full guide: __tooling__/docs/tools/baker/gsc.md`
|
|
|
23485
23802
|
});
|
|
23486
23803
|
|
|
23487
23804
|
// src/commands/history/index.ts
|
|
23488
|
-
import { defineCommand as
|
|
23805
|
+
import { defineCommand as defineCommand110 } from "citty";
|
|
23489
23806
|
registerSchema({
|
|
23490
23807
|
command: "history.list",
|
|
23491
23808
|
description: "Start here: unified account history (audit log) \u2014 everything that changed on this account, newest first: publishes, chat lifecycle, backlog actions, team changes, setup links, tags, schedules, ad-platform writes, followed advertisers, media, creatives, reports, domains, and integrations. Use it to see what happened recently before planning work. Compact by default; add --full for raw metadata per entry.",
|
|
@@ -23540,7 +23857,7 @@ function parseBoundedInt2(raw, name, min, max) {
|
|
|
23540
23857
|
}
|
|
23541
23858
|
return value;
|
|
23542
23859
|
}
|
|
23543
|
-
var listCommand7 =
|
|
23860
|
+
var listCommand7 = defineCommand110({
|
|
23544
23861
|
meta: {
|
|
23545
23862
|
name: "list",
|
|
23546
23863
|
description: "List recent account changes (unified audit log), newest first."
|
|
@@ -23586,7 +23903,7 @@ var listCommand7 = defineCommand108({
|
|
|
23586
23903
|
}
|
|
23587
23904
|
}
|
|
23588
23905
|
});
|
|
23589
|
-
var historyCommand =
|
|
23906
|
+
var historyCommand = defineCommand110({
|
|
23590
23907
|
meta: {
|
|
23591
23908
|
name: "history",
|
|
23592
23909
|
description: `Unified account history (audit log): what changed, who did it, and when.
|
|
@@ -23596,10 +23913,10 @@ Full guide: __tooling__/docs/tools/baker/history.md`
|
|
|
23596
23913
|
});
|
|
23597
23914
|
|
|
23598
23915
|
// src/commands/images/index.ts
|
|
23599
|
-
import { defineCommand as
|
|
23916
|
+
import { defineCommand as defineCommand134 } from "citty";
|
|
23600
23917
|
|
|
23601
23918
|
// src/commands/images/crop.ts
|
|
23602
|
-
import { defineCommand as
|
|
23919
|
+
import { defineCommand as defineCommand111 } from "citty";
|
|
23603
23920
|
|
|
23604
23921
|
// src/lib/image/crop-sprite.ts
|
|
23605
23922
|
import sharp from "sharp";
|
|
@@ -23614,7 +23931,7 @@ function cropSprite(input, region) {
|
|
|
23614
23931
|
|
|
23615
23932
|
// src/lib/image/io.ts
|
|
23616
23933
|
import { randomBytes } from "crypto";
|
|
23617
|
-
import { glob as fsGlob, readFile as
|
|
23934
|
+
import { glob as fsGlob, readFile as readFile19, rename, stat as stat4, writeFile as writeFile10 } from "fs/promises";
|
|
23618
23935
|
import { dirname as dirname2, extname as extname2, join as join4, resolve as resolve4 } from "path";
|
|
23619
23936
|
var REMOTE_RE = /^https?:\/\//i;
|
|
23620
23937
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -23650,11 +23967,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
23650
23967
|
}
|
|
23651
23968
|
return Buffer.from(await response.arrayBuffer());
|
|
23652
23969
|
}
|
|
23653
|
-
return
|
|
23970
|
+
return readFile19(pathOrUrl);
|
|
23654
23971
|
}
|
|
23655
|
-
async function isDirectory(
|
|
23972
|
+
async function isDirectory(path28) {
|
|
23656
23973
|
try {
|
|
23657
|
-
const s = await stat4(
|
|
23974
|
+
const s = await stat4(path28);
|
|
23658
23975
|
return s.isDirectory();
|
|
23659
23976
|
} catch {
|
|
23660
23977
|
return false;
|
|
@@ -23673,7 +23990,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
23673
23990
|
const absolute = resolve4(targetPath);
|
|
23674
23991
|
const dir = dirname2(absolute);
|
|
23675
23992
|
const tmp = join4(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
23676
|
-
await
|
|
23993
|
+
await writeFile10(tmp, data);
|
|
23677
23994
|
await rename(tmp, absolute);
|
|
23678
23995
|
}
|
|
23679
23996
|
|
|
@@ -23724,7 +24041,7 @@ function emitError2(err) {
|
|
|
23724
24041
|
}
|
|
23725
24042
|
process.exit(1);
|
|
23726
24043
|
}
|
|
23727
|
-
var cropCommand =
|
|
24044
|
+
var cropCommand = defineCommand111({
|
|
23728
24045
|
meta: {
|
|
23729
24046
|
name: "crop",
|
|
23730
24047
|
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"
|
|
@@ -23760,7 +24077,7 @@ var cropCommand = defineCommand109({
|
|
|
23760
24077
|
});
|
|
23761
24078
|
|
|
23762
24079
|
// src/commands/images/delete.ts
|
|
23763
|
-
import { defineCommand as
|
|
24080
|
+
import { defineCommand as defineCommand112 } from "citty";
|
|
23764
24081
|
registerSchema({
|
|
23765
24082
|
command: "images.delete",
|
|
23766
24083
|
description: "Delete an image by ID",
|
|
@@ -23774,7 +24091,7 @@ registerSchema({
|
|
|
23774
24091
|
}
|
|
23775
24092
|
}
|
|
23776
24093
|
});
|
|
23777
|
-
var deleteCommand =
|
|
24094
|
+
var deleteCommand = defineCommand112({
|
|
23778
24095
|
meta: {
|
|
23779
24096
|
name: "delete",
|
|
23780
24097
|
description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
|
|
@@ -23815,7 +24132,7 @@ var deleteCommand = defineCommand110({
|
|
|
23815
24132
|
});
|
|
23816
24133
|
|
|
23817
24134
|
// src/commands/images/dimensions.ts
|
|
23818
|
-
import { defineCommand as
|
|
24135
|
+
import { defineCommand as defineCommand113 } from "citty";
|
|
23819
24136
|
|
|
23820
24137
|
// src/lib/image/dimensions.ts
|
|
23821
24138
|
import { imageSize } from "image-size";
|
|
@@ -23838,7 +24155,7 @@ registerSchema({
|
|
|
23838
24155
|
target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
|
|
23839
24156
|
}
|
|
23840
24157
|
});
|
|
23841
|
-
var dimensionsCommand =
|
|
24158
|
+
var dimensionsCommand = defineCommand113({
|
|
23842
24159
|
meta: {
|
|
23843
24160
|
name: "dimensions",
|
|
23844
24161
|
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"
|
|
@@ -23882,7 +24199,7 @@ var dimensionsCommand = defineCommand111({
|
|
|
23882
24199
|
});
|
|
23883
24200
|
|
|
23884
24201
|
// src/commands/images/extract.ts
|
|
23885
|
-
import { defineCommand as
|
|
24202
|
+
import { defineCommand as defineCommand114 } from "citty";
|
|
23886
24203
|
registerSchema({
|
|
23887
24204
|
command: "images.extract",
|
|
23888
24205
|
description: "Extract images from a URL via Firecrawl (formats: images).",
|
|
@@ -23898,7 +24215,7 @@ registerSchema({
|
|
|
23898
24215
|
}
|
|
23899
24216
|
}
|
|
23900
24217
|
});
|
|
23901
|
-
var extractCommand =
|
|
24218
|
+
var extractCommand = defineCommand114({
|
|
23902
24219
|
meta: {
|
|
23903
24220
|
name: "extract",
|
|
23904
24221
|
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"
|
|
@@ -23936,7 +24253,7 @@ var extractCommand = defineCommand112({
|
|
|
23936
24253
|
});
|
|
23937
24254
|
|
|
23938
24255
|
// src/commands/images/find.ts
|
|
23939
|
-
import { defineCommand as
|
|
24256
|
+
import { defineCommand as defineCommand115 } from "citty";
|
|
23940
24257
|
registerSchema({
|
|
23941
24258
|
command: "images.find",
|
|
23942
24259
|
description: "Fanout image search: library first, then opted-in external providers.",
|
|
@@ -23968,7 +24285,7 @@ registerSchema({
|
|
|
23968
24285
|
}
|
|
23969
24286
|
}
|
|
23970
24287
|
});
|
|
23971
|
-
var findCommand =
|
|
24288
|
+
var findCommand = defineCommand115({
|
|
23972
24289
|
meta: {
|
|
23973
24290
|
name: "find",
|
|
23974
24291
|
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"
|
|
@@ -24016,8 +24333,8 @@ var findCommand = defineCommand113({
|
|
|
24016
24333
|
});
|
|
24017
24334
|
|
|
24018
24335
|
// src/commands/images/generate.ts
|
|
24019
|
-
import { readFile as
|
|
24020
|
-
import { defineCommand as
|
|
24336
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
24337
|
+
import { defineCommand as defineCommand116 } from "citty";
|
|
24021
24338
|
import sharp2 from "sharp";
|
|
24022
24339
|
var GENERATE_TIMEOUT_MS = 18e4;
|
|
24023
24340
|
var REFERENCE_MAX_EDGE = 1536;
|
|
@@ -24106,7 +24423,7 @@ async function resolveReferences(spec) {
|
|
|
24106
24423
|
}
|
|
24107
24424
|
let raw;
|
|
24108
24425
|
try {
|
|
24109
|
-
raw = await
|
|
24426
|
+
raw = await readFile20(entry);
|
|
24110
24427
|
} catch {
|
|
24111
24428
|
throw new ApiError("VALIDATION_ERROR", `Reference file not found: ${entry}`);
|
|
24112
24429
|
}
|
|
@@ -24120,10 +24437,10 @@ async function resolveReferences(spec) {
|
|
|
24120
24437
|
}
|
|
24121
24438
|
return out;
|
|
24122
24439
|
}
|
|
24123
|
-
var generateCommand =
|
|
24440
|
+
var generateCommand = defineCommand116({
|
|
24124
24441
|
meta: {
|
|
24125
24442
|
name: "generate",
|
|
24126
|
-
description: "Generate an image with AI and store it in the library (cost-tracked per request via OpenRouter usage). Models mirror the canvas: google/gemini-3.1-flash-image-preview (Nano Banana flash \u2014 default, fast, extreme aspect ratios)
|
|
24443
|
+
description: "Generate an image with AI and store it in the library (cost-tracked per request via OpenRouter usage). Models mirror the canvas: google/gemini-3.1-flash-image-preview (Nano Banana flash \u2014 default, fast, extreme aspect ratios), google/gemini-3-pro-image-preview (Nano Banana Pro \u2014 highest fidelity), openai/gpt-image-2 (photoreal, cleanest in-image text, best for ad/landing reproduction \u2014 no --image-size, and no 4:5 / 5:4), 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 openai/gpt-image-2 --aspect-ratio 3:2\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]]'"
|
|
24127
24444
|
},
|
|
24128
24445
|
args: {
|
|
24129
24446
|
prompt: { type: "positional", description: "What to generate", required: false },
|
|
@@ -24172,7 +24489,7 @@ var generateCommand = defineCommand114({
|
|
|
24172
24489
|
});
|
|
24173
24490
|
|
|
24174
24491
|
// src/commands/images/get.ts
|
|
24175
|
-
import { defineCommand as
|
|
24492
|
+
import { defineCommand as defineCommand117 } from "citty";
|
|
24176
24493
|
registerSchema({
|
|
24177
24494
|
command: "images.get",
|
|
24178
24495
|
description: "Get a single image by ID",
|
|
@@ -24180,7 +24497,7 @@ registerSchema({
|
|
|
24180
24497
|
id: { type: "string", description: "Image ID", required: true }
|
|
24181
24498
|
}
|
|
24182
24499
|
});
|
|
24183
|
-
var getCommand2 =
|
|
24500
|
+
var getCommand2 = defineCommand117({
|
|
24184
24501
|
meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
|
|
24185
24502
|
args: {
|
|
24186
24503
|
id: { type: "positional", description: "Image ID", required: false },
|
|
@@ -24216,7 +24533,7 @@ var getCommand2 = defineCommand115({
|
|
|
24216
24533
|
});
|
|
24217
24534
|
|
|
24218
24535
|
// src/commands/images/gif.ts
|
|
24219
|
-
import { defineCommand as
|
|
24536
|
+
import { defineCommand as defineCommand118 } from "citty";
|
|
24220
24537
|
registerSchema({
|
|
24221
24538
|
command: "images.gif",
|
|
24222
24539
|
description: "Search Giphy for GIFs / reaction memes (paid social creative).",
|
|
@@ -24248,7 +24565,7 @@ registerSchema({
|
|
|
24248
24565
|
}
|
|
24249
24566
|
}
|
|
24250
24567
|
});
|
|
24251
|
-
var gifCommand =
|
|
24568
|
+
var gifCommand = defineCommand118({
|
|
24252
24569
|
meta: {
|
|
24253
24570
|
name: "gif",
|
|
24254
24571
|
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"
|
|
@@ -24295,7 +24612,35 @@ var gifCommand = defineCommand116({
|
|
|
24295
24612
|
});
|
|
24296
24613
|
|
|
24297
24614
|
// src/commands/images/google.ts
|
|
24298
|
-
import { defineCommand as
|
|
24615
|
+
import { defineCommand as defineCommand119 } from "citty";
|
|
24616
|
+
|
|
24617
|
+
// src/commands/images/searchHints.ts
|
|
24618
|
+
var FALLBACK = {
|
|
24619
|
+
stock: "Still empty \u2192 `baker images find <query> --sources library,pinterest,google` (one call across the providers stock does not cover \u2014 `--sources` is required, `find` alone searches the library only) or `baker images generate` to make the asset. Do not re-run this search with reshuffled flags.",
|
|
24620
|
+
google: "Still empty \u2192 `baker images generate` to make the asset. Google is the last-resort provider; there is nothing below it to retry."
|
|
24621
|
+
};
|
|
24622
|
+
function emptyResultHints({ provider, hitCount, activeFilters }) {
|
|
24623
|
+
if (hitCount > 0) {
|
|
24624
|
+
return [];
|
|
24625
|
+
}
|
|
24626
|
+
const hints = [];
|
|
24627
|
+
if (activeFilters.length > 0) {
|
|
24628
|
+
hints.push(
|
|
24629
|
+
`No hits. Drop the filters before touching the query \u2014 ${activeFilters.join(", ")} narrowed this search and filter combinations are the usual cause of an empty result.`
|
|
24630
|
+
);
|
|
24631
|
+
}
|
|
24632
|
+
hints.push(FALLBACK[provider]);
|
|
24633
|
+
return hints;
|
|
24634
|
+
}
|
|
24635
|
+
function activeFilterFlags(args, candidates) {
|
|
24636
|
+
return candidates.filter((flag) => args[flag] !== void 0 && args[flag] !== "").map((flag) => `--${flag}`);
|
|
24637
|
+
}
|
|
24638
|
+
|
|
24639
|
+
// src/commands/images/google.ts
|
|
24640
|
+
var GOOGLE_ERROR_FIX = {
|
|
24641
|
+
action: "use_different_resource",
|
|
24642
|
+
explanation: "Generate the asset instead of retrying Google. Google is the last-resort image provider. Run `baker images generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
|
|
24643
|
+
};
|
|
24299
24644
|
registerSchema({
|
|
24300
24645
|
command: "images.google",
|
|
24301
24646
|
description: "Google Images search via the official Custom Search JSON API. Unverified source \u2014 inspect before placing.",
|
|
@@ -24331,7 +24676,7 @@ registerSchema({
|
|
|
24331
24676
|
}
|
|
24332
24677
|
}
|
|
24333
24678
|
});
|
|
24334
|
-
var googleCommand2 =
|
|
24679
|
+
var googleCommand2 = defineCommand119({
|
|
24335
24680
|
meta: {
|
|
24336
24681
|
name: "google",
|
|
24337
24682
|
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"
|
|
@@ -24366,10 +24711,18 @@ var googleCommand2 = defineCommand117({
|
|
|
24366
24711
|
if (args["auto-ingest"]) body.autoIngest = Number(args["auto-ingest"]);
|
|
24367
24712
|
if (args.context) body.descriptionContext = args.context;
|
|
24368
24713
|
const data = await apiPost("/api/images/google", body);
|
|
24369
|
-
|
|
24714
|
+
const hints = emptyResultHints({
|
|
24715
|
+
provider: "google",
|
|
24716
|
+
hitCount: data.hits.length,
|
|
24717
|
+
activeFilters: activeFilterFlags(args, ["type", "size", "color", "safe"])
|
|
24718
|
+
});
|
|
24719
|
+
writeJson({ ok: true, data, ...hints.length ? { hints } : {} });
|
|
24370
24720
|
} catch (err) {
|
|
24371
24721
|
if (err instanceof ApiError) {
|
|
24372
|
-
writeJson({
|
|
24722
|
+
writeJson({
|
|
24723
|
+
ok: false,
|
|
24724
|
+
error: { code: err.code, message: err.message, fix: GOOGLE_ERROR_FIX }
|
|
24725
|
+
});
|
|
24373
24726
|
process.exit(1);
|
|
24374
24727
|
}
|
|
24375
24728
|
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
@@ -24379,7 +24732,7 @@ var googleCommand2 = defineCommand117({
|
|
|
24379
24732
|
});
|
|
24380
24733
|
|
|
24381
24734
|
// src/commands/images/icon.ts
|
|
24382
|
-
import { defineCommand as
|
|
24735
|
+
import { defineCommand as defineCommand120 } from "citty";
|
|
24383
24736
|
registerSchema({
|
|
24384
24737
|
command: "images.icon",
|
|
24385
24738
|
description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
|
|
@@ -24405,7 +24758,7 @@ registerSchema({
|
|
|
24405
24758
|
}
|
|
24406
24759
|
}
|
|
24407
24760
|
});
|
|
24408
|
-
var iconCommand =
|
|
24761
|
+
var iconCommand = defineCommand120({
|
|
24409
24762
|
meta: {
|
|
24410
24763
|
name: "icon",
|
|
24411
24764
|
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'"
|
|
@@ -24445,7 +24798,7 @@ var iconCommand = defineCommand118({
|
|
|
24445
24798
|
});
|
|
24446
24799
|
|
|
24447
24800
|
// src/commands/images/ingest.ts
|
|
24448
|
-
import { defineCommand as
|
|
24801
|
+
import { defineCommand as defineCommand121 } from "citty";
|
|
24449
24802
|
registerSchema({
|
|
24450
24803
|
command: "images.ingest",
|
|
24451
24804
|
description: "Ingest a remote image URL into the library (full describe + embed).",
|
|
@@ -24457,7 +24810,7 @@ registerSchema({
|
|
|
24457
24810
|
context: { type: "string", description: "Description context hint", required: false }
|
|
24458
24811
|
}
|
|
24459
24812
|
});
|
|
24460
|
-
var ingestCommand =
|
|
24813
|
+
var ingestCommand = defineCommand121({
|
|
24461
24814
|
meta: {
|
|
24462
24815
|
name: "ingest",
|
|
24463
24816
|
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"
|
|
@@ -24499,32 +24852,7 @@ var ingestCommand = defineCommand119({
|
|
|
24499
24852
|
});
|
|
24500
24853
|
|
|
24501
24854
|
// src/commands/images/library.ts
|
|
24502
|
-
import { defineCommand as
|
|
24503
|
-
|
|
24504
|
-
// src/commands/images/logoHints.ts
|
|
24505
|
-
var LOGO_QUERY_RE = /\blogos?\b|\bwordmark\b|\bbrand mark\b/i;
|
|
24506
|
-
function withLogoShape(row) {
|
|
24507
|
-
return {
|
|
24508
|
-
...row,
|
|
24509
|
-
logoShape: classifyLogoShape({
|
|
24510
|
-
aspectRatio: row.aspectRatio,
|
|
24511
|
-
hasAlpha: row.hasAlpha,
|
|
24512
|
-
solidity: row.solidity
|
|
24513
|
-
})
|
|
24514
|
-
};
|
|
24515
|
-
}
|
|
24516
|
-
function buildLogoLibraryHints(query, rows) {
|
|
24517
|
-
if (!LOGO_QUERY_RE.test(query)) return [];
|
|
24518
|
-
const plates = rows.filter((row) => row.logoShape === "plated-icon");
|
|
24519
|
-
if (plates.length === 0) return [];
|
|
24520
|
-
const names = plates.map((row) => row.name).filter((name) => Boolean(name)).slice(0, 4);
|
|
24521
|
-
const subject = names.length > 0 ? names.join(", ") : `${plates.length} result(s)`;
|
|
24522
|
-
return [
|
|
24523
|
-
`PLATED ICON: ${subject} \u2014 solid tile with the mark knocked out, not a wordmark. In a logo strip it renders as a filled box and verify blocks it (logo-plate). Re-source with 'baker images logo <domain> --variant logo', or strip the plate with 'baker images normalize <file> --remove-bg --shrink-to-content'. Only use a plate in slots under ~32px.`
|
|
24524
|
-
];
|
|
24525
|
-
}
|
|
24526
|
-
|
|
24527
|
-
// src/commands/images/library.ts
|
|
24855
|
+
import { defineCommand as defineCommand122 } from "citty";
|
|
24528
24856
|
registerSchema({
|
|
24529
24857
|
command: "images.library",
|
|
24530
24858
|
description: "Search the company image library. Returns only ready images.",
|
|
@@ -24550,7 +24878,7 @@ registerSchema({
|
|
|
24550
24878
|
}
|
|
24551
24879
|
}
|
|
24552
24880
|
});
|
|
24553
|
-
var libraryCommand =
|
|
24881
|
+
var libraryCommand = defineCommand122({
|
|
24554
24882
|
meta: {
|
|
24555
24883
|
name: "library",
|
|
24556
24884
|
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"
|
|
@@ -24589,10 +24917,8 @@ var libraryCommand = defineCommand120({
|
|
|
24589
24917
|
if (minScore !== void 0) {
|
|
24590
24918
|
data = data.filter((r) => typeof r.score === "number" && r.score >= minScore);
|
|
24591
24919
|
}
|
|
24592
|
-
const shaped = data.map(withLogoShape);
|
|
24593
|
-
const hints = buildLogoLibraryHints(query, shaped);
|
|
24594
24920
|
writeOutput(
|
|
24595
|
-
{ ok: true, data
|
|
24921
|
+
{ ok: true, data },
|
|
24596
24922
|
args.output || "json",
|
|
24597
24923
|
args.fields ? args.fields.split(",") : void 0,
|
|
24598
24924
|
args.full
|
|
@@ -24609,17 +24935,12 @@ var libraryCommand = defineCommand120({
|
|
|
24609
24935
|
});
|
|
24610
24936
|
|
|
24611
24937
|
// src/commands/images/logo.ts
|
|
24612
|
-
import { defineCommand as
|
|
24613
|
-
var MAX_DOMAINS = 20;
|
|
24938
|
+
import { defineCommand as defineCommand123 } from "citty";
|
|
24614
24939
|
registerSchema({
|
|
24615
24940
|
command: "images.logo",
|
|
24616
24941
|
description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
|
|
24617
24942
|
args: {
|
|
24618
|
-
domain: {
|
|
24619
|
-
type: "string",
|
|
24620
|
-
description: "Brand domain, or a comma-separated list for a whole strip (e.g. stripe.com,intercom.com)",
|
|
24621
|
-
required: true
|
|
24622
|
-
},
|
|
24943
|
+
domain: { type: "string", description: "Brand domain (e.g. stripe.com)", required: true },
|
|
24623
24944
|
variant: { type: "string", description: "icon | logo | symbol", required: false },
|
|
24624
24945
|
"auto-ingest": {
|
|
24625
24946
|
type: "number",
|
|
@@ -24639,13 +24960,13 @@ registerSchema({
|
|
|
24639
24960
|
}
|
|
24640
24961
|
}
|
|
24641
24962
|
});
|
|
24642
|
-
var logoCommand =
|
|
24963
|
+
var logoCommand = defineCommand123({
|
|
24643
24964
|
meta: {
|
|
24644
24965
|
name: "logo",
|
|
24645
|
-
description: "Brand logo via Brandfetch CDN. Returns up to 5 variants (icon, light/dark logo, light/dark symbol). Auto-ingests the first variant.\n\
|
|
24966
|
+
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"
|
|
24646
24967
|
},
|
|
24647
24968
|
args: {
|
|
24648
|
-
domain: { type: "positional", description: "Brand domain
|
|
24969
|
+
domain: { type: "positional", description: "Brand domain", required: false },
|
|
24649
24970
|
variant: { type: "string", description: "icon|logo|symbol", required: false },
|
|
24650
24971
|
"auto-ingest": { type: "string", description: "Ingest top N (0-20, default 1)", required: false },
|
|
24651
24972
|
"no-auto-ingest": { type: "boolean", description: "Skip auto-ingest", required: false },
|
|
@@ -24653,50 +24974,18 @@ var logoCommand = defineCommand121({
|
|
|
24653
24974
|
},
|
|
24654
24975
|
run: async ({ args }) => {
|
|
24655
24976
|
try {
|
|
24656
|
-
const
|
|
24657
|
-
|
|
24658
|
-
...new Set(
|
|
24659
|
-
(raw ?? "").split(",").map((d) => d.trim()).filter(Boolean)
|
|
24660
|
-
)
|
|
24661
|
-
];
|
|
24662
|
-
if (domains.length === 0) {
|
|
24977
|
+
const domain = args.domain;
|
|
24978
|
+
if (!domain) {
|
|
24663
24979
|
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Domain is required" } });
|
|
24664
24980
|
process.exit(1);
|
|
24665
24981
|
}
|
|
24666
|
-
|
|
24667
|
-
|
|
24668
|
-
|
|
24669
|
-
|
|
24670
|
-
|
|
24671
|
-
|
|
24672
|
-
|
|
24673
|
-
});
|
|
24674
|
-
process.exit(1);
|
|
24675
|
-
}
|
|
24676
|
-
const base = {};
|
|
24677
|
-
if (args.variant) base.variant = args.variant;
|
|
24678
|
-
if (args["auto-ingest"] !== void 0) base.autoIngest = Number(args["auto-ingest"]);
|
|
24679
|
-
else if (args["no-auto-ingest"]) base.autoIngest = 0;
|
|
24680
|
-
if (args.context) base.descriptionContext = args.context;
|
|
24681
|
-
const fetchOne = (domain) => apiPost("/api/images/logo", { ...base, domain });
|
|
24682
|
-
if (domains.length === 1) {
|
|
24683
|
-
writeJson({ ok: true, data: await fetchOne(domains[0]) });
|
|
24684
|
-
return;
|
|
24685
|
-
}
|
|
24686
|
-
const settled = await Promise.all(
|
|
24687
|
-
domains.map(async (domain) => {
|
|
24688
|
-
try {
|
|
24689
|
-
return { domain, ...await fetchOne(domain) };
|
|
24690
|
-
} catch (err) {
|
|
24691
|
-
return { domain, hits: [], error: err instanceof ApiError ? err.message : "Lookup failed" };
|
|
24692
|
-
}
|
|
24693
|
-
})
|
|
24694
|
-
);
|
|
24695
|
-
const empty = settled.filter((r) => r.hits.length === 0).map((r) => r.domain);
|
|
24696
|
-
const hints = empty.length > 0 ? [
|
|
24697
|
-
`NO LOGO: ${empty.join(", ")} \u2014 Brandfetch doesn't know ${empty.length === 1 ? "this brand" : "these brands"}. Fall back per domain: 'baker images extract <domain> --auto-ingest 5', then 'baker images icon <brand> --set simple-icons'. Still nothing \u2192 'baker actions create' to acquire the file. Do not render the name as text.`
|
|
24698
|
-
] : [];
|
|
24699
|
-
writeJson({ ok: true, data: { results: settled }, ...hints.length > 0 ? { hints } : {} });
|
|
24982
|
+
const body = { domain };
|
|
24983
|
+
if (args.variant) body.variant = args.variant;
|
|
24984
|
+
if (args["auto-ingest"] !== void 0) body.autoIngest = Number(args["auto-ingest"]);
|
|
24985
|
+
else if (args["no-auto-ingest"]) body.autoIngest = 0;
|
|
24986
|
+
if (args.context) body.descriptionContext = args.context;
|
|
24987
|
+
const data = await apiPost("/api/images/logo", body);
|
|
24988
|
+
writeJson({ ok: true, data });
|
|
24700
24989
|
} catch (err) {
|
|
24701
24990
|
if (err instanceof ApiError) {
|
|
24702
24991
|
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
@@ -24709,7 +24998,7 @@ var logoCommand = defineCommand121({
|
|
|
24709
24998
|
});
|
|
24710
24999
|
|
|
24711
25000
|
// src/commands/images/normalize.ts
|
|
24712
|
-
import { defineCommand as
|
|
25001
|
+
import { defineCommand as defineCommand124 } from "citty";
|
|
24713
25002
|
|
|
24714
25003
|
// src/lib/image/color-changer.ts
|
|
24715
25004
|
import quantize from "quantize";
|
|
@@ -24740,7 +25029,6 @@ function getDominantEdgeColor(data, width, height) {
|
|
|
24740
25029
|
const colorCount = {};
|
|
24741
25030
|
function accumulateColor(i, j) {
|
|
24742
25031
|
const idx = (i * width + j) * 4;
|
|
24743
|
-
if ((data[idx + 3] ?? 0) < 10) return;
|
|
24744
25032
|
const colorKey = `${data[idx]},${data[idx + 1]},${data[idx + 2]}`;
|
|
24745
25033
|
colorCount[colorKey] = (colorCount[colorKey] ?? 0) + 1;
|
|
24746
25034
|
}
|
|
@@ -24753,7 +25041,7 @@ function getDominantEdgeColor(data, width, height) {
|
|
|
24753
25041
|
accumulateColor(i, width - 1);
|
|
24754
25042
|
}
|
|
24755
25043
|
let maxCount = 0;
|
|
24756
|
-
let dominantColor =
|
|
25044
|
+
let dominantColor = { r: 0, g: 0, b: 0 };
|
|
24757
25045
|
for (const key in colorCount) {
|
|
24758
25046
|
const count = colorCount[key];
|
|
24759
25047
|
if (count > maxCount) {
|
|
@@ -24764,27 +25052,6 @@ function getDominantEdgeColor(data, width, height) {
|
|
|
24764
25052
|
}
|
|
24765
25053
|
return dominantColor;
|
|
24766
25054
|
}
|
|
24767
|
-
function opaqueSolidity(data, width, height) {
|
|
24768
|
-
let minX = width;
|
|
24769
|
-
let minY = height;
|
|
24770
|
-
let maxX = -1;
|
|
24771
|
-
let maxY = -1;
|
|
24772
|
-
let opaque = 0;
|
|
24773
|
-
for (let y = 0; y < height; y++) {
|
|
24774
|
-
for (let x = 0; x < width; x++) {
|
|
24775
|
-
if ((data[(y * width + x) * 4 + 3] ?? 0) <= 200) continue;
|
|
24776
|
-
opaque++;
|
|
24777
|
-
if (x < minX) minX = x;
|
|
24778
|
-
if (x > maxX) maxX = x;
|
|
24779
|
-
if (y < minY) minY = y;
|
|
24780
|
-
if (y > maxY) maxY = y;
|
|
24781
|
-
}
|
|
24782
|
-
}
|
|
24783
|
-
if (maxX < 0) return 0;
|
|
24784
|
-
const boxArea = (maxX - minX + 1) * (maxY - minY + 1);
|
|
24785
|
-
return boxArea === 0 ? 0 : opaque / boxArea;
|
|
24786
|
-
}
|
|
24787
|
-
var PLATE_SOLIDITY_THRESHOLD2 = 0.6;
|
|
24788
25055
|
function hasTransparency(data, threshold = 0.02) {
|
|
24789
25056
|
let transparentPixels = 0;
|
|
24790
25057
|
let totalPixels = 0;
|
|
@@ -24849,9 +25116,6 @@ function removeBackground(data, width, height, colorRangeThreshold = COLOR_RANGE
|
|
|
24849
25116
|
return data;
|
|
24850
25117
|
}
|
|
24851
25118
|
const dominantEdgeColor = getDominantEdgeColor(data, width, height);
|
|
24852
|
-
if (!dominantEdgeColor) {
|
|
24853
|
-
return data;
|
|
24854
|
-
}
|
|
24855
25119
|
const isGradient = hasGradientColors(data);
|
|
24856
25120
|
const result = Buffer.from(data);
|
|
24857
25121
|
if (isGradient) {
|
|
@@ -25129,8 +25393,8 @@ async function processInternal(inputBuffer, isSVG, options) {
|
|
|
25129
25393
|
const metadata = await sharp3(inputBuffer).metadata();
|
|
25130
25394
|
let alreadyTransparent = false;
|
|
25131
25395
|
if (metadata.hasAlpha) {
|
|
25132
|
-
const { data: alphaData
|
|
25133
|
-
alreadyTransparent =
|
|
25396
|
+
const { data: alphaData } = await sharp3(inputBuffer).raw().toBuffer({ resolveWithObject: true });
|
|
25397
|
+
alreadyTransparent = hasTransparency(alphaData, 0.05);
|
|
25134
25398
|
}
|
|
25135
25399
|
let { data: processedData, info } = await sharp3(inputBuffer).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
25136
25400
|
if (options.color) {
|
|
@@ -25466,7 +25730,7 @@ function coerceRawArgs(args) {
|
|
|
25466
25730
|
"dry-run": bool(args["dry-run"])
|
|
25467
25731
|
};
|
|
25468
25732
|
}
|
|
25469
|
-
var normalizeCommand =
|
|
25733
|
+
var normalizeCommand = defineCommand124({
|
|
25470
25734
|
meta: {
|
|
25471
25735
|
name: "normalize",
|
|
25472
25736
|
description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
|
|
@@ -25521,7 +25785,7 @@ Examples:
|
|
|
25521
25785
|
});
|
|
25522
25786
|
|
|
25523
25787
|
// src/commands/images/pinterest.ts
|
|
25524
|
-
import { defineCommand as
|
|
25788
|
+
import { defineCommand as defineCommand125 } from "citty";
|
|
25525
25789
|
registerSchema({
|
|
25526
25790
|
command: "images.pinterest",
|
|
25527
25791
|
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.",
|
|
@@ -25541,7 +25805,7 @@ registerSchema({
|
|
|
25541
25805
|
}
|
|
25542
25806
|
}
|
|
25543
25807
|
});
|
|
25544
|
-
var pinterestCommand =
|
|
25808
|
+
var pinterestCommand = defineCommand125({
|
|
25545
25809
|
meta: {
|
|
25546
25810
|
name: "pinterest",
|
|
25547
25811
|
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'"
|
|
@@ -25581,7 +25845,7 @@ var pinterestCommand = defineCommand123({
|
|
|
25581
25845
|
});
|
|
25582
25846
|
|
|
25583
25847
|
// src/commands/images/screenshot.ts
|
|
25584
|
-
import { defineCommand as
|
|
25848
|
+
import { defineCommand as defineCommand126 } from "citty";
|
|
25585
25849
|
registerSchema({
|
|
25586
25850
|
command: "images.screenshot",
|
|
25587
25851
|
description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
|
|
@@ -25597,7 +25861,7 @@ registerSchema({
|
|
|
25597
25861
|
}
|
|
25598
25862
|
}
|
|
25599
25863
|
});
|
|
25600
|
-
var screenshotCommand =
|
|
25864
|
+
var screenshotCommand = defineCommand126({
|
|
25601
25865
|
meta: {
|
|
25602
25866
|
name: "screenshot",
|
|
25603
25867
|
description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
|
|
@@ -25660,7 +25924,7 @@ var screenshotCommand = defineCommand124({
|
|
|
25660
25924
|
});
|
|
25661
25925
|
|
|
25662
25926
|
// src/commands/images/search.ts
|
|
25663
|
-
import { defineCommand as
|
|
25927
|
+
import { defineCommand as defineCommand127 } from "citty";
|
|
25664
25928
|
registerSchema({
|
|
25665
25929
|
command: "images.search",
|
|
25666
25930
|
description: "Search images by text query. Only returns ready images.",
|
|
@@ -25676,7 +25940,7 @@ registerSchema({
|
|
|
25676
25940
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
25677
25941
|
}
|
|
25678
25942
|
});
|
|
25679
|
-
var searchCommand =
|
|
25943
|
+
var searchCommand = defineCommand127({
|
|
25680
25944
|
meta: {
|
|
25681
25945
|
name: "search",
|
|
25682
25946
|
description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
|
|
@@ -25736,7 +26000,7 @@ var searchCommand = defineCommand125({
|
|
|
25736
26000
|
});
|
|
25737
26001
|
|
|
25738
26002
|
// src/commands/images/sticker.ts
|
|
25739
|
-
import { defineCommand as
|
|
26003
|
+
import { defineCommand as defineCommand128 } from "citty";
|
|
25740
26004
|
registerSchema({
|
|
25741
26005
|
command: "images.sticker",
|
|
25742
26006
|
description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
|
|
@@ -25768,7 +26032,7 @@ registerSchema({
|
|
|
25768
26032
|
}
|
|
25769
26033
|
}
|
|
25770
26034
|
});
|
|
25771
|
-
var stickerCommand =
|
|
26035
|
+
var stickerCommand = defineCommand128({
|
|
25772
26036
|
meta: {
|
|
25773
26037
|
name: "sticker",
|
|
25774
26038
|
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"
|
|
@@ -25815,7 +26079,11 @@ var stickerCommand = defineCommand126({
|
|
|
25815
26079
|
});
|
|
25816
26080
|
|
|
25817
26081
|
// src/commands/images/stock.ts
|
|
25818
|
-
import { defineCommand as
|
|
26082
|
+
import { defineCommand as defineCommand129 } from "citty";
|
|
26083
|
+
var STOCK_ERROR_FIX = {
|
|
26084
|
+
action: "use_different_resource",
|
|
26085
|
+
explanation: "Switch provider instead of retrying stock search. Stock search is one of several image sources. Run `baker images find <query> --sources library,pinterest,google` (`--sources` is required \u2014 `find` alone searches the library only) or `baker images generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
|
|
26086
|
+
};
|
|
25819
26087
|
registerSchema({
|
|
25820
26088
|
command: "images.stock",
|
|
25821
26089
|
description: "Stock photo, vector illustration, icon-set, and PSD search via Magnific (Freepik's developer API).",
|
|
@@ -25873,7 +26141,22 @@ registerSchema({
|
|
|
25873
26141
|
}
|
|
25874
26142
|
}
|
|
25875
26143
|
});
|
|
25876
|
-
|
|
26144
|
+
function buildStockRequest(query, args) {
|
|
26145
|
+
const body = { query };
|
|
26146
|
+
if (args.type) body.contentType = args.type;
|
|
26147
|
+
if (args.orientation) body.orientation = args.orientation;
|
|
26148
|
+
if (args.license) body.license = args.license;
|
|
26149
|
+
if (args.color) body.color = args.color;
|
|
26150
|
+
if (args.ai) body.aiGenerated = args.ai;
|
|
26151
|
+
if (args.people) body.people = args.people;
|
|
26152
|
+
if (args.order) body.order = args.order;
|
|
26153
|
+
if (args.limit) body.limit = Number(args.limit);
|
|
26154
|
+
if (args.page) body.page = Number(args.page);
|
|
26155
|
+
if (args["auto-ingest"]) body.autoIngest = Number(args["auto-ingest"]);
|
|
26156
|
+
if (args.context) body.descriptionContext = args.context;
|
|
26157
|
+
return body;
|
|
26158
|
+
}
|
|
26159
|
+
var stockCommand = defineCommand129({
|
|
25877
26160
|
meta: {
|
|
25878
26161
|
name: "stock",
|
|
25879
26162
|
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"
|
|
@@ -25903,23 +26186,23 @@ var stockCommand = defineCommand127({
|
|
|
25903
26186
|
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Query is required" } });
|
|
25904
26187
|
process.exit(1);
|
|
25905
26188
|
}
|
|
25906
|
-
const
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
if (args.order) body.order = args.order;
|
|
25914
|
-
if (args.limit) body.limit = Number(args.limit);
|
|
25915
|
-
if (args.page) body.page = Number(args.page);
|
|
25916
|
-
if (args["auto-ingest"]) body.autoIngest = Number(args["auto-ingest"]);
|
|
25917
|
-
if (args.context) body.descriptionContext = args.context;
|
|
25918
|
-
const data = await apiPost("/api/images/stock", body);
|
|
25919
|
-
writeJson({ ok: true, data });
|
|
26189
|
+
const data = await apiPost("/api/images/stock", buildStockRequest(query, args));
|
|
26190
|
+
const hints = emptyResultHints({
|
|
26191
|
+
provider: "stock",
|
|
26192
|
+
hitCount: data.hits.length,
|
|
26193
|
+
activeFilters: activeFilterFlags(args, ["type", "orientation", "license", "color", "ai", "people"])
|
|
26194
|
+
});
|
|
26195
|
+
writeJson({ ok: true, data, ...hints.length ? { hints } : {} });
|
|
25920
26196
|
} catch (err) {
|
|
25921
26197
|
if (err instanceof ApiError) {
|
|
25922
|
-
writeJson({
|
|
26198
|
+
writeJson({
|
|
26199
|
+
ok: false,
|
|
26200
|
+
error: {
|
|
26201
|
+
code: err.code,
|
|
26202
|
+
message: err.message,
|
|
26203
|
+
fix: STOCK_ERROR_FIX
|
|
26204
|
+
}
|
|
26205
|
+
});
|
|
25923
26206
|
process.exit(1);
|
|
25924
26207
|
}
|
|
25925
26208
|
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
@@ -25929,7 +26212,7 @@ var stockCommand = defineCommand127({
|
|
|
25929
26212
|
});
|
|
25930
26213
|
|
|
25931
26214
|
// src/lib/tags-command.ts
|
|
25932
|
-
import { defineCommand as
|
|
26215
|
+
import { defineCommand as defineCommand130 } from "citty";
|
|
25933
26216
|
function makeTagsCommand(command, label, endpoint) {
|
|
25934
26217
|
registerSchema({
|
|
25935
26218
|
command: `${command}.tags`,
|
|
@@ -25938,7 +26221,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
25938
26221
|
output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
|
|
25939
26222
|
}
|
|
25940
26223
|
});
|
|
25941
|
-
return
|
|
26224
|
+
return defineCommand130({
|
|
25942
26225
|
meta: {
|
|
25943
26226
|
name: "tags",
|
|
25944
26227
|
description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
|
|
@@ -25974,7 +26257,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
25974
26257
|
var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
|
|
25975
26258
|
|
|
25976
26259
|
// src/commands/images/upload.ts
|
|
25977
|
-
import { defineCommand as
|
|
26260
|
+
import { defineCommand as defineCommand131 } from "citty";
|
|
25978
26261
|
registerSchema({
|
|
25979
26262
|
command: "images.upload",
|
|
25980
26263
|
description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
|
|
@@ -26012,7 +26295,7 @@ registerSchema({
|
|
|
26012
26295
|
function isRemoteUrl2(value) {
|
|
26013
26296
|
return /^https?:\/\//i.test(value);
|
|
26014
26297
|
}
|
|
26015
|
-
var uploadCommand =
|
|
26298
|
+
var uploadCommand = defineCommand131({
|
|
26016
26299
|
meta: {
|
|
26017
26300
|
name: "upload",
|
|
26018
26301
|
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'"
|
|
@@ -26105,7 +26388,7 @@ async function uploadLocal(target, args) {
|
|
|
26105
26388
|
}
|
|
26106
26389
|
|
|
26107
26390
|
// src/commands/images/upscale.ts
|
|
26108
|
-
import { defineCommand as
|
|
26391
|
+
import { defineCommand as defineCommand132 } from "citty";
|
|
26109
26392
|
registerSchema({
|
|
26110
26393
|
command: "images.upscale",
|
|
26111
26394
|
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).",
|
|
@@ -26120,7 +26403,7 @@ registerSchema({
|
|
|
26120
26403
|
}
|
|
26121
26404
|
});
|
|
26122
26405
|
var POLL_INTERVAL_MS3 = 1500;
|
|
26123
|
-
var upscaleCommand =
|
|
26406
|
+
var upscaleCommand = defineCommand132({
|
|
26124
26407
|
meta: {
|
|
26125
26408
|
name: "upscale",
|
|
26126
26409
|
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"
|
|
@@ -26175,7 +26458,7 @@ var upscaleCommand = defineCommand130({
|
|
|
26175
26458
|
});
|
|
26176
26459
|
|
|
26177
26460
|
// src/commands/images/use.ts
|
|
26178
|
-
import { defineCommand as
|
|
26461
|
+
import { defineCommand as defineCommand133 } from "citty";
|
|
26179
26462
|
registerSchema({
|
|
26180
26463
|
command: "images.use",
|
|
26181
26464
|
description: "Ingest a URL and wait for the library record to be ready.",
|
|
@@ -26191,7 +26474,7 @@ registerSchema({
|
|
|
26191
26474
|
}
|
|
26192
26475
|
});
|
|
26193
26476
|
var POLL_INTERVAL_MS4 = 1500;
|
|
26194
|
-
var useCommand =
|
|
26477
|
+
var useCommand = defineCommand133({
|
|
26195
26478
|
meta: {
|
|
26196
26479
|
name: "use",
|
|
26197
26480
|
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"
|
|
@@ -26237,7 +26520,7 @@ var useCommand = defineCommand131({
|
|
|
26237
26520
|
});
|
|
26238
26521
|
|
|
26239
26522
|
// src/commands/images/index.ts
|
|
26240
|
-
var imagesCommand =
|
|
26523
|
+
var imagesCommand = defineCommand134({
|
|
26241
26524
|
meta: {
|
|
26242
26525
|
name: "images",
|
|
26243
26526
|
description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
|
|
@@ -26308,16 +26591,16 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
26308
26591
|
});
|
|
26309
26592
|
|
|
26310
26593
|
// src/commands/landing/index.ts
|
|
26311
|
-
import { defineCommand as
|
|
26594
|
+
import { defineCommand as defineCommand136 } from "citty";
|
|
26312
26595
|
|
|
26313
26596
|
// src/commands/landing/critique.ts
|
|
26314
26597
|
import { readdir as readdir8, stat as stat6 } from "fs/promises";
|
|
26315
|
-
import
|
|
26316
|
-
import { defineCommand as
|
|
26598
|
+
import path27 from "path";
|
|
26599
|
+
import { defineCommand as defineCommand135 } from "citty";
|
|
26317
26600
|
|
|
26318
26601
|
// src/engine/landing/lib/brand-tokens.ts
|
|
26319
|
-
import { readFile as
|
|
26320
|
-
import
|
|
26602
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
26603
|
+
import path24 from "path";
|
|
26321
26604
|
|
|
26322
26605
|
// src/engine/landing/lib/color.ts
|
|
26323
26606
|
var NEUTRAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
@@ -26479,14 +26762,14 @@ function parseBrandMd(brandMd, fonts, colors) {
|
|
|
26479
26762
|
}
|
|
26480
26763
|
}
|
|
26481
26764
|
async function loadBrandTokens(projectRoot) {
|
|
26482
|
-
const globalCss = await safeRead(
|
|
26483
|
-
const brandMd = await safeRead(
|
|
26765
|
+
const globalCss = await safeRead(path24.join(projectRoot, "src", "styles", "global.css"));
|
|
26766
|
+
const brandMd = await safeRead(path24.join(projectRoot, "src", "brand", "BRAND.md"));
|
|
26484
26767
|
if (!globalCss && !brandMd) return EMPTY;
|
|
26485
26768
|
return parseBrandTokens(globalCss, brandMd);
|
|
26486
26769
|
}
|
|
26487
26770
|
async function safeRead(file) {
|
|
26488
26771
|
try {
|
|
26489
|
-
return await
|
|
26772
|
+
return await readFile21(file, "utf8");
|
|
26490
26773
|
} catch {
|
|
26491
26774
|
return "";
|
|
26492
26775
|
}
|
|
@@ -27272,40 +27555,40 @@ function describeCounts(findings) {
|
|
|
27272
27555
|
}
|
|
27273
27556
|
|
|
27274
27557
|
// src/commands/landing/snapshot.ts
|
|
27275
|
-
import { mkdir as
|
|
27276
|
-
import
|
|
27558
|
+
import { mkdir as mkdir8, rename as rename2, writeFile as writeFile11 } from "fs/promises";
|
|
27559
|
+
import path25 from "path";
|
|
27277
27560
|
var CRITIC_VERSION = "1";
|
|
27278
27561
|
function critiqueCacheDir(projectRoot) {
|
|
27279
|
-
return
|
|
27562
|
+
return path25.join(projectRoot, ".cache", "landing-critique");
|
|
27280
27563
|
}
|
|
27281
27564
|
function snapshotPath(projectRoot, slug) {
|
|
27282
|
-
return
|
|
27565
|
+
return path25.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
27283
27566
|
}
|
|
27284
27567
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
27285
|
-
await
|
|
27568
|
+
await mkdir8(critiqueCacheDir(projectRoot), { recursive: true });
|
|
27286
27569
|
const dest = snapshotPath(projectRoot, snapshot.slug);
|
|
27287
27570
|
const tmp = `${dest}.tmp`;
|
|
27288
|
-
await
|
|
27571
|
+
await writeFile11(tmp, `${JSON.stringify(snapshot, null, 2)}
|
|
27289
27572
|
`, "utf8");
|
|
27290
27573
|
await rename2(tmp, dest);
|
|
27291
27574
|
}
|
|
27292
27575
|
|
|
27293
27576
|
// src/commands/landing/source-version.ts
|
|
27294
|
-
import { readdir as readdir7, readFile as
|
|
27295
|
-
import
|
|
27577
|
+
import { readdir as readdir7, readFile as readFile22, stat as stat5 } from "fs/promises";
|
|
27578
|
+
import path26 from "path";
|
|
27296
27579
|
async function landingSourceRelPaths(landingDir) {
|
|
27297
27580
|
const rel = [];
|
|
27298
|
-
if (await isFile(
|
|
27299
|
-
const componentsDir =
|
|
27581
|
+
if (await isFile(path26.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
27582
|
+
const componentsDir = path26.join(landingDir, "_components");
|
|
27300
27583
|
for (const abs of await walkAstro(componentsDir)) {
|
|
27301
|
-
rel.push(
|
|
27584
|
+
rel.push(path26.relative(landingDir, abs).split(path26.sep).join("/"));
|
|
27302
27585
|
}
|
|
27303
27586
|
return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
27304
27587
|
}
|
|
27305
27588
|
async function readLandingSources(landingDir) {
|
|
27306
27589
|
const rel = await landingSourceRelPaths(landingDir);
|
|
27307
27590
|
const out = [];
|
|
27308
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
27591
|
+
for (const r of rel) out.push({ path: r, text: await readFile22(path26.join(landingDir, r), "utf8") });
|
|
27309
27592
|
return out;
|
|
27310
27593
|
}
|
|
27311
27594
|
async function computeLandingSourceSha(landingDir) {
|
|
@@ -27314,7 +27597,7 @@ async function computeLandingSourceSha(landingDir) {
|
|
|
27314
27597
|
for (const r of rel) {
|
|
27315
27598
|
let bytes;
|
|
27316
27599
|
try {
|
|
27317
|
-
bytes = await
|
|
27600
|
+
bytes = await readFile22(path26.join(landingDir, r));
|
|
27318
27601
|
} catch {
|
|
27319
27602
|
bytes = Buffer.alloc(0);
|
|
27320
27603
|
}
|
|
@@ -27338,7 +27621,7 @@ async function walkAstro(dir) {
|
|
|
27338
27621
|
}
|
|
27339
27622
|
const out = [];
|
|
27340
27623
|
for (const entry of entries) {
|
|
27341
|
-
const abs =
|
|
27624
|
+
const abs = path26.join(dir, entry.name);
|
|
27342
27625
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
27343
27626
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
27344
27627
|
}
|
|
@@ -27359,14 +27642,14 @@ registerSchema({
|
|
|
27359
27642
|
}
|
|
27360
27643
|
});
|
|
27361
27644
|
var SLUG_RE = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
27362
|
-
function
|
|
27645
|
+
function fail5(code, message, fix) {
|
|
27363
27646
|
process.stderr.write(
|
|
27364
27647
|
`${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
|
|
27365
27648
|
`
|
|
27366
27649
|
);
|
|
27367
27650
|
process.exit(2);
|
|
27368
27651
|
}
|
|
27369
|
-
var critiqueCommand2 =
|
|
27652
|
+
var critiqueCommand2 = defineCommand135({
|
|
27370
27653
|
meta: {
|
|
27371
27654
|
name: "critique",
|
|
27372
27655
|
description: "Start here: `baker landing critique <slug>` after building or editing a landing. Deterministic design-quality critic (ADVISORY \u2014 findings never fail it). Flags the known AI design tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md as the allowlist. Also records the critique that publishing requires \u2014 run it before finishing a landing."
|
|
@@ -27379,15 +27662,15 @@ var critiqueCommand2 = defineCommand133({
|
|
|
27379
27662
|
const slug = String(args.slug);
|
|
27380
27663
|
const projectRoot = process.cwd();
|
|
27381
27664
|
if (!SLUG_RE.test(slug) || slug.includes("..")) {
|
|
27382
|
-
|
|
27665
|
+
fail5(
|
|
27383
27666
|
"INVALID_SLUG",
|
|
27384
27667
|
`"${slug}" is not a landing slug \u2014 use the folder name directly under src/pages/ (letters, digits, dashes; not a path, not a _-private folder).`,
|
|
27385
27668
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
27386
27669
|
);
|
|
27387
27670
|
}
|
|
27388
|
-
const landingDir =
|
|
27671
|
+
const landingDir = path27.resolve(projectRoot, "src", "pages", slug);
|
|
27389
27672
|
if (!await isDir(landingDir)) {
|
|
27390
|
-
|
|
27673
|
+
fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
27391
27674
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
27392
27675
|
});
|
|
27393
27676
|
}
|
|
@@ -27440,7 +27723,7 @@ var critiqueCommand2 = defineCommand133({
|
|
|
27440
27723
|
});
|
|
27441
27724
|
async function listLandingSlugs(projectRoot) {
|
|
27442
27725
|
try {
|
|
27443
|
-
const entries = await readdir8(
|
|
27726
|
+
const entries = await readdir8(path27.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
27444
27727
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
27445
27728
|
} catch {
|
|
27446
27729
|
return [];
|
|
@@ -27466,7 +27749,7 @@ async function isDir(p) {
|
|
|
27466
27749
|
}
|
|
27467
27750
|
|
|
27468
27751
|
// src/commands/landing/index.ts
|
|
27469
|
-
var landingCommand =
|
|
27752
|
+
var landingCommand = defineCommand136({
|
|
27470
27753
|
meta: {
|
|
27471
27754
|
name: "landing",
|
|
27472
27755
|
description: `Design-quality tools for landing pages (src/pages/<slug>/).
|
|
@@ -27482,7 +27765,7 @@ Subcommands:
|
|
|
27482
27765
|
});
|
|
27483
27766
|
|
|
27484
27767
|
// src/commands/mcp/index.ts
|
|
27485
|
-
import { defineCommand as
|
|
27768
|
+
import { defineCommand as defineCommand137 } from "citty";
|
|
27486
27769
|
var SCOPES = ["user", "user_org", "company", "org"];
|
|
27487
27770
|
function parseScope(raw) {
|
|
27488
27771
|
const scope = raw === void 0 ? "company" : String(raw);
|
|
@@ -27508,7 +27791,7 @@ function parseHeaders(raw) {
|
|
|
27508
27791
|
}
|
|
27509
27792
|
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
27510
27793
|
}
|
|
27511
|
-
function
|
|
27794
|
+
function fail6(err) {
|
|
27512
27795
|
if (err instanceof ApiError) {
|
|
27513
27796
|
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
27514
27797
|
process.exit(1);
|
|
@@ -27521,7 +27804,7 @@ registerSchema({
|
|
|
27521
27804
|
description: "List every third-party tool this chat can reach: managed integrations (Attio, Slack, Gmail, Google Sheets, \u2026) plus custom MCP servers. Start here when the user mentions an external tool.",
|
|
27522
27805
|
args: {}
|
|
27523
27806
|
});
|
|
27524
|
-
var connectedCommand =
|
|
27807
|
+
var connectedCommand = defineCommand137({
|
|
27525
27808
|
meta: {
|
|
27526
27809
|
name: "connected",
|
|
27527
27810
|
description: `List all connected third-party tools \u2014 managed integrations plus custom MCP servers.
|
|
@@ -27559,7 +27842,7 @@ A tool the user names that is NOT listed here is simply not connected yet.`
|
|
|
27559
27842
|
hints
|
|
27560
27843
|
});
|
|
27561
27844
|
} catch (err) {
|
|
27562
|
-
|
|
27845
|
+
fail6(err);
|
|
27563
27846
|
}
|
|
27564
27847
|
}
|
|
27565
27848
|
});
|
|
@@ -27568,7 +27851,7 @@ registerSchema({
|
|
|
27568
27851
|
description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
|
|
27569
27852
|
args: {}
|
|
27570
27853
|
});
|
|
27571
|
-
var listCommand8 =
|
|
27854
|
+
var listCommand8 = defineCommand137({
|
|
27572
27855
|
meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
|
|
27573
27856
|
run: async () => {
|
|
27574
27857
|
try {
|
|
@@ -27579,7 +27862,7 @@ var listCommand8 = defineCommand135({
|
|
|
27579
27862
|
);
|
|
27580
27863
|
writeJson({ ok: true, data: data.servers });
|
|
27581
27864
|
} catch (err) {
|
|
27582
|
-
|
|
27865
|
+
fail6(err);
|
|
27583
27866
|
}
|
|
27584
27867
|
}
|
|
27585
27868
|
});
|
|
@@ -27593,7 +27876,7 @@ registerSchema({
|
|
|
27593
27876
|
header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
|
|
27594
27877
|
}
|
|
27595
27878
|
});
|
|
27596
|
-
var addCommand =
|
|
27879
|
+
var addCommand = defineCommand137({
|
|
27597
27880
|
meta: {
|
|
27598
27881
|
name: "add",
|
|
27599
27882
|
description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
|
|
@@ -27625,7 +27908,7 @@ Examples:
|
|
|
27625
27908
|
});
|
|
27626
27909
|
writeJson({ ok: true, data });
|
|
27627
27910
|
} catch (err) {
|
|
27628
|
-
|
|
27911
|
+
fail6(err);
|
|
27629
27912
|
}
|
|
27630
27913
|
}
|
|
27631
27914
|
});
|
|
@@ -27634,7 +27917,7 @@ registerSchema({
|
|
|
27634
27917
|
description: "Remove a company custom MCP server by name.",
|
|
27635
27918
|
args: { name: { type: "string", description: "Server name to remove", required: true } }
|
|
27636
27919
|
});
|
|
27637
|
-
var removeCommand4 =
|
|
27920
|
+
var removeCommand4 = defineCommand137({
|
|
27638
27921
|
meta: {
|
|
27639
27922
|
name: "remove",
|
|
27640
27923
|
description: `Remove a company custom MCP server by name.
|
|
@@ -27652,11 +27935,11 @@ Example:
|
|
|
27652
27935
|
});
|
|
27653
27936
|
writeJson({ ok: true, data });
|
|
27654
27937
|
} catch (err) {
|
|
27655
|
-
|
|
27938
|
+
fail6(err);
|
|
27656
27939
|
}
|
|
27657
27940
|
}
|
|
27658
27941
|
});
|
|
27659
|
-
var mcpCommand =
|
|
27942
|
+
var mcpCommand = defineCommand137({
|
|
27660
27943
|
meta: {
|
|
27661
27944
|
name: "mcp",
|
|
27662
27945
|
description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
|
|
@@ -27682,10 +27965,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
|
|
|
27682
27965
|
});
|
|
27683
27966
|
|
|
27684
27967
|
// src/commands/research/index.ts
|
|
27685
|
-
import { defineCommand as
|
|
27968
|
+
import { defineCommand as defineCommand148 } from "citty";
|
|
27686
27969
|
|
|
27687
27970
|
// src/commands/research/advertisers.ts
|
|
27688
|
-
import { defineCommand as
|
|
27971
|
+
import { defineCommand as defineCommand138 } from "citty";
|
|
27689
27972
|
|
|
27690
27973
|
// src/commands/research/output.ts
|
|
27691
27974
|
var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
|
|
@@ -27798,7 +28081,7 @@ var FIELDS3 = {
|
|
|
27798
28081
|
etv: "Estimated traffic value (USD)",
|
|
27799
28082
|
visibility: "SERP visibility score (0-1)"
|
|
27800
28083
|
};
|
|
27801
|
-
var advertisersCommand =
|
|
28084
|
+
var advertisersCommand = defineCommand138({
|
|
27802
28085
|
meta: {
|
|
27803
28086
|
name: "advertisers",
|
|
27804
28087
|
description: `Find domains competing for a keyword in Google SERPs.
|
|
@@ -27845,7 +28128,7 @@ Examples:
|
|
|
27845
28128
|
});
|
|
27846
28129
|
|
|
27847
28130
|
// src/commands/research/autocomplete.ts
|
|
27848
|
-
import { defineCommand as
|
|
28131
|
+
import { defineCommand as defineCommand139 } from "citty";
|
|
27849
28132
|
registerSchema({
|
|
27850
28133
|
command: "research.autocomplete",
|
|
27851
28134
|
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).",
|
|
@@ -27868,7 +28151,7 @@ registerSchema({
|
|
|
27868
28151
|
var FIELDS4 = {
|
|
27869
28152
|
suggestion: "Autocomplete suggestion from Google"
|
|
27870
28153
|
};
|
|
27871
|
-
var autocompleteCommand =
|
|
28154
|
+
var autocompleteCommand = defineCommand139({
|
|
27872
28155
|
meta: {
|
|
27873
28156
|
name: "autocomplete",
|
|
27874
28157
|
description: `Get Google Autocomplete suggestions for keyword expansion.
|
|
@@ -27914,7 +28197,7 @@ Examples:
|
|
|
27914
28197
|
});
|
|
27915
28198
|
|
|
27916
28199
|
// src/commands/research/countries.ts
|
|
27917
|
-
import { defineCommand as
|
|
28200
|
+
import { defineCommand as defineCommand140 } from "citty";
|
|
27918
28201
|
registerSchema({
|
|
27919
28202
|
command: "research.countries",
|
|
27920
28203
|
description: "List all supported country codes for --location flag in research commands.",
|
|
@@ -27971,7 +28254,7 @@ var FIELDS5 = {
|
|
|
27971
28254
|
code: "Country code to pass as --location",
|
|
27972
28255
|
name: "Country name"
|
|
27973
28256
|
};
|
|
27974
|
-
var countriesCommand =
|
|
28257
|
+
var countriesCommand = defineCommand140({
|
|
27975
28258
|
meta: {
|
|
27976
28259
|
name: "countries",
|
|
27977
28260
|
description: "List all supported country codes for --location flag."
|
|
@@ -27982,7 +28265,7 @@ var countriesCommand = defineCommand138({
|
|
|
27982
28265
|
});
|
|
27983
28266
|
|
|
27984
28267
|
// src/commands/research/intent.ts
|
|
27985
|
-
import { defineCommand as
|
|
28268
|
+
import { defineCommand as defineCommand141 } from "citty";
|
|
27986
28269
|
registerSchema({
|
|
27987
28270
|
command: "research.intent",
|
|
27988
28271
|
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.",
|
|
@@ -28005,7 +28288,7 @@ var FIELDS6 = {
|
|
|
28005
28288
|
intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
|
|
28006
28289
|
probability: "Confidence score 0.0-1.0"
|
|
28007
28290
|
};
|
|
28008
|
-
var intentCommand =
|
|
28291
|
+
var intentCommand = defineCommand141({
|
|
28009
28292
|
meta: {
|
|
28010
28293
|
name: "intent",
|
|
28011
28294
|
description: `Classify Google Search intent for keywords. Returns intent type and confidence.
|
|
@@ -28053,7 +28336,7 @@ Examples:
|
|
|
28053
28336
|
});
|
|
28054
28337
|
|
|
28055
28338
|
// src/commands/research/keyword-gap.ts
|
|
28056
|
-
import { defineCommand as
|
|
28339
|
+
import { defineCommand as defineCommand142 } from "citty";
|
|
28057
28340
|
registerSchema({
|
|
28058
28341
|
command: "research.keyword-gap",
|
|
28059
28342
|
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.",
|
|
@@ -28082,7 +28365,7 @@ var FIELDS7 = {
|
|
|
28082
28365
|
cpc: "Cost per click USD",
|
|
28083
28366
|
their_position: "Competitor's ranking position"
|
|
28084
28367
|
};
|
|
28085
|
-
var keywordGapCommand =
|
|
28368
|
+
var keywordGapCommand = defineCommand142({
|
|
28086
28369
|
meta: {
|
|
28087
28370
|
name: "keyword-gap",
|
|
28088
28371
|
description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
|
|
@@ -28156,7 +28439,7 @@ Examples:
|
|
|
28156
28439
|
});
|
|
28157
28440
|
|
|
28158
28441
|
// src/commands/research/keywords-for-site.ts
|
|
28159
|
-
import { defineCommand as
|
|
28442
|
+
import { defineCommand as defineCommand143 } from "citty";
|
|
28160
28443
|
registerSchema({
|
|
28161
28444
|
command: "research.keywords-for-site",
|
|
28162
28445
|
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.",
|
|
@@ -28189,7 +28472,7 @@ var FIELDS8 = {
|
|
|
28189
28472
|
competition: "LOW, MEDIUM, or HIGH",
|
|
28190
28473
|
competition_index: "Competition score 0-100"
|
|
28191
28474
|
};
|
|
28192
|
-
var keywordsForSiteCommand =
|
|
28475
|
+
var keywordsForSiteCommand = defineCommand143({
|
|
28193
28476
|
meta: {
|
|
28194
28477
|
name: "keywords-for-site",
|
|
28195
28478
|
description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
|
|
@@ -28242,7 +28525,7 @@ Examples:
|
|
|
28242
28525
|
});
|
|
28243
28526
|
|
|
28244
28527
|
// src/commands/research/languages.ts
|
|
28245
|
-
import { defineCommand as
|
|
28528
|
+
import { defineCommand as defineCommand144 } from "citty";
|
|
28246
28529
|
registerSchema({
|
|
28247
28530
|
command: "research.languages",
|
|
28248
28531
|
description: "List all supported language codes for --language flag in research commands.",
|
|
@@ -28272,7 +28555,7 @@ var FIELDS9 = {
|
|
|
28272
28555
|
code: "Language code to pass as --language",
|
|
28273
28556
|
name: "Language name (also accepted by --language)"
|
|
28274
28557
|
};
|
|
28275
|
-
var languagesCommand2 =
|
|
28558
|
+
var languagesCommand2 = defineCommand144({
|
|
28276
28559
|
meta: {
|
|
28277
28560
|
name: "languages",
|
|
28278
28561
|
description: "List all supported language codes for --language flag."
|
|
@@ -28283,7 +28566,7 @@ var languagesCommand2 = defineCommand142({
|
|
|
28283
28566
|
});
|
|
28284
28567
|
|
|
28285
28568
|
// src/commands/research/lighthouse.ts
|
|
28286
|
-
import { defineCommand as
|
|
28569
|
+
import { defineCommand as defineCommand145 } from "citty";
|
|
28287
28570
|
registerSchema({
|
|
28288
28571
|
command: "research.lighthouse",
|
|
28289
28572
|
description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
|
|
@@ -28302,7 +28585,7 @@ var FIELDS10 = {
|
|
|
28302
28585
|
speed_index_ms: "Speed Index in ms (good: < 3400)",
|
|
28303
28586
|
interactive_ms: "Time to Interactive in ms (good: < 3800)"
|
|
28304
28587
|
};
|
|
28305
|
-
var lighthouseCommand =
|
|
28588
|
+
var lighthouseCommand = defineCommand145({
|
|
28306
28589
|
meta: {
|
|
28307
28590
|
name: "lighthouse",
|
|
28308
28591
|
description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
|
|
@@ -28340,7 +28623,7 @@ Examples:
|
|
|
28340
28623
|
});
|
|
28341
28624
|
|
|
28342
28625
|
// src/commands/research/relevant-pages.ts
|
|
28343
|
-
import { defineCommand as
|
|
28626
|
+
import { defineCommand as defineCommand146 } from "citty";
|
|
28344
28627
|
registerSchema({
|
|
28345
28628
|
command: "research.relevant-pages",
|
|
28346
28629
|
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).",
|
|
@@ -28366,7 +28649,7 @@ var FIELDS11 = {
|
|
|
28366
28649
|
keywords: "Total organic keywords the page ranks for",
|
|
28367
28650
|
top_10: "Keywords in positions 1-10"
|
|
28368
28651
|
};
|
|
28369
|
-
var relevantPagesCommand =
|
|
28652
|
+
var relevantPagesCommand = defineCommand146({
|
|
28370
28653
|
meta: {
|
|
28371
28654
|
name: "relevant-pages",
|
|
28372
28655
|
description: `Get the top pages of a competitor domain with traffic data.
|
|
@@ -28412,7 +28695,7 @@ Examples:
|
|
|
28412
28695
|
});
|
|
28413
28696
|
|
|
28414
28697
|
// src/commands/research/web.ts
|
|
28415
|
-
import { defineCommand as
|
|
28698
|
+
import { defineCommand as defineCommand147 } from "citty";
|
|
28416
28699
|
registerSchema({
|
|
28417
28700
|
command: "research.web",
|
|
28418
28701
|
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).",
|
|
@@ -28463,7 +28746,7 @@ async function runDeepResearch(question) {
|
|
|
28463
28746
|
}
|
|
28464
28747
|
throw new Error("Deep research timed out");
|
|
28465
28748
|
}
|
|
28466
|
-
var webCommand =
|
|
28749
|
+
var webCommand = defineCommand147({
|
|
28467
28750
|
meta: {
|
|
28468
28751
|
name: "web",
|
|
28469
28752
|
description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
|
|
@@ -28523,7 +28806,7 @@ Examples:
|
|
|
28523
28806
|
});
|
|
28524
28807
|
|
|
28525
28808
|
// src/commands/research/index.ts
|
|
28526
|
-
var researchCommand =
|
|
28809
|
+
var researchCommand = defineCommand148({
|
|
28527
28810
|
meta: {
|
|
28528
28811
|
name: "research",
|
|
28529
28812
|
description: `Competitive intelligence and AI-powered research commands.
|
|
@@ -28564,10 +28847,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
|
|
|
28564
28847
|
});
|
|
28565
28848
|
|
|
28566
28849
|
// src/commands/scheduled-actions/index.ts
|
|
28567
|
-
import { defineCommand as
|
|
28850
|
+
import { defineCommand as defineCommand155 } from "citty";
|
|
28568
28851
|
|
|
28569
28852
|
// src/commands/scheduled-actions/create.ts
|
|
28570
|
-
import { defineCommand as
|
|
28853
|
+
import { defineCommand as defineCommand149 } from "citty";
|
|
28571
28854
|
|
|
28572
28855
|
// src/commands/scheduled-actions/shared.ts
|
|
28573
28856
|
var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
|
|
@@ -28682,7 +28965,7 @@ registerSchema({
|
|
|
28682
28965
|
prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
|
|
28683
28966
|
}
|
|
28684
28967
|
});
|
|
28685
|
-
var createCommand2 =
|
|
28968
|
+
var createCommand2 = defineCommand149({
|
|
28686
28969
|
meta: {
|
|
28687
28970
|
name: "create",
|
|
28688
28971
|
description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
|
|
@@ -28731,7 +29014,7 @@ var createCommand2 = defineCommand147({
|
|
|
28731
29014
|
});
|
|
28732
29015
|
|
|
28733
29016
|
// src/commands/scheduled-actions/delete.ts
|
|
28734
|
-
import { defineCommand as
|
|
29017
|
+
import { defineCommand as defineCommand150 } from "citty";
|
|
28735
29018
|
registerSchema({
|
|
28736
29019
|
command: "scheduled-actions.delete",
|
|
28737
29020
|
description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
|
|
@@ -28739,7 +29022,7 @@ registerSchema({
|
|
|
28739
29022
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
28740
29023
|
}
|
|
28741
29024
|
});
|
|
28742
|
-
var deleteCommand2 =
|
|
29025
|
+
var deleteCommand2 = defineCommand150({
|
|
28743
29026
|
meta: {
|
|
28744
29027
|
name: "delete",
|
|
28745
29028
|
description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
|
|
@@ -28768,7 +29051,7 @@ var deleteCommand2 = defineCommand148({
|
|
|
28768
29051
|
});
|
|
28769
29052
|
|
|
28770
29053
|
// src/commands/scheduled-actions/get.ts
|
|
28771
|
-
import { defineCommand as
|
|
29054
|
+
import { defineCommand as defineCommand151 } from "citty";
|
|
28772
29055
|
registerSchema({
|
|
28773
29056
|
command: "scheduled-actions.get",
|
|
28774
29057
|
description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
|
|
@@ -28776,7 +29059,7 @@ registerSchema({
|
|
|
28776
29059
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
28777
29060
|
}
|
|
28778
29061
|
});
|
|
28779
|
-
var getCommand3 =
|
|
29062
|
+
var getCommand3 = defineCommand151({
|
|
28780
29063
|
meta: {
|
|
28781
29064
|
name: "get",
|
|
28782
29065
|
description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
|
|
@@ -28813,13 +29096,13 @@ var getCommand3 = defineCommand149({
|
|
|
28813
29096
|
});
|
|
28814
29097
|
|
|
28815
29098
|
// src/commands/scheduled-actions/list.ts
|
|
28816
|
-
import { defineCommand as
|
|
29099
|
+
import { defineCommand as defineCommand152 } from "citty";
|
|
28817
29100
|
registerSchema({
|
|
28818
29101
|
command: "scheduled-actions.list",
|
|
28819
29102
|
description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
|
|
28820
29103
|
args: {}
|
|
28821
29104
|
});
|
|
28822
|
-
var listCommand9 =
|
|
29105
|
+
var listCommand9 = defineCommand152({
|
|
28823
29106
|
meta: {
|
|
28824
29107
|
name: "list",
|
|
28825
29108
|
description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
|
|
@@ -28840,7 +29123,7 @@ var listCommand9 = defineCommand150({
|
|
|
28840
29123
|
});
|
|
28841
29124
|
|
|
28842
29125
|
// src/commands/scheduled-actions/trigger.ts
|
|
28843
|
-
import { defineCommand as
|
|
29126
|
+
import { defineCommand as defineCommand153 } from "citty";
|
|
28844
29127
|
registerSchema({
|
|
28845
29128
|
command: "scheduled-actions.trigger",
|
|
28846
29129
|
description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
|
|
@@ -28848,7 +29131,7 @@ registerSchema({
|
|
|
28848
29131
|
id: { type: "string", description: "Published scheduled action ID", required: true }
|
|
28849
29132
|
}
|
|
28850
29133
|
});
|
|
28851
|
-
var triggerCommand =
|
|
29134
|
+
var triggerCommand = defineCommand153({
|
|
28852
29135
|
meta: {
|
|
28853
29136
|
name: "trigger",
|
|
28854
29137
|
description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
|
|
@@ -28885,7 +29168,7 @@ var triggerCommand = defineCommand151({
|
|
|
28885
29168
|
});
|
|
28886
29169
|
|
|
28887
29170
|
// src/commands/scheduled-actions/update.ts
|
|
28888
|
-
import { defineCommand as
|
|
29171
|
+
import { defineCommand as defineCommand154 } from "citty";
|
|
28889
29172
|
registerSchema({
|
|
28890
29173
|
command: "scheduled-actions.update",
|
|
28891
29174
|
description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
|
|
@@ -28910,7 +29193,7 @@ registerSchema({
|
|
|
28910
29193
|
prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
|
|
28911
29194
|
}
|
|
28912
29195
|
});
|
|
28913
|
-
var updateCommand2 =
|
|
29196
|
+
var updateCommand2 = defineCommand154({
|
|
28914
29197
|
meta: {
|
|
28915
29198
|
name: "update",
|
|
28916
29199
|
description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
|
|
@@ -28981,7 +29264,7 @@ var updateCommand2 = defineCommand152({
|
|
|
28981
29264
|
});
|
|
28982
29265
|
|
|
28983
29266
|
// src/commands/scheduled-actions/index.ts
|
|
28984
|
-
var scheduledActionsCommand =
|
|
29267
|
+
var scheduledActionsCommand = defineCommand155({
|
|
28985
29268
|
meta: {
|
|
28986
29269
|
name: "scheduled-actions",
|
|
28987
29270
|
description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
|
|
@@ -29008,8 +29291,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
29008
29291
|
});
|
|
29009
29292
|
|
|
29010
29293
|
// src/commands/schema.ts
|
|
29011
|
-
import { defineCommand as
|
|
29012
|
-
var schemaCommand =
|
|
29294
|
+
import { defineCommand as defineCommand156 } from "citty";
|
|
29295
|
+
var schemaCommand = defineCommand156({
|
|
29013
29296
|
meta: {
|
|
29014
29297
|
name: "schema",
|
|
29015
29298
|
description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
|
|
@@ -29051,7 +29334,7 @@ var schemaCommand = defineCommand154({
|
|
|
29051
29334
|
});
|
|
29052
29335
|
|
|
29053
29336
|
// src/commands/tags/index.ts
|
|
29054
|
-
import { defineCommand as
|
|
29337
|
+
import { defineCommand as defineCommand157 } from "citty";
|
|
29055
29338
|
|
|
29056
29339
|
// src/commands/tags/shared.ts
|
|
29057
29340
|
function failApi3(err) {
|
|
@@ -29117,7 +29400,7 @@ async function listTags(json) {
|
|
|
29117
29400
|
failApi3(err);
|
|
29118
29401
|
}
|
|
29119
29402
|
}
|
|
29120
|
-
var listCommand10 =
|
|
29403
|
+
var listCommand10 = defineCommand157({
|
|
29121
29404
|
meta: {
|
|
29122
29405
|
name: "list",
|
|
29123
29406
|
description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
|
|
@@ -29136,7 +29419,7 @@ async function listDraft3() {
|
|
|
29136
29419
|
failApi3(err);
|
|
29137
29420
|
}
|
|
29138
29421
|
}
|
|
29139
|
-
var draftCommand3 =
|
|
29422
|
+
var draftCommand3 = defineCommand157({
|
|
29140
29423
|
meta: {
|
|
29141
29424
|
name: "draft",
|
|
29142
29425
|
description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create)."
|
|
@@ -29145,7 +29428,7 @@ var draftCommand3 = defineCommand155({
|
|
|
29145
29428
|
await listDraft3();
|
|
29146
29429
|
}
|
|
29147
29430
|
});
|
|
29148
|
-
var tagsCommand3 =
|
|
29431
|
+
var tagsCommand3 = defineCommand157({
|
|
29149
29432
|
meta: {
|
|
29150
29433
|
name: "tags",
|
|
29151
29434
|
description: `Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) \u2014 production tags plus the changes staged in this chat.
|
|
@@ -29171,10 +29454,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
29171
29454
|
});
|
|
29172
29455
|
|
|
29173
29456
|
// src/commands/testimonials/index.ts
|
|
29174
|
-
import { defineCommand as
|
|
29457
|
+
import { defineCommand as defineCommand161 } from "citty";
|
|
29175
29458
|
|
|
29176
29459
|
// src/commands/testimonials/get.ts
|
|
29177
|
-
import { defineCommand as
|
|
29460
|
+
import { defineCommand as defineCommand158 } from "citty";
|
|
29178
29461
|
registerSchema({
|
|
29179
29462
|
command: "testimonials.get",
|
|
29180
29463
|
description: "Get a single testimonial by ID",
|
|
@@ -29182,7 +29465,7 @@ registerSchema({
|
|
|
29182
29465
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
29183
29466
|
}
|
|
29184
29467
|
});
|
|
29185
|
-
var getCommand4 =
|
|
29468
|
+
var getCommand4 = defineCommand158({
|
|
29186
29469
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
29187
29470
|
args: {
|
|
29188
29471
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -29219,7 +29502,7 @@ var getCommand4 = defineCommand156({
|
|
|
29219
29502
|
});
|
|
29220
29503
|
|
|
29221
29504
|
// src/commands/testimonials/list.ts
|
|
29222
|
-
import { defineCommand as
|
|
29505
|
+
import { defineCommand as defineCommand159 } from "citty";
|
|
29223
29506
|
registerSchema({
|
|
29224
29507
|
command: "testimonials.list",
|
|
29225
29508
|
description: "List testimonials with optional filters.",
|
|
@@ -29249,7 +29532,7 @@ registerSchema({
|
|
|
29249
29532
|
limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
|
|
29250
29533
|
}
|
|
29251
29534
|
});
|
|
29252
|
-
var listCommand11 =
|
|
29535
|
+
var listCommand11 = defineCommand159({
|
|
29253
29536
|
meta: {
|
|
29254
29537
|
name: "list",
|
|
29255
29538
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -29298,7 +29581,7 @@ var listCommand11 = defineCommand157({
|
|
|
29298
29581
|
});
|
|
29299
29582
|
|
|
29300
29583
|
// src/commands/testimonials/search.ts
|
|
29301
|
-
import { defineCommand as
|
|
29584
|
+
import { defineCommand as defineCommand160 } from "citty";
|
|
29302
29585
|
function languageBiasHint(results, requestedLanguage) {
|
|
29303
29586
|
if (requestedLanguage) {
|
|
29304
29587
|
return null;
|
|
@@ -29376,7 +29659,7 @@ function buildSearchRequest(query, args) {
|
|
|
29376
29659
|
}
|
|
29377
29660
|
return body;
|
|
29378
29661
|
}
|
|
29379
|
-
var searchCommand2 =
|
|
29662
|
+
var searchCommand2 = defineCommand160({
|
|
29380
29663
|
meta: {
|
|
29381
29664
|
name: "search",
|
|
29382
29665
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -29432,7 +29715,7 @@ var searchCommand2 = defineCommand158({
|
|
|
29432
29715
|
var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
29433
29716
|
|
|
29434
29717
|
// src/commands/testimonials/index.ts
|
|
29435
|
-
var testimonialsCommand =
|
|
29718
|
+
var testimonialsCommand = defineCommand161({
|
|
29436
29719
|
meta: {
|
|
29437
29720
|
name: "testimonials",
|
|
29438
29721
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -29454,10 +29737,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
29454
29737
|
});
|
|
29455
29738
|
|
|
29456
29739
|
// src/commands/videos/index.ts
|
|
29457
|
-
import { defineCommand as
|
|
29740
|
+
import { defineCommand as defineCommand166 } from "citty";
|
|
29458
29741
|
|
|
29459
29742
|
// src/commands/videos/delete.ts
|
|
29460
|
-
import { defineCommand as
|
|
29743
|
+
import { defineCommand as defineCommand162 } from "citty";
|
|
29461
29744
|
registerSchema({
|
|
29462
29745
|
command: "videos.delete",
|
|
29463
29746
|
description: "Delete a video by ID",
|
|
@@ -29471,7 +29754,7 @@ registerSchema({
|
|
|
29471
29754
|
}
|
|
29472
29755
|
}
|
|
29473
29756
|
});
|
|
29474
|
-
var deleteCommand3 =
|
|
29757
|
+
var deleteCommand3 = defineCommand162({
|
|
29475
29758
|
meta: {
|
|
29476
29759
|
name: "delete",
|
|
29477
29760
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -29512,7 +29795,7 @@ var deleteCommand3 = defineCommand160({
|
|
|
29512
29795
|
});
|
|
29513
29796
|
|
|
29514
29797
|
// src/commands/videos/get.ts
|
|
29515
|
-
import { defineCommand as
|
|
29798
|
+
import { defineCommand as defineCommand163 } from "citty";
|
|
29516
29799
|
registerSchema({
|
|
29517
29800
|
command: "videos.get",
|
|
29518
29801
|
description: "Get a single video by ID",
|
|
@@ -29520,7 +29803,7 @@ registerSchema({
|
|
|
29520
29803
|
id: { type: "string", description: "Video ID", required: true }
|
|
29521
29804
|
}
|
|
29522
29805
|
});
|
|
29523
|
-
var getCommand5 =
|
|
29806
|
+
var getCommand5 = defineCommand163({
|
|
29524
29807
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
29525
29808
|
args: {
|
|
29526
29809
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -29557,7 +29840,7 @@ var getCommand5 = defineCommand161({
|
|
|
29557
29840
|
});
|
|
29558
29841
|
|
|
29559
29842
|
// src/commands/videos/search.ts
|
|
29560
|
-
import { defineCommand as
|
|
29843
|
+
import { defineCommand as defineCommand164 } from "citty";
|
|
29561
29844
|
registerSchema({
|
|
29562
29845
|
command: "videos.search",
|
|
29563
29846
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -29567,7 +29850,7 @@ registerSchema({
|
|
|
29567
29850
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
29568
29851
|
}
|
|
29569
29852
|
});
|
|
29570
|
-
var searchCommand3 =
|
|
29853
|
+
var searchCommand3 = defineCommand164({
|
|
29571
29854
|
meta: {
|
|
29572
29855
|
name: "search",
|
|
29573
29856
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -29617,9 +29900,9 @@ var searchCommand3 = defineCommand162({
|
|
|
29617
29900
|
var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
29618
29901
|
|
|
29619
29902
|
// src/commands/videos/upload.ts
|
|
29620
|
-
import { readFile as
|
|
29903
|
+
import { readFile as readFile23, stat as stat7 } from "fs/promises";
|
|
29621
29904
|
import { extname as extname3 } from "path";
|
|
29622
|
-
import { defineCommand as
|
|
29905
|
+
import { defineCommand as defineCommand165 } from "citty";
|
|
29623
29906
|
var MIME_MAP = {
|
|
29624
29907
|
".mp4": "video/mp4",
|
|
29625
29908
|
".mov": "video/quicktime",
|
|
@@ -29653,7 +29936,7 @@ function detectContentType(filePath) {
|
|
|
29653
29936
|
}
|
|
29654
29937
|
return mime;
|
|
29655
29938
|
}
|
|
29656
|
-
var uploadCommand2 =
|
|
29939
|
+
var uploadCommand2 = defineCommand165({
|
|
29657
29940
|
meta: {
|
|
29658
29941
|
name: "upload",
|
|
29659
29942
|
description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
|
|
@@ -29682,7 +29965,7 @@ var uploadCommand2 = defineCommand163({
|
|
|
29682
29965
|
return;
|
|
29683
29966
|
}
|
|
29684
29967
|
const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
|
|
29685
|
-
const fileBuffer = await
|
|
29968
|
+
const fileBuffer = await readFile23(filePath);
|
|
29686
29969
|
const uploadResponse = await fetch(uploadUrl, {
|
|
29687
29970
|
method: "PUT",
|
|
29688
29971
|
headers: { "Content-Type": contentType },
|
|
@@ -29707,7 +29990,7 @@ var uploadCommand2 = defineCommand163({
|
|
|
29707
29990
|
});
|
|
29708
29991
|
|
|
29709
29992
|
// src/commands/videos/index.ts
|
|
29710
|
-
var videosCommand =
|
|
29993
|
+
var videosCommand = defineCommand166({
|
|
29711
29994
|
meta: {
|
|
29712
29995
|
name: "videos",
|
|
29713
29996
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
|
|
@@ -29731,10 +30014,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
29731
30014
|
});
|
|
29732
30015
|
|
|
29733
30016
|
// src/commands/winning-ads/index.ts
|
|
29734
|
-
import { defineCommand as
|
|
30017
|
+
import { defineCommand as defineCommand179 } from "citty";
|
|
29735
30018
|
|
|
29736
30019
|
// src/commands/winning-ads/advertisers.ts
|
|
29737
|
-
import { defineCommand as
|
|
30020
|
+
import { defineCommand as defineCommand167 } from "citty";
|
|
29738
30021
|
|
|
29739
30022
|
// src/commands/winning-ads/shared.ts
|
|
29740
30023
|
function splitList(value) {
|
|
@@ -29787,7 +30070,7 @@ function advertiserNormalizer(record, full) {
|
|
|
29787
30070
|
last_synced_at: record.last_synced_at ?? null
|
|
29788
30071
|
};
|
|
29789
30072
|
}
|
|
29790
|
-
var advertisersCommand2 =
|
|
30073
|
+
var advertisersCommand2 = defineCommand167({
|
|
29791
30074
|
meta: {
|
|
29792
30075
|
name: "advertisers",
|
|
29793
30076
|
description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
|
|
@@ -29845,7 +30128,7 @@ var advertisersCommand2 = defineCommand165({
|
|
|
29845
30128
|
});
|
|
29846
30129
|
|
|
29847
30130
|
// src/commands/winning-ads/brief.ts
|
|
29848
|
-
import { defineCommand as
|
|
30131
|
+
import { defineCommand as defineCommand168 } from "citty";
|
|
29849
30132
|
registerSchema({
|
|
29850
30133
|
command: "winning-ads.brief",
|
|
29851
30134
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -29891,7 +30174,7 @@ function parseDna(raw) {
|
|
|
29891
30174
|
}
|
|
29892
30175
|
return parsed;
|
|
29893
30176
|
}
|
|
29894
|
-
var briefCommand =
|
|
30177
|
+
var briefCommand = defineCommand168({
|
|
29895
30178
|
meta: {
|
|
29896
30179
|
name: "brief",
|
|
29897
30180
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -29927,7 +30210,7 @@ var briefCommand = defineCommand166({
|
|
|
29927
30210
|
});
|
|
29928
30211
|
|
|
29929
30212
|
// src/commands/winning-ads/content.ts
|
|
29930
|
-
import { defineCommand as
|
|
30213
|
+
import { defineCommand as defineCommand169 } from "citty";
|
|
29931
30214
|
registerSchema({
|
|
29932
30215
|
command: "winning-ads.content",
|
|
29933
30216
|
description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
|
|
@@ -29940,7 +30223,7 @@ registerSchema({
|
|
|
29940
30223
|
}
|
|
29941
30224
|
}
|
|
29942
30225
|
});
|
|
29943
|
-
var contentCommand =
|
|
30226
|
+
var contentCommand = defineCommand169({
|
|
29944
30227
|
meta: {
|
|
29945
30228
|
name: "content",
|
|
29946
30229
|
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -29989,7 +30272,7 @@ var contentCommand = defineCommand167({
|
|
|
29989
30272
|
});
|
|
29990
30273
|
|
|
29991
30274
|
// src/commands/winning-ads/feed.ts
|
|
29992
|
-
import { defineCommand as
|
|
30275
|
+
import { defineCommand as defineCommand170 } from "citty";
|
|
29993
30276
|
function buildFeedParams(input) {
|
|
29994
30277
|
const params = {};
|
|
29995
30278
|
const advertiser = splitList(input.advertiser);
|
|
@@ -30041,7 +30324,7 @@ registerSchema({
|
|
|
30041
30324
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
30042
30325
|
}
|
|
30043
30326
|
});
|
|
30044
|
-
var feedCommand =
|
|
30327
|
+
var feedCommand = defineCommand170({
|
|
30045
30328
|
meta: {
|
|
30046
30329
|
name: "feed",
|
|
30047
30330
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -30126,7 +30409,7 @@ var feedCommand = defineCommand168({
|
|
|
30126
30409
|
});
|
|
30127
30410
|
|
|
30128
30411
|
// src/commands/winning-ads/follow.ts
|
|
30129
|
-
import { defineCommand as
|
|
30412
|
+
import { defineCommand as defineCommand171 } from "citty";
|
|
30130
30413
|
var PLATFORMS = ["meta", "linkedin"];
|
|
30131
30414
|
registerSchema({
|
|
30132
30415
|
command: "winning-ads.follow",
|
|
@@ -30141,7 +30424,7 @@ registerSchema({
|
|
|
30141
30424
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
30142
30425
|
}
|
|
30143
30426
|
});
|
|
30144
|
-
var followCommand =
|
|
30427
|
+
var followCommand = defineCommand171({
|
|
30145
30428
|
meta: {
|
|
30146
30429
|
name: "follow",
|
|
30147
30430
|
description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
|
|
@@ -30188,7 +30471,7 @@ var followCommand = defineCommand169({
|
|
|
30188
30471
|
});
|
|
30189
30472
|
|
|
30190
30473
|
// src/commands/winning-ads/follow-competitors.ts
|
|
30191
|
-
import { defineCommand as
|
|
30474
|
+
import { defineCommand as defineCommand172 } from "citty";
|
|
30192
30475
|
var PLATFORMS2 = ["meta", "linkedin"];
|
|
30193
30476
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
30194
30477
|
function buildFollowBatchBody(input) {
|
|
@@ -30221,7 +30504,7 @@ registerSchema({
|
|
|
30221
30504
|
}
|
|
30222
30505
|
}
|
|
30223
30506
|
});
|
|
30224
|
-
var followCompetitorsCommand =
|
|
30507
|
+
var followCompetitorsCommand = defineCommand172({
|
|
30225
30508
|
meta: {
|
|
30226
30509
|
name: "follow-competitors",
|
|
30227
30510
|
description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
|
|
@@ -30296,7 +30579,7 @@ var followCompetitorsCommand = defineCommand170({
|
|
|
30296
30579
|
});
|
|
30297
30580
|
|
|
30298
30581
|
// src/commands/winning-ads/following.ts
|
|
30299
|
-
import { defineCommand as
|
|
30582
|
+
import { defineCommand as defineCommand173 } from "citty";
|
|
30300
30583
|
registerSchema({
|
|
30301
30584
|
command: "winning-ads.following",
|
|
30302
30585
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
|
|
@@ -30329,7 +30612,7 @@ function followingNormalizer(record, full) {
|
|
|
30329
30612
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
30330
30613
|
};
|
|
30331
30614
|
}
|
|
30332
|
-
var followingCommand =
|
|
30615
|
+
var followingCommand = defineCommand173({
|
|
30333
30616
|
meta: {
|
|
30334
30617
|
name: "following",
|
|
30335
30618
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
|
|
@@ -30364,7 +30647,7 @@ var followingCommand = defineCommand171({
|
|
|
30364
30647
|
});
|
|
30365
30648
|
|
|
30366
30649
|
// src/commands/winning-ads/patterns.ts
|
|
30367
|
-
import { defineCommand as
|
|
30650
|
+
import { defineCommand as defineCommand174 } from "citty";
|
|
30368
30651
|
registerSchema({
|
|
30369
30652
|
command: "winning-ads.patterns",
|
|
30370
30653
|
description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
|
|
@@ -30403,7 +30686,7 @@ function discriminatorRow(record) {
|
|
|
30403
30686
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
30404
30687
|
};
|
|
30405
30688
|
}
|
|
30406
|
-
var patternsCommand =
|
|
30689
|
+
var patternsCommand = defineCommand174({
|
|
30407
30690
|
meta: {
|
|
30408
30691
|
name: "patterns",
|
|
30409
30692
|
description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
|
|
@@ -30459,7 +30742,7 @@ var patternsCommand = defineCommand172({
|
|
|
30459
30742
|
});
|
|
30460
30743
|
|
|
30461
30744
|
// src/commands/winning-ads/search.ts
|
|
30462
|
-
import { defineCommand as
|
|
30745
|
+
import { defineCommand as defineCommand175 } from "citty";
|
|
30463
30746
|
registerSchema({
|
|
30464
30747
|
command: "winning-ads.search",
|
|
30465
30748
|
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.",
|
|
@@ -30567,7 +30850,7 @@ function buildSearchBody(args) {
|
|
|
30567
30850
|
}
|
|
30568
30851
|
return body;
|
|
30569
30852
|
}
|
|
30570
|
-
var searchCommand4 =
|
|
30853
|
+
var searchCommand4 = defineCommand175({
|
|
30571
30854
|
meta: {
|
|
30572
30855
|
name: "search",
|
|
30573
30856
|
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"
|
|
@@ -30682,7 +30965,7 @@ var searchCommand4 = defineCommand173({
|
|
|
30682
30965
|
});
|
|
30683
30966
|
|
|
30684
30967
|
// src/commands/winning-ads/seeds.ts
|
|
30685
|
-
import { defineCommand as
|
|
30968
|
+
import { defineCommand as defineCommand176 } from "citty";
|
|
30686
30969
|
function leanRow(r) {
|
|
30687
30970
|
return {
|
|
30688
30971
|
key: r.key,
|
|
@@ -30710,7 +30993,7 @@ function makeSeedCommand(opts) {
|
|
|
30710
30993
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
30711
30994
|
}
|
|
30712
30995
|
});
|
|
30713
|
-
return
|
|
30996
|
+
return defineCommand176({
|
|
30714
30997
|
meta: { name: opts.name, description: opts.description },
|
|
30715
30998
|
args: {
|
|
30716
30999
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -30759,7 +31042,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
30759
31042
|
});
|
|
30760
31043
|
|
|
30761
31044
|
// src/commands/winning-ads/unfollow.ts
|
|
30762
|
-
import { defineCommand as
|
|
31045
|
+
import { defineCommand as defineCommand177 } from "citty";
|
|
30763
31046
|
registerSchema({
|
|
30764
31047
|
command: "winning-ads.unfollow",
|
|
30765
31048
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -30767,7 +31050,7 @@ registerSchema({
|
|
|
30767
31050
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
30768
31051
|
}
|
|
30769
31052
|
});
|
|
30770
|
-
var unfollowCommand =
|
|
31053
|
+
var unfollowCommand = defineCommand177({
|
|
30771
31054
|
meta: {
|
|
30772
31055
|
name: "unfollow",
|
|
30773
31056
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -30788,7 +31071,7 @@ var unfollowCommand = defineCommand175({
|
|
|
30788
31071
|
});
|
|
30789
31072
|
|
|
30790
31073
|
// src/commands/winning-ads/winners.ts
|
|
30791
|
-
import { defineCommand as
|
|
31074
|
+
import { defineCommand as defineCommand178 } from "citty";
|
|
30792
31075
|
registerSchema({
|
|
30793
31076
|
command: "winning-ads.winners",
|
|
30794
31077
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -30798,7 +31081,7 @@ registerSchema({
|
|
|
30798
31081
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
30799
31082
|
}
|
|
30800
31083
|
});
|
|
30801
|
-
var winnersCommand =
|
|
31084
|
+
var winnersCommand = defineCommand178({
|
|
30802
31085
|
meta: {
|
|
30803
31086
|
name: "winners",
|
|
30804
31087
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -30848,7 +31131,7 @@ var winnersCommand = defineCommand176({
|
|
|
30848
31131
|
});
|
|
30849
31132
|
|
|
30850
31133
|
// src/commands/winning-ads/index.ts
|
|
30851
|
-
var winningAdsCommand =
|
|
31134
|
+
var winningAdsCommand = defineCommand179({
|
|
30852
31135
|
meta: {
|
|
30853
31136
|
name: "winning-ads",
|
|
30854
31137
|
description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
|
|
@@ -30920,7 +31203,7 @@ function getCliVersion() {
|
|
|
30920
31203
|
}
|
|
30921
31204
|
|
|
30922
31205
|
// src/cli.ts
|
|
30923
|
-
var main =
|
|
31206
|
+
var main = defineCommand180({
|
|
30924
31207
|
meta: {
|
|
30925
31208
|
name: "baker",
|
|
30926
31209
|
version: getCliVersion(),
|
|
@@ -30936,6 +31219,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
30936
31219
|
actions: actionsCommand,
|
|
30937
31220
|
"scheduled-actions": scheduledActionsCommand,
|
|
30938
31221
|
ads: adsCommand2,
|
|
31222
|
+
brand: brandCommand,
|
|
30939
31223
|
ga4: ga4Command,
|
|
30940
31224
|
gsc: gscCommand,
|
|
30941
31225
|
research: researchCommand,
|