@koda-sl/baker-cli 0.362.0 → 0.364.0-dev.18349e66a
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 +8 -4
- package/dist/{chunk-AI4HWQVN.js → chunk-K2IKLMCM.js} +4 -4
- package/dist/chunk-K2IKLMCM.js.map +1 -0
- package/dist/cli.js +475 -129
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-AI4HWQVN.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
validateCanvasDeep,
|
|
70
70
|
withoutCastName,
|
|
71
71
|
ytDlpBlockSignal
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-K2IKLMCM.js";
|
|
73
73
|
import {
|
|
74
74
|
csvOrJson,
|
|
75
75
|
daysAgoIso,
|
|
@@ -22933,9 +22933,49 @@ Subcommands:
|
|
|
22933
22933
|
|
|
22934
22934
|
// src/commands/ads/linkedin/creatives.ts
|
|
22935
22935
|
import { defineCommand as defineCommand44 } from "citty";
|
|
22936
|
+
|
|
22937
|
+
// src/commands/ads/list-rows.ts
|
|
22936
22938
|
function asRecord(value) {
|
|
22937
22939
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
22938
22940
|
}
|
|
22941
|
+
function readPath2(row, path48) {
|
|
22942
|
+
let current = row;
|
|
22943
|
+
for (const segment of path48.split(".")) {
|
|
22944
|
+
const record = asRecord(current);
|
|
22945
|
+
if (!record) return void 0;
|
|
22946
|
+
current = record[segment];
|
|
22947
|
+
}
|
|
22948
|
+
return current;
|
|
22949
|
+
}
|
|
22950
|
+
function projectFields(rows, paths) {
|
|
22951
|
+
return rows.map((row) => {
|
|
22952
|
+
const projected = {};
|
|
22953
|
+
for (const path48 of paths) {
|
|
22954
|
+
const value = readPath2(row, path48);
|
|
22955
|
+
if (value !== void 0) {
|
|
22956
|
+
projected[path48] = value;
|
|
22957
|
+
}
|
|
22958
|
+
}
|
|
22959
|
+
return projected;
|
|
22960
|
+
});
|
|
22961
|
+
}
|
|
22962
|
+
function shapeListRows(rows, args5, compact3) {
|
|
22963
|
+
const raw = rows;
|
|
22964
|
+
const fields = typeof args5.fields === "string" ? args5.fields : void 0;
|
|
22965
|
+
if (fields) {
|
|
22966
|
+
const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
|
|
22967
|
+
if (paths.length > 0) {
|
|
22968
|
+
return { rows: projectFields(raw, paths), fields: paths };
|
|
22969
|
+
}
|
|
22970
|
+
}
|
|
22971
|
+
if (args5.full) return { rows: raw };
|
|
22972
|
+
return { rows: rows.map(compact3.row), fields: [...compact3.columns] };
|
|
22973
|
+
}
|
|
22974
|
+
|
|
22975
|
+
// src/commands/ads/linkedin/creatives.ts
|
|
22976
|
+
function asRecord2(value) {
|
|
22977
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
22978
|
+
}
|
|
22939
22979
|
function asString(value) {
|
|
22940
22980
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
22941
22981
|
}
|
|
@@ -22943,12 +22983,12 @@ function creativeDestination(content) {
|
|
|
22943
22983
|
if (!content) return { format: "unknown" };
|
|
22944
22984
|
const reference = asString(content.reference);
|
|
22945
22985
|
if (reference) return { format: "post", postUrn: reference };
|
|
22946
|
-
const textAd =
|
|
22986
|
+
const textAd = asRecord2(content.textAd);
|
|
22947
22987
|
if (textAd) return { format: "text", landingUrl: asString(textAd.landingPage) };
|
|
22948
|
-
const spotlight =
|
|
22988
|
+
const spotlight = asRecord2(content.spotlight);
|
|
22949
22989
|
if (spotlight) return { format: "spotlight", landingUrl: asString(spotlight.landingPage) };
|
|
22950
|
-
if (
|
|
22951
|
-
if (
|
|
22990
|
+
if (asRecord2(content.follower)) return { format: "follower" };
|
|
22991
|
+
if (asRecord2(content.jobs)) return { format: "jobs" };
|
|
22952
22992
|
return { format: "unknown" };
|
|
22953
22993
|
}
|
|
22954
22994
|
var COMPACT_COLUMNS = [
|
|
@@ -22976,27 +23016,6 @@ function compactRow(row) {
|
|
|
22976
23016
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
22977
23017
|
};
|
|
22978
23018
|
}
|
|
22979
|
-
function readPath2(row, path48) {
|
|
22980
|
-
let current = row;
|
|
22981
|
-
for (const segment of path48.split(".")) {
|
|
22982
|
-
const record = asRecord(current);
|
|
22983
|
-
if (!record) return void 0;
|
|
22984
|
-
current = record[segment];
|
|
22985
|
-
}
|
|
22986
|
-
return current;
|
|
22987
|
-
}
|
|
22988
|
-
function projectFields(rows, paths) {
|
|
22989
|
-
return rows.map((row) => {
|
|
22990
|
-
const projected = {};
|
|
22991
|
-
for (const path48 of paths) {
|
|
22992
|
-
const value = readPath2(row, path48);
|
|
22993
|
-
if (value !== void 0) {
|
|
22994
|
-
projected[path48] = value;
|
|
22995
|
-
}
|
|
22996
|
-
}
|
|
22997
|
-
return projected;
|
|
22998
|
-
});
|
|
22999
|
-
}
|
|
23000
23019
|
function unmappedShapeWarnings(rows, rawContentReturned) {
|
|
23001
23020
|
const unmapped = rows.filter((row) => row.content && creativeDestination(row.content).format === "unknown");
|
|
23002
23021
|
if (unmapped.length === 0) return [];
|
|
@@ -23008,18 +23027,6 @@ function unmappedShapeWarnings(rows, rawContentReturned) {
|
|
|
23008
23027
|
}
|
|
23009
23028
|
];
|
|
23010
23029
|
}
|
|
23011
|
-
function shapeRows(rows, args5) {
|
|
23012
|
-
const raw = rows;
|
|
23013
|
-
const fields = typeof args5.fields === "string" ? args5.fields : void 0;
|
|
23014
|
-
if (fields) {
|
|
23015
|
-
const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
|
|
23016
|
-
if (paths.length > 0) {
|
|
23017
|
-
return { rows: projectFields(raw, paths), fields: paths };
|
|
23018
|
-
}
|
|
23019
|
-
}
|
|
23020
|
-
if (args5.full) return { rows: raw };
|
|
23021
|
-
return { rows: rows.map(compactRow), fields: [...COMPACT_COLUMNS] };
|
|
23022
|
-
}
|
|
23023
23030
|
var listArgs3 = {
|
|
23024
23031
|
"account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
|
|
23025
23032
|
"account-urn": { type: "string", description: "Alias for --account-id" },
|
|
@@ -23051,7 +23058,10 @@ var creativesListCommand = defineCommand44({
|
|
|
23051
23058
|
if (args5.limit) params.limit = String(args5.limit);
|
|
23052
23059
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
23053
23060
|
const data = await apiGet("/api/ads/linkedin/creatives", params);
|
|
23054
|
-
const shaped =
|
|
23061
|
+
const shaped = shapeListRows(data, args5, {
|
|
23062
|
+
row: compactRow,
|
|
23063
|
+
columns: COMPACT_COLUMNS
|
|
23064
|
+
});
|
|
23055
23065
|
const fmt = csvOrJson2(args5);
|
|
23056
23066
|
if (fmt !== "json") {
|
|
23057
23067
|
writeAdsOutput(shaped.rows, fmt, shaped.fields);
|
|
@@ -24069,22 +24079,178 @@ Examples:
|
|
|
24069
24079
|
|
|
24070
24080
|
// src/commands/ads/meta/activities.ts
|
|
24071
24081
|
import { defineCommand as defineCommand56 } from "citty";
|
|
24082
|
+
|
|
24083
|
+
// src/commands/ads/meta/list-rows.ts
|
|
24084
|
+
function pick(row, keys) {
|
|
24085
|
+
const picked = {};
|
|
24086
|
+
for (const key of keys) {
|
|
24087
|
+
if (row[key] !== void 0) picked[key] = row[key];
|
|
24088
|
+
}
|
|
24089
|
+
return picked;
|
|
24090
|
+
}
|
|
24091
|
+
function asRecord3(value) {
|
|
24092
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
24093
|
+
}
|
|
24094
|
+
var CAMPAIGN_COLUMNS = [
|
|
24095
|
+
"id",
|
|
24096
|
+
"name",
|
|
24097
|
+
"effective_status",
|
|
24098
|
+
"objective",
|
|
24099
|
+
"daily_budget",
|
|
24100
|
+
"lifetime_budget",
|
|
24101
|
+
"bid_strategy",
|
|
24102
|
+
"special_ad_categories",
|
|
24103
|
+
"stop_time",
|
|
24104
|
+
"created_time"
|
|
24105
|
+
];
|
|
24106
|
+
var ADSET_KEYS = [
|
|
24107
|
+
"id",
|
|
24108
|
+
"name",
|
|
24109
|
+
"campaign_id",
|
|
24110
|
+
"effective_status",
|
|
24111
|
+
"optimization_goal",
|
|
24112
|
+
"billing_event",
|
|
24113
|
+
"bid_strategy",
|
|
24114
|
+
"daily_budget",
|
|
24115
|
+
"lifetime_budget",
|
|
24116
|
+
"destination_type"
|
|
24117
|
+
];
|
|
24118
|
+
var ADSET_COLUMNS = [...ADSET_KEYS, "learning_stage", "end_time", "created_time"];
|
|
24119
|
+
function compactAdSet(row) {
|
|
24120
|
+
const learning = asRecord3(row.learning_stage_info)?.status;
|
|
24121
|
+
return {
|
|
24122
|
+
...pick(row, ADSET_KEYS),
|
|
24123
|
+
...typeof learning === "string" ? { learning_stage: learning } : {},
|
|
24124
|
+
...pick(row, ["end_time", "created_time"])
|
|
24125
|
+
};
|
|
24126
|
+
}
|
|
24127
|
+
var AD_COLUMNS = [
|
|
24128
|
+
"id",
|
|
24129
|
+
"name",
|
|
24130
|
+
"adset_id",
|
|
24131
|
+
"campaign_id",
|
|
24132
|
+
"effective_status",
|
|
24133
|
+
"creative_id",
|
|
24134
|
+
"issues",
|
|
24135
|
+
"created_time"
|
|
24136
|
+
];
|
|
24137
|
+
function adIssues(value) {
|
|
24138
|
+
if (!Array.isArray(value)) return [];
|
|
24139
|
+
return value.flatMap((entry) => {
|
|
24140
|
+
const issue = asRecord3(entry);
|
|
24141
|
+
const text2 = issue?.error_summary ?? issue?.error_message;
|
|
24142
|
+
return typeof text2 === "string" && text2 ? [text2] : [];
|
|
24143
|
+
});
|
|
24144
|
+
}
|
|
24145
|
+
function compactAd(row) {
|
|
24146
|
+
const creativeId = asRecord3(row.creative)?.id;
|
|
24147
|
+
const issues = adIssues(row.issues_info);
|
|
24148
|
+
return {
|
|
24149
|
+
...pick(row, ["id", "name", "adset_id", "campaign_id", "effective_status"]),
|
|
24150
|
+
...creativeId !== void 0 ? { creative_id: creativeId } : {},
|
|
24151
|
+
...issues.length > 0 ? { issues } : {},
|
|
24152
|
+
...pick(row, ["created_time"])
|
|
24153
|
+
};
|
|
24154
|
+
}
|
|
24155
|
+
var ACTIVITY_COLUMNS = [
|
|
24156
|
+
"event_time",
|
|
24157
|
+
"event_type",
|
|
24158
|
+
"actor_name",
|
|
24159
|
+
"object_type",
|
|
24160
|
+
"object_name",
|
|
24161
|
+
"object_id",
|
|
24162
|
+
"change"
|
|
24163
|
+
];
|
|
24164
|
+
function isScalar(value) {
|
|
24165
|
+
return value === null || typeof value === "string" || typeof value === "number";
|
|
24166
|
+
}
|
|
24167
|
+
function activityChange(extraData) {
|
|
24168
|
+
if (typeof extraData !== "string") return void 0;
|
|
24169
|
+
let parsed;
|
|
24170
|
+
try {
|
|
24171
|
+
parsed = JSON.parse(extraData);
|
|
24172
|
+
} catch {
|
|
24173
|
+
return void 0;
|
|
24174
|
+
}
|
|
24175
|
+
const data = asRecord3(parsed);
|
|
24176
|
+
if (!data || !("new_value" in data) || !isScalar(data.new_value) || !isScalar(data.old_value ?? null)) {
|
|
24177
|
+
return void 0;
|
|
24178
|
+
}
|
|
24179
|
+
const show = (value) => value === null || value === void 0 ? "(none)" : String(value);
|
|
24180
|
+
return `${show(data.old_value)} \u2192 ${show(data.new_value)}`;
|
|
24181
|
+
}
|
|
24182
|
+
function compactActivity(row) {
|
|
24183
|
+
const change = activityChange(row.extra_data);
|
|
24184
|
+
return {
|
|
24185
|
+
...pick(row, ["event_time", "event_type", "actor_name", "object_type", "object_name", "object_id"]),
|
|
24186
|
+
...change !== void 0 ? { change } : {}
|
|
24187
|
+
};
|
|
24188
|
+
}
|
|
24189
|
+
var PROJECTIONS = {
|
|
24190
|
+
campaigns: { row: (row) => pick(row, CAMPAIGN_COLUMNS), columns: CAMPAIGN_COLUMNS },
|
|
24191
|
+
adsets: { row: compactAdSet, columns: ADSET_COLUMNS },
|
|
24192
|
+
ads: { row: compactAd, columns: AD_COLUMNS },
|
|
24193
|
+
activities: { row: compactActivity, columns: ACTIVITY_COLUMNS }
|
|
24194
|
+
};
|
|
24195
|
+
function shapeMetaListRows(kind, rows, args5) {
|
|
24196
|
+
return shapeListRows(rows, args5, PROJECTIONS[kind]);
|
|
24197
|
+
}
|
|
24198
|
+
var META_LIST_SHAPE_ARGS = {
|
|
24199
|
+
fields: {
|
|
24200
|
+
type: "string",
|
|
24201
|
+
description: "CSV of dotted paths to return instead of the compact row, e.g. id,name,targeting.geo_locations"
|
|
24202
|
+
},
|
|
24203
|
+
full: { type: "boolean", description: "Return every field Meta sent (targeting, tracking specs, raw extra_data)" },
|
|
24204
|
+
output: { type: "string", description: "json|csv|jsonl|md \u2014 md prints a table of the compact row", default: "json" }
|
|
24205
|
+
};
|
|
24206
|
+
function registerMetaListSchema(kind, description, args5) {
|
|
24207
|
+
registerSchema({
|
|
24208
|
+
command: `ads.meta.${kind}`,
|
|
24209
|
+
description,
|
|
24210
|
+
args: Object.fromEntries(
|
|
24211
|
+
Object.entries(args5).map(([name, def]) => [
|
|
24212
|
+
name,
|
|
24213
|
+
{
|
|
24214
|
+
type: def.type,
|
|
24215
|
+
description: def.description ?? "",
|
|
24216
|
+
required: false,
|
|
24217
|
+
...def.default !== void 0 ? { default: def.default } : {}
|
|
24218
|
+
}
|
|
24219
|
+
])
|
|
24220
|
+
)
|
|
24221
|
+
});
|
|
24222
|
+
}
|
|
24223
|
+
|
|
24224
|
+
// src/commands/ads/meta/activities.ts
|
|
24225
|
+
var listArgs4 = {
|
|
24226
|
+
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
24227
|
+
days: { type: "string", description: "Lookback days (default 7)" },
|
|
24228
|
+
limit: { type: "string", description: "Max rows" },
|
|
24229
|
+
"no-cache": NO_CACHE_ARG,
|
|
24230
|
+
...META_LIST_SHAPE_ARGS
|
|
24231
|
+
};
|
|
24232
|
+
registerMetaListSchema(
|
|
24233
|
+
"activities",
|
|
24234
|
+
"Account change log. Compact by default, one row per event: event_time, event_type, actor_name, object_type, object_name, object_id, and change ('old \u2192 new' when the change is a single value). Start here: baker ads meta activities --account-id act_123 --days 2 --output md. --fields event_time,object_name,extra_data or --full for the raw change payload.",
|
|
24235
|
+
listArgs4
|
|
24236
|
+
);
|
|
24072
24237
|
var activitiesCommand = defineCommand56({
|
|
24073
24238
|
meta: {
|
|
24074
24239
|
name: "activities",
|
|
24075
24240
|
description: `Audit log of recent ad-account changes (created, paused, edited). Default lookback 7 days,
|
|
24076
24241
|
~90 day retention server-side.
|
|
24077
24242
|
|
|
24243
|
+
Compact by default, one row per event: event_time, event_type, actor_name, object_type,
|
|
24244
|
+
object_name, object_id, and change ("old \u2192 new" when the change is a single value: status,
|
|
24245
|
+
name, budget, goal). --fields takes dotted paths (extra_data holds the raw change); --full returns
|
|
24246
|
+
every field Meta sent.
|
|
24247
|
+
|
|
24078
24248
|
Examples:
|
|
24079
|
-
baker ads meta activities --account-id act_123 --days 14
|
|
24080
|
-
|
|
24081
|
-
|
|
24082
|
-
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
24083
|
-
days: { type: "string", description: "Lookback days (default 7)" },
|
|
24084
|
-
limit: { type: "string", description: "Max rows" },
|
|
24085
|
-
"no-cache": NO_CACHE_ARG,
|
|
24086
|
-
output: { type: "string", description: "Output format", default: "json" }
|
|
24249
|
+
baker ads meta activities --account-id act_123 --days 14
|
|
24250
|
+
baker ads meta activities --account-id act_123 --days 2 --output md
|
|
24251
|
+
baker ads meta activities --account-id act_123 --fields event_time,object_name,extra_data`
|
|
24087
24252
|
},
|
|
24253
|
+
args: listArgs4,
|
|
24088
24254
|
run: async ({ args: args5 }) => {
|
|
24089
24255
|
const accountId = resolveAccountIdArg(args5);
|
|
24090
24256
|
try {
|
|
@@ -24093,12 +24259,13 @@ Examples:
|
|
|
24093
24259
|
if (args5.limit) params.limit = args5.limit;
|
|
24094
24260
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
24095
24261
|
const data = await apiGet("/api/ads/meta/activities", params);
|
|
24262
|
+
const shaped = shapeMetaListRows("activities", data, args5);
|
|
24096
24263
|
const fmt = csvOrJson(args5);
|
|
24097
24264
|
if (fmt !== "json") {
|
|
24098
|
-
writeAdsOutput(
|
|
24265
|
+
writeAdsOutput(shaped.rows, fmt, shaped.fields);
|
|
24099
24266
|
return;
|
|
24100
24267
|
}
|
|
24101
|
-
writeAdsJson({ ok: true, data });
|
|
24268
|
+
writeAdsJson({ ok: true, data: shaped.rows });
|
|
24102
24269
|
} catch (err) {
|
|
24103
24270
|
handleMetaError(err);
|
|
24104
24271
|
}
|
|
@@ -26541,7 +26708,7 @@ var adsArchiveCommand = statusCommand3("archive", "ad.update", "ad");
|
|
|
26541
26708
|
var adsDuplicateCommand = duplicateCommand2("ad", "ad");
|
|
26542
26709
|
|
|
26543
26710
|
// src/commands/ads/meta/ads.ts
|
|
26544
|
-
var
|
|
26711
|
+
var listArgs5 = {
|
|
26545
26712
|
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
26546
26713
|
"adset-id": { type: "string", description: "Filter to one adset" },
|
|
26547
26714
|
"campaign-id": { type: "string", description: "Filter to one campaign" },
|
|
@@ -26549,14 +26716,19 @@ var listArgs4 = {
|
|
|
26549
26716
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
26550
26717
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26551
26718
|
"no-cache": NO_CACHE_ARG,
|
|
26552
|
-
|
|
26719
|
+
...META_LIST_SHAPE_ARGS
|
|
26553
26720
|
};
|
|
26721
|
+
registerMetaListSchema(
|
|
26722
|
+
"ads",
|
|
26723
|
+
"List ads in a Meta ad account. Compact by default: id, name, adset_id, campaign_id, effective_status, creative_id, issues (why an ad is WITH_ISSUES), created_time. Start here: baker ads meta ads --adset-id 6123 --output md. --fields takes dotted paths (e.g. id,tracking_specs); --full returns every field Meta sent.",
|
|
26724
|
+
listArgs5
|
|
26725
|
+
);
|
|
26554
26726
|
var listCommand4 = defineCommand58({
|
|
26555
26727
|
meta: {
|
|
26556
26728
|
name: "list",
|
|
26557
26729
|
description: "List ads in a Meta ad account. Runs by default when no subcommand is given. Example: baker ads meta ads --account-id act_123"
|
|
26558
26730
|
},
|
|
26559
|
-
args:
|
|
26731
|
+
args: listArgs5,
|
|
26560
26732
|
run: async ({ args: args5 }) => {
|
|
26561
26733
|
const accountId = resolveAccountIdArg(args5);
|
|
26562
26734
|
const effectiveStatus = resolveEffectiveStatus(args5);
|
|
@@ -26568,12 +26740,13 @@ var listCommand4 = defineCommand58({
|
|
|
26568
26740
|
if (args5.limit) params.limit = args5.limit;
|
|
26569
26741
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26570
26742
|
const data = await apiGet("/api/ads/meta/ads", params);
|
|
26743
|
+
const shaped = shapeMetaListRows("ads", data, args5);
|
|
26571
26744
|
const fmt = csvOrJson(args5);
|
|
26572
26745
|
if (fmt !== "json") {
|
|
26573
|
-
writeAdsOutput(
|
|
26746
|
+
writeAdsOutput(shaped.rows, fmt, shaped.fields);
|
|
26574
26747
|
return;
|
|
26575
26748
|
}
|
|
26576
|
-
writeAdsJson({ ok: true, data });
|
|
26749
|
+
writeAdsJson({ ok: true, data: shaped.rows });
|
|
26577
26750
|
} catch (err) {
|
|
26578
26751
|
handleMetaError(err);
|
|
26579
26752
|
}
|
|
@@ -26587,9 +26760,14 @@ Defaults to ACTIVE only \u2014 pass --all-statuses to widen. Write subcommands s
|
|
|
26587
26760
|
|
|
26588
26761
|
The dashboard shows effective_status (e.g. WITH_ISSUES, PENDING_REVIEW, DISAPPROVED, ADSET_PAUSED), not status.
|
|
26589
26762
|
|
|
26763
|
+
Listing rows are compact: id, name, adset_id, campaign_id, effective_status, creative_id, issues
|
|
26764
|
+
(why an ad is WITH_ISSUES), created_time. --fields takes dotted paths (e.g. tracking_specs,
|
|
26765
|
+
preview_shareable_link); --full returns every field.
|
|
26766
|
+
|
|
26590
26767
|
Examples:
|
|
26591
26768
|
baker ads meta ads --account-id act_123
|
|
26592
26769
|
baker ads meta ads --adset-id 6123
|
|
26770
|
+
baker ads meta ads --adset-id 6123 --fields id,name,preview_shareable_link
|
|
26593
26771
|
baker ads meta ads create --name "Ad 1" --adset meta_temp_as01 --creative meta_temp_cr01
|
|
26594
26772
|
baker ads meta ads pause 6123 --account-id act_123
|
|
26595
26773
|
baker ads meta ads retag 6123 --set "utm_medium=paid-social" \u2014 fix tracking on a LIVE ad, keeping everything else`
|
|
@@ -26608,27 +26786,32 @@ Examples:
|
|
|
26608
26786
|
// subcommand name using THIS command's arg defs, so without them `--account-id act_1`
|
|
26609
26787
|
// would read `act_1` as a subcommand. `default` (not `run`) dispatches the bare
|
|
26610
26788
|
// invocation — citty falls through to `run` after a subcommand, double-executing it.
|
|
26611
|
-
args:
|
|
26789
|
+
args: listArgs5,
|
|
26612
26790
|
default: "list"
|
|
26613
26791
|
});
|
|
26614
26792
|
|
|
26615
26793
|
// src/commands/ads/meta/adsets.ts
|
|
26616
26794
|
import { defineCommand as defineCommand59 } from "citty";
|
|
26617
|
-
var
|
|
26795
|
+
var listArgs6 = {
|
|
26618
26796
|
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
26619
26797
|
"campaign-id": { type: "string", description: "Filter to one campaign" },
|
|
26620
26798
|
"effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
|
|
26621
26799
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
26622
26800
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26623
26801
|
"no-cache": NO_CACHE_ARG,
|
|
26624
|
-
|
|
26802
|
+
...META_LIST_SHAPE_ARGS
|
|
26625
26803
|
};
|
|
26804
|
+
registerMetaListSchema(
|
|
26805
|
+
"adsets",
|
|
26806
|
+
"List ad sets in a Meta ad account. Compact by default: id, name, campaign_id, effective_status, optimization_goal, billing_event, bid_strategy, budgets, destination_type, learning_stage, end_time, created_time. Targeting is not in the compact row \u2014 ask for it with --fields id,name,targeting (or --full). Start here: baker ads meta adsets --campaign-id 6123 --all-statuses.",
|
|
26807
|
+
listArgs6
|
|
26808
|
+
);
|
|
26626
26809
|
var listCommand5 = defineCommand59({
|
|
26627
26810
|
meta: {
|
|
26628
26811
|
name: "list",
|
|
26629
26812
|
description: "List ad sets in a Meta ad account. Runs by default when no subcommand is given. Example: baker ads meta adsets --account-id act_123"
|
|
26630
26813
|
},
|
|
26631
|
-
args:
|
|
26814
|
+
args: listArgs6,
|
|
26632
26815
|
run: async ({ args: args5 }) => {
|
|
26633
26816
|
const accountId = resolveAccountIdArg(args5);
|
|
26634
26817
|
const effectiveStatus = resolveEffectiveStatus(args5);
|
|
@@ -26639,12 +26822,13 @@ var listCommand5 = defineCommand59({
|
|
|
26639
26822
|
if (args5.limit) params.limit = args5.limit;
|
|
26640
26823
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26641
26824
|
const data = await apiGet("/api/ads/meta/adsets", params);
|
|
26825
|
+
const shaped = shapeMetaListRows("adsets", data, args5);
|
|
26642
26826
|
const fmt = csvOrJson(args5);
|
|
26643
26827
|
if (fmt !== "json") {
|
|
26644
|
-
writeAdsOutput(
|
|
26828
|
+
writeAdsOutput(shaped.rows, fmt, shaped.fields);
|
|
26645
26829
|
return;
|
|
26646
26830
|
}
|
|
26647
|
-
writeAdsJson({ ok: true, data });
|
|
26831
|
+
writeAdsJson({ ok: true, data: shaped.rows });
|
|
26648
26832
|
} catch (err) {
|
|
26649
26833
|
handleMetaError(err);
|
|
26650
26834
|
}
|
|
@@ -26656,9 +26840,14 @@ var adsetsCommand = defineCommand59({
|
|
|
26656
26840
|
description: `List ad sets in a Meta ad account, or stage writes (create/update/pause/resume/archive/duplicate).
|
|
26657
26841
|
Defaults to ACTIVE only. Write subcommands stage ops on this chat's draft.
|
|
26658
26842
|
|
|
26843
|
+
Listing rows are compact: id, name, campaign_id, effective_status, optimization_goal, billing_event,
|
|
26844
|
+
bid_strategy, budgets, destination_type, learning_stage, end_time, created_time. Targeting is not in
|
|
26845
|
+
the compact row \u2014 ask for it with --fields (dotted paths) or --full.
|
|
26846
|
+
|
|
26659
26847
|
Examples:
|
|
26660
26848
|
baker ads meta adsets --account-id act_123
|
|
26661
26849
|
baker ads meta adsets --campaign-id 6123456789
|
|
26850
|
+
baker ads meta adsets --campaign-id 6123456789 --fields id,name,targeting.geo_locations
|
|
26662
26851
|
baker ads meta adsets create --campaign meta_temp_ab12 --name "US 25-54" --optimization-goal LINK_CLICKS --billing-event IMPRESSIONS --daily-budget 50 --targeting-file t.json`
|
|
26663
26852
|
},
|
|
26664
26853
|
subCommands: {
|
|
@@ -26674,13 +26863,13 @@ Examples:
|
|
|
26674
26863
|
// subcommand name using THIS command's arg defs, so without them `--account-id act_1`
|
|
26675
26864
|
// would read `act_1` as a subcommand. `default` (not `run`) dispatches the bare
|
|
26676
26865
|
// invocation — citty falls through to `run` after a subcommand, double-executing it.
|
|
26677
|
-
args:
|
|
26866
|
+
args: listArgs6,
|
|
26678
26867
|
default: "list"
|
|
26679
26868
|
});
|
|
26680
26869
|
|
|
26681
26870
|
// src/commands/ads/meta/audiences.ts
|
|
26682
26871
|
import { defineCommand as defineCommand60 } from "citty";
|
|
26683
|
-
var
|
|
26872
|
+
var listArgs7 = {
|
|
26684
26873
|
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
26685
26874
|
limit: { type: "string", description: "Max rows" },
|
|
26686
26875
|
"no-cache": NO_CACHE_ARG,
|
|
@@ -26691,7 +26880,7 @@ var listCommand6 = defineCommand60({
|
|
|
26691
26880
|
name: "list",
|
|
26692
26881
|
description: "List custom audiences. Runs by default when no subcommand is given. Example: baker ads meta audiences --account-id act_123"
|
|
26693
26882
|
},
|
|
26694
|
-
args:
|
|
26883
|
+
args: listArgs7,
|
|
26695
26884
|
run: async ({ args: args5 }) => {
|
|
26696
26885
|
const accountId = resolveAccountIdArg(args5);
|
|
26697
26886
|
try {
|
|
@@ -26729,7 +26918,7 @@ Examples:
|
|
|
26729
26918
|
// subcommand name using THIS command's arg defs, so without them `--account-id act_1`
|
|
26730
26919
|
// would read `act_1` as a subcommand. `default` (not `run`) dispatches the bare
|
|
26731
26920
|
// invocation — citty falls through to `run` after a subcommand, double-executing it.
|
|
26732
|
-
args:
|
|
26921
|
+
args: listArgs7,
|
|
26733
26922
|
default: "list"
|
|
26734
26923
|
});
|
|
26735
26924
|
|
|
@@ -26766,20 +26955,25 @@ Examples:
|
|
|
26766
26955
|
|
|
26767
26956
|
// src/commands/ads/meta/campaigns.ts
|
|
26768
26957
|
import { defineCommand as defineCommand62 } from "citty";
|
|
26769
|
-
var
|
|
26958
|
+
var listArgs8 = {
|
|
26770
26959
|
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
26771
26960
|
"effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
|
|
26772
26961
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter and return everything" },
|
|
26773
26962
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26774
26963
|
"no-cache": NO_CACHE_ARG,
|
|
26775
|
-
|
|
26964
|
+
...META_LIST_SHAPE_ARGS
|
|
26776
26965
|
};
|
|
26966
|
+
registerMetaListSchema(
|
|
26967
|
+
"campaigns",
|
|
26968
|
+
"List campaigns in a Meta ad account. Compact by default: id, name, effective_status, objective, budgets, bid_strategy, special_ad_categories, stop_time, created_time. ACTIVE only unless --all-statuses. Start here: baker ads meta campaigns --account-id act_123 --all-statuses --output md. --fields takes dotted paths for anything else; --full returns every field Meta sent.",
|
|
26969
|
+
listArgs8
|
|
26970
|
+
);
|
|
26777
26971
|
var listCommand7 = defineCommand62({
|
|
26778
26972
|
meta: {
|
|
26779
26973
|
name: "list",
|
|
26780
26974
|
description: "List campaigns for a Meta ad account. Runs by default when no subcommand is given. Example: baker ads meta campaigns --account-id act_123"
|
|
26781
26975
|
},
|
|
26782
|
-
args:
|
|
26976
|
+
args: listArgs8,
|
|
26783
26977
|
run: async ({ args: args5 }) => {
|
|
26784
26978
|
const accountId = resolveAccountIdArg(args5);
|
|
26785
26979
|
const effectiveStatus = resolveEffectiveStatus(args5);
|
|
@@ -26789,12 +26983,13 @@ var listCommand7 = defineCommand62({
|
|
|
26789
26983
|
if (args5.limit) params.limit = args5.limit;
|
|
26790
26984
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26791
26985
|
const data = await apiGet("/api/ads/meta/campaigns", params);
|
|
26986
|
+
const shaped = shapeMetaListRows("campaigns", data, args5);
|
|
26792
26987
|
const fmt = csvOrJson(args5);
|
|
26793
26988
|
if (fmt !== "json") {
|
|
26794
|
-
writeAdsOutput(
|
|
26989
|
+
writeAdsOutput(shaped.rows, fmt, shaped.fields);
|
|
26795
26990
|
return;
|
|
26796
26991
|
}
|
|
26797
|
-
writeAdsJson({ ok: true, data });
|
|
26992
|
+
writeAdsJson({ ok: true, data: shaped.rows });
|
|
26798
26993
|
} catch (err) {
|
|
26799
26994
|
handleMetaError(err);
|
|
26800
26995
|
}
|
|
@@ -26810,9 +27005,14 @@ apply only when the chat is published.
|
|
|
26810
27005
|
In Meta's API "campaign" is the top-of-tree object (what marketers usually call a campaign group).
|
|
26811
27006
|
The dashboard shows effective_status: ACTIVE, PAUSED, DELETED, ARCHIVED, IN_PROCESS, WITH_ISSUES, etc.
|
|
26812
27007
|
|
|
27008
|
+
Listing rows are compact: id, name, effective_status, objective, daily/lifetime budget, bid_strategy,
|
|
27009
|
+
special_ad_categories, stop_time, created_time. --fields takes dotted paths for anything else; --full returns every field.
|
|
27010
|
+
|
|
26813
27011
|
Examples:
|
|
26814
27012
|
baker ads meta campaigns --account-id act_123 # ACTIVE only
|
|
26815
27013
|
baker ads meta campaigns --account-id act_123 --all-statuses # everything
|
|
27014
|
+
baker ads meta campaigns --account-id act_123 --all-statuses --output md # as a table
|
|
27015
|
+
baker ads meta campaigns --account-id act_123 --fields id,name,budget_remaining,start_time
|
|
26816
27016
|
baker ads meta campaigns create --account-id act_123 --name "Q3" --objective OUTCOME_LEADS --daily-budget 50
|
|
26817
27017
|
baker ads meta campaigns pause 6123 --account-id act_123`
|
|
26818
27018
|
},
|
|
@@ -26829,13 +27029,13 @@ Examples:
|
|
|
26829
27029
|
// subcommand name using THIS command's arg defs, so without them `--account-id act_1`
|
|
26830
27030
|
// would read `act_1` as a subcommand. `default` (not `run`) dispatches the bare
|
|
26831
27031
|
// invocation — citty falls through to `run` after a subcommand, double-executing it.
|
|
26832
|
-
args:
|
|
27032
|
+
args: listArgs8,
|
|
26833
27033
|
default: "list"
|
|
26834
27034
|
});
|
|
26835
27035
|
|
|
26836
27036
|
// src/commands/ads/meta/creatives.ts
|
|
26837
27037
|
import { defineCommand as defineCommand63 } from "citty";
|
|
26838
|
-
var
|
|
27038
|
+
var listArgs9 = {
|
|
26839
27039
|
"account-id": { type: "string", description: "Meta ad account ID (required if no --creative-id)" },
|
|
26840
27040
|
"creative-id": { type: "string", description: "Single creative ID" },
|
|
26841
27041
|
limit: { type: "string", description: "Max rows (default 500)" },
|
|
@@ -26847,7 +27047,7 @@ var listCommand8 = defineCommand63({
|
|
|
26847
27047
|
name: "list",
|
|
26848
27048
|
description: "List ad creatives, or fetch one by ID. Runs by default when no subcommand is given. Example: baker ads meta creatives --account-id act_123"
|
|
26849
27049
|
},
|
|
26850
|
-
args:
|
|
27050
|
+
args: listArgs9,
|
|
26851
27051
|
run: async ({ args: args5 }) => {
|
|
26852
27052
|
try {
|
|
26853
27053
|
const params = {};
|
|
@@ -26893,7 +27093,7 @@ Examples:
|
|
|
26893
27093
|
// subcommand name using THIS command's arg defs, so without them `--account-id act_1`
|
|
26894
27094
|
// would read `act_1` as a subcommand. `default` (not `run`) dispatches the bare
|
|
26895
27095
|
// invocation — citty falls through to `run` after a subcommand, double-executing it.
|
|
26896
|
-
args:
|
|
27096
|
+
args: listArgs9,
|
|
26897
27097
|
default: "list"
|
|
26898
27098
|
});
|
|
26899
27099
|
|
|
@@ -26941,7 +27141,7 @@ registerSchema({
|
|
|
26941
27141
|
output: { type: "string", description: "json|csv", required: false, default: "json" }
|
|
26942
27142
|
}
|
|
26943
27143
|
});
|
|
26944
|
-
var
|
|
27144
|
+
var listArgs10 = {
|
|
26945
27145
|
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
26946
27146
|
full: { type: "boolean", description: "Include the raw rule JSON, retention and creation times" },
|
|
26947
27147
|
limit: { type: "string", description: "Max rows" },
|
|
@@ -26953,7 +27153,7 @@ var listCommand9 = defineCommand64({
|
|
|
26953
27153
|
name: "list",
|
|
26954
27154
|
description: "List custom conversions. Runs by default when no subcommand is given. Example: baker ads meta custom-conversions --account-id act_123"
|
|
26955
27155
|
},
|
|
26956
|
-
args:
|
|
27156
|
+
args: listArgs10,
|
|
26957
27157
|
run: async ({ args: args5 }) => {
|
|
26958
27158
|
const accountId = resolveAccountIdArg(args5);
|
|
26959
27159
|
try {
|
|
@@ -26991,7 +27191,7 @@ Examples:
|
|
|
26991
27191
|
// `args` stays even though the parent has no `run`: citty scans rawArgs for
|
|
26992
27192
|
// the subcommand name using THIS command's arg defs. `default` (not `run`)
|
|
26993
27193
|
// dispatches the bare invocation.
|
|
26994
|
-
args:
|
|
27194
|
+
args: listArgs10,
|
|
26995
27195
|
default: "list"
|
|
26996
27196
|
});
|
|
26997
27197
|
|
|
@@ -28703,7 +28903,7 @@ registerSchema({
|
|
|
28703
28903
|
output: { type: "string", description: "Format: json|csv|md", required: false, default: "json" }
|
|
28704
28904
|
}
|
|
28705
28905
|
});
|
|
28706
|
-
var
|
|
28906
|
+
var listArgs11 = {
|
|
28707
28907
|
"account-id": { type: "string", description: "X Ads account ID" },
|
|
28708
28908
|
"funding-instrument-ids": { type: "string", description: "CSV of funding instrument IDs" },
|
|
28709
28909
|
"campaign-ids": { type: "string", description: "CSV of campaign IDs" },
|
|
@@ -28773,7 +28973,7 @@ Examples:
|
|
|
28773
28973
|
},
|
|
28774
28974
|
// A parent must never declare `run`: citty falls through to it AFTER the
|
|
28775
28975
|
// matched subcommand, so every write would also trigger a full live list.
|
|
28776
|
-
args:
|
|
28976
|
+
args: listArgs11,
|
|
28777
28977
|
default: "list"
|
|
28778
28978
|
});
|
|
28779
28979
|
|
|
@@ -34544,9 +34744,9 @@ import { defineCommand as defineCommand105 } from "citty";
|
|
|
34544
34744
|
import { defineCommand as defineCommand100 } from "citty";
|
|
34545
34745
|
|
|
34546
34746
|
// src/commands/avatars/casting.ts
|
|
34547
|
-
var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate`, `baker studio animate` and `baker canvas scaffold-
|
|
34747
|
+
var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate`, `baker studio animate`, `baker canvas scaffold-ad`, `baker canvas scaffold-static-ad` and `baker canvas scaffold-video`. Do NOT hand-roll it by pasting `subjectDescription` into the prompt or by passing the sheet through `--reference` \u2014 both render something that merely resembles them. `--avatar` is also the ONLY thing that carries their ACCENT and delivery into a clip. An avatar has no pinned voice by design (ADR 0005): the video model renders the voice, and the region on their profile is the only thing steering it \u2014 without the flag the clip is performed flat, in whatever accent the model picks on its own. `baker canvas scaffold-video` casts only their FACE: a reproduced video keeps the source ad's accent direction, so never tell the user it speaks in theirs.";
|
|
34548
34748
|
var VERBATIM_RULE = "When you do write the description yourself \u2014 a hand-built canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
|
|
34549
|
-
var VIDEO_ROUTING = "In video, a photoreal presenter renders on `google/veo-3.1` (or `google/veo-3.1-fast`), never `bytedance/seedance-2.0` \u2014 it refuses photoreal human faces, AI-generated ones included.
|
|
34749
|
+
var VIDEO_ROUTING = "In video, a photoreal presenter renders on `google/veo-3.1` (or `google/veo-3.1-fast`), never `bytedance/seedance-2.0` \u2014 it refuses photoreal human faces, AI-generated ones included. Reproducing a video the user gave you: `baker canvas scaffold-video \u2026 --avatar <handle> --real-face`.";
|
|
34550
34750
|
function castingHints(avatar) {
|
|
34551
34751
|
if (avatar.status === "generating" && avatar.needsFace) {
|
|
34552
34752
|
return [
|
|
@@ -37026,6 +37226,21 @@ import { toCardinal as nwPl } from "n2words/pl-PL";
|
|
|
37026
37226
|
import { toCardinal as nwPt } from "n2words/pt-PT";
|
|
37027
37227
|
import { z as z41 } from "zod";
|
|
37028
37228
|
|
|
37229
|
+
// src/engine/scaffold/castAvatar.ts
|
|
37230
|
+
var SOURCE_IDENTITY_FIELDS = [
|
|
37231
|
+
"perceived_age_band",
|
|
37232
|
+
"perceived_gender_presentation",
|
|
37233
|
+
"perceived_ethnicity_or_skin_tone",
|
|
37234
|
+
"hair",
|
|
37235
|
+
"facial_hair",
|
|
37236
|
+
"build_body_type",
|
|
37237
|
+
"distinguishing_features"
|
|
37238
|
+
];
|
|
37239
|
+
function recastPersonEntry(entry, avatarDescription) {
|
|
37240
|
+
for (const field of SOURCE_IDENTITY_FIELDS) delete entry[field];
|
|
37241
|
+
entry.description = avatarDescription;
|
|
37242
|
+
}
|
|
37243
|
+
|
|
37029
37244
|
// src/engine/scaffold/lib/shoot-modes.ts
|
|
37030
37245
|
var SHOOT_MODES = [
|
|
37031
37246
|
"ugc_selfie",
|
|
@@ -37580,6 +37795,30 @@ var RecurringElement = z41.object({
|
|
|
37580
37795
|
appears_in: z41.array(AppearsItem).optional()
|
|
37581
37796
|
}).loose();
|
|
37582
37797
|
var RecurringElements = z41.array(RecurringElement);
|
|
37798
|
+
function castAvatarsOnElements(elementsInput, avatars) {
|
|
37799
|
+
const elements = RecurringElements.parse(elementsInput);
|
|
37800
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
37801
|
+
let next = 0;
|
|
37802
|
+
const cast = elements.map((el) => {
|
|
37803
|
+
if (el.type.toLowerCase() !== "person" || el.same_as) return el;
|
|
37804
|
+
const avatar = avatars[next];
|
|
37805
|
+
if (!avatar) return el;
|
|
37806
|
+
next += 1;
|
|
37807
|
+
if (el.label) byLabel.set(el.label, avatar);
|
|
37808
|
+
const description = avatar.subjectDescription?.trim();
|
|
37809
|
+
return { ...el, ...description ? { description } : {}, source_image: avatar.sheetUrl, source_is_sheet: true };
|
|
37810
|
+
});
|
|
37811
|
+
return cast.map((el) => {
|
|
37812
|
+
const avatar = el.same_as ? byLabel.get(el.same_as) : void 0;
|
|
37813
|
+
return avatar ? { ...el, source_image: avatar.sheetUrl, source_is_sheet: true } : el;
|
|
37814
|
+
});
|
|
37815
|
+
}
|
|
37816
|
+
function avatarsNotCast(elementsInput, handles) {
|
|
37817
|
+
const people = RecurringElements.parse(elementsInput).filter(
|
|
37818
|
+
(el) => el.type.toLowerCase() === "person" && !el.same_as
|
|
37819
|
+
).length;
|
|
37820
|
+
return handles.slice(people);
|
|
37821
|
+
}
|
|
37583
37822
|
function sanitizeId(raw, fallback) {
|
|
37584
37823
|
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
37585
37824
|
return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
|
|
@@ -37632,6 +37871,12 @@ function genAspectsFor(videoModel) {
|
|
|
37632
37871
|
if (/kling/.test(videoModel)) return ["1:1", "16:9", "9:16"];
|
|
37633
37872
|
return SEEDANCE_GEN_ASPECTS;
|
|
37634
37873
|
}
|
|
37874
|
+
function stampCastEntry(entry, el, label) {
|
|
37875
|
+
entry.reference_image = label;
|
|
37876
|
+
if (el.source_is_sheet && !el.same_as && el.type.toLowerCase() === "person" && el.description) {
|
|
37877
|
+
recastPersonEntry(entry, el.description);
|
|
37878
|
+
}
|
|
37879
|
+
}
|
|
37635
37880
|
function annotateBlueprintWithElements(blueprintInput, elementsInput) {
|
|
37636
37881
|
if (!blueprintInput || typeof blueprintInput !== "object") return blueprintInput;
|
|
37637
37882
|
const elements = RecurringElements.parse(elementsInput);
|
|
@@ -37641,10 +37886,8 @@ function annotateBlueprintWithElements(blueprintInput, elementsInput) {
|
|
|
37641
37886
|
for (const { el, label } of assignElementLabels(elements)) {
|
|
37642
37887
|
const scenes = [...presenceOf(el).keys()].sort((a, b) => a - b);
|
|
37643
37888
|
summary.push({ reference_image: label, type: el.type, description: el.description ?? null, scenes });
|
|
37644
|
-
|
|
37645
|
-
|
|
37646
|
-
if (entry && typeof entry === "object") entry.reference_image = label;
|
|
37647
|
-
}
|
|
37889
|
+
const entry = el.cast_id && Array.isArray(cast) ? cast.find((c) => c?.id === el.cast_id) : void 0;
|
|
37890
|
+
if (entry && typeof entry === "object") stampCastEntry(entry, el, label);
|
|
37648
37891
|
}
|
|
37649
37892
|
clone.reference_elements = summary;
|
|
37650
37893
|
return clone;
|
|
@@ -37718,8 +37961,11 @@ function todoPath(el, label) {
|
|
|
37718
37961
|
const desc = el.description ? ` \u2014 ${el.description}` : "";
|
|
37719
37962
|
const expr = el.expression ? `, with a ${el.expression} expression` : "";
|
|
37720
37963
|
const t = el.type.toLowerCase();
|
|
37721
|
-
const fresh = t === "
|
|
37964
|
+
const fresh = t === "animal" || t === "location" ? " [SOURCE FRESH \u2014 a DIFFERENT animal/set than the original ad; do not reuse the source's]" : "";
|
|
37722
37965
|
const same = el.same_as ? ` [SAME INDIVIDUAL as ${el.same_as} \u2014 a different wardrobe/look of the same person; reuse that cast person, change only the outfit]` : "";
|
|
37966
|
+
if (t === "person") {
|
|
37967
|
+
return `[TODO: cast a company Avatar as ${label} (person)${desc}${expr}${same} \u2014 re-run scaffold-video with \`--avatar <handle>\` (\`baker avatars list\`; \`baker avatars create\` invents a new face). NEVER a photo of a real person found on the web or taken from the source ad: the image model reproduces that face]`;
|
|
37968
|
+
}
|
|
37723
37969
|
return `[TODO: drop one real source image for ${label} (${el.type})${desc}${expr} \u2014 reused across every frame it appears in${fresh}${same}; use a path relative to this canvas file, never an absolute path]`;
|
|
37724
37970
|
}
|
|
37725
37971
|
function buildElementSlots(elements) {
|
|
@@ -37970,7 +38216,7 @@ function buildFrameRef(edge, url, framePrompt, present2, ctx, nodes) {
|
|
|
37970
38216
|
const t = s.type.toLowerCase();
|
|
37971
38217
|
return t === "person" || t === "animal";
|
|
37972
38218
|
});
|
|
37973
|
-
const useOriginalAnchor = Boolean(url) && castSlots.length === 0;
|
|
38219
|
+
const useOriginalAnchor = Boolean(url) && castSlots.length === 0 && !ctx.sourceShowsPeople;
|
|
37974
38220
|
const hasOriginal = useOriginalAnchor;
|
|
37975
38221
|
const originalRef = useOriginalAnchor && url ? ingestFrameRef(url, edge, ctx, nodes) : void 0;
|
|
37976
38222
|
const presentOrdered = orderSlotsByIdentity(present2);
|
|
@@ -38436,6 +38682,7 @@ function buildCompositeScene(layout, regions, comp, scene, i, present2, mode, na
|
|
|
38436
38682
|
sceneIndex: i,
|
|
38437
38683
|
genAr: opts.genAr,
|
|
38438
38684
|
reuse: opts.reuse,
|
|
38685
|
+
sourceShowsPeople: opts.sourceShowsPeople,
|
|
38439
38686
|
imageModel: opts.imageModel,
|
|
38440
38687
|
shootMode: mode,
|
|
38441
38688
|
sceneStyle: sceneStyleOf(scene),
|
|
@@ -38711,6 +38958,9 @@ function onCameraDialogue(blueprint) {
|
|
|
38711
38958
|
const mode = blueprint.global?.voiceover?.mode;
|
|
38712
38959
|
return mode !== "voiceover" && mode !== "none";
|
|
38713
38960
|
}
|
|
38961
|
+
function sourceShowsPeople(blueprint, slots) {
|
|
38962
|
+
return (blueprint.global?.cast ?? []).length > 0 || slots.some((s) => s.type.toLowerCase() === "person");
|
|
38963
|
+
}
|
|
38714
38964
|
var castIdSet = (blueprint) => new Set((blueprint.global?.cast ?? []).map((c) => c.id).filter((id) => Boolean(id)));
|
|
38715
38965
|
var LANGUAGE_WORDS = [
|
|
38716
38966
|
[/\b(french|fran[çc]ais|francaise)\b/, "french"],
|
|
@@ -39026,6 +39276,7 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
|
|
|
39026
39276
|
sceneIndex: anchor,
|
|
39027
39277
|
genAr: env.genAr,
|
|
39028
39278
|
reuse: env.reuse,
|
|
39279
|
+
sourceShowsPeople: env.sourceShowsPeople,
|
|
39029
39280
|
imageModel: env.opts.imageModel,
|
|
39030
39281
|
shootMode: mode,
|
|
39031
39282
|
sceneStyle: sceneStyleOf(anchorScene),
|
|
@@ -39220,6 +39471,7 @@ function emitCompositeInTimeline(composite, scene, i, isLast, env, canonical2, e
|
|
|
39220
39471
|
outAr: env.outAr,
|
|
39221
39472
|
genAr: env.genAr,
|
|
39222
39473
|
reuse: env.reuse,
|
|
39474
|
+
sourceShowsPeople: env.sourceShowsPeople,
|
|
39223
39475
|
imageModel: env.opts.imageModel,
|
|
39224
39476
|
videoModel: env.opts.videoModel,
|
|
39225
39477
|
resolution: env.opts.resolution,
|
|
@@ -39316,6 +39568,7 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
|
|
|
39316
39568
|
sceneIndex: i,
|
|
39317
39569
|
genAr: env.genAr,
|
|
39318
39570
|
reuse: env.reuse,
|
|
39571
|
+
sourceShowsPeople: env.sourceShowsPeople,
|
|
39319
39572
|
imageModel: env.opts.imageModel,
|
|
39320
39573
|
shootMode: mode,
|
|
39321
39574
|
sceneStyle: sceneStyleOf(scene),
|
|
@@ -39416,6 +39669,7 @@ function buildTimeline(blueprint, slots, opts, nodes) {
|
|
|
39416
39669
|
outAr: aspect.outAr,
|
|
39417
39670
|
genAr: aspect.genAr,
|
|
39418
39671
|
reuse,
|
|
39672
|
+
sourceShowsPeople: sourceShowsPeople(blueprint, slots),
|
|
39419
39673
|
cameraOn: onCameraDialogue(blueprint),
|
|
39420
39674
|
casts: castIdSet(blueprint),
|
|
39421
39675
|
ingestCache: /* @__PURE__ */ new Map(),
|
|
@@ -39946,11 +40200,7 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
|
|
|
39946
40200
|
nodes.push({
|
|
39947
40201
|
id: slot.id,
|
|
39948
40202
|
type: "ingest",
|
|
39949
|
-
params: {
|
|
39950
|
-
source: "path",
|
|
39951
|
-
path: supplied || todoPath(element, slot.label),
|
|
39952
|
-
expect: "image"
|
|
39953
|
-
}
|
|
40203
|
+
params: /^https?:\/\//i.test(supplied) ? { source: "url", url: supplied, expect: "image" } : { source: "path", path: supplied || todoPath(element, slot.label), expect: "image" }
|
|
39954
40204
|
});
|
|
39955
40205
|
});
|
|
39956
40206
|
buildElementSheets(slots, nodes, opts.sheetModel);
|
|
@@ -40204,7 +40454,7 @@ var VIDEO_GUIDE = [
|
|
|
40204
40454
|
"0. RE-CRAFT THE SCRIPT FIRST (don't clone). This reference already won in-market, but copying a video is much harder than a static: the hook is targeting and may not transfer, and the message must become TRUE for our brand. Work the `metadata.todo.script_recraft` checklist \u2014 for each scene judge its role (hook/body/CTA), decide keep/cut/reorder/replace, and re-author every line for OUR customer's pain + OUR offer. See `references/script-craft.md` (hook/body/CTA framework) and the `meta-ads-playbook` skill. Most of the work lives here.",
|
|
40205
40455
|
"1. Edit each frame's prompt IN PLACE. Every `s<i>_start` keyframe node has its OWN self-contained `params.prompt` (the FRAME DESCRIPTION) \u2014 editing one changes only that frame. Rewrite the cast, product, claims, palette into the ad you want. The frame is RECAST to the el_* reference images you drop (the source ad's people are never reused), so describe pose/action/framing here and let the references carry identity.",
|
|
40206
40456
|
"1b. STORYBOARD FIRST \u2014 align the look on the cheap stills before clips bill. Each scene's keyframe IS your storyboard; `metadata.video.motion_board` lays out each scene's frame, time window, spoken line, and the graphics scheduled in it. Lock the keyframes + check each graphic lands on its spoken beat, THEN run the clips (images are cheap, videos aren't; the cache re-bills only what you change). See references/video-reproduce.md.",
|
|
40207
|
-
"2.
|
|
40457
|
+
"2. Fill each `el_*` ingest `[TODO]` path. A PERSON is a company Avatar's identity sheet, cast by re-scaffolding with `--avatar <handle>` (a sheet URL pasted by hand would be sheeted again) \u2014 never a photo of a real person, whose face the image model would reproduce; every other element is ONE real source image. Each is reused across every frame it appears in, so the same identity stays consistent. `baker canvas run` REFUSES to start until every `[TODO]` slot holds a real source \u2014 so this is mandatory, not optional.",
|
|
40208
40458
|
"3. Talking heads are NATIVE: a scene with one on-camera speaker is voiced by the video model itself (the line goes in the clip's prompt; models with a `generate_audio` toggle get it set, and the default model always renders audio), so lips and voice are generated together \u2014 no separate tts, no post-hoc lip-sync. Edit the line in the scene's `s<i>_clip` prompt to re-author the words TRUE for your brand. Off-camera narration scenes still use a sequenced `tts` per turn.",
|
|
40209
40459
|
"4. Voice consistency: every native talking clip's audio is re-voiced to ONE brand voice via `audio_voice_convert` (timing preserved \u2192 lips stay matched). Confirm the `voice_select` casting (one per speaker) \u2014 its `voice_id` is the brand voice; set its gender/language so the voice matches the creator.",
|
|
40210
40460
|
"5. Overlays are REAL HTML you paint. Open `video-overlay-composition/index.html`: the reference's overlays are seeded inside `#overlay-root` as plain elements (text + a `.pos-*` class + `data-start`/`data-dur`). Restyle the CSS freely \u2014 build lower-thirds, a ticker, whatever the look needs \u2014 and replace a logo placeholder with a real `<img>` you source (`baker images icon/sticker/gif/logo`) and drop in that dir. The runtime only shows/hides by timestamp; it makes no styling decisions. Drop `brand-bold.otf` / `brand-regular.otf` there for on-brand type.",
|
|
@@ -40272,7 +40522,7 @@ function buildVideoTodo(report, overlayCount, floatingCount, opts, blueprint) {
|
|
|
40272
40522
|
review_storyboard_before_clips: "STORYBOARD FIRST. Each scene's keyframe (s<i>_start) IS your storyboard \u2014 align the LOOK on it before clips bill. Images are cheap, videos aren't, and the content-addressed cache re-bills only changed nodes, so iterate prompts here first and let the storyboard lock before the video_generate clips run. `metadata.video.motion_board` lists each scene's keyframe, window, spoken line, and the graphics scheduled in it \u2014 walk it scene by scene.",
|
|
40273
40523
|
motion_board: "`metadata.video.motion_board` maps which overlay/graphic fires on which spoken beat (per scene: window_s, spoken line, each graphic's at_s). Review it so every graphic lands on the word it punctuates \u2014 don't let an overlay drift off its beat. Adjust an overlay's data-start in video-overlay-composition/index.html (or appears_at_s in prompt.json) to retime it.",
|
|
40274
40524
|
assets_required: "MANDATORY: drop a real image at every el_* [TODO] ingest before running \u2014 `baker canvas run` refuses to start (and bills nothing) until each placeholder holds a real source.",
|
|
40275
|
-
sourcing: 'Resolve each el_* [TODO]: (
|
|
40525
|
+
sourcing: 'Resolve each el_* [TODO]: a PERSON is a company Avatar \u2014 pass `--avatar <handle>` to scaffold-video (`baker avatars list`; `baker avatars create` for a new invented face) \u2014 re-scaffold rather than paste a sheet URL at the slot, which would be sheeted again; never a photo of a real person from the web or the source ad, because the image model reproduces that face. Everything else: the client\'s OWN assets first \u2014 `baker images library "<subject>"` and `baker images logo <domain>` for the brand mark; otherwise a FRESH real reference \u2014 `baker images find "<subject>" --sources pinterest` (photo-real/candid, best for pets, sets and objects) or generate one with the image model. el_location and animals are [SOURCE FRESH]: a DIFFERENT set/animal than the original ad.',
|
|
40276
40526
|
recurring_elements_to_supply: report.elements,
|
|
40277
40527
|
text_strategy: "Decide per ad: text is either baked by the generated creative OR painted via the overlay HTML \u2014 not both. Default here is clean text-free frames + the HTML overlay layer (video-overlay-composition/index.html) as the single text source, which you fully control.",
|
|
40278
40528
|
timeline: "Automatic: each clip is generated at >= its scene length then trimmed back to the real scene duration, so the concatenated picture stays on the same timeline as the audio. You don't manage it.",
|
|
@@ -40381,7 +40631,8 @@ function videoReport(input, elementsInput) {
|
|
|
40381
40631
|
type: el.type,
|
|
40382
40632
|
description: el.description ?? null,
|
|
40383
40633
|
scenes: [...presenceOf(el).keys()].sort((a, b) => a - b),
|
|
40384
|
-
asset_todo: todoPath(el, label)
|
|
40634
|
+
asset_todo: el.source_image?.trim() ? null : todoPath(el, label),
|
|
40635
|
+
...el.source_image?.trim() ? { source_image: el.source_image.trim() } : {}
|
|
40385
40636
|
})),
|
|
40386
40637
|
dialogue,
|
|
40387
40638
|
sfx_count: sfxCount,
|
|
@@ -42259,7 +42510,6 @@ import { z as z43 } from "zod";
|
|
|
42259
42510
|
var GEN_ASPECT_RATIOS = /* @__PURE__ */ new Set(["1:1", "4:5", "9:16", "16:9", "4:3", "3:4", "2:3", "3:2", "21:9"]);
|
|
42260
42511
|
var DEFAULT_ASPECT_RATIO = "9:16";
|
|
42261
42512
|
var SHEET_SUBJECT_TYPE2 = {
|
|
42262
|
-
person: "person",
|
|
42263
42513
|
animal: "character"
|
|
42264
42514
|
};
|
|
42265
42515
|
var ACTOR_SHEET_MODEL2 = "google/gemini-3-pro-image-preview";
|
|
@@ -42269,7 +42519,8 @@ var ADAPT_IMAGE_SIZE = "2K";
|
|
|
42269
42519
|
var ADAPT_GUIDANCE = "Keep the headline, logo, CTA, and hero subject fully visible in every ratio. Reproduce every text string verbatim \u2014 no dropped, added, or altered characters \u2014 and preserve the exact brand-color treatment (e.g. a black\u2192red word pivot), never flattening it.";
|
|
42270
42520
|
var Blueprint = z43.object({
|
|
42271
42521
|
meta: z43.object({ estimated_aspect_ratio: z43.string().optional() }).loose().optional(),
|
|
42272
|
-
text_content: z43.array(z43.object({ text: z43.string().optional() }).loose()).optional()
|
|
42522
|
+
text_content: z43.array(z43.object({ text: z43.string().optional() }).loose()).optional(),
|
|
42523
|
+
people: z43.array(z43.unknown()).optional()
|
|
42273
42524
|
}).loose();
|
|
42274
42525
|
var ElementLocator = z43.object({
|
|
42275
42526
|
collection: z43.enum(["subjects", "people", "brands_logos"]),
|
|
@@ -42285,6 +42536,24 @@ var MainElement = z43.object({
|
|
|
42285
42536
|
locator: ElementLocator.optional()
|
|
42286
42537
|
}).loose();
|
|
42287
42538
|
var MainElements = z43.array(MainElement);
|
|
42539
|
+
var isPerson = (el) => el.type.toLowerCase() === "person";
|
|
42540
|
+
function castAvatarsOnStaticElements(elementsInput, avatars) {
|
|
42541
|
+
let next = 0;
|
|
42542
|
+
return MainElements.parse(elementsInput).map((el) => {
|
|
42543
|
+
const avatar = isPerson(el) ? avatars[next] : void 0;
|
|
42544
|
+
if (!avatar) return el;
|
|
42545
|
+
next += 1;
|
|
42546
|
+
const description = avatar.subjectDescription?.trim();
|
|
42547
|
+
return { ...el, ...description ? { description } : {}, source_image: avatar.sheetUrl };
|
|
42548
|
+
});
|
|
42549
|
+
}
|
|
42550
|
+
function staticAvatarsNotCast(elementsInput, handles) {
|
|
42551
|
+
return handles.slice(MainElements.parse(elementsInput).filter(isPerson).length);
|
|
42552
|
+
}
|
|
42553
|
+
function suppliedImage(el) {
|
|
42554
|
+
const url = typeof el.source_image === "string" ? el.source_image.trim() : "";
|
|
42555
|
+
return /^https?:\/\//i.test(url) ? url : void 0;
|
|
42556
|
+
}
|
|
42288
42557
|
function sanitizeId2(raw, fallback) {
|
|
42289
42558
|
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
42290
42559
|
return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
|
|
@@ -42311,7 +42580,9 @@ function annotateBlueprintWithLabels(blueprintInput, elementsInput) {
|
|
|
42311
42580
|
const collection = clone[loc.collection];
|
|
42312
42581
|
const entry = Array.isArray(collection) ? collection[loc.index] : void 0;
|
|
42313
42582
|
if (entry && typeof entry === "object") {
|
|
42314
|
-
|
|
42583
|
+
const record = entry;
|
|
42584
|
+
record.reference_image = label;
|
|
42585
|
+
if (suppliedImage(el) && isPerson(el) && el.description) recastPersonEntry(record, el.description);
|
|
42315
42586
|
}
|
|
42316
42587
|
}
|
|
42317
42588
|
return clone;
|
|
@@ -42325,6 +42596,7 @@ function roleForType2(type) {
|
|
|
42325
42596
|
case "product":
|
|
42326
42597
|
return "the showcased product; keep this exact product identity. Ignore any caption text printed on this reference.";
|
|
42327
42598
|
case "person":
|
|
42599
|
+
return "the emotional hero \u2014 the brand's cast person: a company Avatar's identity sheet, a multi-view turnaround of ONE person the brand may show. Keep this exact face, hair and build, and render them once in the pose, gaze, wardrobe and expression the blueprint specifies \u2014 never as a grid. Ignore the cell labels printed on the sheet.";
|
|
42328
42600
|
case "animal":
|
|
42329
42601
|
return "the emotional hero \u2014 the face/muzzle/eyes the viewer locks onto and exactly where AI artifacts (malformed mouths, teeth, foam, extra fingers) betray a fake. It MUST be grounded in this real reference, never invented from prose. Keep this identity AND the expression the blueprint specifies. Ignore any caption text printed on this reference.";
|
|
42330
42602
|
default:
|
|
@@ -42335,7 +42607,10 @@ function todoPath2(el, label) {
|
|
|
42335
42607
|
const desc = el.description ? ` \u2014 ${el.description}` : "";
|
|
42336
42608
|
const expr = el.expression ? `, with a ${el.expression} expression` : "";
|
|
42337
42609
|
const t = el.type.toLowerCase();
|
|
42338
|
-
|
|
42610
|
+
if (t === "person") {
|
|
42611
|
+
return `[TODO: cast a company Avatar as ${label} (person)${desc}${expr} \u2014 REQUIRED: this is the emotional hero and the render keeps this face, so re-run scaffold-static-ad with \`--avatar <handle>\` (\`baker avatars list\`; \`baker avatars create\` invents a new face); never a found photo of a real person (web search, the source ad), who never agreed to appear in this ad]`;
|
|
42612
|
+
}
|
|
42613
|
+
const lock = t === "animal" ? " \u2014 REQUIRED: this is the emotional hero; ground it in a real reference (image-library / video-library / Pinterest), do not delete this slot and free-generate" : "";
|
|
42339
42614
|
return `[TODO: drop a real image for ${label} (${el.type})${desc}${expr}${lock} \u2014 replace with a path relative to this canvas file, never an absolute path]`;
|
|
42340
42615
|
}
|
|
42341
42616
|
function sheetSubjectDescription(el) {
|
|
@@ -42375,6 +42650,9 @@ function textFidelityDirective(blueprint, hasFont) {
|
|
|
42375
42650
|
"Keep all type crisp, legible, and large enough to read cleanly; no garbled, warped, or duplicated characters, and no unreadable microtext."
|
|
42376
42651
|
].join("\n");
|
|
42377
42652
|
}
|
|
42653
|
+
function showsPerson(blueprint, elements) {
|
|
42654
|
+
return (blueprint.people?.length ?? 0) > 0 || elements.some(isPerson);
|
|
42655
|
+
}
|
|
42378
42656
|
function scaffoldStaticAd(input, elementsInput, opts) {
|
|
42379
42657
|
const blueprint = Blueprint.parse(input);
|
|
42380
42658
|
const elements = MainElements.parse(elementsInput);
|
|
@@ -42397,10 +42675,11 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
42397
42675
|
let id = sanitizeId2(`el_${label}`, `el_${i}`);
|
|
42398
42676
|
while (usedIds.has(id)) id = `${id}_${i}`;
|
|
42399
42677
|
usedIds.add(id);
|
|
42678
|
+
const supplied = suppliedImage(el);
|
|
42400
42679
|
nodes.push({
|
|
42401
42680
|
id,
|
|
42402
42681
|
type: "ingest",
|
|
42403
|
-
params: { source: "path", path: todoPath2(el, label), expect: "image" }
|
|
42682
|
+
params: supplied ? { source: "url", url: supplied, expect: "image" } : { source: "path", path: todoPath2(el, label), expect: "image" }
|
|
42404
42683
|
});
|
|
42405
42684
|
let ref = `$ref:${id}.asset`;
|
|
42406
42685
|
const subjectType = includeActorSheets ? SHEET_SUBJECT_TYPE2[el.type.toLowerCase()] : void 0;
|
|
@@ -42440,15 +42719,18 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
42440
42719
|
params: { text: specimenText(blueprint), font_size: 72, line_height: 1.2, max_width: 1600 }
|
|
42441
42720
|
});
|
|
42442
42721
|
}
|
|
42722
|
+
const originalAsReference = !showsPerson(blueprint, elements);
|
|
42443
42723
|
const reference = [
|
|
42444
42724
|
...elementSlots.map((s) => s.ref),
|
|
42445
42725
|
...hasFont ? ["$ref:type_ref.image"] : [],
|
|
42446
|
-
"$ref:original.asset"
|
|
42726
|
+
...originalAsReference ? ["$ref:original.asset"] : []
|
|
42447
42727
|
];
|
|
42448
42728
|
const legend = [
|
|
42449
42729
|
...elementSlots.map((s) => `- ${s.label} \u2014 ${roleForType2(s.type)}`),
|
|
42450
42730
|
...hasFont ? ["- TYPE SPECIMEN \u2014 render every text string in this exact typeface; ignore its color and layout."] : [],
|
|
42451
|
-
|
|
42731
|
+
...originalAsReference ? [
|
|
42732
|
+
"- ORIGINAL \u2014 use ONLY for composition, layout, and proportions. IGNORE its text, its logo, its brand name, and its colors entirely \u2014 they belong to a different brand."
|
|
42733
|
+
] : []
|
|
42452
42734
|
].join("\n");
|
|
42453
42735
|
const exclusionsLast = imageProfileFor(opts.genModel)?.constraintPlacement === "last";
|
|
42454
42736
|
const exclusionsBlock = "Exclusions: no invented badges, icons, or seals; render exactly the subjects the blueprint names and no others; no competitor branding; no watermark.";
|
|
@@ -42457,9 +42739,11 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
42457
42739
|
"",
|
|
42458
42740
|
"Commit to ONE dominant capture/render treatment for the whole ad \u2014 do not stack conflicting camera, lens, lighting, or style specs; take the look from the blueprint.",
|
|
42459
42741
|
"",
|
|
42460
|
-
|
|
42461
|
-
|
|
42462
|
-
|
|
42742
|
+
...reference.length > 0 ? [
|
|
42743
|
+
"REFERENCE IMAGES (in the order provided) \u2014 the blueprint refers to these by their reference_image label:",
|
|
42744
|
+
legend,
|
|
42745
|
+
""
|
|
42746
|
+
] : [],
|
|
42463
42747
|
"TEXT \u2014 RENDER THE COPY EXACTLY:",
|
|
42464
42748
|
textFidelityDirective(blueprint, hasFont),
|
|
42465
42749
|
"",
|
|
@@ -42475,7 +42759,9 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
42475
42759
|
nodes.push({
|
|
42476
42760
|
id: "gen",
|
|
42477
42761
|
type: "image_generate",
|
|
42478
|
-
|
|
42762
|
+
// An empty list fails the node's schema (`min(1)`): with nothing to ground on
|
|
42763
|
+
// (no element, no font, a person in the original) the blueprint renders alone.
|
|
42764
|
+
inputs: { ...reference.length > 0 ? { reference } : {}, target_blueprint: "$ref:prompt.asset" },
|
|
42479
42765
|
params: {
|
|
42480
42766
|
model: opts.genModel,
|
|
42481
42767
|
// The hero renders at the closest ratio its model actually accepts; the
|
|
@@ -42512,7 +42798,7 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
42512
42798
|
schema: "baker-canvas/1",
|
|
42513
42799
|
metadata: {
|
|
42514
42800
|
name: "static ad reproduction",
|
|
42515
|
-
description: "Scaffolded by `baker canvas scaffold-static-ad`. Edit `prompt.json` (the blueprint generated from the source image) into the ad you want, drop a real asset at each [TODO] ingest path (logo/product/subject/badge + brand font), then `baker canvas run`. The original is wired for composition only."
|
|
42801
|
+
description: "Scaffolded by `baker canvas scaffold-static-ad`. Edit `prompt.json` (the blueprint generated from the source image) into the ad you want, drop a real asset at each [TODO] ingest path (logo/product/subject/badge + brand font), then `baker canvas run`. The original is wired for composition only, and left out of the render when it shows a person, whose face the generator would copy."
|
|
42516
42802
|
},
|
|
42517
42803
|
nodes,
|
|
42518
42804
|
output: fanOut ? { node: "adapt", output: "images" } : { node: "gen", output: "images" }
|
|
@@ -42528,7 +42814,7 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
42528
42814
|
label,
|
|
42529
42815
|
type: el.type,
|
|
42530
42816
|
description: el.description ?? null,
|
|
42531
|
-
asset_todo: todoPath2(el, label)
|
|
42817
|
+
asset_todo: suppliedImage(el) ? "none \u2014 cast from a company Avatar's identity sheet" : todoPath2(el, label)
|
|
42532
42818
|
})),
|
|
42533
42819
|
includes_font: opts.includeFont !== false,
|
|
42534
42820
|
actor_sheets: includeActorSheets ? assignElementLabels2(elements).filter(({ el }) => SHEET_SUBJECT_TYPE2[el.type.toLowerCase()]).map(({ label }) => label) : [],
|
|
@@ -42751,6 +43037,35 @@ var syncDefinitionCommand = defineCommand113({
|
|
|
42751
43037
|
}
|
|
42752
43038
|
});
|
|
42753
43039
|
|
|
43040
|
+
// src/commands/avatars/resolveCast.ts
|
|
43041
|
+
async function resolveCastAvatar(handle) {
|
|
43042
|
+
try {
|
|
43043
|
+
const avatar = await readAvatars("/api/avatars/get", { handle });
|
|
43044
|
+
if (avatar.status && avatar.status !== "ready") {
|
|
43045
|
+
return {
|
|
43046
|
+
error: `Avatar \`${handle}\` is ${avatar.status}, not ready \u2014 \`baker avatars get ${handle} --wait\` returns the moment it is.`
|
|
43047
|
+
};
|
|
43048
|
+
}
|
|
43049
|
+
if (!avatar.sheetUrl)
|
|
43050
|
+
return { error: `Avatar \`${handle}\` has no identity sheet yet, so the ad cannot ground on their face.` };
|
|
43051
|
+
return { sheetUrl: avatar.sheetUrl, subjectDescription: avatar.subjectDescription };
|
|
43052
|
+
} catch (e) {
|
|
43053
|
+
return {
|
|
43054
|
+
error: `Could not read avatar \`${handle}\`: ${e instanceof Error ? e.message : String(e)}. \`baker avatars list\` shows who there is.`
|
|
43055
|
+
};
|
|
43056
|
+
}
|
|
43057
|
+
}
|
|
43058
|
+
async function resolveCastAvatars(flag) {
|
|
43059
|
+
const handles = String(flag ?? "").split(",").map((h) => h.trim()).filter(Boolean);
|
|
43060
|
+
const avatars = [];
|
|
43061
|
+
for (const handle of handles) {
|
|
43062
|
+
const resolved = await resolveCastAvatar(handle);
|
|
43063
|
+
if ("error" in resolved) return resolved;
|
|
43064
|
+
avatars.push(resolved);
|
|
43065
|
+
}
|
|
43066
|
+
return { handles, avatars };
|
|
43067
|
+
}
|
|
43068
|
+
|
|
42754
43069
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
42755
43070
|
async function fileExists(target2) {
|
|
42756
43071
|
try {
|
|
@@ -42854,12 +43169,12 @@ function fail3(code, message) {
|
|
|
42854
43169
|
process.exit(2);
|
|
42855
43170
|
}
|
|
42856
43171
|
function resolveModels(args5) {
|
|
42857
|
-
const
|
|
43172
|
+
const pick2 = (flag, kind, fallback) => args5[flag] ? String(args5[flag]) : resolveModel(kind, fallback);
|
|
42858
43173
|
return {
|
|
42859
|
-
describeModel:
|
|
42860
|
-
selectModel:
|
|
42861
|
-
layoutModel:
|
|
42862
|
-
genModel:
|
|
43174
|
+
describeModel: pick2("describe-model", "image_describe", "~google/gemini-pro-latest"),
|
|
43175
|
+
selectModel: pick2("select-model", "text_generate", "~google/gemini-flash-latest"),
|
|
43176
|
+
layoutModel: pick2("layout-model", "text_generate", "~google/gemini-flash-latest"),
|
|
43177
|
+
genModel: pick2("gen-model", "image_generate", "openai/gpt-image-2")
|
|
42863
43178
|
};
|
|
42864
43179
|
}
|
|
42865
43180
|
var PLATFORM_PLACEMENTS = {
|
|
@@ -42954,7 +43269,7 @@ async function runVisionPasses(canvas) {
|
|
|
42954
43269
|
var scaffoldStaticAdCommand = defineCommand114({
|
|
42955
43270
|
meta: {
|
|
42956
43271
|
name: "scaffold-static-ad",
|
|
42957
|
-
description: "Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes \u2014 image_describe (the blueprint, baked to prompt.json as the editable 'prompt'), an AI selection of the image's MAIN identity elements, and a structured global-layout pass (the column/row grid with per-region bounds and text sizes) \u2014 then scaffolds a canvas that wires one [TODO] ingest slot per element (logo/product/subject/badge + brand font) into image_generate. By default it also fans the hero out to the platform's placement ratios (via image_aspect_adapt \u2014 the hero ratio is free, the rest are billed AI recompositions); pass --placements none for a single base ad, or a preset to pick the set. Edit prompt.json and drop the real assets, then `baker canvas run` it."
|
|
43272
|
+
description: "Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes \u2014 image_describe (the blueprint, baked to prompt.json as the editable 'prompt'), an AI selection of the image's MAIN identity elements, and a structured global-layout pass (the column/row grid with per-region bounds and text sizes) \u2014 then scaffolds a canvas that wires one [TODO] ingest slot per element (logo/product/subject/badge + brand font) into image_generate. By default it also fans the hero out to the platform's placement ratios (via image_aspect_adapt \u2014 the hero ratio is free, the rest are billed AI recompositions); pass --placements none for a single base ad, or a preset to pick the set. Edit prompt.json and drop the real assets, then `baker canvas run` it. 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."
|
|
42958
43273
|
},
|
|
42959
43274
|
args: {
|
|
42960
43275
|
file: { type: "positional", required: true, description: "Path or http(s) URL to the source/inspiration image" },
|
|
@@ -42982,7 +43297,11 @@ var scaffoldStaticAdCommand = defineCommand114({
|
|
|
42982
43297
|
"skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" },
|
|
42983
43298
|
"skip-actor-sheets": {
|
|
42984
43299
|
type: "boolean",
|
|
42985
|
-
description: "Ground each
|
|
43300
|
+
description: "Ground each animal hero on its lone dropped photo instead of a generated multi-view sheet (a person hero always grounds on the company Avatar sheet cast with --avatar)"
|
|
43301
|
+
},
|
|
43302
|
+
avatar: {
|
|
43303
|
+
type: "string",
|
|
43304
|
+
description: "Cast company avatars as the ad's people \u2014 pass this whenever the image has a person in it. One handle, or several comma-separated in the order the people were detected (`--avatar marta` / `--avatar marta,joan`). Each person slot grounds on that avatar's identity sheet and prompt.json describes them in the avatar's own words, so the ad shows the company's cast instead of the source ad's. `baker avatars list` shows who there is; checked before anything is billed."
|
|
42986
43305
|
}
|
|
42987
43306
|
},
|
|
42988
43307
|
async run({ args: args5 }) {
|
|
@@ -43002,6 +43321,8 @@ var scaffoldStaticAdCommand = defineCommand114({
|
|
|
43002
43321
|
);
|
|
43003
43322
|
await mkdir6(path21.dirname(outPath), { recursive: true });
|
|
43004
43323
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args5);
|
|
43324
|
+
const cast = await resolveCastAvatars(args5.avatar);
|
|
43325
|
+
if ("error" in cast) return fail3("avatar", cast.error);
|
|
43005
43326
|
let durableSourceUrl;
|
|
43006
43327
|
if (referencesDir) {
|
|
43007
43328
|
const client = buildBackendClient();
|
|
@@ -43021,7 +43342,10 @@ var scaffoldStaticAdCommand = defineCommand114({
|
|
|
43021
43342
|
layoutModel,
|
|
43022
43343
|
args5.context ? String(args5.context) : void 0
|
|
43023
43344
|
);
|
|
43024
|
-
const
|
|
43345
|
+
const vision = await runVisionPasses(describeCanvas);
|
|
43346
|
+
const { blueprint, layout, creditsSpent } = vision;
|
|
43347
|
+
const elements = castAvatarsOnStaticElements(vision.elements, cast.avatars);
|
|
43348
|
+
const uncast = staticAvatarsNotCast(vision.elements, cast.handles);
|
|
43025
43349
|
const annotated = annotateBlueprintWithLabels(blueprint, elements);
|
|
43026
43350
|
if (layout && annotated && typeof annotated === "object") {
|
|
43027
43351
|
annotated.layout = layout;
|
|
@@ -43109,6 +43433,11 @@ var scaffoldStaticAdCommand = defineCommand114({
|
|
|
43109
43433
|
definition_path: definitionPath ?? void 0,
|
|
43110
43434
|
source_reference: durableSourceUrl ?? void 0,
|
|
43111
43435
|
output: canvas.output,
|
|
43436
|
+
...uncast.length ? {
|
|
43437
|
+
hints: [
|
|
43438
|
+
`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.`
|
|
43439
|
+
]
|
|
43440
|
+
} : {},
|
|
43112
43441
|
models: { describe: describeModel, select: selectModel, layout: layoutModel, gen: opts.genModel },
|
|
43113
43442
|
aspect_ratio: report.aspect_ratio,
|
|
43114
43443
|
placements: report.placements,
|
|
@@ -43123,7 +43452,7 @@ var scaffoldStaticAdCommand = defineCommand114({
|
|
|
43123
43452
|
edit_prompt: `Edit ${path21.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.`,
|
|
43124
43453
|
assets_to_supply: report.elements,
|
|
43125
43454
|
font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path (the describe pass recorded the ad's typefaces under `fonts` in prompt.json \u2014 match those). The font is wired into the render as a TYPE SPECIMEN reference so generated text takes the brand letterforms. Delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
|
|
43126
|
-
actor_sheets: report.actor_sheets.length > 0 ? `Each
|
|
43455
|
+
actor_sheets: report.actor_sheets.length > 0 ? `Each animal hero (${report.actor_sheets.join(", ")}) is fused into a generated multi-view reference sheet (image_reference_sheet) that the render grounds on \u2014 so drop ONE clean photo at that hero's ingest and the sheet builds the consistent turnaround. Pass --skip-actor-sheets to ground on the lone photo instead. A person hero is cast from a company Avatar (--avatar <handle>), never a found photo.` : "none (no animal heroes detected, or --skip-actor-sheets). A person hero is cast from a company Avatar (--avatar <handle>), never a found photo.",
|
|
43127
43456
|
placements: report.placements === null ? `Single format (${report.aspect_ratio}). Pass --placements meta_feed_stories|meta_all|linkedin|\u2026 to auto-fan-out to a platform's set.` : `Auto-fans the approved hero out to ${report.placements} \u2192 ${report.output_formats.join(", ")}. The hero ratio (${report.aspect_ratio}) passes through free; each other ratio is a billed render, AI-recomposed (not cropped). Re-read the copy and confirm the brand-color treatment on EVERY ratio before publishing \u2014 adaptation re-generates and can silently mangle text or flatten a color device. Pass --placements none for a single base ad.`,
|
|
43128
43457
|
note: "Populate as you go: for each [TODO] ingest slot, source its real asset and wire it into the slot right away \u2014 one at a time, not all sourced first then reconciled at the end. For any LOGO slot, use the company's committed brand logo from `src/brand/logos/` (per BRAND.md's Logos map) \u2014 do NOT re-fetch it with `baker images logo <domain>` when a brand logo exists, because the Brandfetch CDN often serves an OUTDATED mark and that is how an old logo ships on a live ad. When every slot is filled, `baker canvas validate` then `baker canvas run`. Running generates a billed image \u2014 it is not free."
|
|
43129
43458
|
}
|
|
@@ -44879,18 +45208,18 @@ function parseBudget(raw) {
|
|
|
44879
45208
|
throw new Error(`--budget must be one of ${VIDEO_BUDGETS.join(", ")} (got "${v}")`);
|
|
44880
45209
|
}
|
|
44881
45210
|
function resolveModels2(args5) {
|
|
44882
|
-
const
|
|
45211
|
+
const pick2 = (flag, kind, fallback) => args5[flag] ? String(args5[flag]) : resolveModel2(kind, fallback);
|
|
44883
45212
|
return {
|
|
44884
45213
|
// Flash by default: ~2-3× faster per call than Pro, which keeps each
|
|
44885
45214
|
// per-scene deconstruct step well inside the action time budget and turns a
|
|
44886
45215
|
// ~9 min run into ~3-4 min. Override with --deconstruct-model
|
|
44887
45216
|
// ~google/gemini-pro-latest for the densest extraction when speed matters less.
|
|
44888
|
-
deconstructModel:
|
|
44889
|
-
selectModel:
|
|
45217
|
+
deconstructModel: pick2("deconstruct-model", "video_deconstruct", "~google/gemini-flash-latest"),
|
|
45218
|
+
selectModel: pick2("select-model", "text_generate", "~google/gemini-flash-latest"),
|
|
44890
45219
|
// Default to the strongest image model (matches the static-ad scaffold); the
|
|
44891
45220
|
// frame generators need the most faithful text/identity reproduction. Override
|
|
44892
45221
|
// with --image-model for a cheaper/faster pass.
|
|
44893
|
-
imageModel:
|
|
45222
|
+
imageModel: pick2("image-model", "image_generate", "openai/gpt-image-2.5-sunburst")
|
|
44894
45223
|
};
|
|
44895
45224
|
}
|
|
44896
45225
|
function hasPhotorealCast(elements) {
|
|
@@ -44990,7 +45319,7 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
|
|
|
44990
45319
|
var scaffoldVideoCommand = defineCommand116({
|
|
44991
45320
|
meta: {
|
|
44992
45321
|
name: "scaffold-video",
|
|
44993
|
-
description: "Turn a reference video into a runnable reproduction canvas in one command. Runs billed passes \u2014 video_deconstruct (the full scene-by-scene blueprint + transcript, baked to a split, editable blueprint: a global prompt.json plus one small scenes/sNN.json per scene) and an AI selection of the video's RECURRING identity elements (person/animal/product/logo) \u2014 then scaffolds a pipeline where every scene boundary is a static-ad-grade frame (the blueprint as target_blueprint, a reference legend, the real frame as anchor) and each recurring element gets ONE shared [TODO] ingest slot wired into every frame it appears in. The clips feed Seedance an ultra-detailed motion brief (action, camera, dialogue, transcript). Edit a scene's scenes/sNN.json (or prompt.json for global cast/palette/brand),
|
|
45322
|
+
description: "Turn a reference video into a runnable reproduction canvas in one command. Runs billed passes \u2014 video_deconstruct (the full scene-by-scene blueprint + transcript, baked to a split, editable blueprint: a global prompt.json plus one small scenes/sNN.json per scene) and an AI selection of the video's RECURRING identity elements (person/animal/product/logo) \u2014 then scaffolds a pipeline where every scene boundary is a static-ad-grade frame (the blueprint as target_blueprint, a reference legend, the real frame as anchor) and each recurring element gets ONE shared [TODO] ingest slot wired into every frame it appears in. The clips feed Seedance an ultra-detailed motion brief (action, camera, dialogue, transcript). Edit a scene's scenes/sNN.json (or prompt.json for global cast/palette/brand), fill the remaining [TODO] images, then `baker canvas run`. Start here: `baker avatars list`, then pass `--avatar <handle>` when the video shows a person \u2014 the reference's people are played by the company's avatars, never by a photo of anyone real."
|
|
44994
45323
|
},
|
|
44995
45324
|
args: {
|
|
44996
45325
|
file: {
|
|
@@ -45054,6 +45383,10 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
45054
45383
|
resolution: {
|
|
45055
45384
|
type: "string",
|
|
45056
45385
|
description: `Output resolution for generated clips (e.g. "1080p"). Default 1080p \u2014 the highest the video model supports \u2014 so clips keep the keyframe sharpness instead of the model's low default.`
|
|
45386
|
+
},
|
|
45387
|
+
avatar: {
|
|
45388
|
+
type: "string",
|
|
45389
|
+
description: "Cast company avatars as the reference's people \u2014 pass this whenever the video has a person in it. One handle, or several comma-separated in the order the people appear (`--avatar marta` / `--avatar marta,joan`). Each recurring person grounds on that avatar's identity sheet, so the ad shows the company's own cast instead of the source ad's actors. `baker avatars list` shows who there is; checked before anything is billed."
|
|
45057
45390
|
}
|
|
45058
45391
|
},
|
|
45059
45392
|
async run({ args: args5 }) {
|
|
@@ -45093,6 +45426,9 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
45093
45426
|
);
|
|
45094
45427
|
}
|
|
45095
45428
|
const { deconstructModel, selectModel, imageModel } = resolveModels2(args5);
|
|
45429
|
+
const cast = await resolveCastAvatars(args5.avatar);
|
|
45430
|
+
if ("error" in cast) return fail4("avatar", cast.error);
|
|
45431
|
+
const { handles: avatarHandles, avatars } = cast;
|
|
45096
45432
|
const shotThreshold = args5["shot-threshold"] ? Number(args5["shot-threshold"]) : void 0;
|
|
45097
45433
|
const shotCuts = await detectShotCutsBestEffort(videoPath, shotThreshold);
|
|
45098
45434
|
const deconstructCanvas = buildDeconstructCanvas(videoPath, deconstructModel, {
|
|
@@ -45101,7 +45437,15 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
45101
45437
|
focus: args5.focus ? String(args5.focus) : void 0,
|
|
45102
45438
|
shotCuts
|
|
45103
45439
|
});
|
|
45104
|
-
const
|
|
45440
|
+
const analysis = await runAnalysisPasses(deconstructCanvas, selectModel);
|
|
45441
|
+
const { blueprint, transcript, creditsSpent } = analysis;
|
|
45442
|
+
const elements = castAvatarsOnElements(analysis.elements, avatars);
|
|
45443
|
+
const uncast = avatarsNotCast(analysis.elements, avatarHandles);
|
|
45444
|
+
const uncastHint = uncast.length ? {
|
|
45445
|
+
hints: [
|
|
45446
|
+
`NOT CAST: ${uncast.map((h) => `\`${h}\``).join(", ")} \u2014 the element selection found no more recurring people than it cast, so ${uncast.length === 1 ? "this avatar plays" : "these avatars play"} nobody in this canvas. A person shown in only one scene is not a recurring element, so those frames render whoever their scene file describes \u2014 tell the user who is cast and who is not.`
|
|
45447
|
+
]
|
|
45448
|
+
} : {};
|
|
45105
45449
|
const { videoModel, videoRouteReason } = resolveVideoModel(args5, elements);
|
|
45106
45450
|
if (videoRouteReason && !args5["video-model"] && !args5["real-face"] && hasPhotorealCast(elements)) {
|
|
45107
45451
|
process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
|
|
@@ -45191,7 +45535,8 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
45191
45535
|
error: { code: "validation", issues: validation.issues },
|
|
45192
45536
|
canvas_path: outPath,
|
|
45193
45537
|
prompt_path: blueprintPath,
|
|
45194
|
-
note: "The canvas WAS written despite the blocking issue(s) \u2014 fix them in place, then `baker canvas validate` before running."
|
|
45538
|
+
note: "The canvas WAS written despite the blocking issue(s) \u2014 fix them in place, then `baker canvas validate` before running.",
|
|
45539
|
+
...uncastHint
|
|
45195
45540
|
},
|
|
45196
45541
|
null,
|
|
45197
45542
|
2
|
|
@@ -45240,6 +45585,7 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
45240
45585
|
source_reference: sourceRef.url,
|
|
45241
45586
|
composition_dir: compositionDest,
|
|
45242
45587
|
output: canvas.output,
|
|
45588
|
+
...uncastHint,
|
|
45243
45589
|
frames_mode: frames,
|
|
45244
45590
|
models: {
|
|
45245
45591
|
deconstruct: deconstructModel,
|
|
@@ -45283,7 +45629,7 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
45283
45629
|
...report.elements.some((e) => e.type === "person" || e.type === "animal") && /seedance/i.test(videoModel) ? {
|
|
45284
45630
|
content_policy_risk: "You FORCED Seedance (`--video-model bytedance/seedance-2.0`) on an ad with a photoreal on-camera cast. ByteDance's real-person filter will reject those clips (content_policy_blocked, E005) \u2014 no prompt change clears it. Each clip auto-recovers on Veo at render time so the run still completes, but you burn a wasted Seedance attempt per clip. Drop `--video-model` to let the scaffold route the cast to Veo up front, or make the frames less photorealistic."
|
|
45285
45631
|
} : {},
|
|
45286
|
-
note: "
|
|
45632
|
+
note: "Fill each el_* [TODO] \u2014 a person with a company avatar's identity sheet (`--avatar <handle>`), never a photo of a real person; anything else with ONE real source image (reused across every frame that element appears in) \u2014 confirm each voice_select casting, then `baker canvas validate` and `baker canvas run`. Running generates many billed image/video/audio assets \u2014 it is not free."
|
|
45287
45633
|
}
|
|
45288
45634
|
},
|
|
45289
45635
|
null,
|
|
@@ -53962,7 +54308,7 @@ var extractCommand = defineCommand149({
|
|
|
53962
54308
|
import { defineCommand as defineCommand150 } from "citty";
|
|
53963
54309
|
|
|
53964
54310
|
// src/commands/images/providerHits.ts
|
|
53965
|
-
function
|
|
54311
|
+
function asRecord4(value) {
|
|
53966
54312
|
return value && typeof value === "object" ? value : {};
|
|
53967
54313
|
}
|
|
53968
54314
|
function str(value) {
|
|
@@ -53987,7 +54333,7 @@ function providerHitNormalizer(record, full) {
|
|
|
53987
54333
|
if (!full) {
|
|
53988
54334
|
return compact3;
|
|
53989
54335
|
}
|
|
53990
|
-
const group =
|
|
54336
|
+
const group = asRecord4(record.group);
|
|
53991
54337
|
return {
|
|
53992
54338
|
...compact3,
|
|
53993
54339
|
externalUrl: str(record.externalUrl),
|
|
@@ -66250,7 +66596,7 @@ async function listTags(json) {
|
|
|
66250
66596
|
failApi3(err);
|
|
66251
66597
|
}
|
|
66252
66598
|
}
|
|
66253
|
-
var
|
|
66599
|
+
var listArgs12 = {
|
|
66254
66600
|
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
|
|
66255
66601
|
};
|
|
66256
66602
|
var listCommand19 = defineCommand222({
|
|
@@ -66258,7 +66604,7 @@ var listCommand19 = defineCommand222({
|
|
|
66258
66604
|
name: "list",
|
|
66259
66605
|
description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
|
|
66260
66606
|
},
|
|
66261
|
-
args:
|
|
66607
|
+
args: listArgs12,
|
|
66262
66608
|
run: async ({ args: args5 }) => {
|
|
66263
66609
|
await listTags(args5.json === true);
|
|
66264
66610
|
}
|
|
@@ -66306,7 +66652,7 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
66306
66652
|
// command's flags when it scans rawArgs for a subcommand name, and `default` (not
|
|
66307
66653
|
// `run`) dispatches the bare invocation — citty falls through to `run` after a
|
|
66308
66654
|
// subcommand, so a `run` here would also list on every `baker tags draft`.
|
|
66309
|
-
args:
|
|
66655
|
+
args: listArgs12,
|
|
66310
66656
|
default: "list"
|
|
66311
66657
|
});
|
|
66312
66658
|
|