cueframe 0.8.1 → 0.8.3
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 +58 -50
- package/dist/analyze-WA2RW6VG.js +2 -0
- package/dist/chunk-C4HCB4HG.js +12 -0
- package/dist/cueframe.js +66 -86
- package/dist/skills/brand-reel/SKILL.md +2 -2
- package/dist/skills/composing-video/SKILL.md +8 -8
- package/dist/skills/cueframe-cli/SKILL.md +5 -6
- package/dist/skills/cueframe-component-authoring/SKILL.md +88 -108
- package/dist/skills/cueframe-compose-loop/SKILL.md +3 -3
- package/dist/skills/cueframe-product-video/SKILL.md +3 -4
- package/dist/skills/every-format-from-one-edit/SKILL.md +1 -1
- package/dist/skills/launch-video/SKILL.md +2 -2
- package/dist/skills/make-a-social-reel/SKILL.md +2 -2
- package/dist/skills/rebrand-a-video/SKILL.md +2 -2
- package/dist/skills/video-craft-standards/SKILL.md +2 -2
- package/package.json +9 -10
- package/dist/analyze-G6XZWOBG.js +0 -2
- package/dist/chunk-WJWX26F4.js +0 -12
|
@@ -60,8 +60,8 @@ affected behavior and regressions; reserve full-film judging and export for the
|
|
|
60
60
|
4. **Seed and author.** `new_composition` at 9:16 → `apply_composition` with the footage beats
|
|
61
61
|
and `setCropIntents` segments tiling each clip (subject-focused, motivated zooms).
|
|
62
62
|
5. **Author every graphic as a bespoke card.** `kind:"card"` HTML using the brand's type and its
|
|
63
|
-
palette as FIELDS — plates, bands, blocks of the brand color, not tinted defaults.
|
|
64
|
-
|
|
63
|
+
palette as FIELDS — plates, bands, blocks of the brand color, not tinted defaults. Animate
|
|
64
|
+
it in its CSS (`--cf-enter`/`--cf-exit`) or with `clip.setKeyframes` in a FOLLOWING call. The
|
|
65
65
|
test: if another brand's hex swapped in would still look correct, the layout isn't bespoke
|
|
66
66
|
enough — redo it.
|
|
67
67
|
6. **Honor the audio decision.** If silence was requested, preserve it without searching for a bed. Otherwise, when music serves the plan: `search_resources` for a bed honoring the kit's
|
|
@@ -78,10 +78,10 @@ Chromium captures; the clock never advances). Use these instead:
|
|
|
78
78
|
2. **`transitionIn` on any overlay clip** — `{ transitionIn: { type: "fade", duration: 0.4 } }`
|
|
79
79
|
now applies to overlay-family clips (cards, components, primitives), not just video.
|
|
80
80
|
Use it for entrances; author exits in-card via `--cf-exit`.
|
|
81
|
-
3. **`
|
|
82
|
-
`{ type: "
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
3. **`clip.setKeyframes` op** — animate the whole clip on one channel:
|
|
82
|
+
`{ type: "clip.setKeyframes", clipId, property, keyframes: [{ time, value, ease? }, …] }`
|
|
83
|
+
for `opacity`, `scale`, `rotation`, `position.x` or `position.y` (times in seconds from the
|
|
84
|
+
clip's start). For motion beyond that, author a native component with `create_component`.
|
|
85
85
|
|
|
86
86
|
---
|
|
87
87
|
|
|
@@ -208,7 +208,7 @@ soundtrack is a measurable share of whether anyone keeps watching.
|
|
|
208
208
|
reach for `volumeDb` only when you intend to break the hierarchy.
|
|
209
209
|
|
|
210
210
|
0d. **A brand kit can carry a SOUND identity — set it once, it rides every
|
|
211
|
-
compose.** `create_brand_kit`'s `audio` block (echoed on
|
|
211
|
+
compose.** `create_brand_kit`'s `audio` block (echoed on
|
|
212
212
|
`get_profile`) holds the brand's sonic taste; every ref is a palette reference
|
|
213
213
|
(a pack `soundId` | `bedId` | an org `mediaId` from `list_catalog`'s `audio[]`).
|
|
214
214
|
Four optional fields, resolution precedence **brand > org > pack** (the audio
|
|
@@ -328,7 +328,7 @@ Each playbook is an illustrative format default; explicit intent and scope gover
|
|
|
328
328
|
|
|
329
329
|
**Frameworks:** Hook → Problem → Solution → Proof → CTA. Decide the CTA first, script it last; write for the ear.
|
|
330
330
|
**A fully animated explainer is a FIRST-CLASS grounded format**, not a fallback for missing footage. An explainer built on authored diagrams of the *actual* mechanism (its real pipeline, the real data) is grounded — the grounding is that each visual depicts THIS thing specifically, not a decorative loop. Reach for it deliberately, not as a consolation for "no footage."
|
|
331
|
-
**Reach for:** an authored `card` clip (your own HTML/typography,
|
|
331
|
+
**Reach for:** an authored `card` clip (your own HTML/typography, animated in its CSS or with `clip.setKeyframes`) to anchor an abstract mechanism with a real diagram — process/algorithm/data viz, not cinematic footage · `lower-third` for term labels · one idea per beat · when using real footage, narrate to the detected transcript timing.
|
|
332
332
|
**Anti-patterns:** a wall of narration over stock gradient loops with no visual keyed to the words (the generic-filler tell — a purposeful diagram of the real mechanism is the opposite); explaining features before establishing the problem; using `generate_media` text-to-video to fake a "product" that doesn't match the real UI (it hallucinates specifics).
|
|
333
333
|
**Quality bar:** every beat has a visual keyed to what's being said — grounded and specific to this content, not decoration; the hook lands in 3s.
|
|
334
334
|
|
|
@@ -378,12 +378,12 @@ Each playbook is an illustrative format default; explicit intent and scope gover
|
|
|
378
378
|
| Name someone / label a step | `lower-third` scene from `list_catalog` |
|
|
379
379
|
| Title / closing wordmark | `cinematic-title` scene (closing, not opening) |
|
|
380
380
|
| Grade the look | `color-grade` scene/effect |
|
|
381
|
-
| Anchor an abstract mechanism | An authored `card` clip
|
|
381
|
+
| Anchor an abstract mechanism | An authored `card` clip animated in its CSS or with `clip.setKeyframes` (diagrams of the real mechanism — NOT cinematic; a first-class grounded path) |
|
|
382
382
|
| Long-form → clips | `suggest_briefs` → `wait_job(kind:"suggestions")` → `compose({ suggestionId })` |
|
|
383
383
|
| Auto-reframe to another aspect | `derive_composition` (content-aware 9:16 / 1:1 / 4:5 from a 16:9 master) |
|
|
384
384
|
| Discover what to build with | `list_catalog` — pass filters such as `category`/`kind`/`tier`/`useCase`/`mood`/`intent`, plus `limit` and the returned pagination cursor. For builtin packages, request `projection:"component-library"` and `origin:"builtin"`, then resolve the selected exact identity before insertion. Each entry carries `placement` (how to author it), `tier`/`useCase`, and `fixedCopy` (baked text no param changes). Read `cueframe://component/{id}` for the full **prop schema**. |
|
|
385
385
|
| Own / fork a custom graphic | `get_component_source` (exact scoped v3 pin for builtins) / `create_component` / `update_component` (preserve the complete source snapshot) — **see `cueframe-component-authoring`** for the full fork→preview→push loop |
|
|
386
|
-
| Several cells that share space and must reflow together (feature grid, rail + fluid content, cast + type) | An authored
|
|
386
|
+
| Several cells that share space and must reflow together (feature grid, rail + fluid content, cast + type) | An authored card or native component laid out with CSS flex/grid (fixed tracks for content-sized cells, `fr` for the rest), animated from the frame. Static PiP / split is clip `region` + `fit` instead. Never CSS-scale the layout. **See `cueframe-component-authoring`** |
|
|
387
387
|
| Seed + edit the timeline | `apply_composition` (atomic batch ops; `dry_run:true` to check, `if_match` for OCC) — this is where clips, reframe, captions, and format are authored |
|
|
388
388
|
| Set the brand | `create_brand_kit` (402-gated) → set `brandKitId` on the **project** (`create_project` / `update_project`). It lives on the project, NOT on a composition op — trying to set it elsewhere is silently dropped. |
|
|
389
389
|
| Shape-check (NOT quality) | `validate_composition` → `{ valid, errors[] }` (`unknown_primitive`, `invalid_primitive_params`, `clip_source_track_mismatch`, `reframe_coverage_gap`, …); does **not** check trim bounds or image-on-video-track (those fail at save/render) |
|
|
@@ -199,14 +199,13 @@ npx -y cueframe@0.8 init --json
|
|
|
199
199
|
# 2a. Scaffold a component in cueframe/components/<id>/ as owned, editable source.
|
|
200
200
|
npx -y cueframe@0.8 new component my-badge --json
|
|
201
201
|
|
|
202
|
-
#
|
|
203
|
-
#
|
|
202
|
+
# The scaffold is a native (version 4) source: template.html + definition.json, with its
|
|
203
|
+
# manifest in cueframe.json. Built-in forks copy the exact template, definition and manifest.
|
|
204
204
|
|
|
205
|
-
# 3. Edit cueframe/components/<id>/
|
|
206
|
-
#
|
|
207
|
-
# The nested asset manifest remains version 2.
|
|
205
|
+
# 3. Edit cueframe/components/<id>/template.html (one HyperFrames sub-composition ending in
|
|
206
|
+
# __cfScene.mount(...)) and definition.json (inputs, defaults, timing, capabilities).
|
|
208
207
|
|
|
209
|
-
# 4. Publish to CueFrame so it can render (
|
|
208
|
+
# 4. Publish to CueFrame so it can render (the server admits the source or names the rule it breaks).
|
|
210
209
|
npx -y cueframe@0.8 push my-badge --json # one component
|
|
211
210
|
npx -y cueframe@0.8 sync --json # every component in the workspace
|
|
212
211
|
|
|
@@ -27,12 +27,12 @@ See the `cueframe-scene-shot` skill and `cueframe://scene-shot`. Author a compon
|
|
|
27
27
|
you need behaviour the scene contract does not express.
|
|
28
28
|
|
|
29
29
|
**Source format matters:**
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
- New and changed components are native (source version 4): `{version:4, componentId, template,
|
|
31
|
+
manifest, definition}`. `template` is one HyperFrames sub-composition whose script ends in one
|
|
32
|
+
`__cfScene.mount(...)`; the manifest is `{version:4, runtime:'hyperframes', hfContract:1,
|
|
33
|
+
kind:'scene', assets, graphics, alphaPolicy}`. The definition declares input schema, defaults,
|
|
34
|
+
timing, brand/media bindings and capabilities. The server instructions' Component Contract has
|
|
35
|
+
the full rules. Version-3 TSX sources are retired and refused as `component_source_retired`.
|
|
36
36
|
- Neither renderer runs authored source in the editor process. Local and hosted execution
|
|
37
37
|
use isolated render processes and exact immutable source revisions.
|
|
38
38
|
|
|
@@ -40,14 +40,14 @@ you need behaviour the scene contract does not express.
|
|
|
40
40
|
|
|
41
41
|
Use the live tool schema for request envelopes; hosted write fields are normally under `body`.
|
|
42
42
|
|
|
43
|
-
1. **Discover and read a built-in:** call `
|
|
43
|
+
1. **Discover and read a built-in:** call `list_catalog` with
|
|
44
44
|
`projection: "component-library", origin: "builtin", libraryKind: "definition"`.
|
|
45
45
|
Select the intended card and preserve its complete scoped source reference. Call
|
|
46
46
|
`get_component_source` with that reference's `componentId`, `sourceVersionId`,
|
|
47
47
|
`builtinCollection` (its scope collection).
|
|
48
48
|
ID-only builtin reads are unsupported. An unavailable exact pin is a refusal; never
|
|
49
49
|
retry another scope or an unpinned head.
|
|
50
|
-
2. **Author/own it:** edit the complete
|
|
50
|
+
2. **Author/own it:** edit the complete native `sourceSnapshot`; use your chosen owned ID
|
|
51
51
|
consistently in its `componentId` and the request. Send it as `body.source` to
|
|
52
52
|
`create_component`, with required metadata. Preserve its definition, capability
|
|
53
53
|
declarations, manifest and source format. For an existing component, keep its ID and
|
|
@@ -69,96 +69,89 @@ Use the live tool schema for request envelopes; hosted write fields are normally
|
|
|
69
69
|
Set preview duration deliberately: `durationInFrames` is not a trim window and can retime
|
|
70
70
|
duration-dependent motion. Preserve production timing; inspect the live tool's request shape.
|
|
71
71
|
4. **Check integration:** add it as a `{kind:'component', componentId, props:{},
|
|
72
|
-
execution:{version:
|
|
72
|
+
execution:{version:4,componentId,sourceVersionId,params,seed,assets}, placement:{version:3,kind:'clip'}}` clip
|
|
73
73
|
(`apply_composition`), then inspect saved-composition stills or a scoped `preview_clip` for
|
|
74
74
|
placement and motion. Use `create_render` → `wait_job kind:"render"` when delivering a
|
|
75
75
|
finished MP4, not per tweak.
|
|
76
76
|
|
|
77
77
|
## Path B — CLI / coding agent (customer-owned source under git)
|
|
78
78
|
|
|
79
|
-
Source lives in `cueframe/components/<id>/` (the `cueframe.json` workspace); edit
|
|
80
|
-
|
|
79
|
+
Source lives in `cueframe/components/<id>/` (the `cueframe.json` workspace); edit the native
|
|
80
|
+
template locally, push to render.
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
83
|
npx -y cueframe@0.8 init --json # scaffold cueframe.json + cueframe/components/
|
|
84
84
|
npx -y cueframe@0.8 new component my-badge --json # scaffold a component in the workspace
|
|
85
|
-
# New scaffolds contain
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
npx -y cueframe@0.8 push my-badge --json # upload index.tsx as the component tsxSource
|
|
85
|
+
# New scaffolds contain template.html and definition.json (native source, version 4).
|
|
86
|
+
# …edit cueframe/components/<id>/template.html and definition.json in your editor…
|
|
87
|
+
npx -y cueframe@0.8 push my-badge --json # send the native source; the server admits it
|
|
89
88
|
# or: npx -y cueframe@0.8 sync --json # push every component in the workspace
|
|
90
89
|
# reference the component id in your composition, then render:
|
|
91
90
|
npx -y cueframe@0.8 render <projectId> -o ./out.mp4 --json
|
|
92
91
|
```
|
|
93
92
|
|
|
94
|
-
The CLI writes `
|
|
95
|
-
|
|
96
|
-
`
|
|
97
|
-
|
|
98
|
-
parts** from the selected `sourceSnapshot`:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
their real behavior instead of running `new --force` over existing source. To preview before
|
|
93
|
+
The CLI writes `template.html` and `definition.json` (input schema, defaults, timing and
|
|
94
|
+
capabilities). The version-4 manifest lives at `cueframe.json` → `components.<id>.manifest`.
|
|
95
|
+
`push`/`sync` send these as one native `source`, checked against the contract's source schema;
|
|
96
|
+
the server's admission (template lint, determinism, declarations) refuses with a named rule.
|
|
97
|
+
For a builtin fork, copy **all three parts** from the selected `sourceSnapshot`: template,
|
|
98
|
+
definition and manifest. A workspace component that still has a version-3 `index.tsx` is
|
|
99
|
+
refused as `component_source_retired` before any API call; author it again as a native template. To preview before
|
|
102
100
|
a full render, push it and use the appropriate component preview loop.
|
|
103
101
|
|
|
104
102
|
## Path C — Director editing a local desktop project (project-embedded source)
|
|
105
103
|
|
|
106
|
-
When `get_context` reports the project lane as local, motion does not
|
|
107
|
-
component.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
`
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
does not imply HMR: source updates still compile.
|
|
114
|
-
|
|
115
|
-
If those tools are unavailable, add/update through `apply_composition` and inspect the local
|
|
116
|
-
timeline. An inline custom overlay has this shape:
|
|
117
|
-
|
|
118
|
-
```json
|
|
119
|
-
{
|
|
120
|
-
"type": "clip.add",
|
|
121
|
-
"clip": {
|
|
122
|
-
"id": "local-motion",
|
|
123
|
-
"kind": "overlay",
|
|
124
|
-
"startTime": 0,
|
|
125
|
-
"duration": 3,
|
|
126
|
-
"source": {
|
|
127
|
-
"kind": "overlay",
|
|
128
|
-
"primitiveId": "custom",
|
|
129
|
-
"params": {
|
|
130
|
-
"jsxSource": "export default function Graphic({ durationInFrames, params, assets, render }) { ... }",
|
|
131
|
-
"componentManifest": { "version": 2, "assets": {}, "graphics": { "api": "canvas2d", "required": true, "accelerationPreference": "software-allowed" }, "alphaPolicy": "requires-transparency" },
|
|
132
|
-
"componentDefinition": { "inputSchema": { "type": "object", "properties": {}, "additionalProperties": false }, "defaults": {}, "timing": { "defaultDuration": { "frames": 90, "fps": 30 } }, "brandBindings": [], "capabilities": [{ "kind": "clip" }] },
|
|
133
|
-
"componentAssets": {}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
"newTrack": true
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
The desktop compiles the module in an isolated child process and creates it in the desktop's component
|
|
142
|
-
library as a complete v3 source. Declare the actual parameter schema and defaults in
|
|
143
|
-
`componentDefinition`; the empty schema above is for a graphic with no custom parameters.
|
|
144
|
-
Keep a fork's existing definition and nested manifest together with its source. Inline source creates a component only: a clip whose component already exists with other
|
|
145
|
-
source is refused as `component_exists`, and that component changes through edits.
|
|
146
|
-
The timeline and final export render that exact version; no preview media is baked. A compile failure
|
|
147
|
-
refuses the whole batch and leaves the composition untouched. Use `create_component` only for a reusable
|
|
148
|
-
org-catalog component on a reachable cloud lane.
|
|
149
|
-
|
|
150
|
-
For media-backed graphics, declare each slot in `componentManifest.assets`, bind the slot to a
|
|
151
|
-
project media id in `componentAssets`, and read its request-scoped handle from `assets`. The renderer
|
|
152
|
-
localizes and hashes the file before execution. Remote URLs, undeclared assets, arbitrary filesystem
|
|
153
|
-
paths, WebGL substitution, and static/card substitution are not fallback paths.
|
|
104
|
+
When `get_context` reports the project lane as local, motion does not need an account or an org
|
|
105
|
+
component. Components here are native (source version 4) too:
|
|
106
|
+
- **New component:** open a draft with `open_component_preview`, passing `template`,
|
|
107
|
+
`component_manifest` (`{version:4, runtime:'hyperframes', hfContract:1, kind:'scene', assets,
|
|
108
|
+
graphics, alphaPolicy}`) and `component_definition`.
|
|
109
|
+
- **Existing component:** pass `component_id` instead, to start from the clip's pinned version or
|
|
110
|
+
the library version.
|
|
154
111
|
|
|
155
|
-
|
|
156
|
-
`
|
|
112
|
+
The draft is admitted in full: template lint, determinism and the variable declaration.
|
|
113
|
+
`data-composition-variables` must equal the definition's projection (its scalar inputSchema
|
|
114
|
+
properties, then the manifest's slots).
|
|
157
115
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
116
|
+
Edit the draft with `update_component_preview` (exact-text `{oldText, newText, replaceAll?}`
|
|
117
|
+
edits). Check exact frames with `render_component_preview_frame`, and inspect playback for motion.
|
|
118
|
+
`commit_component_preview` publishes the checked source to the library and pins the clip, with a
|
|
119
|
+
fresh ETag. `read_component` and `edit_component` read and edit the library directly. Close the
|
|
120
|
+
preview when finished.
|
|
121
|
+
|
|
122
|
+
Media slots are image or video, declared in `component_manifest.assets` and bound to project
|
|
123
|
+
media ids in `component_assets`. The template passes them to `MediaTileScene` as
|
|
124
|
+
`streams: { hero: { asset: assets.hero } }`. Declare `alphaPolicy` explicitly:
|
|
125
|
+
`requires-transparency` for overlays, `allows-opaque` for full-frame plates.
|
|
126
|
+
|
|
127
|
+
Every frame must be a pure function of `render.frame`, params and `render.seed`. Timers, `Date`,
|
|
128
|
+
network, imports, other globals and CSS animation are refused at admission. The timeline and final
|
|
129
|
+
export render the exact committed version. Use `create_component` only for a reusable org-catalog
|
|
130
|
+
component on a reachable cloud lane.
|
|
131
|
+
|
|
132
|
+
## Upgrading an old project
|
|
133
|
+
|
|
134
|
+
`get_composition` returns `needsUpgrade` only when clips from before the native cutover remain:
|
|
135
|
+
`[{clipId, kind, componentId?, sourceVersionId?, primitiveId?, successorComponentId?,
|
|
136
|
+
successorSourceVersionId?}]`. Saves and renders refuse those clips. A retired component is
|
|
137
|
+
refused with `component_source_retired`, and a retired
|
|
138
|
+
primitive with `render_unknown_primitive`. Upgrade only the listed clips. Keep every other clip,
|
|
139
|
+
timing, track and brand-kit binding as it is.
|
|
140
|
+
|
|
141
|
+
- **Any entry with a `successorSourceVersionId`:** call `repin_component` with its clip ids. It
|
|
142
|
+
moves them to the native successor and keeps their saved inputs.
|
|
143
|
+
- **`v3-component` with no successor** (a custom pre-cutover component): read its source, params,
|
|
144
|
+
timing and placement, and author a native replacement with `create_component` as a version-4
|
|
145
|
+
template following the server instructions' Component Contract.
|
|
146
|
+
- **`unpinned-component`:** the clip carries a `retired` record (what was stored before the cutover), with its
|
|
147
|
+
props preserved. Re-author it from those props the same way.
|
|
148
|
+
- **`retired-primitive`:** rebuild what `primitiveId` drew from the clip's params, as a card or
|
|
149
|
+
a native component.
|
|
150
|
+
|
|
151
|
+
For each replacement, use `apply_composition` to replace only that clip at the same start,
|
|
152
|
+
duration, track and placement, pinned to the new exact `sourceVersionId`. Map the old params
|
|
153
|
+
onto the new inputs; do not invent new content. Verify each clip with `preview_frame` at a time
|
|
154
|
+
inside its window and confirm `needsUpgrade` is gone. Only then render (`create_render`).
|
|
162
155
|
|
|
163
156
|
## Choosing what to fork
|
|
164
157
|
Use the `component-library` projection to choose a definition, inspect its input schema and
|
|
@@ -166,41 +159,28 @@ capabilities, then fetch its exact scoped source as described in Path A. Presets
|
|
|
166
159
|
are different card kinds: follow their exact definition/document references rather than treating
|
|
167
160
|
those IDs as source IDs. Inspect appropriate previews before judging visual behavior.
|
|
168
161
|
|
|
169
|
-
## Layout inside a component —
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
tracksEnd={[{ weight: 0, basis: 520 }, 1, { weight: 1, split: [2, 1] }]}
|
|
180
|
-
fits={[{ mode: 'stretch' }, { mode: 'position' }, { mode: 'matte', aspect: 1 }, { mode: 'stretch' }]}
|
|
181
|
-
gap={16} width={R.w * width} height={R.h * height}>
|
|
182
|
-
<Rail /> <Type /> <Art /> <Block />
|
|
183
|
-
</FlexLayout>
|
|
162
|
+
## Layout inside a component — CSS flex/grid
|
|
163
|
+
|
|
164
|
+
When several cells must share space (a feature grid, a fixed rail beside fluid content, a cast of
|
|
165
|
+
objects next to type), lay them out with CSS flex or grid in the HTML, not hand-placed pixels.
|
|
166
|
+
Use the template of a native component, or a `card`:
|
|
167
|
+
|
|
168
|
+
```html
|
|
169
|
+
<div style="display:grid; grid-template-columns: 520px 1fr 3fr; gap:16px; width:100%; height:100%">
|
|
170
|
+
<div class="rail"></div><div class="type"></div><div class="art"></div>
|
|
171
|
+
</div>
|
|
184
172
|
```
|
|
185
173
|
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
-
|
|
191
|
-
|
|
192
|
-
- Inside a `params.region` container pass `width`/`height` = the region's pixel size so nested splits and
|
|
193
|
-
aspect fits solve against the region, not the frame. **Never CSS-`scale()` the layout** — it resamples;
|
|
194
|
-
size the container instead.
|
|
195
|
-
- 3D in the same rig: `solveFlex(tree, size, weights)` returns the boxes; `flexBoxToThree(box, size)`
|
|
196
|
-
centres a mesh under `<ThreeCanvas orthographic>` (frustum pinned 1:1 to pixels), so one canvas holds
|
|
197
|
-
every object exactly on its cell.
|
|
198
|
-
- Same inputs, same boxes everywhere: the solve is pure and yoga-backed; nothing is measured from the DOM.
|
|
199
|
-
Text is placed, not measured — declare sizes with `basis` when a cell must fit its content.
|
|
174
|
+
- Fixed tracks (`520px`) hold content-sized cells. Fractions (`1fr`, `3fr`) share what is left.
|
|
175
|
+
- Animate a reflow from the frame, never from time: in a card, drive `grid-template-columns`
|
|
176
|
+
or `flex-basis` from `var(--cf-progress)` (or `--cf-enter`/`--cf-exit`); in a native template,
|
|
177
|
+
derive it from `render.frame`.
|
|
178
|
+
- Size the container to its region instead of CSS-`scale()`-ing the layout, which resamples.
|
|
179
|
+
Use `object-fit` for media in a cell.
|
|
200
180
|
|
|
201
181
|
## Don't
|
|
202
182
|
- Don't expect a forked primitive to use rich named props — read `params` inside and use the explicit
|
|
203
183
|
`assets` and `render` bags for media and frame state.
|
|
204
|
-
- When a built-in fits the request, fork its exact
|
|
205
|
-
definition by copying only its
|
|
184
|
+
- When a built-in fits the request, fork its exact snapshot using Path A. Never discard its
|
|
185
|
+
definition by copying only its template; preserve the definition and manifest too.
|
|
206
186
|
- Don't `create_component` a fresh id each fix iteration — `update_component` (same id) is the loop.
|
|
@@ -107,9 +107,9 @@ Read `critique` — it names the lowest criterion first and says why. Fix **that
|
|
|
107
107
|
thing** (re-author the relevant ops), then score again. One criterion per pass:
|
|
108
108
|
chasing all four at once thrashes. Example: editorial 4 "two subjects off-center
|
|
109
109
|
vs centered intent" → tighten the reframe onto face-0 → re-score. A layout critique
|
|
110
|
-
("cramped", "unbalanced", "text crowds the art") on a component
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
("cramped", "unbalanced", "text crowds the art") on a laid-out card or component is a
|
|
111
|
+
change to its CSS grid tracks or flex basis. Re-author those; never nudge pixels (see
|
|
112
|
+
`cueframe-component-authoring`).
|
|
113
113
|
|
|
114
114
|
## 5. Stop — then render
|
|
115
115
|
|
|
@@ -82,12 +82,11 @@ product demo:
|
|
|
82
82
|
| device/browser frame | `device-mockup-zoom` / `browser-flow` | overlay |
|
|
83
83
|
| captions (if narrated) | the top-level `captions` field | — |
|
|
84
84
|
| finish/look | `color-grade` + `vignette` | effect |
|
|
85
|
-
| several cells sharing space that should reflow (feature grid, rail + fluid content) | author
|
|
85
|
+
| several cells sharing space that should reflow (feature grid, rail + fluid content) | author one card or native component laid out with CSS flex/grid (see `cueframe-component-authoring`) | overlay (authored) |
|
|
86
86
|
|
|
87
87
|
Static PiP / split-screen is `region` + `fit` on the clips (EX3 / EX4). Cells that must
|
|
88
|
-
**reflow together** or hold mixed content are one authored component
|
|
89
|
-
|
|
90
|
-
no children, so on its own it draws only the rig outlines.
|
|
88
|
+
**reflow together** or hold mixed content are one authored card or native component laid out
|
|
89
|
+
with CSS flex/grid.
|
|
91
90
|
|
|
92
91
|
A primitive is the `source` of a clip; the clip wraps it with `id`/`startTime`/`duration`:
|
|
93
92
|
- Overlay clip (on a `kind:"overlay"` track): `{ "id":"t1", "startTime":1, "duration":3, "source":{ "kind":"overlay", "primitiveId":"lower-third", "params":{…}, "zPlane":"front" } }`.
|
|
@@ -54,7 +54,7 @@ A correction to one sibling does not require rendering the master or the other u
|
|
|
54
54
|
the beat exists to show.
|
|
55
55
|
4. **Fix per-sibling, surgically.** Where a crop or card misbehaves, `apply_composition` on
|
|
56
56
|
THAT sibling — nudge a `setCropIntents` segment, resize a card for the narrower frame
|
|
57
|
-
(re-
|
|
57
|
+
(re-apply its `clip.setKeyframes` if you replace the card). Don't
|
|
58
58
|
re-edit the story; the story is the master's.
|
|
59
59
|
5. **Confirm the audio carried.** The master's decided audio — bed, ducking, beat-aligned cuts
|
|
60
60
|
(`materializeGrid` markers), or its intentional silence — rides into each sibling. Verify
|
|
@@ -55,8 +55,8 @@ affected window and regressions without automatically rerunning the full-film ju
|
|
|
55
55
|
`frame-center` in the gaps, tiling every clip. The single biggest amateur-to-pro delta in
|
|
56
56
|
product video.
|
|
57
57
|
5. **Cards for the claims and the end card.** Key lines and the closing CTA are `kind:"card"`
|
|
58
|
-
HTML in the brand's type and palette
|
|
59
|
-
`
|
|
58
|
+
HTML in the brand's type and palette, animated in its CSS or with
|
|
59
|
+
`clip.setKeyframes` in a FOLLOWING `apply_composition` call. One CTA on the end card, held
|
|
60
60
|
long enough to read, plus the date if there is one.
|
|
61
61
|
6. **Audio when requested or appropriate.** Preserve explicit silence; otherwise: `search_resources` for a bed honoring the brand kit's
|
|
62
62
|
`musicMood`, energy arcing toward the reveal; place it `audioRole:"music"`,
|
|
@@ -58,8 +58,8 @@ affected window and regressions without automatically rerunning the full-film ju
|
|
|
58
58
|
`active-speaker` or `face` focus when a person carries it, `point` focus on the action
|
|
59
59
|
otherwise. A wide shot letterboxed into 9:16 is a fail, not a fallback.
|
|
60
60
|
5. **Author the type as cards.** Brand text is `kind:"card"` HTML — your typography,
|
|
61
|
-
palette-as-fields, considered alignment —
|
|
62
|
-
`
|
|
61
|
+
palette-as-fields, considered alignment — animated in its CSS or with
|
|
62
|
+
`clip.setKeyframes` in a FOLLOWING `apply_composition` call. Catalog text primitives are
|
|
63
63
|
utility only.
|
|
64
64
|
6. **Honor requested silence or existing audio.** When a bed is appropriate, search a bed with `search_resources` honoring
|
|
65
65
|
the brand kit's `musicMood`, place it as an `audioRole:"music"` clip, then `materializeGrid`
|
|
@@ -61,8 +61,8 @@ keep the final re-render separate from previews used to resolve individual styli
|
|
|
61
61
|
hex is silent, which is why you hunt it by eye.
|
|
62
62
|
5. **Fix the holdouts surgically.** `apply_composition` edits per offending clip: rebind card
|
|
63
63
|
fields to `$brand:` tokens (so the NEXT rebrand really is one line), swap the old-wordmark
|
|
64
|
-
media, re-grade footage toward the new palette. A replaced card gets
|
|
65
|
-
`
|
|
64
|
+
media, re-grade footage toward the new palette. A replaced card gets its
|
|
65
|
+
`clip.setKeyframes` re-applied in a FOLLOWING call. If the requested scope is styling, retain its layout; rebuild only for an explicitly requested redesign.
|
|
66
66
|
6. **Review audio only when in scope.** A styling correction preserves existing audio and timing; for a broader brand change: check the bed against the
|
|
67
67
|
new kit's `musicMood`. Wrong mood → `search_resources` a replacement (license recorded),
|
|
68
68
|
same role and ducking, `materializeGrid` re-run if cut timing shifts. Still right → say so
|
|
@@ -45,8 +45,8 @@ motion and listen to affected audio, not only stills or judge scores. Keep the u
|
|
|
45
45
|
scope and acceptance criteria; approved simulated UI is not evidence of a real product capture.
|
|
46
46
|
|
|
47
47
|
- **DEFAULT** Brand/text graphics are AUTHORED — `kind:"card"` HTML (your
|
|
48
|
-
typography, layout, palette-as-fields)
|
|
49
|
-
or a
|
|
48
|
+
typography, layout, palette-as-fields) animated in CSS or with `clip.setKeyframes`,
|
|
49
|
+
or a native component (`create_component`). Catalog text primitives are for utility only.
|
|
50
50
|
**Consider:** a brand graphic rendered from a catalog text primitive.
|
|
51
51
|
- **DEFAULT** Audio is decided, never defaulted: a music bed (curated packs via
|
|
52
52
|
`import_resource {kind:"music-pack"}`) or ambience/loops (Freesound via
|
package/package.json
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cueframe",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.8.3",
|
|
4
|
+
"description": "CueFrame video editing CLI: manage media, edit compositions and render MP4s from terminals and scripts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cueframe",
|
|
7
|
-
"video",
|
|
8
|
-
"
|
|
7
|
+
"video-editing",
|
|
8
|
+
"video-rendering",
|
|
9
9
|
"composition",
|
|
10
10
|
"cli",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"remotion"
|
|
11
|
+
"automation",
|
|
12
|
+
"mcp"
|
|
14
13
|
],
|
|
15
|
-
"homepage": "https://
|
|
14
|
+
"homepage": "https://cueframe.ai/cli",
|
|
16
15
|
"author": "CueFrame, Inc.",
|
|
17
16
|
"license": "Apache-2.0",
|
|
18
17
|
"type": "module",
|
|
@@ -46,8 +45,8 @@
|
|
|
46
45
|
"@types/node": "^24.0.0",
|
|
47
46
|
"tsup": "^8.5.1",
|
|
48
47
|
"vitest": "^4.1.2",
|
|
49
|
-
"@cueframe/sdk": "^0.
|
|
50
|
-
"@cueframe/skills": "0.
|
|
48
|
+
"@cueframe/sdk": "^0.7.1",
|
|
49
|
+
"@cueframe/skills": "0.3.0"
|
|
51
50
|
},
|
|
52
51
|
"repository": {
|
|
53
52
|
"type": "git",
|
package/dist/analyze-G6XZWOBG.js
DELETED
package/dist/chunk-WJWX26F4.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import{a as g}from"./chunk-3QSA3EBY.js";var u=class extends Error{status;code;details;requestId;constructor(t,o,n){super(o.message),this.name="ApiError",this.status=t,this.code=o.code,this.details=o.details,this.requestId=n}},v=class extends Error{url;timeoutMs;constructor(t,o){super(`Request timed out after ${o}ms: ${t}`),this.name="TimeoutError",this.url=t,this.timeoutMs=o}},q=class extends Error{url;constructor(t,o){super(`Request failed before a response: ${t}`,{cause:o}),this.name="NetworkError",this.url=t}},y={baseUrl:""};function I(e){return y={timeoutMs:3e4,...e},y}function w(){return y}function et(e,t){return/^https?:\/\//.test(t)?t:e.replace(/\/$/,"")+(t.startsWith("/")?t:`/${t}`)}async function F(e,t={}){if(!y.baseUrl)throw new Error("@cueframe/sdk: createClient({baseUrl}) must be called before issuing requests.");let o=y.fetch??fetch,n=et(y.baseUrl,e),r=new Headers({accept:"application/json"});for(let[c,f]of Object.entries(y.headers??{}))r.set(c,f);t.headers&&new Headers(t.headers).forEach((c,f)=>r.set(f,c)),y.apiKey&&!r.has("authorization")&&r.set("authorization",`Bearer ${y.apiKey}`);let s=y.timeoutMs??3e4,i=new AbortController,p=setTimeout(()=>i.abort(),s),d=t.signal?AbortSignal.any([t.signal,i.signal]):i.signal;try{let c=await o(n,{...t,headers:r,signal:d}),f={};c.headers.forEach((S,x)=>{f[x.toLowerCase()]=S});let l=null;if(c.status!==204)if((c.headers.get("content-type")??"").includes("application/json"))try{l=await c.json()}catch{l=null}else l=await c.text();return{status:c.status,statusText:c.statusText,headers:f,body:l,requestId:f["x-request-id"]}}catch(c){throw t.signal?.aborted?c:i.signal.aborted?new v(n,s):new q(n,c)}finally{clearTimeout(p)}}async function a(e,t={}){let o=await F(e,t);if(o.status>=400){let n;throw o.body&&typeof o.body=="object"&&"error"in o.body?n=o.body.error??{code:"unknown",message:o.statusText}:n={code:"unknown",message:o.statusText},new u(o.status,n,o.requestId)}if(o.status!==204)return o.body}var tt={unauthorized:"The credential is invalid or expired for this target, or it belongs to a different environment. Re-authenticate the way you connected: reconnect the MCP server, run `cueframe login` in a terminal, or supply a current API key.",forbidden:"The credential's permissions don't cover this operation. The server message above names the exact missing ones and the `permissions` body that grants them; a key's permissions are fixed at creation, so this needs a NEW key, not a change to this one.",not_found:"No such resource in this org (cross-org access also reads as 404). Check the id, or enumerate with the matching list command/tool.",media_not_ready:"Media is still processing. Wait for the `media.transcribed` webhook or retry after ~30s.",suggestions_already_complete:"Suggestions already exist for this media item. To re-run, call DELETE /v1/media/:id/suggestions first to reset state.",precondition_required:"Fetch the resource with GET first to obtain its ETag, then resend the write with `If-Match: <etag>`. `cueframe composition get` returns one for you.",stale_etag:"Resource was modified since your last GET \u2014 `details.currentEtag` is the value to thread next. Re-GET (or run `cueframe composition get`) and rebase your edits before retrying.",conflict:"ETag mismatch \u2014 the resource was modified since your last GET. Re-fetch and merge before retrying.",media_in_use:"Media is referenced by other resources. See `details.projectIds` and `details.sessionIds` \u2014 remove those references before deleting.",session_in_use:"Session is referenced by a composition. See `details.projectIds` \u2014 remove those references before deleting.",render_not_cancellable:"Render is already in a terminal state (complete / error / cancelled) and cannot be cancelled.",rate_limited:"Rate limit exceeded. Back off using the `retry-after` response header.",invalid_composition:"Composition violates an invariant (see `details`). Common ones: clip_source_track_kind_mismatch, track_clips_overlap, transition_no_predecessor.",clip_not_found:"The clip suggestion publicId doesn't exist in this org. Run `cueframe clips <mediaItemId>` to enumerate valid sug_* ids.",service_unavailable:"The processing pipeline is temporarily unavailable. Retry shortly; if it persists, contact support@cueframe.ai with the requestId.",payment_required:"The request exceeds a billing limit. For generation, the estimated cost is over the per-call ceiling (see `details.estimateUsd` / `details.ceilingUsd`) \u2014 lower the requested duration or raise the ceiling."};function j(e){return e instanceof u?tt[e.code]??null:null}async function*ot(e,t,o){let n=w();if(!n?.baseUrl)throw new Error(`@cueframe/sdk: createClient({baseUrl}) must be called before ${t}.`);let r=n.baseUrl.replace(/\/$/,"")+e,s=new Headers({accept:"text/event-stream",...n.headers??{}});n.apiKey&&!s.has("authorization")&&s.set("authorization",`Bearer ${n.apiKey}`);let p=await(n.fetch??fetch)(r,{method:"GET",headers:s,signal:o.signal});if(!p.ok){let l;try{l=(await p.json()).error??{code:"unknown",message:p.statusText}}catch{l={code:"unknown",message:p.statusText}}throw new u(p.status,l,p.headers.get("x-request-id")??void 0)}if(!p.body)throw new Error("SSE response has no body");let d=p.body.getReader(),c=new TextDecoder("utf-8"),f="";try{for(;;){let{value:l,done:S}=await d.read();if(S)break;f+=c.decode(l,{stream:!0});let x=f.indexOf(`
|
|
3
|
-
|
|
4
|
-
`);for(;x!==-1;){let Qe=f.slice(0,x);f=f.slice(x+2);let B=rt(Qe);B&&(yield B),x=f.indexOf(`
|
|
5
|
-
|
|
6
|
-
`)}}}finally{try{d.releaseLock()}catch{}}}function rt(e){if(!e.trim())return null;let t=null,o=[];for(let n of e.split(`
|
|
7
|
-
`)){if(!n||n.startsWith(":"))continue;let r=n.indexOf(":");if(r===-1)continue;let s=n.slice(0,r),i=n.slice(r+1).replace(/^ /,"");s==="event"?t=i:s==="data"&&o.push(i)}return!t||o.length===0?null:{event:t,data:o.join(`
|
|
8
|
-
`)}}async function*nt(e,t,o={}){let n=`/v1/projects/${encodeURIComponent(e)}/renders/${encodeURIComponent(t)}/stream`;for await(let r of ot(n,"iterRenderEvents",o)){let s=st(r);s&&(yield s)}}function st(e){let{event:t,data:o}=e;if(o===null||t!=="progress"&&t!=="complete"&&t!=="error")return null;let n;try{n=JSON.parse(o)}catch{return null}return{event:t,data:n}}var b={};g(b,{composeFromSuggestion:()=>dt,getComposeFromSuggestionUrl:()=>G,getListMediaSuggestionsUrl:()=>L,getResetMediaSuggestionsUrl:()=>$,getSuggestBriefsUrl:()=>N,listMediaSuggestions:()=>ut,resetMediaSuggestions:()=>yt,suggestBriefs:()=>lt});var G=e=>`/v1/projects/${e}/composition/from-suggestion`,dt=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(G(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},L=(e,t)=>{let o=new URLSearchParams;Object.entries(t||{}).forEach(([r,s])=>{s!==void 0&&o.append(r,s===null?"null":String(s))});let n=o.toString();return n.length>0?`/v1/media/${e}/suggestions?${n}`:`/v1/media/${e}/suggestions`},ut=async(e,t,o)=>a(L(e,t),{...o,method:"GET"}),N=e=>`/v1/media/${e}/suggestions`,lt=async(e,t)=>a(N(e),{...t,method:"POST"}),$=e=>`/v1/media/${e}/suggestions`,yt=async(e,t)=>a($(e),{...t,method:"DELETE"});var X={};g(X,{createComponent:()=>Rt,getComponent:()=>bt,getComponentSource:()=>ht,getCreateComponentUrl:()=>_,getGetComponentSourceUrl:()=>z,getGetComponentUrl:()=>V,getListCatalogUrl:()=>J,getPatchComponentUrl:()=>W,getResolveBuiltinComponentUrl:()=>D,getUpdateComponentUrl:()=>H,listCatalog:()=>xt,patchComponent:()=>St,resolveBuiltinComponent:()=>gt,updateComponent:()=>Ct});var D=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([n,r])=>{r!==void 0&&t.append(n,r===null?"null":String(r))});let o=t.toString();return o.length>0?`/v1/components/builtin/resolve?${o}`:"/v1/components/builtin/resolve"},gt=async(e,t)=>a(D(e),{...t,method:"GET"}),J=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([n,r])=>{r!==void 0&&t.append(n,r===null?"null":String(r))});let o=t.toString();return o.length>0?`/v1/components?${o}`:"/v1/components"},xt=async(e,t)=>a(J(e),{...t,method:"GET"}),_=()=>"/v1/components",Rt=async(e,t)=>{let o=n=>{if(!n)return{};if(n instanceof Headers)return Object.fromEntries(n.entries());if(Symbol.iterator in n)return Object.fromEntries(Array.from(n,s=>Array.from(s)));let r={};for(let[s,i]of Object.entries(n))i!==void 0&&(r[s]=i);return r};return a(_(),{...t,method:"POST",headers:{"Content-Type":"application/json",...o(t?.headers)},body:JSON.stringify(e)})},H=e=>`/v1/components/${e}`,Ct=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(H(e),{...o,method:"PUT",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},W=e=>`/v1/components/${e}`,St=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(W(e),{...o,method:"PATCH",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},V=e=>`/v1/components/${e}`,bt=async(e,t)=>a(V(e),{...t,method:"GET"}),z=(e,t)=>{let o=new URLSearchParams;Object.entries(t||{}).forEach(([r,s])=>{s!==void 0&&o.append(r,s===null?"null":String(s))});let n=o.toString();return n.length>0?`/v1/components/${e}/source?${n}`:`/v1/components/${e}/source`},ht=async(e,t,o)=>a(z(e,t),{...o,method:"GET"});var te={};g(te,{cancelExport:()=>At,createFcpxmlExport:()=>It,createPremiereExport:()=>wt,getCancelExportUrl:()=>ee,getCreateFcpxmlExportUrl:()=>Z,getCreatePremiereExportUrl:()=>Y,getExport:()=>jt,getGetExportUrl:()=>Q});var Z=e=>`/v1/projects/${e}/exports/fcpxml`,It=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(Z(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},Y=e=>`/v1/projects/${e}/exports/premiere`,wt=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(Y(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},Q=(e,t)=>`/v1/projects/${e}/exports/${t}`,jt=async(e,t,o)=>a(Q(e,t),{...o,method:"GET"}),ee=(e,t)=>`/v1/projects/${e}/exports/${t}/cancel`,At=async(e,t,o)=>a(ee(e,t),{...o,method:"POST"});var Ce={};g(Ce,{analyzeTimeline:()=>Yt,analyzeTranscript:()=>Zt,createMediaUpload:()=>Ft,createUploadToken:()=>Bt,deleteMedia:()=>Dt,finalizeMedia:()=>Lt,generateMedia:()=>Nt,getAnalyzeTimelineUrl:()=>Re,getAnalyzeTranscriptUrl:()=>xe,getCreateMediaUploadUrl:()=>ne,getCreateUploadTokenUrl:()=>re,getDeleteMediaUrl:()=>ce,getFinalizeMediaUrl:()=>ie,getGenerateMediaUrl:()=>ae,getGetMediaContextUrl:()=>de,getGetMediaTranscriptUrl:()=>me,getGetMediaUrl:()=>fe,getImportMediaUrl:()=>pe,getListMediaFactsUrl:()=>ye,getListMediaUrl:()=>se,getMedia:()=>Jt,getMediaContext:()=>Ht,getMediaTranscript:()=>_t,getPurchaseMediaFactUrl:()=>ge,getSearchLibraryUrl:()=>oe,getTriggerMatteBakeUrl:()=>le,getTriggerSubjectDetectionUrl:()=>ue,importMedia:()=>$t,listMedia:()=>Gt,listMediaFacts:()=>zt,purchaseMediaFact:()=>Xt,searchLibrary:()=>Kt,triggerMatteBake:()=>Vt,triggerSubjectDetection:()=>Wt});var oe=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([n,r])=>{r!==void 0&&t.append(n,r===null?"null":String(r))});let o=t.toString();return o.length>0?`/v1/media/search?${o}`:"/v1/media/search"},Kt=async(e,t)=>a(oe(e),{...t,method:"GET"}),re=()=>"/v1/uploads/token",Bt=async(e,t)=>{let o=n=>{if(!n)return{};if(n instanceof Headers)return Object.fromEntries(n.entries());if(Symbol.iterator in n)return Object.fromEntries(Array.from(n,s=>Array.from(s)));let r={};for(let[s,i]of Object.entries(n))i!==void 0&&(r[s]=i);return r};return a(re(),{...t,method:"POST",headers:{"Content-Type":"application/json",...o(t?.headers)},body:JSON.stringify(e)})},ne=()=>"/v1/media",Ft=async(e,t)=>{let o=n=>{if(!n)return{};if(n instanceof Headers)return Object.fromEntries(n.entries());if(Symbol.iterator in n)return Object.fromEntries(Array.from(n,s=>Array.from(s)));let r={};for(let[s,i]of Object.entries(n))i!==void 0&&(r[s]=i);return r};return a(ne(),{...t,method:"POST",headers:{"Content-Type":"application/json",...o(t?.headers)},body:JSON.stringify(e)})},se=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([n,r])=>{r!==void 0&&t.append(n,r===null?"null":String(r))});let o=t.toString();return o.length>0?`/v1/media?${o}`:"/v1/media"},Gt=async(e,t)=>a(se(e),{...t,method:"GET"}),ie=e=>`/v1/media/${e}/finalize`,Lt=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(ie(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},ae=()=>"/v1/media/generate",Nt=async(e,t)=>{let o=n=>{if(!n)return{};if(n instanceof Headers)return Object.fromEntries(n.entries());if(Symbol.iterator in n)return Object.fromEntries(Array.from(n,s=>Array.from(s)));let r={};for(let[s,i]of Object.entries(n))i!==void 0&&(r[s]=i);return r};return a(ae(),{...t,method:"POST",headers:{"Content-Type":"application/json",...o(t?.headers)},body:JSON.stringify(e)})},pe=()=>"/v1/media/import",$t=async(e,t)=>{let o=n=>{if(!n)return{};if(n instanceof Headers)return Object.fromEntries(n.entries());if(Symbol.iterator in n)return Object.fromEntries(Array.from(n,s=>Array.from(s)));let r={};for(let[s,i]of Object.entries(n))i!==void 0&&(r[s]=i);return r};return a(pe(),{...t,method:"POST",headers:{"Content-Type":"application/json",...o(t?.headers)},body:JSON.stringify(e)})},ce=e=>`/v1/media/${e}`,Dt=async(e,t)=>a(ce(e),{...t,method:"DELETE"}),fe=e=>`/v1/media/${e}`,Jt=async(e,t)=>a(fe(e),{...t,method:"GET"}),me=(e,t)=>{let o=new URLSearchParams;Object.entries(t||{}).forEach(([r,s])=>{s!==void 0&&o.append(r,s===null?"null":String(s))});let n=o.toString();return n.length>0?`/v1/media/${e}/transcript?${n}`:`/v1/media/${e}/transcript`},_t=async(e,t,o)=>a(me(e,t),{...o,method:"GET"}),de=e=>`/v1/media/${e}/context`,Ht=async(e,t)=>a(de(e),{...t,method:"GET"}),ue=(e,t)=>{let o=new URLSearchParams;Object.entries(t||{}).forEach(([r,s])=>{s!==void 0&&o.append(r,s===null?"null":String(s))});let n=o.toString();return n.length>0?`/v1/media/${e}/detect-subjects?${n}`:`/v1/media/${e}/detect-subjects`},Wt=async(e,t,o)=>a(ue(e,t),{...o,method:"POST"}),le=e=>`/v1/media/${e}/matte`,Vt=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(le(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},ye=(e,t)=>{let o=new URLSearchParams;Object.entries(t||{}).forEach(([r,s])=>{s!==void 0&&o.append(r,s===null?"null":String(s))});let n=o.toString();return n.length>0?`/v1/media/${e}/facts?${n}`:`/v1/media/${e}/facts`},zt=async(e,t,o)=>a(ye(e,t),{...o,method:"GET"}),ge=e=>`/v1/media/${e}/facts`,Xt=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(ge(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},xe=e=>`/v1/media/${e}/transcript/analyze`,Zt=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(xe(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},Re=()=>"/v1/timeline-analysis",Yt=async(e,t)=>{let o=n=>{if(!n)return{};if(n instanceof Headers)return Object.fromEntries(n.entries());if(Symbol.iterator in n)return Object.fromEntries(Array.from(n,s=>Array.from(s)));let r={};for(let[s,i]of Object.entries(n))i!==void 0&&(r[s]=i);return r};return a(Re(),{...t,method:"POST",headers:{"Content-Type":"application/json",...o(t?.headers)},body:JSON.stringify(e)})};var ke={};g(ke,{createProject:()=>eo,deleteProject:()=>ro,getCreateProjectUrl:()=>be,getDeleteProjectUrl:()=>ve,getGetProjectContextUrl:()=>qe,getGetProjectUrl:()=>he,getListProjectsUrl:()=>Se,getPatchProjectUrl:()=>Pe,getProject:()=>to,getProjectContext:()=>no,listProjects:()=>Qt,patchProject:()=>oo});var Se=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([n,r])=>{r!==void 0&&t.append(n,r===null?"null":String(r))});let o=t.toString();return o.length>0?`/v1/projects?${o}`:"/v1/projects"},Qt=async(e,t)=>a(Se(e),{...t,method:"GET"}),be=()=>"/v1/projects",eo=async(e,t)=>{let o=n=>{if(!n)return{};if(n instanceof Headers)return Object.fromEntries(n.entries());if(Symbol.iterator in n)return Object.fromEntries(Array.from(n,s=>Array.from(s)));let r={};for(let[s,i]of Object.entries(n))i!==void 0&&(r[s]=i);return r};return a(be(),{...t,method:"POST",headers:{"Content-Type":"application/json",...o(t?.headers)},body:JSON.stringify(e)})},he=e=>`/v1/projects/${e}`,to=async(e,t)=>a(he(e),{...t,method:"GET"}),Pe=e=>`/v1/projects/${e}`,oo=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(Pe(e),{...o,method:"PATCH",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},ve=e=>`/v1/projects/${e}`,ro=async(e,t)=>a(ve(e),{...t,method:"DELETE"}),qe=e=>`/v1/projects/${e}/context`,no=async(e,t)=>a(qe(e),{...t,method:"GET"});var Ue={};g(Ue,{cancelRender:()=>co,createRender:()=>so,getCancelRenderUrl:()=>je,getCreateRenderUrl:()=>Ee,getGetRenderUrl:()=>we,getListProjectRendersUrl:()=>Te,getPreviewClipUrl:()=>Ie,getRefreshRenderUrlUrl:()=>Oe,getRender:()=>po,getRetryRenderUrl:()=>Ae,getStreamRenderUrl:()=>Me,listProjectRenders:()=>io,previewClip:()=>ao,refreshRenderUrl:()=>mo,retryRender:()=>fo,streamRender:()=>uo});var Ee=e=>`/v1/projects/${e}/renders`,so=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(Ee(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},Te=(e,t)=>{let o=new URLSearchParams;Object.entries(t||{}).forEach(([r,s])=>{s!==void 0&&o.append(r,s===null?"null":String(s))});let n=o.toString();return n.length>0?`/v1/projects/${e}/renders?${n}`:`/v1/projects/${e}/renders`},io=async(e,t,o)=>a(Te(e,t),{...o,method:"GET"}),Ie=e=>`/v1/projects/${e}/preview-clip`,ao=async(e,t,o)=>{let n=r=>{if(!r)return{};if(r instanceof Headers)return Object.fromEntries(r.entries());if(Symbol.iterator in r)return Object.fromEntries(Array.from(r,i=>Array.from(i)));let s={};for(let[i,p]of Object.entries(r))p!==void 0&&(s[i]=p);return s};return a(Ie(e),{...o,method:"POST",headers:{"Content-Type":"application/json",...n(o?.headers)},body:JSON.stringify(t)})},we=(e,t)=>`/v1/projects/${e}/renders/${t}`,po=async(e,t,o)=>a(we(e,t),{...o,method:"GET"}),je=(e,t)=>`/v1/projects/${e}/renders/${t}/cancel`,co=async(e,t,o)=>a(je(e,t),{...o,method:"POST"}),Ae=(e,t)=>`/v1/projects/${e}/renders/${t}/retry`,fo=async(e,t,o)=>a(Ae(e,t),{...o,method:"POST"}),Oe=(e,t)=>`/v1/projects/${e}/renders/${t}/refresh-url`,mo=async(e,t,o)=>a(Oe(e,t),{...o,method:"POST"}),Me=(e,t)=>`/v1/projects/${e}/renders/${t}/stream`,uo=async(e,t,o)=>a(Me(e,t),{...o,method:"GET"});var $e={};g($e,{createSession:()=>xo,deleteSession:()=>Co,getCreateSessionUrl:()=>Be,getDeleteSessionUrl:()=>Ge,getGetMuxJobUrl:()=>Ne,getGetSessionUrl:()=>Fe,getListSessionsUrl:()=>Ke,getMuxJob:()=>bo,getSession:()=>Ro,getTriggerMuxJobUrl:()=>Le,listSessions:()=>go,triggerMuxJob:()=>So});var Ke=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([n,r])=>{r!==void 0&&t.append(n,r===null?"null":String(r))});let o=t.toString();return o.length>0?`/v1/sessions?${o}`:"/v1/sessions"},go=async(e,t)=>a(Ke(e),{...t,method:"GET"}),Be=()=>"/v1/sessions",xo=async(e,t)=>{let o=n=>{if(!n)return{};if(n instanceof Headers)return Object.fromEntries(n.entries());if(Symbol.iterator in n)return Object.fromEntries(Array.from(n,s=>Array.from(s)));let r={};for(let[s,i]of Object.entries(n))i!==void 0&&(r[s]=i);return r};return a(Be(),{...t,method:"POST",headers:{"Content-Type":"application/json",...o(t?.headers)},body:JSON.stringify(e)})},Fe=e=>`/v1/sessions/${e}`,Ro=async(e,t)=>a(Fe(e),{...t,method:"GET"}),Ge=e=>`/v1/sessions/${e}`,Co=async(e,t)=>a(Ge(e),{...t,method:"DELETE"}),Le=e=>`/v1/sessions/${e}/mux-job`,So=async(e,t)=>a(Le(e),{...t,method:"POST"}),Ne=e=>`/v1/sessions/${e}/mux-job`,bo=async(e,t)=>a(Ne(e),{...t,method:"GET"});var k="X-CueFrame-Client";var Eo=/^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$/,To=["web","console","clips"];function Io(e,t){return To.includes(e)===(t==="browser")}function De(e){if(!Eo.test(e.version))throw new Error(`${k} cannot carry the version ${JSON.stringify(e.version)}.`);if(!Io(e.surface,e.os))throw new Error(`${k} cannot carry ${e.surface} on ${e.os}.`);return`${e.surface}/${e.version}/${e.os}`}function Je(e){return e==="darwin"?"macos":e==="win32"?"windows":e==="linux"?"linux":"unknown"}import{readFileSync as wo,writeFileSync as jo,mkdirSync as Ao,existsSync as Oo}from"fs";import{join as _e}from"path";import{homedir as Mo}from"os";var m={Success:0,UserError:2,ApiClientError:3,ApiServerError:4,NetworkError:5,PaymentRequired:6};function A(e){if(e instanceof u){if(e.status===402)return m.PaymentRequired;if(e.status>=500)return m.ApiServerError;if(e.status>=400)return m.ApiClientError}let t=e?.exitCode;return typeof t=="number"?t:m.NetworkError}function DP(e){return e===402?m.PaymentRequired:e>=500?m.ApiServerError:e>=400?m.ApiClientError:m.Success}function h(e){process.exit(e)}var He=_e(Mo(),".cueframe"),E=_e(He,"auth.json"),R={local:"http://127.0.0.1:3211",prod:"https://api.cueframe.ai"},Uo="prod";function zP(e=Uo){return process.env.CUEFRAME_API_URL??R[e]??R.prod}var P=class extends Error{constructor(o,n){super(n);this.code=o;this.name="AuthStoreError"}code;exitCode=m.UserError};function XP(e){if(!/^cf_(?:live|test)_[A-Za-z0-9_-]{8,}$/.test(e))throw new P("auth_invalid","Invalid API key. Expected a key beginning with cf_live_ or cf_test_.")}function We(){if(!Oo(E))return{targets:{}};let e=wo(E,"utf-8");try{return JSON.parse(e)}catch{throw new P("auth_config_corrupt",`${E} is corrupt and cannot be parsed. Delete it and re-run \`cueframe login\`.`)}}function Ko(e){Ao(He,{recursive:!0,mode:448}),jo(E,JSON.stringify(e,null,2),{mode:384})}function ZP(e,t="prod",o){let n=We(),r=R[t]??R.prod;n.targets[t]={apiKey:e,apiUrl:o??r},Ko(n)}function Ve(e="prod"){let t=process.env.CUEFRAME_API_KEY,o=process.env.CUEFRAME_API_URL;if(t)return{apiKey:t,apiUrl:o??R[e]??R.prod};let r=We().targets[e];if(!r)throw new P("auth_missing",`No auth configured for target "${e}". Run \`cueframe login --target ${e}\` (browser sign-in) or \`cueframe auth <api-key> --target ${e}\` (CI / headless).`);return r}function Bo(){return{[k]:De({surface:"cli",version:"0.8.1",os:Je(process.platform)})}}function ze(e){let{apiKey:t,apiUrl:o}=Ve(e);I({baseUrl:o,apiKey:t,headers:Bo()})}function T(e){let t=e instanceof u?`${e.message} (${e.code}${e.status?`, ${e.status}`:""})`:e instanceof Error?e.message:String(e),o=j(e);return o?`${t}
|
|
9
|
-
\u2192 ${o}`:t}import Fo from"ora";import Go from"pino";var Lo="v1",Xe=Go({level:"trace",base:{schemaVersion:Lo},messageKey:"event",timestamp:()=>`,"ts":${Date.now()}`,formatters:{level:()=>({_:0})}});function O(e,t){return Fo({text:e,isSilent:!!t}).start()}function C(e,t,o={}){e.json&&Xe.info(o,t)}function iv(e,t,o){if(e.json){console.log(JSON.stringify(t,null,2));return}o()}function M(e,t,o,n){let r=o instanceof Error?o.message:String(o),s=typeof o?.code=="string"?o.code:void 0,i=o?.status;e.json?Xe.info({phase:t,message:r,...s?{code:s}:{},...typeof i=="number"?{status:i}:{},exitCode:n},"error"):process.stderr.write(`\u2716 ${t}: ${T(o)}
|
|
10
|
-
`)}function U(e,t,o,n){e.json&&M(e,t,o,n)}function av(e,t){t.json||console.log(e)}function No(e){for(let t=0;t<e.length;t++){let o=e.charCodeAt(t);if(o<=31||o===127)return!0}return!1}function Ye(e,t,o){e.length===0&&Ze(`${t} is empty.`,o),No(e)&&Ze(`${t} contains a control character (newline, tab, or null byte). Identifiers must be plain text \u2014 check for a stray copy-paste artifact.`,o)}function Ze(e,t){M(t,"preflight",new Error(e),m.UserError),h(m.UserError)}var $o=5e3,K=300*1e3;async function xv(e,t){if(Ye(e,"mediaItemId",t),t.dryRun){C(t,"analyze_dry_run",{mediaItemId:e,target:t.target,wait:!!t.wait,wouldCall:"POST /v1/media/:id/suggestions"}),t.json||console.log(`[dry-run] Would trigger clip analysis for ${e} on ${t.target}.`);return}ze(t.target),C(t,"analyze_prepare",{mediaItemId:e,target:t.target,wait:!!t.wait});let o=O("Triggering clip analysis\u2026",t.json),n;try{n=await b.suggestBriefs(e),o.succeed(`Analysis ${n.alreadyRunning?"already running":"started"}`)}catch(i){o.fail(`Failed to start analysis: ${T(i)}`),U(t,"analyze_prepare",i,A(i)),h(A(i))}if(C(t,"analyze_ready",{mediaItemId:e,status:n.status,alreadyRunning:!!n.alreadyRunning}),!t.wait){t.json||console.log(`
|
|
11
|
-
Run \`cueframe clips ${e}\` to check results.`);return}let r=O("Waiting for analysis to complete\u2026",t.json),s=Date.now();for(;;){await new Promise(i=>setTimeout(i,$o)),Date.now()-s>K&&(r.fail(`Timed out after ${Math.round(K/1e3)}s. Run \`cueframe clips ${e}\` to check later.`),U(t,"analyze_progress",new Error(`Analysis did not complete within ${Math.round(K/1e3)}s`),m.UserError),h(m.UserError));try{let i=await b.listMediaSuggestions(e);if(i.suggestions.length>0){r.succeed(`Analysis complete \u2014 ${i.suggestions.length} clips found`),C(t,"analyze_complete",{mediaItemId:e,durationMs:Date.now()-s,suggestionCount:i.suggestions.length,suggestions:i.suggestions}),t.json||i.suggestions.forEach((d,c)=>{let f=(d.trim.end-d.trim.start).toFixed(1);console.log(`
|
|
12
|
-
${c+1}. ${d.title} (${d.id})`),console.log(` ${d.trim.start.toFixed(1)}s \u2013 ${d.trim.end.toFixed(1)}s (${f}s) score: ${d.score}`),console.log(` ${d.reason}`)});return}let p=Math.round((Date.now()-s)/1e3);r.text=`Waiting for analysis\u2026 (${p}s)`,C(t,"analyze_progress",{mediaItemId:e,elapsedSec:p})}catch{}}}export{u as a,I as b,F as c,a as d,j as e,nt as f,b as g,X as h,te as i,Ce as j,ke as k,Ue as l,$e as m,m as n,A as o,DP as p,h as q,zP as r,XP as s,ZP as t,Bo as u,ze as v,T as w,O as x,C as y,iv as z,M as A,U as B,av as C,Ye as D,xv as E};
|