@koda-sl/baker-cli 0.153.0 → 0.154.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 CHANGED
@@ -2623,6 +2623,52 @@ baker history list --category ads --full # ad writes with raw metadata
2623
2623
 
2624
2624
  ---
2625
2625
 
2626
+ ### `baker hubspot forms list | view` · `baker hubspot meetings list | view`
2627
+
2628
+ Read-only view of the company's **connected HubSpot account** — the forms and meeting links (calendars) that live there, plus the settings inside HubSpot that decide how each behaves once placed in a Form. Fetched from HubSpot live on every call; nothing is cached.
2629
+
2630
+ The reason this exists: a form's **post-submit action** (inline thank-you vs redirect to a URL), its embed version, its consent mode, and a calendar's booking fields are *not* persisted onto the flow node when a form is picked. They cannot be read from `_data.json` or `baker flows show` — only from here.
2631
+
2632
+ ```bash
2633
+ baker hubspot forms list # every form: embed type + post-submit action
2634
+ baker hubspot forms list --redirecting-only # which forms redirect away on submit
2635
+ baker hubspot forms list --embed-type legacy # only legacy-embed forms
2636
+ baker hubspot forms view <formId> # fields, consent mode, full configuration
2637
+ baker hubspot forms view <formId> --full # + the raw HubSpot payload
2638
+ baker hubspot forms view <formId> --as-node # the form.external blob for a flow node
2639
+ baker hubspot meetings list # every calendar + whether booking redirects
2640
+ baker hubspot meetings view <slug> # booking-form fields for one calendar
2641
+ baker hubspot meetings view <slug> --as-node # the form.external blob for a flow node
2642
+ ```
2643
+
2644
+ **`forms list` flags:** `--search <text>` (name substring), `--redirecting-only`, `--embed-type legacy|v4|unknown`. Rows carry only what it takes to choose; `portalId` is returned once on `data`, not per row.
2645
+ **`forms view`:** positional `<formId>`; `--full` adds the untouched HubSpot payload; `--as-node` returns the resource blob instead of the readable view.
2646
+ **`meetings list` flags:** `--search <text>`, `--redirecting-only`.
2647
+ **`meetings view`:** positional `<slug>` — the only reliable source for a calendar's booking fields (HubSpot omits `customParams` from the list endpoint, so `fieldCount: null` there means unknown, not zero); `--as-node` as above.
2648
+
2649
+ **`--as-node`** returns exactly the object a flow node's `form.external` expects, so an agent can wire a HubSpot step by editing the flow's `_data.json` instead of interrupting the user with the `request_flow_input` approval form. It includes two fields the dashboard picker omits and the renderer needs: `configuration.postSubmitAction` (drives post-submit redirect suppression) and `region` (selects the v4 embed host — without it a v4 form on a eu1/ap1 portal loads the wrong script host).
2650
+
2651
+ **Response:**
2652
+
2653
+ ```json
2654
+ {
2655
+ "ok": true,
2656
+ "data": {
2657
+ "portalId": 1234567,
2658
+ "forms": [
2659
+ { "id": "…", "name": "Demo request", "embedType": "v4", "fieldCount": 4, "postSubmitAction": { "type": "redirect_url", "value": "https://example.com/thanks" } }
2660
+ ]
2661
+ },
2662
+ "meta": { "count": 1 }
2663
+ }
2664
+ ```
2665
+
2666
+ No write surface against HubSpot itself — a form's fields, redirect, or consent text are changed inside HubSpot. Wiring one into a Form step is done with `--as-node` (above), not `request_flow_input`; the approval form is only needed when no account is connected or the right form is ambiguous. When no HubSpot account is connected, every command exits 1 with `NO_HUBSPOT_CONNECTION` and a `fix` telling the agent to route the user to **Brain → Integrations → Tools**.
2667
+
2668
+ `meetings list` reports `redirectKnown: false` when HubSpot omitted the calendar's booking settings; `redirectUrl: null` alongside it means *unknown*, not "no redirect". `--redirecting-only` deliberately keeps those rows rather than answering "none" from missing data.
2669
+
2670
+ ---
2671
+
2626
2672
  ### `baker chats list | view | transcript | diff`
2627
2673
 
2628
2674
  Read-only inspection of **other Sessions (chats) on the same account** — so an agent can reuse prior work ("like we did in that other chat, but for X"). Same-account only; never crosses a company boundary and has no write surface.
@@ -3415,6 +3461,7 @@ Generate video. Async with polling. Two curated models.
3415
3461
  | `first_frame` | image | no | Starting keyframe (image-to-video) |
3416
3462
  | `last_frame` | image | no | Ending keyframe |
3417
3463
  | `reference` | image | no | Alternative to `first_frame` when only one image is given |
3464
+ | `references` | image[] | no | Reference-to-video: several images used as visual guidance (this person, this product) instead of an exact opening frame. **Mutually exclusive with `first_frame`** — when both are wired the frame wins and these are ignored, so pick one mode. |
3418
3465
 
3419
3466
  Accepted ref-image MIMEs vary by model — see per-model sections below.
3420
3467
 
@@ -1119,6 +1119,7 @@ var OPENROUTER_IMAGE_SIZE = ["1K", "2K", "4K"];
1119
1119
  var OPENROUTER_IMAGE_SIZE_EXTENDED = ["0.5K", ...OPENROUTER_IMAGE_SIZE];
1120
1120
  var OPENROUTER_IMAGE_QUALITY = ["auto", "low", "medium", "high"];
1121
1121
  var SEEDANCE_DURATIONS = [4, 5, 6, 8, 10, 12, 15];
1122
+ var KLING_DURATIONS = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1122
1123
  var ELEVENLABS_OUTPUT_FORMATS = [
1123
1124
  "mp3_22050_32",
1124
1125
  "mp3_44100_32",
@@ -1376,9 +1377,13 @@ var MODEL_REGISTRY = {
1376
1377
  video_generate: {
1377
1378
  "bytedance/seedance-2.0": {
1378
1379
  // Routed via Replicate's official `bytedance/seedance-2.0` model. NOTE:
1379
- // ByteDance's upstream "real person" likeness filter still blocks photoreal
1380
- // human reference frames on ANY reseller the escape is a synthetic/AI
1381
- // presenter face or routing real faces to Veo, not the provider choice.
1380
+ // ByteDance's upstream likeness filter blocks photoreal human faces on ANY
1381
+ // reseller, and it does NOT care whether the person is real: it classifies
1382
+ // pixels, so an AI-generated face that looks photographic is refused
1383
+ // exactly like a photograph (E005 / content_policy_blocked). Confirmed in
1384
+ // production against faces we generated ourselves. The only escape is Veo
1385
+ // — NOT a synthetic presenter, which an earlier version of this note
1386
+ // wrongly recommended and which sends you round the same loop.
1382
1387
  label: "ByteDance Seedance 2.0",
1383
1388
  inputs: [],
1384
1389
  optional_inputs: [{ kind: "image", mimes: REPLICATE_IMAGE_MIMES }],
@@ -1412,7 +1417,7 @@ var MODEL_REGISTRY = {
1412
1417
  prompt: { kind: "string" },
1413
1418
  negative_prompt: { kind: "string" },
1414
1419
  aspect_ratio: { kind: "string", enum: ["16:9", "9:16"] },
1415
- resolution: { kind: "string", enum: ["720p", "1080p"] },
1420
+ resolution: { kind: "string", enum: ["720p", "1080p", "4K"] },
1416
1421
  duration: { kind: "number", enum: [4, 6, 8] },
1417
1422
  seed: { kind: "number" },
1418
1423
  generate_audio: { kind: "boolean" },
@@ -1424,9 +1429,14 @@ var MODEL_REGISTRY = {
1424
1429
  "kwaivgi/kling-v3.0-pro": {
1425
1430
  // Motion-transfer / dynamic multi-shot beats. Reachable through the default
1426
1431
  // OpenRouter gateway (generic video body — no google-vertex block), so it
1427
- // needs no direct-provider exception. Cost is usage-based (known from the
1428
- // provider response), so it has no pre-flight cost estimate. `cfg_scale`
1429
- // trades prompt adherence vs motion freedom; higher = closer to prompt.
1432
+ // needs no direct-provider exception. `cfg_scale` trades prompt adherence
1433
+ // vs motion freedom; higher = closer to prompt.
1434
+ //
1435
+ // Params below are the ones OpenRouter actually advertises for this model
1436
+ // (GET /api/v1/videos/models). Three were wrong before and each was a free
1437
+ // 400 or a silently unsupported knob: it renders 720p ONLY (1080p was
1438
+ // offered and rejected), it takes ANY duration from 3-15s (we allowed just
1439
+ // 5 and 10), and it does not support `seed` at all.
1430
1440
  label: "Kling 3.0",
1431
1441
  inputs: [],
1432
1442
  optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
@@ -1436,10 +1446,9 @@ var MODEL_REGISTRY = {
1436
1446
  // over-length prompt fails validate (free) not the billed call.
1437
1447
  prompt: { kind: "string", maxLength: 2500 },
1438
1448
  negative_prompt: { kind: "string" },
1439
- aspect_ratio: { kind: "string", enum: ["1:1", "16:9", "9:16"] },
1440
- resolution: { kind: "string", enum: ["720p", "1080p"] },
1441
- duration: { kind: "number", enum: [5, 10] },
1442
- seed: { kind: "number" },
1449
+ aspect_ratio: { kind: "string", enum: ["16:9", "9:16", "1:1"] },
1450
+ resolution: { kind: "string", enum: ["720p"] },
1451
+ duration: { kind: "number", enum: KLING_DURATIONS },
1443
1452
  generate_audio: { kind: "boolean" },
1444
1453
  cfg_scale: { kind: "number", min: 0, max: 1 }
1445
1454
  }
@@ -1455,7 +1464,7 @@ var MODEL_REGISTRY = {
1455
1464
  prompt: { kind: "string" },
1456
1465
  negative_prompt: { kind: "string" },
1457
1466
  aspect_ratio: { kind: "string", enum: ["16:9", "9:16"] },
1458
- resolution: { kind: "string", enum: ["720p", "1080p"] },
1467
+ resolution: { kind: "string", enum: ["720p", "1080p", "4K"] },
1459
1468
  duration: { kind: "number", enum: [4, 6, 8] },
1460
1469
  seed: { kind: "number" },
1461
1470
  generate_audio: { kind: "boolean" },
@@ -1777,6 +1786,58 @@ function validateValue(key, value, schema, model) {
1777
1786
  }
1778
1787
  }
1779
1788
 
1789
+ // ../canvas-contract/src/videoCost.ts
1790
+ var CREDITS_PER_USD = 100;
1791
+ var SEEDANCE_USD_PER_SECOND = {
1792
+ "480p": 0.18,
1793
+ "720p": 0.5,
1794
+ "1080p": 0.62,
1795
+ "4k": 0.62
1796
+ };
1797
+ var DEFAULT_VIDEO_RESOLUTION = "720p";
1798
+ var DEFAULT_VIDEO_DURATION_S = 5;
1799
+ function isSeedanceModel(model) {
1800
+ return model.startsWith("bytedance/seedance");
1801
+ }
1802
+ var FALLBACK_USD_PER_SECOND = 0.5;
1803
+ function seedanceUsdPerSecond(resolution) {
1804
+ return SEEDANCE_USD_PER_SECOND[resolution ?? DEFAULT_VIDEO_RESOLUTION] ?? FALLBACK_USD_PER_SECOND;
1805
+ }
1806
+ var USD_PER_SECOND = {
1807
+ // Kling publishes one flat rate across every resolution and mode.
1808
+ "kwaivgi/kling-v3.0-pro": { silent: 0.112, audio: 0.168 },
1809
+ // Veo prices 4K far above the base tier — and 4K is a selectable resolution,
1810
+ // so folding it into the base rate under-quotes the dearest clip we can make.
1811
+ "google/veo-3.1": {
1812
+ silent: 0.2,
1813
+ audio: 0.4,
1814
+ byResolution: { "4K": { silent: 0.4, audio: 0.6 } }
1815
+ },
1816
+ "google/veo-3.1-fast": {
1817
+ silent: 0.1,
1818
+ audio: 0.12,
1819
+ byResolution: {
1820
+ "720p": { silent: 0.08, audio: 0.1 },
1821
+ "4K": { silent: 0.25, audio: 0.3 }
1822
+ }
1823
+ }
1824
+ };
1825
+ function estimateVideoCostUsd({ model, duration, resolution, generateAudio }) {
1826
+ const seconds = duration ?? DEFAULT_VIDEO_DURATION_S;
1827
+ if (isSeedanceModel(model)) return seedanceUsdPerSecond(resolution) * seconds;
1828
+ const published = USD_PER_SECOND[model];
1829
+ if (published) {
1830
+ const rate = (resolution ? published.byResolution?.[resolution] : void 0) ?? published;
1831
+ return (generateAudio ? rate.audio : rate.silent) * seconds;
1832
+ }
1833
+ const dearestKnownRate = Math.max(...Object.values(SEEDANCE_USD_PER_SECOND));
1834
+ return dearestKnownRate * seconds;
1835
+ }
1836
+ function estimateVideoCredits(input) {
1837
+ const credits = estimateVideoCostUsd(input) * CREDITS_PER_USD;
1838
+ return Math.ceil(Number(credits.toFixed(6)));
1839
+ }
1840
+
1780
1841
  // src/engine/lib/concurrency.ts
1781
1842
  var DEFAULT_CONCURRENCY = 8;
1782
1843
  function resolveConcurrency(...candidates) {
@@ -2573,7 +2634,7 @@ function looksLikeHttpUrl(value) {
2573
2634
  var VEO_PERSON_GENERATION = "allow_adult";
2574
2635
  var VEO_NEGATIVE_PROMPT = "subtitles, captions, on-screen text, watermark, logo, warped face, distorted hands, extra fingers, low quality";
2575
2636
  var VEO_DURATIONS = [4, 6, 8];
2576
- var KLING_DURATIONS = [5, 10];
2637
+ var KLING_DURATIONS2 = [5, 10];
2577
2638
  var KLING_NEGATIVE_PROMPT = "warped face, distorted hands, extra fingers, morphing, flicker, on-screen text, watermark, low quality";
2578
2639
  var KLING_CFG_SCALE = 0.7;
2579
2640
  var SPEAKS_PROSE = (line) => `The person speaks to camera; lip-sync follows the dialogue verbatim, with delivery and emotion carried in the wording itself (no bracketed cues). Dialogue: "${line}"`;
@@ -2619,7 +2680,7 @@ var KLING_PROFILE = {
2619
2680
  // Kling takes a negative_prompt PARAM instead (paramDefaults).
2620
2681
  keyframeInstruction: "Preserve the composition and colors of the first frame; animate the motion described.",
2621
2682
  wordBudget: 200,
2622
- durationSet: KLING_DURATIONS,
2683
+ durationSet: KLING_DURATIONS2,
2623
2684
  paramDefaults: { negative_prompt: KLING_NEGATIVE_PROMPT, cfg_scale: KLING_CFG_SCALE }
2624
2685
  };
2625
2686
  function clipProfileFor(modelId) {
@@ -7453,12 +7514,33 @@ var videoGenerateNode = delegated({
7453
7514
  inputs: z28.object({
7454
7515
  first_frame: ImageRef.optional(),
7455
7516
  last_frame: ImageRef.optional(),
7456
- reference: ImageRef.optional()
7517
+ reference: ImageRef.optional(),
7518
+ /**
7519
+ * Reference-to-video: several images the model uses as visual guidance
7520
+ * (this person, this product) instead of an exact opening frame. Mutually
7521
+ * exclusive with `first_frame` — OpenRouter treats a request carrying both
7522
+ * as image-to-video and ignores these, so wire one or the other.
7523
+ */
7524
+ references: z28.array(ImageRef).optional()
7457
7525
  }).loose(),
7458
7526
  params: VideoGenerateParams,
7459
7527
  outputs: z28.object({ video: VideoRef }).strict(),
7460
7528
  outputKinds: { video: "video" },
7461
- cost: () => ({ credits: 50, seconds_estimate: 120 })
7529
+ // Priced from the shared contract, so `validate`'s quote and the charge the
7530
+ // backend applies come from ONE table. A flat number here under-quoted a
7531
+ // 1080p 8s clip by ~10x, and the agent reads this out to the user as "this
7532
+ // will cost N" before spending it.
7533
+ cost: ({ params }) => ({
7534
+ credits: estimateVideoCredits({
7535
+ model: params.model,
7536
+ duration: params.duration,
7537
+ resolution: params.resolution,
7538
+ // Audio bills at a separate, higher rate on every model that offers it
7539
+ // (Veo 3.1 doubles), so omitting it quotes half the real cost.
7540
+ generateAudio: params.generate_audio
7541
+ }),
7542
+ seconds_estimate: 120
7543
+ })
7462
7544
  });
7463
7545
 
7464
7546
  // src/engine/nodes/remote/videoBackgroundRemove.ts
@@ -7941,4 +8023,4 @@ export {
7941
8023
  defaultRegistry,
7942
8024
  createEngineFromEnv
7943
8025
  };
7944
- //# sourceMappingURL=chunk-OO5BDH3J.js.map
8026
+ //# sourceMappingURL=chunk-7Z6C5OVO.js.map