cueframe 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cueframe",
3
- "version": "0.4.2",
4
- "description": "Agent-native CLI for CueFrame \u2014 author compositions and render video from the terminal (early-access preview).",
3
+ "version": "0.4.3",
4
+ "description": "Agent-native CLI for CueFrame — author compositions and render video from the terminal (early-access preview).",
5
5
  "keywords": [
6
6
  "cueframe",
7
7
  "video",
@@ -13,8 +13,8 @@
13
13
  "remotion"
14
14
  ],
15
15
  "homepage": "https://docs.cueframe.ai",
16
- "author": "CueFrame",
17
- "license": "MIT",
16
+ "author": "CueFrame Inc.",
17
+ "license": "AGPL-3.0-only",
18
18
  "type": "module",
19
19
  "bin": {
20
20
  "cueframe": "dist/cueframe.js"
@@ -38,20 +38,14 @@
38
38
  "open": "^10.1.0",
39
39
  "ora": "^8.2.0",
40
40
  "pino": "^10.3.1",
41
- "typescript": "^5.8.3"
42
- },
43
- "scripts": {
44
- "build": "tsup",
45
- "check": "tsc --noEmit",
46
- "test": "vitest run",
47
- "prepublishOnly": "tsup"
41
+ "typescript": "^5.9.3"
48
42
  },
49
43
  "devDependencies": {
50
- "@cueframe/sdk": "workspace:*",
51
44
  "@types/node": "^22.15.0",
52
45
  "tsup": "^8.5.1",
53
- "vitest": "^4.1.0",
54
- "@cueframe/component-kit": "workspace:*"
46
+ "vitest": "^4.1.2",
47
+ "@cueframe/sdk": "0.1.1",
48
+ "@cueframe/primitives": "0.1.0"
55
49
  },
56
50
  "repository": {
57
51
  "type": "git",
@@ -60,5 +54,10 @@
60
54
  },
61
55
  "bugs": {
62
56
  "url": "https://github.com/cueframe-ai/cueframe/issues"
57
+ },
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "check": "tsc --noEmit",
61
+ "test": "vitest run"
63
62
  }
64
63
  }
@@ -1,20 +1,3 @@
1
- <!--
2
- SSOT for the "how do I connect to CueFrame" block that every task skill states.
3
-
4
- Not a skill: both readers skip non-directories (generate-skills.mjs checks
5
- `entry.isDirectory()`, install.ts checks for `<name>/SKILL.md`), so a bare file
6
- here is inert.
7
-
8
- Edit THIS file, then update every skill's `<!-- cueframe:connect -->` block to
9
- match it — upstream regenerates the copies mechanically.
10
-
11
- WHY THIS EXISTS. The block used to live in seven hand-maintained copies, and one
12
- fact stated in seven places drifts: a correction landed in some copies and not
13
- the rest, and the untouched lines had already diverged into five phrasings of
14
- the same step. One fact, seven copies, five versions.
15
-
16
- `skillConnect.drift.test.ts` fails if any copy diverges from this file.
17
- -->
18
1
  - **Claude Code:** offer to run
19
2
  `claude mcp add --transport http cueframe https://api.cueframe.ai/v1/mcp` — a browser
20
3
  opens and the user clicks **Allow**. No API key.
@@ -63,8 +63,9 @@ Then straight back to hunting the moment — setup is a footnote, not a phase.
63
63
  6. **Captions from the real transcript.** `captions.fromTranscript` with the mediaId and the
64
64
  clip's window; style via `setCaptionStyle` — plated, high-contrast, never over the mouth or
65
65
  eyes. Where the footage allows, put the emphasized keyword BEHIND the speaker as an overlay
66
- clip at `zPlane:"behind-subject"` (the caption layer itself carries no zPlane) — the
67
- signature depth move.
66
+ clip at `zPlane:"behind-subject"` — the focused signature depth move. If the user explicitly
67
+ wants every running caption behind the speaker, set the caption layer's own
68
+ `zPlane:"behind-subject"`; that choice resolves cutouts across every captioned source window.
68
69
  7. **Decide the audio — required.** Speech is the spine here, but decide the rest: a subtle bed
69
70
  from `search_resources` honoring the brand kit's `musicMood`, ducked under the voice — or
70
71
  intentional speech-only, recorded with its reason in the delivery note. Either is fine;
@@ -496,8 +496,8 @@ Each playbook is a **floor**, not a template — reason above it. Every beat map
496
496
  | Bring in the real thing | `import_media` — requires `url` (public https) + `filename` + `contentType` (from the fixed enum: `video/mp4`, `video/quicktime`, `image/png`, `audio/mpeg`, …). Poll `list_media` or `create_webhook` on `media.completed` (status inside: complete|failed). |
497
497
  | Know where subject + speech are | `get_media_context` → face boxes per frame (with `faceId`) + transcript |
498
498
  | Keep the speaker framed / punch-in | `setCropIntents` op (the PREFERRED authoring path) or inline `reframe.segments` on a `clip.add` source. Each segment needs `startSec`/`endSec`/`focus`/`zoom`; **segments must TILE the clip** (an uncovered span → `reframe_coverage_gap` at validate). `focus.mode`: `frame-center` \| `point{x,y}` \| `face{faceId, shotScale?: close\|standard\|wide}` \| `all-faces` \| `active-speaker`. `zoom<1` punches in, `=1` full frame, `>1` rejected (min 0.1). Times in **seconds**. |
499
- | Captions that track real speech | `composition.captions.segments[].words[]` = `{text, startMs, endMs, emphasis?, annotate?: circle\|underline}` — word times in **milliseconds**. Author the words directly with the `setCaptions` op (segments wholesale — how a generated-VO film gets its word-synced spine without a source transcript); style via `setCaptionStyle`: `position` (top\|center\|bottom), `entrance` (fade\|word-pop\|stagger-up), `emphasisPlate` + plate colors for legibility. **Captions have NO `zPlane`.** |
500
- | Text behind the speaker's head | An `overlay` clip (a title/label primitive) with `zPlane:"behind-subject"` — NOT the caption layer. Render bakes the person matte + handles occlusion. |
499
+ | Captions that track real speech | `composition.captions.segments[].words[]` = `{text, startMs, endMs, emphasis?, annotate?: circle\|underline}` — word times in **milliseconds**. Author the words directly with the `setCaptions` op (segments wholesale — how a generated-VO film gets its word-synced spine without a source transcript); style via `setCaptionStyle`: `position` (top\|center\|bottom), `entrance` (fade\|word-pop\|stagger-up), `emphasisPlate` + plate colors for legibility. Captions default to front; set top-level `composition.captions.zPlane:"behind-subject"` only when the user wants the whole caption layer behind the speaker. It resolves person cutouts for every overlapping source window and degrades visibly to front where the legibility gate refuses the effect. |
500
+ | Text behind the speaker's head | An `overlay` clip (a title/label primitive) with `zPlane:"behind-subject"`. This is the focused hero treatment; caption-layer depth is a separate whole-layer choice. Render resolves the person cutout + handles occlusion. |
501
501
  | Name someone / label a step | `lower-third` scene from `list_catalog` |
502
502
  | Title / closing wordmark | `cinematic-title` scene (closing, not opening) |
503
503
  | Grade the look | `color-grade` scene/effect |
@@ -103,9 +103,10 @@ Everything in §1, plus:
103
103
  - **MUST** Active-speaker reframe (`focus: active-speaker` / `face`) — a
104
104
  moving camera, not a static center crop.
105
105
  - **MUST** Captions timed to the real transcript (`captions.fromTranscript`).
106
- The behind-the-speaker LOOK is an overlay text layer at
107
- `zPlane:"behind-subject"` where the footage allows — the caption layer
108
- itself always renders in front.
106
+ The focused behind-the-speaker LOOK is an overlay text layer at
107
+ `zPlane:"behind-subject"` where the footage allows. Use caption-layer
108
+ `zPlane:"behind-subject"` only when the user explicitly wants every running
109
+ caption behind the speaker and the legibility gate accepts it.
109
110
  - **MUST** Dead air and false starts cut (`addExcludedRange` /
110
111
  `cutAndRipple`).
111
112
 
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import{C as a}from"./chunk-7TCLKYUZ.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(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};