cueframe 0.6.0 → 0.8.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.
@@ -382,7 +382,7 @@ Each playbook is an illustrative format default; explicit intent and scope gover
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
- | Own / fork a custom graphic | `get_component_source` / `create_component` (self-contained tsxSource) / `update_component` — **see `cueframe-component-authoring`** for the full fork→preview→push loop |
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
386
  | Several cells that share space and must reflow together (feature grid, rail + fluid content, cast + type) | An authored component on `FlexLayout` from `@cueframe/animate` — tracks are **weights** (`{ weight: 0, basis: px }` = content-sized, pushes neighbours), `tracksEnd` animates the reflow per frame, `fits` per cell (`stretch`/`contain`/`cover`/`position`/`scale`/`matte`). 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. |
@@ -261,4 +261,4 @@ frames) before declaring done.
261
261
  which is the engine's job now.
262
262
  - This recipe lives in a skill on purpose: the engine stays use-case-free; the
263
263
  product opinions (what to extract, which overlays, how to brand) live here and
264
- ship via `npx -y cueframe@0.6 install`.
264
+ ship via `npx -y cueframe@0.8 install`.
@@ -31,11 +31,11 @@ both MCP and this CLI exist so you do not have to.
31
31
 
32
32
  ## Overview
33
33
 
34
- The `cueframe` CLI drives the CueFrame platform from the terminal. Optimized for agents — every command supports `--json` (NDJSON event stream) and the binary self-describes via `npx -y cueframe@0.6 describe --json`.
34
+ The `cueframe` CLI drives the CueFrame platform from the terminal. Optimized for agents — every command supports `--json` (NDJSON event stream) and the binary self-describes via `npx -y cueframe@0.8 describe --json`.
35
35
 
36
- **Run it as `npx -y cueframe@0.6 …` — every command in this skill is written that way, and
36
+ **Run it as `npx -y cueframe@0.8 …` — every command in this skill is written that way, and
37
37
  that form is executable verbatim.** There is no `cueframe` on your PATH unless you put it
38
- there: `npx -y cueframe@0.6 install` wires the MCP server and drops these skills, but npx
38
+ there: `npx -y cueframe@0.8 install` wires the MCP server and drops these skills, but npx
39
39
  unpacks to an ephemeral cache, so it installs no binary. `npx` re-resolves the current
40
40
  version on each call (~0.5s warm, ~5s cold), which also means these commands never go
41
41
  stale. If you are running many in a row and want to skip that, `npm i -g cueframe` once
@@ -46,9 +46,9 @@ the documented path.
46
46
  **The framework is three verbs:** `upload` (register media) → `composition put` (author the timeline) → `render` (→ MP4). The `Composition` is the contract — render takes the saved composition, nothing else. Everything domain-specific (AI clip suggestions, FCPXML/Premiere export) is a **use-case on-ramp** layered on top, not part of the core.
47
47
 
48
48
  **Core principle — discover, don't guess.** Never author a composition from the
49
- examples below alone. The live contract is `npx -y cueframe@0.6 schema composition` (prints
49
+ examples below alone. The live contract is `npx -y cueframe@0.8 schema composition` (prints
50
50
  the real wire schema the server enforces); confirm any draft with
51
- `npx -y cueframe@0.6 composition validate -b @file.json` BEFORE you render. Validation is
51
+ `npx -y cueframe@0.8 composition validate -b @file.json` BEFORE you render. Validation is
52
52
  free and instant; a render is neither. The examples here are orientation — the
53
53
  schema is the source of truth.
54
54
 
@@ -70,20 +70,20 @@ delivery, not checking each edit. CLI validation does not replace visual evidenc
70
70
  # Commands run against production; auth is stored in ~/.cueframe/auth.json.
71
71
 
72
72
  # 1. Register media (one or many — mixed video / image / audio).
73
- npx -y cueframe@0.6 upload "/path/to/source.mp4" --json # → mediaItemId (process_complete)
73
+ npx -y cueframe@0.8 upload "/path/to/source.mp4" --json # → mediaItemId (process_complete)
74
74
 
75
75
  # 2. Create a project (sets aspect/format).
76
- npx -y cueframe@0.6 project create -n "promo" -a 9:16 --json # → projectId
76
+ npx -y cueframe@0.8 project create -n "promo" -a 9:16 --json # → projectId
77
77
 
78
78
  # 3. Author the composition: tracks of clips (per-clip reframe/trim), optional
79
79
  # captions (captions.segments), brandKitId. The composition is the SSOT.
80
80
  # Discover the live schema, then validate the draft BEFORE saving/rendering:
81
- npx -y cueframe@0.6 schema composition # → live wire schema
82
- npx -y cueframe@0.6 composition validate -b @composition.json # → ✓ valid | per-field errors (no render)
83
- npx -y cueframe@0.6 composition put <projectId> -b @composition.json # ETag auto
81
+ npx -y cueframe@0.8 schema composition # → live wire schema
82
+ npx -y cueframe@0.8 composition validate -b @composition.json # → ✓ valid | per-field errors (no render)
83
+ npx -y cueframe@0.8 composition put <projectId> -b @composition.json # ETag auto
84
84
 
85
85
  # 4. Render the saved composition to MP4. SSE-watches + downloads when done.
86
- npx -y cueframe@0.6 render <projectId> -o ./out.mp4 --json
86
+ npx -y cueframe@0.8 render <projectId> -o ./out.mp4 --json
87
87
  # → render_queued → render_progress* → render_complete
88
88
  #
89
89
  # `render` takes ONLY a projectId and renders the SAVED composition (tracks +
@@ -92,14 +92,14 @@ npx -y cueframe@0.6 render <projectId> -o ./out.mp4 --json
92
92
  # a hand-authored composition; if absent, the clip is rendered without captions.
93
93
 
94
94
  # Reattach to an in-flight render later (e.g. after disconnect):
95
- npx -y cueframe@0.6 render watch <renderId> -p <projectId> -o ./out.mp4 --json
95
+ npx -y cueframe@0.8 render watch <renderId> -p <projectId> -o ./out.mp4 --json
96
96
  ```
97
97
 
98
98
  ## Anatomy of a composition.json
99
99
 
100
100
  This section orients you to the shape; the authoritative contract is
101
- `npx -y cueframe@0.6 schema composition` (the live wire schema) — run it when a field is
102
- unclear, and `npx -y cueframe@0.6 composition validate -b @file.json` to check a draft.
101
+ `npx -y cueframe@0.8 schema composition` (the live wire schema) — run it when a field is
102
+ unclear, and `npx -y cueframe@0.8 composition validate -b @file.json` to check a draft.
103
103
 
104
104
  `composition put` takes the **bare** `Composition` object (no wrapper; the CLI POSTs
105
105
  the `@file` verbatim and handles the ETag). Minimal valid shape:
@@ -166,18 +166,18 @@ the composition yourself (above).
166
166
 
167
167
  ```bash
168
168
  # 1. Upload + chain the AI clip pass.
169
- npx -y cueframe@0.6 upload "/path/to/source.mp4" --analyze --json # mediaItemId + suggestions
170
- # (or run it explicitly: npx -y cueframe@0.6 analyze <mediaItemId> --wait --json)
169
+ npx -y cueframe@0.8 upload "/path/to/source.mp4" --analyze --json # mediaItemId + suggestions
170
+ # (or run it explicitly: npx -y cueframe@0.8 analyze <mediaItemId> --wait --json)
171
171
 
172
172
  # 2. List clip suggestions (publicId sug_…, title, startMs/endMs, score).
173
- npx -y cueframe@0.6 clips <mediaItemId> --json
173
+ npx -y cueframe@0.8 clips <mediaItemId> --json
174
174
 
175
175
  # 3. Create a project AND author its composition from the chosen suggestion.
176
- npx -y cueframe@0.6 project create -n "paul-klein-shorts" -a 9:16 --from-suggestion <sug_…> --json
177
- # (or, on an existing project: npx -y cueframe@0.6 composition from-suggestion <projectId> -s <sug_…>)
176
+ npx -y cueframe@0.8 project create -n "paul-klein-shorts" -a 9:16 --from-suggestion <sug_…> --json
177
+ # (or, on an existing project: npx -y cueframe@0.8 composition from-suggestion <projectId> -s <sug_…>)
178
178
 
179
179
  # 4. Render — same framework verb as above.
180
- npx -y cueframe@0.6 render <projectId> -o ./out.mp4 --json
180
+ npx -y cueframe@0.8 render <projectId> -o ./out.mp4 --json
181
181
  ```
182
182
 
183
183
  Both flows converge on `composition put` → `render`; the on-ramp just authors the
@@ -194,51 +194,51 @@ video and composites it (the shared renderer never runs your code).
194
194
 
195
195
  ```bash
196
196
  # 1. Scaffold a workspace (writes cueframe.json + cueframe/components/). Idempotent.
197
- npx -y cueframe@0.6 init --json
197
+ npx -y cueframe@0.8 init --json
198
198
 
199
199
  # 2a. Scaffold a component in cueframe/components/<id>/ as owned, editable source.
200
- npx -y cueframe@0.6 new component my-badge --json
200
+ npx -y cueframe@0.8 new component my-badge --json
201
201
 
202
- # 2b. …or FORK a built-in: discover ids from the catalog (npx -y cueframe@0.6 api GET /v1/components),
203
- # fetch that primitive's source with the `get_component_source` tool, and paste it into
204
- # cueframe/components/my-badge/index.tsx as your starting point.
202
+ # Built-in forks require an exact v3 sourceSnapshot; retain its TSX, definition and manifest.
203
+ # The CLI sends index.tsx + definition.json + the workspace asset manifest as one v3 source.
205
204
 
206
205
  # 3. Edit cueframe/components/<id>/ — it's standard Remotion. The component is mounted
207
- # ({ durationInFrames, params, assets, render }) at render; declare its v2 manifest.
206
+ # with params/assets/render; definition.json declares its source-v3 capabilities and timing.
207
+ # The nested asset manifest remains version 2.
208
208
 
209
209
  # 4. Publish to CueFrame so it can render (uploads index.tsx as the component's tsxSource).
210
- npx -y cueframe@0.6 push my-badge --json # one component
211
- npx -y cueframe@0.6 sync --json # every component in the workspace
210
+ npx -y cueframe@0.8 push my-badge --json # one component
211
+ npx -y cueframe@0.8 sync --json # every component in the workspace
212
212
 
213
213
  # 5. Reference the component id in your composition as a {kind:'component', componentId, props}
214
214
  # clip, then render with the same verb. Preview a single component first via the MCP
215
215
  # preview_component loop.
216
- npx -y cueframe@0.6 render <projectId> -o ./out.mp4 --json
216
+ npx -y cueframe@0.8 render <projectId> -o ./out.mp4 --json
217
217
  ```
218
218
 
219
219
  ## Quick reference
220
220
 
221
221
  | Need to… | Command |
222
222
  |---|---|
223
- | See every command + flags | `npx -y cueframe@0.6 describe --json` |
224
- | Install/refresh managed skills | `npx -y cueframe@0.6 install` (shared `~/.agents/skills` and Claude `~/.claude/skills`; locally changed or untracked packages are preserved and reported) |
225
- | List projects | `npx -y cueframe@0.6 list -n 50` |
226
- | List your media | `npx -y cueframe@0.6 clips` (no mediaId arg) |
227
- | Discover the live composition schema | `npx -y cueframe@0.6 schema composition` |
228
- | Validate a draft (no save, no render) | `npx -y cueframe@0.6 composition validate -b @plan.json` |
229
- | Read composition + ETag | `npx -y cueframe@0.6 composition get <projectId> --json` |
230
- | Write composition | `npx -y cueframe@0.6 composition put <projectId> -b @plan.json` (ETag auto) |
231
- | Export to Final Cut | `npx -y cueframe@0.6 export fcpxml <projectId> -s <sug> --wait -o cut.zip` |
232
- | Export to Premiere | `npx -y cueframe@0.6 export premiere <projectId> -s <sug> --wait -o cut.zip` |
233
- | Anything not wrapped | `npx -y cueframe@0.6 api GET /v1/... -i` (escape hatch, like `gh api`) |
234
- | Validate a composition without mutating | `npx -y cueframe@0.6 composition validate -b @plan.json` |
223
+ | See every command + flags | `npx -y cueframe@0.8 describe --json` |
224
+ | Install/refresh managed skills | `npx -y cueframe@0.8 install` (shared `~/.agents/skills` and Claude `~/.claude/skills`; locally changed or untracked packages are preserved and reported) |
225
+ | List projects | `npx -y cueframe@0.8 list -n 50` |
226
+ | List your media | `npx -y cueframe@0.8 clips` (no mediaId arg) |
227
+ | Discover the live composition schema | `npx -y cueframe@0.8 schema composition` |
228
+ | Validate a draft (no save, no render) | `npx -y cueframe@0.8 composition validate -b @plan.json` |
229
+ | Read composition + ETag | `npx -y cueframe@0.8 composition get <projectId> --json` |
230
+ | Write composition | `npx -y cueframe@0.8 composition put <projectId> -b @plan.json` (ETag auto) |
231
+ | Export to Final Cut | `npx -y cueframe@0.8 export fcpxml <projectId> -s <sug> --wait -o cut.zip` |
232
+ | Export to Premiere | `npx -y cueframe@0.8 export premiere <projectId> -s <sug> --wait -o cut.zip` |
233
+ | Anything not wrapped | `npx -y cueframe@0.8 api GET /v1/... -i` (escape hatch, like `gh api`) |
234
+ | Validate a composition without mutating | `npx -y cueframe@0.8 composition validate -b @plan.json` |
235
235
 
236
236
  ## Auth
237
237
 
238
238
  Commands run against production. Authenticate once:
239
239
 
240
- - `npx -y cueframe@0.6 login` — OAuth device flow (laptops).
241
- - `npx -y cueframe@0.6 auth <cf_live_… key>` — save a static API key (CI / headless).
240
+ - `npx -y cueframe@0.8 login` — OAuth device flow (laptops).
241
+ - `npx -y cueframe@0.8 auth <cf_live_… key>` — save a static API key (CI / headless).
242
242
 
243
243
  Creds are stored in `~/.cueframe/auth.json`; the key can also come from the `CUEFRAME_API_KEY` env var.
244
244
 
@@ -256,11 +256,11 @@ Errors come as `{"event":"error", "phase":"<step>", "message":"...", "code":"...
256
256
 
257
257
  | Mistake | Reality |
258
258
  |---|---|
259
- | Reaching for `curl …/v1/...` because the CLI didn't have a command | Use `npx -y cueframe@0.6 api METHOD /v1/...` — same auth, same JSON pipeline. |
260
- | Polling for render completion with a sleep loop | `npx -y cueframe@0.6 render` already SSE-watches and exits when done. Use `render watch <id>` to reattach. |
259
+ | Reaching for `curl …/v1/...` because the CLI didn't have a command | Use `npx -y cueframe@0.8 api METHOD /v1/...` — same auth, same JSON pipeline. |
260
+ | Polling for render completion with a sleep loop | `npx -y cueframe@0.8 render` already SSE-watches and exits when done. Use `render watch <id>` to reattach. |
261
261
  | Skipping `--json` and trying to parse human output | Human output is for humans. Agents always use `--json`. |
262
262
  | Hand-editing the composition without an ETag | `composition put` GETs the ETag for you; `from-suggestion` rebuilds from a clip. Don't hand-craft unless you have to. |
263
- | Calling `npx -y cueframe@0.6 render <projectId> <suggestionId>` (old two-positional shape) | `render` takes ONLY a projectId; commander rejects the extra arg. It renders the SAVED composition — author it first via `composition put` (hand-author) or `project create --from-suggestion <sug>` (clip-finder on-ramp). |
263
+ | Calling `npx -y cueframe@0.8 render <projectId> <suggestionId>` (old two-positional shape) | `render` takes ONLY a projectId; commander rejects the extra arg. It renders the SAVED composition — author it first via `composition put` (hand-author) or `project create --from-suggestion <sug>` (clip-finder on-ramp). |
264
264
  | Assuming `render` needs a clip suggestion / that captions only come from a suggestion | No — `render` renders the saved composition directly; a suggestion is optional. Captions come from `composition.captions` (author them in the `put` JSON). The clip-suggestion on-ramp just fills those captions for you by slicing the transcript at author time. |
265
265
 
266
266
  ## When this skill applies (routing)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: cueframe-component-authoring
3
- description: Use when authoring, forking, or customizing a CueFrame motion-graphics component/primitive/graphic — e.g. "customize the cinematic-title primitive", "make my own animated lower-third", "fork a CueFrame template and edit it", "author a custom graphic and render it". Covers cloud MCP, local desktop, and CLI project paths through the same component-v2 contract. Use BEFORE hand-writing a component from scratch — forking a built-in is usually the faster start.
3
+ description: Use when authoring, forking, or customizing a CueFrame motion-graphics component/primitive/graphic — e.g. "customize the cinematic-title primitive", "make my own animated lower-third", "fork a CueFrame template and edit it", "author a custom graphic and render it". Covers cloud MCP, local desktop, and CLI project paths using complete versioned source snapshots. Use BEFORE hand-writing a component from scratch — forking a built-in is usually the faster start.
4
4
  ---
5
5
 
6
6
  # CueFrame component authoring (fork / customize / own)
@@ -16,8 +16,8 @@ For scoped revisions, preserve unrelated timing, media, audio and shared brand-k
16
16
  Use `cueframe-compose-loop`'s [preview workflow](../cueframe-compose-loop/references/preview-workflow.md)
17
17
  to select hosted or local evidence and separate iteration from final delivery.
18
18
 
19
- CueFrame ships 87 built-in motion-graphics **primitives** (titles, lower-thirds, captions,
20
- effects, transitions, scenes). You can **fork any of them as editable source**, customize it,
19
+ CueFrame ships built-in motion-graphics definitions (titles, lower-thirds, captions,
20
+ effects, transitions and layouts). Discover the current catalog; fork an exact definition, customize it,
21
21
  and render it on CueFrame — or author a brand-new component. This is shadcn-for-video: own the
22
22
  component code; CueFrame is the reliable engine that renders it.
23
23
 
@@ -26,38 +26,50 @@ component code; CueFrame is the reliable engine that renders it.
26
26
  See the `cueframe-scene-shot` skill and `cueframe://scene-shot`. Author a component here when
27
27
  you need behaviour the scene contract does not express.
28
28
 
29
- **The two facts that shape everything:**
30
- - A component is mounted through contract v2 as
31
- **`({ durationInFrames, params, assets, render }) => JSX`**. `render` contains frame, fps,
32
- dimensions, deterministic seed, and color space. A from-scratch component must default-export
33
- this shape; older components may ignore the added properties.
34
- - Neither renderer runs authored component source in the editor process. Hosted org components are
35
- baked in an isolated box. A local project compiles source in a separate headless-Chromium child
36
- process, pins the immutable source version in the project, and uses that version for the editor and export.
29
+ **Source format matters:**
30
+ - Every executable component uses source v3: keep the complete `sourceSnapshot`, including `definition`,
31
+ `manifest`, and `tsxSource`. Its definition declares input schema, defaults, timing,
32
+ brand/media bindings and capabilities. Do not infer these from the source code or flatten
33
+ a v3 source into a v2 module. The nested asset manifest can still have `version: 2`.
34
+ - The mount signature is `({ durationInFrames, params, assets, render }) => JSX`.
35
+ Source2 inputs require explicit migration; they are not accepted for editing or rendering.
36
+ - Neither renderer runs authored source in the editor process. Local and hosted execution
37
+ use isolated render processes and exact immutable source revisions.
37
38
 
38
39
  ## Path A — MCP agent (no filesystem; source lives server-side)
39
40
 
40
- Single self-contained module per component (`create_component` takes one `tsxSource`).
41
-
42
- 1. **Fork a built-in:** `get_component_source(componentId: "cinematic-title")` → returns the
43
- primitive's editable single-module `tsxSource` (the named component + the adapter). Discover
44
- ids from the catalog (`/v1/components`). *(Or author from scratch — skip to step 2.)*
45
- 2. **Author/own it:** edit the `tsxSource`, declare its v2 `manifest`, then call
46
- `create_component({ componentId, tsxSource, manifest, … })` to store your customer-owned copy.
47
- If forked, `ejectedFrom` is stamped.
41
+ Use the live tool schema for request envelopes; hosted write fields are normally under `body`.
42
+
43
+ 1. **Discover and read a built-in:** call `list_components` with
44
+ `projection: "component-library", origin: "builtin", libraryKind: "definition"`.
45
+ Select the intended card and preserve its complete scoped source reference. Call
46
+ `get_component_source` with that reference's `componentId`, `sourceVersionId`,
47
+ `builtinCollection` (its scope collection).
48
+ ID-only builtin reads are unsupported. An unavailable exact pin is a refusal; never
49
+ retry another scope or an unpinned head.
50
+ 2. **Author/own it:** edit the complete v3 `sourceSnapshot`; use your chosen owned ID
51
+ consistently in its `componentId` and the request. Send it as `body.source` to
52
+ `create_component`, with required metadata. Preserve its definition, capability
53
+ declarations, manifest and source format. For an existing component, keep its ID and
54
+ use `update_component` with the complete source. Do not send an old pin as the identity
55
+ of changed source: saving produces a new immutable source version.
48
56
  3. **Iterate (author→preview→fix):** `preview_component({ componentId, params })` queues a durable
49
57
  preview and returns a `jobId`; call `wait_job(kind:"preview")`. The typed terminal result is a
50
58
  verified artifact produced by the same bundle contract used by final render. A failed
51
59
  `component_does_not_compile` job carries the compiler error — read it, fix via `update_component`
52
60
  (same id, in place), and preview again.
53
- **Once the component is placed in a project, pass that project on every update:**
54
- `update_component({ componentId, body: { tsxSource, projectId, compositionId? } })`. A placed clip
61
+ **Repin only the requested scope:** for all matching placements in one composition, use
62
+ `update_component({ componentId, body: { source: editedSnapshot, projectId, compositionId? } })`
63
+ with the observed revision and dependency guards. A placed clip
55
64
  renders the exact version it pins; only the composition you name moves to the new version in the
56
65
  same write (`repinned` says what moved). Without `projectId` your clips keep rendering the old
57
- version, and `placementsPinnedToPrevious` counts them.
66
+ version, and `placementsPinnedToPrevious` counts them. For a single placement, update the
67
+ source without `projectId`, then change only that clip to the returned exact source pin
68
+ using the observed concurrency guards.
58
69
  Set preview duration deliberately: `durationInFrames` is not a trim window and can retime
59
70
  duration-dependent motion. Preserve production timing; inspect the live tool's request shape.
60
- 4. **Check integration:** add it as a `{kind:'component', componentId, props}` clip
71
+ 4. **Check integration:** add it as a `{kind:'component', componentId, props:{},
72
+ execution:{version:3,componentId,sourceVersionId,params,seed}, placement:{version:3,kind:'clip'}}` clip
61
73
  (`apply_composition`), then inspect saved-composition stills or a scoped `preview_clip` for
62
74
  placement and motion. Use `create_render` → `wait_job kind:"render"` when delivering a
63
75
  finished MP4, not per tweak.
@@ -68,26 +80,32 @@ Source lives in `cueframe/components/<id>/` (the `cueframe.json` workspace); edi
68
80
  standard Remotion, push to render.
69
81
 
70
82
  ```bash
71
- npx -y cueframe@0.6 init --json # scaffold cueframe.json + cueframe/components/
72
- npx -y cueframe@0.6 new component my-badge --json # scaffold a component in the workspace
73
- # to start from a built-in instead of a blank file: fetch its source with the
74
- # `get_component_source` tool and paste it into cueframe/components/my-badge/index.tsx
83
+ npx -y cueframe@0.8 init --json # scaffold cueframe.json + cueframe/components/
84
+ npx -y cueframe@0.8 new component my-badge --json # scaffold a component in the workspace
85
+ # New scaffolds contain index.tsx, config.ts and definition.json (source v3).
86
+ # For a builtin fork, retain its TSX, complete definition and asset manifest together.
75
87
  # …edit cueframe/components/<id>/ in your editor (standard Remotion)…
76
- npx -y cueframe@0.6 push my-badge --json # upload index.tsx as the component tsxSource
77
- # or: npx -y cueframe@0.6 sync --json # push every component in the workspace
88
+ npx -y cueframe@0.8 push my-badge --json # upload index.tsx as the component tsxSource
89
+ # or: npx -y cueframe@0.8 sync --json # push every component in the workspace
78
90
  # reference the component id in your composition, then render:
79
- npx -y cueframe@0.6 render <projectId> -o ./out.mp4 --json
91
+ npx -y cueframe@0.8 render <projectId> -o ./out.mp4 --json
80
92
  ```
81
93
 
82
- The CLI writes a 3-file layout (`index.tsx` = renderable module + adapter; `_config.ts` + `config.ts`
83
- = local workspace metadata). `push` sends `index.tsx` as the single `tsxSource` — the same shape the
84
- MCP path stores. To preview before a full render, push it and use the MCP `preview_component` loop.
94
+ The CLI writes `index.tsx` (module), `config.ts` (metadata) and `definition.json` (complete v3
95
+ input schema, defaults, timing and capabilities). Its asset manifest lives at
96
+ `cueframe.json` → `components.<id>.manifest`; that nested manifest retains version 2.
97
+ `push`/`sync` send these as one v3 `source` snapshot. For a builtin fork, copy **all three source
98
+ parts** from the selected `sourceSnapshot`: TSX, definition and manifest; choose the owned ID
99
+ consistently. Never replace a definition with the blank scaffold's defaults.
100
+ Older workspaces without `definition.json` are refused before API calls: review and declare
101
+ their real behavior instead of running `new --force` over existing source. To preview before
102
+ a full render, push it and use the appropriate component preview loop.
85
103
 
86
104
  ## Path C — Director editing a local desktop project (project-embedded source)
87
105
 
88
106
  When `get_context` reports the project lane as local, motion does not require an account or an org
89
107
  component. When exposed, use `open_component_preview` to compile an isolated preview (new source,
90
- or `component_id` for a component already in the desktop's library), `update_component_preview`
108
+ with an explicit `component_definition`, or `component_id` to preserve an existing library source), `update_component_preview`
91
109
  with exact-text edits (`{oldText, newText, replaceAll?}`, each `oldText` matching once), and
92
110
  `render_component_preview_frame` for exact frame evidence. Inspect playback for motion;
93
111
  `commit_component_preview` publishes the checked source to the library and pins the clip, with a
@@ -111,6 +129,7 @@ timeline. An inline custom overlay has this shape:
111
129
  "params": {
112
130
  "jsxSource": "export default function Graphic({ durationInFrames, params, assets, render }) { ... }",
113
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" }] },
114
133
  "componentAssets": {}
115
134
  }
116
135
  }
@@ -120,7 +139,9 @@ timeline. An inline custom overlay has this shape:
120
139
  ```
121
140
 
122
141
  The desktop compiles the module in an isolated child process and creates it in the desktop's component
123
- library. Inline source creates a component only: a clip whose component already exists with other
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
124
145
  source is refused as `component_exists`, and that component changes through edits.
125
146
  The timeline and final export render that exact version; no preview media is baked. A compile failure
126
147
  refuses the whole batch and leaves the composition untouched. Use `create_component` only for a reusable
@@ -140,10 +161,10 @@ loops, refs, constructors, typed arrays, canvas, and WebGPU when needed. Drive v
140
161
  `render.frame`/`render.fps` and seed variation from `render.seed`; ambient time/randomness is refused.
141
162
 
142
163
  ## Choosing what to fork
143
- The catalog (`/v1/components`) gives each primitive's `description` / `useWhen` / `tags` / `mood` /
144
- `tier` / `useCase` / `examples` (example params) + `propSchema` + its source via `get_component_source`.
145
- Read the spec + the code to pick a starting point. (Rendered preview thumbnails are a separate
146
- follow-up — today you choose from prose + params + source, or compose+`preview_frame` to see it.)
164
+ Use the `component-library` projection to choose a definition, inspect its input schema and
165
+ capabilities, then fetch its exact scoped source as described in Path A. Presets and recipes
166
+ are different card kinds: follow their exact definition/document references rather than treating
167
+ those IDs as source IDs. Inspect appropriate previews before judging visual behavior.
147
168
 
148
169
  ## Layout inside a component — `FlexLayout` / `solveFlex`
149
170
  When a graphic has several cells that must share space — a feature grid, a fixed rail beside
@@ -180,8 +201,6 @@ const R = params.region ?? { x: 0, y: 0, w: 1, h: 1 };
180
201
  ## Don't
181
202
  - Don't expect a forked primitive to use rich named props — read `params` inside and use the explicit
182
203
  `assets` and `render` bags for media and frame state.
183
- - Don't hand-roll a graphic from zero when a built-in is close — fork it (fetch the source with
184
- `get_component_source`, paste it into a `npx -y cueframe@0.6 new component` scaffold) and edit.
204
+ - When a built-in fits the request, fork its exact v3 snapshot using Path A. Never discard its
205
+ definition by copying only its TSX; preserve the definition and manifest too.
185
206
  - Don't `create_component` a fresh id each fix iteration — `update_component` (same id) is the loop.
186
- - Don't update a placed component without its `projectId` in the body — the placed clips keep the
187
- version they pin, so the next preview or render shows the old graphic.
@@ -23,7 +23,7 @@ When a required capability is missing, explain the limitation and the supported
23
23
  - **Claude.ai / Claude Desktop:** Settings → Connectors → add a custom connector with
24
24
  `https://api.cueframe.ai/v1/mcp`.
25
25
  - **Other agents (Cursor, Codex, VS Code, Windsurf, Zed, Antigravity CLI, Copilot CLI, …):**
26
- use `npx -y cueframe@0.6 install --help` to select the intended client; preserve existing configuration and avoid duplicate installs.
26
+ use `npx -y cueframe@0.8 install --help` to select the intended client; preserve existing configuration and avoid duplicate installs.
27
27
  - **ChatGPT:** the CLI cannot wire it because there is no local config to write; add
28
28
  `https://api.cueframe.ai/v1/mcp` as a custom connector instead.
29
29
  - **No account (pay per call):** connect
@@ -21,7 +21,7 @@ to author, not how to transport it.
21
21
 
22
22
  **Drive the primitives over MCP if you can call tools** (`new_composition` /
23
23
  `apply_composition` / `validate_composition` / `create_render` — see the
24
- `cueframe-connect` skill for the one-line setup). The `npx -y cueframe@0.6 …` commands
24
+ `cueframe-connect` skill for the one-line setup). The `npx -y cueframe@0.8 …` commands
25
25
  written out below are the same operations for a terminal or CI job with no MCP
26
26
  host; see the `cueframe-cli` skill.
27
27
 
@@ -64,7 +64,7 @@ style's) over each window. Result → `source.reframe.segments` on the clip.
64
64
  ## 3. Compose
65
65
 
66
66
  One video track, the screen recording as a media clip carrying the reframe.
67
- `npx -y cueframe@0.6 composition put <projectId> -b @composition.json` (see `cueframe-cli`).
67
+ `npx -y cueframe@0.8 composition put <projectId> -b @composition.json` (see `cueframe-cli`).
68
68
  Choose `-a 16:9` for landscape product video, `9:16` for social.
69
69
 
70
70
  ## 4. Polish — layer overlay primitives (this is what makes it look "produced")
@@ -102,21 +102,21 @@ check framing with `preview_frame` and click/zoom timing with an affected-window
102
102
  For a small revision, verify that change and relevant regressions without exporting the whole film.
103
103
  When delivering the finished video:
104
104
 
105
- `npx -y cueframe@0.6 render <projectId> -o out.mp4 --json`. Verify the output (ffprobe +
105
+ `npx -y cueframe@0.8 render <projectId> -o out.mp4 --json`. Verify the output (ffprobe +
106
106
  sample frames) before declaring done.
107
107
 
108
108
  ## Worked examples — composition JSON
109
109
 
110
110
  These are **orientation, not the contract.** The live wire schema is
111
- `npx -y cueframe@0.6 schema composition`, and the way to know a draft is correct is
112
- `npx -y cueframe@0.6 composition validate -b @composition.json` — it runs the same
111
+ `npx -y cueframe@0.8 schema composition`, and the way to know a draft is correct is
112
+ `npx -y cueframe@0.8 composition validate -b @composition.json` — it runs the same
113
113
  validator the server enforces, with no save and no render. Discover + validate
114
114
  before you render; never spend a render to find a schema mistake.
115
115
 
116
116
  Each block below is a complete `@composition.json` (the bare object `composition put`
117
117
  accepts: top-level `v`, `format`, `tracks`, optional `captions`). All five validate
118
118
  against the real wire schema + save invariants (swap the `<…MediaId>` placeholders for
119
- real `npx -y cueframe@0.6 upload` ids). Conventions that keep them valid:
119
+ real `npx -y cueframe@0.8 upload` ids). Conventions that keep them valid:
120
120
 
121
121
  - Tracks hold **`contents`** (not `clips`); each clip wraps a `source`.
122
122
  - A clip's `source.kind` must match the track `kind`: `media` → `video`/`image`/`audio`
@@ -298,4 +298,4 @@ Talking-head with an `active-speaker` reframe and a `heroText` title at `zPlane:
298
298
  background. See EX3 / EX4 below.
299
299
  - This recipe lives in a skill on purpose: the engine stays use-case-free; the
300
300
  product opinions (style, which primitives, layout) live here and ship via
301
- `npx -y cueframe@0.6 install`. Add sibling recipes (`cueframe-podcast-clip`, …) the same way.
301
+ `npx -y cueframe@0.8 install`. Add sibling recipes (`cueframe-podcast-clip`, …) the same way.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cueframe",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Agent-native CLI for CueFrame — author compositions and render video from the terminal (early-access preview).",
5
5
  "keywords": [
6
6
  "cueframe",
@@ -46,8 +46,8 @@
46
46
  "@types/node": "^24.0.0",
47
47
  "tsup": "^8.5.1",
48
48
  "vitest": "^4.1.2",
49
- "@cueframe/sdk": "^0.5.0",
50
- "@cueframe/skills": "0.1.2"
49
+ "@cueframe/sdk": "^0.6.1",
50
+ "@cueframe/skills": "0.2.2"
51
51
  },
52
52
  "repository": {
53
53
  "type": "git",
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import{E as a}from"./chunk-U3YHQOER.js";import"./chunk-3QSA3EBY.js";export{a as analyzeCommand};
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env node
2
- import{a as y}from"./chunk-3QSA3EBY.js";var u=class extends Error{status;code;details;requestId;constructor(t,r,s){super(r.message),this.name="ApiError",this.status=t,this.code=r.code,this.details=r.details,this.requestId=s}},v=class extends Error{url;timeoutMs;constructor(t,r){super(`Request timed out after ${r}ms: ${t}`),this.name="TimeoutError",this.url=t,this.timeoutMs=r}},q=class extends Error{url;constructor(t,r){super(`Request failed before a response: ${t}`,{cause:r}),this.name="NetworkError",this.url=t}},g={baseUrl:""};function I(e){return g={timeoutMs:3e4,...e},g}function j(){return g}function et(e,t){return/^https?:\/\//.test(t)?t:e.replace(/\/$/,"")+(t.startsWith("/")?t:`/${t}`)}async function F(e,t={}){if(!g.baseUrl)throw new Error("@cueframe/sdk: createClient({baseUrl}) must be called before issuing requests.");let r=g.fetch??fetch,s=et(g.baseUrl,e),o=new Headers({accept:"application/json"});for(let[c,m]of Object.entries(g.headers??{}))o.set(c,m);t.headers&&new Headers(t.headers).forEach((c,m)=>o.set(m,c)),g.apiKey&&!o.has("authorization")&&o.set("authorization",`Bearer ${g.apiKey}`);let n=g.timeoutMs??3e4,i=new AbortController,p=setTimeout(()=>i.abort(),n),d=t.signal?AbortSignal.any([t.signal,i.signal]):i.signal;try{let c=await r(s,{...t,headers:o,signal:d}),m={};c.headers.forEach((b,x)=>{m[x.toLowerCase()]=b});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:m,body:l,requestId:m["x-request-id"]}}catch(c){throw t.signal?.aborted?c:i.signal.aborted?new v(s,n):new q(s,c)}finally{clearTimeout(p)}}async function a(e,t={}){let r=await F(e,t);if(r.status>=400){let s;throw r.body&&typeof r.body=="object"&&"error"in r.body?s=r.body.error??{code:"unknown",message:r.statusText}:s={code:"unknown",message:r.statusText},new u(r.status,s,r.requestId)}if(r.status!==204)return r.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 O(e){return e instanceof u?tt[e.code]??null:null}async function*rt(e,t,r){let s=j();if(!s?.baseUrl)throw new Error(`@cueframe/sdk: createClient({baseUrl}) must be called before ${t}.`);let o=s.baseUrl.replace(/\/$/,"")+e,n=new Headers({accept:"text/event-stream",...s.headers??{}});s.apiKey&&!n.has("authorization")&&n.set("authorization",`Bearer ${s.apiKey}`);let p=await(s.fetch??fetch)(o,{method:"GET",headers:n,signal:r.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"),m="";try{for(;;){let{value:l,done:b}=await d.read();if(b)break;m+=c.decode(l,{stream:!0});let x=m.indexOf(`
3
-
4
- `);for(;x!==-1;){let Qe=m.slice(0,x);m=m.slice(x+2);let B=ot(Qe);B&&(yield B),x=m.indexOf(`
5
-
6
- `)}}}finally{try{d.releaseLock()}catch{}}}function ot(e){if(!e.trim())return null;let t=null,r=[];for(let s of e.split(`
7
- `)){if(!s||s.startsWith(":"))continue;let o=s.indexOf(":");if(o===-1)continue;let n=s.slice(0,o),i=s.slice(o+1).replace(/^ /,"");n==="event"?t=i:n==="data"&&r.push(i)}return!t||r.length===0?null:{event:t,data:r.join(`
8
- `)}}async function*st(e,t,r={}){let s=`/v1/projects/${encodeURIComponent(e)}/renders/${encodeURIComponent(t)}/stream`;for await(let o of rt(s,"iterRenderEvents",r)){let n=nt(o);n&&(yield n)}}function nt(e){let{event:t,data:r}=e;if(r===null||t!=="progress"&&t!=="complete"&&t!=="error")return null;let s;try{s=JSON.parse(r)}catch{return null}return{event:t,data:s}}var P={};y(P,{composeFromSuggestion:()=>dt,getComposeFromSuggestionUrl:()=>L,getListMediaSuggestionsUrl:()=>G,getResetMediaSuggestionsUrl:()=>$,getSuggestBriefsUrl:()=>N,listMediaSuggestions:()=>ut,resetMediaSuggestions:()=>gt,suggestBriefs:()=>lt});var L=e=>`/v1/projects/${e}/composition/from-suggestion`,dt=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(L(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},G=(e,t)=>{let r=new URLSearchParams;Object.entries(t||{}).forEach(([o,n])=>{n!==void 0&&r.append(o,n===null?"null":String(n))});let s=r.toString();return s.length>0?`/v1/media/${e}/suggestions?${s}`:`/v1/media/${e}/suggestions`},ut=async(e,t,r)=>a(G(e,t),{...r,method:"GET"}),N=e=>`/v1/media/${e}/suggestions`,lt=async(e,t)=>a(N(e),{...t,method:"POST"}),$=e=>`/v1/media/${e}/suggestions`,gt=async(e,t)=>a($(e),{...t,method:"DELETE"});var X={};y(X,{createComponent:()=>Rt,getComponent:()=>Pt,getComponentSource:()=>St,getCreateComponentUrl:()=>H,getGetComponentSourceUrl:()=>z,getGetComponentUrl:()=>V,getListCatalogUrl:()=>_,getPatchComponentUrl:()=>W,getResolveBuiltinComponentUrl:()=>J,getUpdateComponentUrl:()=>D,listCatalog:()=>xt,patchComponent:()=>bt,resolveBuiltinComponent:()=>yt,updateComponent:()=>Ct});var J=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([s,o])=>{o!==void 0&&t.append(s,o===null?"null":String(o))});let r=t.toString();return r.length>0?`/v1/components/builtin/resolve?${r}`:"/v1/components/builtin/resolve"},yt=async(e,t)=>a(J(e),{...t,method:"GET"}),_=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([s,o])=>{o!==void 0&&t.append(s,o===null?"null":String(o))});let r=t.toString();return r.length>0?`/v1/components?${r}`:"/v1/components"},xt=async(e,t)=>a(_(e),{...t,method:"GET"}),H=()=>"/v1/components",Rt=async(e,t)=>{let r=s=>{if(!s)return{};if(s instanceof Headers)return Object.fromEntries(s.entries());if(Symbol.iterator in s)return Object.fromEntries(Array.from(s,n=>Array.from(n)));let o={};for(let[n,i]of Object.entries(s))i!==void 0&&(o[n]=i);return o};return a(H(),{...t,method:"POST",headers:{"Content-Type":"application/json",...r(t?.headers)},body:JSON.stringify(e)})},D=e=>`/v1/components/${e}`,Ct=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(D(e),{...r,method:"PUT",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},W=e=>`/v1/components/${e}`,bt=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(W(e),{...r,method:"PATCH",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},V=e=>`/v1/components/${e}`,Pt=async(e,t)=>a(V(e),{...t,method:"GET"}),z=(e,t)=>{let r=new URLSearchParams;Object.entries(t||{}).forEach(([o,n])=>{n!==void 0&&r.append(o,n===null?"null":String(n))});let s=r.toString();return s.length>0?`/v1/components/${e}/source?${s}`:`/v1/components/${e}/source`},St=async(e,t,r)=>a(z(e,t),{...r,method:"GET"});var te={};y(te,{cancelExport:()=>wt,createFcpxmlExport:()=>It,createPremiereExport:()=>jt,getCancelExportUrl:()=>ee,getCreateFcpxmlExportUrl:()=>Z,getCreatePremiereExportUrl:()=>Y,getExport:()=>Ot,getGetExportUrl:()=>Q});var Z=e=>`/v1/projects/${e}/exports/fcpxml`,It=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(Z(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},Y=e=>`/v1/projects/${e}/exports/premiere`,jt=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(Y(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},Q=(e,t)=>`/v1/projects/${e}/exports/${t}`,Ot=async(e,t,r)=>a(Q(e,t),{...r,method:"GET"}),ee=(e,t)=>`/v1/projects/${e}/exports/${t}/cancel`,wt=async(e,t,r)=>a(ee(e,t),{...r,method:"POST"});var Ce={};y(Ce,{analyzeTimeline:()=>Yt,analyzeTranscript:()=>Zt,createMediaUpload:()=>Ft,createUploadToken:()=>Bt,deleteMedia:()=>Jt,finalizeMedia:()=>Gt,generateMedia:()=>Nt,getAnalyzeTimelineUrl:()=>Re,getAnalyzeTranscriptUrl:()=>xe,getCreateMediaUploadUrl:()=>se,getCreateUploadTokenUrl:()=>oe,getDeleteMediaUrl:()=>ce,getFinalizeMediaUrl:()=>ie,getGenerateMediaUrl:()=>ae,getGetMediaContextUrl:()=>de,getGetMediaTranscriptUrl:()=>fe,getGetMediaUrl:()=>me,getImportMediaUrl:()=>pe,getListMediaFactsUrl:()=>ge,getListMediaUrl:()=>ne,getMedia:()=>_t,getMediaContext:()=>Dt,getMediaTranscript:()=>Ht,getPurchaseMediaFactUrl:()=>ye,getSearchLibraryUrl:()=>re,getTriggerMatteBakeUrl:()=>le,getTriggerSubjectDetectionUrl:()=>ue,importMedia:()=>$t,listMedia:()=>Lt,listMediaFacts:()=>zt,purchaseMediaFact:()=>Xt,searchLibrary:()=>Kt,triggerMatteBake:()=>Vt,triggerSubjectDetection:()=>Wt});var re=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([s,o])=>{o!==void 0&&t.append(s,o===null?"null":String(o))});let r=t.toString();return r.length>0?`/v1/media/search?${r}`:"/v1/media/search"},Kt=async(e,t)=>a(re(e),{...t,method:"GET"}),oe=()=>"/v1/uploads/token",Bt=async(e,t)=>{let r=s=>{if(!s)return{};if(s instanceof Headers)return Object.fromEntries(s.entries());if(Symbol.iterator in s)return Object.fromEntries(Array.from(s,n=>Array.from(n)));let o={};for(let[n,i]of Object.entries(s))i!==void 0&&(o[n]=i);return o};return a(oe(),{...t,method:"POST",headers:{"Content-Type":"application/json",...r(t?.headers)},body:JSON.stringify(e)})},se=()=>"/v1/media",Ft=async(e,t)=>{let r=s=>{if(!s)return{};if(s instanceof Headers)return Object.fromEntries(s.entries());if(Symbol.iterator in s)return Object.fromEntries(Array.from(s,n=>Array.from(n)));let o={};for(let[n,i]of Object.entries(s))i!==void 0&&(o[n]=i);return o};return a(se(),{...t,method:"POST",headers:{"Content-Type":"application/json",...r(t?.headers)},body:JSON.stringify(e)})},ne=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([s,o])=>{o!==void 0&&t.append(s,o===null?"null":String(o))});let r=t.toString();return r.length>0?`/v1/media?${r}`:"/v1/media"},Lt=async(e,t)=>a(ne(e),{...t,method:"GET"}),ie=e=>`/v1/media/${e}/finalize`,Gt=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(ie(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},ae=()=>"/v1/media/generate",Nt=async(e,t)=>{let r=s=>{if(!s)return{};if(s instanceof Headers)return Object.fromEntries(s.entries());if(Symbol.iterator in s)return Object.fromEntries(Array.from(s,n=>Array.from(n)));let o={};for(let[n,i]of Object.entries(s))i!==void 0&&(o[n]=i);return o};return a(ae(),{...t,method:"POST",headers:{"Content-Type":"application/json",...r(t?.headers)},body:JSON.stringify(e)})},pe=()=>"/v1/media/import",$t=async(e,t)=>{let r=s=>{if(!s)return{};if(s instanceof Headers)return Object.fromEntries(s.entries());if(Symbol.iterator in s)return Object.fromEntries(Array.from(s,n=>Array.from(n)));let o={};for(let[n,i]of Object.entries(s))i!==void 0&&(o[n]=i);return o};return a(pe(),{...t,method:"POST",headers:{"Content-Type":"application/json",...r(t?.headers)},body:JSON.stringify(e)})},ce=e=>`/v1/media/${e}`,Jt=async(e,t)=>a(ce(e),{...t,method:"DELETE"}),me=e=>`/v1/media/${e}`,_t=async(e,t)=>a(me(e),{...t,method:"GET"}),fe=(e,t)=>{let r=new URLSearchParams;Object.entries(t||{}).forEach(([o,n])=>{n!==void 0&&r.append(o,n===null?"null":String(n))});let s=r.toString();return s.length>0?`/v1/media/${e}/transcript?${s}`:`/v1/media/${e}/transcript`},Ht=async(e,t,r)=>a(fe(e,t),{...r,method:"GET"}),de=e=>`/v1/media/${e}/context`,Dt=async(e,t)=>a(de(e),{...t,method:"GET"}),ue=(e,t)=>{let r=new URLSearchParams;Object.entries(t||{}).forEach(([o,n])=>{n!==void 0&&r.append(o,n===null?"null":String(n))});let s=r.toString();return s.length>0?`/v1/media/${e}/detect-subjects?${s}`:`/v1/media/${e}/detect-subjects`},Wt=async(e,t,r)=>a(ue(e,t),{...r,method:"POST"}),le=e=>`/v1/media/${e}/matte`,Vt=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(le(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},ge=(e,t)=>{let r=new URLSearchParams;Object.entries(t||{}).forEach(([o,n])=>{n!==void 0&&r.append(o,n===null?"null":String(n))});let s=r.toString();return s.length>0?`/v1/media/${e}/facts?${s}`:`/v1/media/${e}/facts`},zt=async(e,t,r)=>a(ge(e,t),{...r,method:"GET"}),ye=e=>`/v1/media/${e}/facts`,Xt=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(ye(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},xe=e=>`/v1/media/${e}/transcript/analyze`,Zt=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(xe(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},Re=()=>"/v1/timeline-analysis",Yt=async(e,t)=>{let r=s=>{if(!s)return{};if(s instanceof Headers)return Object.fromEntries(s.entries());if(Symbol.iterator in s)return Object.fromEntries(Array.from(s,n=>Array.from(n)));let o={};for(let[n,i]of Object.entries(s))i!==void 0&&(o[n]=i);return o};return a(Re(),{...t,method:"POST",headers:{"Content-Type":"application/json",...r(t?.headers)},body:JSON.stringify(e)})};var ke={};y(ke,{createProject:()=>er,deleteProject:()=>or,getCreateProjectUrl:()=>Pe,getDeleteProjectUrl:()=>ve,getGetProjectContextUrl:()=>qe,getGetProjectUrl:()=>Se,getListProjectsUrl:()=>be,getPatchProjectUrl:()=>he,getProject:()=>tr,getProjectContext:()=>sr,listProjects:()=>Qt,patchProject:()=>rr});var be=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([s,o])=>{o!==void 0&&t.append(s,o===null?"null":String(o))});let r=t.toString();return r.length>0?`/v1/projects?${r}`:"/v1/projects"},Qt=async(e,t)=>a(be(e),{...t,method:"GET"}),Pe=()=>"/v1/projects",er=async(e,t)=>{let r=s=>{if(!s)return{};if(s instanceof Headers)return Object.fromEntries(s.entries());if(Symbol.iterator in s)return Object.fromEntries(Array.from(s,n=>Array.from(n)));let o={};for(let[n,i]of Object.entries(s))i!==void 0&&(o[n]=i);return o};return a(Pe(),{...t,method:"POST",headers:{"Content-Type":"application/json",...r(t?.headers)},body:JSON.stringify(e)})},Se=e=>`/v1/projects/${e}`,tr=async(e,t)=>a(Se(e),{...t,method:"GET"}),he=e=>`/v1/projects/${e}`,rr=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(he(e),{...r,method:"PATCH",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},ve=e=>`/v1/projects/${e}`,or=async(e,t)=>a(ve(e),{...t,method:"DELETE"}),qe=e=>`/v1/projects/${e}/context`,sr=async(e,t)=>a(qe(e),{...t,method:"GET"});var Ue={};y(Ue,{cancelRender:()=>cr,createRender:()=>nr,getCancelRenderUrl:()=>Oe,getCreateRenderUrl:()=>Ee,getGetRenderUrl:()=>je,getListProjectRendersUrl:()=>Te,getPreviewClipUrl:()=>Ie,getRefreshRenderUrlUrl:()=>Ae,getRender:()=>pr,getRetryRenderUrl:()=>we,getStreamRenderUrl:()=>Me,listProjectRenders:()=>ir,previewClip:()=>ar,refreshRenderUrl:()=>fr,retryRender:()=>mr,streamRender:()=>dr});var Ee=e=>`/v1/projects/${e}/renders`,nr=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(Ee(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},Te=(e,t)=>{let r=new URLSearchParams;Object.entries(t||{}).forEach(([o,n])=>{n!==void 0&&r.append(o,n===null?"null":String(n))});let s=r.toString();return s.length>0?`/v1/projects/${e}/renders?${s}`:`/v1/projects/${e}/renders`},ir=async(e,t,r)=>a(Te(e,t),{...r,method:"GET"}),Ie=e=>`/v1/projects/${e}/preview-clip`,ar=async(e,t,r)=>{let s=o=>{if(!o)return{};if(o instanceof Headers)return Object.fromEntries(o.entries());if(Symbol.iterator in o)return Object.fromEntries(Array.from(o,i=>Array.from(i)));let n={};for(let[i,p]of Object.entries(o))p!==void 0&&(n[i]=p);return n};return a(Ie(e),{...r,method:"POST",headers:{"Content-Type":"application/json",...s(r?.headers)},body:JSON.stringify(t)})},je=(e,t)=>`/v1/projects/${e}/renders/${t}`,pr=async(e,t,r)=>a(je(e,t),{...r,method:"GET"}),Oe=(e,t)=>`/v1/projects/${e}/renders/${t}/cancel`,cr=async(e,t,r)=>a(Oe(e,t),{...r,method:"POST"}),we=(e,t)=>`/v1/projects/${e}/renders/${t}/retry`,mr=async(e,t,r)=>a(we(e,t),{...r,method:"POST"}),Ae=(e,t)=>`/v1/projects/${e}/renders/${t}/refresh-url`,fr=async(e,t,r)=>a(Ae(e,t),{...r,method:"POST"}),Me=(e,t)=>`/v1/projects/${e}/renders/${t}/stream`,dr=async(e,t,r)=>a(Me(e,t),{...r,method:"GET"});var $e={};y($e,{createSession:()=>yr,deleteSession:()=>Rr,getCreateSessionUrl:()=>Be,getDeleteSessionUrl:()=>Le,getGetMuxJobUrl:()=>Ne,getGetSessionUrl:()=>Fe,getListSessionsUrl:()=>Ke,getMuxJob:()=>br,getSession:()=>xr,getTriggerMuxJobUrl:()=>Ge,listSessions:()=>gr,triggerMuxJob:()=>Cr});var Ke=e=>{let t=new URLSearchParams;Object.entries(e||{}).forEach(([s,o])=>{o!==void 0&&t.append(s,o===null?"null":String(o))});let r=t.toString();return r.length>0?`/v1/sessions?${r}`:"/v1/sessions"},gr=async(e,t)=>a(Ke(e),{...t,method:"GET"}),Be=()=>"/v1/sessions",yr=async(e,t)=>{let r=s=>{if(!s)return{};if(s instanceof Headers)return Object.fromEntries(s.entries());if(Symbol.iterator in s)return Object.fromEntries(Array.from(s,n=>Array.from(n)));let o={};for(let[n,i]of Object.entries(s))i!==void 0&&(o[n]=i);return o};return a(Be(),{...t,method:"POST",headers:{"Content-Type":"application/json",...r(t?.headers)},body:JSON.stringify(e)})},Fe=e=>`/v1/sessions/${e}`,xr=async(e,t)=>a(Fe(e),{...t,method:"GET"}),Le=e=>`/v1/sessions/${e}`,Rr=async(e,t)=>a(Le(e),{...t,method:"DELETE"}),Ge=e=>`/v1/sessions/${e}/mux-job`,Cr=async(e,t)=>a(Ge(e),{...t,method:"POST"}),Ne=e=>`/v1/sessions/${e}/mux-job`,br=async(e,t)=>a(Ne(e),{...t,method:"GET"});var k="X-CueFrame-Client";var kr=/^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$/,Er=["web","console","clips"];function Tr(e,t){return Er.includes(e)===(t==="browser")}function Je(e){if(!kr.test(e.version))throw new Error(`${k} cannot carry the version ${JSON.stringify(e.version)}.`);if(!Tr(e.surface,e.os))throw new Error(`${k} cannot carry ${e.surface} on ${e.os}.`);return`${e.surface}/${e.version}/${e.os}`}function _e(e){return e==="darwin"?"macos":e==="win32"?"windows":e==="linux"?"linux":"unknown"}import{readFileSync as Ir,writeFileSync as jr,mkdirSync as Or,existsSync as wr}from"fs";import{join as He}from"path";import{homedir as Ar}from"os";var f={Success:0,UserError:2,ApiClientError:3,ApiServerError:4,NetworkError:5,PaymentRequired:6};function w(e){if(e instanceof u){if(e.status===402)return f.PaymentRequired;if(e.status>=500)return f.ApiServerError;if(e.status>=400)return f.ApiClientError}let t=e?.exitCode;return typeof t=="number"?t:f.NetworkError}function Qb(e){return e===402?f.PaymentRequired:e>=500?f.ApiServerError:e>=400?f.ApiClientError:f.Success}function S(e){process.exit(e)}var De=He(Ar(),".cueframe"),E=He(De,"auth.json"),R={local:"http://127.0.0.1:3211",prod:"https://api.cueframe.ai"},Mr="prod";function nP(e=Mr){return process.env.CUEFRAME_API_URL??R[e]??R.prod}var h=class extends Error{constructor(r,s){super(s);this.code=r;this.name="AuthStoreError"}code;exitCode=f.UserError};function iP(e){if(!/^cf_(?:live|test)_[A-Za-z0-9_-]{8,}$/.test(e))throw new h("auth_invalid","Invalid API key. Expected a key beginning with cf_live_ or cf_test_.")}function We(){if(!wr(E))return{targets:{}};let e=Ir(E,"utf-8");try{return JSON.parse(e)}catch{throw new h("auth_config_corrupt",`${E} is corrupt and cannot be parsed. Delete it and re-run \`cueframe login\`.`)}}function Ur(e){Or(De,{recursive:!0,mode:448}),jr(E,JSON.stringify(e,null,2),{mode:384})}function aP(e,t="prod",r){let s=We(),o=R[t]??R.prod;s.targets[t]={apiKey:e,apiUrl:r??o},Ur(s)}function Ve(e="prod"){let t=process.env.CUEFRAME_API_KEY,r=process.env.CUEFRAME_API_URL;if(t)return{apiKey:t,apiUrl:r??R[e]??R.prod};let o=We().targets[e];if(!o)throw new h("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 o}function Kr(){return{[k]:Je({surface:"cli",version:"0.6.0",os:_e(process.platform)})}}function ze(e){let{apiKey:t,apiUrl:r}=Ve(e);I({baseUrl:r,apiKey:t,headers:Kr()})}function T(e){let t=e instanceof u?`${e.message} (${e.code}${e.status?`, ${e.status}`:""})`:e instanceof Error?e.message:String(e),r=O(e);return r?`${t}
9
- \u2192 ${r}`:t}import Br from"ora";import Fr from"pino";var Lr="v1",Xe=Fr({level:"trace",base:{schemaVersion:Lr},messageKey:"event",timestamp:()=>`,"ts":${Date.now()}`,formatters:{level:()=>({_:0})}});function A(e,t){return Br({text:e,isSilent:!!t}).start()}function C(e,t,r={}){e.json&&Xe.info(r,t)}function yP(e,t,r){if(e.json){console.log(JSON.stringify(t,null,2));return}r()}function M(e,t,r,s){let o=r instanceof Error?r.message:String(r),n=typeof r?.code=="string"?r.code:void 0,i=r?.status;e.json?Xe.info({phase:t,message:o,...n?{code:n}:{},...typeof i=="number"?{status:i}:{},exitCode:s},"error"):process.stderr.write(`\u2716 ${t}: ${T(r)}
10
- `)}function U(e,t,r,s){e.json&&M(e,t,r,s)}function xP(e,t){t.json||console.log(e)}function Gr(e){for(let t=0;t<e.length;t++){let r=e.charCodeAt(t);if(r<=31||r===127)return!0}return!1}function Ye(e,t,r){e.length===0&&Ze(`${t} is empty.`,r),Gr(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.`,r)}function Ze(e,t){M(t,"preflight",new Error(e),f.UserError),S(f.UserError)}var Nr=5e3,K=300*1e3;async function EP(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 r=A("Triggering clip analysis\u2026",t.json),s;try{s=await P.suggestBriefs(e),r.succeed(`Analysis ${s.alreadyRunning?"already running":"started"}`)}catch(i){r.fail(`Failed to start analysis: ${T(i)}`),U(t,"analyze_prepare",i,w(i)),S(w(i))}if(C(t,"analyze_ready",{mediaItemId:e,status:s.status,alreadyRunning:!!s.alreadyRunning}),!t.wait){t.json||console.log(`
11
- Run \`cueframe clips ${e}\` to check results.`);return}let o=A("Waiting for analysis to complete\u2026",t.json),n=Date.now();for(;;){await new Promise(i=>setTimeout(i,Nr)),Date.now()-n>K&&(o.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`),f.UserError),S(f.UserError));try{let i=await P.listMediaSuggestions(e);if(i.suggestions.length>0){o.succeed(`Analysis complete \u2014 ${i.suggestions.length} clips found`),C(t,"analyze_complete",{mediaItemId:e,durationMs:Date.now()-n,suggestionCount:i.suggestions.length,suggestions:i.suggestions}),t.json||i.suggestions.forEach((d,c)=>{let m=(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 (${m}s) score: ${d.score}`),console.log(` ${d.reason}`)});return}let p=Math.round((Date.now()-n)/1e3);o.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,O as e,st as f,P as g,X as h,te as i,Ce as j,ke as k,Ue as l,$e as m,f as n,w as o,Qb as p,S as q,nP as r,iP as s,aP as t,Kr as u,ze as v,T as w,A as x,C as y,yP as z,M as A,U as B,xP as C,Ye as D,EP as E};