@koda-sl/baker-cli 0.124.0-dev.8e4328629 → 0.124.0-dev.efe965ce2
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 +62 -1
- package/dist/{chunk-Q3K5TXC6.js → chunk-TKL2CJ6G.js} +100 -14
- package/dist/chunk-TKL2CJ6G.js.map +1 -0
- package/dist/cli.js +2526 -1513
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.d.ts +40 -1
- package/dist/engine/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-Q3K5TXC6.js.map +0 -1
package/README.md
CHANGED
|
@@ -2132,6 +2132,8 @@ Auto-ingests with prefetched bytes (no double-fetch). The screenshot bytes thems
|
|
|
2132
2132
|
|
|
2133
2133
|
ScreenshotOne caches captures for 30 days, so re-shotting the same URL within that window returns the cached capture.
|
|
2134
2134
|
|
|
2135
|
+
If the target page is unreachable or returns a non-2xx status (e.g. a 404 path or a login wall), the command fails with an actionable `VALIDATION_ERROR` naming the page and the status it returned, plus a `fix` hint — verify the URL and retry, or continue without the screenshot. It no longer surfaces a generic "Internal server error".
|
|
2136
|
+
|
|
2135
2137
|
**Flags:**
|
|
2136
2138
|
|
|
2137
2139
|
| Flag | Description |
|
|
@@ -2502,6 +2504,46 @@ Rules:
|
|
|
2502
2504
|
|
|
2503
2505
|
---
|
|
2504
2506
|
|
|
2507
|
+
### Marketing Tags (`baker tags`)
|
|
2508
|
+
|
|
2509
|
+
Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, PostHog, …) — production tags overlaid with the changes staged in this chat.
|
|
2510
|
+
|
|
2511
|
+
**The CLI is read-only.** Every tag change (create, edit, delete) goes through the `request_tag_input` tool (`baker_ui` MCP server): the agent proposes one or more changes — each becomes a tab in one blocking approval form — pre-filling the non-secret fields it knows; the user reviews, edits, fills secret fields, and approves or skips each tab. Approved changes stage on the chat and apply when the chat is published; discarding the chat drops them.
|
|
2512
|
+
|
|
2513
|
+
`BAKER_CHAT_ID` must be set.
|
|
2514
|
+
|
|
2515
|
+
```bash
|
|
2516
|
+
baker tags list # effective view: production + staged, full readable config + secret status
|
|
2517
|
+
baker tags draft # review the staged changes awaiting publish
|
|
2518
|
+
```
|
|
2519
|
+
|
|
2520
|
+
Notes:
|
|
2521
|
+
|
|
2522
|
+
- **`list` prints each tag's full readable config.** Every non-secret field is shown in full under the tag — the Clarity `projectId`, GA4 `measurementId`, the entire custom `code` snippet, etc. — so the agent reuses a stored value to pre-fill a follow-up change instead of asking the user for something already installed. Secrets are never among these fields. (`--json` emits the same data as the raw envelope.)
|
|
2523
|
+
- **Secrets never travel through this CLI or the chat.** Secret fields (`accessToken`, `apiSecret`, `authorizationToken`, `apiKey`, `conversionToken`, `oauthProviderId`) are entered only in the dashboard's secure tag form and flow straight into the staged draft; tool responses only ever name which secret fields are set/pending.
|
|
2524
|
+
- Staged creates get a server-generated `tag_temp_*` ref (returned in the tool result and printed by `list`). Use it (or a real tag id) as flow side-effect `tagIds` — the published tag keeps resolving under the temp ref.
|
|
2525
|
+
- Proposing a delete on a `tag_temp_*` ref drops the staged create instead.
|
|
2526
|
+
- Single-instance types (`code`, `posthog`, `datafast`) reject a second instance against the chat's effective view.
|
|
2527
|
+
- Conflicts at publish (tag deleted in the dashboard, config invalid) skip the op with a recorded reason — they never block the publish.
|
|
2528
|
+
|
|
2529
|
+
### Forms / Flows (`baker flows`)
|
|
2530
|
+
|
|
2531
|
+
Read this workspace's Forms (flows) and the configuration status of each one's **confidential fields** — side-effect connection secrets, OAuth connections, and third-party field definitions (HubSpot, Calendly, HighLevel, SavvyCal). Reads the local `_data.json` files; no secret values are ever decrypted or printed.
|
|
2532
|
+
|
|
2533
|
+
**The CLI is read-only.** Author a Form's structure (nodes, conditions, side effects, field mappings) by editing its `_data.json` with the flow-builder skill. Configure the confidential fields it reports as `[missing]` / `[needs connection]` / `not selected` through the `request_flow_input` tool (`baker_ui` MCP server): the agent authors the surrounding structure, references it by `nodeId`/`sideEffectId`, and the user enters secrets, connects/picks OAuth accounts, and picks third-party forms in the dashboard. Those values are written (encrypted, where a secret) into the Form on the chat's branch and go live when the chat is published.
|
|
2534
|
+
|
|
2535
|
+
```bash
|
|
2536
|
+
baker flows list # every Form + how many confidential fields still need setup
|
|
2537
|
+
baker flows show contact # one Form's confidential fields and their status
|
|
2538
|
+
baker flows show contact --full # include the full flow tree (secret values redacted)
|
|
2539
|
+
```
|
|
2540
|
+
|
|
2541
|
+
Notes:
|
|
2542
|
+
|
|
2543
|
+
- **Secrets never travel through this CLI or the chat.** `show` reports only whether each field is configured (`[set]`/`[missing]`, `[connected]`/`[needs connection]`, selected/not selected) — never a value. Configured secrets appear only as `[configured]` ciphertext markers.
|
|
2544
|
+
- Confidential side-effect types: `httpWebhook`, `zapier`, `crmble` (typed credentials); `pipedrive`, `googleSpreadsheet`, `hubspotForm`, `goHighlevelContact` (OAuth connection + resource); `email`. Widget node types needing a resource pick: `calendly`, `savvycal`, `hubspot`, `hubspotMeeting`, `highlevel`, `highlevelForm`.
|
|
2545
|
+
- `request_flow_input` changes stage on the chat's branch and apply when the chat is published; discarding the chat drops them.
|
|
2546
|
+
|
|
2505
2547
|
### Action Items (`baker actions`)
|
|
2506
2548
|
|
|
2507
2549
|
Manage action items for the current chat. Most write operations stage on the chat's draft and apply atomically when the chat is published. Claim/release run live so other chats can see who's working on what.
|
|
@@ -3945,6 +3987,7 @@ Validate, then execute the graph. Blocks until done. Logs one line per node. Ret
|
|
|
3945
3987
|
| `--cache-policy <policy>` | `read_write` | `read_write`, `bypass`, or `read_only`. |
|
|
3946
3988
|
| `--concurrency <n>` | `5` (or `BAKER_CANVAS_CONCURRENCY`) | Max nodes executing at once within a layer. |
|
|
3947
3989
|
| `--remote-cache <on\|off>` | `on` (or `BAKER_CANVAS_REMOTE_CACHE`) | Company-scoped remote cache + durable asset persistence. |
|
|
3990
|
+
| `--max-credits <n>` | uncapped (or `BAKER_CANVAS_MAX_CREDITS`) | Credit ceiling: aborts before billing when the estimate exceeds it, and at layer boundaries once actual spend does. Completed nodes stay cached, so retrying with a higher cap loses nothing. |
|
|
3948
3991
|
| `--no-record` | records | Skip posting the durable run-history record (and its live progress). |
|
|
3949
3992
|
|
|
3950
3993
|
**Run history streams live.** The run posts its plan (every node + its dependency edges) the moment validation passes, then re-posts a progress snapshot as each node starts and settles — the dashboard's creative workflow graph shows nodes flipping pending → running → done in real time, with each node's outputs attached as they land. A failed run keeps its per-node trail (what completed, what died). All best-effort: an unreachable backend never changes the run's outcome.
|
|
@@ -4069,7 +4112,25 @@ baker canvas run ./static-ad.canvas.json
|
|
|
4069
4112
|
|
|
4070
4113
|
Scaffolding runs (and bills) the two vision passes; **running** the result generates a billed image. `baker canvas validate` does not check that the `[TODO]` paths exist — supply the real files before `run`.
|
|
4071
4114
|
|
|
4072
|
-
**Resuming an interrupted run.** A long `baker canvas run` (multi-clip video) that is killed mid-render — session end, sandbox pause — leaves a marker under the outputs dir. The next `baker canvas run` of the same canvas automatically **resumes** that run: it reuses the run id so still-running billed jobs re-attach instead of being abandoned and re-billed, and completed nodes come from the cache. A clean completion (or a handled failure) clears the marker, so a normal re-run starts a fresh generation. Force a new run with `--fresh`, or pin a specific run with `--run-id <id
|
|
4115
|
+
**Resuming an interrupted run.** A long `baker canvas run` (multi-clip video) that is killed mid-render — session end, sandbox pause — leaves a marker under the outputs dir. The next `baker canvas run` of the same canvas automatically **resumes** that run: it reuses the run id so still-running billed jobs re-attach instead of being abandoned and re-billed, and completed nodes come from the cache. Resume also works from a **different workspace or a fresh sandbox**: when no local marker survives, the run history is consulted and an interrupted (or stale) run of the exact same canvas is adopted automatically. Ctrl-C / SIGTERM aborts gracefully — no new nodes dispatch, a resumable snapshot is flushed, and the marker survives. A clean completion (or a handled failure) clears the marker, so a normal re-run starts a fresh generation. Force a new run with `--fresh`, or pin a specific run with `--run-id <id>` (also the escape hatch to adopt a run that is reported as concurrently live). Independent same-layer nodes (e.g. video clips) fan out in parallel up to `--parallel`/`--concurrency` (default 8; env `BAKER_CANVAS_CONCURRENCY`).
|
|
4116
|
+
|
|
4117
|
+
#### `baker canvas rerun <slug> [flags]`
|
|
4118
|
+
|
|
4119
|
+
Re-run a creative's latest recorded canvas **from run history** — no local files needed. Every `canvas run` of a creative uploads a portable snapshot (the canvas JSON plus the local files it ingests: prompt blueprints, composition dirs, reference images) to durable storage and records it on the run. `rerun` restores those files into `src/creatives/<slug>/` (sha-verified; files already matching are left untouched) and then executes the normal run flow — so an interrupted run **resumes** (in-flight jobs re-attach) and a completed one re-renders from the cache at zero credits.
|
|
4120
|
+
|
|
4121
|
+
| Flag | Default | Meaning |
|
|
4122
|
+
| --- | --- | --- |
|
|
4123
|
+
| `--force-remote` | off | Overwrite local files whose content differs from the snapshot (otherwise a conflict aborts with the differing paths). |
|
|
4124
|
+
| `--fresh` | off | Start a new run id instead of resuming an interrupted one. |
|
|
4125
|
+
| `--regenerate <ids>` | — | Same as `canvas run --regenerate`. |
|
|
4126
|
+
| `--concurrency <n>` | — | Same as `canvas run --concurrency`. |
|
|
4127
|
+
|
|
4128
|
+
```bash
|
|
4129
|
+
baker canvas rerun spring-offer-4x5
|
|
4130
|
+
baker canvas rerun spring-offer-4x5 --force-remote --regenerate gen_4x5
|
|
4131
|
+
```
|
|
4132
|
+
|
|
4133
|
+
Use it when a creative was built in another conversation (or its sandbox is gone) and you need to continue or re-render it here. Files the snapshot could not include (missing at run time, or oversized) are listed as warnings — supply those locally only if the run actually needs to regenerate the nodes that read them.
|
|
4073
4134
|
|
|
4074
4135
|
#### `baker canvas inspect <run_id> [--thumbnails]`
|
|
4075
4136
|
|
|
@@ -812,10 +812,10 @@ var BackendClient = class {
|
|
|
812
812
|
await sleep(pollInterval(attempt));
|
|
813
813
|
}
|
|
814
814
|
}
|
|
815
|
-
presignAssetUpload(sha256, mime, signal) {
|
|
815
|
+
presignAssetUpload(sha256, mime, signal, purpose) {
|
|
816
816
|
return this.http.postJson(
|
|
817
817
|
"/api/canvas/assets/presign",
|
|
818
|
-
{ sha256, mime },
|
|
818
|
+
{ sha256, mime, purpose },
|
|
819
819
|
signal
|
|
820
820
|
);
|
|
821
821
|
}
|
|
@@ -840,6 +840,39 @@ var BackendClient = class {
|
|
|
840
840
|
async recordRun(payload, signal) {
|
|
841
841
|
await this.http.postJson("/api/canvas/runs", payload, signal);
|
|
842
842
|
}
|
|
843
|
+
/**
|
|
844
|
+
* Resumable-run lookup — GET /api/canvas/runs/active. Returns the newest
|
|
845
|
+
* still-running/interrupted run for the creative (pinned to the exact canvas
|
|
846
|
+
* sha), so a fresh sandbox can adopt its run id and re-attach billed
|
|
847
|
+
* in-flight jobs. Null on no active run — or an older backend without the
|
|
848
|
+
* route (both 404).
|
|
849
|
+
*/
|
|
850
|
+
async getActiveRun(creativeSlug, canvasSha, signal) {
|
|
851
|
+
const params = new URLSearchParams({ slug: creativeSlug });
|
|
852
|
+
if (canvasSha) params.set("sha", canvasSha);
|
|
853
|
+
try {
|
|
854
|
+
const res = await this.http.getJson(`/api/canvas/runs/active?${params}`, signal);
|
|
855
|
+
return res.run;
|
|
856
|
+
} catch (e) {
|
|
857
|
+
if (e instanceof BackendHttpError && "status" in e.detail && e.detail.status === 404) return null;
|
|
858
|
+
throw e;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Portable-rerun lookup — GET /api/canvas/runs/latest. The newest run for
|
|
863
|
+
* the creative that recorded a canvas snapshot manifest; null when none
|
|
864
|
+
* exists (or the backend predates the route — both 404).
|
|
865
|
+
*/
|
|
866
|
+
async getLatestSnapshotRun(creativeSlug, signal) {
|
|
867
|
+
const params = new URLSearchParams({ slug: creativeSlug });
|
|
868
|
+
try {
|
|
869
|
+
const res = await this.http.getJson(`/api/canvas/runs/latest?${params}`, signal);
|
|
870
|
+
return res.run;
|
|
871
|
+
} catch (e) {
|
|
872
|
+
if (e instanceof BackendHttpError && "status" in e.detail && e.detail.status === 404) return null;
|
|
873
|
+
throw e;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
843
876
|
/**
|
|
844
877
|
* Chat-scoped blueprint sync — POST /api/creatives/definition. Lets the
|
|
845
878
|
* dashboard draw a scaffolded creative's workflow graph BEFORE the first run.
|
|
@@ -907,6 +940,14 @@ var NodeExecutionError = class extends CanvasError {
|
|
|
907
940
|
this.cause = cause;
|
|
908
941
|
}
|
|
909
942
|
};
|
|
943
|
+
var RunAbortedError = class extends CanvasError {
|
|
944
|
+
reason;
|
|
945
|
+
constructor(reason, message2) {
|
|
946
|
+
super(message2);
|
|
947
|
+
this.name = "RunAbortedError";
|
|
948
|
+
this.reason = reason;
|
|
949
|
+
}
|
|
950
|
+
};
|
|
910
951
|
var LayerExecutionError = class extends CanvasError {
|
|
911
952
|
failures;
|
|
912
953
|
constructor(failures) {
|
|
@@ -1732,18 +1773,43 @@ function message(e) {
|
|
|
1732
1773
|
}
|
|
1733
1774
|
|
|
1734
1775
|
// src/engine/nodes/remote/upload.ts
|
|
1776
|
+
var PUT_MAX_ATTEMPTS = 4;
|
|
1777
|
+
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1735
1778
|
async function presignAndPut(args) {
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1779
|
+
let lastFailure = null;
|
|
1780
|
+
for (let attempt = 0; attempt < PUT_MAX_ATTEMPTS; attempt++) {
|
|
1781
|
+
if (args.ctx.signal?.aborted) break;
|
|
1782
|
+
if (attempt > 0) await sleep2(500 * 2 ** (attempt - 1) * (1 + Math.random() * 0.25));
|
|
1783
|
+
const result = await attemptPresignedPut(args);
|
|
1784
|
+
if (result.ok) return result.url;
|
|
1785
|
+
lastFailure = result.failure;
|
|
1786
|
+
if (!result.retryable) break;
|
|
1787
|
+
}
|
|
1788
|
+
throw lastFailure ?? new Error("upload: aborted before the PUT could start");
|
|
1789
|
+
}
|
|
1790
|
+
async function attemptPresignedPut(args) {
|
|
1791
|
+
try {
|
|
1792
|
+
const { putUrl, publicUrl } = await args.ctx.client.presignAssetUpload(args.sha256, args.mime, args.ctx.signal);
|
|
1793
|
+
const putRes = await fetch(putUrl, {
|
|
1794
|
+
method: "PUT",
|
|
1795
|
+
body: new Uint8Array(args.bytes),
|
|
1796
|
+
headers: { "Content-Type": args.mime },
|
|
1797
|
+
signal: args.ctx.signal
|
|
1798
|
+
});
|
|
1799
|
+
if (putRes.ok) return { ok: true, url: publicUrl };
|
|
1800
|
+
return {
|
|
1801
|
+
ok: false,
|
|
1802
|
+
failure: new Error(`upload: presigned PUT failed ${putRes.status} ${putRes.statusText}`),
|
|
1803
|
+
// Only transient statuses warrant a replay — a 400/403 fails identically every attempt.
|
|
1804
|
+
retryable: putRes.status >= 500 || putRes.status === 429 || putRes.status === 408
|
|
1805
|
+
};
|
|
1806
|
+
} catch (e) {
|
|
1807
|
+
return {
|
|
1808
|
+
ok: false,
|
|
1809
|
+
failure: e instanceof Error ? e : new Error(String(e)),
|
|
1810
|
+
retryable: args.ctx.signal?.aborted !== true
|
|
1811
|
+
};
|
|
1745
1812
|
}
|
|
1746
|
-
return publicUrl;
|
|
1747
1813
|
}
|
|
1748
1814
|
async function ensureUploaded(ref, ctx) {
|
|
1749
1815
|
if (ref.url) return ref;
|
|
@@ -3065,6 +3131,12 @@ var Engine = class {
|
|
|
3065
3131
|
async run(input, opts = {}) {
|
|
3066
3132
|
const validation = await this.validateDeep(input);
|
|
3067
3133
|
if (!validation.ok) throw new ValidationError(validation.issues);
|
|
3134
|
+
if (opts.max_credits !== void 0 && validation.estimatedCredits > opts.max_credits) {
|
|
3135
|
+
throw new RunAbortedError(
|
|
3136
|
+
"cost_cap",
|
|
3137
|
+
`estimated ${validation.estimatedCredits} credits exceeds the ${opts.max_credits}-credit cap \u2014 nothing was billed; raise --max-credits or shrink the canvas`
|
|
3138
|
+
);
|
|
3139
|
+
}
|
|
3068
3140
|
const canvas = validation.canvas;
|
|
3069
3141
|
const runId = opts.run_id ?? `r_${ulid()}`;
|
|
3070
3142
|
const writer = new OutputWriter({ outputsDir: this.outputsDir, runId });
|
|
@@ -3115,7 +3187,17 @@ var Engine = class {
|
|
|
3115
3187
|
const layers = topologicalLayers(graph);
|
|
3116
3188
|
const limit = resolveConcurrency(opts.concurrency);
|
|
3117
3189
|
for (const layer of layers) {
|
|
3190
|
+
if (opts.signal?.aborted) throw new RunAbortedError("signal", "run aborted before layer dispatch");
|
|
3191
|
+
if (opts.max_credits !== void 0 && counters.totalCredits > opts.max_credits) {
|
|
3192
|
+
throw new RunAbortedError(
|
|
3193
|
+
"cost_cap",
|
|
3194
|
+
`spent ${counters.totalCredits} credits, over the ${opts.max_credits}-credit cap \u2014 completed nodes are cached; raise --max-credits to continue where this stopped`
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3118
3197
|
const settled = await mapWithConcurrency(layer, limit, (nodeId) => {
|
|
3198
|
+
if (opts.signal?.aborted) {
|
|
3199
|
+
return Promise.reject(new RunAbortedError("signal", "run aborted before node dispatch"));
|
|
3200
|
+
}
|
|
3119
3201
|
this.emitProgress(opts, { kind: "node_start", node_id: nodeId });
|
|
3120
3202
|
return this.executeOne(canvas, nodeId, outputs, runId, writer, opts, needsBytes.has(nodeId)).then((r) => {
|
|
3121
3203
|
if (r.cached) counters.cachedNodes++;
|
|
@@ -3138,10 +3220,12 @@ var Engine = class {
|
|
|
3138
3220
|
settled.forEach((result, i) => {
|
|
3139
3221
|
const nodeId = layer[i];
|
|
3140
3222
|
if (result.status === "rejected" && nodeId) {
|
|
3223
|
+
if (result.reason instanceof RunAbortedError) return;
|
|
3141
3224
|
failures.push({ nodeId, reason: result.reason });
|
|
3142
3225
|
this.emitProgress(opts, { kind: "node_failed", node_id: nodeId });
|
|
3143
3226
|
}
|
|
3144
3227
|
});
|
|
3228
|
+
if (opts.signal?.aborted) throw new RunAbortedError("signal", "run aborted by signal");
|
|
3145
3229
|
if (failures.length === 1 && failures[0]) throw failures[0].reason;
|
|
3146
3230
|
if (failures.length > 1) throw new LayerExecutionError(failures);
|
|
3147
3231
|
}
|
|
@@ -6929,7 +7013,9 @@ function createEngineFromEnv(opts = {}) {
|
|
|
6929
7013
|
}
|
|
6930
7014
|
|
|
6931
7015
|
export {
|
|
7016
|
+
BackendClient,
|
|
6932
7017
|
requireCredentialsFromEnv,
|
|
7018
|
+
RunAbortedError,
|
|
6933
7019
|
LayerExecutionError,
|
|
6934
7020
|
describeFailureReason,
|
|
6935
7021
|
SEEDANCE_DURATIONS,
|
|
@@ -6945,7 +7031,7 @@ export {
|
|
|
6945
7031
|
sha256Hex,
|
|
6946
7032
|
elementMentionKeywords,
|
|
6947
7033
|
toModelSafeImage,
|
|
6948
|
-
BackendClient2
|
|
7034
|
+
BackendClient2,
|
|
6949
7035
|
Engine2 as Engine,
|
|
6950
7036
|
LocalAssetStore2 as LocalAssetStore,
|
|
6951
7037
|
LocalCacheStore2 as LocalCacheStore,
|
|
@@ -6955,4 +7041,4 @@ export {
|
|
|
6955
7041
|
defaultRegistry,
|
|
6956
7042
|
createEngineFromEnv
|
|
6957
7043
|
};
|
|
6958
|
-
//# sourceMappingURL=chunk-
|
|
7044
|
+
//# sourceMappingURL=chunk-TKL2CJ6G.js.map
|