@koda-sl/baker-cli 0.115.0 → 0.116.0-dev.3bcc79f9c
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 +33 -0
- package/dist/{chunk-ZWMMCLJ4.js → chunk-OCMOQOIJ.js} +241 -57
- package/dist/chunk-OCMOQOIJ.js.map +1 -0
- package/dist/cli.js +287 -86
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.d.ts +30 -0
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZWMMCLJ4.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
BackendClient,
|
|
3
4
|
ELEVENLABS_MAX_MUSIC_LENGTH_MS,
|
|
4
5
|
IMAGE_GENERATE_MODELS,
|
|
5
6
|
LayerExecutionError,
|
|
6
7
|
MODEL_REGISTRY,
|
|
7
8
|
SEEDANCE_DURATIONS,
|
|
8
9
|
ValidationError,
|
|
10
|
+
collectAssetRefLikes,
|
|
9
11
|
createEngineFromEnv,
|
|
10
12
|
defaultRegistry,
|
|
11
13
|
describeFailureReason,
|
|
12
14
|
generateCatalog,
|
|
15
|
+
isPersistedAssetRef,
|
|
16
|
+
requireCredentialsFromEnv,
|
|
13
17
|
resolveConcurrency,
|
|
18
|
+
sha256Hex,
|
|
19
|
+
ulid,
|
|
14
20
|
validateCanvasDeep
|
|
15
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-OCMOQOIJ.js";
|
|
16
22
|
|
|
17
23
|
// src/cli.ts
|
|
18
24
|
import { defineCommand as defineCommand157, runMain } from "citty";
|
|
@@ -151,9 +157,9 @@ async function handleResponse(response) {
|
|
|
151
157
|
throw new ApiError("INTERNAL_ERROR", "Failed to parse API response as JSON");
|
|
152
158
|
}
|
|
153
159
|
}
|
|
154
|
-
async function apiGet(
|
|
160
|
+
async function apiGet(path13, params) {
|
|
155
161
|
const env = getEnv();
|
|
156
|
-
const url = new URL(
|
|
162
|
+
const url = new URL(path13, env.BAKER_API_URL);
|
|
157
163
|
if (params) {
|
|
158
164
|
const clean = sanitizeParams(params);
|
|
159
165
|
for (const [key, value] of Object.entries(clean)) {
|
|
@@ -178,12 +184,12 @@ async function apiGet(path12, params) {
|
|
|
178
184
|
}
|
|
179
185
|
return handleResponse(response);
|
|
180
186
|
}
|
|
181
|
-
async function apiPost(
|
|
187
|
+
async function apiPost(path13, body, opts) {
|
|
182
188
|
const env = getEnv();
|
|
183
189
|
const timeoutMs = opts?.timeoutMs ?? 6e4;
|
|
184
190
|
let response;
|
|
185
191
|
try {
|
|
186
|
-
response = await fetchWithRateLimitRetry(new URL(
|
|
192
|
+
response = await fetchWithRateLimitRetry(new URL(path13, env.BAKER_API_URL).toString(), {
|
|
187
193
|
method: "POST",
|
|
188
194
|
headers: {
|
|
189
195
|
Authorization: `Bearer ${env.BAKER_API_KEY}`,
|
|
@@ -2875,31 +2881,31 @@ function cachePath(category, key) {
|
|
|
2875
2881
|
return join2(dir, `${hashKey(key)}.json`);
|
|
2876
2882
|
}
|
|
2877
2883
|
function cacheGet(category, key) {
|
|
2878
|
-
const
|
|
2879
|
-
if (!existsSync2(
|
|
2884
|
+
const path13 = cachePath(category, key);
|
|
2885
|
+
if (!existsSync2(path13)) {
|
|
2880
2886
|
return null;
|
|
2881
2887
|
}
|
|
2882
2888
|
try {
|
|
2883
|
-
const raw = readFileSync2(
|
|
2889
|
+
const raw = readFileSync2(path13, "utf-8");
|
|
2884
2890
|
const entry = JSON.parse(raw);
|
|
2885
2891
|
if (entry.expiresAt < Date.now()) {
|
|
2886
|
-
rmSync(
|
|
2892
|
+
rmSync(path13, { force: true });
|
|
2887
2893
|
return null;
|
|
2888
2894
|
}
|
|
2889
2895
|
return entry;
|
|
2890
2896
|
} catch {
|
|
2891
|
-
rmSync(
|
|
2897
|
+
rmSync(path13, { force: true });
|
|
2892
2898
|
return null;
|
|
2893
2899
|
}
|
|
2894
2900
|
}
|
|
2895
2901
|
function cacheSet(category, key, data, ttlMs, fields) {
|
|
2896
|
-
const
|
|
2902
|
+
const path13 = cachePath(category, key);
|
|
2897
2903
|
const entry = {
|
|
2898
2904
|
expiresAt: Date.now() + ttlMs,
|
|
2899
2905
|
data,
|
|
2900
2906
|
fields
|
|
2901
2907
|
};
|
|
2902
|
-
writeFileSync(
|
|
2908
|
+
writeFileSync(path13, JSON.stringify(entry), "utf-8");
|
|
2903
2909
|
}
|
|
2904
2910
|
var HOUR = 60 * 60 * 1e3;
|
|
2905
2911
|
var MINUTE = 60 * 1e3;
|
|
@@ -4812,11 +4818,11 @@ function rawTextEntries(value) {
|
|
|
4812
4818
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
4813
4819
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
4814
4820
|
}
|
|
4815
|
-
function rawFileEntries(
|
|
4816
|
-
if (typeof
|
|
4821
|
+
function rawFileEntries(path13) {
|
|
4822
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
4817
4823
|
return [];
|
|
4818
4824
|
}
|
|
4819
|
-
return readFileSync3(
|
|
4825
|
+
return readFileSync3(path13, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
4820
4826
|
}
|
|
4821
4827
|
function keywordEntries(args) {
|
|
4822
4828
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -4839,19 +4845,19 @@ function keywordEntries(args) {
|
|
|
4839
4845
|
}
|
|
4840
4846
|
return entries;
|
|
4841
4847
|
}
|
|
4842
|
-
function loadJsonFileArg(
|
|
4843
|
-
if (typeof
|
|
4848
|
+
function loadJsonFileArg(path13) {
|
|
4849
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
4844
4850
|
return {};
|
|
4845
4851
|
}
|
|
4846
4852
|
try {
|
|
4847
|
-
const parsed = JSON.parse(readFileSync3(
|
|
4853
|
+
const parsed = JSON.parse(readFileSync3(path13, "utf8"));
|
|
4848
4854
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4849
|
-
failWriteValidation(`${
|
|
4855
|
+
failWriteValidation(`${path13} must contain a JSON object`);
|
|
4850
4856
|
}
|
|
4851
4857
|
return parsed;
|
|
4852
4858
|
} catch (err) {
|
|
4853
4859
|
if (err instanceof SyntaxError) {
|
|
4854
|
-
failWriteValidation(`${
|
|
4860
|
+
failWriteValidation(`${path13} is not valid JSON: ${err.message}`);
|
|
4855
4861
|
}
|
|
4856
4862
|
throw err;
|
|
4857
4863
|
}
|
|
@@ -4940,10 +4946,10 @@ async function stageUpdate(kind, customerId, target, payload) {
|
|
|
4940
4946
|
async function stageTarget(kind, customerId, target) {
|
|
4941
4947
|
await stageGoogleOp({ kind, customerId, target });
|
|
4942
4948
|
}
|
|
4943
|
-
async function draftAction(
|
|
4949
|
+
async function draftAction(path13, body) {
|
|
4944
4950
|
try {
|
|
4945
4951
|
const chatId = requireChatId();
|
|
4946
|
-
const response = await apiPost(
|
|
4952
|
+
const response = await apiPost(path13, { chatId, ...body });
|
|
4947
4953
|
writeJsonEnvelope(response);
|
|
4948
4954
|
} catch (err) {
|
|
4949
4955
|
handleGoogleError(err);
|
|
@@ -8484,19 +8490,19 @@ function failWriteValidation2(message) {
|
|
|
8484
8490
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
8485
8491
|
process.exit(1);
|
|
8486
8492
|
}
|
|
8487
|
-
function loadJsonFileArg2(
|
|
8488
|
-
if (typeof
|
|
8493
|
+
function loadJsonFileArg2(path13) {
|
|
8494
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
8489
8495
|
return {};
|
|
8490
8496
|
}
|
|
8491
8497
|
try {
|
|
8492
|
-
const parsed = JSON.parse(readFileSync7(
|
|
8498
|
+
const parsed = JSON.parse(readFileSync7(path13, "utf8"));
|
|
8493
8499
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8494
|
-
failWriteValidation2(`${
|
|
8500
|
+
failWriteValidation2(`${path13} must contain a JSON object`);
|
|
8495
8501
|
}
|
|
8496
8502
|
return parsed;
|
|
8497
8503
|
} catch (err) {
|
|
8498
8504
|
if (err instanceof SyntaxError) {
|
|
8499
|
-
failWriteValidation2(`${
|
|
8505
|
+
failWriteValidation2(`${path13} is not valid JSON: ${err.message}`);
|
|
8500
8506
|
}
|
|
8501
8507
|
throw err;
|
|
8502
8508
|
}
|
|
@@ -8559,15 +8565,15 @@ function parseLocaleFlag(value) {
|
|
|
8559
8565
|
}
|
|
8560
8566
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
8561
8567
|
}
|
|
8562
|
-
function loadTargetingFileArg(
|
|
8563
|
-
if (typeof
|
|
8568
|
+
function loadTargetingFileArg(path13) {
|
|
8569
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
8564
8570
|
return void 0;
|
|
8565
8571
|
}
|
|
8566
|
-
const parsed = loadJsonFileArg2(
|
|
8572
|
+
const parsed = loadJsonFileArg2(path13);
|
|
8567
8573
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
8568
8574
|
if (!criteria.include) {
|
|
8569
8575
|
failWriteValidation2(
|
|
8570
|
-
`${
|
|
8576
|
+
`${path13} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
8571
8577
|
);
|
|
8572
8578
|
}
|
|
8573
8579
|
return criteria;
|
|
@@ -8602,14 +8608,14 @@ function parseCsvLine(line) {
|
|
|
8602
8608
|
cells.push(current);
|
|
8603
8609
|
return cells.map((cell) => cell.trim());
|
|
8604
8610
|
}
|
|
8605
|
-
function parseListFileArg(
|
|
8606
|
-
if (typeof
|
|
8611
|
+
function parseListFileArg(path13, maxRows) {
|
|
8612
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
8607
8613
|
return void 0;
|
|
8608
8614
|
}
|
|
8609
|
-
const raw = readFileSync7(
|
|
8615
|
+
const raw = readFileSync7(path13, "utf8");
|
|
8610
8616
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
8611
8617
|
if (lines.length < 2) {
|
|
8612
|
-
failWriteValidation2(`${
|
|
8618
|
+
failWriteValidation2(`${path13} needs a header row and at least one data row`);
|
|
8613
8619
|
}
|
|
8614
8620
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
8615
8621
|
const rows = [];
|
|
@@ -8628,7 +8634,7 @@ function parseListFileArg(path12, maxRows) {
|
|
|
8628
8634
|
}
|
|
8629
8635
|
}
|
|
8630
8636
|
if (rows.length > maxRows) {
|
|
8631
|
-
failWriteValidation2(`${
|
|
8637
|
+
failWriteValidation2(`${path13} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
8632
8638
|
}
|
|
8633
8639
|
return { columns, rows };
|
|
8634
8640
|
}
|
|
@@ -12831,7 +12837,7 @@ async function probeDuration(filePath) {
|
|
|
12831
12837
|
|
|
12832
12838
|
// src/commands/canvas/run.ts
|
|
12833
12839
|
import { readFile as readFile2 } from "fs/promises";
|
|
12834
|
-
import
|
|
12840
|
+
import path5 from "path";
|
|
12835
12841
|
import { defineCommand as defineCommand85 } from "citty";
|
|
12836
12842
|
|
|
12837
12843
|
// src/commands/canvas/placeholders.ts
|
|
@@ -12875,9 +12881,102 @@ function isResolvableRelative(value) {
|
|
|
12875
12881
|
return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path2.isAbsolute(value);
|
|
12876
12882
|
}
|
|
12877
12883
|
|
|
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
|
+
|
|
12878
12977
|
// src/commands/canvas/run-retention.ts
|
|
12879
12978
|
import { rm } from "fs/promises";
|
|
12880
|
-
import
|
|
12979
|
+
import path4 from "path";
|
|
12881
12980
|
function runDirsToPrune(entries, keep, currentRunId) {
|
|
12882
12981
|
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
12883
12982
|
if (keep <= 0) return runs;
|
|
@@ -12894,7 +12993,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
12894
12993
|
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
12895
12994
|
if (toPrune.length === 0) return;
|
|
12896
12995
|
for (const dir of toPrune) {
|
|
12897
|
-
await rm(
|
|
12996
|
+
await rm(path4.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
12898
12997
|
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
12899
12998
|
);
|
|
12900
12999
|
}
|
|
@@ -12921,10 +13020,22 @@ var runCommand = defineCommand85({
|
|
|
12921
13020
|
"keep-runs": {
|
|
12922
13021
|
type: "string",
|
|
12923
13022
|
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)"
|
|
12924
13035
|
}
|
|
12925
13036
|
},
|
|
12926
13037
|
async run({ args }) {
|
|
12927
|
-
const filePath =
|
|
13038
|
+
const filePath = path5.resolve(String(args.file));
|
|
12928
13039
|
const raw = await readFile2(filePath, "utf8");
|
|
12929
13040
|
let parsed;
|
|
12930
13041
|
try {
|
|
@@ -12935,7 +13046,7 @@ var runCommand = defineCommand85({
|
|
|
12935
13046
|
`);
|
|
12936
13047
|
process.exit(2);
|
|
12937
13048
|
}
|
|
12938
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
13049
|
+
parsed = resolveRelativeCanvasPaths(parsed, path5.dirname(filePath));
|
|
12939
13050
|
const pending = unsuppliedPlaceholderAssets(parsed);
|
|
12940
13051
|
if (pending.length > 0) {
|
|
12941
13052
|
process.stderr.write(
|
|
@@ -12955,16 +13066,26 @@ var runCommand = defineCommand85({
|
|
|
12955
13066
|
);
|
|
12956
13067
|
process.exit(2);
|
|
12957
13068
|
}
|
|
13069
|
+
const remoteCache = args["remote-cache"] !== void 0 ? String(args["remote-cache"]) !== "off" : void 0;
|
|
12958
13070
|
const engine = createEngineFromEnv({
|
|
12959
13071
|
cacheDir: args["cache-dir"] ? String(args["cache-dir"]) : void 0,
|
|
12960
13072
|
outputsDir: args["outputs-dir"] ? String(args["outputs-dir"]) : void 0,
|
|
12961
13073
|
log: (line) => process.stdout.write(`${line}
|
|
12962
|
-
`)
|
|
13074
|
+
`),
|
|
13075
|
+
remoteCache
|
|
12963
13076
|
});
|
|
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();
|
|
12964
13085
|
try {
|
|
12965
13086
|
const policy = args["cache-policy"] ?? "read_write";
|
|
12966
13087
|
const result = await engine.run(parsed, {
|
|
12967
|
-
run_id:
|
|
13088
|
+
run_id: runId,
|
|
12968
13089
|
cache_policy: policy,
|
|
12969
13090
|
concurrency: resolveConcurrency(
|
|
12970
13091
|
// --concurrency wins; --parallel is the discoverable alias for the same bound.
|
|
@@ -12972,9 +13093,10 @@ var runCommand = defineCommand85({
|
|
|
12972
13093
|
process.env.BAKER_CANVAS_CONCURRENCY
|
|
12973
13094
|
)
|
|
12974
13095
|
});
|
|
13096
|
+
if (record) await record(buildRunRecord(result, recordMeta));
|
|
12975
13097
|
const keepRuns = args["keep-runs"] !== void 0 ? Number(args["keep-runs"]) : void 0;
|
|
12976
13098
|
if (keepRuns !== void 0 && Number.isFinite(keepRuns)) {
|
|
12977
|
-
const outputsDir = args["outputs-dir"] ?
|
|
13099
|
+
const outputsDir = args["outputs-dir"] ? path5.resolve(String(args["outputs-dir"])) : path5.resolve("canvas");
|
|
12978
13100
|
await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
|
|
12979
13101
|
`));
|
|
12980
13102
|
}
|
|
@@ -13002,6 +13124,7 @@ var runCommand = defineCommand85({
|
|
|
13002
13124
|
}
|
|
13003
13125
|
if (e instanceof LayerExecutionError) {
|
|
13004
13126
|
const failures = e.failures.map((f) => ({ node_id: f.nodeId, message: describeFailureReason(f.reason) }));
|
|
13127
|
+
if (record) await record(buildFailedRunRecord(runId, e.message, recordMeta));
|
|
13005
13128
|
process.stderr.write(
|
|
13006
13129
|
`${JSON.stringify({ ok: false, error: { code: "runtime", message: e.message, failures } }, null, 2)}
|
|
13007
13130
|
`
|
|
@@ -13009,16 +13132,30 @@ var runCommand = defineCommand85({
|
|
|
13009
13132
|
process.exit(1);
|
|
13010
13133
|
}
|
|
13011
13134
|
const msg = e instanceof Error ? e.message : String(e);
|
|
13135
|
+
if (record) await record(buildFailedRunRecord(runId, msg, recordMeta));
|
|
13012
13136
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "runtime", message: msg } }, null, 2)}
|
|
13013
13137
|
`);
|
|
13014
13138
|
process.exit(1);
|
|
13015
13139
|
}
|
|
13016
13140
|
}
|
|
13017
13141
|
});
|
|
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
|
+
}
|
|
13018
13155
|
|
|
13019
13156
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
13020
13157
|
import { readFile as readFile3, writeFile } from "fs/promises";
|
|
13021
|
-
import
|
|
13158
|
+
import path7 from "path";
|
|
13022
13159
|
import { defineCommand as defineCommand86 } from "citty";
|
|
13023
13160
|
|
|
13024
13161
|
// src/engine/scaffold/staticAd.ts
|
|
@@ -13205,15 +13342,20 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
13205
13342
|
}
|
|
13206
13343
|
|
|
13207
13344
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
13208
|
-
import
|
|
13209
|
-
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd()) {
|
|
13345
|
+
import path6 from "path";
|
|
13346
|
+
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
13210
13347
|
const file = rawFile.trim();
|
|
13211
13348
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
13212
|
-
const imageSource = imageIsUrl ? file :
|
|
13213
|
-
const outPath = out ?
|
|
13214
|
-
const blueprintPath =
|
|
13349
|
+
const imageSource = imageIsUrl ? file : path6.resolve(cwd, file);
|
|
13350
|
+
const outPath = out ? path6.resolve(cwd, out) : slug ? path6.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path6.join(cwd, "static-ad.canvas.json") : path6.join(path6.dirname(imageSource), "static-ad.canvas.json");
|
|
13351
|
+
const blueprintPath = path6.join(path6.dirname(outPath), "prompt.json");
|
|
13215
13352
|
return { imageIsUrl, imageSource, outPath, blueprintPath };
|
|
13216
13353
|
}
|
|
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
|
+
}
|
|
13217
13359
|
|
|
13218
13360
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
13219
13361
|
function resolveModel(kind, preferred) {
|
|
@@ -13375,6 +13517,10 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13375
13517
|
file: { type: "positional", required: true, description: "Path or http(s) URL to the source/inspiration image" },
|
|
13376
13518
|
context: { type: "string", description: "Known provenance (advertiser, category, market) to ground the describe" },
|
|
13377
13519
|
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
|
+
},
|
|
13378
13524
|
"describe-model": { type: "string", description: "Override the image_describe model id" },
|
|
13379
13525
|
"select-model": { type: "string", description: "Override the text_generate model id for element selection" },
|
|
13380
13526
|
"layout-model": { type: "string", description: "Override the text_generate model id for the layout pass" },
|
|
@@ -13383,9 +13529,19 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13383
13529
|
"skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" }
|
|
13384
13530
|
},
|
|
13385
13531
|
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
|
+
}
|
|
13386
13540
|
const { imageIsUrl, imageSource, outPath, blueprintPath } = resolveScaffoldStaticAdPaths(
|
|
13387
13541
|
String(args.file),
|
|
13388
|
-
args.out ? String(args.out) : void 0
|
|
13542
|
+
args.out ? String(args.out) : void 0,
|
|
13543
|
+
process.cwd(),
|
|
13544
|
+
slug
|
|
13389
13545
|
);
|
|
13390
13546
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
|
|
13391
13547
|
const describeCanvas = buildDescribeCanvas(
|
|
@@ -13445,7 +13601,7 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13445
13601
|
run_estimated_credits: validation.estimatedCredits
|
|
13446
13602
|
},
|
|
13447
13603
|
checklist: {
|
|
13448
|
-
edit_prompt: `Edit ${
|
|
13604
|
+
edit_prompt: `Edit ${path7.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.`,
|
|
13449
13605
|
assets_to_supply: report.elements,
|
|
13450
13606
|
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)",
|
|
13451
13607
|
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."
|
|
@@ -13461,7 +13617,7 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13461
13617
|
|
|
13462
13618
|
// src/commands/canvas/scaffold-video.ts
|
|
13463
13619
|
import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
|
|
13464
|
-
import
|
|
13620
|
+
import path10 from "path";
|
|
13465
13621
|
import { defineCommand as defineCommand87 } from "citty";
|
|
13466
13622
|
|
|
13467
13623
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
@@ -16245,23 +16401,23 @@ function videoReport(input, elementsInput) {
|
|
|
16245
16401
|
|
|
16246
16402
|
// src/commands/canvas/composition-path.ts
|
|
16247
16403
|
import { existsSync as existsSync4 } from "fs";
|
|
16248
|
-
import
|
|
16404
|
+
import path8 from "path";
|
|
16249
16405
|
function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
|
|
16250
|
-
const rel =
|
|
16406
|
+
const rel = path8.join("canvas", name);
|
|
16251
16407
|
let dir = startDir;
|
|
16252
16408
|
for (let i = 0; i < maxDepth; i++) {
|
|
16253
|
-
const candidate =
|
|
16254
|
-
if (exists(
|
|
16255
|
-
const parent =
|
|
16409
|
+
const candidate = path8.join(dir, rel);
|
|
16410
|
+
if (exists(path8.join(candidate, "meta.json"))) return candidate;
|
|
16411
|
+
const parent = path8.dirname(dir);
|
|
16256
16412
|
if (parent === dir) break;
|
|
16257
16413
|
dir = parent;
|
|
16258
16414
|
}
|
|
16259
|
-
return
|
|
16415
|
+
return path8.resolve(startDir, "../../../", rel);
|
|
16260
16416
|
}
|
|
16261
16417
|
|
|
16262
16418
|
// src/commands/canvas/gitignore.ts
|
|
16263
16419
|
import { appendFile, readFile as readFile5 } from "fs/promises";
|
|
16264
|
-
import
|
|
16420
|
+
import path9 from "path";
|
|
16265
16421
|
function missingGitignoreEntries(existing, entries) {
|
|
16266
16422
|
const present = new Set(
|
|
16267
16423
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -16269,7 +16425,7 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
16269
16425
|
return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
|
|
16270
16426
|
}
|
|
16271
16427
|
async function ensureGitignore(dir, entries) {
|
|
16272
|
-
const file =
|
|
16428
|
+
const file = path9.join(dir, ".gitignore");
|
|
16273
16429
|
let existing;
|
|
16274
16430
|
try {
|
|
16275
16431
|
existing = await readFile5(file, "utf8");
|
|
@@ -16330,7 +16486,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
16330
16486
|
async function stageCaptions(outDir, transcript) {
|
|
16331
16487
|
const text = transcript?.trim();
|
|
16332
16488
|
if (!text || text === "[]") return {};
|
|
16333
|
-
const compositionPath =
|
|
16489
|
+
const compositionPath = path10.join(outDir, "tiktok-captions-composition");
|
|
16334
16490
|
await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
16335
16491
|
return { compositionPath };
|
|
16336
16492
|
}
|
|
@@ -16348,10 +16504,10 @@ function patchCompositionHtml(html, dims) {
|
|
|
16348
16504
|
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`);
|
|
16349
16505
|
}
|
|
16350
16506
|
async function stampCompositionDims(compositionDir, dims) {
|
|
16351
|
-
const metaPath =
|
|
16507
|
+
const metaPath = path10.join(compositionDir, "meta.json");
|
|
16352
16508
|
const rawMeta = await readFile6(metaPath, "utf8");
|
|
16353
16509
|
await writeFile2(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
16354
|
-
const htmlPath =
|
|
16510
|
+
const htmlPath = path10.join(compositionDir, "index.html");
|
|
16355
16511
|
const rawHtml = await readFile6(htmlPath, "utf8");
|
|
16356
16512
|
await writeFile2(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
16357
16513
|
}
|
|
@@ -16491,6 +16647,10 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16491
16647
|
args: {
|
|
16492
16648
|
file: { type: "positional", required: true, description: "Path to the reference video" },
|
|
16493
16649
|
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
|
+
},
|
|
16494
16654
|
frames: { type: "string", description: '"generate" (default, anchored regen) or "reuse" (wire real frames in)' },
|
|
16495
16655
|
ambient: {
|
|
16496
16656
|
type: "boolean",
|
|
@@ -16517,11 +16677,19 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16517
16677
|
}
|
|
16518
16678
|
},
|
|
16519
16679
|
async run({ args }) {
|
|
16520
|
-
const videoPath =
|
|
16521
|
-
const base =
|
|
16522
|
-
const
|
|
16523
|
-
|
|
16524
|
-
|
|
16680
|
+
const videoPath = path10.resolve(String(args.file));
|
|
16681
|
+
const base = path10.basename(videoPath, path10.extname(videoPath));
|
|
16682
|
+
const slug = args.slug ? String(args.slug) : void 0;
|
|
16683
|
+
if (slug && !isValidScaffoldSlug(slug)) {
|
|
16684
|
+
process.stderr.write(
|
|
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");
|
|
16525
16693
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
16526
16694
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
16527
16695
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -16561,10 +16729,10 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16561
16729
|
`
|
|
16562
16730
|
);
|
|
16563
16731
|
}
|
|
16564
|
-
const compositionDest =
|
|
16732
|
+
const compositionDest = path10.join(outDir, "video-overlay-composition");
|
|
16565
16733
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
16566
16734
|
await stampCompositionDims(compositionDest, outDims);
|
|
16567
|
-
const indexPath =
|
|
16735
|
+
const indexPath = path10.join(compositionDest, "index.html");
|
|
16568
16736
|
const overlayHtml = buildOverlayHtml(blueprint);
|
|
16569
16737
|
const indexHtml = await readFile6(indexPath, "utf8");
|
|
16570
16738
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
@@ -16580,9 +16748,9 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16580
16748
|
const opts = {
|
|
16581
16749
|
imageModel,
|
|
16582
16750
|
videoModel,
|
|
16583
|
-
overlayCompositionPath:
|
|
16584
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
16585
|
-
blueprintPath:
|
|
16751
|
+
overlayCompositionPath: path10.relative(outDir, compositionDest),
|
|
16752
|
+
captionsCompositionPath: captions.compositionPath ? path10.relative(outDir, captions.compositionPath) : void 0,
|
|
16753
|
+
blueprintPath: path10.relative(outDir, blueprintPath),
|
|
16586
16754
|
frames,
|
|
16587
16755
|
ambient: Boolean(args.ambient),
|
|
16588
16756
|
...args.aspect ? { aspect: String(args.aspect) } : {},
|
|
@@ -16624,7 +16792,7 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16624
16792
|
run_estimated_credits: validation.estimatedCredits
|
|
16625
16793
|
},
|
|
16626
16794
|
checklist: {
|
|
16627
|
-
edit_prompt: `Edit ${
|
|
16795
|
+
edit_prompt: `Edit ${path10.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.`,
|
|
16628
16796
|
recurring_elements_to_supply: report.elements,
|
|
16629
16797
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
16630
16798
|
scene: d.scene,
|
|
@@ -16651,7 +16819,7 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16651
16819
|
|
|
16652
16820
|
// src/commands/canvas/set-prompt.ts
|
|
16653
16821
|
import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
|
|
16654
|
-
import
|
|
16822
|
+
import path11 from "path";
|
|
16655
16823
|
import { defineCommand as defineCommand88 } from "citty";
|
|
16656
16824
|
function setNodePrompt(canvas, nodeId, text) {
|
|
16657
16825
|
const nodes = canvas?.nodes;
|
|
@@ -16679,7 +16847,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16679
16847
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
16680
16848
|
},
|
|
16681
16849
|
async run({ args }) {
|
|
16682
|
-
const filePath =
|
|
16850
|
+
const filePath = path11.resolve(String(args.file));
|
|
16683
16851
|
let canvas;
|
|
16684
16852
|
try {
|
|
16685
16853
|
canvas = JSON.parse(await readFile7(filePath, "utf8"));
|
|
@@ -16689,7 +16857,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16689
16857
|
process.exit(2);
|
|
16690
16858
|
}
|
|
16691
16859
|
let text;
|
|
16692
|
-
if (args["text-file"]) text = await readFile7(
|
|
16860
|
+
if (args["text-file"]) text = await readFile7(path11.resolve(String(args["text-file"])), "utf8");
|
|
16693
16861
|
else if (args.text !== void 0) text = String(args.text);
|
|
16694
16862
|
else {
|
|
16695
16863
|
process.stderr.write(
|
|
@@ -16710,7 +16878,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16710
16878
|
process.exit(2);
|
|
16711
16879
|
return;
|
|
16712
16880
|
}
|
|
16713
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
16881
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path11.dirname(filePath)), defaultRegistry());
|
|
16714
16882
|
if (!validation.ok) {
|
|
16715
16883
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
16716
16884
|
`);
|
|
@@ -16726,7 +16894,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16726
16894
|
|
|
16727
16895
|
// src/commands/canvas/validate.ts
|
|
16728
16896
|
import { readFile as readFile8 } from "fs/promises";
|
|
16729
|
-
import
|
|
16897
|
+
import path12 from "path";
|
|
16730
16898
|
import { defineCommand as defineCommand89 } from "citty";
|
|
16731
16899
|
var validateCommand = defineCommand89({
|
|
16732
16900
|
meta: {
|
|
@@ -16735,7 +16903,7 @@ var validateCommand = defineCommand89({
|
|
|
16735
16903
|
},
|
|
16736
16904
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
16737
16905
|
async run({ args }) {
|
|
16738
|
-
const filePath =
|
|
16906
|
+
const filePath = path12.resolve(String(args.file));
|
|
16739
16907
|
const raw = await readFile8(filePath, "utf8");
|
|
16740
16908
|
let parsed;
|
|
16741
16909
|
try {
|
|
@@ -16746,7 +16914,7 @@ var validateCommand = defineCommand89({
|
|
|
16746
16914
|
`);
|
|
16747
16915
|
process.exit(2);
|
|
16748
16916
|
}
|
|
16749
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
16917
|
+
parsed = resolveRelativeCanvasPaths(parsed, path12.dirname(filePath));
|
|
16750
16918
|
const result = await validateCanvasDeep(parsed, defaultRegistry());
|
|
16751
16919
|
if (!result.ok) {
|
|
16752
16920
|
process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
|
|
@@ -16883,6 +17051,16 @@ registerSchema({
|
|
|
16883
17051
|
type: "string",
|
|
16884
17052
|
description: "Optional URL of the original reference ad",
|
|
16885
17053
|
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
|
|
16886
17064
|
}
|
|
16887
17065
|
}
|
|
16888
17066
|
});
|
|
@@ -16892,6 +17070,13 @@ function detectCreativeContentType(filePath) {
|
|
|
16892
17070
|
unsupportedMessage: "Unsupported creative image extension. Use PNG, JPG, or WebP."
|
|
16893
17071
|
});
|
|
16894
17072
|
}
|
|
17073
|
+
function chatIdFromEnv() {
|
|
17074
|
+
try {
|
|
17075
|
+
return getEnv().BAKER_CHAT_ID || void 0;
|
|
17076
|
+
} catch {
|
|
17077
|
+
return void 0;
|
|
17078
|
+
}
|
|
17079
|
+
}
|
|
16895
17080
|
function parseOptionalUrl(value) {
|
|
16896
17081
|
if (value === void 0 || value.trim() === "") {
|
|
16897
17082
|
return void 0;
|
|
@@ -16922,7 +17107,11 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
|
|
|
16922
17107
|
return publishImageAsCreative(deps, {
|
|
16923
17108
|
imageId: upload.imageId,
|
|
16924
17109
|
title,
|
|
16925
|
-
sourceReferenceUrl
|
|
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()
|
|
16926
17115
|
});
|
|
16927
17116
|
}
|
|
16928
17117
|
var publishCommand = defineCommand91({
|
|
@@ -16938,6 +17127,16 @@ var publishCommand = defineCommand91({
|
|
|
16938
17127
|
type: "string",
|
|
16939
17128
|
description: "Optional URL of the original reference ad",
|
|
16940
17129
|
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
|
|
16941
17140
|
}
|
|
16942
17141
|
},
|
|
16943
17142
|
run: async ({ args }) => {
|
|
@@ -16956,7 +17155,9 @@ var publishCommand = defineCommand91({
|
|
|
16956
17155
|
file,
|
|
16957
17156
|
title,
|
|
16958
17157
|
context: args.context,
|
|
16959
|
-
sourceReferenceUrl: args.sourceReferenceUrl
|
|
17158
|
+
sourceReferenceUrl: args.sourceReferenceUrl,
|
|
17159
|
+
slug: args.slug,
|
|
17160
|
+
runId: args.runId
|
|
16960
17161
|
});
|
|
16961
17162
|
writeJson({ ok: true, data });
|
|
16962
17163
|
} catch (err) {
|
|
@@ -17854,9 +18055,9 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
17854
18055
|
}
|
|
17855
18056
|
return readFile10(pathOrUrl);
|
|
17856
18057
|
}
|
|
17857
|
-
async function isDirectory(
|
|
18058
|
+
async function isDirectory(path13) {
|
|
17858
18059
|
try {
|
|
17859
|
-
const s = await stat2(
|
|
18060
|
+
const s = await stat2(path13);
|
|
17860
18061
|
return s.isDirectory();
|
|
17861
18062
|
} catch {
|
|
17862
18063
|
return false;
|