@koda-sl/baker-cli 0.116.0-dev.3bcc79f9c → 0.116.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 +2 -34
- package/dist/{chunk-OCMOQOIJ.js → chunk-ZWMMCLJ4.js} +57 -241
- package/dist/chunk-ZWMMCLJ4.js.map +1 -0
- package/dist/cli.js +231 -300
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.d.ts +0 -30
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-OCMOQOIJ.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
BackendClient,
|
|
4
3
|
ELEVENLABS_MAX_MUSIC_LENGTH_MS,
|
|
5
4
|
IMAGE_GENERATE_MODELS,
|
|
6
5
|
LayerExecutionError,
|
|
7
6
|
MODEL_REGISTRY,
|
|
8
7
|
SEEDANCE_DURATIONS,
|
|
9
8
|
ValidationError,
|
|
10
|
-
collectAssetRefLikes,
|
|
11
9
|
createEngineFromEnv,
|
|
12
10
|
defaultRegistry,
|
|
13
11
|
describeFailureReason,
|
|
14
12
|
generateCatalog,
|
|
15
|
-
isPersistedAssetRef,
|
|
16
|
-
requireCredentialsFromEnv,
|
|
17
13
|
resolveConcurrency,
|
|
18
|
-
sha256Hex,
|
|
19
|
-
ulid,
|
|
20
14
|
validateCanvasDeep
|
|
21
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ZWMMCLJ4.js";
|
|
22
16
|
|
|
23
17
|
// src/cli.ts
|
|
24
18
|
import { defineCommand as defineCommand157, runMain } from "citty";
|
|
@@ -157,9 +151,9 @@ async function handleResponse(response) {
|
|
|
157
151
|
throw new ApiError("INTERNAL_ERROR", "Failed to parse API response as JSON");
|
|
158
152
|
}
|
|
159
153
|
}
|
|
160
|
-
async function apiGet(
|
|
154
|
+
async function apiGet(path12, params) {
|
|
161
155
|
const env = getEnv();
|
|
162
|
-
const url = new URL(
|
|
156
|
+
const url = new URL(path12, env.BAKER_API_URL);
|
|
163
157
|
if (params) {
|
|
164
158
|
const clean = sanitizeParams(params);
|
|
165
159
|
for (const [key, value] of Object.entries(clean)) {
|
|
@@ -184,12 +178,12 @@ async function apiGet(path13, params) {
|
|
|
184
178
|
}
|
|
185
179
|
return handleResponse(response);
|
|
186
180
|
}
|
|
187
|
-
async function apiPost(
|
|
181
|
+
async function apiPost(path12, body, opts) {
|
|
188
182
|
const env = getEnv();
|
|
189
183
|
const timeoutMs = opts?.timeoutMs ?? 6e4;
|
|
190
184
|
let response;
|
|
191
185
|
try {
|
|
192
|
-
response = await fetchWithRateLimitRetry(new URL(
|
|
186
|
+
response = await fetchWithRateLimitRetry(new URL(path12, env.BAKER_API_URL).toString(), {
|
|
193
187
|
method: "POST",
|
|
194
188
|
headers: {
|
|
195
189
|
Authorization: `Bearer ${env.BAKER_API_KEY}`,
|
|
@@ -994,9 +988,58 @@ var LINKEDIN_LIMITS = {
|
|
|
994
988
|
questionsSoftMax: 4,
|
|
995
989
|
// playbook: >4 fields tanks completion rate
|
|
996
990
|
customQuestionsMax: 3,
|
|
997
|
-
|
|
991
|
+
customQuestionTextMax: 100,
|
|
992
|
+
choiceOptionsMin: 2,
|
|
993
|
+
choiceOptionsMax: 30,
|
|
994
|
+
choiceOptionTextMax: 100,
|
|
995
|
+
thankYouMessageMax: 300,
|
|
996
|
+
privacyPolicyTextMax: 2e3,
|
|
997
|
+
legalDisclaimerMax: 2e3,
|
|
998
|
+
consentsMax: 5,
|
|
999
|
+
// Campaign Manager caps disclosure checkboxes at 5
|
|
1000
|
+
consentTextMax: 500,
|
|
1001
|
+
hiddenFieldsMax: 20,
|
|
1002
|
+
hiddenFieldNameMax: 100,
|
|
1003
|
+
hiddenFieldValueMax: 2e3
|
|
998
1004
|
}
|
|
999
1005
|
};
|
|
1006
|
+
var LEAD_FORM_PREDEFINED_FIELDS = [
|
|
1007
|
+
// Contact
|
|
1008
|
+
"FIRST_NAME",
|
|
1009
|
+
"LAST_NAME",
|
|
1010
|
+
"EMAIL",
|
|
1011
|
+
"PHONE_NUMBER",
|
|
1012
|
+
"CITY",
|
|
1013
|
+
"STATE",
|
|
1014
|
+
"COUNTRY",
|
|
1015
|
+
"ZIP_CODE",
|
|
1016
|
+
"WORK_EMAIL",
|
|
1017
|
+
// Work
|
|
1018
|
+
"JOB_TITLE",
|
|
1019
|
+
"JOB_FUNCTION",
|
|
1020
|
+
"SENIORITY",
|
|
1021
|
+
"YEARS_OF_EXPERIENCE",
|
|
1022
|
+
// Company
|
|
1023
|
+
"COMPANY_NAME",
|
|
1024
|
+
"COMPANY_SIZE",
|
|
1025
|
+
"INDUSTRY",
|
|
1026
|
+
// Education
|
|
1027
|
+
"DEGREE",
|
|
1028
|
+
"FIELD_OF_STUDY",
|
|
1029
|
+
"SCHOOL_NAME",
|
|
1030
|
+
"START_DATE",
|
|
1031
|
+
"GRADUATION_DATE",
|
|
1032
|
+
// Demographic
|
|
1033
|
+
"GENDER"
|
|
1034
|
+
];
|
|
1035
|
+
var LEAD_FORM_QUESTION_TYPES = ["SINGLE_LINE_TEXT", "MULTIPLE_CHOICE"];
|
|
1036
|
+
var LEAD_FORM_CONFIRMATION_CTAS = [
|
|
1037
|
+
"VIEW_COMPANY_WEBSITE",
|
|
1038
|
+
"DOWNLOAD",
|
|
1039
|
+
"LEARN_MORE",
|
|
1040
|
+
"VIEW_NOW",
|
|
1041
|
+
"TRY_NOW"
|
|
1042
|
+
];
|
|
1000
1043
|
var CAMPAIGN_TYPES = ["SPONSORED_UPDATES", "TEXT_AD", "SPONSORED_INMAILS", "DYNAMIC"];
|
|
1001
1044
|
var COST_TYPES = ["CPC", "CPM", "CPV"];
|
|
1002
1045
|
var OBJECTIVE_TYPES = [
|
|
@@ -1463,21 +1506,101 @@ var conversionUpdateSchema = z3.object({
|
|
|
1463
1506
|
attributionType: z3.enum(ATTRIBUTION_TYPES).optional(),
|
|
1464
1507
|
enabled: z3.boolean().optional()
|
|
1465
1508
|
}).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
|
|
1466
|
-
var
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1509
|
+
var LEAD = LINKEDIN_LIMITS.leadForm;
|
|
1510
|
+
var leadFormQuestionSchema = z3.object({
|
|
1511
|
+
name: z3.string().min(1).max(LEAD.customQuestionTextMax),
|
|
1512
|
+
predefinedField: z3.enum(LEAD_FORM_PREDEFINED_FIELDS).optional(),
|
|
1513
|
+
questionType: z3.enum(LEAD_FORM_QUESTION_TYPES).optional(),
|
|
1514
|
+
options: z3.array(z3.string().min(1).max(LEAD.choiceOptionTextMax)).optional()
|
|
1515
|
+
}).superRefine((q, ctx) => {
|
|
1516
|
+
if (q.predefinedField && q.questionType) {
|
|
1517
|
+
ctx.addIssue({
|
|
1518
|
+
code: "custom",
|
|
1519
|
+
path: ["questionType"],
|
|
1520
|
+
message: "a question is either a predefinedField or a custom questionType, not both"
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
if (q.questionType === "MULTIPLE_CHOICE") {
|
|
1524
|
+
const count = q.options?.length ?? 0;
|
|
1525
|
+
if (count < LEAD.choiceOptionsMin || count > LEAD.choiceOptionsMax) {
|
|
1526
|
+
ctx.addIssue({
|
|
1527
|
+
code: "custom",
|
|
1528
|
+
path: ["options"],
|
|
1529
|
+
message: `MULTIPLE_CHOICE needs ${LEAD.choiceOptionsMin}-${LEAD.choiceOptionsMax} options`
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
if (q.questionType !== "MULTIPLE_CHOICE" && q.options?.length) {
|
|
1534
|
+
ctx.addIssue({
|
|
1535
|
+
code: "custom",
|
|
1536
|
+
path: ["options"],
|
|
1537
|
+
message: "options are only valid on MULTIPLE_CHOICE questions"
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
});
|
|
1541
|
+
var leadFormConsentSchema = z3.object({
|
|
1542
|
+
text: z3.string().min(1).max(LEAD.consentTextMax),
|
|
1543
|
+
required: z3.boolean().default(false)
|
|
1544
|
+
});
|
|
1545
|
+
var leadFormHiddenFieldSchema = z3.object({
|
|
1546
|
+
name: z3.string().min(1).max(LEAD.hiddenFieldNameMax),
|
|
1547
|
+
value: z3.string().max(LEAD.hiddenFieldValueMax)
|
|
1548
|
+
});
|
|
1549
|
+
var leadFormThankYouSchema = z3.object({
|
|
1550
|
+
message: z3.string().max(LEAD.thankYouMessageMax).optional(),
|
|
1551
|
+
cta: z3.enum(LEAD_FORM_CONFIRMATION_CTAS).optional(),
|
|
1552
|
+
landingUrl: httpsUrlSchema.optional(),
|
|
1553
|
+
/** "Book an appointment" — a Calendly / Chili Piper scheduling link, in place of a landing page. */
|
|
1554
|
+
appointmentUrl: httpsUrlSchema.optional()
|
|
1555
|
+
}).superRefine((t, ctx) => {
|
|
1556
|
+
if (t.landingUrl && t.appointmentUrl) {
|
|
1557
|
+
ctx.addIssue({
|
|
1558
|
+
code: "custom",
|
|
1559
|
+
path: ["appointmentUrl"],
|
|
1560
|
+
message: "link the confirmation to a landingUrl OR an appointmentUrl, not both"
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
if (t.cta && !t.landingUrl) {
|
|
1564
|
+
ctx.addIssue({ code: "custom", path: ["cta"], message: "a confirmation cta needs a landingUrl" });
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
var leadFormFields = {
|
|
1568
|
+
name: z3.string().min(1).max(LEAD.nameMax),
|
|
1569
|
+
headline: z3.string().min(1).max(LEAD.headlineMax),
|
|
1570
|
+
description: z3.string().max(LEAD.descriptionMax).optional(),
|
|
1571
|
+
/** Form banner. Baker library id (uploaded at publish) or an image URN already on LinkedIn. */
|
|
1572
|
+
formImageId: bakerMediaIdSchema.optional(),
|
|
1573
|
+
formImageUrn: z3.string().regex(IMAGE_URN_REGEX).optional(),
|
|
1574
|
+
/** Form language, e.g. { country: "US", language: "en" }. Defaults to the account locale on LinkedIn. */
|
|
1575
|
+
locale: z3.object({ country: z3.string().length(2), language: z3.string().length(2) }).optional(),
|
|
1470
1576
|
privacyPolicyUrl: httpsUrlSchema,
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
legalDisclaimer: z3.string().optional(),
|
|
1577
|
+
privacyPolicyText: z3.string().max(LEAD.privacyPolicyTextMax).optional(),
|
|
1578
|
+
questions: z3.array(leadFormQuestionSchema).min(1).max(LEAD.questionsMax),
|
|
1579
|
+
consents: z3.array(leadFormConsentSchema).max(LEAD.consentsMax).optional(),
|
|
1580
|
+
hiddenFields: z3.array(leadFormHiddenFieldSchema).max(LEAD.hiddenFieldsMax).optional(),
|
|
1581
|
+
thankYou: leadFormThankYouSchema.optional(),
|
|
1582
|
+
legalDisclaimer: z3.string().max(LEAD.legalDisclaimerMax).optional(),
|
|
1477
1583
|
raw: z3.record(z3.string(), z3.unknown()).optional()
|
|
1584
|
+
};
|
|
1585
|
+
var leadFormCreateSchema = z3.object(leadFormFields).superRefine((p, ctx) => {
|
|
1586
|
+
if (p.formImageId && p.formImageUrn) {
|
|
1587
|
+
ctx.addIssue({
|
|
1588
|
+
code: "custom",
|
|
1589
|
+
path: ["formImageId"],
|
|
1590
|
+
message: "provide at most one of formImageId (Baker library) or formImageUrn (already on LinkedIn)"
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
const customCount = p.questions.filter((q) => q.questionType).length;
|
|
1594
|
+
if (customCount > LEAD.customQuestionsMax) {
|
|
1595
|
+
ctx.addIssue({
|
|
1596
|
+
code: "custom",
|
|
1597
|
+
path: ["questions"],
|
|
1598
|
+
message: `at most ${LEAD.customQuestionsMax} custom questions (LinkedIn limit); the rest must be predefined profile fields`
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1478
1601
|
});
|
|
1479
1602
|
var leadFormUpdateSchema = z3.object({
|
|
1480
|
-
name:
|
|
1603
|
+
name: leadFormFields.name.optional(),
|
|
1481
1604
|
raw: z3.record(z3.string(), z3.unknown()).optional()
|
|
1482
1605
|
}).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
|
|
1483
1606
|
var LINKEDIN_DRAFT_OP_KINDS = [
|
|
@@ -2881,31 +3004,31 @@ function cachePath(category, key) {
|
|
|
2881
3004
|
return join2(dir, `${hashKey(key)}.json`);
|
|
2882
3005
|
}
|
|
2883
3006
|
function cacheGet(category, key) {
|
|
2884
|
-
const
|
|
2885
|
-
if (!existsSync2(
|
|
3007
|
+
const path12 = cachePath(category, key);
|
|
3008
|
+
if (!existsSync2(path12)) {
|
|
2886
3009
|
return null;
|
|
2887
3010
|
}
|
|
2888
3011
|
try {
|
|
2889
|
-
const raw = readFileSync2(
|
|
3012
|
+
const raw = readFileSync2(path12, "utf-8");
|
|
2890
3013
|
const entry = JSON.parse(raw);
|
|
2891
3014
|
if (entry.expiresAt < Date.now()) {
|
|
2892
|
-
rmSync(
|
|
3015
|
+
rmSync(path12, { force: true });
|
|
2893
3016
|
return null;
|
|
2894
3017
|
}
|
|
2895
3018
|
return entry;
|
|
2896
3019
|
} catch {
|
|
2897
|
-
rmSync(
|
|
3020
|
+
rmSync(path12, { force: true });
|
|
2898
3021
|
return null;
|
|
2899
3022
|
}
|
|
2900
3023
|
}
|
|
2901
3024
|
function cacheSet(category, key, data, ttlMs, fields) {
|
|
2902
|
-
const
|
|
3025
|
+
const path12 = cachePath(category, key);
|
|
2903
3026
|
const entry = {
|
|
2904
3027
|
expiresAt: Date.now() + ttlMs,
|
|
2905
3028
|
data,
|
|
2906
3029
|
fields
|
|
2907
3030
|
};
|
|
2908
|
-
writeFileSync(
|
|
3031
|
+
writeFileSync(path12, JSON.stringify(entry), "utf-8");
|
|
2909
3032
|
}
|
|
2910
3033
|
var HOUR = 60 * 60 * 1e3;
|
|
2911
3034
|
var MINUTE = 60 * 1e3;
|
|
@@ -4818,11 +4941,11 @@ function rawTextEntries(value) {
|
|
|
4818
4941
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
4819
4942
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
4820
4943
|
}
|
|
4821
|
-
function rawFileEntries(
|
|
4822
|
-
if (typeof
|
|
4944
|
+
function rawFileEntries(path12) {
|
|
4945
|
+
if (typeof path12 !== "string" || path12.length === 0) {
|
|
4823
4946
|
return [];
|
|
4824
4947
|
}
|
|
4825
|
-
return readFileSync3(
|
|
4948
|
+
return readFileSync3(path12, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
4826
4949
|
}
|
|
4827
4950
|
function keywordEntries(args) {
|
|
4828
4951
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -4845,19 +4968,19 @@ function keywordEntries(args) {
|
|
|
4845
4968
|
}
|
|
4846
4969
|
return entries;
|
|
4847
4970
|
}
|
|
4848
|
-
function loadJsonFileArg(
|
|
4849
|
-
if (typeof
|
|
4971
|
+
function loadJsonFileArg(path12) {
|
|
4972
|
+
if (typeof path12 !== "string" || path12.length === 0) {
|
|
4850
4973
|
return {};
|
|
4851
4974
|
}
|
|
4852
4975
|
try {
|
|
4853
|
-
const parsed = JSON.parse(readFileSync3(
|
|
4976
|
+
const parsed = JSON.parse(readFileSync3(path12, "utf8"));
|
|
4854
4977
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4855
|
-
failWriteValidation(`${
|
|
4978
|
+
failWriteValidation(`${path12} must contain a JSON object`);
|
|
4856
4979
|
}
|
|
4857
4980
|
return parsed;
|
|
4858
4981
|
} catch (err) {
|
|
4859
4982
|
if (err instanceof SyntaxError) {
|
|
4860
|
-
failWriteValidation(`${
|
|
4983
|
+
failWriteValidation(`${path12} is not valid JSON: ${err.message}`);
|
|
4861
4984
|
}
|
|
4862
4985
|
throw err;
|
|
4863
4986
|
}
|
|
@@ -4946,10 +5069,10 @@ async function stageUpdate(kind, customerId, target, payload) {
|
|
|
4946
5069
|
async function stageTarget(kind, customerId, target) {
|
|
4947
5070
|
await stageGoogleOp({ kind, customerId, target });
|
|
4948
5071
|
}
|
|
4949
|
-
async function draftAction(
|
|
5072
|
+
async function draftAction(path12, body) {
|
|
4950
5073
|
try {
|
|
4951
5074
|
const chatId = requireChatId();
|
|
4952
|
-
const response = await apiPost(
|
|
5075
|
+
const response = await apiPost(path12, { chatId, ...body });
|
|
4953
5076
|
writeJsonEnvelope(response);
|
|
4954
5077
|
} catch (err) {
|
|
4955
5078
|
handleGoogleError(err);
|
|
@@ -8490,19 +8613,19 @@ function failWriteValidation2(message) {
|
|
|
8490
8613
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
8491
8614
|
process.exit(1);
|
|
8492
8615
|
}
|
|
8493
|
-
function loadJsonFileArg2(
|
|
8494
|
-
if (typeof
|
|
8616
|
+
function loadJsonFileArg2(path12) {
|
|
8617
|
+
if (typeof path12 !== "string" || path12.length === 0) {
|
|
8495
8618
|
return {};
|
|
8496
8619
|
}
|
|
8497
8620
|
try {
|
|
8498
|
-
const parsed = JSON.parse(readFileSync7(
|
|
8621
|
+
const parsed = JSON.parse(readFileSync7(path12, "utf8"));
|
|
8499
8622
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8500
|
-
failWriteValidation2(`${
|
|
8623
|
+
failWriteValidation2(`${path12} must contain a JSON object`);
|
|
8501
8624
|
}
|
|
8502
8625
|
return parsed;
|
|
8503
8626
|
} catch (err) {
|
|
8504
8627
|
if (err instanceof SyntaxError) {
|
|
8505
|
-
failWriteValidation2(`${
|
|
8628
|
+
failWriteValidation2(`${path12} is not valid JSON: ${err.message}`);
|
|
8506
8629
|
}
|
|
8507
8630
|
throw err;
|
|
8508
8631
|
}
|
|
@@ -8565,15 +8688,15 @@ function parseLocaleFlag(value) {
|
|
|
8565
8688
|
}
|
|
8566
8689
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
8567
8690
|
}
|
|
8568
|
-
function loadTargetingFileArg(
|
|
8569
|
-
if (typeof
|
|
8691
|
+
function loadTargetingFileArg(path12) {
|
|
8692
|
+
if (typeof path12 !== "string" || path12.length === 0) {
|
|
8570
8693
|
return void 0;
|
|
8571
8694
|
}
|
|
8572
|
-
const parsed = loadJsonFileArg2(
|
|
8695
|
+
const parsed = loadJsonFileArg2(path12);
|
|
8573
8696
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
8574
8697
|
if (!criteria.include) {
|
|
8575
8698
|
failWriteValidation2(
|
|
8576
|
-
`${
|
|
8699
|
+
`${path12} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
8577
8700
|
);
|
|
8578
8701
|
}
|
|
8579
8702
|
return criteria;
|
|
@@ -8608,14 +8731,14 @@ function parseCsvLine(line) {
|
|
|
8608
8731
|
cells.push(current);
|
|
8609
8732
|
return cells.map((cell) => cell.trim());
|
|
8610
8733
|
}
|
|
8611
|
-
function parseListFileArg(
|
|
8612
|
-
if (typeof
|
|
8734
|
+
function parseListFileArg(path12, maxRows) {
|
|
8735
|
+
if (typeof path12 !== "string" || path12.length === 0) {
|
|
8613
8736
|
return void 0;
|
|
8614
8737
|
}
|
|
8615
|
-
const raw = readFileSync7(
|
|
8738
|
+
const raw = readFileSync7(path12, "utf8");
|
|
8616
8739
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
8617
8740
|
if (lines.length < 2) {
|
|
8618
|
-
failWriteValidation2(`${
|
|
8741
|
+
failWriteValidation2(`${path12} needs a header row and at least one data row`);
|
|
8619
8742
|
}
|
|
8620
8743
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
8621
8744
|
const rows = [];
|
|
@@ -8634,7 +8757,7 @@ function parseListFileArg(path13, maxRows) {
|
|
|
8634
8757
|
}
|
|
8635
8758
|
}
|
|
8636
8759
|
if (rows.length > maxRows) {
|
|
8637
|
-
failWriteValidation2(`${
|
|
8760
|
+
failWriteValidation2(`${path12} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
8638
8761
|
}
|
|
8639
8762
|
return { columns, rows };
|
|
8640
8763
|
}
|
|
@@ -9268,7 +9391,10 @@ var leadFormsCreateCommand = defineCommand38({
|
|
|
9268
9391
|
meta: {
|
|
9269
9392
|
name: "create",
|
|
9270
9393
|
description: `Stage a new Lead Gen Form from a JSON file. ${STAGED_NOTE}
|
|
9271
|
-
|
|
9394
|
+
Required: name, headline (\u226460), privacyPolicyUrl, questions[] (\u226412; playbook: \u22644 for completion).
|
|
9395
|
+
Each question is a predefined profile field ({ name, predefinedField: "EMAIL" }) or a custom question ({ name, questionType: "MULTIPLE_CHOICE", options: [...] }; \u22643 custom).
|
|
9396
|
+
Best-practice fields the preview will nudge for if missing: 1-3 qualifying questions, consents[] (disclosure checkboxes), thankYou.message + thankYou.landingUrl|appointmentUrl.
|
|
9397
|
+
Also supported: locale, formImageId|formImageUrn, privacyPolicyText, hiddenFields[], legalDisclaimer, thankYou.cta. Example: baker ads linkedin lead-forms create --file form.json`
|
|
9272
9398
|
},
|
|
9273
9399
|
args: {
|
|
9274
9400
|
...accountArgs,
|
|
@@ -12837,7 +12963,7 @@ async function probeDuration(filePath) {
|
|
|
12837
12963
|
|
|
12838
12964
|
// src/commands/canvas/run.ts
|
|
12839
12965
|
import { readFile as readFile2 } from "fs/promises";
|
|
12840
|
-
import
|
|
12966
|
+
import path4 from "path";
|
|
12841
12967
|
import { defineCommand as defineCommand85 } from "citty";
|
|
12842
12968
|
|
|
12843
12969
|
// src/commands/canvas/placeholders.ts
|
|
@@ -12881,102 +13007,9 @@ function isResolvableRelative(value) {
|
|
|
12881
13007
|
return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path2.isAbsolute(value);
|
|
12882
13008
|
}
|
|
12883
13009
|
|
|
12884
|
-
// src/commands/canvas/run-record.ts
|
|
12885
|
-
import path3 from "path";
|
|
12886
|
-
var MAX_RUN_NODES = 200;
|
|
12887
|
-
var MAX_OUTPUTS_PER_NODE = 10;
|
|
12888
|
-
var MAX_FINAL_OUTPUTS = 10;
|
|
12889
|
-
var MAX_CREATIVE_SLUG_LENGTH = 100;
|
|
12890
|
-
function creativeSlugFromCanvasPath(filePath) {
|
|
12891
|
-
const normalized = filePath.split(path3.sep).join("/");
|
|
12892
|
-
const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
|
|
12893
|
-
const slug = match?.[1] ?? null;
|
|
12894
|
-
return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
|
|
12895
|
-
}
|
|
12896
|
-
var OUTPUT_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
|
|
12897
|
-
function toRecordOutput(slot, value) {
|
|
12898
|
-
const refs = collectAssetRefLikes(value);
|
|
12899
|
-
const ref = refs.length === 1 ? refs[0] : null;
|
|
12900
|
-
if (!ref || !isPersistedAssetRef(ref)) return null;
|
|
12901
|
-
const kind = typeof ref.kind === "string" && OUTPUT_KINDS.has(ref.kind) ? ref.kind : null;
|
|
12902
|
-
if (!kind) return null;
|
|
12903
|
-
return {
|
|
12904
|
-
slot,
|
|
12905
|
-
kind,
|
|
12906
|
-
sha256: ref.sha256,
|
|
12907
|
-
url: ref.url,
|
|
12908
|
-
mime: ref.mime,
|
|
12909
|
-
width: typeof ref.width === "number" ? ref.width : void 0,
|
|
12910
|
-
height: typeof ref.height === "number" ? ref.height : void 0,
|
|
12911
|
-
durationMs: typeof ref.duration_ms === "number" ? ref.duration_ms : void 0
|
|
12912
|
-
};
|
|
12913
|
-
}
|
|
12914
|
-
function nodeOutputsToRecord(nodeOutputs) {
|
|
12915
|
-
const out = [];
|
|
12916
|
-
for (const [slot, value] of Object.entries(nodeOutputs)) {
|
|
12917
|
-
if (Array.isArray(value)) {
|
|
12918
|
-
value.forEach((item, i) => {
|
|
12919
|
-
const rec = toRecordOutput(`${slot}#${i}`, item);
|
|
12920
|
-
if (rec) out.push(rec);
|
|
12921
|
-
});
|
|
12922
|
-
} else {
|
|
12923
|
-
const rec = toRecordOutput(slot, value);
|
|
12924
|
-
if (rec) out.push(rec);
|
|
12925
|
-
}
|
|
12926
|
-
}
|
|
12927
|
-
return out.slice(0, MAX_OUTPUTS_PER_NODE);
|
|
12928
|
-
}
|
|
12929
|
-
function finalOutputsToRecord(output) {
|
|
12930
|
-
if (Array.isArray(output)) {
|
|
12931
|
-
return output.map((item, i) => toRecordOutput(`final#${i}`, item)).filter((rec2) => rec2 !== null).slice(0, MAX_FINAL_OUTPUTS);
|
|
12932
|
-
}
|
|
12933
|
-
const rec = toRecordOutput("final", output);
|
|
12934
|
-
return rec ? [rec] : [];
|
|
12935
|
-
}
|
|
12936
|
-
function buildRunRecord(result, meta) {
|
|
12937
|
-
const nodes = result.node_runs.slice(0, MAX_RUN_NODES).map((run) => ({
|
|
12938
|
-
nodeId: run.node_id,
|
|
12939
|
-
nodeType: run.node_type,
|
|
12940
|
-
cached: run.cached,
|
|
12941
|
-
credits: run.credits,
|
|
12942
|
-
durationMs: run.duration_ms,
|
|
12943
|
-
outputs: nodeOutputsToRecord(result.outputs_by_node[run.node_id] ?? {})
|
|
12944
|
-
}));
|
|
12945
|
-
const finalOutputs = finalOutputsToRecord(result.output);
|
|
12946
|
-
return {
|
|
12947
|
-
runId: result.run_id,
|
|
12948
|
-
creativeSlug: meta.creativeSlug,
|
|
12949
|
-
canvasPath: meta.canvasPath,
|
|
12950
|
-
canvasSha: meta.canvasSha,
|
|
12951
|
-
chatId: meta.chatId,
|
|
12952
|
-
status: "completed",
|
|
12953
|
-
stats: {
|
|
12954
|
-
totalNodes: result.stats.total_nodes,
|
|
12955
|
-
cachedNodes: result.stats.cached_nodes,
|
|
12956
|
-
totalCredits: result.stats.total_credits,
|
|
12957
|
-
durationMs: result.stats.duration_ms
|
|
12958
|
-
},
|
|
12959
|
-
nodes,
|
|
12960
|
-
finalOutputs: finalOutputs.length > 0 ? finalOutputs : void 0
|
|
12961
|
-
};
|
|
12962
|
-
}
|
|
12963
|
-
function buildFailedRunRecord(runId, errorMessage, meta) {
|
|
12964
|
-
return {
|
|
12965
|
-
runId,
|
|
12966
|
-
creativeSlug: meta.creativeSlug,
|
|
12967
|
-
canvasPath: meta.canvasPath,
|
|
12968
|
-
canvasSha: meta.canvasSha,
|
|
12969
|
-
chatId: meta.chatId,
|
|
12970
|
-
status: "failed",
|
|
12971
|
-
errorMessage: errorMessage.slice(0, 2e3),
|
|
12972
|
-
stats: { totalNodes: 0, cachedNodes: 0, totalCredits: 0, durationMs: 0 },
|
|
12973
|
-
nodes: []
|
|
12974
|
-
};
|
|
12975
|
-
}
|
|
12976
|
-
|
|
12977
13010
|
// src/commands/canvas/run-retention.ts
|
|
12978
13011
|
import { rm } from "fs/promises";
|
|
12979
|
-
import
|
|
13012
|
+
import path3 from "path";
|
|
12980
13013
|
function runDirsToPrune(entries, keep, currentRunId) {
|
|
12981
13014
|
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
12982
13015
|
if (keep <= 0) return runs;
|
|
@@ -12993,7 +13026,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
12993
13026
|
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
12994
13027
|
if (toPrune.length === 0) return;
|
|
12995
13028
|
for (const dir of toPrune) {
|
|
12996
|
-
await rm(
|
|
13029
|
+
await rm(path3.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
12997
13030
|
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
12998
13031
|
);
|
|
12999
13032
|
}
|
|
@@ -13020,22 +13053,10 @@ var runCommand = defineCommand85({
|
|
|
13020
13053
|
"keep-runs": {
|
|
13021
13054
|
type: "string",
|
|
13022
13055
|
description: "After the run, prune old r_* run dirs, keeping the N newest (off by default)"
|
|
13023
|
-
},
|
|
13024
|
-
"remote-cache": {
|
|
13025
|
-
type: "string",
|
|
13026
|
-
description: "on | off \u2014 company-scoped remote cache + durable asset persistence (default on; env BAKER_CANVAS_REMOTE_CACHE)"
|
|
13027
|
-
},
|
|
13028
|
-
// citty consumes any `--no-<flag>` as a negation of `<flag>`, so the
|
|
13029
|
-
// opt-out spelling `--no-record` requires the flag to be named `record`
|
|
13030
|
-
// (a literal "no-record" arg would never receive a value).
|
|
13031
|
-
record: {
|
|
13032
|
-
type: "boolean",
|
|
13033
|
-
default: true,
|
|
13034
|
-
description: "Post the durable run-history record to Baker (disable with --no-record)"
|
|
13035
13056
|
}
|
|
13036
13057
|
},
|
|
13037
13058
|
async run({ args }) {
|
|
13038
|
-
const filePath =
|
|
13059
|
+
const filePath = path4.resolve(String(args.file));
|
|
13039
13060
|
const raw = await readFile2(filePath, "utf8");
|
|
13040
13061
|
let parsed;
|
|
13041
13062
|
try {
|
|
@@ -13046,7 +13067,7 @@ var runCommand = defineCommand85({
|
|
|
13046
13067
|
`);
|
|
13047
13068
|
process.exit(2);
|
|
13048
13069
|
}
|
|
13049
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
13070
|
+
parsed = resolveRelativeCanvasPaths(parsed, path4.dirname(filePath));
|
|
13050
13071
|
const pending = unsuppliedPlaceholderAssets(parsed);
|
|
13051
13072
|
if (pending.length > 0) {
|
|
13052
13073
|
process.stderr.write(
|
|
@@ -13066,26 +13087,16 @@ var runCommand = defineCommand85({
|
|
|
13066
13087
|
);
|
|
13067
13088
|
process.exit(2);
|
|
13068
13089
|
}
|
|
13069
|
-
const remoteCache = args["remote-cache"] !== void 0 ? String(args["remote-cache"]) !== "off" : void 0;
|
|
13070
13090
|
const engine = createEngineFromEnv({
|
|
13071
13091
|
cacheDir: args["cache-dir"] ? String(args["cache-dir"]) : void 0,
|
|
13072
13092
|
outputsDir: args["outputs-dir"] ? String(args["outputs-dir"]) : void 0,
|
|
13073
13093
|
log: (line) => process.stdout.write(`${line}
|
|
13074
|
-
`)
|
|
13075
|
-
remoteCache
|
|
13094
|
+
`)
|
|
13076
13095
|
});
|
|
13077
|
-
const runId = args["run-id"] ? String(args["run-id"]) : `r_${ulid()}`;
|
|
13078
|
-
const recordMeta = {
|
|
13079
|
-
creativeSlug: creativeSlugFromCanvasPath(filePath) ?? void 0,
|
|
13080
|
-
canvasPath: path5.relative(process.cwd(), filePath) || void 0,
|
|
13081
|
-
canvasSha: sha256Hex(Buffer.from(raw)),
|
|
13082
|
-
chatId: getEnv().BAKER_CHAT_ID || void 0
|
|
13083
|
-
};
|
|
13084
|
-
const record = args.record === false ? null : buildRecorder();
|
|
13085
13096
|
try {
|
|
13086
13097
|
const policy = args["cache-policy"] ?? "read_write";
|
|
13087
13098
|
const result = await engine.run(parsed, {
|
|
13088
|
-
run_id:
|
|
13099
|
+
run_id: args["run-id"] ? String(args["run-id"]) : void 0,
|
|
13089
13100
|
cache_policy: policy,
|
|
13090
13101
|
concurrency: resolveConcurrency(
|
|
13091
13102
|
// --concurrency wins; --parallel is the discoverable alias for the same bound.
|
|
@@ -13093,10 +13104,9 @@ var runCommand = defineCommand85({
|
|
|
13093
13104
|
process.env.BAKER_CANVAS_CONCURRENCY
|
|
13094
13105
|
)
|
|
13095
13106
|
});
|
|
13096
|
-
if (record) await record(buildRunRecord(result, recordMeta));
|
|
13097
13107
|
const keepRuns = args["keep-runs"] !== void 0 ? Number(args["keep-runs"]) : void 0;
|
|
13098
13108
|
if (keepRuns !== void 0 && Number.isFinite(keepRuns)) {
|
|
13099
|
-
const outputsDir = args["outputs-dir"] ?
|
|
13109
|
+
const outputsDir = args["outputs-dir"] ? path4.resolve(String(args["outputs-dir"])) : path4.resolve("canvas");
|
|
13100
13110
|
await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
|
|
13101
13111
|
`));
|
|
13102
13112
|
}
|
|
@@ -13124,7 +13134,6 @@ var runCommand = defineCommand85({
|
|
|
13124
13134
|
}
|
|
13125
13135
|
if (e instanceof LayerExecutionError) {
|
|
13126
13136
|
const failures = e.failures.map((f) => ({ node_id: f.nodeId, message: describeFailureReason(f.reason) }));
|
|
13127
|
-
if (record) await record(buildFailedRunRecord(runId, e.message, recordMeta));
|
|
13128
13137
|
process.stderr.write(
|
|
13129
13138
|
`${JSON.stringify({ ok: false, error: { code: "runtime", message: e.message, failures } }, null, 2)}
|
|
13130
13139
|
`
|
|
@@ -13132,30 +13141,16 @@ var runCommand = defineCommand85({
|
|
|
13132
13141
|
process.exit(1);
|
|
13133
13142
|
}
|
|
13134
13143
|
const msg = e instanceof Error ? e.message : String(e);
|
|
13135
|
-
if (record) await record(buildFailedRunRecord(runId, msg, recordMeta));
|
|
13136
13144
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "runtime", message: msg } }, null, 2)}
|
|
13137
13145
|
`);
|
|
13138
13146
|
process.exit(1);
|
|
13139
13147
|
}
|
|
13140
13148
|
}
|
|
13141
13149
|
});
|
|
13142
|
-
function buildRecorder() {
|
|
13143
|
-
return async (payload) => {
|
|
13144
|
-
try {
|
|
13145
|
-
const creds = requireCredentialsFromEnv();
|
|
13146
|
-
const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
|
|
13147
|
-
await client.recordRun(payload);
|
|
13148
|
-
} catch (e) {
|
|
13149
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
13150
|
-
process.stderr.write(`[warn] run record not persisted (${msg})
|
|
13151
|
-
`);
|
|
13152
|
-
}
|
|
13153
|
-
};
|
|
13154
|
-
}
|
|
13155
13150
|
|
|
13156
13151
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
13157
13152
|
import { readFile as readFile3, writeFile } from "fs/promises";
|
|
13158
|
-
import
|
|
13153
|
+
import path6 from "path";
|
|
13159
13154
|
import { defineCommand as defineCommand86 } from "citty";
|
|
13160
13155
|
|
|
13161
13156
|
// src/engine/scaffold/staticAd.ts
|
|
@@ -13342,20 +13337,15 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
13342
13337
|
}
|
|
13343
13338
|
|
|
13344
13339
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
13345
|
-
import
|
|
13346
|
-
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd()
|
|
13340
|
+
import path5 from "path";
|
|
13341
|
+
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd()) {
|
|
13347
13342
|
const file = rawFile.trim();
|
|
13348
13343
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
13349
|
-
const imageSource = imageIsUrl ? file :
|
|
13350
|
-
const outPath = out ?
|
|
13351
|
-
const blueprintPath =
|
|
13344
|
+
const imageSource = imageIsUrl ? file : path5.resolve(cwd, file);
|
|
13345
|
+
const outPath = out ? path5.resolve(cwd, out) : imageIsUrl ? path5.join(cwd, "static-ad.canvas.json") : path5.join(path5.dirname(imageSource), "static-ad.canvas.json");
|
|
13346
|
+
const blueprintPath = path5.join(path5.dirname(outPath), "prompt.json");
|
|
13352
13347
|
return { imageIsUrl, imageSource, outPath, blueprintPath };
|
|
13353
13348
|
}
|
|
13354
|
-
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
13355
|
-
var SCAFFOLD_SLUG_MAX_LENGTH = 100;
|
|
13356
|
-
function isValidScaffoldSlug(slug) {
|
|
13357
|
-
return slug.length <= SCAFFOLD_SLUG_MAX_LENGTH && SCAFFOLD_SLUG_PATTERN.test(slug);
|
|
13358
|
-
}
|
|
13359
13349
|
|
|
13360
13350
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
13361
13351
|
function resolveModel(kind, preferred) {
|
|
@@ -13517,10 +13507,6 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13517
13507
|
file: { type: "positional", required: true, description: "Path or http(s) URL to the source/inspiration image" },
|
|
13518
13508
|
context: { type: "string", description: "Known provenance (advertiser, category, market) to ground the describe" },
|
|
13519
13509
|
out: { type: "string", description: "Output canvas path (default <image-dir>/static-ad.canvas.json)" },
|
|
13520
|
-
slug: {
|
|
13521
|
-
type: "string",
|
|
13522
|
-
description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
|
|
13523
|
-
},
|
|
13524
13510
|
"describe-model": { type: "string", description: "Override the image_describe model id" },
|
|
13525
13511
|
"select-model": { type: "string", description: "Override the text_generate model id for element selection" },
|
|
13526
13512
|
"layout-model": { type: "string", description: "Override the text_generate model id for the layout pass" },
|
|
@@ -13529,19 +13515,9 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13529
13515
|
"skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" }
|
|
13530
13516
|
},
|
|
13531
13517
|
async run({ args }) {
|
|
13532
|
-
const slug = args.slug ? String(args.slug) : void 0;
|
|
13533
|
-
if (slug && !isValidScaffoldSlug(slug)) {
|
|
13534
|
-
process.stderr.write(
|
|
13535
|
-
`${JSON.stringify({ ok: false, error: { code: "invalid_slug", message: "--slug must be lowercase kebab (a-z, 0-9, hyphens), max 100 chars" } }, null, 2)}
|
|
13536
|
-
`
|
|
13537
|
-
);
|
|
13538
|
-
process.exit(2);
|
|
13539
|
-
}
|
|
13540
13518
|
const { imageIsUrl, imageSource, outPath, blueprintPath } = resolveScaffoldStaticAdPaths(
|
|
13541
13519
|
String(args.file),
|
|
13542
|
-
args.out ? String(args.out) : void 0
|
|
13543
|
-
process.cwd(),
|
|
13544
|
-
slug
|
|
13520
|
+
args.out ? String(args.out) : void 0
|
|
13545
13521
|
);
|
|
13546
13522
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
|
|
13547
13523
|
const describeCanvas = buildDescribeCanvas(
|
|
@@ -13601,7 +13577,7 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13601
13577
|
run_estimated_credits: validation.estimatedCredits
|
|
13602
13578
|
},
|
|
13603
13579
|
checklist: {
|
|
13604
|
-
edit_prompt: `Edit ${
|
|
13580
|
+
edit_prompt: `Edit ${path6.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
|
|
13605
13581
|
assets_to_supply: report.elements,
|
|
13606
13582
|
font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path, or delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
|
|
13607
13583
|
note: "Replace every [TODO] ingest path with a real file, then `baker canvas validate` and `baker canvas run`. Running generates a billed image \u2014 it is not free."
|
|
@@ -13617,7 +13593,7 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13617
13593
|
|
|
13618
13594
|
// src/commands/canvas/scaffold-video.ts
|
|
13619
13595
|
import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
|
|
13620
|
-
import
|
|
13596
|
+
import path9 from "path";
|
|
13621
13597
|
import { defineCommand as defineCommand87 } from "citty";
|
|
13622
13598
|
|
|
13623
13599
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
@@ -16401,23 +16377,23 @@ function videoReport(input, elementsInput) {
|
|
|
16401
16377
|
|
|
16402
16378
|
// src/commands/canvas/composition-path.ts
|
|
16403
16379
|
import { existsSync as existsSync4 } from "fs";
|
|
16404
|
-
import
|
|
16380
|
+
import path7 from "path";
|
|
16405
16381
|
function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
|
|
16406
|
-
const rel =
|
|
16382
|
+
const rel = path7.join("canvas", name);
|
|
16407
16383
|
let dir = startDir;
|
|
16408
16384
|
for (let i = 0; i < maxDepth; i++) {
|
|
16409
|
-
const candidate =
|
|
16410
|
-
if (exists(
|
|
16411
|
-
const parent =
|
|
16385
|
+
const candidate = path7.join(dir, rel);
|
|
16386
|
+
if (exists(path7.join(candidate, "meta.json"))) return candidate;
|
|
16387
|
+
const parent = path7.dirname(dir);
|
|
16412
16388
|
if (parent === dir) break;
|
|
16413
16389
|
dir = parent;
|
|
16414
16390
|
}
|
|
16415
|
-
return
|
|
16391
|
+
return path7.resolve(startDir, "../../../", rel);
|
|
16416
16392
|
}
|
|
16417
16393
|
|
|
16418
16394
|
// src/commands/canvas/gitignore.ts
|
|
16419
16395
|
import { appendFile, readFile as readFile5 } from "fs/promises";
|
|
16420
|
-
import
|
|
16396
|
+
import path8 from "path";
|
|
16421
16397
|
function missingGitignoreEntries(existing, entries) {
|
|
16422
16398
|
const present = new Set(
|
|
16423
16399
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -16425,7 +16401,7 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
16425
16401
|
return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
|
|
16426
16402
|
}
|
|
16427
16403
|
async function ensureGitignore(dir, entries) {
|
|
16428
|
-
const file =
|
|
16404
|
+
const file = path8.join(dir, ".gitignore");
|
|
16429
16405
|
let existing;
|
|
16430
16406
|
try {
|
|
16431
16407
|
existing = await readFile5(file, "utf8");
|
|
@@ -16486,7 +16462,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
16486
16462
|
async function stageCaptions(outDir, transcript) {
|
|
16487
16463
|
const text = transcript?.trim();
|
|
16488
16464
|
if (!text || text === "[]") return {};
|
|
16489
|
-
const compositionPath =
|
|
16465
|
+
const compositionPath = path9.join(outDir, "tiktok-captions-composition");
|
|
16490
16466
|
await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
16491
16467
|
return { compositionPath };
|
|
16492
16468
|
}
|
|
@@ -16504,10 +16480,10 @@ function patchCompositionHtml(html, dims) {
|
|
|
16504
16480
|
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`);
|
|
16505
16481
|
}
|
|
16506
16482
|
async function stampCompositionDims(compositionDir, dims) {
|
|
16507
|
-
const metaPath =
|
|
16483
|
+
const metaPath = path9.join(compositionDir, "meta.json");
|
|
16508
16484
|
const rawMeta = await readFile6(metaPath, "utf8");
|
|
16509
16485
|
await writeFile2(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
16510
|
-
const htmlPath =
|
|
16486
|
+
const htmlPath = path9.join(compositionDir, "index.html");
|
|
16511
16487
|
const rawHtml = await readFile6(htmlPath, "utf8");
|
|
16512
16488
|
await writeFile2(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
16513
16489
|
}
|
|
@@ -16647,10 +16623,6 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16647
16623
|
args: {
|
|
16648
16624
|
file: { type: "positional", required: true, description: "Path to the reference video" },
|
|
16649
16625
|
out: { type: "string", description: "Output canvas path (default <video-dir>/<name>.video.canvas.json)" },
|
|
16650
|
-
slug: {
|
|
16651
|
-
type: "string",
|
|
16652
|
-
description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
|
|
16653
|
-
},
|
|
16654
16626
|
frames: { type: "string", description: '"generate" (default, anchored regen) or "reuse" (wire real frames in)' },
|
|
16655
16627
|
ambient: {
|
|
16656
16628
|
type: "boolean",
|
|
@@ -16677,19 +16649,11 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16677
16649
|
}
|
|
16678
16650
|
},
|
|
16679
16651
|
async run({ args }) {
|
|
16680
|
-
const videoPath =
|
|
16681
|
-
const base =
|
|
16682
|
-
const
|
|
16683
|
-
|
|
16684
|
-
|
|
16685
|
-
`${JSON.stringify({ ok: false, error: { code: "invalid_slug", message: "--slug must be lowercase kebab (a-z, 0-9, hyphens), max 100 chars" } }, null, 2)}
|
|
16686
|
-
`
|
|
16687
|
-
);
|
|
16688
|
-
process.exit(2);
|
|
16689
|
-
}
|
|
16690
|
-
const outPath = args.out ? path10.resolve(String(args.out)) : slug ? path10.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path10.join(path10.dirname(videoPath), `${base}.video.canvas.json`);
|
|
16691
|
-
const outDir = path10.dirname(outPath);
|
|
16692
|
-
const blueprintPath = path10.join(outDir, "prompt.json");
|
|
16652
|
+
const videoPath = path9.resolve(String(args.file));
|
|
16653
|
+
const base = path9.basename(videoPath, path9.extname(videoPath));
|
|
16654
|
+
const outPath = args.out ? path9.resolve(String(args.out)) : path9.join(path9.dirname(videoPath), `${base}.video.canvas.json`);
|
|
16655
|
+
const outDir = path9.dirname(outPath);
|
|
16656
|
+
const blueprintPath = path9.join(outDir, "prompt.json");
|
|
16693
16657
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
16694
16658
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
16695
16659
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -16729,10 +16693,10 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16729
16693
|
`
|
|
16730
16694
|
);
|
|
16731
16695
|
}
|
|
16732
|
-
const compositionDest =
|
|
16696
|
+
const compositionDest = path9.join(outDir, "video-overlay-composition");
|
|
16733
16697
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
16734
16698
|
await stampCompositionDims(compositionDest, outDims);
|
|
16735
|
-
const indexPath =
|
|
16699
|
+
const indexPath = path9.join(compositionDest, "index.html");
|
|
16736
16700
|
const overlayHtml = buildOverlayHtml(blueprint);
|
|
16737
16701
|
const indexHtml = await readFile6(indexPath, "utf8");
|
|
16738
16702
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
@@ -16748,9 +16712,9 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16748
16712
|
const opts = {
|
|
16749
16713
|
imageModel,
|
|
16750
16714
|
videoModel,
|
|
16751
|
-
overlayCompositionPath:
|
|
16752
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
16753
|
-
blueprintPath:
|
|
16715
|
+
overlayCompositionPath: path9.relative(outDir, compositionDest),
|
|
16716
|
+
captionsCompositionPath: captions.compositionPath ? path9.relative(outDir, captions.compositionPath) : void 0,
|
|
16717
|
+
blueprintPath: path9.relative(outDir, blueprintPath),
|
|
16754
16718
|
frames,
|
|
16755
16719
|
ambient: Boolean(args.ambient),
|
|
16756
16720
|
...args.aspect ? { aspect: String(args.aspect) } : {},
|
|
@@ -16792,7 +16756,7 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16792
16756
|
run_estimated_credits: validation.estimatedCredits
|
|
16793
16757
|
},
|
|
16794
16758
|
checklist: {
|
|
16795
|
-
edit_prompt: `Edit ${
|
|
16759
|
+
edit_prompt: `Edit ${path9.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
|
|
16796
16760
|
recurring_elements_to_supply: report.elements,
|
|
16797
16761
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
16798
16762
|
scene: d.scene,
|
|
@@ -16819,7 +16783,7 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16819
16783
|
|
|
16820
16784
|
// src/commands/canvas/set-prompt.ts
|
|
16821
16785
|
import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
|
|
16822
|
-
import
|
|
16786
|
+
import path10 from "path";
|
|
16823
16787
|
import { defineCommand as defineCommand88 } from "citty";
|
|
16824
16788
|
function setNodePrompt(canvas, nodeId, text) {
|
|
16825
16789
|
const nodes = canvas?.nodes;
|
|
@@ -16847,7 +16811,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16847
16811
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
16848
16812
|
},
|
|
16849
16813
|
async run({ args }) {
|
|
16850
|
-
const filePath =
|
|
16814
|
+
const filePath = path10.resolve(String(args.file));
|
|
16851
16815
|
let canvas;
|
|
16852
16816
|
try {
|
|
16853
16817
|
canvas = JSON.parse(await readFile7(filePath, "utf8"));
|
|
@@ -16857,7 +16821,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16857
16821
|
process.exit(2);
|
|
16858
16822
|
}
|
|
16859
16823
|
let text;
|
|
16860
|
-
if (args["text-file"]) text = await readFile7(
|
|
16824
|
+
if (args["text-file"]) text = await readFile7(path10.resolve(String(args["text-file"])), "utf8");
|
|
16861
16825
|
else if (args.text !== void 0) text = String(args.text);
|
|
16862
16826
|
else {
|
|
16863
16827
|
process.stderr.write(
|
|
@@ -16878,7 +16842,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16878
16842
|
process.exit(2);
|
|
16879
16843
|
return;
|
|
16880
16844
|
}
|
|
16881
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
16845
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path10.dirname(filePath)), defaultRegistry());
|
|
16882
16846
|
if (!validation.ok) {
|
|
16883
16847
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
16884
16848
|
`);
|
|
@@ -16894,7 +16858,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16894
16858
|
|
|
16895
16859
|
// src/commands/canvas/validate.ts
|
|
16896
16860
|
import { readFile as readFile8 } from "fs/promises";
|
|
16897
|
-
import
|
|
16861
|
+
import path11 from "path";
|
|
16898
16862
|
import { defineCommand as defineCommand89 } from "citty";
|
|
16899
16863
|
var validateCommand = defineCommand89({
|
|
16900
16864
|
meta: {
|
|
@@ -16903,7 +16867,7 @@ var validateCommand = defineCommand89({
|
|
|
16903
16867
|
},
|
|
16904
16868
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
16905
16869
|
async run({ args }) {
|
|
16906
|
-
const filePath =
|
|
16870
|
+
const filePath = path11.resolve(String(args.file));
|
|
16907
16871
|
const raw = await readFile8(filePath, "utf8");
|
|
16908
16872
|
let parsed;
|
|
16909
16873
|
try {
|
|
@@ -16914,7 +16878,7 @@ var validateCommand = defineCommand89({
|
|
|
16914
16878
|
`);
|
|
16915
16879
|
process.exit(2);
|
|
16916
16880
|
}
|
|
16917
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
16881
|
+
parsed = resolveRelativeCanvasPaths(parsed, path11.dirname(filePath));
|
|
16918
16882
|
const result = await validateCanvasDeep(parsed, defaultRegistry());
|
|
16919
16883
|
if (!result.ok) {
|
|
16920
16884
|
process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
|
|
@@ -17051,16 +17015,6 @@ registerSchema({
|
|
|
17051
17015
|
type: "string",
|
|
17052
17016
|
description: "Optional URL of the original reference ad",
|
|
17053
17017
|
required: false
|
|
17054
|
-
},
|
|
17055
|
-
slug: {
|
|
17056
|
-
type: "string",
|
|
17057
|
-
description: "Creative slug (src/creatives/<slug>/) \u2014 attaches the image to that creative's row",
|
|
17058
|
-
required: false
|
|
17059
|
-
},
|
|
17060
|
-
runId: {
|
|
17061
|
-
type: "string",
|
|
17062
|
-
description: "Canvas run id (r_\u2026) of the approved generation to pin as published",
|
|
17063
|
-
required: false
|
|
17064
17018
|
}
|
|
17065
17019
|
}
|
|
17066
17020
|
});
|
|
@@ -17070,13 +17024,6 @@ function detectCreativeContentType(filePath) {
|
|
|
17070
17024
|
unsupportedMessage: "Unsupported creative image extension. Use PNG, JPG, or WebP."
|
|
17071
17025
|
});
|
|
17072
17026
|
}
|
|
17073
|
-
function chatIdFromEnv() {
|
|
17074
|
-
try {
|
|
17075
|
-
return getEnv().BAKER_CHAT_ID || void 0;
|
|
17076
|
-
} catch {
|
|
17077
|
-
return void 0;
|
|
17078
|
-
}
|
|
17079
|
-
}
|
|
17080
17027
|
function parseOptionalUrl(value) {
|
|
17081
17028
|
if (value === void 0 || value.trim() === "") {
|
|
17082
17029
|
return void 0;
|
|
@@ -17107,11 +17054,7 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
|
|
|
17107
17054
|
return publishImageAsCreative(deps, {
|
|
17108
17055
|
imageId: upload.imageId,
|
|
17109
17056
|
title,
|
|
17110
|
-
sourceReferenceUrl
|
|
17111
|
-
slug: args.slug,
|
|
17112
|
-
runId: args.runId,
|
|
17113
|
-
// Attribute the publish to the driving chat (injected by the bridge).
|
|
17114
|
-
chatId: chatIdFromEnv()
|
|
17057
|
+
sourceReferenceUrl
|
|
17115
17058
|
});
|
|
17116
17059
|
}
|
|
17117
17060
|
var publishCommand = defineCommand91({
|
|
@@ -17127,16 +17070,6 @@ var publishCommand = defineCommand91({
|
|
|
17127
17070
|
type: "string",
|
|
17128
17071
|
description: "Optional URL of the original reference ad",
|
|
17129
17072
|
required: false
|
|
17130
|
-
},
|
|
17131
|
-
slug: {
|
|
17132
|
-
type: "string",
|
|
17133
|
-
description: "Creative slug (src/creatives/<slug>/) \u2014 attaches the image to that creative's row",
|
|
17134
|
-
required: false
|
|
17135
|
-
},
|
|
17136
|
-
runId: {
|
|
17137
|
-
type: "string",
|
|
17138
|
-
description: "Canvas run id (r_\u2026) of the approved generation to pin as published",
|
|
17139
|
-
required: false
|
|
17140
17073
|
}
|
|
17141
17074
|
},
|
|
17142
17075
|
run: async ({ args }) => {
|
|
@@ -17155,9 +17088,7 @@ var publishCommand = defineCommand91({
|
|
|
17155
17088
|
file,
|
|
17156
17089
|
title,
|
|
17157
17090
|
context: args.context,
|
|
17158
|
-
sourceReferenceUrl: args.sourceReferenceUrl
|
|
17159
|
-
slug: args.slug,
|
|
17160
|
-
runId: args.runId
|
|
17091
|
+
sourceReferenceUrl: args.sourceReferenceUrl
|
|
17161
17092
|
});
|
|
17162
17093
|
writeJson({ ok: true, data });
|
|
17163
17094
|
} catch (err) {
|
|
@@ -18055,9 +17986,9 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
18055
17986
|
}
|
|
18056
17987
|
return readFile10(pathOrUrl);
|
|
18057
17988
|
}
|
|
18058
|
-
async function isDirectory(
|
|
17989
|
+
async function isDirectory(path12) {
|
|
18059
17990
|
try {
|
|
18060
|
-
const s = await stat2(
|
|
17991
|
+
const s = await stat2(path12);
|
|
18061
17992
|
return s.isDirectory();
|
|
18062
17993
|
} catch {
|
|
18063
17994
|
return false;
|