@koda-sl/baker-cli 0.408.0 → 0.410.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -23
- package/dist/{chunk-N72QYDJY.js → chunk-ISCXDKWB.js} +25 -8
- package/dist/{chunk-N72QYDJY.js.map → chunk-ISCXDKWB.js.map} +1 -1
- package/dist/{chunk-5X5LUMVV.js → chunk-ISJOVAJP.js} +2 -2
- package/dist/{chunk-KMCVSEGU.js → chunk-KTMFHBN6.js} +3 -3
- package/dist/chunk-KTMFHBN6.js.map +1 -0
- package/dist/{chunk-WQE4SRLY.js → chunk-S5BY45H7.js} +3 -3
- package/dist/{chunk-X37GPJ54.js → chunk-VKWHVMMD.js} +3 -3
- package/dist/{chunk-3ZN2DUBU.js → chunk-ZIOW64BL.js} +4 -4
- package/dist/cli.js +750 -685
- package/dist/cli.js.map +1 -1
- package/dist/{client-BPKOHQWA.js → client-AY6TPFQ4.js} +4 -4
- package/dist/engine/index.js +3 -3
- package/dist/{env-B7V77K6K.js → env-CWEGTJF3.js} +2 -2
- package/dist/{output-5M2QPMKZ.js → output-EPF3HHJ4.js} +5 -5
- package/dist/{shared-OEQKSDWO.js → shared-53G34I3U.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-KMCVSEGU.js.map +0 -1
- /package/dist/{chunk-5X5LUMVV.js.map → chunk-ISJOVAJP.js.map} +0 -0
- /package/dist/{chunk-WQE4SRLY.js.map → chunk-S5BY45H7.js.map} +0 -0
- /package/dist/{chunk-X37GPJ54.js.map → chunk-VKWHVMMD.js.map} +0 -0
- /package/dist/{chunk-3ZN2DUBU.js.map → chunk-ZIOW64BL.js.map} +0 -0
- /package/dist/{client-BPKOHQWA.js.map → client-AY6TPFQ4.js.map} +0 -0
- /package/dist/{env-B7V77K6K.js.map → env-CWEGTJF3.js.map} +0 -0
- /package/dist/{output-5M2QPMKZ.js.map → output-EPF3HHJ4.js.map} +0 -0
- /package/dist/{shared-OEQKSDWO.js.map → shared-53G34I3U.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
withoutCastName,
|
|
98
98
|
wordCount,
|
|
99
99
|
ytDlpBlockSignal
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-KTMFHBN6.js";
|
|
101
101
|
import {
|
|
102
102
|
NAME_CONTAINS_ARG,
|
|
103
103
|
csvOrJson,
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
resolveEffectiveStatus,
|
|
109
109
|
statusFilterHints,
|
|
110
110
|
todayIso
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-ZIOW64BL.js";
|
|
112
112
|
import {
|
|
113
113
|
buildQueryCacheKey,
|
|
114
114
|
cacheGet,
|
|
@@ -124,19 +124,19 @@ import {
|
|
|
124
124
|
writeAdsJson,
|
|
125
125
|
writeAdsOutput,
|
|
126
126
|
writeJsonEnvelope
|
|
127
|
-
} from "./chunk-
|
|
127
|
+
} from "./chunk-VKWHVMMD.js";
|
|
128
128
|
import {
|
|
129
129
|
ApiError,
|
|
130
130
|
apiGet,
|
|
131
131
|
apiGetWithHeaders,
|
|
132
132
|
apiPost,
|
|
133
133
|
validateConvexId
|
|
134
|
-
} from "./chunk-
|
|
134
|
+
} from "./chunk-S5BY45H7.js";
|
|
135
135
|
import {
|
|
136
136
|
installExitRecord,
|
|
137
137
|
installStreamTaps,
|
|
138
138
|
logInvocation
|
|
139
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-ISJOVAJP.js";
|
|
140
140
|
import {
|
|
141
141
|
__commonJS,
|
|
142
142
|
__esm,
|
|
@@ -171,7 +171,7 @@ import {
|
|
|
171
171
|
winningAdNormalizer,
|
|
172
172
|
writeJson,
|
|
173
173
|
writeOutput
|
|
174
|
-
} from "./chunk-
|
|
174
|
+
} from "./chunk-ISCXDKWB.js";
|
|
175
175
|
|
|
176
176
|
// ../../node_modules/.pnpm/i18n-iso-countries@7.14.0/node_modules/i18n-iso-countries/langs/af.json
|
|
177
177
|
var require_af = __commonJS({
|
|
@@ -21715,7 +21715,7 @@ var require_entry_node = __commonJS({
|
|
|
21715
21715
|
});
|
|
21716
21716
|
|
|
21717
21717
|
// src/cli.ts
|
|
21718
|
-
import { defineCommand as
|
|
21718
|
+
import { defineCommand as defineCommand278, runMain } from "citty";
|
|
21719
21719
|
|
|
21720
21720
|
// src/cache-flag.ts
|
|
21721
21721
|
var NO_CACHE_ARG = {
|
|
@@ -22118,6 +22118,7 @@ var validatedHardFiltersSchema = winnersHardFiltersSchema.extend({
|
|
|
22118
22118
|
first_seen_after: firstSeenBoundSchema.optional(),
|
|
22119
22119
|
first_seen_before: firstSeenBoundSchema.optional()
|
|
22120
22120
|
});
|
|
22121
|
+
var winnersSortSchema = z2.enum(["winner", "longest_running", "newest"]);
|
|
22121
22122
|
var winnersSearchFields = z2.object({
|
|
22122
22123
|
free_text_query: z2.string().max(500).optional(),
|
|
22123
22124
|
ref_ad_id: z2.string().optional(),
|
|
@@ -22133,6 +22134,10 @@ var winnersSearchFields = z2.object({
|
|
|
22133
22134
|
// One page picked for variety (different brands, containers and hooks)
|
|
22134
22135
|
// instead of rank order: a reference set for N concepts. Ignores offset.
|
|
22135
22136
|
diverse: z2.boolean().optional(),
|
|
22137
|
+
// The order of a browse (no query): by winner score (default), by how long
|
|
22138
|
+
// the ad has run, or by when it was first seen. A query ranks by relevance
|
|
22139
|
+
// blended with winner score, so `sort` only means something without one.
|
|
22140
|
+
sort: winnersSortSchema.optional(),
|
|
22136
22141
|
hard_filters: winnersHardFiltersSchema.optional(),
|
|
22137
22142
|
// `score_breakdown` surfaces the "why it ranked" panel; facets drive the
|
|
22138
22143
|
// filter rail. thumbnails/full_dna_url are opt-in extras.
|
|
@@ -22140,9 +22145,11 @@ var winnersSearchFields = z2.object({
|
|
|
22140
22145
|
});
|
|
22141
22146
|
var hasSearchInput = (b) => b.free_text_query !== void 0 || b.ref_ad_id !== void 0 || b.hard_filters !== void 0;
|
|
22142
22147
|
var SEARCH_INPUT_MESSAGE = "Provide free_text_query, ref_ad_id, or hard_filters";
|
|
22143
|
-
var winnersSearchRequestSchema = winnersSearchFields.refine(hasSearchInput, { message: SEARCH_INPUT_MESSAGE });
|
|
22144
22148
|
var winnerSearchScopeSchema = z2.enum(["all", "followed", "followed-first"]);
|
|
22145
|
-
var scopedWinnersSearchRequestSchema = winnersSearchFields.extend({
|
|
22149
|
+
var scopedWinnersSearchRequestSchema = winnersSearchFields.extend({
|
|
22150
|
+
scope: winnerSearchScopeSchema.default("followed-first"),
|
|
22151
|
+
hard_filters: validatedHardFiltersSchema.optional()
|
|
22152
|
+
}).refine(hasSearchInput, { message: SEARCH_INPUT_MESSAGE }).refine((b) => b.scope !== "followed-first" || !b.offset, {
|
|
22146
22153
|
message: "offset is not supported with scope followed-first; raise limit instead"
|
|
22147
22154
|
});
|
|
22148
22155
|
var winningAdDnaSchema = z2.object({
|
|
@@ -22231,7 +22238,12 @@ var winnersSearchResponseSchema = z2.object({
|
|
|
22231
22238
|
country_coverage: z2.array(z2.object({ code: z2.string(), ads: z2.number() })).nullable()
|
|
22232
22239
|
});
|
|
22233
22240
|
var winnerSourceSchema = z2.enum(["followed", "library"]);
|
|
22234
|
-
var referenceUsesSchema = z2.object({
|
|
22241
|
+
var referenceUsesSchema = z2.object({
|
|
22242
|
+
companies: z2.number(),
|
|
22243
|
+
ours: z2.boolean(),
|
|
22244
|
+
approved: z2.number(),
|
|
22245
|
+
rejected: z2.number()
|
|
22246
|
+
});
|
|
22235
22247
|
var scopedWinningAdSchema = winningAdSchema.extend({
|
|
22236
22248
|
source: winnerSourceSchema,
|
|
22237
22249
|
baker_uses: referenceUsesSchema.optional()
|
|
@@ -22277,10 +22289,6 @@ var advertiserConvergenceSchema = z2.object({
|
|
|
22277
22289
|
iteration_velocity_30d: z2.number().nullable(),
|
|
22278
22290
|
last_refresh_completed_at: z2.string().nullable()
|
|
22279
22291
|
});
|
|
22280
|
-
var resolvePreviewRequestSchema = z2.object({
|
|
22281
|
-
input: z2.string().min(1),
|
|
22282
|
-
platform: adLibraryPlatformSchema
|
|
22283
|
-
});
|
|
22284
22292
|
var resolvePreviewSchema = z2.object({
|
|
22285
22293
|
platform: adLibraryPlatformSchema,
|
|
22286
22294
|
external_id: z2.string(),
|
|
@@ -22365,11 +22373,6 @@ var unfollowRequestSchema = z2.object({
|
|
|
22365
22373
|
advertiser_id: z2.string().optional()
|
|
22366
22374
|
});
|
|
22367
22375
|
var unfollowResponseSchema = z2.object({ removed: z2.boolean() });
|
|
22368
|
-
var advertiserWinnersRequestSchema = z2.object({
|
|
22369
|
-
advertiser_id: z2.string().min(1),
|
|
22370
|
-
top: z2.number().int().min(1).max(100).optional(),
|
|
22371
|
-
platform: adLibraryPlatformSchema.optional()
|
|
22372
|
-
});
|
|
22373
22376
|
var advertiserWinnersResponseSchema = z2.object({
|
|
22374
22377
|
advertiser_id: z2.string(),
|
|
22375
22378
|
winners: z2.array(winningAdSchema)
|
|
@@ -22389,7 +22392,9 @@ var feedRequestSchema = z2.object({
|
|
|
22389
22392
|
limit: z2.number().int().min(1).max(200).optional()
|
|
22390
22393
|
});
|
|
22391
22394
|
var feedResponseSchema = z2.object({
|
|
22392
|
-
|
|
22395
|
+
// The feed only ever reads followed brands, so every winner is `followed`:
|
|
22396
|
+
// the same `source` a scoped search carries, so the surfaces agree.
|
|
22397
|
+
winners: z2.array(winningAdSchema.extend({ source: winnerSourceSchema })),
|
|
22393
22398
|
// How many brands the company follows (regardless of the trim).
|
|
22394
22399
|
following_count: z2.number(),
|
|
22395
22400
|
// The advertiser ids actually queried (follows ∩ requested subset).
|
|
@@ -22398,39 +22403,6 @@ var feedResponseSchema = z2.object({
|
|
|
22398
22403
|
// followed them yet.
|
|
22399
22404
|
unknown_requested: z2.array(z2.string())
|
|
22400
22405
|
});
|
|
22401
|
-
var briefRequestSchema = z2.object({
|
|
22402
|
-
// Partial DNA describing the target creative (all optional); the service
|
|
22403
|
-
// pulls its own strategically-similar references.
|
|
22404
|
-
target_dna: z2.record(z2.string(), z2.unknown()).optional(),
|
|
22405
|
-
k_reference_ads: z2.number().int().min(1).max(50).optional(),
|
|
22406
|
-
notes: z2.string().optional()
|
|
22407
|
-
});
|
|
22408
|
-
var briefResponseSchema = z2.object({
|
|
22409
|
-
brief_markdown: z2.string(),
|
|
22410
|
-
reference_ad_ids: z2.array(z2.string())
|
|
22411
|
-
});
|
|
22412
|
-
var cohortSelectorSchema = z2.object({
|
|
22413
|
-
hard_filters: winnersHardFiltersSchema.optional(),
|
|
22414
|
-
ad_ids: z2.array(z2.string()).optional()
|
|
22415
|
-
});
|
|
22416
|
-
var patternsRequestSchema = z2.object({
|
|
22417
|
-
cohort_a: cohortSelectorSchema,
|
|
22418
|
-
cohort_b: cohortSelectorSchema,
|
|
22419
|
-
top_n: z2.number().int().min(1).max(50).optional()
|
|
22420
|
-
});
|
|
22421
|
-
var patternDiscriminatorSchema = z2.object({
|
|
22422
|
-
field: z2.string(),
|
|
22423
|
-
method: z2.string(),
|
|
22424
|
-
score: z2.number(),
|
|
22425
|
-
direction: z2.enum(["favors_a", "favors_b"]),
|
|
22426
|
-
top_values_a: z2.array(z2.string()).nullable(),
|
|
22427
|
-
top_values_b: z2.array(z2.string()).nullable()
|
|
22428
|
-
});
|
|
22429
|
-
var patternsResponseSchema = z2.object({
|
|
22430
|
-
cohort_a_size: z2.number(),
|
|
22431
|
-
cohort_b_size: z2.number(),
|
|
22432
|
-
discriminators: z2.array(patternDiscriminatorSchema)
|
|
22433
|
-
});
|
|
22434
22406
|
var seedCatalogFieldSchema = z2.enum(["hook_archetype", "mechanism_type", "static_format_pattern"]);
|
|
22435
22407
|
var seedCatalogRequestSchema = z2.object({
|
|
22436
22408
|
field: seedCatalogFieldSchema,
|
|
@@ -34520,16 +34492,16 @@ async function runChatgptRead(reference, read, hintsFor4) {
|
|
|
34520
34492
|
}
|
|
34521
34493
|
}
|
|
34522
34494
|
var TRUNCATED_HINT = "These rows are the first 2,000, not the whole account \u2014 narrow the read (--campaign-id, --ad-group-id) before counting them or concluding something is missing.";
|
|
34523
|
-
async function getChatgptList(
|
|
34524
|
-
const { data, headers } = await apiGetWithHeaders(
|
|
34495
|
+
async function getChatgptList(path58, params) {
|
|
34496
|
+
const { data, headers } = await apiGetWithHeaders(path58, params);
|
|
34525
34497
|
return { data, truncated: headers.get(CHATGPT_ADS_TRUNCATED_HEADER) === "true" };
|
|
34526
34498
|
}
|
|
34527
|
-
async function runChatgptListRead(reference,
|
|
34499
|
+
async function runChatgptListRead(reference, path58, params, hintsFor4) {
|
|
34528
34500
|
let truncated = false;
|
|
34529
34501
|
await runChatgptRead(
|
|
34530
34502
|
reference,
|
|
34531
34503
|
async () => {
|
|
34532
|
-
const read = await getChatgptList(
|
|
34504
|
+
const read = await getChatgptList(path58, params);
|
|
34533
34505
|
truncated = read.truncated;
|
|
34534
34506
|
return read.data;
|
|
34535
34507
|
},
|
|
@@ -35334,8 +35306,8 @@ var MEMBER_ARGS = {
|
|
|
35334
35306
|
},
|
|
35335
35307
|
country: { type: "string", description: "Country of phones written without a +code, e.g. ES" }
|
|
35336
35308
|
};
|
|
35337
|
-
function membersFromArgs(args8,
|
|
35338
|
-
const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(
|
|
35309
|
+
function membersFromArgs(args8, readFile43 = (path58) => readFileSync2(path58, "utf8")) {
|
|
35310
|
+
const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(readFile43(args8.file)) : { emails: [], phones: [] };
|
|
35339
35311
|
const given = [...csvArg(args8.emails) ?? [], ...fromFile.emails];
|
|
35340
35312
|
const givenPhones = [...csvArg(args8.phones) ?? [], ...fromFile.phones];
|
|
35341
35313
|
const members = hashedMembers({
|
|
@@ -38709,9 +38681,9 @@ var COPY_LIST_FLAG = {
|
|
|
38709
38681
|
descriptions: "--descriptions",
|
|
38710
38682
|
longHeadlines: "--long-headlines"
|
|
38711
38683
|
};
|
|
38712
|
-
function valueAt(root,
|
|
38684
|
+
function valueAt(root, path58) {
|
|
38713
38685
|
let node = root;
|
|
38714
|
-
for (const key of
|
|
38686
|
+
for (const key of path58) {
|
|
38715
38687
|
if (node === null || typeof node !== "object") return void 0;
|
|
38716
38688
|
node = node[key];
|
|
38717
38689
|
}
|
|
@@ -38944,11 +38916,11 @@ function parseKeywordEntry(raw, defaultMatch) {
|
|
|
38944
38916
|
function rawTextEntries(value2, rawArgs) {
|
|
38945
38917
|
return repeatedValues(rawArgs, "text", value2).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
38946
38918
|
}
|
|
38947
|
-
function rawFileEntries(
|
|
38948
|
-
if (typeof
|
|
38919
|
+
function rawFileEntries(path58) {
|
|
38920
|
+
if (typeof path58 !== "string" || path58.length === 0) {
|
|
38949
38921
|
return [];
|
|
38950
38922
|
}
|
|
38951
|
-
return readFileSync4(
|
|
38923
|
+
return readFileSync4(path58, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
38952
38924
|
}
|
|
38953
38925
|
function keywordEntries(args8, rawArgs) {
|
|
38954
38926
|
const defaultMatch = typeof args8["match-type"] === "string" ? args8["match-type"].toUpperCase() : void 0;
|
|
@@ -38971,14 +38943,14 @@ function keywordEntries(args8, rawArgs) {
|
|
|
38971
38943
|
}
|
|
38972
38944
|
return entries;
|
|
38973
38945
|
}
|
|
38974
|
-
function loadJsonFileArg(
|
|
38975
|
-
if (typeof
|
|
38946
|
+
function loadJsonFileArg(path58) {
|
|
38947
|
+
if (typeof path58 !== "string" || path58.length === 0) {
|
|
38976
38948
|
return {};
|
|
38977
38949
|
}
|
|
38978
|
-
const inline =
|
|
38979
|
-
const source = inline ? "inline JSON" :
|
|
38950
|
+
const inline = path58.trimStart().startsWith("{");
|
|
38951
|
+
const source = inline ? "inline JSON" : path58;
|
|
38980
38952
|
try {
|
|
38981
|
-
const parsed = JSON.parse(inline ?
|
|
38953
|
+
const parsed = JSON.parse(inline ? path58 : readFileSync4(path58, "utf8"));
|
|
38982
38954
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
38983
38955
|
failWriteValidation2(`${source} must contain a JSON object`);
|
|
38984
38956
|
}
|
|
@@ -39115,10 +39087,10 @@ async function stageUpdate(kind, customerId, target2, payload, hints2) {
|
|
|
39115
39087
|
async function stageTarget(kind, customerId, target2, hints2) {
|
|
39116
39088
|
await stageGoogleOp({ kind, customerId, target: target2 }, hints2);
|
|
39117
39089
|
}
|
|
39118
|
-
async function draftAction(
|
|
39090
|
+
async function draftAction(path58, body, chat) {
|
|
39119
39091
|
try {
|
|
39120
39092
|
const chatId = resolveChatId(chat);
|
|
39121
|
-
const response = await apiPost(
|
|
39093
|
+
const response = await apiPost(path58, { chatId, ...body });
|
|
39122
39094
|
writeJsonEnvelope(response);
|
|
39123
39095
|
} catch (err) {
|
|
39124
39096
|
handleGoogleError(err);
|
|
@@ -41538,14 +41510,14 @@ function mappingRowProblem(row) {
|
|
|
41538
41510
|
|
|
41539
41511
|
// ../api/src/event-sources/template.ts
|
|
41540
41512
|
var MENTION = /\{\{\s*([^{}]+?)\s*\}\}/g;
|
|
41541
|
-
function mentionOf(
|
|
41542
|
-
return `{{${
|
|
41513
|
+
function mentionOf(path58) {
|
|
41514
|
+
return `{{${path58}}}`;
|
|
41543
41515
|
}
|
|
41544
41516
|
function templateMentions(template) {
|
|
41545
41517
|
const seen = [];
|
|
41546
41518
|
for (const match of template.matchAll(MENTION)) {
|
|
41547
|
-
const
|
|
41548
|
-
if (
|
|
41519
|
+
const path58 = (match[1] ?? "").trim();
|
|
41520
|
+
if (path58 !== "" && !seen.includes(path58)) seen.push(path58);
|
|
41549
41521
|
}
|
|
41550
41522
|
return seen;
|
|
41551
41523
|
}
|
|
@@ -44920,17 +44892,17 @@ function ignoredColumnHints(columns) {
|
|
|
44920
44892
|
`Ignored ${columns.length} column(s) that are not part of a conversion: ${columns.join(", ")}. A conversion carries an order id, a date, and something to match on: a click id (gclid / gbraid / wbraid), an email, a phone, or all four of givenName + familyName + country + postalCode. Optionally a value + currency, conversionCount, and eventSource (WEB / APP / IN_STORE / PHONE / OTHER). A column named just \`Name\` is ignored on purpose \u2014 Google needs the first and last name separately, so split it before re-running.`
|
|
44921
44893
|
];
|
|
44922
44894
|
}
|
|
44923
|
-
function readRowsFile(
|
|
44924
|
-
const inline =
|
|
44895
|
+
function readRowsFile(path58) {
|
|
44896
|
+
const inline = path58.trimStart().startsWith("[") || path58.trimStart().startsWith("{");
|
|
44925
44897
|
let text2;
|
|
44926
44898
|
try {
|
|
44927
|
-
text2 = inline ?
|
|
44899
|
+
text2 = inline ? path58 : readFileSync6(path58, "utf8");
|
|
44928
44900
|
} catch (err) {
|
|
44929
|
-
failWriteValidation2(`could not read ${
|
|
44901
|
+
failWriteValidation2(`could not read ${path58}: ${err instanceof Error ? err.message : String(err)}`);
|
|
44930
44902
|
}
|
|
44931
44903
|
const parsed = parseConversionRowsText(text2);
|
|
44932
44904
|
if (!parsed.ok) {
|
|
44933
|
-
failWriteValidation2(`${inline ? "the conversions passed inline" :
|
|
44905
|
+
failWriteValidation2(`${inline ? "the conversions passed inline" : path58}: ${parsed.error}`);
|
|
44934
44906
|
}
|
|
44935
44907
|
return { rows: parsed.rows, ignoredColumns: parsed.ignoredColumns };
|
|
44936
44908
|
}
|
|
@@ -46831,19 +46803,19 @@ function failWriteValidation3(message) {
|
|
|
46831
46803
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
46832
46804
|
process.exit(1);
|
|
46833
46805
|
}
|
|
46834
|
-
function loadJsonFileArg2(
|
|
46835
|
-
if (typeof
|
|
46806
|
+
function loadJsonFileArg2(path58) {
|
|
46807
|
+
if (typeof path58 !== "string" || path58.length === 0) {
|
|
46836
46808
|
return {};
|
|
46837
46809
|
}
|
|
46838
46810
|
try {
|
|
46839
|
-
const parsed = JSON.parse(readFileSync7(
|
|
46811
|
+
const parsed = JSON.parse(readFileSync7(path58, "utf8"));
|
|
46840
46812
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
46841
|
-
failWriteValidation3(`${
|
|
46813
|
+
failWriteValidation3(`${path58} must contain a JSON object`);
|
|
46842
46814
|
}
|
|
46843
46815
|
return parsed;
|
|
46844
46816
|
} catch (err) {
|
|
46845
46817
|
if (err instanceof SyntaxError) {
|
|
46846
|
-
failWriteValidation3(`${
|
|
46818
|
+
failWriteValidation3(`${path58} is not valid JSON: ${err.message}`);
|
|
46847
46819
|
}
|
|
46848
46820
|
throw err;
|
|
46849
46821
|
}
|
|
@@ -46928,15 +46900,15 @@ function parseLocaleFlag(value2) {
|
|
|
46928
46900
|
}
|
|
46929
46901
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
46930
46902
|
}
|
|
46931
|
-
function loadTargetingFileArg(
|
|
46932
|
-
if (typeof
|
|
46903
|
+
function loadTargetingFileArg(path58) {
|
|
46904
|
+
if (typeof path58 !== "string" || path58.length === 0) {
|
|
46933
46905
|
return void 0;
|
|
46934
46906
|
}
|
|
46935
|
-
const parsed = loadJsonFileArg2(
|
|
46907
|
+
const parsed = loadJsonFileArg2(path58);
|
|
46936
46908
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
46937
46909
|
if (!criteria.include) {
|
|
46938
46910
|
failWriteValidation3(
|
|
46939
|
-
`${
|
|
46911
|
+
`${path58} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
46940
46912
|
);
|
|
46941
46913
|
}
|
|
46942
46914
|
return criteria;
|
|
@@ -46971,14 +46943,14 @@ function parseCsvLine(line) {
|
|
|
46971
46943
|
cells.push(current);
|
|
46972
46944
|
return cells.map((cell3) => cell3.trim());
|
|
46973
46945
|
}
|
|
46974
|
-
function parseListFileArg(
|
|
46975
|
-
if (typeof
|
|
46946
|
+
function parseListFileArg(path58, maxRows) {
|
|
46947
|
+
if (typeof path58 !== "string" || path58.length === 0) {
|
|
46976
46948
|
return void 0;
|
|
46977
46949
|
}
|
|
46978
|
-
const raw = readFileSync7(
|
|
46950
|
+
const raw = readFileSync7(path58, "utf8");
|
|
46979
46951
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
46980
46952
|
if (lines.length < 2) {
|
|
46981
|
-
failWriteValidation3(`${
|
|
46953
|
+
failWriteValidation3(`${path58} needs a header row and at least one data row`);
|
|
46982
46954
|
}
|
|
46983
46955
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
46984
46956
|
const rows = [];
|
|
@@ -46997,7 +46969,7 @@ function parseListFileArg(path59, maxRows) {
|
|
|
46997
46969
|
}
|
|
46998
46970
|
}
|
|
46999
46971
|
if (rows.length > maxRows) {
|
|
47000
|
-
failWriteValidation3(`${
|
|
46972
|
+
failWriteValidation3(`${path58} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
47001
46973
|
}
|
|
47002
46974
|
return { columns, rows };
|
|
47003
46975
|
}
|
|
@@ -47093,11 +47065,11 @@ function readPositionals(args8) {
|
|
|
47093
47065
|
function splitIdList(raw) {
|
|
47094
47066
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
47095
47067
|
}
|
|
47096
|
-
function idsFileEntries(
|
|
47097
|
-
if (typeof
|
|
47068
|
+
function idsFileEntries(path58) {
|
|
47069
|
+
if (typeof path58 !== "string" || path58.length === 0) {
|
|
47098
47070
|
return [];
|
|
47099
47071
|
}
|
|
47100
|
-
return readFileSync7(
|
|
47072
|
+
return readFileSync7(path58, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
47101
47073
|
}
|
|
47102
47074
|
function requireTargets2(args8, entity) {
|
|
47103
47075
|
const positionals = readPositionals(args8);
|
|
@@ -49858,9 +49830,9 @@ import { defineCommand as defineCommand64 } from "citty";
|
|
|
49858
49830
|
function asRecord(value2) {
|
|
49859
49831
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) ? value2 : void 0;
|
|
49860
49832
|
}
|
|
49861
|
-
function readPath2(row,
|
|
49833
|
+
function readPath2(row, path58) {
|
|
49862
49834
|
let current = row;
|
|
49863
|
-
for (const segment of
|
|
49835
|
+
for (const segment of path58.split(".")) {
|
|
49864
49836
|
const record = asRecord(current);
|
|
49865
49837
|
if (!record) return void 0;
|
|
49866
49838
|
current = record[segment];
|
|
@@ -49870,10 +49842,10 @@ function readPath2(row, path59) {
|
|
|
49870
49842
|
function projectFields(rows, paths) {
|
|
49871
49843
|
return rows.map((row) => {
|
|
49872
49844
|
const projected = {};
|
|
49873
|
-
for (const
|
|
49874
|
-
const value2 = readPath2(row,
|
|
49845
|
+
for (const path58 of paths) {
|
|
49846
|
+
const value2 = readPath2(row, path58);
|
|
49875
49847
|
if (value2 !== void 0) {
|
|
49876
|
-
projected[
|
|
49848
|
+
projected[path58] = value2;
|
|
49877
49849
|
}
|
|
49878
49850
|
}
|
|
49879
49851
|
return projected;
|
|
@@ -49886,16 +49858,16 @@ function shapeListRows(rows, args8, compact5) {
|
|
|
49886
49858
|
const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
|
|
49887
49859
|
if (paths.length > 0) {
|
|
49888
49860
|
const projected = projectFields(raw, paths).map((row, i) => {
|
|
49889
|
-
const missing = paths.filter((
|
|
49861
|
+
const missing = paths.filter((path58) => row[path58] === void 0);
|
|
49890
49862
|
const source = rows[i];
|
|
49891
49863
|
if (missing.length === 0 || source === void 0) return row;
|
|
49892
49864
|
const compactRow3 = compact5.row(source);
|
|
49893
|
-
for (const
|
|
49894
|
-
if (compactRow3[
|
|
49865
|
+
for (const path58 of missing) {
|
|
49866
|
+
if (compactRow3[path58] !== void 0) row[path58] = compactRow3[path58];
|
|
49895
49867
|
}
|
|
49896
49868
|
return row;
|
|
49897
49869
|
});
|
|
49898
|
-
const unmatched = paths.filter((
|
|
49870
|
+
const unmatched = paths.filter((path58) => projected.every((row) => row[path58] === void 0));
|
|
49899
49871
|
if (projected.length === 0 || unmatched.length === 0) return { rows: projected, fields: paths };
|
|
49900
49872
|
const carried = /* @__PURE__ */ new Set([...compact5.columns, ...raw.flatMap((row) => Object.keys(row))]);
|
|
49901
49873
|
return {
|
|
@@ -51255,54 +51227,54 @@ function ruleFields(node) {
|
|
|
51255
51227
|
([key, value2]) => (key === "and" || key === "or") && Array.isArray(value2) ? value2.flatMap(ruleFields) : [key]
|
|
51256
51228
|
);
|
|
51257
51229
|
}
|
|
51258
|
-
function branchRefusal(key, value2,
|
|
51230
|
+
function branchRefusal(key, value2, path58) {
|
|
51259
51231
|
if (!Array.isArray(value2) || value2.length === 0) {
|
|
51260
|
-
return `${
|
|
51232
|
+
return `${path58}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
|
|
51261
51233
|
}
|
|
51262
51234
|
for (const [index, child] of value2.entries()) {
|
|
51263
|
-
const refusal = ruleNodeRefusal(child, `${
|
|
51235
|
+
const refusal = ruleNodeRefusal(child, `${path58}.${key}[${index}]`);
|
|
51264
51236
|
if (refusal) {
|
|
51265
51237
|
return refusal;
|
|
51266
51238
|
}
|
|
51267
51239
|
}
|
|
51268
51240
|
return void 0;
|
|
51269
51241
|
}
|
|
51270
|
-
function conditionRefusal(key, value2,
|
|
51271
|
-
return key === "and" || key === "or" ? branchRefusal(key, value2,
|
|
51242
|
+
function conditionRefusal(key, value2, path58) {
|
|
51243
|
+
return key === "and" || key === "or" ? branchRefusal(key, value2, path58) : leafRefusal(key, value2, path58);
|
|
51272
51244
|
}
|
|
51273
|
-
function ruleNodeRefusal(node,
|
|
51245
|
+
function ruleNodeRefusal(node, path58) {
|
|
51274
51246
|
if (!isRecord2(node)) {
|
|
51275
|
-
return `${
|
|
51247
|
+
return `${path58} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
|
|
51276
51248
|
}
|
|
51277
51249
|
const entries = Object.entries(node);
|
|
51278
51250
|
if (entries.length === 0) {
|
|
51279
|
-
return `${
|
|
51251
|
+
return `${path58} is empty \u2014 a rule with no condition counts every event the pixel sends`;
|
|
51280
51252
|
}
|
|
51281
51253
|
if (entries.length > 1) {
|
|
51282
|
-
return `${
|
|
51254
|
+
return `${path58} names ${entries.length} conditions side by side (${entries.map(([key]) => key).join(", ")}) \u2014 Meta reads one branch or one field test per object, so combine them with {"and":[\u2026]}`;
|
|
51283
51255
|
}
|
|
51284
51256
|
for (const [key, value2] of entries) {
|
|
51285
|
-
const refusal = conditionRefusal(key, value2,
|
|
51257
|
+
const refusal = conditionRefusal(key, value2, path58);
|
|
51286
51258
|
if (refusal) {
|
|
51287
51259
|
return refusal;
|
|
51288
51260
|
}
|
|
51289
51261
|
}
|
|
51290
51262
|
return void 0;
|
|
51291
51263
|
}
|
|
51292
|
-
function leafRefusal(field, condition,
|
|
51264
|
+
function leafRefusal(field, condition, path58) {
|
|
51293
51265
|
if (!isRecord2(condition)) {
|
|
51294
|
-
return `${
|
|
51266
|
+
return `${path58}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
|
|
51295
51267
|
}
|
|
51296
51268
|
const tests = Object.entries(condition);
|
|
51297
51269
|
if (tests.length === 0) {
|
|
51298
|
-
return `${
|
|
51270
|
+
return `${path58}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
|
|
51299
51271
|
}
|
|
51300
51272
|
if (tests.length > 1) {
|
|
51301
|
-
return `${
|
|
51273
|
+
return `${path58}.${field} names ${tests.length} operators at once (${tests.map(([operator]) => operator).join(", ")}) \u2014 test a field once per condition and combine them with {"and":[\u2026]}`;
|
|
51302
51274
|
}
|
|
51303
51275
|
for (const [operator] of tests) {
|
|
51304
51276
|
if (!RULE_OPERATORS[operator]) {
|
|
51305
|
-
return `${
|
|
51277
|
+
return `${path58}.${field} uses "${operator}", which is not a Meta rule operator \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")} (URL matching is i_contains, not contains or includes)`;
|
|
51306
51278
|
}
|
|
51307
51279
|
}
|
|
51308
51280
|
return void 0;
|
|
@@ -52018,11 +51990,11 @@ var updateStatusSchema2 = z45.enum(UPDATE_STATUSES);
|
|
|
52018
51990
|
function currencyMinimums2(currencyCode) {
|
|
52019
51991
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
52020
51992
|
}
|
|
52021
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
51993
|
+
function validateDailyBudgetFloor(money, ctx, path58) {
|
|
52022
51994
|
if (money?.currencyCode) {
|
|
52023
51995
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
52024
51996
|
if (Number(money.amount) < min) {
|
|
52025
|
-
ctx.addIssue({ code: "custom", path:
|
|
51997
|
+
ctx.addIssue({ code: "custom", path: path58, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
52026
51998
|
}
|
|
52027
51999
|
}
|
|
52028
52000
|
}
|
|
@@ -52940,19 +52912,19 @@ function refuseDeleteByUpdate(payload, group) {
|
|
|
52940
52912
|
);
|
|
52941
52913
|
}
|
|
52942
52914
|
}
|
|
52943
|
-
function loadJsonFileArg3(
|
|
52944
|
-
if (typeof
|
|
52915
|
+
function loadJsonFileArg3(path58) {
|
|
52916
|
+
if (typeof path58 !== "string" || path58.length === 0) {
|
|
52945
52917
|
return {};
|
|
52946
52918
|
}
|
|
52947
52919
|
try {
|
|
52948
|
-
const parsed = JSON.parse(readFileSync11(
|
|
52920
|
+
const parsed = JSON.parse(readFileSync11(path58, "utf8"));
|
|
52949
52921
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
52950
|
-
failWriteValidation4(`${
|
|
52922
|
+
failWriteValidation4(`${path58} must contain a JSON object`);
|
|
52951
52923
|
}
|
|
52952
52924
|
return parsed;
|
|
52953
52925
|
} catch (err) {
|
|
52954
52926
|
if (err instanceof SyntaxError) {
|
|
52955
|
-
failWriteValidation4(`${
|
|
52927
|
+
failWriteValidation4(`${path58} is not valid JSON: ${err.message}`);
|
|
52956
52928
|
}
|
|
52957
52929
|
throw err;
|
|
52958
52930
|
}
|
|
@@ -53530,10 +53502,10 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
|
|
|
53530
53502
|
}
|
|
53531
53503
|
},
|
|
53532
53504
|
run: async ({ args: args8 }) => {
|
|
53533
|
-
const { apiPost: apiPost2 } = await import("./client-
|
|
53534
|
-
const { requireChatId: requireChatId2 } = await import("./env-
|
|
53535
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
53536
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
53505
|
+
const { apiPost: apiPost2 } = await import("./client-AY6TPFQ4.js");
|
|
53506
|
+
const { requireChatId: requireChatId2 } = await import("./env-CWEGTJF3.js");
|
|
53507
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-EPF3HHJ4.js");
|
|
53508
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-53G34I3U.js");
|
|
53537
53509
|
try {
|
|
53538
53510
|
const accountId = bareAccountId2(args8);
|
|
53539
53511
|
const urlTags = typeof args8["url-tags"] === "string" ? args8["url-tags"] : void 0;
|
|
@@ -53684,10 +53656,10 @@ function duplicateCommand2(entity, label) {
|
|
|
53684
53656
|
replace: { type: "boolean", description: "Pause the original once the copy publishes" }
|
|
53685
53657
|
},
|
|
53686
53658
|
run: async ({ args: args8 }) => {
|
|
53687
|
-
const { apiPost: apiPost2 } = await import("./client-
|
|
53688
|
-
const { requireChatId: requireChatId2 } = await import("./env-
|
|
53689
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
53690
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
53659
|
+
const { apiPost: apiPost2 } = await import("./client-AY6TPFQ4.js");
|
|
53660
|
+
const { requireChatId: requireChatId2 } = await import("./env-CWEGTJF3.js");
|
|
53661
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-EPF3HHJ4.js");
|
|
53662
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-53G34I3U.js");
|
|
53691
53663
|
try {
|
|
53692
53664
|
const accountId = bareAccountId2(args8);
|
|
53693
53665
|
const chatId = requireChatId2();
|
|
@@ -58723,12 +58695,12 @@ function flowExists(slug) {
|
|
|
58723
58695
|
return existsSync3(join2(flowsDir(), slug, "_data.json"));
|
|
58724
58696
|
}
|
|
58725
58697
|
function readFlowTree(slug) {
|
|
58726
|
-
const
|
|
58727
|
-
if (!existsSync3(
|
|
58698
|
+
const path58 = join2(flowsDir(), slug, "_data.json");
|
|
58699
|
+
if (!existsSync3(path58)) {
|
|
58728
58700
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
58729
58701
|
}
|
|
58730
58702
|
try {
|
|
58731
|
-
return JSON.parse(readFileSync13(
|
|
58703
|
+
return JSON.parse(readFileSync13(path58, "utf-8"));
|
|
58732
58704
|
} catch (error) {
|
|
58733
58705
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
58734
58706
|
}
|
|
@@ -59688,12 +59660,12 @@ function readPeopleFile(args8) {
|
|
|
59688
59660
|
}
|
|
59689
59661
|
function unreadablePeopleFile(args8) {
|
|
59690
59662
|
if (!args8["people-file"]) return void 0;
|
|
59691
|
-
const
|
|
59663
|
+
const path58 = String(args8["people-file"]);
|
|
59692
59664
|
try {
|
|
59693
|
-
readFileSync14(
|
|
59665
|
+
readFileSync14(path58, "utf8");
|
|
59694
59666
|
return void 0;
|
|
59695
59667
|
} catch {
|
|
59696
|
-
return `Couldn't read --people-file ${
|
|
59668
|
+
return `Couldn't read --people-file ${path58}. Check the path, or pass the people inline with --person.`;
|
|
59697
59669
|
}
|
|
59698
59670
|
}
|
|
59699
59671
|
function peopleLimitError(args8) {
|
|
@@ -60665,11 +60637,11 @@ function parseFieldArgs(argv) {
|
|
|
60665
60637
|
for (const pair of parseRepeated(argv, "--field")) {
|
|
60666
60638
|
const eq = pair.indexOf("=");
|
|
60667
60639
|
const field = eq === -1 ? pair : pair.slice(0, eq);
|
|
60668
|
-
const
|
|
60640
|
+
const path58 = eq === -1 ? "" : pair.slice(eq + 1).trim();
|
|
60669
60641
|
const problem = mappingRowProblem(field);
|
|
60670
60642
|
if (problem !== null) throw new ConversionArgsError(problem);
|
|
60671
|
-
if (
|
|
60672
|
-
mapping[field] =
|
|
60643
|
+
if (path58 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
|
|
60644
|
+
mapping[field] = path58;
|
|
60673
60645
|
}
|
|
60674
60646
|
return mapping;
|
|
60675
60647
|
}
|
|
@@ -62271,11 +62243,11 @@ function unwrap(response) {
|
|
|
62271
62243
|
}
|
|
62272
62244
|
return response.data;
|
|
62273
62245
|
}
|
|
62274
|
-
async function readAvatars(
|
|
62275
|
-
return unwrap(await apiGet(
|
|
62246
|
+
async function readAvatars(path58, params) {
|
|
62247
|
+
return unwrap(await apiGet(path58, params));
|
|
62276
62248
|
}
|
|
62277
|
-
async function writeAvatars(
|
|
62278
|
-
return unwrap(await apiPost(
|
|
62249
|
+
async function writeAvatars(path58, body) {
|
|
62250
|
+
return unwrap(await apiPost(path58, body));
|
|
62279
62251
|
}
|
|
62280
62252
|
|
|
62281
62253
|
// src/commands/avatars/create.ts
|
|
@@ -63184,12 +63156,12 @@ function missingFontFiles(urls, available) {
|
|
|
63184
63156
|
function planFontAdoption(sources, families) {
|
|
63185
63157
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
63186
63158
|
const byFamily = /* @__PURE__ */ new Map();
|
|
63187
|
-
for (const { path:
|
|
63188
|
-
const dir = posix.dirname(
|
|
63159
|
+
for (const { path: path58, source } of sources) {
|
|
63160
|
+
const dir = posix.dirname(path58);
|
|
63189
63161
|
for (const face of declaredFontFaces(source)) {
|
|
63190
63162
|
if (!wanted.has(face.family)) continue;
|
|
63191
63163
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
63192
|
-
perFile.set(
|
|
63164
|
+
perFile.set(path58, [...perFile.get(path58) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
63193
63165
|
byFamily.set(face.family, perFile);
|
|
63194
63166
|
}
|
|
63195
63167
|
}
|
|
@@ -68196,8 +68168,8 @@ function relayPrompt(args8) {
|
|
|
68196
68168
|
...args8.exclusionsLast ? ["", EXCLUSIONS] : []
|
|
68197
68169
|
].join("\n");
|
|
68198
68170
|
}
|
|
68199
|
-
function ingestNode(id,
|
|
68200
|
-
return { id, type: "ingest", params: { source: "path", path:
|
|
68171
|
+
function ingestNode(id, path58, expect) {
|
|
68172
|
+
return { id, type: "ingest", params: { source: "path", path: path58, expect } };
|
|
68201
68173
|
}
|
|
68202
68174
|
function annotateBlueprintWithLabels(blueprintInput, elementsInput) {
|
|
68203
68175
|
if (!blueprintInput || typeof blueprintInput !== "object") return blueprintInput;
|
|
@@ -68452,8 +68424,7 @@ function fontLabel(font) {
|
|
|
68452
68424
|
const origin = {
|
|
68453
68425
|
brand_file: "the brand's font file",
|
|
68454
68426
|
tokens: "named in tokens.json, loaded as a web font",
|
|
68455
|
-
brand_md: "named in BRAND.md, loaded as a web font"
|
|
68456
|
-
default: "the default, since the brand names no typeface"
|
|
68427
|
+
brand_md: "named in BRAND.md, loaded as a web font"
|
|
68457
68428
|
};
|
|
68458
68429
|
const name = font.family ?? ("path" in font.slot ? font.slot.path.split("/").pop() ?? font.slot.path : "a web font");
|
|
68459
68430
|
return `${name} (${origin[font.origin]})`;
|
|
@@ -71574,8 +71545,8 @@ var rerunCommand = defineCommand132({
|
|
|
71574
71545
|
});
|
|
71575
71546
|
|
|
71576
71547
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
71577
|
-
import { mkdir as mkdir8, readFile as
|
|
71578
|
-
import
|
|
71548
|
+
import { mkdir as mkdir8, readFile as readFile24, writeFile as writeFile12 } from "fs/promises";
|
|
71549
|
+
import path30 from "path";
|
|
71579
71550
|
import { defineCommand as defineCommand134 } from "citty";
|
|
71580
71551
|
|
|
71581
71552
|
// src/commands/canvas/creative-limit.ts
|
|
@@ -71637,7 +71608,7 @@ import path26 from "path";
|
|
|
71637
71608
|
|
|
71638
71609
|
// src/engine/scaffold/static-fonts.ts
|
|
71639
71610
|
import { marked } from "marked";
|
|
71640
|
-
var
|
|
71611
|
+
var HEADLINE_WEIGHT = 800;
|
|
71641
71612
|
var HEADLINE_WORDS = /\b(headings?|headlines?|display|titles?|h[1-3])\b/i;
|
|
71642
71613
|
var BODY_WORDS = /\b(body|paragraphs?|text|copy)\b/i;
|
|
71643
71614
|
var PROHIBITION = /\b(never|don'?t|do not|avoid|instead of|ban(?:ned)?|no)\b/i;
|
|
@@ -71741,7 +71712,7 @@ async function brandHeadlineFont(args8) {
|
|
|
71741
71712
|
if (args8.file) return fileFace(args8.file);
|
|
71742
71713
|
const named = args8.named.headline ?? args8.named.body;
|
|
71743
71714
|
if (!named) return null;
|
|
71744
|
-
const url = await args8.resolveUrl(named.family,
|
|
71715
|
+
const url = await args8.resolveUrl(named.family, HEADLINE_WEIGHT);
|
|
71745
71716
|
return url ? { slot: { url }, family: named.family, origin: named.origin } : null;
|
|
71746
71717
|
}
|
|
71747
71718
|
|
|
@@ -71914,7 +71885,7 @@ function fileExists(target2) {
|
|
|
71914
71885
|
}
|
|
71915
71886
|
|
|
71916
71887
|
// src/commands/canvas/creative-definition.ts
|
|
71917
|
-
import
|
|
71888
|
+
import { parseDocument as parseDocument2 } from "yaml";
|
|
71918
71889
|
var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
|
|
71919
71890
|
var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
|
|
71920
71891
|
function titleFromSlug(slug) {
|
|
@@ -71953,6 +71924,9 @@ function buildCreativeDefinition(input) {
|
|
|
71953
71924
|
lines.push(`status: ${input.status ?? "draft"}`);
|
|
71954
71925
|
if (input.sourceReferenceUrl) lines.push(`sourceReferenceUrl: ${yamlScalar(input.sourceReferenceUrl)}`);
|
|
71955
71926
|
if (input.sourceAdvertiser) lines.push(`sourceAdvertiser: ${yamlScalar(input.sourceAdvertiser)}`);
|
|
71927
|
+
if (input.sourceAdvertiserId) lines.push(`sourceAdvertiserId: ${yamlScalar(input.sourceAdvertiserId)}`);
|
|
71928
|
+
if (input.sourceAdId) lines.push(`sourceAdId: ${yamlScalar(input.sourceAdId)}`);
|
|
71929
|
+
if (input.sourcePlatform) lines.push(`sourcePlatform: ${input.sourcePlatform}`);
|
|
71956
71930
|
if (input.sourceKind) lines.push(`sourceKind: ${input.sourceKind}`);
|
|
71957
71931
|
if (input.sourcePath) lines.push(`sourcePath: ${yamlScalar(input.sourcePath)}`);
|
|
71958
71932
|
lines.push("---", "");
|
|
@@ -71960,18 +71934,31 @@ function buildCreativeDefinition(input) {
|
|
|
71960
71934
|
lines.push("");
|
|
71961
71935
|
return lines.join("\n");
|
|
71962
71936
|
}
|
|
71937
|
+
var SOURCE_FIELD_KEYS = ["sourceAdId", "sourcePlatform", "sourceAdvertiserId"];
|
|
71938
|
+
function parseDefinitionSourceFields(raw) {
|
|
71939
|
+
const match = raw.match(/^---\r?\n([\s\S]*?\r?\n)?---/);
|
|
71940
|
+
if (!match) return {};
|
|
71941
|
+
const doc = parseDocument2(match[1] ?? "");
|
|
71942
|
+
if (doc.errors.length > 0) return {};
|
|
71943
|
+
const fields = {};
|
|
71944
|
+
for (const key of SOURCE_FIELD_KEYS) {
|
|
71945
|
+
const value2 = doc.get(key);
|
|
71946
|
+
if (typeof value2 === "string" && value2.trim()) fields[key] = value2.trim();
|
|
71947
|
+
}
|
|
71948
|
+
return fields;
|
|
71949
|
+
}
|
|
71963
71950
|
|
|
71964
71951
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
71965
|
-
import
|
|
71952
|
+
import path28 from "path";
|
|
71966
71953
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
71967
71954
|
const file = rawFile.trim();
|
|
71968
71955
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
71969
|
-
const imageSource = imageIsUrl ? file :
|
|
71970
|
-
const outPath = out ?
|
|
71971
|
-
const blueprintPath2 =
|
|
71972
|
-
const creativeDir = slug ?
|
|
71973
|
-
const definitionPath = creativeDir ?
|
|
71974
|
-
const referencesDir = creativeDir ?
|
|
71956
|
+
const imageSource = imageIsUrl ? file : path28.resolve(cwd, file);
|
|
71957
|
+
const outPath = out ? path28.resolve(cwd, out) : slug ? path28.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path28.join(cwd, "static-ad.canvas.json") : path28.join(path28.dirname(imageSource), "static-ad.canvas.json");
|
|
71958
|
+
const blueprintPath2 = path28.join(path28.dirname(outPath), "prompt.json");
|
|
71959
|
+
const creativeDir = slug ? path28.dirname(outPath) : null;
|
|
71960
|
+
const definitionPath = creativeDir ? path28.join(creativeDir, "_definition.md") : null;
|
|
71961
|
+
const referencesDir = creativeDir ? path28.join(creativeDir, "references") : null;
|
|
71975
71962
|
return { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, creativeDir, definitionPath, referencesDir };
|
|
71976
71963
|
}
|
|
71977
71964
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -71981,8 +71968,8 @@ function isValidScaffoldSlug(slug) {
|
|
|
71981
71968
|
}
|
|
71982
71969
|
|
|
71983
71970
|
// src/commands/canvas/sync-definition.ts
|
|
71984
|
-
import { readdir as readdir11, stat as stat4 } from "fs/promises";
|
|
71985
|
-
import
|
|
71971
|
+
import { readdir as readdir11, readFile as readFile23, stat as stat4 } from "fs/promises";
|
|
71972
|
+
import path29 from "path";
|
|
71986
71973
|
import { defineCommand as defineCommand133 } from "citty";
|
|
71987
71974
|
|
|
71988
71975
|
// src/commands/canvas/definition-graph.ts
|
|
@@ -72029,6 +72016,21 @@ function canvasToDefinitionGraph(canvas) {
|
|
|
72029
72016
|
}
|
|
72030
72017
|
|
|
72031
72018
|
// src/commands/canvas/sync-definition.ts
|
|
72019
|
+
function buildDefinitionSyncPayload(input, graph, chatId) {
|
|
72020
|
+
const { canvas: _canvas, ...fields } = input;
|
|
72021
|
+
return Object.fromEntries([
|
|
72022
|
+
...Object.entries(fields).filter(([, v]) => v !== void 0),
|
|
72023
|
+
["graph", graph],
|
|
72024
|
+
["chatId", chatId]
|
|
72025
|
+
]);
|
|
72026
|
+
}
|
|
72027
|
+
async function readDefinitionSourceFields(canvasPath) {
|
|
72028
|
+
try {
|
|
72029
|
+
return parseDefinitionSourceFields(await readFile23(path29.join(path29.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
72030
|
+
} catch {
|
|
72031
|
+
return {};
|
|
72032
|
+
}
|
|
72033
|
+
}
|
|
72032
72034
|
async function syncCreativeDefinitionBestEffort(input) {
|
|
72033
72035
|
const chatId = process.env.BAKER_CHAT_ID;
|
|
72034
72036
|
if (!chatId) return;
|
|
@@ -72037,16 +72039,7 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
72037
72039
|
try {
|
|
72038
72040
|
const creds = requireCredentialsFromEnv();
|
|
72039
72041
|
const client = new BackendClient2({ baseUrl: creds.url, apiKey: creds.apiKey });
|
|
72040
|
-
await client.syncCreativeDefinition(
|
|
72041
|
-
slug: input.slug,
|
|
72042
|
-
title: input.title,
|
|
72043
|
-
platform: input.platform,
|
|
72044
|
-
formats: input.formats,
|
|
72045
|
-
sourceReferenceUrl: input.sourceReferenceUrl,
|
|
72046
|
-
graph,
|
|
72047
|
-
sourceSha: input.sourceSha,
|
|
72048
|
-
chatId
|
|
72049
|
-
});
|
|
72042
|
+
await client.syncCreativeDefinition(buildDefinitionSyncPayload(input, graph, chatId));
|
|
72050
72043
|
process.stdout.write(`[definition] synced workflow graph (${graph.nodes.length} nodes) \u2014 view it in the dashboard
|
|
72051
72044
|
`);
|
|
72052
72045
|
} catch (e) {
|
|
@@ -72056,15 +72049,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
72056
72049
|
}
|
|
72057
72050
|
}
|
|
72058
72051
|
async function resolveCanvasPath(inputPath) {
|
|
72059
|
-
const resolved =
|
|
72052
|
+
const resolved = path29.resolve(inputPath);
|
|
72060
72053
|
let dir = resolved;
|
|
72061
72054
|
try {
|
|
72062
72055
|
if ((await stat4(resolved)).isFile()) {
|
|
72063
72056
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
72064
|
-
dir =
|
|
72057
|
+
dir = path29.dirname(resolved);
|
|
72065
72058
|
}
|
|
72066
72059
|
} catch {
|
|
72067
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
72060
|
+
dir = resolved.endsWith(".canvas.json") ? path29.dirname(resolved) : resolved;
|
|
72068
72061
|
}
|
|
72069
72062
|
let entries;
|
|
72070
72063
|
try {
|
|
@@ -72075,7 +72068,7 @@ async function resolveCanvasPath(inputPath) {
|
|
|
72075
72068
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
72076
72069
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
72077
72070
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
72078
|
-
return chosen ?
|
|
72071
|
+
return chosen ? path29.join(dir, chosen) : null;
|
|
72079
72072
|
}
|
|
72080
72073
|
var syncDefinitionCommand = defineCommand133({
|
|
72081
72074
|
meta: {
|
|
@@ -72105,7 +72098,7 @@ var syncDefinitionCommand = defineCommand133({
|
|
|
72105
72098
|
} catch {
|
|
72106
72099
|
return;
|
|
72107
72100
|
}
|
|
72108
|
-
await syncCreativeDefinitionBestEffort({ slug, canvas, sourceSha });
|
|
72101
|
+
await syncCreativeDefinitionBestEffort({ slug, canvas, sourceSha, ...await readDefinitionSourceFields(canvasPath) });
|
|
72109
72102
|
}
|
|
72110
72103
|
});
|
|
72111
72104
|
|
|
@@ -72138,9 +72131,120 @@ async function resolveCastAvatars(flag) {
|
|
|
72138
72131
|
return { handles, avatars };
|
|
72139
72132
|
}
|
|
72140
72133
|
|
|
72134
|
+
// src/commands/canvas/library-reference.ts
|
|
72135
|
+
var LIBRARY_PLATFORMS = ["meta", "linkedin", "tiktok"];
|
|
72136
|
+
function resolveSourcePlatform(flag) {
|
|
72137
|
+
const value2 = flag?.trim();
|
|
72138
|
+
if (!value2) return { ok: true, platform: "meta" };
|
|
72139
|
+
if (LIBRARY_PLATFORMS.includes(value2)) return { ok: true, platform: value2 };
|
|
72140
|
+
return {
|
|
72141
|
+
ok: false,
|
|
72142
|
+
message: `--source-platform "${value2}" is not a library platform. Use one of: ${LIBRARY_PLATFORMS.join(", ")}.`
|
|
72143
|
+
};
|
|
72144
|
+
}
|
|
72145
|
+
async function fetchLibraryReference(adId, platform) {
|
|
72146
|
+
const data = await apiGet("/api/ad-library/ad-content", { id: adId, platform });
|
|
72147
|
+
if (!data?.ad) throw new Error(`the library returned no ad for ${adId} on ${platform}`);
|
|
72148
|
+
return data.ad;
|
|
72149
|
+
}
|
|
72150
|
+
function filled(entries) {
|
|
72151
|
+
return Object.fromEntries(
|
|
72152
|
+
Object.entries(entries).filter(([, v]) => v !== null && v !== void 0 && !(Array.isArray(v) && v.length === 0))
|
|
72153
|
+
);
|
|
72154
|
+
}
|
|
72155
|
+
function libraryReferenceBlock(ad, ids2) {
|
|
72156
|
+
const textBlocks = ad.on_screen_text.flatMap(
|
|
72157
|
+
(o) => o.text ? [{ text: o.text, ...o.role ? { role: o.role } : {}, ...o.position ? { position: o.position } : {} }] : []
|
|
72158
|
+
);
|
|
72159
|
+
const advertiser = ad.advertiser ?? void 0;
|
|
72160
|
+
const advertiserId = ids2.advertiserId ?? ad.advertiser_id ?? void 0;
|
|
72161
|
+
return {
|
|
72162
|
+
ad_id: ids2.adId,
|
|
72163
|
+
platform: ids2.platform,
|
|
72164
|
+
...advertiser ? { advertiser } : {},
|
|
72165
|
+
...advertiserId ? { advertiser_id: advertiserId } : {},
|
|
72166
|
+
...ad.layout.format_pattern ? { format_pattern: ad.layout.format_pattern } : {},
|
|
72167
|
+
layout: filled({
|
|
72168
|
+
composition: ad.layout.composition,
|
|
72169
|
+
focal_subject: ad.layout.focal_subject,
|
|
72170
|
+
aspect_ratio: ad.layout.aspect_ratio,
|
|
72171
|
+
background: ad.layout.background,
|
|
72172
|
+
palette_mood: ad.layout.palette_mood,
|
|
72173
|
+
colors: ad.layout.colors,
|
|
72174
|
+
production: ad.layout.production,
|
|
72175
|
+
aesthetic: ad.layout.aesthetic,
|
|
72176
|
+
cta_in_image: ad.layout.cta_in_image,
|
|
72177
|
+
markers: ad.layout.markers
|
|
72178
|
+
}),
|
|
72179
|
+
message: filled({
|
|
72180
|
+
mechanism: ad.message.mechanism,
|
|
72181
|
+
awareness: ad.message.awareness_stage,
|
|
72182
|
+
sophistication: ad.message.sophistication,
|
|
72183
|
+
angle: ad.angle,
|
|
72184
|
+
hook_archetype: ad.hook_archetype,
|
|
72185
|
+
pain_points: ad.message.pain_points,
|
|
72186
|
+
desires: ad.message.desires,
|
|
72187
|
+
value_props: ad.message.value_props,
|
|
72188
|
+
objections_handled: ad.message.objections_handled,
|
|
72189
|
+
proof: ad.message.proof,
|
|
72190
|
+
risk_reversal: ad.message.risk_reversal
|
|
72191
|
+
}),
|
|
72192
|
+
offer: filled({
|
|
72193
|
+
offer_type: ad.offer.offer_type,
|
|
72194
|
+
pricing_model: ad.offer.pricing_model,
|
|
72195
|
+
entry_price: ad.offer.entry_price,
|
|
72196
|
+
no_credit_card: ad.offer.no_credit_card,
|
|
72197
|
+
guarantee: ad.offer.guarantee,
|
|
72198
|
+
guarantee_days: ad.offer.guarantee_days,
|
|
72199
|
+
urgency: ad.offer.urgency,
|
|
72200
|
+
bonus: ad.offer.bonus
|
|
72201
|
+
}),
|
|
72202
|
+
text_blocks: textBlocks,
|
|
72203
|
+
copy: filled({ primary_text: ad.primary_text, headline: ad.headline, cta: ad.cta })
|
|
72204
|
+
};
|
|
72205
|
+
}
|
|
72206
|
+
function referenceDescribeContext(reference, agentContext) {
|
|
72207
|
+
const own = agentContext?.trim() || void 0;
|
|
72208
|
+
if (!reference) return own;
|
|
72209
|
+
const facts = [];
|
|
72210
|
+
if (reference.advertiser) facts.push(`advertiser: ${reference.advertiser}`);
|
|
72211
|
+
if (reference.format_pattern) facts.push(`container (format_pattern): ${reference.format_pattern}`);
|
|
72212
|
+
if (typeof reference.layout.composition === "string") facts.push(`composition: ${reference.layout.composition}`);
|
|
72213
|
+
if (typeof reference.message.mechanism === "string") facts.push(`persuasion mechanism: ${reference.message.mechanism}`);
|
|
72214
|
+
if (typeof reference.message.awareness === "string") facts.push(`awareness stage: ${reference.message.awareness}`);
|
|
72215
|
+
if (typeof reference.offer.offer_type === "string") facts.push(`offer: ${reference.offer.offer_type}`);
|
|
72216
|
+
if (reference.text_blocks.length > 0) {
|
|
72217
|
+
facts.push(
|
|
72218
|
+
`text blocks: ${reference.text_blocks.map((b) => `"${b.text}"${b.role ? ` (${b.role})` : ""}`).join("; ")}`
|
|
72219
|
+
);
|
|
72220
|
+
}
|
|
72221
|
+
const library = `The ads library has already read this ad: ${facts.join(". ")}. Your blueprint must agree with it on the container and the mechanism; refine and detail them, never contradict them, and transcribe the text blocks exactly.`;
|
|
72222
|
+
return own ? `${own}. ${library}` : library;
|
|
72223
|
+
}
|
|
72224
|
+
function mergeReferenceIntoBlueprint(blueprint, reference) {
|
|
72225
|
+
if (!reference || !blueprint || typeof blueprint !== "object" || Array.isArray(blueprint)) return blueprint;
|
|
72226
|
+
const merged = { ...blueprint, reference };
|
|
72227
|
+
if (winningMechanisms(blueprint).length === 0) {
|
|
72228
|
+
const container = reference.format_pattern;
|
|
72229
|
+
const mechanism = typeof reference.message.mechanism === "string" ? reference.message.mechanism : void 0;
|
|
72230
|
+
const device = container && mechanism ? `${container} container carrying ${mechanism}` : container && `${container} container` || mechanism && `${mechanism} persuasion mechanism`;
|
|
72231
|
+
if (device) {
|
|
72232
|
+
merged.winning_mechanisms = [
|
|
72233
|
+
{
|
|
72234
|
+
kind: "structural",
|
|
72235
|
+
device,
|
|
72236
|
+
why_it_works: "The winners library recorded this ad as a winner on this container and persuasion mechanism.",
|
|
72237
|
+
source: "library"
|
|
72238
|
+
}
|
|
72239
|
+
];
|
|
72240
|
+
}
|
|
72241
|
+
}
|
|
72242
|
+
return merged;
|
|
72243
|
+
}
|
|
72244
|
+
|
|
72141
72245
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
72142
72246
|
function toCanvasRelative2(canvasDir, file) {
|
|
72143
|
-
const rel =
|
|
72247
|
+
const rel = path30.relative(canvasDir, file).split(path30.sep).join("/");
|
|
72144
72248
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
72145
72249
|
}
|
|
72146
72250
|
async function uploadSourceAsReference(source, isUrl, client) {
|
|
@@ -72152,7 +72256,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
|
|
|
72152
72256
|
throw new Error(`failed to download source image (${e instanceof Error ? e.message : String(e)})`);
|
|
72153
72257
|
}
|
|
72154
72258
|
} else {
|
|
72155
|
-
bytes = await
|
|
72259
|
+
bytes = await readFile24(source);
|
|
72156
72260
|
}
|
|
72157
72261
|
const safe = await toModelSafeImage(bytes);
|
|
72158
72262
|
const sha2562 = sha256Hex(safe.bytes);
|
|
@@ -72292,13 +72396,102 @@ async function runVisionPasses(canvas) {
|
|
|
72292
72396
|
return fail4("read_outputs", e instanceof Error ? e.message : String(e));
|
|
72293
72397
|
}
|
|
72294
72398
|
}
|
|
72399
|
+
var SOURCE_AD_ID_DESCRIPTION = "The winners-library ad this creative adapts (the `ad_id` from `baker winning-ads search`). The library's read of that ad (container, layout, colours, text blocks with their roles, persuasion mechanism, offer) is fetched and written into prompt.json as `reference`, the describe pass is told it so the two reads agree, and the source is recorded on the creative from this scaffold on. With it, the image is optional: the library's picture is used when none is given. Pass it whenever the reference came from the library.";
|
|
72400
|
+
var SOURCE_PLATFORM_DESCRIPTION = `Which platform the library ad is on: ${LIBRARY_PLATFORMS.join("|")} (default meta).`;
|
|
72401
|
+
var SOURCE_ADVERTISER_ID_DESCRIPTION = "The library advertiser id of the reference (the `advertiser_id` beside the ad in search results). Defaults to the one the library returns for the ad.";
|
|
72402
|
+
registerSchema({
|
|
72403
|
+
command: "canvas.scaffold-static-ad",
|
|
72404
|
+
description: "Turn a reference image into a runnable static-ad canvas: billed image_describe + element-selection passes, then a canvas on which the image model renders the finished ad from prompt.json. Start here for a static ad adapted from a winning ad: pass `--source-ad-id <ad_id> --source-platform <platform>` from the search result, so the library's structured read grounds the blueprint and the source is recorded on the creative; the image may then be omitted. `baker avatars list`, then `--avatar <handle>` when the image shows a person.",
|
|
72405
|
+
args: {
|
|
72406
|
+
file: {
|
|
72407
|
+
type: "positional",
|
|
72408
|
+
required: false,
|
|
72409
|
+
description: "Path or http(s) URL to the reference image. Optional with --source-ad-id: the library's picture is used."
|
|
72410
|
+
},
|
|
72411
|
+
"source-ad-id": { type: "string", required: false, description: SOURCE_AD_ID_DESCRIPTION },
|
|
72412
|
+
"source-platform": {
|
|
72413
|
+
type: "string",
|
|
72414
|
+
required: false,
|
|
72415
|
+
description: SOURCE_PLATFORM_DESCRIPTION,
|
|
72416
|
+
enum: [...LIBRARY_PLATFORMS]
|
|
72417
|
+
},
|
|
72418
|
+
"source-advertiser-id": { type: "string", required: false, description: SOURCE_ADVERTISER_ID_DESCRIPTION },
|
|
72419
|
+
context: {
|
|
72420
|
+
type: "string",
|
|
72421
|
+
required: false,
|
|
72422
|
+
description: "Known provenance (advertiser, category, market, their brand palette) to ground the describe pass"
|
|
72423
|
+
},
|
|
72424
|
+
slug: {
|
|
72425
|
+
type: "string",
|
|
72426
|
+
required: false,
|
|
72427
|
+
description: "Creative slug: writes src/creatives/<slug>/<slug>.canvas.json, prompt.json and _definition.md"
|
|
72428
|
+
},
|
|
72429
|
+
title: { type: "string", required: false, description: "Creative title for _definition.md (default: title-cased slug)" },
|
|
72430
|
+
platform: {
|
|
72431
|
+
type: "string",
|
|
72432
|
+
required: false,
|
|
72433
|
+
description: "Ad platform for _definition.md and the default placement set (meta|google|linkedin|tiktok|youtube|x|other; default meta)"
|
|
72434
|
+
},
|
|
72435
|
+
advertiser: {
|
|
72436
|
+
type: "string",
|
|
72437
|
+
required: false,
|
|
72438
|
+
description: "Source advertiser name for _definition.md (default: the library ad's advertiser when --source-ad-id is given)"
|
|
72439
|
+
},
|
|
72440
|
+
placements: {
|
|
72441
|
+
type: "string",
|
|
72442
|
+
required: false,
|
|
72443
|
+
description: "Formats delivered: meta_feed|meta_feed_stories|meta_all|linkedin|google_demandgen|tiktok|youtube, or none for a single base ad (default from --platform)"
|
|
72444
|
+
},
|
|
72445
|
+
aspect: { type: "string", required: false, description: "Force the lead format (else inferred from the image)" },
|
|
72446
|
+
takes: { type: "string", required: false, description: "Finished ads rendered and judged, 1-3 (default 3)" },
|
|
72447
|
+
avatar: {
|
|
72448
|
+
type: "string",
|
|
72449
|
+
required: false,
|
|
72450
|
+
description: "Cast company avatars as the ad's people, comma-separated in detection order (`marta` or `marta,joan`)"
|
|
72451
|
+
},
|
|
72452
|
+
"skip-actor-sheets": {
|
|
72453
|
+
type: "boolean",
|
|
72454
|
+
required: false,
|
|
72455
|
+
description: "Ground each animal hero on its lone dropped photo instead of a generated multi-view sheet"
|
|
72456
|
+
},
|
|
72457
|
+
out: { type: "string", required: false, description: "Output canvas path (default <image-dir>/static-ad.canvas.json)" },
|
|
72458
|
+
"describe-model": { type: "string", required: false, description: "Override the image_describe model id" },
|
|
72459
|
+
"select-model": { type: "string", required: false, description: "Override the element-selection text_generate model id" },
|
|
72460
|
+
"gen-model": { type: "string", required: false, description: "Override the image_generate model id" }
|
|
72461
|
+
}
|
|
72462
|
+
});
|
|
72463
|
+
async function lookupReference(args8) {
|
|
72464
|
+
const adId = args8["source-ad-id"] ? String(args8["source-ad-id"]).trim() : "";
|
|
72465
|
+
if (!adId) return { status: "none" };
|
|
72466
|
+
const platformResult = resolveSourcePlatform(args8["source-platform"] ? String(args8["source-platform"]) : void 0);
|
|
72467
|
+
if (!platformResult.ok) return fail4("invalid_source_platform", platformResult.message);
|
|
72468
|
+
const platform = platformResult.platform;
|
|
72469
|
+
const advertiserId = args8["source-advertiser-id"] ? String(args8["source-advertiser-id"]).trim() : void 0;
|
|
72470
|
+
try {
|
|
72471
|
+
const ad = await fetchLibraryReference(adId, platform);
|
|
72472
|
+
return {
|
|
72473
|
+
status: "grounded",
|
|
72474
|
+
reference: libraryReferenceBlock(ad, { adId, platform, advertiserId }),
|
|
72475
|
+
mediaUrl: ad.media_url ?? void 0
|
|
72476
|
+
};
|
|
72477
|
+
} catch (e) {
|
|
72478
|
+
return { status: "unavailable", adId, platform, message: e instanceof Error ? e.message : String(e) };
|
|
72479
|
+
}
|
|
72480
|
+
}
|
|
72295
72481
|
var scaffoldStaticAdCommand = defineCommand134({
|
|
72296
72482
|
meta: {
|
|
72297
72483
|
name: "scaffold-static-ad",
|
|
72298
|
-
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') and an AI selection of the image's MAIN identity elements \u2014 then scaffolds a canvas on which the image model renders the FINISHED ad: up to three candidates from prompt.json, the copy quoted verbatim, the brand's typeface shown as a specimen and the logo from src/brand/logos/ as a reference (only when the concept shows one), judged on one idea, exact words, layout, mechanism, brand and artifacts; every other placement format is the winner re-laid by the model, and a format the model has no shape for is cropped exact. The run reads every word back and refuses to call the ad ready when one is off. No copy means the picture is the ad. 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. Start here: `baker avatars list`, then pass `--avatar <handle>` when the image shows a person \u2014 the ad's people are played by the company's avatars, never by a photo of anyone real."
|
|
72484
|
+
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') and an AI selection of the image's MAIN identity elements \u2014 then scaffolds a canvas on which the image model renders the FINISHED ad: up to three candidates from prompt.json, the copy quoted verbatim, the brand's typeface shown as a specimen and the logo from src/brand/logos/ as a reference (only when the concept shows one), judged on one idea, exact words, layout, mechanism, brand and artifacts; every other placement format is the winner re-laid by the model, and a format the model has no shape for is cropped exact. The run reads every word back and refuses to call the ad ready when one is off. No copy means the picture is the ad. 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. Start here: `baker avatars list`, then pass `--avatar <handle>` when the image shows a person \u2014 the ad's people are played by the company's avatars, never by a photo of anyone real. A reference from the library: pass `--source-ad-id <ad_id> --source-platform <platform>` so its structured read grounds the blueprint and the source is recorded on the creative."
|
|
72299
72485
|
},
|
|
72300
72486
|
args: {
|
|
72301
|
-
file: {
|
|
72487
|
+
file: {
|
|
72488
|
+
type: "positional",
|
|
72489
|
+
required: false,
|
|
72490
|
+
description: "Path or http(s) URL to the source/inspiration image (optional with --source-ad-id)"
|
|
72491
|
+
},
|
|
72492
|
+
"source-ad-id": { type: "string", description: SOURCE_AD_ID_DESCRIPTION },
|
|
72493
|
+
"source-platform": { type: "string", description: SOURCE_PLATFORM_DESCRIPTION },
|
|
72494
|
+
"source-advertiser-id": { type: "string", description: SOURCE_ADVERTISER_ID_DESCRIPTION },
|
|
72302
72495
|
context: { type: "string", description: "Known provenance (advertiser, category, market) to ground the describe" },
|
|
72303
72496
|
out: { type: "string", description: "Output canvas path (default <image-dir>/static-ad.canvas.json)" },
|
|
72304
72497
|
slug: {
|
|
@@ -72310,7 +72503,10 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72310
72503
|
type: "string",
|
|
72311
72504
|
description: "Ad platform (meta|google|linkedin|tiktok|youtube|x|other; default meta). Sets _definition.md AND, unless --placements is given, the formats the ad is delivered in (meta \u2192 4:5+9:16, google \u2192 1:1+4:5+16:9, linkedin \u2192 1:1, tiktok \u2192 9:16, youtube \u2192 16:9; x/other \u2192 one format)."
|
|
72312
72505
|
},
|
|
72313
|
-
advertiser: {
|
|
72506
|
+
advertiser: {
|
|
72507
|
+
type: "string",
|
|
72508
|
+
description: "Source advertiser recorded in _definition.md (default: the library ad's advertiser)"
|
|
72509
|
+
},
|
|
72314
72510
|
"describe-model": { type: "string", description: "Override the image_describe model id" },
|
|
72315
72511
|
"select-model": { type: "string", description: "Override the text_generate model id for element selection" },
|
|
72316
72512
|
"gen-model": { type: "string", description: "Override the image_generate model id" },
|
|
@@ -72344,14 +72540,24 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72344
72540
|
);
|
|
72345
72541
|
process.exit(2);
|
|
72346
72542
|
}
|
|
72347
|
-
const
|
|
72348
|
-
|
|
72349
|
-
|
|
72350
|
-
|
|
72351
|
-
|
|
72352
|
-
|
|
72543
|
+
const lookup = await lookupReference(args8);
|
|
72544
|
+
if (lookup.status === "unavailable") {
|
|
72545
|
+
process.stderr.write(
|
|
72546
|
+
`[warn] winners-library ad ${lookup.adId} (${lookup.platform}) could not be read (${lookup.message}); scaffolding without its structured read
|
|
72547
|
+
`
|
|
72548
|
+
);
|
|
72549
|
+
}
|
|
72550
|
+
const reference = lookup.status === "grounded" ? lookup.reference : void 0;
|
|
72551
|
+
const fileArg2 = args8.file ? String(args8.file) : void 0;
|
|
72552
|
+
const sourceFile = fileArg2 ?? (lookup.status === "grounded" ? lookup.mediaUrl : void 0);
|
|
72553
|
+
if (!sourceFile) {
|
|
72554
|
+
if (lookup.status === "none") return fail4("missing_source", "Pass the reference image, or --source-ad-id <ad_id> to use a library ad.");
|
|
72555
|
+
if (lookup.status === "unavailable") return fail4("source_unavailable", `No image given and the library ad could not be read (${lookup.message}).`);
|
|
72556
|
+
return fail4("source_unavailable", `The library holds no picture for ad ${reference?.ad_id}; pass the image.`);
|
|
72557
|
+
}
|
|
72558
|
+
const { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(sourceFile, args8.out ? String(args8.out) : void 0, process.cwd(), slug);
|
|
72353
72559
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
72354
|
-
await mkdir8(
|
|
72560
|
+
await mkdir8(path30.dirname(outPath), { recursive: true });
|
|
72355
72561
|
let models;
|
|
72356
72562
|
try {
|
|
72357
72563
|
models = resolveModels(args8);
|
|
@@ -72377,13 +72583,14 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72377
72583
|
durableSourceUrl !== void 0 || imageIsUrl,
|
|
72378
72584
|
describeModel,
|
|
72379
72585
|
selectModel,
|
|
72380
|
-
args8.context ? String(args8.context) : void 0
|
|
72586
|
+
referenceDescribeContext(reference, args8.context ? String(args8.context) : void 0)
|
|
72381
72587
|
);
|
|
72382
72588
|
const vision = await runVisionPasses(describeCanvas);
|
|
72383
|
-
const {
|
|
72589
|
+
const { creditsSpent } = vision;
|
|
72590
|
+
const blueprint = mergeReferenceIntoBlueprint(vision.blueprint, reference);
|
|
72384
72591
|
const elements = castAvatarsOnStaticElements(vision.elements, cast.avatars);
|
|
72385
72592
|
const uncast = staticAvatarsNotCast(vision.elements, cast.handles);
|
|
72386
|
-
const canvasDir =
|
|
72593
|
+
const canvasDir = path30.dirname(outPath);
|
|
72387
72594
|
const workspaceRoot = await findBrandRoot(canvasDir) ?? process.cwd();
|
|
72388
72595
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
72389
72596
|
const annotated = annotateBlueprintWithLabels(blueprint, elements);
|
|
@@ -72431,7 +72638,7 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72431
72638
|
} catch (e) {
|
|
72432
72639
|
return fail4("scaffold", e instanceof Error ? e.message : String(e));
|
|
72433
72640
|
}
|
|
72434
|
-
const validation = await validateScaffoldedStaticAd(canvas,
|
|
72641
|
+
const validation = await validateScaffoldedStaticAd(canvas, path30.dirname(outPath));
|
|
72435
72642
|
if (validation.blocking.length > 0) {
|
|
72436
72643
|
process.stderr.write(
|
|
72437
72644
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.blocking } }, null, 2)}
|
|
@@ -72441,6 +72648,15 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72441
72648
|
}
|
|
72442
72649
|
await writeFile12(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
72443
72650
|
`, "utf8");
|
|
72651
|
+
const sourceFields = reference ? {
|
|
72652
|
+
sourceAdId: reference.ad_id,
|
|
72653
|
+
sourcePlatform: reference.platform,
|
|
72654
|
+
...reference.advertiser_id ? { sourceAdvertiserId: reference.advertiser_id } : {}
|
|
72655
|
+
} : lookup.status === "unavailable" ? {
|
|
72656
|
+
sourceAdId: lookup.adId,
|
|
72657
|
+
sourcePlatform: lookup.platform,
|
|
72658
|
+
...args8["source-advertiser-id"] ? { sourceAdvertiserId: String(args8["source-advertiser-id"]).trim() } : {}
|
|
72659
|
+
} : {};
|
|
72444
72660
|
if (definitionPath && !await fileExists(definitionPath)) {
|
|
72445
72661
|
await writeFile12(
|
|
72446
72662
|
definitionPath,
|
|
@@ -72450,7 +72666,8 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72450
72666
|
platform: definitionPlatform,
|
|
72451
72667
|
formats: resolveFormatList(report.output_formats),
|
|
72452
72668
|
sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : void 0),
|
|
72453
|
-
sourceAdvertiser: args8.advertiser ? String(args8.advertiser) : args8.context ? String(args8.context) : void 0,
|
|
72669
|
+
sourceAdvertiser: args8.advertiser ? String(args8.advertiser) : reference?.advertiser ?? (args8.context ? String(args8.context) : void 0),
|
|
72670
|
+
...sourceFields,
|
|
72454
72671
|
sourceKind: "image",
|
|
72455
72672
|
description: describeBlueprintIntent(blueprint)
|
|
72456
72673
|
}),
|
|
@@ -72464,11 +72681,15 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72464
72681
|
platform: definitionPlatform,
|
|
72465
72682
|
formats: resolveFormatList(report.output_formats),
|
|
72466
72683
|
sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : void 0),
|
|
72684
|
+
...sourceFields,
|
|
72467
72685
|
canvas,
|
|
72468
72686
|
sourceSha: await computeSourceSha(outPath)
|
|
72469
72687
|
});
|
|
72470
72688
|
}
|
|
72471
72689
|
const hints2 = [
|
|
72690
|
+
...lookup.status === "unavailable" ? [
|
|
72691
|
+
`REFERENCE NOT READ: the winners library could not return ad ${lookup.adId} (${lookup.message}). The blueprint is the describe pass alone and prompt.json has no \`reference\` block; the source ids are still recorded. Check the ad id and platform, or continue from the picture.`
|
|
72692
|
+
] : [],
|
|
72472
72693
|
...uncast.length ? [
|
|
72473
72694
|
`NOT CAST: ${uncast.map((h) => `\`${h}\``).join(", ")} \u2014 the element selection found fewer people than avatars passed, so ${uncast.length === 1 ? "this avatar plays" : "these avatars play"} nobody in this ad. Tell the user who is cast and who is not.`
|
|
72474
72695
|
] : [],
|
|
@@ -72485,6 +72706,15 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72485
72706
|
prompt_path: blueprintPath2,
|
|
72486
72707
|
definition_path: definitionPath ?? void 0,
|
|
72487
72708
|
source_reference: durableSourceUrl ?? void 0,
|
|
72709
|
+
...reference ? {
|
|
72710
|
+
reference: {
|
|
72711
|
+
ad_id: reference.ad_id,
|
|
72712
|
+
platform: reference.platform,
|
|
72713
|
+
advertiser_id: reference.advertiser_id,
|
|
72714
|
+
format_pattern: reference.format_pattern,
|
|
72715
|
+
mechanism: reference.message.mechanism
|
|
72716
|
+
}
|
|
72717
|
+
} : {},
|
|
72488
72718
|
output: canvas.output,
|
|
72489
72719
|
...hints2.length ? { hints: hints2 } : {},
|
|
72490
72720
|
models: { describe: describeModel, select: selectModel, gen: opts.genModel },
|
|
@@ -72497,7 +72727,7 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72497
72727
|
run_estimated_credits: validation.estimatedCredits
|
|
72498
72728
|
},
|
|
72499
72729
|
checklist: {
|
|
72500
|
-
edit_prompt: `Edit ${
|
|
72730
|
+
edit_prompt: `Edit ${path30.basename(blueprintPath2)} \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.${reference ? " Its `reference` block is the library's read of the source ad (container, layout, mechanism, offer, text blocks): keep it as the account of why the ad won, and rebuild it for our brand." : ""}`,
|
|
72501
72731
|
winning_mechanisms: report.winning_mechanisms,
|
|
72502
72732
|
rebuild_the_mechanism: "`winning_mechanisms` is WHY the source ad works. Rewrite each one for our brand in prompt.json (a rhyme stays a rhyme in our words, a before/after stays a before/after of our product); never copy the source's literal copy.",
|
|
72503
72733
|
assets_to_supply: report.elements,
|
|
@@ -72558,8 +72788,8 @@ function clientLogoMissNote(decision, domain) {
|
|
|
72558
72788
|
}
|
|
72559
72789
|
|
|
72560
72790
|
// src/commands/canvas/scaffold-ad.ts
|
|
72561
|
-
import { copyFile, cp, mkdir as mkdir9, readFile as
|
|
72562
|
-
import
|
|
72791
|
+
import { copyFile, cp, mkdir as mkdir9, readFile as readFile26, stat as stat5, writeFile as writeFile14 } from "fs/promises";
|
|
72792
|
+
import path32 from "path";
|
|
72563
72793
|
import { defineCommand as defineCommand135 } from "citty";
|
|
72564
72794
|
|
|
72565
72795
|
// src/engine/scaffold/ad-spec.ts
|
|
@@ -73273,8 +73503,8 @@ function injectAdBrandOverlay(indexHtml, overlayHtml, style = {}) {
|
|
|
73273
73503
|
}
|
|
73274
73504
|
|
|
73275
73505
|
// src/commands/canvas/composition-dims.ts
|
|
73276
|
-
import { readFile as
|
|
73277
|
-
import
|
|
73506
|
+
import { readFile as readFile25, writeFile as writeFile13 } from "fs/promises";
|
|
73507
|
+
import path31 from "path";
|
|
73278
73508
|
function patchCompositionMeta(metaJson, dims) {
|
|
73279
73509
|
try {
|
|
73280
73510
|
const meta = JSON.parse(metaJson);
|
|
@@ -73289,11 +73519,11 @@ function patchCompositionHtml(html, dims) {
|
|
|
73289
73519
|
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`);
|
|
73290
73520
|
}
|
|
73291
73521
|
async function stampCompositionDims(compositionDir, dims) {
|
|
73292
|
-
const metaPath =
|
|
73293
|
-
const rawMeta = await
|
|
73522
|
+
const metaPath = path31.join(compositionDir, "meta.json");
|
|
73523
|
+
const rawMeta = await readFile25(metaPath, "utf8");
|
|
73294
73524
|
await writeFile13(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
73295
|
-
const htmlPath =
|
|
73296
|
-
const rawHtml = await
|
|
73525
|
+
const htmlPath = path31.join(compositionDir, "index.html");
|
|
73526
|
+
const rawHtml = await readFile25(htmlPath, "utf8");
|
|
73297
73527
|
await writeFile13(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
73298
73528
|
}
|
|
73299
73529
|
|
|
@@ -73376,7 +73606,7 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73376
73606
|
const specPath = args8.spec;
|
|
73377
73607
|
let parsed;
|
|
73378
73608
|
try {
|
|
73379
|
-
parsed = JSON.parse(await
|
|
73609
|
+
parsed = JSON.parse(await readFile26(specPath, "utf-8"));
|
|
73380
73610
|
} catch (e) {
|
|
73381
73611
|
writeJson({
|
|
73382
73612
|
ok: false,
|
|
@@ -73537,7 +73767,7 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73537
73767
|
] : [];
|
|
73538
73768
|
const markPath = spec.data.brand?.logo?.trim();
|
|
73539
73769
|
if (!noBrand && markPath?.toLowerCase().endsWith(".svg")) {
|
|
73540
|
-
const svg = await
|
|
73770
|
+
const svg = await readFile26(markPath, "utf-8").catch(() => null);
|
|
73541
73771
|
if (svg === null) {
|
|
73542
73772
|
writeJson({
|
|
73543
73773
|
ok: false,
|
|
@@ -73589,8 +73819,8 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73589
73819
|
videoModel: chosenVideoModel,
|
|
73590
73820
|
...avatarVoice ? { avatarVoice } : {}
|
|
73591
73821
|
});
|
|
73592
|
-
const slug = args8.slug ??
|
|
73593
|
-
const outPath = args8.out ?? (args8.slug ?
|
|
73822
|
+
const slug = args8.slug ?? path32.basename(specPath).replace(/\.[^.]+$/, "");
|
|
73823
|
+
const outPath = args8.out ?? (args8.slug ? path32.join("src/creatives", slug, `${slug}.canvas.json`) : path32.join(path32.dirname(specPath), `${slug}.canvas.json`));
|
|
73594
73824
|
if (args8.slug) await enforceNewCreativeLimit(slug, outPath);
|
|
73595
73825
|
let outAr;
|
|
73596
73826
|
try {
|
|
@@ -73608,28 +73838,28 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73608
73838
|
return;
|
|
73609
73839
|
}
|
|
73610
73840
|
const outDims = canvasDims(outAr);
|
|
73611
|
-
const outDir =
|
|
73841
|
+
const outDir = path32.dirname(outPath);
|
|
73612
73842
|
await mkdir9(outDir, { recursive: true });
|
|
73613
|
-
const compositionDest =
|
|
73614
|
-
const captionsDest =
|
|
73843
|
+
const compositionDest = path32.join(outDir, "video-overlay-composition");
|
|
73844
|
+
const captionsDest = path32.join(outDir, "tiktok-captions-composition");
|
|
73615
73845
|
await cp(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
73616
73846
|
await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
|
|
73617
73847
|
await stampCompositionDims(compositionDest, outDims);
|
|
73618
73848
|
await stampCompositionDims(captionsDest, outDims);
|
|
73619
|
-
const captionsIndex =
|
|
73620
|
-
await writeFile14(captionsIndex, injectAdCaptionPlacement(await
|
|
73849
|
+
const captionsIndex = path32.join(captionsDest, "index.html");
|
|
73850
|
+
await writeFile14(captionsIndex, injectAdCaptionPlacement(await readFile26(captionsIndex, "utf-8"), outAr), "utf-8");
|
|
73621
73851
|
const face = await stageBrandDisplayFace(outDir, captionsDest);
|
|
73622
73852
|
if (face) {
|
|
73623
|
-
await writeFile14(captionsIndex, injectAdDisplayFace(await
|
|
73853
|
+
await writeFile14(captionsIndex, injectAdDisplayFace(await readFile26(captionsIndex, "utf-8"), face.file), "utf-8");
|
|
73624
73854
|
}
|
|
73625
|
-
const blueprintPath2 =
|
|
73626
|
-
const blueprintStylePath =
|
|
73855
|
+
const blueprintPath2 = path32.join(outDir, "prompt.json");
|
|
73856
|
+
const blueprintStylePath = path32.join(outDir, "prompt.style.json");
|
|
73627
73857
|
await writeSceneFiles(outDir, blueprint);
|
|
73628
73858
|
await writeFile14(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
|
|
73629
|
-
await writeFile14(
|
|
73859
|
+
await writeFile14(path32.join(outDir, AD_SPEC_FILE), `${JSON.stringify(specArtifact, null, 2)}
|
|
73630
73860
|
`, "utf8");
|
|
73631
73861
|
const logoPath = noBrand ? void 0 : spec.data.brand?.logo?.trim();
|
|
73632
|
-
const transcriptPath =
|
|
73862
|
+
const transcriptPath = path32.join(outDir, "transcript.json");
|
|
73633
73863
|
if (spec.data.voiceover === false) {
|
|
73634
73864
|
await writeFile14(transcriptPath, `${JSON.stringify(staticTranscript(spec.data, chosenVideoModel), null, 2)}
|
|
73635
73865
|
`, "utf-8");
|
|
@@ -73641,10 +73871,10 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73641
73871
|
// that stops the whole canvas. The same sheet model every other scaffold uses.
|
|
73642
73872
|
sheetModel: ACTOR_SHEET_MODEL,
|
|
73643
73873
|
videoModel: chosenVideoModel,
|
|
73644
|
-
overlayCompositionPath:
|
|
73645
|
-
captionsCompositionPath:
|
|
73646
|
-
blueprintPath:
|
|
73647
|
-
blueprintStylePath:
|
|
73874
|
+
overlayCompositionPath: path32.relative(outDir, compositionDest),
|
|
73875
|
+
captionsCompositionPath: path32.relative(outDir, captionsDest),
|
|
73876
|
+
blueprintPath: path32.relative(outDir, blueprintPath2),
|
|
73877
|
+
blueprintStylePath: path32.relative(outDir, blueprintStylePath),
|
|
73648
73878
|
aspect: spec.data.format.aspect_ratio,
|
|
73649
73879
|
resolution: spec.data.format.resolution,
|
|
73650
73880
|
// A beat is one clause and one card, so the cap has to clear a whole clause.
|
|
@@ -73683,17 +73913,17 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73683
73913
|
// the stock voice this exists to avoid. It is to convert the LATER extracts onto the
|
|
73684
73914
|
// first beat of the same speaker, which keeps a voice the clip really produced.
|
|
73685
73915
|
...avatar ? { keepRenderedVoice: true } : {},
|
|
73686
|
-
...spec.data.voiceover === false ? { staticTranscriptPath:
|
|
73916
|
+
...spec.data.voiceover === false ? { staticTranscriptPath: path32.relative(outDir, transcriptPath) } : {}
|
|
73687
73917
|
};
|
|
73688
73918
|
const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
|
|
73689
73919
|
const renderNode5 = canvas.nodes.find((n) => n.type === "hyperframe_render");
|
|
73690
|
-
const renderedDir = renderNode5 ?
|
|
73920
|
+
const renderedDir = renderNode5 ? path32.join(outDir, String(renderNode5.params?.composition ?? "")) : null;
|
|
73691
73921
|
let staged = null;
|
|
73692
73922
|
let markIsLight = false;
|
|
73693
73923
|
if (logoPath && renderedDir) {
|
|
73694
|
-
const ext =
|
|
73924
|
+
const ext = path32.extname(logoPath) || ".png";
|
|
73695
73925
|
try {
|
|
73696
|
-
await copyFile(logoPath,
|
|
73926
|
+
await copyFile(logoPath, path32.join(renderedDir, `brand-mark${ext}`));
|
|
73697
73927
|
staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
|
|
73698
73928
|
markIsLight = await markInkIsLight(logoPath);
|
|
73699
73929
|
} catch {
|
|
@@ -73712,12 +73942,12 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73712
73942
|
plateIsDark: plateIsDark(spec.data.brand?.palette)
|
|
73713
73943
|
}) : "";
|
|
73714
73944
|
if (overlayHtml && renderedDir) {
|
|
73715
|
-
const indexPath =
|
|
73716
|
-
const html = await
|
|
73945
|
+
const indexPath = path32.join(renderedDir, "index.html");
|
|
73946
|
+
const html = await readFile26(indexPath, "utf-8");
|
|
73717
73947
|
await writeFile14(indexPath, injectAdBrandOverlay(html, overlayHtml, { aspect: outAr, ...face ? { face: AD_DISPLAY_FAMILY } : {} }), "utf-8");
|
|
73718
73948
|
}
|
|
73719
73949
|
await writeFile14(
|
|
73720
|
-
|
|
73950
|
+
path32.join(outDir, REBUILD_FILE),
|
|
73721
73951
|
`${JSON.stringify({ elements: adSpecCastElements(spec.data, avatar), opts }, null, 2)}
|
|
73722
73952
|
`,
|
|
73723
73953
|
"utf8"
|
|
@@ -73747,7 +73977,7 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73747
73977
|
return;
|
|
73748
73978
|
}
|
|
73749
73979
|
if (args8.slug) {
|
|
73750
|
-
const definitionPath =
|
|
73980
|
+
const definitionPath = path32.join(outDir, "_definition.md");
|
|
73751
73981
|
const definitionExists = await stat5(definitionPath).then(() => true, () => false);
|
|
73752
73982
|
if (!definitionExists) {
|
|
73753
73983
|
await writeFile14(
|
|
@@ -73865,9 +74095,9 @@ async function fetchClientLogo() {
|
|
|
73865
74095
|
if (!response.ok) return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
73866
74096
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
73867
74097
|
const extension = /\.(svg|png|jpg|jpeg|webp)(?:\?|$)/i.exec(decision.url)?.[1]?.toLowerCase() ?? "svg";
|
|
73868
|
-
const dir =
|
|
74098
|
+
const dir = path32.join(BRAND_DIR, "logos");
|
|
73869
74099
|
await mkdir9(dir, { recursive: true });
|
|
73870
|
-
const file =
|
|
74100
|
+
const file = path32.join(dir, `brand-mark.${extension}`);
|
|
73871
74101
|
await writeFile14(file, bytes);
|
|
73872
74102
|
return { ok: true, path: file };
|
|
73873
74103
|
} catch {
|
|
@@ -73886,14 +74116,14 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
73886
74116
|
if (!face) return null;
|
|
73887
74117
|
try {
|
|
73888
74118
|
if ("path" in face.slot) {
|
|
73889
|
-
const file2 = `brand-face${
|
|
73890
|
-
await copyFile(face.slot.path,
|
|
73891
|
-
return { file: file2, label: `the font file \`${
|
|
74119
|
+
const file2 = `brand-face${path32.extname(face.slot.path).toLowerCase()}`;
|
|
74120
|
+
await copyFile(face.slot.path, path32.join(compositionDir, file2));
|
|
74121
|
+
return { file: file2, label: `the font file \`${path32.relative(root, face.slot.path)}\`` };
|
|
73892
74122
|
}
|
|
73893
74123
|
const { buffer } = await fetchExternalBytes(face.slot.url, { maxBytes: MAX_FONT_BYTES });
|
|
73894
74124
|
const extension = /\.(woff2|woff|ttf|otf)(?:\?|$)/i.exec(face.slot.url)?.[1]?.toLowerCase() ?? "woff2";
|
|
73895
74125
|
const file = `brand-face.${extension}`;
|
|
73896
|
-
await writeFile14(
|
|
74126
|
+
await writeFile14(path32.join(compositionDir, file), buffer);
|
|
73897
74127
|
return { file, label: `${face.family ?? "the brand's typeface"}, loaded as a web font` };
|
|
73898
74128
|
} catch {
|
|
73899
74129
|
return null;
|
|
@@ -73901,9 +74131,9 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
73901
74131
|
}
|
|
73902
74132
|
|
|
73903
74133
|
// src/commands/canvas/scaffold-video.ts
|
|
73904
|
-
import { cp as cp2, mkdir as mkdir10, readFile as
|
|
74134
|
+
import { cp as cp2, mkdir as mkdir10, readFile as readFile29, rm as rm8, writeFile as writeFile15 } from "fs/promises";
|
|
73905
74135
|
import { tmpdir as tmpdir3 } from "os";
|
|
73906
|
-
import
|
|
74136
|
+
import path34 from "path";
|
|
73907
74137
|
import { defineCommand as defineCommand136 } from "citty";
|
|
73908
74138
|
|
|
73909
74139
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -73943,7 +74173,7 @@ function routeVideoModel(input) {
|
|
|
73943
74173
|
|
|
73944
74174
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
73945
74175
|
import { execFile as execFile3 } from "child_process";
|
|
73946
|
-
import { mkdtemp as mkdtemp2, readdir as readdir12, readFile as
|
|
74176
|
+
import { mkdtemp as mkdtemp2, readdir as readdir12, readFile as readFile27, rm as rm7 } from "fs/promises";
|
|
73947
74177
|
import { tmpdir as tmpdir2 } from "os";
|
|
73948
74178
|
import { join as join3 } from "path";
|
|
73949
74179
|
import { promisify as promisify3 } from "util";
|
|
@@ -74019,7 +74249,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
74019
74249
|
);
|
|
74020
74250
|
const csvName = (await readdir12(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
74021
74251
|
if (!csvName) return [];
|
|
74022
|
-
return parsePySceneDetectCsvCuts(await
|
|
74252
|
+
return parsePySceneDetectCsvCuts(await readFile27(join3(outDir, csvName), "utf-8"));
|
|
74023
74253
|
} finally {
|
|
74024
74254
|
await rm7(outDir, { recursive: true, force: true });
|
|
74025
74255
|
}
|
|
@@ -74043,8 +74273,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
74043
74273
|
}
|
|
74044
74274
|
|
|
74045
74275
|
// src/commands/canvas/gitignore.ts
|
|
74046
|
-
import { appendFile, readFile as
|
|
74047
|
-
import
|
|
74276
|
+
import { appendFile, readFile as readFile28 } from "fs/promises";
|
|
74277
|
+
import path33 from "path";
|
|
74048
74278
|
function missingGitignoreEntries(existing, entries) {
|
|
74049
74279
|
const present2 = new Set(
|
|
74050
74280
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -74052,10 +74282,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
74052
74282
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
74053
74283
|
}
|
|
74054
74284
|
async function ensureGitignore(dir, entries) {
|
|
74055
|
-
const file =
|
|
74285
|
+
const file = path33.join(dir, ".gitignore");
|
|
74056
74286
|
let existing;
|
|
74057
74287
|
try {
|
|
74058
|
-
existing = await
|
|
74288
|
+
existing = await readFile28(file, "utf8");
|
|
74059
74289
|
} catch {
|
|
74060
74290
|
return;
|
|
74061
74291
|
}
|
|
@@ -74097,7 +74327,7 @@ async function loadTranscriptBestEffort(ref2) {
|
|
|
74097
74327
|
async function stageCaptions(outDir, transcript) {
|
|
74098
74328
|
const text2 = transcript?.trim();
|
|
74099
74329
|
if (!text2 || text2 === "[]") return {};
|
|
74100
|
-
const compositionPath =
|
|
74330
|
+
const compositionPath = path34.join(outDir, "tiktok-captions-composition");
|
|
74101
74331
|
await cp2(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
74102
74332
|
return { compositionPath };
|
|
74103
74333
|
}
|
|
@@ -74133,7 +74363,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
74133
74363
|
"video/x-matroska": ".mkv"
|
|
74134
74364
|
};
|
|
74135
74365
|
function referenceVideoExt(url, contentType) {
|
|
74136
|
-
const fromPath =
|
|
74366
|
+
const fromPath = path34.extname(new URL(url).pathname).toLowerCase();
|
|
74137
74367
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
74138
74368
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
74139
74369
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -74159,7 +74389,7 @@ function framesModeReport(canvas, requested) {
|
|
|
74159
74389
|
return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
|
|
74160
74390
|
}
|
|
74161
74391
|
async function materializeReferenceVideo(fileArg2) {
|
|
74162
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
74392
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path34.resolve(fileArg2);
|
|
74163
74393
|
let bytes;
|
|
74164
74394
|
let contentType;
|
|
74165
74395
|
try {
|
|
@@ -74171,7 +74401,7 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
74171
74401
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
74172
74402
|
}
|
|
74173
74403
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
74174
|
-
const dest =
|
|
74404
|
+
const dest = path34.join(
|
|
74175
74405
|
tmpdir3(),
|
|
74176
74406
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
74177
74407
|
);
|
|
@@ -74398,12 +74628,12 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74398
74628
|
} catch (e) {
|
|
74399
74629
|
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
74400
74630
|
}
|
|
74401
|
-
const base =
|
|
74402
|
-
const outPath = args8.out ?
|
|
74631
|
+
const base = path34.basename(videoPath, path34.extname(videoPath));
|
|
74632
|
+
const outPath = args8.out ? path34.resolve(String(args8.out)) : slug ? path34.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path34.join(path34.dirname(videoPath), `${base}.video.canvas.json`);
|
|
74403
74633
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
74404
|
-
const outDir =
|
|
74405
|
-
const blueprintPath2 =
|
|
74406
|
-
const blueprintStylePath =
|
|
74634
|
+
const outDir = path34.dirname(outPath);
|
|
74635
|
+
const blueprintPath2 = path34.join(outDir, "prompt.json");
|
|
74636
|
+
const blueprintStylePath = path34.join(outDir, "prompt.style.json");
|
|
74407
74637
|
const frames = args8.frames === "reuse" ? "reuse" : "generate";
|
|
74408
74638
|
const maxScenes = args8["max-scenes"] ? Number(args8["max-scenes"]) : void 0;
|
|
74409
74639
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -74472,12 +74702,12 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74472
74702
|
`
|
|
74473
74703
|
);
|
|
74474
74704
|
}
|
|
74475
|
-
const compositionDest =
|
|
74705
|
+
const compositionDest = path34.join(outDir, "video-overlay-composition");
|
|
74476
74706
|
await cp2(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
74477
74707
|
await stampCompositionDims(compositionDest, outDims);
|
|
74478
|
-
const indexPath =
|
|
74708
|
+
const indexPath = path34.join(compositionDest, "index.html");
|
|
74479
74709
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript), videoModel });
|
|
74480
|
-
const indexHtml = await
|
|
74710
|
+
const indexHtml = await readFile29(indexPath, "utf8");
|
|
74481
74711
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
74482
74712
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
74483
74713
|
fail4(
|
|
@@ -74491,10 +74721,10 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74491
74721
|
const opts = {
|
|
74492
74722
|
imageModel,
|
|
74493
74723
|
videoModel,
|
|
74494
|
-
overlayCompositionPath:
|
|
74495
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
74496
|
-
blueprintPath:
|
|
74497
|
-
blueprintStylePath:
|
|
74724
|
+
overlayCompositionPath: path34.relative(outDir, compositionDest),
|
|
74725
|
+
captionsCompositionPath: captions.compositionPath ? path34.relative(outDir, captions.compositionPath) : void 0,
|
|
74726
|
+
blueprintPath: path34.relative(outDir, blueprintPath2),
|
|
74727
|
+
blueprintStylePath: path34.relative(outDir, blueprintStylePath),
|
|
74498
74728
|
frames,
|
|
74499
74729
|
ambient: Boolean(args8.ambient),
|
|
74500
74730
|
seamDedup: resolveSeamDedup(args8["seam-dedup"]),
|
|
@@ -74522,7 +74752,7 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74522
74752
|
await writeFile15(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
74523
74753
|
`, "utf8");
|
|
74524
74754
|
await writeFile15(
|
|
74525
|
-
|
|
74755
|
+
path34.join(outDir, REBUILD_FILE),
|
|
74526
74756
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
74527
74757
|
`,
|
|
74528
74758
|
"utf8"
|
|
@@ -74549,7 +74779,7 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74549
74779
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
74550
74780
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
74551
74781
|
if (slug) {
|
|
74552
|
-
const definitionPath =
|
|
74782
|
+
const definitionPath = path34.join(outDir, "_definition.md");
|
|
74553
74783
|
if (!await fileExists(definitionPath)) {
|
|
74554
74784
|
await writeFile15(
|
|
74555
74785
|
definitionPath,
|
|
@@ -74605,7 +74835,7 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74605
74835
|
graph: canvas.metadata?.video?.graph_stats
|
|
74606
74836
|
},
|
|
74607
74837
|
checklist: {
|
|
74608
|
-
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 \`${
|
|
74838
|
+
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 \`${path34.basename(blueprintPath2)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path34.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 ${path34.basename(blueprintStylePath)}; both are regenerated.`,
|
|
74609
74839
|
recurring_elements_to_supply: report.elements,
|
|
74610
74840
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
74611
74841
|
scene: d.scene,
|
|
@@ -74642,8 +74872,8 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74642
74872
|
});
|
|
74643
74873
|
|
|
74644
74874
|
// src/commands/canvas/set-prompt.ts
|
|
74645
|
-
import { readFile as
|
|
74646
|
-
import
|
|
74875
|
+
import { readFile as readFile30, writeFile as writeFile16 } from "fs/promises";
|
|
74876
|
+
import path35 from "path";
|
|
74647
74877
|
import { defineCommand as defineCommand137 } from "citty";
|
|
74648
74878
|
function setNodePrompt(canvas, nodeId, text2) {
|
|
74649
74879
|
const nodes = canvas?.nodes;
|
|
@@ -74672,8 +74902,8 @@ var SetPromptError = class extends Error {
|
|
|
74672
74902
|
}
|
|
74673
74903
|
};
|
|
74674
74904
|
async function setPromptInCanvasFile(canvasPath, nodeId, text2) {
|
|
74675
|
-
const filePath =
|
|
74676
|
-
const original = await
|
|
74905
|
+
const filePath = path35.resolve(canvasPath);
|
|
74906
|
+
const original = await readFile30(filePath, "utf8");
|
|
74677
74907
|
let canvas;
|
|
74678
74908
|
try {
|
|
74679
74909
|
canvas = JSON.parse(original);
|
|
@@ -74740,7 +74970,7 @@ var setPromptCommand = defineCommand137({
|
|
|
74740
74970
|
},
|
|
74741
74971
|
async run({ args: args8 }) {
|
|
74742
74972
|
let text2;
|
|
74743
|
-
if (args8["text-file"]) text2 = await
|
|
74973
|
+
if (args8["text-file"]) text2 = await readFile30(path35.resolve(String(args8["text-file"])), "utf8");
|
|
74744
74974
|
else if (args8.text !== void 0) text2 = String(args8.text);
|
|
74745
74975
|
else {
|
|
74746
74976
|
process.stderr.write(
|
|
@@ -74797,7 +75027,7 @@ var staleCommand = defineCommand138({
|
|
|
74797
75027
|
});
|
|
74798
75028
|
|
|
74799
75029
|
// src/commands/canvas/validate.ts
|
|
74800
|
-
import
|
|
75030
|
+
import path36 from "path";
|
|
74801
75031
|
import { defineCommand as defineCommand139 } from "citty";
|
|
74802
75032
|
var validateCommand = defineCommand139({
|
|
74803
75033
|
meta: {
|
|
@@ -74806,7 +75036,7 @@ var validateCommand = defineCommand139({
|
|
|
74806
75036
|
},
|
|
74807
75037
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
74808
75038
|
async run({ args: args8 }) {
|
|
74809
|
-
const filePath =
|
|
75039
|
+
const filePath = path36.resolve(String(args8.file));
|
|
74810
75040
|
let prepared;
|
|
74811
75041
|
try {
|
|
74812
75042
|
prepared = await prepareCanvas(filePath, { log: (line) => process.stderr.write(`${line}
|
|
@@ -75368,7 +75598,7 @@ var feedbackCommand = defineCommand144({
|
|
|
75368
75598
|
|
|
75369
75599
|
// src/commands/creatives/performance.ts
|
|
75370
75600
|
import { readFileSync as readFileSync16 } from "fs";
|
|
75371
|
-
import
|
|
75601
|
+
import path37 from "path";
|
|
75372
75602
|
import { defineCommand as defineCommand145 } from "citty";
|
|
75373
75603
|
import { parse as parseYaml2 } from "yaml";
|
|
75374
75604
|
var DESCRIPTION4 = `How each Baker creative is doing in the live Meta ads that show it: spend, impressions, CTR, CPC, results, cost per result, frequency and a fatigue verdict, next to the angle, hook, mechanism and dominant slot its definition set out to test. Video creatives add hookRate (percent of impressions that watched 3 seconds) and holdRate (percent of those that reached a ThruPlay). Use it before briefing new creatives, to learn which angle and mechanism to make next and which creatives to replace.
|
|
@@ -75421,7 +75651,7 @@ function definitionInsights(markdown) {
|
|
|
75421
75651
|
}
|
|
75422
75652
|
function readLocalDefinition(slug) {
|
|
75423
75653
|
try {
|
|
75424
|
-
return readFileSync16(
|
|
75654
|
+
return readFileSync16(path37.join(process.cwd(), "src", "creatives", slug, "_definition.md"), "utf8");
|
|
75425
75655
|
} catch {
|
|
75426
75656
|
return null;
|
|
75427
75657
|
}
|
|
@@ -75497,7 +75727,7 @@ import { Buffer as Buffer2 } from "buffer";
|
|
|
75497
75727
|
import { defineCommand as defineCommand146 } from "citty";
|
|
75498
75728
|
|
|
75499
75729
|
// src/commands/images/api.ts
|
|
75500
|
-
import { readFile as
|
|
75730
|
+
import { readFile as readFile31 } from "fs/promises";
|
|
75501
75731
|
import { basename, extname } from "path";
|
|
75502
75732
|
var imageProcessingTimeoutMs = 18e4;
|
|
75503
75733
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -75511,7 +75741,7 @@ var mimeMap = {
|
|
|
75511
75741
|
".avif": "image/avif"
|
|
75512
75742
|
};
|
|
75513
75743
|
var defaultImageApiDeps = {
|
|
75514
|
-
readFile:
|
|
75744
|
+
readFile: readFile31,
|
|
75515
75745
|
post: apiPost,
|
|
75516
75746
|
get: apiGet,
|
|
75517
75747
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -75577,7 +75807,7 @@ async function waitForReadyImage(deps, imageId, opts = {}) {
|
|
|
75577
75807
|
}
|
|
75578
75808
|
|
|
75579
75809
|
// src/commands/videos/api.ts
|
|
75580
|
-
import { readFile as
|
|
75810
|
+
import { readFile as readFile32 } from "fs/promises";
|
|
75581
75811
|
import { basename as basename2, extname as extname2 } from "path";
|
|
75582
75812
|
var videoProcessingTimeoutMs = 18e4;
|
|
75583
75813
|
var videoReadyPollIntervalMs = 3e3;
|
|
@@ -75589,7 +75819,7 @@ var mimeMap2 = {
|
|
|
75589
75819
|
".mkv": "video/x-matroska"
|
|
75590
75820
|
};
|
|
75591
75821
|
var defaultVideoApiDeps = {
|
|
75592
|
-
readFile:
|
|
75822
|
+
readFile: readFile32,
|
|
75593
75823
|
post: apiPost,
|
|
75594
75824
|
get: apiGet,
|
|
75595
75825
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
@@ -75980,12 +76210,12 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
75980
76210
|
import { defineCommand as defineCommand149 } from "citty";
|
|
75981
76211
|
|
|
75982
76212
|
// src/commands/experiment/composition.ts
|
|
75983
|
-
import { readdir as readdir14, readFile as
|
|
75984
|
-
import
|
|
76213
|
+
import { readdir as readdir14, readFile as readFile34 } from "fs/promises";
|
|
76214
|
+
import path39 from "path";
|
|
75985
76215
|
|
|
75986
76216
|
// src/commands/landing/import-graph.ts
|
|
75987
|
-
import { readdir as readdir13, readFile as
|
|
75988
|
-
import
|
|
76217
|
+
import { readdir as readdir13, readFile as readFile33, stat as stat6 } from "fs/promises";
|
|
76218
|
+
import path38 from "path";
|
|
75989
76219
|
var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
|
|
75990
76220
|
var RESOLVE_CANDIDATES = [
|
|
75991
76221
|
"",
|
|
@@ -76018,7 +76248,7 @@ async function walkFiles(dir) {
|
|
|
76018
76248
|
}
|
|
76019
76249
|
const out = [];
|
|
76020
76250
|
for (const entry of entries) {
|
|
76021
|
-
const full =
|
|
76251
|
+
const full = path38.join(dir, entry.name);
|
|
76022
76252
|
if (entry.isDirectory()) {
|
|
76023
76253
|
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
76024
76254
|
out.push(...await walkFiles(full));
|
|
@@ -76032,8 +76262,8 @@ async function resolveSpecifier(specifier, fromFile, root) {
|
|
|
76032
76262
|
const bare = specifier.split("?")[0] ?? "";
|
|
76033
76263
|
if (bare.includes("*")) return null;
|
|
76034
76264
|
let base;
|
|
76035
|
-
if (bare.startsWith("/")) base =
|
|
76036
|
-
else if (bare.startsWith(".")) base =
|
|
76265
|
+
if (bare.startsWith("/")) base = path38.join(root, bare.slice(1));
|
|
76266
|
+
else if (bare.startsWith(".")) base = path38.resolve(path38.dirname(fromFile), bare);
|
|
76037
76267
|
else return null;
|
|
76038
76268
|
for (const suffix of RESOLVE_CANDIDATES) {
|
|
76039
76269
|
const candidate = base + suffix;
|
|
@@ -76055,13 +76285,13 @@ async function importedFiles(text2, file, root) {
|
|
|
76055
76285
|
}
|
|
76056
76286
|
async function landingGraphFiles(root, slug) {
|
|
76057
76287
|
const seen = /* @__PURE__ */ new Set();
|
|
76058
|
-
const queue = await walkFiles(
|
|
76288
|
+
const queue = await walkFiles(path38.join(root, "src/pages", slug));
|
|
76059
76289
|
while (queue.length > 0) {
|
|
76060
76290
|
const file = queue.pop();
|
|
76061
76291
|
if (file === void 0 || seen.has(file)) continue;
|
|
76062
76292
|
let text2;
|
|
76063
76293
|
try {
|
|
76064
|
-
text2 = await
|
|
76294
|
+
text2 = await readFile33(file, "utf8");
|
|
76065
76295
|
} catch {
|
|
76066
76296
|
continue;
|
|
76067
76297
|
}
|
|
@@ -76070,7 +76300,7 @@ async function landingGraphFiles(root, slug) {
|
|
|
76070
76300
|
if (!seen.has(target2)) queue.push(target2);
|
|
76071
76301
|
}
|
|
76072
76302
|
}
|
|
76073
|
-
return new Set([...seen].map((abs) =>
|
|
76303
|
+
return new Set([...seen].map((abs) => path38.relative(root, abs).split(path38.sep).join("/")));
|
|
76074
76304
|
}
|
|
76075
76305
|
|
|
76076
76306
|
// src/commands/experiment/composition.ts
|
|
@@ -76115,7 +76345,7 @@ function formsOnlyIn(these, those) {
|
|
|
76115
76345
|
}
|
|
76116
76346
|
async function knownFlows(root) {
|
|
76117
76347
|
try {
|
|
76118
|
-
const entries = await readdir14(
|
|
76348
|
+
const entries = await readdir14(path39.join(root, "src/lib/flow-engine/flows"), { withFileTypes: true });
|
|
76119
76349
|
return new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
76120
76350
|
} catch {
|
|
76121
76351
|
return /* @__PURE__ */ new Set();
|
|
@@ -76126,7 +76356,7 @@ async function readForms(root, slug) {
|
|
|
76126
76356
|
const [files, flows] = await Promise.all([landingGraphFiles(root, slug), knownFlows(root)]);
|
|
76127
76357
|
if (flows.size === 0) return [];
|
|
76128
76358
|
const texts = await Promise.all(
|
|
76129
|
-
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) =>
|
|
76359
|
+
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) => readFile34(path39.join(root, file), "utf8"))
|
|
76130
76360
|
);
|
|
76131
76361
|
return formsReferenced(texts, flows);
|
|
76132
76362
|
} catch {
|
|
@@ -76136,8 +76366,8 @@ async function readForms(root, slug) {
|
|
|
76136
76366
|
|
|
76137
76367
|
// src/commands/experiment/fold.ts
|
|
76138
76368
|
import { statSync } from "fs";
|
|
76139
|
-
import { cp as cp3, mkdir as mkdir11, readdir as readdir15, readFile as
|
|
76140
|
-
import
|
|
76369
|
+
import { cp as cp3, mkdir as mkdir11, readdir as readdir15, readFile as readFile35, rm as rm9, stat as stat7, writeFile as writeFile17 } from "fs/promises";
|
|
76370
|
+
import path41 from "path";
|
|
76141
76371
|
|
|
76142
76372
|
// ../api/src/experiments/variants.ts
|
|
76143
76373
|
var VARIANT_SEPARATOR = "--";
|
|
@@ -76162,21 +76392,21 @@ function nextVariantNumber(page, existingSlugs, archived) {
|
|
|
76162
76392
|
}
|
|
76163
76393
|
|
|
76164
76394
|
// src/commands/landing/repoint.ts
|
|
76165
|
-
import
|
|
76395
|
+
import path40 from "path";
|
|
76166
76396
|
var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
76167
76397
|
function isInside3(abs, dir) {
|
|
76168
|
-
return abs === dir || abs.startsWith(dir +
|
|
76398
|
+
return abs === dir || abs.startsWith(dir + path40.sep);
|
|
76169
76399
|
}
|
|
76170
76400
|
function toSpecifier(from, to) {
|
|
76171
|
-
const rel =
|
|
76401
|
+
const rel = path40.relative(from, to).split(path40.sep).join("/");
|
|
76172
76402
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
76173
76403
|
}
|
|
76174
76404
|
function repointSpecifier(spec, opts) {
|
|
76175
76405
|
if (!spec.startsWith(".")) return spec;
|
|
76176
|
-
const abs =
|
|
76406
|
+
const abs = path40.resolve(opts.fromDir, spec);
|
|
76177
76407
|
if (opts.forkedTargets?.has(abs)) {
|
|
76178
|
-
const withinControl =
|
|
76179
|
-
return toSpecifier(opts.toDir,
|
|
76408
|
+
const withinControl = path40.relative(opts.controlDir, abs);
|
|
76409
|
+
return toSpecifier(opts.toDir, path40.resolve(opts.variantDir, withinControl));
|
|
76180
76410
|
}
|
|
76181
76411
|
if (!isInside3(abs, opts.controlDir)) return spec;
|
|
76182
76412
|
return toSpecifier(opts.toDir, abs);
|
|
@@ -76188,9 +76418,9 @@ function repointFile(text2, opts) {
|
|
|
76188
76418
|
}
|
|
76189
76419
|
function repointMoved(text2, opts) {
|
|
76190
76420
|
return text2.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
|
|
76191
|
-
const abs =
|
|
76192
|
-
const within =
|
|
76193
|
-
const target2 = within.startsWith("..") ||
|
|
76421
|
+
const abs = path40.resolve(opts.fromDir, spec);
|
|
76422
|
+
const within = path40.relative(opts.movedFrom, abs);
|
|
76423
|
+
const target2 = within.startsWith("..") || path40.isAbsolute(within) ? abs : path40.resolve(opts.movedTo, within);
|
|
76194
76424
|
return `${quote}${toSpecifier(opts.toDir, target2)}${quote}`;
|
|
76195
76425
|
});
|
|
76196
76426
|
}
|
|
@@ -76199,7 +76429,7 @@ function repointMoved(text2, opts) {
|
|
|
76199
76429
|
var ARCHIVE_ROOT = "src/_variants";
|
|
76200
76430
|
var QUOTED_RELATIVE2 = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
76201
76431
|
function posix2(p) {
|
|
76202
|
-
return p.split(
|
|
76432
|
+
return p.split(path41.sep).join("/");
|
|
76203
76433
|
}
|
|
76204
76434
|
async function isDir(p) {
|
|
76205
76435
|
try {
|
|
@@ -76218,39 +76448,39 @@ async function filesUnder(dir, prefix = "") {
|
|
|
76218
76448
|
const out = [];
|
|
76219
76449
|
for (const entry of entries) {
|
|
76220
76450
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
76221
|
-
if (entry.isDirectory()) out.push(...await filesUnder(
|
|
76451
|
+
if (entry.isDirectory()) out.push(...await filesUnder(path41.join(dir, entry.name), rel));
|
|
76222
76452
|
else out.push(rel);
|
|
76223
76453
|
}
|
|
76224
76454
|
return out.sort();
|
|
76225
76455
|
}
|
|
76226
76456
|
var TEXT_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte|md|json|css)$/;
|
|
76227
76457
|
function archiveRelPath(abs, base) {
|
|
76228
|
-
if (!abs.startsWith(base +
|
|
76229
|
-
return posix2(
|
|
76458
|
+
if (!abs.startsWith(base + path41.sep)) return null;
|
|
76459
|
+
return posix2(path41.relative(base, abs));
|
|
76230
76460
|
}
|
|
76231
76461
|
async function archiveArm(opts) {
|
|
76232
76462
|
const { root, armDir, pageDir, archiveDir, armSlug } = opts;
|
|
76233
76463
|
const targets = await archiveContents({ root, armDir, pageDir, armSlug });
|
|
76234
|
-
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs,
|
|
76464
|
+
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs, path41.join(archiveDir, rel)]));
|
|
76235
76465
|
const written = [];
|
|
76236
76466
|
for (const [rel, abs] of [...targets].sort()) {
|
|
76237
|
-
const dest =
|
|
76238
|
-
await mkdir11(
|
|
76467
|
+
const dest = path41.join(archiveDir, rel);
|
|
76468
|
+
await mkdir11(path41.dirname(dest), { recursive: true });
|
|
76239
76469
|
if (!TEXT_EXT.test(rel)) {
|
|
76240
76470
|
await cp3(abs, dest);
|
|
76241
76471
|
} else {
|
|
76242
|
-
const text2 = await
|
|
76472
|
+
const text2 = await readFile35(abs, "utf8");
|
|
76243
76473
|
await writeFile17(dest, repointIntoArchive(text2, abs, dest, archivedBySource), "utf8");
|
|
76244
76474
|
}
|
|
76245
|
-
written.push(posix2(
|
|
76475
|
+
written.push(posix2(path41.relative(root, dest)));
|
|
76246
76476
|
}
|
|
76247
76477
|
return written;
|
|
76248
76478
|
}
|
|
76249
76479
|
async function archiveContents(opts) {
|
|
76250
76480
|
const { root, armDir, pageDir, armSlug } = opts;
|
|
76251
76481
|
const graph = await landingGraphFiles(root, armSlug);
|
|
76252
|
-
const reachable = new Set([...graph].map((rel) =>
|
|
76253
|
-
for (const rel of await filesUnder(armDir)) reachable.add(
|
|
76482
|
+
const reachable = new Set([...graph].map((rel) => path41.resolve(root, rel)));
|
|
76483
|
+
for (const rel of await filesUnder(armDir)) reachable.add(path41.join(armDir, rel));
|
|
76254
76484
|
const sorted = [...reachable].sort();
|
|
76255
76485
|
const targets = /* @__PURE__ */ new Map();
|
|
76256
76486
|
for (const base of [pageDir, armDir]) {
|
|
@@ -76263,14 +76493,14 @@ async function archiveContents(opts) {
|
|
|
76263
76493
|
return targets;
|
|
76264
76494
|
}
|
|
76265
76495
|
function repointIntoArchive(text2, sourceAbs, destAbs, archivedBySource) {
|
|
76266
|
-
const fromDir =
|
|
76267
|
-
const toDir =
|
|
76496
|
+
const fromDir = path41.dirname(sourceAbs);
|
|
76497
|
+
const toDir = path41.dirname(destAbs);
|
|
76268
76498
|
return text2.replace(QUOTED_RELATIVE2, (match, quote, spec, offset) => {
|
|
76269
76499
|
const [specPath, suffix] = splitSuffix(spec);
|
|
76270
|
-
const target2 =
|
|
76500
|
+
const target2 = path41.resolve(fromDir, specPath);
|
|
76271
76501
|
const resolved = resolveInArchive(target2, archivedBySource) ?? resolveOnDisk(target2, isImportPosition(text2, offset));
|
|
76272
76502
|
if (resolved === null) return match;
|
|
76273
|
-
const rel = posix2(
|
|
76503
|
+
const rel = posix2(path41.relative(toDir, resolved));
|
|
76274
76504
|
return `${quote}${rel.startsWith(".") ? rel : `./${rel}`}${suffix}${quote}`;
|
|
76275
76505
|
});
|
|
76276
76506
|
}
|
|
@@ -76304,25 +76534,25 @@ async function promoteVariantFiles(opts) {
|
|
|
76304
76534
|
const written = [];
|
|
76305
76535
|
for (const rel of await filesUnder(variantDir)) {
|
|
76306
76536
|
if (rel === "_definition.md") continue;
|
|
76307
|
-
const from =
|
|
76308
|
-
const to =
|
|
76309
|
-
await mkdir11(
|
|
76537
|
+
const from = path41.join(variantDir, rel);
|
|
76538
|
+
const to = path41.join(pageDir, rel);
|
|
76539
|
+
await mkdir11(path41.dirname(to), { recursive: true });
|
|
76310
76540
|
if (!TEXT_EXT.test(rel)) {
|
|
76311
76541
|
await cp3(from, to);
|
|
76312
76542
|
} else {
|
|
76313
|
-
const text2 = await
|
|
76543
|
+
const text2 = await readFile35(from, "utf8");
|
|
76314
76544
|
await writeFile17(
|
|
76315
76545
|
to,
|
|
76316
76546
|
repointMoved(text2, {
|
|
76317
|
-
fromDir:
|
|
76318
|
-
toDir:
|
|
76547
|
+
fromDir: path41.dirname(from),
|
|
76548
|
+
toDir: path41.dirname(to),
|
|
76319
76549
|
movedFrom: variantDir,
|
|
76320
76550
|
movedTo: pageDir
|
|
76321
76551
|
}),
|
|
76322
76552
|
"utf8"
|
|
76323
76553
|
);
|
|
76324
76554
|
}
|
|
76325
|
-
written.push(posix2(
|
|
76555
|
+
written.push(posix2(path41.relative(root, to)));
|
|
76326
76556
|
}
|
|
76327
76557
|
return written;
|
|
76328
76558
|
}
|
|
@@ -76346,8 +76576,8 @@ function archiveRecord(target2, variantNumber) {
|
|
|
76346
76576
|
`;
|
|
76347
76577
|
}
|
|
76348
76578
|
async function foldExperiment(root, target2) {
|
|
76349
|
-
const pageDir =
|
|
76350
|
-
const variantDir =
|
|
76579
|
+
const pageDir = path41.resolve(root, "src/pages", target2.landingSlug);
|
|
76580
|
+
const variantDir = path41.resolve(root, "src/pages", target2.variantSlug);
|
|
76351
76581
|
const variantNumber = variantNumberOf(target2.variantSlug);
|
|
76352
76582
|
if (variantNumber === null) {
|
|
76353
76583
|
return {
|
|
@@ -76361,7 +76591,7 @@ async function foldExperiment(root, target2) {
|
|
|
76361
76591
|
return { error: `src/pages/${target2.variantSlug}/ is already gone \u2014 this test looks folded in already` };
|
|
76362
76592
|
}
|
|
76363
76593
|
const archiveKey = target2.landingInternalId ?? target2.landingSlug;
|
|
76364
|
-
const archiveDir =
|
|
76594
|
+
const archiveDir = path41.resolve(root, ARCHIVE_ROOT, archiveKey, String(variantNumber));
|
|
76365
76595
|
await rm9(archiveDir, { recursive: true, force: true });
|
|
76366
76596
|
await mkdir11(archiveDir, { recursive: true });
|
|
76367
76597
|
const written = await archiveArm({
|
|
@@ -76371,8 +76601,8 @@ async function foldExperiment(root, target2) {
|
|
|
76371
76601
|
archiveDir,
|
|
76372
76602
|
armSlug: target2.survivor === "variant" ? target2.landingSlug : target2.variantSlug
|
|
76373
76603
|
});
|
|
76374
|
-
await writeFile17(
|
|
76375
|
-
written.push(posix2(
|
|
76604
|
+
await writeFile17(path41.join(archiveDir, "_variant.json"), archiveRecord(target2, variantNumber), "utf8");
|
|
76605
|
+
written.push(posix2(path41.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
|
|
76376
76606
|
if (target2.survivor === "variant") {
|
|
76377
76607
|
written.push(...await promoteVariantFiles({ root, pageDir, variantDir }));
|
|
76378
76608
|
}
|
|
@@ -76380,7 +76610,7 @@ async function foldExperiment(root, target2) {
|
|
|
76380
76610
|
return {
|
|
76381
76611
|
experimentId: target2.experimentId,
|
|
76382
76612
|
page: target2.landingSlug,
|
|
76383
|
-
archivedTo: posix2(
|
|
76613
|
+
archivedTo: posix2(path41.join(ARCHIVE_ROOT, archiveKey, String(variantNumber))),
|
|
76384
76614
|
written,
|
|
76385
76615
|
removed: [`src/pages/${target2.variantSlug}`]
|
|
76386
76616
|
};
|
|
@@ -77152,11 +77382,11 @@ function parseEvidence(raw, rawArgs) {
|
|
|
77152
77382
|
}
|
|
77153
77383
|
|
|
77154
77384
|
// src/commands/experiment/identity.ts
|
|
77155
|
-
import { readFile as
|
|
77156
|
-
import
|
|
77385
|
+
import { readFile as readFile36 } from "fs/promises";
|
|
77386
|
+
import path42 from "path";
|
|
77157
77387
|
async function readPageInternalId(root, slug) {
|
|
77158
77388
|
try {
|
|
77159
|
-
const text2 = await
|
|
77389
|
+
const text2 = await readFile36(path42.resolve(root, "src", "pages", slug, "_definition.md"), "utf8");
|
|
77160
77390
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text2)?.[1];
|
|
77161
77391
|
} catch {
|
|
77162
77392
|
return void 0;
|
|
@@ -77164,8 +77394,8 @@ async function readPageInternalId(root, slug) {
|
|
|
77164
77394
|
}
|
|
77165
77395
|
|
|
77166
77396
|
// src/commands/experiment/measurement.ts
|
|
77167
|
-
import { readFile as
|
|
77168
|
-
import
|
|
77397
|
+
import { readFile as readFile37 } from "fs/promises";
|
|
77398
|
+
import path43 from "path";
|
|
77169
77399
|
function eventDeclarationsIn(source) {
|
|
77170
77400
|
const found = [];
|
|
77171
77401
|
const pattern = /\b(data-baker-(?:view|event))\s*=\s*["']([^"']+)["']/g;
|
|
@@ -77225,7 +77455,7 @@ function sectionsOf(files, slug) {
|
|
|
77225
77455
|
return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index."));
|
|
77226
77456
|
}
|
|
77227
77457
|
async function declarationsOf(root, files) {
|
|
77228
|
-
const sources = await Promise.all(files.map((file) =>
|
|
77458
|
+
const sources = await Promise.all(files.map((file) => readFile37(path43.join(root, file), "utf8").catch(() => "")));
|
|
77229
77459
|
return sources.flatMap(eventDeclarationsIn);
|
|
77230
77460
|
}
|
|
77231
77461
|
|
|
@@ -78277,10 +78507,10 @@ function parseValueExpression(raw) {
|
|
|
78277
78507
|
return parts.map(parsePart);
|
|
78278
78508
|
}
|
|
78279
78509
|
function trackingFieldIds() {
|
|
78280
|
-
const
|
|
78281
|
-
if (!existsSync9(
|
|
78510
|
+
const path58 = join4(flowsDir(), "..", "tracking.ts");
|
|
78511
|
+
if (!existsSync9(path58)) return null;
|
|
78282
78512
|
try {
|
|
78283
|
-
const source = readFileSync17(
|
|
78513
|
+
const source = readFileSync17(path58, "utf-8");
|
|
78284
78514
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
78285
78515
|
if (!block2) return null;
|
|
78286
78516
|
const ids2 = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -78862,13 +79092,13 @@ function specsFromFile(parsed) {
|
|
|
78862
79092
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
78863
79093
|
});
|
|
78864
79094
|
}
|
|
78865
|
-
function readSpecFile(
|
|
79095
|
+
function readSpecFile(path58) {
|
|
78866
79096
|
let raw;
|
|
78867
79097
|
try {
|
|
78868
|
-
raw =
|
|
79098
|
+
raw = path58 === "-" ? readFileSync18(0, "utf-8") : readFileSync18(path58, "utf-8");
|
|
78869
79099
|
} catch (error) {
|
|
78870
79100
|
refuse(
|
|
78871
|
-
`Could not read ${
|
|
79101
|
+
`Could not read ${path58 === "-" ? "the mapping from stdin" : `"${path58}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
78872
79102
|
);
|
|
78873
79103
|
}
|
|
78874
79104
|
let parsed;
|
|
@@ -78876,7 +79106,7 @@ function readSpecFile(path59) {
|
|
|
78876
79106
|
parsed = JSON.parse(raw);
|
|
78877
79107
|
} catch (error) {
|
|
78878
79108
|
refuse(
|
|
78879
|
-
`${
|
|
79109
|
+
`${path58 === "-" ? "stdin" : `"${path58}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
78880
79110
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
78881
79111
|
);
|
|
78882
79112
|
}
|
|
@@ -79175,21 +79405,21 @@ var ARRAY_FIELDS = [
|
|
|
79175
79405
|
"tagIds"
|
|
79176
79406
|
];
|
|
79177
79407
|
var ARRAY_OWNERS = ["", "body"];
|
|
79178
|
-
function dropUnsetOptionals(sideEffect,
|
|
79408
|
+
function dropUnsetOptionals(sideEffect, path58) {
|
|
79179
79409
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
79180
79410
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
79181
79411
|
delete sideEffect[key];
|
|
79182
|
-
return [{ path:
|
|
79412
|
+
return [{ path: path58, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
79183
79413
|
});
|
|
79184
79414
|
}
|
|
79185
|
-
function fillNulledArrays(target2, prefix,
|
|
79415
|
+
function fillNulledArrays(target2, prefix, path58) {
|
|
79186
79416
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
79187
79417
|
if (!(key in target2) || target2[key] !== null) return [];
|
|
79188
79418
|
target2[key] = [];
|
|
79189
|
-
return [{ path:
|
|
79419
|
+
return [{ path: path58, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
79190
79420
|
});
|
|
79191
79421
|
}
|
|
79192
|
-
function tidyEmailRecipients(sideEffect,
|
|
79422
|
+
function tidyEmailRecipients(sideEffect, path58) {
|
|
79193
79423
|
if (sideEffect.type !== "email") return [];
|
|
79194
79424
|
const config = sideEffect.encryptedConfig;
|
|
79195
79425
|
if (!config || typeof config !== "object" || Array.isArray(config)) return [];
|
|
@@ -79200,7 +79430,7 @@ function tidyEmailRecipients(sideEffect, path59) {
|
|
|
79200
79430
|
const tidied = to.join(", ");
|
|
79201
79431
|
if (tidied === raw) return [];
|
|
79202
79432
|
config.email = tidied;
|
|
79203
|
-
return [{ path:
|
|
79433
|
+
return [{ path: path58, change: "tidied the recipient list's spacing and separators" }];
|
|
79204
79434
|
}
|
|
79205
79435
|
function sideEffectsOf(node) {
|
|
79206
79436
|
if (!Array.isArray(node.sideEffects)) return [];
|
|
@@ -79209,13 +79439,13 @@ function sideEffectsOf(node) {
|
|
|
79209
79439
|
);
|
|
79210
79440
|
}
|
|
79211
79441
|
function normalizeSideEffect(sideEffect, where) {
|
|
79212
|
-
const
|
|
79442
|
+
const path58 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
79213
79443
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
79214
79444
|
const target2 = owner ? sideEffect[owner] : sideEffect;
|
|
79215
79445
|
if (!target2 || typeof target2 !== "object") return [];
|
|
79216
|
-
return fillNulledArrays(target2, owner ? `${owner}.` : "",
|
|
79446
|
+
return fillNulledArrays(target2, owner ? `${owner}.` : "", path58);
|
|
79217
79447
|
});
|
|
79218
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
79448
|
+
return [...dropUnsetOptionals(sideEffect, path58), ...arrays, ...tidyEmailRecipients(sideEffect, path58)];
|
|
79219
79449
|
}
|
|
79220
79450
|
function normalizeFlowTree(tree) {
|
|
79221
79451
|
const changes = [];
|
|
@@ -80607,10 +80837,10 @@ async function stageOps(ops) {
|
|
|
80607
80837
|
handleError5(err);
|
|
80608
80838
|
}
|
|
80609
80839
|
}
|
|
80610
|
-
async function draftAction2(
|
|
80840
|
+
async function draftAction2(path58, body, chat) {
|
|
80611
80841
|
const chatId = resolveChatId(chat);
|
|
80612
80842
|
try {
|
|
80613
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
80843
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path58, { chatId, ...body }));
|
|
80614
80844
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
80615
80845
|
return data;
|
|
80616
80846
|
} catch (err) {
|
|
@@ -83105,7 +83335,7 @@ function cropSprite(input, region) {
|
|
|
83105
83335
|
|
|
83106
83336
|
// src/lib/image/io.ts
|
|
83107
83337
|
import { randomBytes } from "crypto";
|
|
83108
|
-
import { glob as fsGlob, readFile as
|
|
83338
|
+
import { glob as fsGlob, readFile as readFile38, rename, stat as stat8, writeFile as writeFile18 } from "fs/promises";
|
|
83109
83339
|
import { dirname as dirname2, extname as extname3, join as join5, resolve as resolve4 } from "path";
|
|
83110
83340
|
var REMOTE_RE = /^https?:\/\//i;
|
|
83111
83341
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -83138,11 +83368,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
83138
83368
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
83139
83369
|
return buffer;
|
|
83140
83370
|
}
|
|
83141
|
-
return
|
|
83371
|
+
return readFile38(pathOrUrl);
|
|
83142
83372
|
}
|
|
83143
|
-
async function isDirectory(
|
|
83373
|
+
async function isDirectory(path58) {
|
|
83144
83374
|
try {
|
|
83145
|
-
const s = await stat8(
|
|
83375
|
+
const s = await stat8(path58);
|
|
83146
83376
|
return s.isDirectory();
|
|
83147
83377
|
} catch {
|
|
83148
83378
|
return false;
|
|
@@ -83570,13 +83800,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
83570
83800
|
}
|
|
83571
83801
|
function disambiguate(paths) {
|
|
83572
83802
|
const taken = /* @__PURE__ */ new Set();
|
|
83573
|
-
return paths.map((
|
|
83574
|
-
if (!taken.has(
|
|
83575
|
-
taken.add(
|
|
83576
|
-
return
|
|
83803
|
+
return paths.map((path58) => {
|
|
83804
|
+
if (!taken.has(path58)) {
|
|
83805
|
+
taken.add(path58);
|
|
83806
|
+
return path58;
|
|
83577
83807
|
}
|
|
83578
|
-
const ext = extname4(
|
|
83579
|
-
const stem =
|
|
83808
|
+
const ext = extname4(path58);
|
|
83809
|
+
const stem = path58.slice(0, path58.length - ext.length);
|
|
83580
83810
|
let n = 2;
|
|
83581
83811
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
83582
83812
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -83702,10 +83932,10 @@ async function runDownloads(plan) {
|
|
|
83702
83932
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
83703
83933
|
const downloaded = [];
|
|
83704
83934
|
for (const [index, item] of fetched.entries()) {
|
|
83705
|
-
const
|
|
83935
|
+
const path58 = paths[index] ?? item.path;
|
|
83706
83936
|
try {
|
|
83707
|
-
await atomicWrite(
|
|
83708
|
-
downloaded.push({ input: item.input, output:
|
|
83937
|
+
await atomicWrite(path58, item.buffer);
|
|
83938
|
+
downloaded.push({ input: item.input, output: path58, bytes: item.buffer.length, contentType: item.contentType });
|
|
83709
83939
|
} catch (err) {
|
|
83710
83940
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
83711
83941
|
}
|
|
@@ -86687,8 +86917,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
86687
86917
|
import { defineCommand as defineCommand207 } from "citty";
|
|
86688
86918
|
|
|
86689
86919
|
// src/commands/landing/critique.ts
|
|
86690
|
-
import { readdir as readdir17, readFile as
|
|
86691
|
-
import
|
|
86920
|
+
import { readdir as readdir17, readFile as readFile40, stat as stat10 } from "fs/promises";
|
|
86921
|
+
import path46 from "path";
|
|
86692
86922
|
import { defineCommand as defineCommand196 } from "citty";
|
|
86693
86923
|
|
|
86694
86924
|
// src/engine/landing/lib/constants.ts
|
|
@@ -87880,13 +88110,13 @@ function describeCounts(findings) {
|
|
|
87880
88110
|
|
|
87881
88111
|
// src/commands/landing/snapshot.ts
|
|
87882
88112
|
import { mkdir as mkdir12, rename as rename2, writeFile as writeFile19 } from "fs/promises";
|
|
87883
|
-
import
|
|
88113
|
+
import path44 from "path";
|
|
87884
88114
|
var CRITIC_VERSION = "4";
|
|
87885
88115
|
function critiqueCacheDir(projectRoot) {
|
|
87886
|
-
return
|
|
88116
|
+
return path44.join(projectRoot, ".cache", "landing-critique");
|
|
87887
88117
|
}
|
|
87888
88118
|
function snapshotPath(projectRoot, slug) {
|
|
87889
|
-
return
|
|
88119
|
+
return path44.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
87890
88120
|
}
|
|
87891
88121
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
87892
88122
|
await mkdir12(critiqueCacheDir(projectRoot), { recursive: true });
|
|
@@ -87898,8 +88128,8 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
|
87898
88128
|
}
|
|
87899
88129
|
|
|
87900
88130
|
// src/commands/landing/source-version.ts
|
|
87901
|
-
import { readdir as readdir16, readFile as
|
|
87902
|
-
import
|
|
88131
|
+
import { readdir as readdir16, readFile as readFile39, stat as stat9 } from "fs/promises";
|
|
88132
|
+
import path45 from "path";
|
|
87903
88133
|
var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
|
|
87904
88134
|
async function landingSourceRelPaths(root, slug) {
|
|
87905
88135
|
const files = await landingGraphFiles(root, slug);
|
|
@@ -87908,7 +88138,7 @@ async function landingSourceRelPaths(root, slug) {
|
|
|
87908
88138
|
async function readLandingSources(root, slug) {
|
|
87909
88139
|
const rel = await landingSourceRelPaths(root, slug);
|
|
87910
88140
|
const out = [];
|
|
87911
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
88141
|
+
for (const r of rel) out.push({ path: r, text: await readFile39(path45.join(root, r), "utf8") });
|
|
87912
88142
|
return out;
|
|
87913
88143
|
}
|
|
87914
88144
|
async function computeLandingSourceSha(root, slug) {
|
|
@@ -87917,7 +88147,7 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
87917
88147
|
for (const r of rel) {
|
|
87918
88148
|
let bytes;
|
|
87919
88149
|
try {
|
|
87920
|
-
bytes = await
|
|
88150
|
+
bytes = await readFile39(path45.join(root, r));
|
|
87921
88151
|
} catch {
|
|
87922
88152
|
bytes = Buffer.alloc(0);
|
|
87923
88153
|
}
|
|
@@ -87927,16 +88157,16 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
87927
88157
|
}
|
|
87928
88158
|
async function computeLegacyLandingSourceSha(landingDir) {
|
|
87929
88159
|
const rel = [];
|
|
87930
|
-
if (await isFile2(
|
|
87931
|
-
for (const abs of await walkAstro(
|
|
87932
|
-
rel.push(
|
|
88160
|
+
if (await isFile2(path45.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
88161
|
+
for (const abs of await walkAstro(path45.join(landingDir, "_components"))) {
|
|
88162
|
+
rel.push(path45.relative(landingDir, abs).split(path45.sep).join("/"));
|
|
87933
88163
|
}
|
|
87934
88164
|
rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
87935
88165
|
const parts = [];
|
|
87936
88166
|
for (const r of rel) {
|
|
87937
88167
|
let bytes;
|
|
87938
88168
|
try {
|
|
87939
|
-
bytes = await
|
|
88169
|
+
bytes = await readFile39(path45.join(landingDir, r));
|
|
87940
88170
|
} catch {
|
|
87941
88171
|
bytes = Buffer.alloc(0);
|
|
87942
88172
|
}
|
|
@@ -87960,7 +88190,7 @@ async function walkAstro(dir) {
|
|
|
87960
88190
|
}
|
|
87961
88191
|
const out = [];
|
|
87962
88192
|
for (const entry of entries) {
|
|
87963
|
-
const abs =
|
|
88193
|
+
const abs = path45.join(dir, entry.name);
|
|
87964
88194
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
87965
88195
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
87966
88196
|
}
|
|
@@ -88021,7 +88251,7 @@ var critiqueCommand2 = defineCommand196({
|
|
|
88021
88251
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
88022
88252
|
);
|
|
88023
88253
|
}
|
|
88024
|
-
if (!await isDir2(
|
|
88254
|
+
if (!await isDir2(path46.resolve(projectRoot, "src", "pages", slug))) {
|
|
88025
88255
|
fail8("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
88026
88256
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
88027
88257
|
});
|
|
@@ -88064,7 +88294,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
88064
88294
|
const [sources, compositionSha, sourceSha] = await Promise.all([
|
|
88065
88295
|
readLandingSources(projectRoot, slug),
|
|
88066
88296
|
computeLandingSourceSha(projectRoot, slug),
|
|
88067
|
-
computeLegacyLandingSourceSha(
|
|
88297
|
+
computeLegacyLandingSourceSha(path46.resolve(projectRoot, "src", "pages", slug))
|
|
88068
88298
|
]);
|
|
88069
88299
|
const report = critiqueLanding({ slug, sources, brand, competitors });
|
|
88070
88300
|
let snapshotFailed = false;
|
|
@@ -88084,7 +88314,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
88084
88314
|
return { slug, report, snapshotFailed };
|
|
88085
88315
|
}
|
|
88086
88316
|
async function readCompetitorNames(projectRoot) {
|
|
88087
|
-
const dir =
|
|
88317
|
+
const dir = path46.join(projectRoot, "src", "content", "competitors");
|
|
88088
88318
|
let entries;
|
|
88089
88319
|
try {
|
|
88090
88320
|
entries = (await readdir17(dir)).filter((f) => f.endsWith(".md"));
|
|
@@ -88093,9 +88323,9 @@ async function readCompetitorNames(projectRoot) {
|
|
|
88093
88323
|
}
|
|
88094
88324
|
const names = [];
|
|
88095
88325
|
for (const entry of entries) {
|
|
88096
|
-
names.push(
|
|
88326
|
+
names.push(path46.basename(entry, ".md").replace(/[-_]+/g, " "));
|
|
88097
88327
|
try {
|
|
88098
|
-
const head = (await
|
|
88328
|
+
const head = (await readFile40(path46.join(dir, entry), "utf8")).slice(0, 2e3);
|
|
88099
88329
|
const titled = /^\s*(?:title|name)\s*:\s*["']?([^"'\n]+)["']?\s*$/im.exec(head);
|
|
88100
88330
|
if (titled?.[1]) names.push(titled[1].trim());
|
|
88101
88331
|
} catch {
|
|
@@ -88105,7 +88335,7 @@ async function readCompetitorNames(projectRoot) {
|
|
|
88105
88335
|
}
|
|
88106
88336
|
async function listLandingSlugs(projectRoot) {
|
|
88107
88337
|
try {
|
|
88108
|
-
const entries = await readdir17(
|
|
88338
|
+
const entries = await readdir17(path46.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
88109
88339
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
88110
88340
|
} catch {
|
|
88111
88341
|
return [];
|
|
@@ -88257,7 +88487,7 @@ var addCommand = defineCommand197({
|
|
|
88257
88487
|
|
|
88258
88488
|
// src/commands/landing/inspiration/code.ts
|
|
88259
88489
|
import { mkdir as mkdir13, writeFile as writeFile20 } from "fs/promises";
|
|
88260
|
-
import
|
|
88490
|
+
import path47 from "path";
|
|
88261
88491
|
import { defineCommand as defineCommand198 } from "citty";
|
|
88262
88492
|
registerSchema({
|
|
88263
88493
|
command: "landing.inspiration.code",
|
|
@@ -88280,9 +88510,9 @@ var codeCommand = defineCommand198({
|
|
|
88280
88510
|
try {
|
|
88281
88511
|
const id = args8.id;
|
|
88282
88512
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
88283
|
-
const dir =
|
|
88513
|
+
const dir = path47.join(process.cwd(), ".baker", "inspiration", id);
|
|
88284
88514
|
await mkdir13(dir, { recursive: true });
|
|
88285
|
-
const file =
|
|
88515
|
+
const file = path47.join(dir, "section.html");
|
|
88286
88516
|
await writeFile20(file, data.html);
|
|
88287
88517
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
88288
88518
|
const fidelity = fidelityHint(data.fidelity);
|
|
@@ -88292,7 +88522,7 @@ var codeCommand = defineCommand198({
|
|
|
88292
88522
|
ok: true,
|
|
88293
88523
|
data: {
|
|
88294
88524
|
id,
|
|
88295
|
-
file:
|
|
88525
|
+
file: path47.relative(process.cwd(), file),
|
|
88296
88526
|
bytes: data.html.length,
|
|
88297
88527
|
fidelity: data.fidelity,
|
|
88298
88528
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -88724,7 +88954,7 @@ function classifyCaptureFailure(error) {
|
|
|
88724
88954
|
|
|
88725
88955
|
// src/engine/landing-library/run.ts
|
|
88726
88956
|
import { mkdir as mkdir14, writeFile as writeFile22 } from "fs/promises";
|
|
88727
|
-
import
|
|
88957
|
+
import path49 from "path";
|
|
88728
88958
|
|
|
88729
88959
|
// ../proxy/src/preflight.ts
|
|
88730
88960
|
import http from "http";
|
|
@@ -90140,9 +90370,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
90140
90370
|
|
|
90141
90371
|
// src/engine/landing-library/report.ts
|
|
90142
90372
|
import { writeFile as writeFile21 } from "fs/promises";
|
|
90143
|
-
import
|
|
90373
|
+
import path48 from "path";
|
|
90144
90374
|
async function writeCaptureReport(manifest, outDir) {
|
|
90145
|
-
const file =
|
|
90375
|
+
const file = path48.join(outDir, "report.html");
|
|
90146
90376
|
await writeFile21(file, renderReport(manifest));
|
|
90147
90377
|
return file;
|
|
90148
90378
|
}
|
|
@@ -90321,32 +90551,32 @@ async function reproducePage(args8) {
|
|
|
90321
90551
|
const { browser, page, outDir, pageUrl, livePageShot } = args8;
|
|
90322
90552
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
90323
90553
|
if (!built) return { bundle: null, fidelity: null };
|
|
90324
|
-
await writeFile22(
|
|
90554
|
+
await writeFile22(path49.join(outDir, "page.html"), built.html);
|
|
90325
90555
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
90326
90556
|
wholePage: true,
|
|
90327
90557
|
timeoutMs: 6e4
|
|
90328
90558
|
});
|
|
90329
90559
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
90330
|
-
await writeFile22(
|
|
90560
|
+
await writeFile22(path49.join(outDir, "page-rendered.png"), rendered);
|
|
90331
90561
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
90332
90562
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
90333
90563
|
}
|
|
90334
90564
|
async function captureOneSection(args8) {
|
|
90335
90565
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args8;
|
|
90336
|
-
const dir =
|
|
90566
|
+
const dir = path49.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
90337
90567
|
await mkdir14(dir, { recursive: true });
|
|
90338
90568
|
const desktop = await captureSection(page, candidate);
|
|
90339
|
-
if (desktop) await writeFile22(
|
|
90569
|
+
if (desktop) await writeFile22(path49.join(dir, "desktop.png"), desktop);
|
|
90340
90570
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
90341
90571
|
const motion = await collectMotion(page, candidate.selector);
|
|
90342
90572
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
90343
90573
|
let fidelity = null;
|
|
90344
90574
|
let fidelityNote;
|
|
90345
90575
|
if (built) {
|
|
90346
|
-
await writeFile22(
|
|
90576
|
+
await writeFile22(path49.join(dir, "section.html"), built.html);
|
|
90347
90577
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
90348
90578
|
if (rendered && desktop) {
|
|
90349
|
-
await writeFile22(
|
|
90579
|
+
await writeFile22(path49.join(dir, "section-rendered.png"), rendered);
|
|
90350
90580
|
const result = await scoreFidelity(desktop, rendered);
|
|
90351
90581
|
fidelity = result.score;
|
|
90352
90582
|
fidelityNote = result.note;
|
|
@@ -90354,9 +90584,9 @@ async function captureOneSection(args8) {
|
|
|
90354
90584
|
}
|
|
90355
90585
|
return {
|
|
90356
90586
|
...candidate,
|
|
90357
|
-
desktopShot: desktop ?
|
|
90587
|
+
desktopShot: desktop ? path49.relative(outDir, path49.join(dir, "desktop.png")) : null,
|
|
90358
90588
|
mobileShot: null,
|
|
90359
|
-
bundle: built ?
|
|
90589
|
+
bundle: built ? path49.relative(outDir, path49.join(dir, "section.html")) : null,
|
|
90360
90590
|
fidelity,
|
|
90361
90591
|
...fidelityNote ? { fidelityNote } : {},
|
|
90362
90592
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -90375,9 +90605,9 @@ async function captureMobileShots(args8) {
|
|
|
90375
90605
|
for (const section of sections) {
|
|
90376
90606
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
90377
90607
|
if (!shot) continue;
|
|
90378
|
-
const file =
|
|
90608
|
+
const file = path49.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
|
|
90379
90609
|
await writeFile22(file, shot);
|
|
90380
|
-
section.mobileShot =
|
|
90610
|
+
section.mobileShot = path49.relative(outDir, file);
|
|
90381
90611
|
}
|
|
90382
90612
|
} finally {
|
|
90383
90613
|
await mobile.context.close();
|
|
@@ -90392,10 +90622,10 @@ async function captureMotionTakes(args8) {
|
|
|
90392
90622
|
const filmOne = async (section) => {
|
|
90393
90623
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
90394
90624
|
if (!take) return;
|
|
90395
|
-
const dir =
|
|
90396
|
-
const file =
|
|
90625
|
+
const dir = path49.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
90626
|
+
const file = path49.join(dir, "motion-filmstrip.png");
|
|
90397
90627
|
await writeFile22(file, take.filmstrip);
|
|
90398
|
-
section.motionFilmstrip =
|
|
90628
|
+
section.motionFilmstrip = path49.relative(outDir, file);
|
|
90399
90629
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
90400
90630
|
};
|
|
90401
90631
|
const queue = [...moving];
|
|
@@ -90452,7 +90682,7 @@ async function captureAlternateViews(args8) {
|
|
|
90452
90682
|
async function reproduceWholePage(args8) {
|
|
90453
90683
|
const { browser, page, outDir, pageUrl, withCode, log } = args8;
|
|
90454
90684
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
90455
|
-
if (fullPage) await writeFile22(
|
|
90685
|
+
if (fullPage) await writeFile22(path49.join(outDir, "full-page.png"), fullPage);
|
|
90456
90686
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
90457
90687
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
90458
90688
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -90523,7 +90753,7 @@ async function openViaLadder(args8) {
|
|
|
90523
90753
|
async function scrapeLanding(options) {
|
|
90524
90754
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
90525
90755
|
const log = options.onProgress ?? (() => void 0);
|
|
90526
|
-
const sectionsDir =
|
|
90756
|
+
const sectionsDir = path49.join(options.outDir, "sections");
|
|
90527
90757
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
90528
90758
|
if (nonPublic) {
|
|
90529
90759
|
throw new BlockedPageError({
|
|
@@ -90585,7 +90815,7 @@ async function scrapeLanding(options) {
|
|
|
90585
90815
|
security: prepared.security,
|
|
90586
90816
|
captureTier: tier
|
|
90587
90817
|
};
|
|
90588
|
-
await writeFile22(
|
|
90818
|
+
await writeFile22(path49.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
90589
90819
|
`);
|
|
90590
90820
|
if (options.report !== false) {
|
|
90591
90821
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -90600,28 +90830,28 @@ async function scrapeLanding(options) {
|
|
|
90600
90830
|
|
|
90601
90831
|
// src/commands/landing/inspiration/captureOut.ts
|
|
90602
90832
|
import { existsSync as existsSync12 } from "fs";
|
|
90603
|
-
import
|
|
90833
|
+
import path50 from "path";
|
|
90604
90834
|
var SCRATCH_DIR = ".baker";
|
|
90605
90835
|
function isWithin(parent, target2) {
|
|
90606
|
-
const relative =
|
|
90607
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
90836
|
+
const relative = path50.relative(parent, target2);
|
|
90837
|
+
return relative === "" || !relative.startsWith("..") && !path50.isAbsolute(relative);
|
|
90608
90838
|
}
|
|
90609
90839
|
function findRepoRoot(from) {
|
|
90610
|
-
let dir =
|
|
90840
|
+
let dir = path50.resolve(from);
|
|
90611
90841
|
for (; ; ) {
|
|
90612
|
-
if (existsSync12(
|
|
90613
|
-
const parent =
|
|
90842
|
+
if (existsSync12(path50.join(dir, ".git"))) return dir;
|
|
90843
|
+
const parent = path50.dirname(dir);
|
|
90614
90844
|
if (parent === dir) return null;
|
|
90615
90845
|
dir = parent;
|
|
90616
90846
|
}
|
|
90617
90847
|
}
|
|
90618
90848
|
function checkCaptureOut(out, options) {
|
|
90619
90849
|
const { cwd, repoRoot } = options;
|
|
90620
|
-
const resolved =
|
|
90850
|
+
const resolved = path50.resolve(cwd, out);
|
|
90621
90851
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
90622
|
-
const scratch =
|
|
90852
|
+
const scratch = path50.join(repoRoot, SCRATCH_DIR);
|
|
90623
90853
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
90624
|
-
const suggestion =
|
|
90854
|
+
const suggestion = path50.posix.join(SCRATCH_DIR, "teardowns", path50.basename(resolved) || "capture");
|
|
90625
90855
|
return {
|
|
90626
90856
|
ok: false,
|
|
90627
90857
|
error: {
|
|
@@ -90795,12 +91025,12 @@ var scrapeCommand = defineCommand201({
|
|
|
90795
91025
|
});
|
|
90796
91026
|
|
|
90797
91027
|
// src/commands/landing/inspiration/search.ts
|
|
90798
|
-
import
|
|
91028
|
+
import path52 from "path";
|
|
90799
91029
|
import { defineCommand as defineCommand202 } from "citty";
|
|
90800
91030
|
|
|
90801
91031
|
// src/commands/landing/inspiration/shot.ts
|
|
90802
91032
|
import { mkdir as mkdir15, writeFile as writeFile23 } from "fs/promises";
|
|
90803
|
-
import
|
|
91033
|
+
import path51 from "path";
|
|
90804
91034
|
import sharp8 from "sharp";
|
|
90805
91035
|
var READABLE_SHOT = {
|
|
90806
91036
|
maxWidth: 1440,
|
|
@@ -90826,9 +91056,9 @@ async function downloadReadableShot(url, file) {
|
|
|
90826
91056
|
const response = await fetch(url);
|
|
90827
91057
|
if (!response.ok) return null;
|
|
90828
91058
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
90829
|
-
await mkdir15(
|
|
91059
|
+
await mkdir15(path51.dirname(file), { recursive: true });
|
|
90830
91060
|
await writeFile23(file, shot);
|
|
90831
|
-
return
|
|
91061
|
+
return path51.relative(process.cwd(), file);
|
|
90832
91062
|
} catch {
|
|
90833
91063
|
return null;
|
|
90834
91064
|
}
|
|
@@ -90920,13 +91150,13 @@ function buildSearchBody(args8) {
|
|
|
90920
91150
|
return body;
|
|
90921
91151
|
}
|
|
90922
91152
|
async function downloadShots(results) {
|
|
90923
|
-
const dir =
|
|
91153
|
+
const dir = path52.join(process.cwd(), ".baker", "inspiration");
|
|
90924
91154
|
const saved = /* @__PURE__ */ new Map();
|
|
90925
91155
|
await Promise.all(
|
|
90926
91156
|
results.map(async (result) => {
|
|
90927
91157
|
const file = await downloadReadableShot(
|
|
90928
91158
|
result.desktopShotUrl,
|
|
90929
|
-
|
|
91159
|
+
path52.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
90930
91160
|
);
|
|
90931
91161
|
if (file) saved.set(result.id, file);
|
|
90932
91162
|
})
|
|
@@ -91154,7 +91384,7 @@ var sequencesCommand = defineCommand203({
|
|
|
91154
91384
|
});
|
|
91155
91385
|
|
|
91156
91386
|
// src/commands/landing/inspiration/view.ts
|
|
91157
|
-
import
|
|
91387
|
+
import path53 from "path";
|
|
91158
91388
|
import { defineCommand as defineCommand204 } from "citty";
|
|
91159
91389
|
registerSchema({
|
|
91160
91390
|
command: "landing.inspiration.view",
|
|
@@ -91187,12 +91417,12 @@ var viewCommand2 = defineCommand204({
|
|
|
91187
91417
|
const id = args8.id;
|
|
91188
91418
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
91189
91419
|
const section = data.section;
|
|
91190
|
-
const dir =
|
|
91420
|
+
const dir = path53.join(process.cwd(), ".baker", "inspiration", id);
|
|
91191
91421
|
const ext = READABLE_SHOT.extension;
|
|
91192
91422
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
91193
|
-
downloadReadableShot(section.desktopShotUrl,
|
|
91194
|
-
downloadReadableShot(section.mobileShotUrl,
|
|
91195
|
-
downloadReadableShot(section.motionFilmstripUrl,
|
|
91423
|
+
downloadReadableShot(section.desktopShotUrl, path53.join(dir, `desktop.${ext}`)),
|
|
91424
|
+
downloadReadableShot(section.mobileShotUrl, path53.join(dir, `mobile.${ext}`)),
|
|
91425
|
+
downloadReadableShot(section.motionFilmstripUrl, path53.join(dir, `motion-filmstrip.${ext}`))
|
|
91196
91426
|
]);
|
|
91197
91427
|
const full = args8.full;
|
|
91198
91428
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -91298,8 +91528,8 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
91298
91528
|
|
|
91299
91529
|
// src/commands/landing/variant.ts
|
|
91300
91530
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
91301
|
-
import { mkdir as mkdir16, readdir as readdir18, readFile as
|
|
91302
|
-
import
|
|
91531
|
+
import { mkdir as mkdir16, readdir as readdir18, readFile as readFile41, stat as stat11, writeFile as writeFile24 } from "fs/promises";
|
|
91532
|
+
import path54 from "path";
|
|
91303
91533
|
import { defineCommand as defineCommand206 } from "citty";
|
|
91304
91534
|
registerSchema({
|
|
91305
91535
|
command: "landing.variant",
|
|
@@ -91326,7 +91556,7 @@ registerSchema({
|
|
|
91326
91556
|
var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
91327
91557
|
async function readInternalId(definitionPath) {
|
|
91328
91558
|
try {
|
|
91329
|
-
const text2 = await
|
|
91559
|
+
const text2 = await readFile41(definitionPath, "utf8");
|
|
91330
91560
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text2)?.[1] ?? null;
|
|
91331
91561
|
} catch {
|
|
91332
91562
|
return null;
|
|
@@ -91335,7 +91565,7 @@ async function readInternalId(definitionPath) {
|
|
|
91335
91565
|
async function archivedVariantNumbers(root, internalId) {
|
|
91336
91566
|
if (!internalId) return [];
|
|
91337
91567
|
try {
|
|
91338
|
-
const entries = await readdir18(
|
|
91568
|
+
const entries = await readdir18(path54.resolve(root, "src/_variants", internalId), { withFileTypes: true });
|
|
91339
91569
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => Number(entry.name)).filter((n) => Number.isInteger(n) && n > 0);
|
|
91340
91570
|
} catch {
|
|
91341
91571
|
return [];
|
|
@@ -91365,7 +91595,7 @@ async function exists2(p) {
|
|
|
91365
91595
|
}
|
|
91366
91596
|
async function listLandingSlugs2(root) {
|
|
91367
91597
|
try {
|
|
91368
|
-
const entries = await readdir18(
|
|
91598
|
+
const entries = await readdir18(path54.resolve(root, "src", "pages"), { withFileTypes: true });
|
|
91369
91599
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
91370
91600
|
} catch {
|
|
91371
91601
|
return [];
|
|
@@ -91381,7 +91611,7 @@ async function listComponents(componentsDir, prefix = "") {
|
|
|
91381
91611
|
const out = [];
|
|
91382
91612
|
for (const entry of entries) {
|
|
91383
91613
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
91384
|
-
if (entry.isDirectory()) out.push(...await listComponents(
|
|
91614
|
+
if (entry.isDirectory()) out.push(...await listComponents(path54.join(componentsDir, entry.name), rel));
|
|
91385
91615
|
else if (entry.name.endsWith(".astro")) out.push(rel);
|
|
91386
91616
|
}
|
|
91387
91617
|
return out.sort();
|
|
@@ -91448,16 +91678,16 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
91448
91678
|
`"${controlSlug}" is already a variant of "${variantOfSlug(controlSlug)}". Make the next variant of the page itself: \`baker landing variant ${variantOfSlug(controlSlug)}\`.`
|
|
91449
91679
|
);
|
|
91450
91680
|
}
|
|
91451
|
-
const controlDir =
|
|
91681
|
+
const controlDir = path54.resolve(projectRoot, "src", "pages", controlSlug);
|
|
91452
91682
|
if (!await isDir3(controlDir)) {
|
|
91453
91683
|
fail9("NOT_FOUND", `No page at src/pages/${controlSlug}/`, {
|
|
91454
91684
|
availableSlugs: await listLandingSlugs2(projectRoot)
|
|
91455
91685
|
});
|
|
91456
91686
|
}
|
|
91457
|
-
if (!await exists2(
|
|
91687
|
+
if (!await exists2(path54.join(controlDir, "index.astro"))) {
|
|
91458
91688
|
fail9("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
|
|
91459
91689
|
}
|
|
91460
|
-
const internalId = await readInternalId(
|
|
91690
|
+
const internalId = await readInternalId(path54.join(controlDir, "_definition.md"));
|
|
91461
91691
|
const variantSlugValue = variantSlug(
|
|
91462
91692
|
controlSlug,
|
|
91463
91693
|
nextVariantNumber(
|
|
@@ -91466,8 +91696,8 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
91466
91696
|
await archivedVariantNumbers(projectRoot, internalId)
|
|
91467
91697
|
)
|
|
91468
91698
|
);
|
|
91469
|
-
const variantDir =
|
|
91470
|
-
const available = await listComponents(
|
|
91699
|
+
const variantDir = path54.resolve(projectRoot, "src", "pages", variantSlugValue);
|
|
91700
|
+
const available = await listComponents(path54.join(controlDir, "_components"));
|
|
91471
91701
|
const forks = [];
|
|
91472
91702
|
for (const name of parseForks(opts.fork, opts.rawArgs)) {
|
|
91473
91703
|
const resolved = resolveForkName(name, available);
|
|
@@ -91483,26 +91713,26 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
91483
91713
|
}
|
|
91484
91714
|
async function writeVariant(opts) {
|
|
91485
91715
|
const { controlDir, variantDir, controlSlug, variantSlug: variantSlug2, forks } = opts;
|
|
91486
|
-
const forkedTargets = new Set(forks.map((f) =>
|
|
91716
|
+
const forkedTargets = new Set(forks.map((f) => path54.join(controlDir, "_components", f)));
|
|
91487
91717
|
const written = [];
|
|
91488
|
-
const indexText = await
|
|
91718
|
+
const indexText = await readFile41(path54.join(controlDir, "index.astro"), "utf8");
|
|
91489
91719
|
await mkdir16(variantDir, { recursive: true });
|
|
91490
91720
|
await writeFile24(
|
|
91491
|
-
|
|
91721
|
+
path54.join(variantDir, "index.astro"),
|
|
91492
91722
|
repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
|
|
91493
91723
|
"utf8"
|
|
91494
91724
|
);
|
|
91495
91725
|
written.push(`src/pages/${variantSlug2}/index.astro`);
|
|
91496
91726
|
for (const fork of forks) {
|
|
91497
|
-
const fromFile =
|
|
91498
|
-
const toFile =
|
|
91499
|
-
const text2 = await
|
|
91500
|
-
await mkdir16(
|
|
91727
|
+
const fromFile = path54.join(controlDir, "_components", fork);
|
|
91728
|
+
const toFile = path54.join(variantDir, "_components", fork);
|
|
91729
|
+
const text2 = await readFile41(fromFile, "utf8");
|
|
91730
|
+
await mkdir16(path54.dirname(toFile), { recursive: true });
|
|
91501
91731
|
await writeFile24(
|
|
91502
91732
|
toFile,
|
|
91503
91733
|
repointFile(text2, {
|
|
91504
|
-
fromDir:
|
|
91505
|
-
toDir:
|
|
91734
|
+
fromDir: path54.dirname(fromFile),
|
|
91735
|
+
toDir: path54.dirname(toFile),
|
|
91506
91736
|
controlDir,
|
|
91507
91737
|
variantDir,
|
|
91508
91738
|
forkedTargets
|
|
@@ -91511,9 +91741,9 @@ async function writeVariant(opts) {
|
|
|
91511
91741
|
);
|
|
91512
91742
|
written.push(`src/pages/${variantSlug2}/_components/${fork}`);
|
|
91513
91743
|
}
|
|
91514
|
-
const controlDefinitionPath =
|
|
91744
|
+
const controlDefinitionPath = path54.join(controlDir, "_definition.md");
|
|
91515
91745
|
if (await exists2(controlDefinitionPath)) {
|
|
91516
|
-
const definition = buildVariantDefinition(await
|
|
91746
|
+
const definition = buildVariantDefinition(await readFile41(controlDefinitionPath, "utf8"), {
|
|
91517
91747
|
internalId: randomUUID3().replace(/-/g, "").slice(0, 8),
|
|
91518
91748
|
variantSlug: variantSlug2,
|
|
91519
91749
|
controlSlug,
|
|
@@ -91521,11 +91751,11 @@ async function writeVariant(opts) {
|
|
|
91521
91751
|
...opts.because ? { because: opts.because } : {},
|
|
91522
91752
|
...opts.change ? { change: opts.change } : {}
|
|
91523
91753
|
});
|
|
91524
|
-
await writeFile24(
|
|
91754
|
+
await writeFile24(path54.join(variantDir, "_definition.md"), definition, "utf8");
|
|
91525
91755
|
written.push(`src/pages/${variantSlug2}/_definition.md`);
|
|
91526
91756
|
}
|
|
91527
|
-
await mkdir16(
|
|
91528
|
-
await writeFile24(
|
|
91757
|
+
await mkdir16(path54.join(variantDir, "_images"), { recursive: true });
|
|
91758
|
+
await writeFile24(path54.join(variantDir, "_images", ".gitkeep"), "", "utf8");
|
|
91529
91759
|
return written;
|
|
91530
91760
|
}
|
|
91531
91761
|
var variantCommand = defineCommand206({
|
|
@@ -94230,8 +94460,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
94230
94460
|
});
|
|
94231
94461
|
|
|
94232
94462
|
// src/commands/scheduled-agents/templates.ts
|
|
94233
|
-
import { readFile as
|
|
94234
|
-
import
|
|
94463
|
+
import { readFile as readFile42 } from "fs/promises";
|
|
94464
|
+
import path55 from "path";
|
|
94235
94465
|
import { defineCommand as defineCommand234 } from "citty";
|
|
94236
94466
|
registerSchema({
|
|
94237
94467
|
command: "scheduled-agents.templates",
|
|
@@ -94338,7 +94568,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
94338
94568
|
}
|
|
94339
94569
|
if (save.length > 0) {
|
|
94340
94570
|
const briefFile = flag("brief-file");
|
|
94341
|
-
const brief2 = briefFile.length > 0 ? await
|
|
94571
|
+
const brief2 = briefFile.length > 0 ? await readFile42(path55.resolve(briefFile), "utf8") : flag("brief");
|
|
94342
94572
|
if (brief2.trim().length === 0) {
|
|
94343
94573
|
failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
94344
94574
|
}
|
|
@@ -94978,7 +95208,7 @@ function parseImageRefs(spec) {
|
|
|
94978
95208
|
}
|
|
94979
95209
|
var defaultDeps = {
|
|
94980
95210
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
94981
|
-
upload: (
|
|
95211
|
+
upload: (path58) => uploadLocalImage({ file: path58, contentType: detectImageContentType(path58), source: "uploaded" })
|
|
94982
95212
|
};
|
|
94983
95213
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
94984
95214
|
const refs = parseImageRefs(spec);
|
|
@@ -94998,10 +95228,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
94998
95228
|
}
|
|
94999
95229
|
return { imageIds, added };
|
|
95000
95230
|
}
|
|
95001
|
-
function uploadFailure(
|
|
95231
|
+
function uploadFailure(path58) {
|
|
95002
95232
|
return (error) => {
|
|
95003
95233
|
if (error instanceof ApiError) throw error;
|
|
95004
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
95234
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path58}" as an image.`);
|
|
95005
95235
|
};
|
|
95006
95236
|
}
|
|
95007
95237
|
|
|
@@ -96101,10 +96331,10 @@ async function stageOp4(op) {
|
|
|
96101
96331
|
handleError8(err);
|
|
96102
96332
|
}
|
|
96103
96333
|
}
|
|
96104
|
-
async function draftAction3(
|
|
96334
|
+
async function draftAction3(path58, body, chat) {
|
|
96105
96335
|
const chatId = resolveChatId(chat);
|
|
96106
96336
|
try {
|
|
96107
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
96337
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path58, { chatId, ...body }));
|
|
96108
96338
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
96109
96339
|
return data;
|
|
96110
96340
|
} catch (err) {
|
|
@@ -97262,7 +97492,7 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
|
|
|
97262
97492
|
import { constants } from "fs";
|
|
97263
97493
|
import { copyFile as copyFile2, mkdir as mkdir17, mkdtemp as mkdtemp3, rm as rm10 } from "fs/promises";
|
|
97264
97494
|
import { tmpdir as tmpdir4 } from "os";
|
|
97265
|
-
import
|
|
97495
|
+
import path56 from "path";
|
|
97266
97496
|
import { defineCommand as defineCommand260 } from "citty";
|
|
97267
97497
|
|
|
97268
97498
|
// src/commands/videos/videoPlatforms.ts
|
|
@@ -97430,8 +97660,8 @@ var fetchCommand2 = defineCommand260({
|
|
|
97430
97660
|
}
|
|
97431
97661
|
});
|
|
97432
97662
|
async function fetchVideo(args8) {
|
|
97433
|
-
const outDir =
|
|
97434
|
-
const workDir = await mkdtemp3(
|
|
97663
|
+
const outDir = path56.resolve(args8.out);
|
|
97664
|
+
const workDir = await mkdtemp3(path56.join(tmpdir4(), "videos-fetch-"));
|
|
97435
97665
|
try {
|
|
97436
97666
|
await mkdir17(outDir, { recursive: true });
|
|
97437
97667
|
const vimeoCookie = captureVimeoCookie();
|
|
@@ -97476,7 +97706,7 @@ function fileStem(info) {
|
|
|
97476
97706
|
}
|
|
97477
97707
|
async function placeInto(outDir, source, stem) {
|
|
97478
97708
|
for (let n = 1; ; n++) {
|
|
97479
|
-
const target2 =
|
|
97709
|
+
const target2 = path56.join(outDir, `${stem}${n === 1 ? "" : `-${n}`}${path56.extname(source)}`);
|
|
97480
97710
|
try {
|
|
97481
97711
|
await copyFile2(source, target2, constants.COPYFILE_EXCL);
|
|
97482
97712
|
return target2;
|
|
@@ -97563,7 +97793,7 @@ var groupCommand2 = defineCommand262({
|
|
|
97563
97793
|
// src/commands/videos/ingest.ts
|
|
97564
97794
|
import { mkdtemp as mkdtemp4, rm as rm11, stat as stat12 } from "fs/promises";
|
|
97565
97795
|
import { tmpdir as tmpdir5 } from "os";
|
|
97566
|
-
import
|
|
97796
|
+
import path57 from "path";
|
|
97567
97797
|
import { defineCommand as defineCommand263 } from "citty";
|
|
97568
97798
|
|
|
97569
97799
|
// src/lib/streamUpload.ts
|
|
@@ -97803,7 +98033,7 @@ function ingestUrl(args8) {
|
|
|
97803
98033
|
}
|
|
97804
98034
|
async function downloadThenIngest(args8, country) {
|
|
97805
98035
|
const vimeoCookie = captureVimeoCookie();
|
|
97806
|
-
const workDir = await mkdtemp4(
|
|
98036
|
+
const workDir = await mkdtemp4(path57.join(tmpdir5(), "videos-ingest-"));
|
|
97807
98037
|
try {
|
|
97808
98038
|
const probe = await probeYtDlp({ url: args8.url, country, vimeoCookie, cookieDir: workDir });
|
|
97809
98039
|
if (isAudioOnly(probe.info)) {
|
|
@@ -98068,7 +98298,7 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
98068
98298
|
});
|
|
98069
98299
|
|
|
98070
98300
|
// src/commands/winning-ads/index.ts
|
|
98071
|
-
import { defineCommand as
|
|
98301
|
+
import { defineCommand as defineCommand277 } from "citty";
|
|
98072
98302
|
|
|
98073
98303
|
// src/commands/winning-ads/advertisers.ts
|
|
98074
98304
|
import { defineCommand as defineCommand267 } from "citty";
|
|
@@ -98181,90 +98411,8 @@ var advertisersCommand2 = defineCommand267({
|
|
|
98181
98411
|
}
|
|
98182
98412
|
});
|
|
98183
98413
|
|
|
98184
|
-
// src/commands/winning-ads/brief.ts
|
|
98185
|
-
import { defineCommand as defineCommand268 } from "citty";
|
|
98186
|
-
registerSchema({
|
|
98187
|
-
command: "winning-ads.brief",
|
|
98188
|
-
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
98189
|
-
args: {
|
|
98190
|
-
dna: {
|
|
98191
|
-
type: "string",
|
|
98192
|
-
description: `Partial target DNA as JSON, e.g. '{"angle":"cost savings","awareness_stage":"solution_aware"}'`,
|
|
98193
|
-
required: false
|
|
98194
|
-
},
|
|
98195
|
-
notes: { type: "string", description: "Free-text guidance for the brief", required: false },
|
|
98196
|
-
k: {
|
|
98197
|
-
type: "number",
|
|
98198
|
-
description: "Number of reference ads to pull 1-50 (default: service default)",
|
|
98199
|
-
required: false
|
|
98200
|
-
}
|
|
98201
|
-
}
|
|
98202
|
-
});
|
|
98203
|
-
function buildBriefBody(args8) {
|
|
98204
|
-
const body = {};
|
|
98205
|
-
if (args8.targetDna && Object.keys(args8.targetDna).length) {
|
|
98206
|
-
body.target_dna = args8.targetDna;
|
|
98207
|
-
}
|
|
98208
|
-
if (args8.notes) {
|
|
98209
|
-
body.notes = args8.notes;
|
|
98210
|
-
}
|
|
98211
|
-
if (args8.k !== void 0 && args8.k !== "") {
|
|
98212
|
-
body.k_reference_ads = Number(args8.k);
|
|
98213
|
-
}
|
|
98214
|
-
return body;
|
|
98215
|
-
}
|
|
98216
|
-
function parseDna(raw) {
|
|
98217
|
-
if (!raw) {
|
|
98218
|
-
return void 0;
|
|
98219
|
-
}
|
|
98220
|
-
let parsed;
|
|
98221
|
-
try {
|
|
98222
|
-
parsed = JSON.parse(raw);
|
|
98223
|
-
} catch {
|
|
98224
|
-
throw new SyntaxError("--dna must be valid JSON");
|
|
98225
|
-
}
|
|
98226
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
98227
|
-
throw new SyntaxError("--dna must be a JSON object");
|
|
98228
|
-
}
|
|
98229
|
-
return parsed;
|
|
98230
|
-
}
|
|
98231
|
-
var briefCommand = defineCommand268({
|
|
98232
|
-
meta: {
|
|
98233
|
-
name: "brief",
|
|
98234
|
-
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
98235
|
-
},
|
|
98236
|
-
args: {
|
|
98237
|
-
dna: { type: "string", description: "Partial target DNA as JSON", required: false },
|
|
98238
|
-
notes: { type: "string", description: "Free-text guidance", required: false },
|
|
98239
|
-
k: { type: "string", description: "Number of reference ads (1-50)", required: false }
|
|
98240
|
-
},
|
|
98241
|
-
run: async ({ args: args8 }) => {
|
|
98242
|
-
try {
|
|
98243
|
-
let targetDna;
|
|
98244
|
-
try {
|
|
98245
|
-
targetDna = parseDna(args8.dna);
|
|
98246
|
-
} catch (err) {
|
|
98247
|
-
writeJson({
|
|
98248
|
-
ok: false,
|
|
98249
|
-
error: { code: "VALIDATION_ERROR", message: err instanceof Error ? err.message : "Invalid --dna" }
|
|
98250
|
-
});
|
|
98251
|
-
process.exit(1);
|
|
98252
|
-
}
|
|
98253
|
-
const body = buildBriefBody({
|
|
98254
|
-
targetDna,
|
|
98255
|
-
notes: args8.notes,
|
|
98256
|
-
k: args8.k
|
|
98257
|
-
});
|
|
98258
|
-
const data = await apiPost("/api/ad-library/brief", body);
|
|
98259
|
-
writeJson({ ok: true, data });
|
|
98260
|
-
} catch (err) {
|
|
98261
|
-
reportError3(err);
|
|
98262
|
-
}
|
|
98263
|
-
}
|
|
98264
|
-
});
|
|
98265
|
-
|
|
98266
98414
|
// src/commands/winning-ads/content.ts
|
|
98267
|
-
import { defineCommand as
|
|
98415
|
+
import { defineCommand as defineCommand268 } from "citty";
|
|
98268
98416
|
registerSchema({
|
|
98269
98417
|
command: "winning-ads.content",
|
|
98270
98418
|
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. Static and carousel ads return the text drawn into the image with the job each piece does (headline, benefit, cta\u2026), the card it sits on, and their format_pattern. Add --full for how it is built (composition, colours, CTA, markers), how it persuades (awareness, mechanism, pains, desires, proof, objections), what it offers (offer type, price, guarantee, urgency), plus speech, pacing and soundtrack for video.",
|
|
@@ -98277,7 +98425,7 @@ registerSchema({
|
|
|
98277
98425
|
}
|
|
98278
98426
|
}
|
|
98279
98427
|
});
|
|
98280
|
-
var contentCommand =
|
|
98428
|
+
var contentCommand = defineCommand268({
|
|
98281
98429
|
meta: {
|
|
98282
98430
|
name: "content",
|
|
98283
98431
|
description: "Read the transcript + on-screen text + copy of one winning ad; --full adds its build, persuasion and offer. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -98326,7 +98474,7 @@ var contentCommand = defineCommand269({
|
|
|
98326
98474
|
});
|
|
98327
98475
|
|
|
98328
98476
|
// src/commands/winning-ads/feed.ts
|
|
98329
|
-
import { defineCommand as
|
|
98477
|
+
import { defineCommand as defineCommand269 } from "citty";
|
|
98330
98478
|
function buildFeedParams(input) {
|
|
98331
98479
|
const params = {};
|
|
98332
98480
|
const advertiser = splitList4(input.advertiser);
|
|
@@ -98378,7 +98526,7 @@ registerSchema({
|
|
|
98378
98526
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
98379
98527
|
}
|
|
98380
98528
|
});
|
|
98381
|
-
var feedCommand2 =
|
|
98529
|
+
var feedCommand2 = defineCommand269({
|
|
98382
98530
|
meta: {
|
|
98383
98531
|
name: "feed",
|
|
98384
98532
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -98463,7 +98611,7 @@ var feedCommand2 = defineCommand270({
|
|
|
98463
98611
|
});
|
|
98464
98612
|
|
|
98465
98613
|
// src/commands/winning-ads/follow.ts
|
|
98466
|
-
import { defineCommand as
|
|
98614
|
+
import { defineCommand as defineCommand270 } from "citty";
|
|
98467
98615
|
var PLATFORMS = adLibraryPlatformSchema.options;
|
|
98468
98616
|
registerSchema({
|
|
98469
98617
|
command: "winning-ads.follow",
|
|
@@ -98478,7 +98626,7 @@ registerSchema({
|
|
|
98478
98626
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
98479
98627
|
}
|
|
98480
98628
|
});
|
|
98481
|
-
var followCommand =
|
|
98629
|
+
var followCommand = defineCommand270({
|
|
98482
98630
|
meta: {
|
|
98483
98631
|
name: "follow",
|
|
98484
98632
|
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 every platform we can resolve. Example: baker winning-ads follow "deel.com" --platform meta'
|
|
@@ -98525,7 +98673,7 @@ var followCommand = defineCommand271({
|
|
|
98525
98673
|
});
|
|
98526
98674
|
|
|
98527
98675
|
// src/commands/winning-ads/follow-competitors.ts
|
|
98528
|
-
import { defineCommand as
|
|
98676
|
+
import { defineCommand as defineCommand271 } from "citty";
|
|
98529
98677
|
var PLATFORMS2 = adLibraryPlatformSchema.options;
|
|
98530
98678
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
98531
98679
|
function buildFollowBatchBody(input) {
|
|
@@ -98558,7 +98706,7 @@ registerSchema({
|
|
|
98558
98706
|
}
|
|
98559
98707
|
}
|
|
98560
98708
|
});
|
|
98561
|
-
var followCompetitorsCommand =
|
|
98709
|
+
var followCompetitorsCommand = defineCommand271({
|
|
98562
98710
|
meta: {
|
|
98563
98711
|
name: "follow-competitors",
|
|
98564
98712
|
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"'
|
|
@@ -98633,7 +98781,7 @@ var followCompetitorsCommand = defineCommand272({
|
|
|
98633
98781
|
});
|
|
98634
98782
|
|
|
98635
98783
|
// src/commands/winning-ads/following.ts
|
|
98636
|
-
import { defineCommand as
|
|
98784
|
+
import { defineCommand as defineCommand272 } from "citty";
|
|
98637
98785
|
registerSchema({
|
|
98638
98786
|
command: "winning-ads.following",
|
|
98639
98787
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts. A brand still adding has counts that are a lie in progress; one with `discovery_failed` has counts that are short because we couldn't finish looking, which is not the same as it running no ads.",
|
|
@@ -98687,7 +98835,7 @@ function followingNormalizer(record, full) {
|
|
|
98687
98835
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
98688
98836
|
};
|
|
98689
98837
|
}
|
|
98690
|
-
var followingCommand =
|
|
98838
|
+
var followingCommand = defineCommand272({
|
|
98691
98839
|
meta: {
|
|
98692
98840
|
name: "following",
|
|
98693
98841
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. A brand's counts are only final once it is ready. Example: baker winning-ads following --output md"
|
|
@@ -98722,113 +98870,18 @@ var followingCommand = defineCommand273({
|
|
|
98722
98870
|
}
|
|
98723
98871
|
});
|
|
98724
98872
|
|
|
98725
|
-
// src/commands/winning-ads/patterns.ts
|
|
98726
|
-
import { defineCommand as defineCommand274 } from "citty";
|
|
98727
|
-
registerSchema({
|
|
98728
|
-
command: "winning-ads.patterns",
|
|
98729
|
-
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.",
|
|
98730
|
-
args: {
|
|
98731
|
-
winners: { type: "string", description: "Comma list of winning ad ids (cohort A)", required: true },
|
|
98732
|
-
duds: { type: "string", description: "Comma list of weaker/dud ad ids (cohort B)", required: true },
|
|
98733
|
-
"top-n": {
|
|
98734
|
-
type: "number",
|
|
98735
|
-
description: "Max discriminators to return 1-50 (default: service default)",
|
|
98736
|
-
required: false
|
|
98737
|
-
}
|
|
98738
|
-
}
|
|
98739
|
-
});
|
|
98740
|
-
function buildPatternsBody(args8) {
|
|
98741
|
-
const winners = splitList4(args8.winners);
|
|
98742
|
-
const duds = splitList4(args8.duds);
|
|
98743
|
-
if (!winners.length || !duds.length) {
|
|
98744
|
-
throw new Error("Provide at least one ad id for both --winners and --duds");
|
|
98745
|
-
}
|
|
98746
|
-
const body = {
|
|
98747
|
-
cohort_a: { ad_ids: winners },
|
|
98748
|
-
cohort_b: { ad_ids: duds }
|
|
98749
|
-
};
|
|
98750
|
-
if (args8.topN !== void 0 && args8.topN !== "") {
|
|
98751
|
-
body.top_n = Number(args8.topN);
|
|
98752
|
-
}
|
|
98753
|
-
return body;
|
|
98754
|
-
}
|
|
98755
|
-
function discriminatorRow(record) {
|
|
98756
|
-
const dir = record.direction === "favors_a" ? "winners" : "duds";
|
|
98757
|
-
return {
|
|
98758
|
-
field: String(record.field ?? ""),
|
|
98759
|
-
favors: dir,
|
|
98760
|
-
score: record.score ?? null,
|
|
98761
|
-
top_values_winners: Array.isArray(record.top_values_a) ? record.top_values_a.join(", ") : "",
|
|
98762
|
-
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
98763
|
-
};
|
|
98764
|
-
}
|
|
98765
|
-
var patternsCommand = defineCommand274({
|
|
98766
|
-
meta: {
|
|
98767
|
-
name: "patterns",
|
|
98768
|
-
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"
|
|
98769
|
-
},
|
|
98770
|
-
args: {
|
|
98771
|
-
winners: { type: "string", description: "Comma list of winning ad ids (cohort A)", required: true },
|
|
98772
|
-
duds: { type: "string", description: "Comma list of weaker/dud ad ids (cohort B)", required: true },
|
|
98773
|
-
"top-n": { type: "string", description: "Max discriminators (1-50)", required: false },
|
|
98774
|
-
output: { type: "string", description: "Output format: json|files|md", required: false, default: "json" },
|
|
98775
|
-
fields: { type: "string", description: "Comma-separated field names to include", required: false }
|
|
98776
|
-
},
|
|
98777
|
-
run: async ({ args: args8 }) => {
|
|
98778
|
-
try {
|
|
98779
|
-
let body;
|
|
98780
|
-
try {
|
|
98781
|
-
body = buildPatternsBody({
|
|
98782
|
-
winners: args8.winners,
|
|
98783
|
-
duds: args8.duds,
|
|
98784
|
-
topN: args8["top-n"]
|
|
98785
|
-
});
|
|
98786
|
-
} catch (err) {
|
|
98787
|
-
writeJson({
|
|
98788
|
-
ok: false,
|
|
98789
|
-
error: { code: "VALIDATION_ERROR", message: err instanceof Error ? err.message : "Invalid cohorts" }
|
|
98790
|
-
});
|
|
98791
|
-
process.exit(1);
|
|
98792
|
-
}
|
|
98793
|
-
const data = await apiPost("/api/ad-library/patterns", body);
|
|
98794
|
-
const output = args8.output || "json";
|
|
98795
|
-
const rawDiscriminators = Array.isArray(data?.discriminators) ? data.discriminators : [];
|
|
98796
|
-
if (output === "json") {
|
|
98797
|
-
writeJson({
|
|
98798
|
-
ok: true,
|
|
98799
|
-
data: {
|
|
98800
|
-
cohort_a_size: data?.cohort_a_size ?? 0,
|
|
98801
|
-
cohort_b_size: data?.cohort_b_size ?? 0,
|
|
98802
|
-
discriminators: rawDiscriminators.map((d) => discriminatorRow(d))
|
|
98803
|
-
}
|
|
98804
|
-
});
|
|
98805
|
-
return;
|
|
98806
|
-
}
|
|
98807
|
-
writeOutput(
|
|
98808
|
-
{ ok: true, data: rawDiscriminators },
|
|
98809
|
-
output,
|
|
98810
|
-
args8.fields ? args8.fields.split(",") : void 0,
|
|
98811
|
-
false,
|
|
98812
|
-
(record) => discriminatorRow(record)
|
|
98813
|
-
);
|
|
98814
|
-
} catch (err) {
|
|
98815
|
-
reportError3(err);
|
|
98816
|
-
}
|
|
98817
|
-
}
|
|
98818
|
-
});
|
|
98819
|
-
|
|
98820
98873
|
// src/commands/winning-ads/search.ts
|
|
98821
|
-
import { defineCommand as
|
|
98874
|
+
import { defineCommand as defineCommand273 } from "citty";
|
|
98822
98875
|
registerSchema({
|
|
98823
98876
|
command: "winning-ads.search",
|
|
98824
|
-
description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url, and visual/container/mechanism when known) to pick a reference to reproduce. A value outside a closed list (--platform, --format, --winner-category, --awareness, --format-pattern) is refused with error.fix.validValues.",
|
|
98877
|
+
description: "Search the ad-dna corpus of scored winning ads, the company's followed brands first. Returns a lean shortlist (advertiser, summary, scores, days_active/active/winner_category as proof, media_url, and visual/container/mechanism when known) to pick a reference to reproduce. Each search is a paid reranker + judge call: one strong search, at most one adjusted retry. A value outside a closed list (--platform, --format, --winner-category, --awareness, --format-pattern, --sort) is refused with error.fix.validValues.",
|
|
98825
98878
|
args: {
|
|
98826
98879
|
query: { type: "string", description: "Free-text natural-language query", required: false },
|
|
98827
98880
|
scope: {
|
|
98828
98881
|
type: "string",
|
|
98829
|
-
description: "Which brands to draw from:
|
|
98882
|
+
description: "Which brands to draw from: followed-first (default: followed brands first, topped up from the library when too few clear the relevance floor; the whole library when the company follows nobody), followed (only brands the company follows), all (the whole shared library). Every result carries source: followed|library.",
|
|
98830
98883
|
required: false,
|
|
98831
|
-
default: "
|
|
98884
|
+
default: "followed-first"
|
|
98832
98885
|
},
|
|
98833
98886
|
"ref-ad-id": {
|
|
98834
98887
|
type: "string",
|
|
@@ -98843,6 +98896,12 @@ registerSchema({
|
|
|
98843
98896
|
default: 3
|
|
98844
98897
|
},
|
|
98845
98898
|
"min-relevance": { type: "number", description: "Relevance floor 0-1; trims weak matches", required: false },
|
|
98899
|
+
sort: {
|
|
98900
|
+
type: "string",
|
|
98901
|
+
description: "Order of a browse with no query: winner (default, by winner score), longest_running (by days active), newest (by first seen). Ignored when a query is given: a query ranks by relevance blended with winner score.",
|
|
98902
|
+
required: false,
|
|
98903
|
+
default: "winner"
|
|
98904
|
+
},
|
|
98846
98905
|
platform: {
|
|
98847
98906
|
type: "string",
|
|
98848
98907
|
description: "Comma list: meta,linkedin,tiktok",
|
|
@@ -98931,6 +98990,7 @@ function buildSearchBody2(args8) {
|
|
|
98931
98990
|
setNumber(body, "limit", args8.limit);
|
|
98932
98991
|
setNumber(body, "max_per_advertiser", args8.maxPerAdvertiser);
|
|
98933
98992
|
setNumber(body, "min_relevance", args8.minRelevance);
|
|
98993
|
+
setString(body, "sort", args8.sort);
|
|
98934
98994
|
if (args8.diverse) {
|
|
98935
98995
|
body.diverse = true;
|
|
98936
98996
|
}
|
|
@@ -99011,8 +99071,8 @@ var FLAG_FOR_FIELD = {
|
|
|
99011
99071
|
first_seen_after: "--first-seen-after",
|
|
99012
99072
|
first_seen_before: "--first-seen-before"
|
|
99013
99073
|
};
|
|
99014
|
-
function flagFor(
|
|
99015
|
-
const field = String(
|
|
99074
|
+
function flagFor(path58) {
|
|
99075
|
+
const field = String(path58[0] === "hard_filters" ? path58[1] : path58[0]);
|
|
99016
99076
|
return FLAG_FOR_FIELD[field] ?? field;
|
|
99017
99077
|
}
|
|
99018
99078
|
function schemaRejection(body) {
|
|
@@ -99051,6 +99111,9 @@ function searchRejection(args8, body) {
|
|
|
99051
99111
|
if (args8.scope !== void 0 && !winnerSearchScopeSchema.safeParse(args8.scope).success) {
|
|
99052
99112
|
return { message: `Unknown --scope "${args8.scope}". Use one of: ${winnerSearchScopeSchema.options.join(", ")}.` };
|
|
99053
99113
|
}
|
|
99114
|
+
if (args8.sort !== void 0 && !winnersSortSchema.safeParse(args8.sort).success) {
|
|
99115
|
+
return { message: `Unknown --sort "${args8.sort}". Use one of: ${winnersSortSchema.options.join(", ")}.` };
|
|
99116
|
+
}
|
|
99054
99117
|
for (const [flag, key] of NUMERIC_FLAGS) {
|
|
99055
99118
|
const value2 = args8[key];
|
|
99056
99119
|
if (value2 !== void 0 && value2 !== "" && Number.isNaN(Number(value2))) {
|
|
@@ -99086,16 +99149,16 @@ function writeSearchResult(data, opts) {
|
|
|
99086
99149
|
writeOutput({ ok: true, data: rawResults, ...withHints2 }, opts.output, opts.fields, opts.full, winningAdNormalizer);
|
|
99087
99150
|
}
|
|
99088
99151
|
var SEARCH_TIMEOUT_MS = 12e4;
|
|
99089
|
-
var searchCommand5 =
|
|
99152
|
+
var searchCommand5 = defineCommand273({
|
|
99090
99153
|
meta: {
|
|
99091
99154
|
name: "search",
|
|
99092
|
-
description: "Search winning reference ads. Start here: baker winning-ads search 'B2B SaaS before/after AI automation' --
|
|
99155
|
+
description: "Search winning reference ads, the company's followed brands first. Start here: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --exclude-advertiser adv_123 --output md. A set of references for N concepts in one call: add --diverse --limit N. The best proven ad in one container from any category: --format-pattern comparison_table (no query needed; add --sort longest_running to order by staying power). Each search is a paid reranker + judge call: one strong search, at most one adjusted retry."
|
|
99093
99156
|
},
|
|
99094
99157
|
args: {
|
|
99095
99158
|
query: { type: "positional", description: "Free-text search query", required: false },
|
|
99096
99159
|
scope: {
|
|
99097
99160
|
type: "string",
|
|
99098
|
-
description: "
|
|
99161
|
+
description: "followed-first (default) | followed | all. followed-first = the company's followed brands first, topped up from the whole library (the whole library when it follows nobody)",
|
|
99099
99162
|
required: false
|
|
99100
99163
|
},
|
|
99101
99164
|
"ref-ad-id": { type: "string", description: "Find ads similar to this ad id", required: false },
|
|
@@ -99111,6 +99174,11 @@ var searchCommand5 = defineCommand275({
|
|
|
99111
99174
|
required: false
|
|
99112
99175
|
},
|
|
99113
99176
|
"min-relevance": { type: "string", description: "Relevance floor 0-1", required: false },
|
|
99177
|
+
sort: {
|
|
99178
|
+
type: "string",
|
|
99179
|
+
description: "Order of a no-query browse: winner (default) | longest_running | newest. Ignored when a query is given",
|
|
99180
|
+
required: false
|
|
99181
|
+
},
|
|
99114
99182
|
platform: {
|
|
99115
99183
|
type: "string",
|
|
99116
99184
|
description: "Comma list of platforms (meta,linkedin,tiktok) \u2014 search one or several",
|
|
@@ -99157,7 +99225,7 @@ var searchCommand5 = defineCommand275({
|
|
|
99157
99225
|
fields: { type: "string", description: "Comma-separated field names to include", required: false },
|
|
99158
99226
|
full: {
|
|
99159
99227
|
type: "boolean",
|
|
99160
|
-
description: "Include DNA detail (angle, persona, hook) +
|
|
99228
|
+
description: "Include DNA detail (angle, persona, hook, awareness, industry) + reach and media kind; mechanism, days_active, active and winner_category are in the default output",
|
|
99161
99229
|
required: false,
|
|
99162
99230
|
default: false
|
|
99163
99231
|
}
|
|
@@ -99171,6 +99239,7 @@ var searchCommand5 = defineCommand275({
|
|
|
99171
99239
|
limit: args8.limit,
|
|
99172
99240
|
maxPerAdvertiser: args8["max-per-advertiser"],
|
|
99173
99241
|
minRelevance: args8["min-relevance"],
|
|
99242
|
+
sort: args8.sort,
|
|
99174
99243
|
platform: args8.platform,
|
|
99175
99244
|
format: args8.format,
|
|
99176
99245
|
winnerCategory: args8["winner-category"],
|
|
@@ -99206,7 +99275,7 @@ var searchCommand5 = defineCommand275({
|
|
|
99206
99275
|
});
|
|
99207
99276
|
|
|
99208
99277
|
// src/commands/winning-ads/seeds.ts
|
|
99209
|
-
import { defineCommand as
|
|
99278
|
+
import { defineCommand as defineCommand274 } from "citty";
|
|
99210
99279
|
function leanRow(r) {
|
|
99211
99280
|
return {
|
|
99212
99281
|
key: r.key,
|
|
@@ -99234,7 +99303,7 @@ function makeSeedCommand(opts) {
|
|
|
99234
99303
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
99235
99304
|
}
|
|
99236
99305
|
});
|
|
99237
|
-
return
|
|
99306
|
+
return defineCommand274({
|
|
99238
99307
|
meta: { name: opts.name, description: opts.description },
|
|
99239
99308
|
args: {
|
|
99240
99309
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -99283,7 +99352,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
99283
99352
|
});
|
|
99284
99353
|
|
|
99285
99354
|
// src/commands/winning-ads/unfollow.ts
|
|
99286
|
-
import { defineCommand as
|
|
99355
|
+
import { defineCommand as defineCommand275 } from "citty";
|
|
99287
99356
|
registerSchema({
|
|
99288
99357
|
command: "winning-ads.unfollow",
|
|
99289
99358
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -99291,7 +99360,7 @@ registerSchema({
|
|
|
99291
99360
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
99292
99361
|
}
|
|
99293
99362
|
});
|
|
99294
|
-
var unfollowCommand =
|
|
99363
|
+
var unfollowCommand = defineCommand275({
|
|
99295
99364
|
meta: {
|
|
99296
99365
|
name: "unfollow",
|
|
99297
99366
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -99312,7 +99381,7 @@ var unfollowCommand = defineCommand277({
|
|
|
99312
99381
|
});
|
|
99313
99382
|
|
|
99314
99383
|
// src/commands/winning-ads/winners.ts
|
|
99315
|
-
import { defineCommand as
|
|
99384
|
+
import { defineCommand as defineCommand276 } from "citty";
|
|
99316
99385
|
registerSchema({
|
|
99317
99386
|
command: "winning-ads.winners",
|
|
99318
99387
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -99322,7 +99391,7 @@ registerSchema({
|
|
|
99322
99391
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
|
|
99323
99392
|
}
|
|
99324
99393
|
});
|
|
99325
|
-
var winnersCommand =
|
|
99394
|
+
var winnersCommand = defineCommand276({
|
|
99326
99395
|
meta: {
|
|
99327
99396
|
name: "winners",
|
|
99328
99397
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -99372,7 +99441,7 @@ var winnersCommand = defineCommand278({
|
|
|
99372
99441
|
});
|
|
99373
99442
|
|
|
99374
99443
|
// src/commands/winning-ads/index.ts
|
|
99375
|
-
var winningAdsCommand =
|
|
99444
|
+
var winningAdsCommand = defineCommand277({
|
|
99376
99445
|
meta: {
|
|
99377
99446
|
name: "winning-ads",
|
|
99378
99447
|
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.
|
|
@@ -99380,7 +99449,7 @@ var winningAdsCommand = defineCommand279({
|
|
|
99380
99449
|
Auth: BAKER_API_KEY (bk_ or bkr_) + BAKER_API_URL (your Convex .convex.site URL).
|
|
99381
99450
|
|
|
99382
99451
|
Subcommands:
|
|
99383
|
-
baker winning-ads search "<free text>" \u2014 semantic search
|
|
99452
|
+
baker winning-ads search "<free text>" \u2014 semantic search, followed brands first; lean shortlist (advertiser, summary, scores, proof, media_url)
|
|
99384
99453
|
baker winning-ads advertisers "<brand>" \u2014 list corpus brands \u2192 advertiser ids (for --exclude-advertiser / --advertiser-id)
|
|
99385
99454
|
baker winning-ads follow "<domain|url|brand>" --platform meta|linkedin|tiktok \u2014 add a brand's ads to your library
|
|
99386
99455
|
baker winning-ads follow-competitors "<domain,domain,...>" \u2014 add MANY brands at once (every competitor by domain)
|
|
@@ -99389,8 +99458,6 @@ Subcommands:
|
|
|
99389
99458
|
baker winning-ads winners <advertiser> \u2014 top winners for one advertiser id
|
|
99390
99459
|
baker winning-ads content <ad-id> \u2014 read ONE ad's transcript + on-screen text + copy (understand a reference before reproducing it)
|
|
99391
99460
|
baker winning-ads unfollow <advertiser> \u2014 stop following a brand
|
|
99392
|
-
baker winning-ads brief \u2014 creative brief grounded in similar winners
|
|
99393
|
-
baker winning-ads patterns --winners \u2026 --duds \u2026 \u2014 what separates a winning cohort from a weak one
|
|
99394
99461
|
baker winning-ads hooks \u2014 top winner-weighted HOOK seeds for a segment (feed the seed to search --hook-archetype)
|
|
99395
99462
|
baker winning-ads mechanisms \u2014 top winner-weighted PRODUCT mechanism seeds (how the product claims to work)
|
|
99396
99463
|
baker winning-ads formats \u2014 top winner-weighted static FORMAT patterns for a segment
|
|
@@ -99406,7 +99473,7 @@ Examples:
|
|
|
99406
99473
|
baker winning-ads content adg_123 --full --output md
|
|
99407
99474
|
baker winning-ads hooks --platform meta --awareness problem_aware --industry saas --output md
|
|
99408
99475
|
baker winning-ads search "fintech onboarding" --hook-archetype callout --winner-category winner --output md
|
|
99409
|
-
baker winning-ads
|
|
99476
|
+
baker winning-ads search --format-pattern comparison_table --sort longest_running --output md
|
|
99410
99477
|
Full guide: __tooling__/docs/tools/baker/winning-ads.md`
|
|
99411
99478
|
},
|
|
99412
99479
|
subCommands: {
|
|
@@ -99419,8 +99486,6 @@ Full guide: __tooling__/docs/tools/baker/winning-ads.md`
|
|
|
99419
99486
|
winners: winnersCommand,
|
|
99420
99487
|
content: contentCommand,
|
|
99421
99488
|
unfollow: unfollowCommand,
|
|
99422
|
-
brief: briefCommand,
|
|
99423
|
-
patterns: patternsCommand,
|
|
99424
99489
|
hooks: hooksCommand,
|
|
99425
99490
|
mechanisms: mechanismsCommand,
|
|
99426
99491
|
formats: formatsCommand
|
|
@@ -99444,7 +99509,7 @@ function getCliVersion() {
|
|
|
99444
99509
|
}
|
|
99445
99510
|
|
|
99446
99511
|
// src/cli.ts
|
|
99447
|
-
var main =
|
|
99512
|
+
var main = defineCommand278({
|
|
99448
99513
|
meta: {
|
|
99449
99514
|
name: "baker",
|
|
99450
99515
|
version: getCliVersion(),
|