@koda-sl/baker-cli 0.475.0 → 0.476.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/{canvas-EMWTXDU3.js → canvas-RCRBTZKW.js} +5 -5
- package/dist/{chunk-P4WQG3XS.js → chunk-DRPSJE62.js} +11 -2
- package/dist/chunk-DRPSJE62.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{scaffold-ad-P5U2ZSTW.js → scaffold-ad-VKOXZENI.js} +2 -2
- package/dist/{scaffold-static-ad-YGKJ7TQS.js → scaffold-static-ad-CMYP2ZZ6.js} +2 -2
- package/dist/{scaffold-video-RZ2TB3PI.js → scaffold-video-AKA5LMEI.js} +2 -2
- package/dist/{sync-definition-MWHPAMKG.js → sync-definition-GKF2KKFC.js} +4 -2
- package/package.json +1 -1
- package/dist/chunk-P4WQG3XS.js.map +0 -1
- /package/dist/{canvas-EMWTXDU3.js.map → canvas-RCRBTZKW.js.map} +0 -0
- /package/dist/{scaffold-ad-P5U2ZSTW.js.map → scaffold-ad-VKOXZENI.js.map} +0 -0
- /package/dist/{scaffold-static-ad-YGKJ7TQS.js.map → scaffold-static-ad-CMYP2ZZ6.js.map} +0 -0
- /package/dist/{scaffold-video-RZ2TB3PI.js.map → scaffold-video-AKA5LMEI.js.map} +0 -0
- /package/dist/{sync-definition-MWHPAMKG.js.map → sync-definition-GKF2KKFC.js.map} +0 -0
package/README.md
CHANGED
|
@@ -4528,7 +4528,8 @@ baker canvas run my-canvas.json --regenerate gen,adapt
|
|
|
4528
4528
|
# edit (a file watcher), but you can invoke it directly. Accepts the
|
|
4529
4529
|
# .canvas.json, its prompt.json, or the creative folder. The rendered image
|
|
4530
4530
|
# still only changes on a real `run`, so the dashboard shows an "edited since
|
|
4531
|
-
# last render" badge in the meantime.
|
|
4531
|
+
# last render" badge in the meantime. It also carries the creative's
|
|
4532
|
+
# _definition.md, which the dashboard shows as the ad's brief.
|
|
4532
4533
|
baker canvas sync-definition src/creatives/<slug>/
|
|
4533
4534
|
|
|
4534
4535
|
# 3. Inspect a finished run (per-node timing, file list, optional video thumbs)
|
|
@@ -32,15 +32,15 @@ Full guide: __tooling__/docs/tools/baker/canvas.md`
|
|
|
32
32
|
"viewer-check": () => import("./viewer-check-GL2UNAGR.js").then((m) => m.viewerCheckCommand),
|
|
33
33
|
catalog: () => import("./catalog-YY33VIDD.js").then((m) => m.catalogCommand),
|
|
34
34
|
inspect: () => import("./inspect-QLFYFQU2.js").then((m) => m.inspectCommand),
|
|
35
|
-
"scaffold-ad": () => import("./scaffold-ad-
|
|
36
|
-
"scaffold-video": () => import("./scaffold-video-
|
|
37
|
-
"scaffold-static-ad": () => import("./scaffold-static-ad-
|
|
35
|
+
"scaffold-ad": () => import("./scaffold-ad-VKOXZENI.js").then((m) => m.scaffoldAdCommand),
|
|
36
|
+
"scaffold-video": () => import("./scaffold-video-AKA5LMEI.js").then((m) => m.scaffoldVideoCommand),
|
|
37
|
+
"scaffold-static-ad": () => import("./scaffold-static-ad-CMYP2ZZ6.js").then((m) => m.scaffoldStaticAdCommand),
|
|
38
38
|
"set-prompt": () => import("./set-prompt-VNJ334YW.js").then((m) => m.setPromptCommand),
|
|
39
|
-
"sync-definition": () => import("./sync-definition-
|
|
39
|
+
"sync-definition": () => import("./sync-definition-GKF2KKFC.js").then((m) => m.syncDefinitionCommand),
|
|
40
40
|
stale: () => import("./stale-Y5HC7BNS.js").then((m) => m.staleCommand)
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
export {
|
|
44
44
|
canvasCommand
|
|
45
45
|
};
|
|
46
|
-
//# sourceMappingURL=canvas-
|
|
46
|
+
//# sourceMappingURL=canvas-RCRBTZKW.js.map
|
|
@@ -85,6 +85,13 @@ async function readDefinitionSourceFields(canvasPath) {
|
|
|
85
85
|
return {};
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
+
async function readDefinitionBody(canvasPath) {
|
|
89
|
+
try {
|
|
90
|
+
return { definition: await readFile(path.join(path.dirname(canvasPath), "_definition.md"), "utf8") };
|
|
91
|
+
} catch {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
88
95
|
async function readDefinitionTitle(canvasPath) {
|
|
89
96
|
try {
|
|
90
97
|
const raw = await readFile(path.join(path.dirname(canvasPath), "_definition.md"), "utf8");
|
|
@@ -189,7 +196,8 @@ var syncDefinitionCommand = defineCommand({
|
|
|
189
196
|
...title ? { title } : {},
|
|
190
197
|
...await readDefinitionSourceFields(canvasPath),
|
|
191
198
|
...await readDefinitionStrategyFields(canvasPath),
|
|
192
|
-
...await readDefinitionSeriesPlace(canvasPath)
|
|
199
|
+
...await readDefinitionSeriesPlace(canvasPath),
|
|
200
|
+
...await readDefinitionBody(canvasPath)
|
|
193
201
|
});
|
|
194
202
|
}
|
|
195
203
|
});
|
|
@@ -197,6 +205,7 @@ var syncDefinitionCommand = defineCommand({
|
|
|
197
205
|
export {
|
|
198
206
|
buildDefinitionSyncPayload,
|
|
199
207
|
readDefinitionSourceFields,
|
|
208
|
+
readDefinitionBody,
|
|
200
209
|
readDefinitionTitle,
|
|
201
210
|
readDefinitionStrategyFields,
|
|
202
211
|
readDefinitionSeriesPlace,
|
|
@@ -204,4 +213,4 @@ export {
|
|
|
204
213
|
resolveCanvasPath,
|
|
205
214
|
syncDefinitionCommand
|
|
206
215
|
};
|
|
207
|
-
//# sourceMappingURL=chunk-
|
|
216
|
+
//# sourceMappingURL=chunk-DRPSJE62.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/canvas/sync-definition.ts","../src/commands/canvas/definition-graph.ts"],"sourcesContent":["import { readdir, readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { parseDocument } from \"yaml\";\nimport { defineCommand } from \"citty\";\nimport { BackendClient } from \"../../engine/index.ts\";\nimport { requireCredentialsFromEnv } from \"../../engine/client/env.ts\";\nimport {\n type DefinitionSeriesPlace,\n type DefinitionSourceFields,\n type DefinitionStrategyFields,\n parseDefinitionSeriesPlace,\n parseDefinitionSourceFields,\n parseDefinitionStrategyFields,\n} from \"./creative-definition.ts\";\nimport { canvasToDefinitionGraph } from \"./definition-graph.ts\";\nimport { prepareCanvas } from \"./prepare-canvas.ts\";\nimport { creativeSlugFromCanvasPath } from \"./run-record.ts\";\n\n/** Why the creative exists (`_definition.md` idea / message / evidence / angle / persona / awareness / hypothesis). */\ntype DefinitionSyncInput = Omit<DefinitionStrategyFields, \"seriesLead\" | \"language\"> & {\n slug: string;\n /** The series lead and language: omitted when not read (the backend keeps them), null when the definition names none (cleared). */\n seriesLead?: string | null;\n language?: string | null;\n /** Omitted on an edit re-sync so the backend keeps the creative's existing title. */\n title?: string;\n platform?: string;\n formats?: string[];\n sourceReferenceUrl?: string;\n /** The library ad it adapted (`_definition.md` sourceAdId / sourcePlatform / sourceAdvertiserId). */\n sourceAdId?: string;\n sourcePlatform?: string;\n sourceAdvertiserId?: string;\n canvas: unknown;\n /** The creative's source fingerprint (`computeSourceSha`): powers the \"edited since last render\" staleness signal. */\n sourceSha?: string;\n /** The `_definition.md` as written, so the dashboard shows the thinking behind the ad; omitted when not read (kept). */\n definition?: string;\n};\n\n/**\n * The body `POST /api/creatives/definition` receives. Absent fields are left out rather than\n * sent as undefined so the backend's `?? existing` keeps what an earlier sync recorded.\n */\nexport function buildDefinitionSyncPayload(\n input: DefinitionSyncInput,\n graph: unknown,\n chatId: string,\n): Record<string, unknown> {\n const { canvas: _canvas, ...fields } = input;\n return Object.fromEntries([\n ...Object.entries(fields).filter(([, v]) => v !== undefined),\n [\"graph\", graph],\n [\"chatId\", chatId],\n ]);\n}\n\n/** The library ids the creative's `_definition.md` names, read from beside its canvas; empty when there is none. */\nexport async function readDefinitionSourceFields(canvasPath: string): Promise<DefinitionSourceFields> {\n try {\n return parseDefinitionSourceFields(await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\"));\n } catch {\n return {};\n }\n}\n\n/** The `_definition.md` beside the canvas, whole; empty when there is none, so the last one synced is kept. */\nexport async function readDefinitionBody(canvasPath: string): Promise<{ definition?: string }> {\n try {\n return { definition: await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\") };\n } catch {\n return {};\n }\n}\n\n/** The creative's title, read from the `_definition.md` beside its canvas; undefined when it names none. */\nexport async function readDefinitionTitle(canvasPath: string): Promise<string | undefined> {\n try {\n const raw = await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\");\n const match = raw.match(/^---\\r?\\n([\\s\\S]*?\\r?\\n)?---/);\n const doc = match ? parseDocument(match[1] ?? \"\") : null;\n const title = doc && doc.errors.length === 0 ? doc.get(\"title\") : undefined;\n return typeof title === \"string\" && title.trim() ? title.trim() : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The creative's strategy, read from the `_definition.md` beside its canvas; empty when there is none. */\nexport async function readDefinitionStrategyFields(canvasPath: string): Promise<DefinitionStrategyFields> {\n try {\n return parseDefinitionStrategyFields(await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\"));\n } catch {\n return {};\n }\n}\n\n/** The series place the `_definition.md` beside the canvas names; empty when it cannot be read, so nothing is cleared. */\nexport async function readDefinitionSeriesPlace(canvasPath: string): Promise<DefinitionSeriesPlace | Record<never, never>> {\n try {\n return (\n parseDefinitionSeriesPlace(await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\")) ?? {}\n );\n } catch {\n return {};\n }\n}\n\n/**\n * Push a creative's parsed node-graph to the backend so the dashboard can draw\n * its workflow (and its wired input thumbnails) BEFORE — and BETWEEN — billed\n * runs. Best-effort: a missing chat id, missing creds, an unparseable canvas, or\n * a failed POST warns and returns — it must never fail the caller (a scaffold\n * that just did the real work, or a watcher reacting to an edit).\n */\nexport async function syncCreativeDefinitionBestEffort(input: DefinitionSyncInput): Promise<void> {\n const chatId = process.env.BAKER_CHAT_ID;\n if (!chatId) return; // Not chat-attached — nothing to preview against.\n const graph = canvasToDefinitionGraph(input.canvas);\n if (!graph || graph.nodes.length === 0) return;\n try {\n const creds = requireCredentialsFromEnv();\n const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });\n await client.syncCreativeDefinition(buildDefinitionSyncPayload(input, graph, chatId));\n process.stdout.write(`[definition] synced workflow graph (${graph.nodes.length} nodes) — view it in the dashboard\\n`);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n process.stderr.write(`[warn] workflow graph not synced (${msg})\\n`);\n }\n}\n\n/**\n * Resolve the canvas file backing a creative from ANY path under its dir — the\n * `{slug}.canvas.json` itself, its sibling `prompt.json`, or the creative\n * directory. Lets a file watcher hand us whichever file changed and still find\n * the one canvas to project. Prefers `{slug}.canvas.json`, else the sole\n * `*.canvas.json` in the directory; null when there is no single canvas.\n */\nexport async function resolveCanvasPath(inputPath: string): Promise<string | null> {\n const resolved = path.resolve(inputPath);\n let dir = resolved;\n try {\n if ((await stat(resolved)).isFile()) {\n if (resolved.endsWith(\".canvas.json\")) return resolved;\n dir = path.dirname(resolved);\n }\n } catch {\n // A deleted/renamed file: fall back to treating the path as its directory.\n dir = resolved.endsWith(\".canvas.json\") ? path.dirname(resolved) : resolved;\n }\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return null;\n }\n const canvases = entries.filter((name) => name.endsWith(\".canvas.json\"));\n const slug = creativeSlugFromCanvasPath(`${dir}/x/`);\n const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : undefined) ?? canvases[0];\n return chosen ? path.join(dir, chosen) : null;\n}\n\nexport const syncDefinitionCommand = defineCommand({\n meta: {\n name: \"sync-definition\",\n description:\n \"Push a creative's current node-graph + input thumbnails to the dashboard without rendering. The graph has the creative's blueprint applied (prompt.json and scenes/*.json edits), exactly as `run` would render it, without changing any file. Runs automatically when you edit a creative's canvas.json or prompt.json.\",\n },\n args: {\n file: {\n type: \"positional\",\n required: true,\n description: \"A path under src/creatives/<slug>/ (the .canvas.json, its prompt.json, or the folder)\",\n },\n title: {\n type: \"boolean\",\n description:\n \"Also record the title its _definition.md gives. Only for a creative just made from another (a series ad, a translation), never on an edit re-sync.\",\n },\n },\n async run({ args }) {\n const canvasPath = await resolveCanvasPath(String(args.file));\n if (!canvasPath) {\n // Nothing to sync (not a creative path, or no canvas yet) — a no-op, not an error.\n return;\n }\n const slug = creativeSlugFromCanvasPath(canvasPath);\n if (!slug) return;\n // The same preflight `run` uses, so the dashboard draws the graph that will render\n // (the scene and copy projections included), in the file's canvas-relative spelling,\n // never this machine's absolute paths. Read-only: the watcher runs this over every\n // creative on every edit, and must never rewrite a creative nobody touched. The\n // fingerprint is taken over the projected graph, as `run` and `baker canvas stale` take it.\n let canvas: unknown;\n let sourceSha: string | undefined;\n try {\n const prepared = await prepareCanvas(canvasPath, { write: false });\n canvas = prepared.fileGraph;\n sourceSha = prepared.sourceSha;\n } catch {\n // Mid-edit invalid JSON (canvas, prompt.json or a scene): skip this tick; the next\n // save re-fires the watcher.\n return;\n }\n // No title/platform/formats: an edit re-sync must never rename or re-classify\n // the creative — the backend preserves what the scaffold already set. The library\n // ids are re-read from `_definition.md`, so an agent that adds or fixes them after\n // the scaffold still gets them recorded without waiting for Publish — and so is the\n // creative's strategy, the moment the agent writes it.\n const title = args.title ? await readDefinitionTitle(canvasPath) : undefined;\n await syncCreativeDefinitionBestEffort({\n slug,\n canvas,\n sourceSha,\n ...(title ? { title } : {}),\n ...(await readDefinitionSourceFields(canvasPath)),\n ...(await readDefinitionStrategyFields(canvasPath)),\n ...(await readDefinitionSeriesPlace(canvasPath)),\n ...(await readDefinitionBody(canvasPath)),\n });\n },\n});\n","import { nodeDependencies } from \"../../engine/engine/graph.ts\";\nimport { nodeParamsPreview } from \"./node-preview.ts\";\n\nexport type DefinitionGraphNode = {\n id: string;\n type: string;\n deps?: string[];\n paramsPreview?: string;\n /**\n * A directly-viewable input image/video for the node BEFORE any run — today\n * an `ingest` node's `source:\"url\"` src. Lets the dashboard show a real\n * thumbnail (a swapped logo/hero) the moment the canvas changes, with no\n * render. Path-source ingests carry no preview (their bytes aren't uploaded\n * outside a run); the graph still shows the `path:` line via paramsPreview.\n */\n inputPreviewUrl?: string;\n};\nexport type CanvasDefinitionGraph = {\n nodes: DefinitionGraphNode[];\n output?: { node: string; output: string };\n};\n\n// Keep the synced blueprint bounded — matches the backend's cap.\nconst MAX_NODES = 300;\n\n// Only image ingests become a pre-run thumbnail: absent `expect` defaults to an\n// image, and a still renders unambiguously in the graph card. Video/font/json\n// ingests carry no preview (a video would need its own player wiring) — their\n// `url:`/`path:` line still shows via paramsPreview.\nconst PREVIEWABLE_INGEST_EXPECTS = new Set([\"image\"]);\n\n/**\n * A directly-viewable input URL for a node's pre-run thumbnail: an `ingest`\n * node pulling an image from a `source:\"url\"` src. Everything else (path-source\n * ingests, generators, non-image expects) returns undefined — only image bytes\n * the dashboard can already fetch become a thumbnail without a run.\n */\nfunction nodeInputPreviewUrl(type: string, params: unknown): string | undefined {\n if (type !== \"ingest\" || !params || typeof params !== \"object\") return undefined;\n const p = params as { source?: unknown; url?: unknown; expect?: unknown };\n if (p.source !== \"url\" || typeof p.url !== \"string\") return undefined;\n if (!/^https?:\\/\\//i.test(p.url)) return undefined;\n if (typeof p.expect === \"string\" && !PREVIEWABLE_INGEST_EXPECTS.has(p.expect)) return undefined;\n return p.url;\n}\n\n/**\n * Parse a canvas JSON into the compact node-graph the dashboard draws before any\n * run — shares the engine's `nodeDependencies` ($ref edges from inputs AND params) so\n * the pre-run blueprint matches the eventual run graph. Best-effort and defensive:\n * a not-yet-valid canvas (mid-edit) returns whatever nodes it can read, or null\n * if there are none, so a caller can skip the sync without failing the command.\n */\nexport function canvasToDefinitionGraph(canvas: unknown): CanvasDefinitionGraph | null {\n const rawNodes = (canvas as { nodes?: unknown })?.nodes;\n if (!Array.isArray(rawNodes)) return null;\n\n const parsed: Array<{ id: string; type: string; inputs: unknown; params: unknown }> = [];\n for (const raw of rawNodes) {\n const id = (raw as { id?: unknown })?.id;\n const type = (raw as { type?: unknown })?.type;\n if (typeof id !== \"string\" || typeof type !== \"string\") continue;\n parsed.push({ id, type, inputs: (raw as { inputs?: unknown }).inputs, params: (raw as { params?: unknown }).params });\n if (parsed.length >= MAX_NODES) break;\n }\n if (parsed.length === 0) return null;\n\n const ids = new Set(parsed.map((n) => n.id));\n const nodes: DefinitionGraphNode[] = parsed.map(({ id, type, inputs, params }) => {\n const deps = nodeDependencies({ id, inputs, params }, ids);\n deps.delete(id);\n const depList = [...deps];\n // The pre-run graph carries each node's spec (model / aspect / size / expect …) +\n // its wired props so a freshly-scaffolded creative shows full node detail in the\n // inspector BEFORE the first billed run, matching what a run later records.\n const paramsPreview = nodeParamsPreview(params, depList);\n const base: DefinitionGraphNode = { id, type };\n if (depList.length > 0) base.deps = depList;\n if (paramsPreview) base.paramsPreview = paramsPreview;\n const inputPreviewUrl = nodeInputPreviewUrl(type, params);\n if (inputPreviewUrl) base.inputPreviewUrl = inputPreviewUrl;\n return base;\n });\n\n const rawOutput = (canvas as { output?: unknown })?.output;\n const outNode = (rawOutput as { node?: unknown })?.node;\n const outSlot = (rawOutput as { output?: unknown })?.output;\n const output =\n typeof outNode === \"string\" && typeof outSlot === \"string\" ? { node: outNode, output: outSlot } : undefined;\n\n return { nodes, output };\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -127,7 +127,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
127
127
|
landing: () => import("./landing-EP23XIYL.js").then((m) => m.landingCommand),
|
|
128
128
|
videos: () => import("./videos-DJHFHZI5.js").then((m) => m.videosCommand),
|
|
129
129
|
testimonials: () => import("./testimonials-NODH7BWK.js").then((m) => m.testimonialsCommand),
|
|
130
|
-
canvas: () => import("./canvas-
|
|
130
|
+
canvas: () => import("./canvas-RCRBTZKW.js").then((m) => m.canvasCommand),
|
|
131
131
|
tags: () => import("./tags-UE2S4UCB.js").then((m) => m.tagsCommand),
|
|
132
132
|
"tag-manager": () => import("./tag-manager-HG76EBNX.js").then((m) => m.tagManagerCommand),
|
|
133
133
|
chats: () => import("./chats-4GAFW3QN.js").then((m) => m.chatsCommand),
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-3UA4THEF.js";
|
|
8
8
|
import {
|
|
9
9
|
syncCreativeDefinitionBestEffort
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DRPSJE62.js";
|
|
11
11
|
import "./chunk-IMH2ZKKC.js";
|
|
12
12
|
import {
|
|
13
13
|
readAvatars
|
|
@@ -1545,4 +1545,4 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
1545
1545
|
export {
|
|
1546
1546
|
scaffoldAdCommand
|
|
1547
1547
|
};
|
|
1548
|
-
//# sourceMappingURL=scaffold-ad-
|
|
1548
|
+
//# sourceMappingURL=scaffold-ad-VKOXZENI.js.map
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "./chunk-3UA4THEF.js";
|
|
13
13
|
import {
|
|
14
14
|
syncCreativeDefinitionBestEffort
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-DRPSJE62.js";
|
|
16
16
|
import {
|
|
17
17
|
buildBackendClient
|
|
18
18
|
} from "./chunk-P6SFEIA5.js";
|
|
@@ -968,4 +968,4 @@ export {
|
|
|
968
968
|
uploadSourceAsReference,
|
|
969
969
|
validateScaffoldedStaticAd
|
|
970
970
|
};
|
|
971
|
-
//# sourceMappingURL=scaffold-static-ad-
|
|
971
|
+
//# sourceMappingURL=scaffold-static-ad-CMYP2ZZ6.js.map
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./chunk-3UA4THEF.js";
|
|
17
17
|
import {
|
|
18
18
|
syncCreativeDefinitionBestEffort
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-DRPSJE62.js";
|
|
20
20
|
import "./chunk-IMH2ZKKC.js";
|
|
21
21
|
import "./chunk-SS445HDO.js";
|
|
22
22
|
import {
|
|
@@ -869,4 +869,4 @@ export {
|
|
|
869
869
|
resolveVideoModel,
|
|
870
870
|
scaffoldVideoCommand
|
|
871
871
|
};
|
|
872
|
-
//# sourceMappingURL=scaffold-video-
|
|
872
|
+
//# sourceMappingURL=scaffold-video-AKA5LMEI.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildDefinitionSyncPayload,
|
|
3
|
+
readDefinitionBody,
|
|
3
4
|
readDefinitionSeriesPlace,
|
|
4
5
|
readDefinitionSourceFields,
|
|
5
6
|
readDefinitionStrategyFields,
|
|
@@ -7,7 +8,7 @@ import {
|
|
|
7
8
|
resolveCanvasPath,
|
|
8
9
|
syncCreativeDefinitionBestEffort,
|
|
9
10
|
syncDefinitionCommand
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-DRPSJE62.js";
|
|
11
12
|
import "./chunk-IMH2ZKKC.js";
|
|
12
13
|
import "./chunk-MSKB6I5E.js";
|
|
13
14
|
import "./chunk-WOLE67OJ.js";
|
|
@@ -41,6 +42,7 @@ import "./chunk-IWLWY2UD.js";
|
|
|
41
42
|
import "./chunk-YQSBHWOI.js";
|
|
42
43
|
export {
|
|
43
44
|
buildDefinitionSyncPayload,
|
|
45
|
+
readDefinitionBody,
|
|
44
46
|
readDefinitionSeriesPlace,
|
|
45
47
|
readDefinitionSourceFields,
|
|
46
48
|
readDefinitionStrategyFields,
|
|
@@ -49,4 +51,4 @@ export {
|
|
|
49
51
|
syncCreativeDefinitionBestEffort,
|
|
50
52
|
syncDefinitionCommand
|
|
51
53
|
};
|
|
52
|
-
//# sourceMappingURL=sync-definition-
|
|
54
|
+
//# sourceMappingURL=sync-definition-GKF2KKFC.js.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/canvas/sync-definition.ts","../src/commands/canvas/definition-graph.ts"],"sourcesContent":["import { readdir, readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { parseDocument } from \"yaml\";\nimport { defineCommand } from \"citty\";\nimport { BackendClient } from \"../../engine/index.ts\";\nimport { requireCredentialsFromEnv } from \"../../engine/client/env.ts\";\nimport {\n type DefinitionSeriesPlace,\n type DefinitionSourceFields,\n type DefinitionStrategyFields,\n parseDefinitionSeriesPlace,\n parseDefinitionSourceFields,\n parseDefinitionStrategyFields,\n} from \"./creative-definition.ts\";\nimport { canvasToDefinitionGraph } from \"./definition-graph.ts\";\nimport { prepareCanvas } from \"./prepare-canvas.ts\";\nimport { creativeSlugFromCanvasPath } from \"./run-record.ts\";\n\n/** Why the creative exists (`_definition.md` idea / message / evidence / angle / persona / awareness / hypothesis). */\ntype DefinitionSyncInput = Omit<DefinitionStrategyFields, \"seriesLead\" | \"language\"> & {\n slug: string;\n /** The series lead and language: omitted when not read (the backend keeps them), null when the definition names none (cleared). */\n seriesLead?: string | null;\n language?: string | null;\n /** Omitted on an edit re-sync so the backend keeps the creative's existing title. */\n title?: string;\n platform?: string;\n formats?: string[];\n sourceReferenceUrl?: string;\n /** The library ad it adapted (`_definition.md` sourceAdId / sourcePlatform / sourceAdvertiserId). */\n sourceAdId?: string;\n sourcePlatform?: string;\n sourceAdvertiserId?: string;\n canvas: unknown;\n /** The creative's source fingerprint (`computeSourceSha`): powers the \"edited since last render\" staleness signal. */\n sourceSha?: string;\n};\n\n/**\n * The body `POST /api/creatives/definition` receives. Absent fields are left out rather than\n * sent as undefined so the backend's `?? existing` keeps what an earlier sync recorded.\n */\nexport function buildDefinitionSyncPayload(\n input: DefinitionSyncInput,\n graph: unknown,\n chatId: string,\n): Record<string, unknown> {\n const { canvas: _canvas, ...fields } = input;\n return Object.fromEntries([\n ...Object.entries(fields).filter(([, v]) => v !== undefined),\n [\"graph\", graph],\n [\"chatId\", chatId],\n ]);\n}\n\n/** The library ids the creative's `_definition.md` names, read from beside its canvas; empty when there is none. */\nexport async function readDefinitionSourceFields(canvasPath: string): Promise<DefinitionSourceFields> {\n try {\n return parseDefinitionSourceFields(await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\"));\n } catch {\n return {};\n }\n}\n\n/** The creative's title, read from the `_definition.md` beside its canvas; undefined when it names none. */\nexport async function readDefinitionTitle(canvasPath: string): Promise<string | undefined> {\n try {\n const raw = await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\");\n const match = raw.match(/^---\\r?\\n([\\s\\S]*?\\r?\\n)?---/);\n const doc = match ? parseDocument(match[1] ?? \"\") : null;\n const title = doc && doc.errors.length === 0 ? doc.get(\"title\") : undefined;\n return typeof title === \"string\" && title.trim() ? title.trim() : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The creative's strategy, read from the `_definition.md` beside its canvas; empty when there is none. */\nexport async function readDefinitionStrategyFields(canvasPath: string): Promise<DefinitionStrategyFields> {\n try {\n return parseDefinitionStrategyFields(await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\"));\n } catch {\n return {};\n }\n}\n\n/** The series place the `_definition.md` beside the canvas names; empty when it cannot be read, so nothing is cleared. */\nexport async function readDefinitionSeriesPlace(canvasPath: string): Promise<DefinitionSeriesPlace | Record<never, never>> {\n try {\n return (\n parseDefinitionSeriesPlace(await readFile(path.join(path.dirname(canvasPath), \"_definition.md\"), \"utf8\")) ?? {}\n );\n } catch {\n return {};\n }\n}\n\n/**\n * Push a creative's parsed node-graph to the backend so the dashboard can draw\n * its workflow (and its wired input thumbnails) BEFORE — and BETWEEN — billed\n * runs. Best-effort: a missing chat id, missing creds, an unparseable canvas, or\n * a failed POST warns and returns — it must never fail the caller (a scaffold\n * that just did the real work, or a watcher reacting to an edit).\n */\nexport async function syncCreativeDefinitionBestEffort(input: DefinitionSyncInput): Promise<void> {\n const chatId = process.env.BAKER_CHAT_ID;\n if (!chatId) return; // Not chat-attached — nothing to preview against.\n const graph = canvasToDefinitionGraph(input.canvas);\n if (!graph || graph.nodes.length === 0) return;\n try {\n const creds = requireCredentialsFromEnv();\n const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });\n await client.syncCreativeDefinition(buildDefinitionSyncPayload(input, graph, chatId));\n process.stdout.write(`[definition] synced workflow graph (${graph.nodes.length} nodes) — view it in the dashboard\\n`);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n process.stderr.write(`[warn] workflow graph not synced (${msg})\\n`);\n }\n}\n\n/**\n * Resolve the canvas file backing a creative from ANY path under its dir — the\n * `{slug}.canvas.json` itself, its sibling `prompt.json`, or the creative\n * directory. Lets a file watcher hand us whichever file changed and still find\n * the one canvas to project. Prefers `{slug}.canvas.json`, else the sole\n * `*.canvas.json` in the directory; null when there is no single canvas.\n */\nexport async function resolveCanvasPath(inputPath: string): Promise<string | null> {\n const resolved = path.resolve(inputPath);\n let dir = resolved;\n try {\n if ((await stat(resolved)).isFile()) {\n if (resolved.endsWith(\".canvas.json\")) return resolved;\n dir = path.dirname(resolved);\n }\n } catch {\n // A deleted/renamed file: fall back to treating the path as its directory.\n dir = resolved.endsWith(\".canvas.json\") ? path.dirname(resolved) : resolved;\n }\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return null;\n }\n const canvases = entries.filter((name) => name.endsWith(\".canvas.json\"));\n const slug = creativeSlugFromCanvasPath(`${dir}/x/`);\n const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : undefined) ?? canvases[0];\n return chosen ? path.join(dir, chosen) : null;\n}\n\nexport const syncDefinitionCommand = defineCommand({\n meta: {\n name: \"sync-definition\",\n description:\n \"Push a creative's current node-graph + input thumbnails to the dashboard without rendering. The graph has the creative's blueprint applied (prompt.json and scenes/*.json edits), exactly as `run` would render it, without changing any file. Runs automatically when you edit a creative's canvas.json or prompt.json.\",\n },\n args: {\n file: {\n type: \"positional\",\n required: true,\n description: \"A path under src/creatives/<slug>/ (the .canvas.json, its prompt.json, or the folder)\",\n },\n title: {\n type: \"boolean\",\n description:\n \"Also record the title its _definition.md gives. Only for a creative just made from another (a series ad, a translation), never on an edit re-sync.\",\n },\n },\n async run({ args }) {\n const canvasPath = await resolveCanvasPath(String(args.file));\n if (!canvasPath) {\n // Nothing to sync (not a creative path, or no canvas yet) — a no-op, not an error.\n return;\n }\n const slug = creativeSlugFromCanvasPath(canvasPath);\n if (!slug) return;\n // The same preflight `run` uses, so the dashboard draws the graph that will render\n // (the scene and copy projections included), in the file's canvas-relative spelling,\n // never this machine's absolute paths. Read-only: the watcher runs this over every\n // creative on every edit, and must never rewrite a creative nobody touched. The\n // fingerprint is taken over the projected graph, as `run` and `baker canvas stale` take it.\n let canvas: unknown;\n let sourceSha: string | undefined;\n try {\n const prepared = await prepareCanvas(canvasPath, { write: false });\n canvas = prepared.fileGraph;\n sourceSha = prepared.sourceSha;\n } catch {\n // Mid-edit invalid JSON (canvas, prompt.json or a scene): skip this tick; the next\n // save re-fires the watcher.\n return;\n }\n // No title/platform/formats: an edit re-sync must never rename or re-classify\n // the creative — the backend preserves what the scaffold already set. The library\n // ids are re-read from `_definition.md`, so an agent that adds or fixes them after\n // the scaffold still gets them recorded without waiting for Publish — and so is the\n // creative's strategy, the moment the agent writes it.\n const title = args.title ? await readDefinitionTitle(canvasPath) : undefined;\n await syncCreativeDefinitionBestEffort({\n slug,\n canvas,\n sourceSha,\n ...(title ? { title } : {}),\n ...(await readDefinitionSourceFields(canvasPath)),\n ...(await readDefinitionStrategyFields(canvasPath)),\n ...(await readDefinitionSeriesPlace(canvasPath)),\n });\n },\n});\n","import { nodeDependencies } from \"../../engine/engine/graph.ts\";\nimport { nodeParamsPreview } from \"./node-preview.ts\";\n\nexport type DefinitionGraphNode = {\n id: string;\n type: string;\n deps?: string[];\n paramsPreview?: string;\n /**\n * A directly-viewable input image/video for the node BEFORE any run — today\n * an `ingest` node's `source:\"url\"` src. Lets the dashboard show a real\n * thumbnail (a swapped logo/hero) the moment the canvas changes, with no\n * render. Path-source ingests carry no preview (their bytes aren't uploaded\n * outside a run); the graph still shows the `path:` line via paramsPreview.\n */\n inputPreviewUrl?: string;\n};\nexport type CanvasDefinitionGraph = {\n nodes: DefinitionGraphNode[];\n output?: { node: string; output: string };\n};\n\n// Keep the synced blueprint bounded — matches the backend's cap.\nconst MAX_NODES = 300;\n\n// Only image ingests become a pre-run thumbnail: absent `expect` defaults to an\n// image, and a still renders unambiguously in the graph card. Video/font/json\n// ingests carry no preview (a video would need its own player wiring) — their\n// `url:`/`path:` line still shows via paramsPreview.\nconst PREVIEWABLE_INGEST_EXPECTS = new Set([\"image\"]);\n\n/**\n * A directly-viewable input URL for a node's pre-run thumbnail: an `ingest`\n * node pulling an image from a `source:\"url\"` src. Everything else (path-source\n * ingests, generators, non-image expects) returns undefined — only image bytes\n * the dashboard can already fetch become a thumbnail without a run.\n */\nfunction nodeInputPreviewUrl(type: string, params: unknown): string | undefined {\n if (type !== \"ingest\" || !params || typeof params !== \"object\") return undefined;\n const p = params as { source?: unknown; url?: unknown; expect?: unknown };\n if (p.source !== \"url\" || typeof p.url !== \"string\") return undefined;\n if (!/^https?:\\/\\//i.test(p.url)) return undefined;\n if (typeof p.expect === \"string\" && !PREVIEWABLE_INGEST_EXPECTS.has(p.expect)) return undefined;\n return p.url;\n}\n\n/**\n * Parse a canvas JSON into the compact node-graph the dashboard draws before any\n * run — shares the engine's `nodeDependencies` ($ref edges from inputs AND params) so\n * the pre-run blueprint matches the eventual run graph. Best-effort and defensive:\n * a not-yet-valid canvas (mid-edit) returns whatever nodes it can read, or null\n * if there are none, so a caller can skip the sync without failing the command.\n */\nexport function canvasToDefinitionGraph(canvas: unknown): CanvasDefinitionGraph | null {\n const rawNodes = (canvas as { nodes?: unknown })?.nodes;\n if (!Array.isArray(rawNodes)) return null;\n\n const parsed: Array<{ id: string; type: string; inputs: unknown; params: unknown }> = [];\n for (const raw of rawNodes) {\n const id = (raw as { id?: unknown })?.id;\n const type = (raw as { type?: unknown })?.type;\n if (typeof id !== \"string\" || typeof type !== \"string\") continue;\n parsed.push({ id, type, inputs: (raw as { inputs?: unknown }).inputs, params: (raw as { params?: unknown }).params });\n if (parsed.length >= MAX_NODES) break;\n }\n if (parsed.length === 0) return null;\n\n const ids = new Set(parsed.map((n) => n.id));\n const nodes: DefinitionGraphNode[] = parsed.map(({ id, type, inputs, params }) => {\n const deps = nodeDependencies({ id, inputs, params }, ids);\n deps.delete(id);\n const depList = [...deps];\n // The pre-run graph carries each node's spec (model / aspect / size / expect …) +\n // its wired props so a freshly-scaffolded creative shows full node detail in the\n // inspector BEFORE the first billed run, matching what a run later records.\n const paramsPreview = nodeParamsPreview(params, depList);\n const base: DefinitionGraphNode = { id, type };\n if (depList.length > 0) base.deps = depList;\n if (paramsPreview) base.paramsPreview = paramsPreview;\n const inputPreviewUrl = nodeInputPreviewUrl(type, params);\n if (inputPreviewUrl) base.inputPreviewUrl = inputPreviewUrl;\n return base;\n });\n\n const rawOutput = (canvas as { output?: unknown })?.output;\n const outNode = (rawOutput as { node?: unknown })?.node;\n const outSlot = (rawOutput as { output?: unknown })?.output;\n const output =\n typeof outNode === \"string\" && typeof outSlot === \"string\" ? { node: outNode, output: outSlot } : undefined;\n\n return { nodes, output };\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|