cueframe 0.3.1 → 0.4.2
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 +56 -30
- package/dist/analyze-6QSD7KHH.js +2 -0
- package/dist/chunk-7TCLKYUZ.js +12 -0
- package/dist/cueframe.js +35 -58
- package/package.json +14 -7
- package/skills/_connect.md +29 -0
- package/skills/add-music-bed/SKILL.md +7 -2
- package/skills/brand-reel/SKILL.md +8 -3
- package/skills/clip-a-talking-head/SKILL.md +7 -2
- package/skills/composing-video/SKILL.md +59 -35
- package/skills/cueframe-brand-demo/SKILL.md +14 -13
- package/skills/cueframe-cli/SKILL.md +54 -44
- package/skills/cueframe-component-authoring/SKILL.md +10 -9
- package/skills/cueframe-product-video/SKILL.md +8 -7
- package/skills/every-format-from-one-edit/SKILL.md +7 -2
- package/skills/extracting-brand-kits/SKILL.md +1 -1
- package/skills/launch-video/SKILL.md +7 -2
- package/skills/make-a-social-reel/SKILL.md +7 -2
- package/skills/rebrand-a-video/SKILL.md +7 -2
- package/dist/analyze-4EZTZEAG.js +0 -2
- package/dist/chunk-4FMFWW74.js +0 -12
package/README.md
CHANGED
|
@@ -2,26 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
Agent-native CLI for **CueFrame** — turn media into rendered video from the
|
|
4
4
|
terminal. Every command speaks `--json` (an NDJSON event stream) and the binary
|
|
5
|
-
self-describes via `cueframe describe --json`, so agents can drive it without
|
|
5
|
+
self-describes via `npx -y cueframe describe --json`, so agents can drive it without
|
|
6
6
|
guesswork.
|
|
7
7
|
|
|
8
|
-
>
|
|
9
|
-
>
|
|
8
|
+
> Connects to the hosted CueFrame service — create an API key at
|
|
9
|
+
> [app.cueframe.ai](https://app.cueframe.ai), or use `npx -y cueframe login`.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Run it
|
|
12
|
+
|
|
13
|
+
There is nothing to install. Every command in this README runs as
|
|
14
|
+
`npx -y cueframe …` and is executable verbatim:
|
|
12
15
|
|
|
13
16
|
```bash
|
|
14
|
-
npx cueframe
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
npx -y cueframe --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`npx -y cueframe install` wires the CueFrame MCP server into every agent on your
|
|
21
|
+
machine — but note it installs **no binary**: npx unpacks to an ephemeral cache and
|
|
22
|
+
leaves nothing on your `PATH`. That is deliberate. `npx` re-resolves the current
|
|
23
|
+
version on every call (~0.5s warm), so these commands can never go stale the way a
|
|
24
|
+
pinned global install does.
|
|
25
|
+
|
|
26
|
+
Running many commands in a row and want to skip that overhead? Install it once and
|
|
27
|
+
drop the `npx -y ` prefix:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm i -g cueframe # optional — needs a user-writable npm prefix
|
|
17
31
|
```
|
|
18
32
|
|
|
33
|
+
This is an optimization, not the supported path. On a default Linux install it fails
|
|
34
|
+
with `EACCES: permission denied, mkdir '/usr/local/lib/node_modules/cueframe'` unless
|
|
35
|
+
you have configured an npm prefix in your home directory.
|
|
36
|
+
|
|
19
37
|
## Authenticate
|
|
20
38
|
|
|
21
39
|
```bash
|
|
22
|
-
cueframe auth <cf_live_… key> # static API key (CI / headless)
|
|
40
|
+
npx -y cueframe auth <cf_live_… key> # static API key (CI / headless)
|
|
23
41
|
# or
|
|
24
|
-
cueframe login # OAuth device flow (browser)
|
|
42
|
+
npx -y cueframe login # OAuth device flow (browser)
|
|
25
43
|
```
|
|
26
44
|
|
|
27
45
|
The key can also be supplied via the `CUEFRAME_API_KEY` environment variable.
|
|
@@ -30,60 +48,68 @@ The key can also be supplied via the `CUEFRAME_API_KEY` environment variable.
|
|
|
30
48
|
|
|
31
49
|
```bash
|
|
32
50
|
# 1. Register media (video / image / audio)
|
|
33
|
-
cueframe upload ./source.mp4 --json # → mediaItemId
|
|
51
|
+
npx -y cueframe upload ./source.mp4 --json # → mediaItemId
|
|
34
52
|
|
|
35
53
|
# 2. Create a project (sets aspect / format)
|
|
36
|
-
cueframe project create -n promo -a 9:16 --json # → projectId
|
|
54
|
+
npx -y cueframe project create -n promo -a 9:16 --json # → projectId
|
|
37
55
|
|
|
38
56
|
# 3. Author the composition — tracks of clips, per-clip reframe/trim,
|
|
39
57
|
# optional captions. The composition is the source of truth.
|
|
40
|
-
cueframe composition put <projectId> -b @composition.json
|
|
58
|
+
npx -y cueframe composition put <projectId> -b @composition.json
|
|
41
59
|
|
|
42
60
|
# 4. Render the saved composition to MP4 (queues, SSE-watches, downloads)
|
|
43
|
-
cueframe render <projectId> -o out.mp4 --json
|
|
61
|
+
npx -y cueframe render <projectId> -o out.mp4 --json
|
|
44
62
|
```
|
|
45
63
|
|
|
46
64
|
`render` takes only a project id and renders whatever composition is saved on
|
|
47
|
-
it — no clip suggestion required. Run `cueframe describe --json` for the full
|
|
65
|
+
it — no clip suggestion required. Run `npx -y cueframe describe --json` for the full
|
|
48
66
|
composition schema and every command + flag.
|
|
49
67
|
|
|
50
68
|
## Let CueFrame pick the clip (podcast → short)
|
|
51
69
|
|
|
52
70
|
```bash
|
|
53
|
-
cueframe upload ./long.mp4 --analyze --json # media + AI clip suggestions
|
|
54
|
-
cueframe clips <mediaItemId> --json # list suggestions (sug_…)
|
|
55
|
-
cueframe project create -n short -a 9:16 --from-suggestion <sug_…> --json
|
|
56
|
-
cueframe render <projectId> -o short.mp4 --json
|
|
71
|
+
npx -y cueframe upload ./long.mp4 --analyze --json # media + AI clip suggestions
|
|
72
|
+
npx -y cueframe clips <mediaItemId> --json # list suggestions (sug_…)
|
|
73
|
+
npx -y cueframe project create -n short -a 9:16 --from-suggestion <sug_…> --json
|
|
74
|
+
npx -y cueframe render <projectId> -o short.mp4 --json
|
|
57
75
|
```
|
|
58
76
|
|
|
59
77
|
Both paths converge on `render`; the suggestion on-ramp just authors the
|
|
60
78
|
composition for you instead of you hand-writing it.
|
|
61
79
|
|
|
62
|
-
##
|
|
63
|
-
|
|
64
|
-
Drop the usage skill into your agent so Claude Code / Cursor / Codex know how to
|
|
65
|
-
drive the CLI:
|
|
80
|
+
## Wire it into your agents
|
|
66
81
|
|
|
67
82
|
```bash
|
|
68
|
-
cueframe install
|
|
83
|
+
npx -y cueframe install
|
|
69
84
|
```
|
|
70
85
|
|
|
86
|
+
Detects every agent on the machine — Claude Code, Cursor, Codex, VS Code, Windsurf,
|
|
87
|
+
Zed, Gemini CLI, Copilot CLI and more — and wires the CueFrame MCP server into each
|
|
88
|
+
one's config. Auth happens on the agent's first call via a browser "Allow"; no API key.
|
|
89
|
+
|
|
90
|
+
It also copies the usage skills to `~/.claude/skills/`. **Only Claude Code reads that
|
|
91
|
+
directory** — every other agent gets the same verb surface as MCP tools from the server,
|
|
92
|
+
so there is nothing further to install for them.
|
|
93
|
+
|
|
94
|
+
Claude Desktop and ChatGPT can't be wired from the command line (neither reads a local
|
|
95
|
+
MCP config) — add `https://api.cueframe.ai/v1/mcp` as a custom connector instead.
|
|
96
|
+
|
|
71
97
|
## For agents
|
|
72
98
|
|
|
73
99
|
- **`--json` everywhere** — NDJSON lifecycle: `<verb>_prepare → <verb>_progress* → <verb>_complete | error`, plus a non-zero exit on failure.
|
|
74
|
-
- **`cueframe describe --json`** — the full command tree as structured JSON.
|
|
75
|
-
- **`cueframe api <METHOD> <path>`** — raw escape hatch over the v1 API.
|
|
100
|
+
- **`npx -y cueframe describe --json`** — the full command tree as structured JSON.
|
|
101
|
+
- **`npx -y cueframe api <METHOD> <path>`** — raw escape hatch over the v1 API.
|
|
76
102
|
|
|
77
103
|
## Common commands
|
|
78
104
|
|
|
79
105
|
| Need to… | Command |
|
|
80
106
|
|---|---|
|
|
81
|
-
| List projects | `cueframe list` |
|
|
82
|
-
| Read a composition + ETag | `cueframe composition get <projectId> --json` |
|
|
83
|
-
| Write a composition | `cueframe composition put <projectId> -b @comp.json` |
|
|
84
|
-
| Export to Final Cut / Premiere | `cueframe export fcpxml \| premiere <projectId> -s <sug_…> --wait -o cut.zip` |
|
|
107
|
+
| List projects | `npx -y cueframe list` |
|
|
108
|
+
| Read a composition + ETag | `npx -y cueframe composition get <projectId> --json` |
|
|
109
|
+
| Write a composition | `npx -y cueframe composition put <projectId> -b @comp.json` |
|
|
110
|
+
| Export to Final Cut / Premiere | `npx -y cueframe export fcpxml \| premiere <projectId> -s <sug_…> --wait -o cut.zip` |
|
|
85
111
|
| Validate without mutating | append `--dry-run` |
|
|
86
112
|
|
|
87
113
|
---
|
|
88
114
|
|
|
89
|
-
© CueFrame —
|
|
115
|
+
© CueFrame — MIT licensed.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as y}from"./chunk-3QSA3EBY.js";var u=class extends Error{status;code;details;requestId;constructor(o,t,s){super(t.message),this.name="ApiError",this.status=o,this.code=t.code,this.details=t.details,this.requestId=s}},v=class extends Error{url;timeoutMs;constructor(o,t){super(`Request timed out after ${t}ms: ${o}`),this.name="TimeoutError",this.url=o,this.timeoutMs=t}},P=class extends Error{url;constructor(o,t){super(`Request failed before a response: ${o}`,{cause:t}),this.name="NetworkError",this.url=o}},l={baseUrl:""};function b(e){return l={timeoutMs:3e4,...e},l}function T(){return l}function Ne(e,o){return/^https?:\/\//.test(o)?o:e.replace(/\/$/,"")+(o.startsWith("/")?o:`/${o}`)}async function j(e,o={}){if(!l.baseUrl)throw new Error("@cueframe/sdk: createClient({baseUrl}) must be called before issuing requests.");let t=l.fetch??fetch,s=Ne(l.baseUrl,e),n=new Headers({accept:"application/json"});for(let[a,f]of Object.entries(l.headers??{}))n.set(a,f);o.headers&&new Headers(o.headers).forEach((a,f)=>n.set(f,a)),l.apiKey&&!n.has("authorization")&&n.set("authorization",`Bearer ${l.apiKey}`);let i=l.timeoutMs??3e4,p=new AbortController,c=setTimeout(()=>p.abort(),i),d=o.signal?AbortSignal.any([o.signal,p.signal]):p.signal;try{let a=await t(s,{...o,headers:n,signal:d}),f={};a.headers.forEach((O,R)=>{f[R.toLowerCase()]=O});let x=null;if(a.status!==204)if((a.headers.get("content-type")??"").includes("application/json"))try{x=await a.json()}catch{x=null}else x=await a.text();return{status:a.status,statusText:a.statusText,headers:f,body:x,requestId:f["x-request-id"]}}catch(a){throw o.signal?.aborted?a:p.signal.aborted?new v(s,i):new P(s,a)}finally{clearTimeout(c)}}async function r(e,o={}){let t=await j(e,o);if(t.status>=400){let s;throw t.body&&typeof t.body=="object"&&"error"in t.body?s=t.body.error??{code:"unknown",message:t.statusText}:s={code:"unknown",message:t.statusText},new u(t.status,s,t.requestId)}if(t.status!==204)return t.body}var ze={unauthorized:"The API key is invalid or expired for this target. Run `cueframe login` (or `cueframe auth <api-key>`), and check the key matches the target environment.",forbidden:"The key's scopes don't allow this operation. Create a key with the needed preset (`cueframe keys create --preset ...`) or use a key with wider scopes.",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 E(e){return e instanceof u?ze[e.code]??null:null}async function*Ze(e,o,t){let s=T();if(!s?.baseUrl)throw new Error(`@cueframe/sdk: createClient({baseUrl}) must be called before ${o}.`);let n=s.baseUrl.replace(/\/$/,"")+e,i=new Headers({accept:"text/event-stream",...s.headers??{}});s.apiKey&&!i.has("authorization")&&i.set("authorization",`Bearer ${s.apiKey}`);let c=await(s.fetch??fetch)(n,{method:"GET",headers:i,signal:t.signal});if(!c.ok){let x;try{x=(await c.json()).error??{code:"unknown",message:c.statusText}}catch{x={code:"unknown",message:c.statusText}}throw new u(c.status,x,c.headers.get("x-request-id")??void 0)}if(!c.body)throw new Error("SSE response has no body");let d=c.body.getReader(),a=new TextDecoder("utf-8"),f="";try{for(;;){let{value:x,done:O}=await d.read();if(O)break;f+=a.decode(x,{stream:!0});let R=f.indexOf(`
|
|
3
|
+
|
|
4
|
+
`);for(;R!==-1;){let De=f.slice(0,R);f=f.slice(R+2);let G=Ve(De);G&&(yield G),R=f.indexOf(`
|
|
5
|
+
|
|
6
|
+
`)}}}finally{try{d.releaseLock()}catch{}}}function Ve(e){if(!e.trim())return null;let o=null,t=[];for(let s of e.split(`
|
|
7
|
+
`)){if(!s||s.startsWith(":"))continue;let n=s.indexOf(":");if(n===-1)continue;let i=s.slice(0,n),p=s.slice(n+1).replace(/^ /,"");i==="event"?o=p:i==="data"&&t.push(p)}return!o||t.length===0?null:{event:o,data:t.join(`
|
|
8
|
+
`)}}async function*He(e,o,t={}){let s=`/v1/projects/${encodeURIComponent(e)}/renders/${encodeURIComponent(o)}/stream`;for await(let n of Ze(s,"iterRenderEvents",t)){let i=Ye(n);i&&(yield i)}}function Ye(e){let{event:o,data:t}=e;if(t===null||o!=="progress"&&o!=="complete"&&o!=="error")return null;let s;try{s=JSON.parse(t)}catch{return null}return{event:o,data:s}}var Q={};y(Q,{analyzeTranscript:()=>Ro,createMediaUpload:()=>no,createUploadToken:()=>so,deleteMedia:()=>mo,finalizeMedia:()=>io,generateMedia:()=>ao,getAnalyzeTranscriptUrl:()=>Y,getCreateMediaUploadUrl:()=>L,getCreateUploadTokenUrl:()=>U,getDeleteMediaUrl:()=>D,getFinalizeMediaUrl:()=>_,getGenerateMediaUrl:()=>J,getGetMediaContextUrl:()=>Z,getGetMediaPerformanceUrl:()=>H,getGetMediaTranscriptUrl:()=>z,getGetMediaUrl:()=>N,getImportMediaUrl:()=>W,getListMediaUrl:()=>$,getMedia:()=>co,getMediaContext:()=>xo,getMediaPerformance:()=>yo,getMediaTranscript:()=>uo,getSearchLibraryUrl:()=>M,getTriggerSubjectDetectionUrl:()=>V,importMedia:()=>fo,listMedia:()=>po,searchLibrary:()=>ro,triggerSubjectDetection:()=>lo});var M=e=>{let o=new URLSearchParams;Object.entries(e||{}).forEach(([s,n])=>{n!==void 0&&o.append(s,n===null?"null":n.toString())});let t=o.toString();return t.length>0?`/v1/media/search?${t}`:"/v1/media/search"},ro=async(e,o)=>r(M(e),{...o,method:"GET"}),U=()=>"/v1/uploads/token",so=async(e,o)=>r(U(),{...o,method:"POST",headers:{"Content-Type":"application/json",...o?.headers},body:JSON.stringify(e)}),L=()=>"/v1/media",no=async(e,o)=>r(L(),{...o,method:"POST",headers:{"Content-Type":"application/json",...o?.headers},body:JSON.stringify(e)}),$=()=>"/v1/media",po=async e=>r($(),{...e,method:"GET"}),_=e=>`/v1/media/${e}/finalize`,io=async(e,o,t)=>r(_(e),{...t,method:"POST",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)}),J=()=>"/v1/media/generate",ao=async(e,o)=>r(J(),{...o,method:"POST",headers:{"Content-Type":"application/json",...o?.headers},body:JSON.stringify(e)}),W=()=>"/v1/media/import",fo=async(e,o)=>r(W(),{...o,method:"POST",headers:{"Content-Type":"application/json",...o?.headers},body:JSON.stringify(e)}),D=e=>`/v1/media/${e}`,mo=async(e,o)=>r(D(e),{...o,method:"DELETE"}),N=e=>`/v1/media/${e}`,co=async(e,o)=>r(N(e),{...o,method:"GET"}),z=e=>`/v1/media/${e}/transcript`,uo=async(e,o)=>r(z(e),{...o,method:"GET"}),Z=e=>`/v1/media/${e}/context`,xo=async(e,o)=>r(Z(e),{...o,method:"GET"}),V=e=>`/v1/media/${e}/detect-subjects`,lo=async(e,o)=>r(V(e),{...o,method:"POST"}),H=e=>`/v1/media/${e}/performance`,yo=async(e,o)=>r(H(e),{...o,method:"GET"}),Y=e=>`/v1/media/${e}/transcript/analyze`,Ro=async(e,o,t)=>r(Y(e),{...t,method:"POST",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)});var ne={};y(ne,{createProject:()=>Co,deleteProject:()=>So,getCreateProjectUrl:()=>ee,getDeleteProjectUrl:()=>re,getGetProjectContextUrl:()=>se,getGetProjectUrl:()=>oe,getListProjectsUrl:()=>X,getPatchProjectUrl:()=>te,getProject:()=>Oo,getProjectContext:()=>vo,listProjects:()=>go,patchProject:()=>ho});var X=()=>"/v1/projects",go=async e=>r(X(),{...e,method:"GET"}),ee=()=>"/v1/projects",Co=async(e,o)=>r(ee(),{...o,method:"POST",headers:{"Content-Type":"application/json",...o?.headers},body:JSON.stringify(e)}),oe=e=>`/v1/projects/${e}`,Oo=async(e,o)=>r(oe(e),{...o,method:"GET"}),te=e=>`/v1/projects/${e}`,ho=async(e,o,t)=>r(te(e),{...t,method:"PATCH",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)}),re=e=>`/v1/projects/${e}`,So=async(e,o)=>r(re(e),{...o,method:"DELETE"}),se=e=>`/v1/projects/${e}/context`,vo=async(e,o)=>r(se(e),{...o,method:"GET"});var xe={};y(xe,{cancelRender:()=>bo,createRender:()=>Po,getCancelRenderUrl:()=>me,getCreateRenderUrl:()=>pe,getGetRenderUrl:()=>fe,getListProjectRendersUrl:()=>ie,getPreviewClipUrl:()=>ae,getRefreshRenderUrlUrl:()=>de,getRender:()=>Io,getRetryRenderUrl:()=>ce,getStreamRenderUrl:()=>ue,listProjectRenders:()=>ko,previewClip:()=>qo,refreshRenderUrl:()=>Eo,retryRender:()=>To,streamRender:()=>wo});var pe=e=>`/v1/projects/${e}/renders`,Po=async(e,o,t)=>r(pe(e),{...t,method:"POST",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)}),ie=e=>`/v1/projects/${e}/renders`,ko=async(e,o)=>r(ie(e),{...o,method:"GET"}),ae=e=>`/v1/projects/${e}/preview-clip`,qo=async(e,o,t)=>r(ae(e),{...t,method:"POST",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)}),fe=(e,o)=>`/v1/projects/${e}/renders/${o}`,Io=async(e,o,t)=>r(fe(e,o),{...t,method:"GET"}),me=(e,o)=>`/v1/projects/${e}/renders/${o}/cancel`,bo=async(e,o,t)=>r(me(e,o),{...t,method:"POST"}),ce=(e,o)=>`/v1/projects/${e}/renders/${o}/retry`,To=async(e,o,t)=>r(ce(e,o),{...t,method:"POST"}),de=(e,o)=>`/v1/projects/${e}/renders/${o}/refresh-url`,Eo=async(e,o,t)=>r(de(e,o),{...t,method:"POST"}),ue=(e,o)=>`/v1/projects/${e}/renders/${o}/stream`,wo=async(e,o,t)=>r(ue(e,o),{...t,method:"GET"});var Ce={};y(Ce,{cancelExport:()=>Fo,createFcpxmlExport:()=>Bo,createPremiereExport:()=>Ko,getCancelExportUrl:()=>ge,getCreateFcpxmlExportUrl:()=>le,getCreatePremiereExportUrl:()=>ye,getExport:()=>Ao,getGetExportUrl:()=>Re});var le=e=>`/v1/projects/${e}/exports/fcpxml`,Bo=async(e,o,t)=>r(le(e),{...t,method:"POST",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)}),ye=e=>`/v1/projects/${e}/exports/premiere`,Ko=async(e,o,t)=>r(ye(e),{...t,method:"POST",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)}),Re=(e,o)=>`/v1/projects/${e}/exports/${o}`,Ao=async(e,o,t)=>r(Re(e,o),{...t,method:"GET"}),ge=(e,o)=>`/v1/projects/${e}/exports/${o}/cancel`,Fo=async(e,o,t)=>r(ge(e,o),{...t,method:"POST"});var qe={};y(qe,{createSession:()=>Uo,deleteSession:()=>$o,getCreateSessionUrl:()=>he,getDeleteSessionUrl:()=>ve,getGetMuxJobUrl:()=>ke,getGetSessionUrl:()=>Se,getListSessionsUrl:()=>Oe,getMuxJob:()=>Jo,getSession:()=>Lo,getTriggerMuxJobUrl:()=>Pe,listSessions:()=>Mo,triggerMuxJob:()=>_o});var Oe=()=>"/v1/sessions",Mo=async e=>r(Oe(),{...e,method:"GET"}),he=()=>"/v1/sessions",Uo=async(e,o)=>r(he(),{...o,method:"POST",headers:{"Content-Type":"application/json",...o?.headers},body:JSON.stringify(e)}),Se=e=>`/v1/sessions/${e}`,Lo=async(e,o)=>r(Se(e),{...o,method:"GET"}),ve=e=>`/v1/sessions/${e}`,$o=async(e,o)=>r(ve(e),{...o,method:"DELETE"}),Pe=e=>`/v1/sessions/${e}/mux-job`,_o=async(e,o)=>r(Pe(e),{...o,method:"POST"}),ke=e=>`/v1/sessions/${e}/mux-job`,Jo=async(e,o)=>r(ke(e),{...o,method:"GET"});var Be={};y(Be,{createComponent:()=>Do,getComponent:()=>zo,getComponentSource:()=>Zo,getCreateComponentUrl:()=>be,getGetComponentSourceUrl:()=>we,getGetComponentUrl:()=>Ee,getListCatalogUrl:()=>Ie,getUpdateComponentUrl:()=>Te,listCatalog:()=>Wo,updateComponent:()=>No});var Ie=e=>{let o=new URLSearchParams;Object.entries(e||{}).forEach(([s,n])=>{n!==void 0&&o.append(s,n===null?"null":n.toString())});let t=o.toString();return t.length>0?`/v1/components?${t}`:"/v1/components"},Wo=async(e,o)=>r(Ie(e),{...o,method:"GET"}),be=()=>"/v1/components",Do=async(e,o)=>r(be(),{...o,method:"POST",headers:{"Content-Type":"application/json",...o?.headers},body:JSON.stringify(e)}),Te=e=>`/v1/components/${e}`,No=async(e,o,t)=>r(Te(e),{...t,method:"PUT",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)}),Ee=e=>`/v1/components/${e}`,zo=async(e,o)=>r(Ee(e),{...o,method:"GET"}),we=e=>`/v1/components/${e}/source`,Zo=async(e,o)=>r(we(e),{...o,method:"GET"});var h={};y(h,{composeFromSuggestion:()=>tt,getComposeFromSuggestionUrl:()=>Ke,getListMediaSuggestionsUrl:()=>Ae,getResetMediaSuggestionsUrl:()=>Ge,getSuggestBriefsUrl:()=>Fe,listMediaSuggestions:()=>rt,resetMediaSuggestions:()=>nt,suggestBriefs:()=>st});var Ke=e=>`/v1/projects/${e}/composition/from-suggestion`,tt=async(e,o,t)=>r(Ke(e),{...t,method:"POST",headers:{"Content-Type":"application/json",...t?.headers},body:JSON.stringify(o)}),Ae=e=>`/v1/media/${e}/suggestions`,rt=async(e,o)=>r(Ae(e),{...o,method:"GET"}),Fe=e=>`/v1/media/${e}/suggestions`,st=async(e,o)=>r(Fe(e),{...o,method:"POST"}),Ge=e=>`/v1/media/${e}/suggestions`,nt=async(e,o)=>r(Ge(e),{...o,method:"DELETE"});import{readFileSync as ut,writeFileSync as xt,mkdirSync as lt,existsSync as yt}from"fs";import{join as je}from"path";import{homedir as Rt}from"os";var m={Success:0,UserError:2,ApiClientError:3,ApiServerError:4,NetworkError:5,PaymentRequired:6};function w(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 o=e?.exitCode;return typeof o=="number"?o:m.NetworkError}function Qq(e){return e===402?m.PaymentRequired:e>=500?m.ApiServerError:e>=400?m.ApiClientError:m.Success}function S(e){process.exit(e)}var Me=je(Rt(),".cueframe"),k=je(Me,"auth.json"),g={local:"http://127.0.0.1:3211",dev:"https://api-dev.cueframe.ai",prod:"https://api.cueframe.ai"},gt="prod";function sI(e=gt){return process.env.CUEFRAME_API_URL??g[e]??g.prod}var q=class extends Error{constructor(t,s){super(s);this.code=t;this.name="AuthStoreError"}exitCode=m.UserError};function Ue(){if(!yt(k))return{targets:{}};let e=ut(k,"utf-8");try{return JSON.parse(e)}catch{throw new q("auth_config_corrupt",`${k} is corrupt and cannot be parsed. Delete it and re-run \`cueframe login\`.`)}}function Ct(e){lt(Me,{recursive:!0,mode:448}),xt(k,JSON.stringify(e,null,2),{mode:384})}function nI(e,o="prod",t){let s=Ue(),n=g[o]??g.prod;s.targets[o]={apiKey:e,apiUrl:t??n},Ct(s)}function Le(e="prod"){let o=process.env.CUEFRAME_API_KEY,t=process.env.CUEFRAME_API_URL;if(o)return{apiKey:o,apiUrl:t??g[e]??g.prod};let n=Ue().targets[e];if(!n)throw new q("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 n}function $e(e){let{apiKey:o,apiUrl:t}=Le(e);b({baseUrl:t,apiKey:o})}function I(e){let o=e instanceof u?`${e.message} (${e.code}${e.status?`, ${e.status}`:""})`:e instanceof Error?e.message:String(e),t=E(e);return t?`${o}
|
|
9
|
+
\u2192 ${t}`:o}import Ot from"ora";import ht from"pino";var St="v1",_e=ht({level:"trace",base:{schemaVersion:St},messageKey:"event",timestamp:()=>`,"ts":${Date.now()}`,formatters:{level:()=>({_:0})}});function B(e,o){return Ot({text:e,isSilent:!!o}).start()}function C(e,o,t={}){e.json&&_e.info(t,o)}function uI(e,o,t){if(e.json){console.log(JSON.stringify(o,null,2));return}t()}function K(e,o,t,s){let n=t instanceof Error?t.message:String(t),i=typeof t?.code=="string"?t.code:void 0,p=t?.status;e.json?_e.info({phase:o,message:n,...i?{code:i}:{},...typeof p=="number"?{status:p}:{},exitCode:s},"error"):process.stderr.write(`\u2716 ${o}: ${I(t)}
|
|
10
|
+
`)}function A(e,o,t,s){e.json&&K(e,o,t,s)}function xI(e,o){o.json||console.log(e)}function vt(e){for(let o=0;o<e.length;o++){let t=e.charCodeAt(o);if(t<=31||t===127)return!0}return!1}function We(e,o,t){e.length===0&&Je(`${o} is empty.`,t),vt(e)&&Je(`${o} contains a control character (newline, tab, or null byte). Identifiers must be plain text \u2014 check for a stray copy-paste artifact.`,t)}function Je(e,o){K(o,"preflight",new Error(e),m.UserError),S(m.UserError)}var Pt=5e3,F=300*1e3;async function PI(e,o){if(We(e,"mediaItemId",o),o.dryRun){C(o,"analyze_dry_run",{mediaItemId:e,target:o.target,wait:!!o.wait,wouldCall:"POST /v1/media/:id/suggestions"}),o.json||console.log(`[dry-run] Would trigger clip analysis for ${e} on ${o.target}.`);return}$e(o.target),C(o,"analyze_prepare",{mediaItemId:e,target:o.target,wait:!!o.wait});let t=B("Triggering clip analysis\u2026",o.json),s;try{s=await h.suggestBriefs(e),t.succeed(s.workflowId?`Analysis started (workflow: ${s.workflowId})`:`Analysis ${s.alreadyRunning?"already running":"started"}`)}catch(p){t.fail(`Failed to start analysis: ${I(p)}`),A(o,"analyze_prepare",p,w(p)),S(w(p))}if(C(o,"analyze_ready",{mediaItemId:e,workflowId:s.workflowId,alreadyRunning:!!s.alreadyRunning}),!o.wait){o.json||console.log(`
|
|
11
|
+
Run \`cueframe clips ${e}\` to check results.`);return}let n=B("Waiting for analysis to complete\u2026",o.json),i=Date.now();for(;;){await new Promise(p=>setTimeout(p,Pt)),Date.now()-i>F&&(n.fail(`Timed out after ${Math.round(F/1e3)}s. Run \`cueframe clips ${e}\` to check later.`),A(o,"analyze_progress",new Error(`Analysis did not complete within ${Math.round(F/1e3)}s`),m.UserError),S(m.UserError));try{let p=await h.listMediaSuggestions(e);if(p.suggestions.length>0){n.succeed(`Analysis complete \u2014 ${p.suggestions.length} clips found`),C(o,"analyze_complete",{mediaItemId:e,durationMs:Date.now()-i,suggestionCount:p.suggestions.length,suggestions:p.suggestions}),o.json||p.suggestions.forEach((d,a)=>{let f=(d.trim.end-d.trim.start).toFixed(1);console.log(`
|
|
12
|
+
${a+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 c=Math.round((Date.now()-i)/1e3);n.text=`Waiting for analysis\u2026 (${c}s)`,C(o,"analyze_progress",{mediaItemId:e,elapsedSec:c})}catch{}}}export{u as a,b,j as c,r as d,E as e,He as f,Q as g,ne as h,xe as i,Ce as j,qe as k,Be as l,h as m,m as n,w as o,Qq as p,S as q,sI as r,nI as s,$e as t,I as u,B as v,C as w,uI as x,K as y,A as z,xI as A,We as B,PI as C};
|