cueframe 0.1.0 → 0.1.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 +1 -1
- package/dist/analyze-CN3MXICA.js +2 -0
- package/dist/chunk-ZBD4U6PD.js +12 -0
- package/dist/cueframe.js +67 -24
- package/package.json +14 -12
- package/skills/cueframe-brand-demo/SKILL.md +248 -0
- package/skills/cueframe-cli/SKILL.md +40 -9
- package/skills/cueframe-component-authoring/SKILL.md +67 -0
- package/dist/analyze-JQBFZK57.js +0 -2
- package/dist/chunk-KEYFXLX4.js +0 -12
package/dist/cueframe.js
CHANGED
|
@@ -1,34 +1,77 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{A as
|
|
2
|
+
import{A as ee,B as w,C as b,D as Ae,a as C,b as p,c as E,d as g,e as P,f as I,g as Ie,h as Re,i as _,j as fe,k as Te,l as Oe,m as N,n as J,o as Pe,p as Y,q as G,r as Q,s as Fe,t as ge,u as l,v as h,w as M,x as m,y as v,z as X}from"./chunk-ZBD4U6PD.js";import{Command as bo}from"commander";import{existsSync as Me,mkdirSync as Bt,copyFileSync as Kt,readdirSync as Gt,statSync as Ht}from"fs";import{homedir as Vt}from"os";import{dirname as Ue,join as te}from"path";import{fileURLToPath as Zt}from"url";function Yt(){let t=Ue(Zt(import.meta.url));for(let e=0;e<8;e++){let r=te(t,"skills");if(Me(r)&&Ht(r).isDirectory())return r;let o=Ue(t);if(o===t)break;t=o}return null}function De(t){let e=Yt();e||(g(t,"install",new Error("Bundled skills/ directory not found"),l.UserError),m(l.UserError));let r=[];for(let o of Gt(e)){let n=te(e,o,"SKILL.md");if(!Me(n))continue;let i=te(Vt(),".claude","skills",o);Bt(i,{recursive:!0});let s=te(i,"SKILL.md");Kt(n,s),r.push(s)}r.length===0&&(g(t,"install",new Error("No SKILL.md files found under skills/"),l.UserError),m(l.UserError)),E(t,{ok:!0,count:r.length,installed:r},()=>{console.log(`\u2713 Installed ${r.length} CueFrame skill(s):`);for(let o of r)console.log(` \u2192 ${o}`);console.log("Restart your agent session (Claude Code / Cursor / Codex) to load them.")})}import{existsSync as ye,mkdirSync as Qt,readFileSync as Le,writeFileSync as he}from"fs";import{join as we}from"path";var Ne="cueframe.json",Xt="cueframe/components",re=".cueframe/";function er(t,e){let r=we(t,".gitignore");if(ye(r)){let o=Le(r,"utf8"),n=o.split(`
|
|
3
|
+
`).map(i=>i.trim());if(n.includes(re)||n.includes(".cueframe"))return!1;if(!e){let i=o.endsWith(`
|
|
4
|
+
`)||o.length===0?"":`
|
|
5
|
+
`;he(r,`${o}${i}
|
|
6
|
+
# CueFrame derived render cache (regenerated; never authored)
|
|
7
|
+
${re}
|
|
8
|
+
`)}return!0}return e||he(r,`# CueFrame derived render cache (regenerated; never authored)
|
|
9
|
+
${re}
|
|
10
|
+
`),!0}function We(t){let e=process.cwd(),r=t.dryRun??!1;p(t,"init_prepare",{cwd:e,dryRun:r});let o=we(e,Ne),n=ye(o),i=Xt;if(n)try{let u=JSON.parse(Le(o,"utf8"));typeof u.componentsDir=="string"&&u.componentsDir.length>0&&(i=u.componentsDir)}catch{}else r||he(o,`${JSON.stringify({componentsDir:i},null,2)}
|
|
11
|
+
`);let s=we(e,i),a=ye(s);!a&&!r&&Qt(s,{recursive:!0});let f=er(e,r);E(t,{ok:!0,dryRun:r,root:e,manifest:{path:o,created:!n},componentsDir:i,componentsDirCreated:!a,gitignoreUpdated:f},()=>{console.log(`\u2713 ${r?"Would initialize":"Initialized"} CueFrame workspace at ${e}`),console.log(` ${n?"kept":"created"} ${Ne} (componentsDir: ${i})`),console.log(` ${a?"exists":"created"} ${i}/`),f&&console.log(` gitignored ${re}`),console.log("Next: cueframe eject <primitive> or cueframe new component <name>")})}import{existsSync as Ee,mkdirSync as lr,readFileSync as ne,readdirSync as Be,statSync as ur,writeFileSync as qe}from"fs";import{dirname as xe,join as H}from"path";import{fileURLToPath as pr}from"url";import S from"typescript";import A from"typescript";var tr="@cueframe/primitives";function rr(t){if(!t.startsWith("."))return t;let e=t.split("/"),r=e.indexOf("lib");return r===-1||!e.slice(0,r).every(o=>o==="."||o==="..")?t:`${tr}/${e.slice(r).join("/")}`}function je(t,e){let r=A.createSourceFile(e,t,A.ScriptTarget.Latest,!1,e.endsWith(".tsx")?A.ScriptKind.TSX:A.ScriptKind.TS),o=[],n=s=>{let a;if(A.isImportDeclaration(s)||A.isExportDeclaration(s)?a=s.moduleSpecifier:A.isCallExpression(s)&&s.expression.kind===A.SyntaxKind.ImportKeyword&&(a=s.arguments[0]),a&&A.isStringLiteral(a)){let f=rr(a.text);f!==a.text&&o.push({start:a.getStart(r)+1,end:a.getEnd()-1,text:f})}A.forEachChild(s,n)};if(n(r),o.length===0)return t;o.sort((s,a)=>a.start-s.start);let i=t;for(let s of o)i=i.slice(0,s.start)+s.text+i.slice(s.end);return i}import{existsSync as oe,readFileSync as or,readdirSync as nr,statSync as ir}from"fs";import{dirname as sr,join as q,parse as ar}from"path";var cr="cueframe/components";var dr="cueframe.json",mr=ge.object({componentsDir:ge.string().min(1).default(cr)}).passthrough();function L(t){let e=t,r=ar(e).root;for(;;){let o=q(e,dr);if(oe(o)){let n=or(o,"utf8"),i=mr.parse(JSON.parse(n));return{root:e,manifest:i}}if(e===r)return null;e=sr(e)}}function ze(t){return q(t.root,t.manifest.componentsDir)}function B(t,e){return q(ze(t),e)}function Je(t){let e=ze(t);return oe(e)?nr(e).filter(r=>{let o=q(e,r);return ir(o).isDirectory()?oe(q(o,"index.tsx"))&&oe(q(o,"config.ts")):!1}).sort():[]}var be="@cueframe/primitives",fr="cueframe.json";function gr(){let t=xe(pr(import.meta.url));for(;;){let e=H(t,"packages","execution","primitives","src");if(Ee(e))return e;let r=xe(t);if(r===t)return null;t=r}}function Ke(t){let e=[];for(let r of Be(t)){if(r==="node_modules"||r==="dist"||r==="__tests__")continue;let o=H(t,r);ur(o).isDirectory()?e.push(...Ke(o)):r==="config.ts"&&e.push(o)}return e}function yr(t){let e=t;for(;S.isAsExpression(e)||S.isParenthesizedExpression(e)||typeof S.isSatisfiesExpression=="function"&&S.isSatisfiesExpression(e);)e=e.expression;return e}function hr(t,e){let r=S.createSourceFile(e,t,S.ScriptTarget.Latest,!0,S.ScriptKind.TS),o;for(let i of r.statements)if(S.isVariableStatement(i)){for(let s of i.declarationList.declarations)if(S.isIdentifier(s.name)&&s.name.text==="config"&&s.initializer){let a=yr(s.initializer);S.isObjectLiteralExpression(a)&&(o=a)}if(o)break}let n={sf:r};if(!o)return n;for(let i of o.properties){if(!S.isPropertyAssignment(i)||!(S.isIdentifier(i.name)||S.isStringLiteral(i.name)))continue;let s=i.name.text;s==="id"&&S.isStringLiteralLike(i.initializer)?n.id=i.initializer.text:s==="version"&&S.isStringLiteralLike(i.initializer)?n.version=i.initializer.text:s==="component"&&S.isIdentifier(i.initializer)&&(n.componentName=i.initializer.text)}return n}function Ge(t,e){let r=t.importClause;if(!r||r.isTypeOnly)return!1;if(r.name&&r.name.text===e)return!0;let o=r.namedBindings;return o&&S.isNamedImports(o)?o.elements.some(n=>!n.isTypeOnly&&n.name.text===e):!1}function wr(t,e){for(let r of t.statements)if(S.isImportDeclaration(r)&&S.isStringLiteral(r.moduleSpecifier)&&Ge(r,e))return r.moduleSpecifier.text}function jr(t,e,r){let o=S.createSourceFile(e,t,S.ScriptTarget.Latest,!1,S.ScriptKind.TS);for(let n of o.statements){if(!S.isImportDeclaration(n)||!S.isStringLiteral(n.moduleSpecifier)||!Ge(n,r))continue;let i=n.moduleSpecifier.getStart(o)+1,s=n.moduleSpecifier.getEnd()-1;return t.slice(0,i)+"./index"+t.slice(s)}return t}function W(t,e){g(t,"eject",new Error(e),l.UserError),m(l.UserError)}function He(t,e){v(t,"primitive",e);let r=t,o=e.dryRun??!1,n=e.force??!1;p(e,"eject_prepare",{primitive:r,dryRun:o,force:n});let i=L(process.cwd());i||W(e,"No CueFrame workspace found (no cueframe.json up-tree). Run `cueframe init` first to scaffold one.");let s=gr();s||W(e,"Built-in primitive source not found \u2014 `cueframe eject` reads in-tree source and must run inside the CueFrame monorepo checkout.");let a,f;for(let U of Ke(s)){let O=hr(ne(U,"utf8"),U);if(O.id===r){a=U,f=O;break}}(!a||!f)&&W(e,`Unknown primitive id "${r}". No built-in primitive declares config.id === "${r}".`),f.componentName||W(e,`Primitive "${r}" config has no \`component:\` identifier to eject.`);let u=f.componentName,c=f.version??"0.0.0",d=wr(f.sf,u);d||W(e,`Could not find the import that provides component "${u}" in ${a}.`);let y=xe(a),x=d.replace(/^\.\//,""),j;for(let U of[".tsx",".ts"]){let O=H(y,x+U);if(Ee(O)){j=O;break}}j||W(e,`Component source for "${r}" not found on disk (resolved "${d}" beside ${a}).`);let k=ne(j,"utf8"),F=je(k,"index.tsx"),R=`/** Owned eject of '${r}'. Adapts the ({durationInFrames, params}) mount contract to the primitive's props. */
|
|
12
|
+
export default function ${u}Owned({ params }: { durationInFrames: number; params: Record<string, unknown> }) {
|
|
13
|
+
return <${u} {...(params as Record<string, never>)} />;
|
|
14
|
+
}
|
|
15
|
+
`,T=`${F.replace(/\s*$/,"")}
|
|
16
|
+
|
|
17
|
+
${R}`,z=ne(a,"utf8"),K=je(z,"_config.ts"),Wt=jr(K,"_config.ts",u),zt=`import component from './index';
|
|
18
|
+
import { config as base } from './_config';
|
|
19
|
+
|
|
20
|
+
export const config = { ...base, component };
|
|
21
|
+
export default config;
|
|
22
|
+
`,D=B(i,r),ke=Ee(D);ke&&Be(D).length>0&&!n&&W(e,`Component dir ${D} already exists and is non-empty. Pass --force to overwrite (this replaces local customizations).`);let pe={"index.tsx":T,"_config.ts":Wt,"config.ts":zt};if(!o){lr(D,{recursive:!0});for(let[Jt,qt]of Object.entries(pe))qe(H(D,Jt),qt);let U=H(i.root,fr),O=JSON.parse(ne(U,"utf8")),_e=O.components??{};_e[r]={ejectedFrom:be,primitiveVersion:c},O.components=_e,qe(U,`${JSON.stringify(O,null,2)}
|
|
23
|
+
`)}E(e,{ok:!0,dryRun:o,primitive:r,component:u,version:c,from:be,dir:D,files:Object.keys(pe),overwritten:ke&&n},()=>{console.log(`\u2713 ${o?"Would eject":"Ejected"} ${r} (${u}) into ${D}`);for(let O of Object.keys(pe))console.log(` ${o?"would write":"wrote"} ${O}`);o||console.log(` stamped cueframe.json components.${r} (${be}@${c})`),console.log("Edit the source, then render it on CueFrame.")})}import{existsSync as br,mkdirSync as Er,readFileSync as xr,readdirSync as vr,writeFileSync as Ve}from"fs";import{join as Ze}from"path";var Sr="cueframe.json";function Cr(t){return t.split(/[^a-zA-Z0-9]+/).filter(Boolean).map(r=>r.charAt(0).toUpperCase()+r.slice(1)).join("")}function $r(t){return t.split(/[^a-zA-Z0-9]+/).filter(Boolean).map(r=>r.charAt(0).toUpperCase()+r.slice(1)).join(" ")}function ve(t,e){g(t,"new_component",new Error(e),l.UserError),m(l.UserError)}function kr(t,e){return`import React from 'react';
|
|
24
|
+
|
|
25
|
+
/** Custom CueFrame component. Mounted as ({ durationInFrames, params }). Edit me. */
|
|
26
|
+
export default function ${e}({ params }: { durationInFrames: number; params: Record<string, unknown> }) {
|
|
27
|
+
return <div style={{ width: '100%', height: '100%', display: 'grid', placeItems: 'center' }}>{String((params as { text?: unknown }).text ?? '${t}')}</div>;
|
|
28
|
+
}
|
|
29
|
+
`}function _r(t,e){return`import { z } from 'zod';
|
|
30
|
+
import component from './index';
|
|
31
|
+
|
|
32
|
+
const paramsSchema = z.object({}).passthrough();
|
|
33
|
+
|
|
34
|
+
export const config = {
|
|
35
|
+
id: '${t}',
|
|
36
|
+
component,
|
|
37
|
+
name: '${e}',
|
|
38
|
+
category: 'ui-blocks',
|
|
39
|
+
kind: 'component',
|
|
40
|
+
version: '0.1.0',
|
|
41
|
+
paramsSchema,
|
|
42
|
+
};
|
|
43
|
+
export default config;
|
|
44
|
+
`}function Ye(t,e){v(t,"name",e);let r=e.dryRun??!1,o=e.force??!1,n=Cr(t);n||ve(e,`name "${t}" has no alphanumeric characters to form a component identifier.`);let i=$r(t);p(e,"new_component_prepare",{name:t,dryRun:r,force:o});let s=L(process.cwd());s||ve(e,"No CueFrame workspace found (no cueframe.json up-tree). Run `cueframe init` first to scaffold one.");let a=B(s,t),f=br(a);f&&vr(a).length>0&&!o&&ve(e,`Component dir ${a} already exists and is non-empty. Pass --force to overwrite (this replaces local source).`);let u={"index.tsx":kr(t,n),"config.ts":_r(t,i)};if(!r){Er(a,{recursive:!0});for(let[x,j]of Object.entries(u))Ve(Ze(a,x),j);let c=Ze(s.root,Sr),d=JSON.parse(xr(c,"utf8")),y=d.components??{};y[t]={custom:!0},d.components=y,Ve(c,`${JSON.stringify(d,null,2)}
|
|
45
|
+
`)}E(e,{ok:!0,dryRun:r,name:t,component:n,dir:a,files:Object.keys(u),overwritten:f&&o},()=>{console.log(`\u2713 ${r?"Would scaffold":"Scaffolded"} custom component ${t} (${n}) into ${a}`);for(let d of Object.keys(u))console.log(` ${r?"would write":"wrote"} ${d}`);r||console.log(` stamped cueframe.json components.${t} (custom)`),console.log("Edit index.tsx, then `cueframe push "+t+"` to publish it to CueFrame.")})}import{existsSync as ie,readFileSync as se}from"fs";import{join as ae}from"path";import $ from"typescript";var Ir="cueframe.json",Rr=["effects","transitions","text","layouts"];function Tr(t){return Rr.includes(t??"")?t:"layouts"}function Or(t){let e=t;for(;$.isAsExpression(e)||$.isParenthesizedExpression(e)||typeof $.isSatisfiesExpression=="function"&&$.isSatisfiesExpression(e);)e=e.expression;return e}function Pr(t,e){for(let r of t.statements)if($.isImportDeclaration(r)&&$.isStringLiteral(r.moduleSpecifier)&&r.moduleSpecifier.text===e)return!0;return!1}function Qe(t,e){let r=$.createSourceFile(e,t,$.ScriptTarget.Latest,!0,$.ScriptKind.TS),o;for(let i of r.statements)if($.isVariableStatement(i)){for(let s of i.declarationList.declarations)if($.isIdentifier(s.name)&&s.name.text==="config"&&s.initializer){let a=Or(s.initializer);$.isObjectLiteralExpression(a)&&(o=a)}if(o)break}let n={};if(!o)return n;for(let i of o.properties){if(!$.isPropertyAssignment(i)||!($.isIdentifier(i.name)||$.isStringLiteral(i.name)))continue;let s=i.name.text;$.isStringLiteralLike(i.initializer)&&(s==="name"?n.name=i.initializer.text:s==="category"?n.category=i.initializer.text:s==="version"&&(n.version=i.initializer.text))}return n}function Fr(t){let e=ae(t,"config.ts");if(!ie(e))return{};let r=se(e,"utf8"),o=$.createSourceFile("config.ts",r,$.ScriptTarget.Latest,!0,$.ScriptKind.TS),n=ae(t,"_config.ts");return Pr(o,"./_config")&&ie(n)?Qe(se(n,"utf8"),"_config.ts"):Qe(r,"config.ts")}function Ar(t){return t.split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function Ur(t,e){let r=ae(t.root,Ir);if(ie(r))try{let n=JSON.parse(se(r,"utf8")).components?.[e]?.ejectedFrom;return typeof n=="string"&&n.length>0?n:void 0}catch{return}}async function Xe(t,e,r){let o=B(t,e),n=ae(o,"index.tsx");if(!ie(n))throw Object.assign(new Error(`Component "${e}" has no index.tsx at ${o}. Scaffold it first (cueframe eject / new component).`),{code:"no_component_source"});let i=se(n,"utf8"),s=Fr(o),a=s.name??Ar(e),f=Tr(s.category),u=Ur(t,e),c=`Custom component "${e}" pushed from the local CueFrame workspace.`,d={type:"object",properties:{}};p(r,"push_component",{id:e,name:a,category:f,version:s.version,ejectedFrom:u});let y=!1;try{await Q.getComponent(e),y=!0}catch(x){if(x instanceof I&&x.status===404)y=!1;else throw x}return y?(await Q.updateComponent(e,{tsxSource:i,name:a,description:c,category:f,propSchema:d,origin:"repo-owned",...u?{ejectedFrom:u}:{}}),p(r,"pushed",{id:e,action:"updated"}),{id:e,action:"updated",name:a,category:f}):(await Q.createComponent({componentId:e,tsxSource:i,name:a,description:c,category:f,propSchema:d,origin:"repo-owned",...u?{ejectedFrom:u}:{}}),p(r,"pushed",{id:e,action:"created"}),{id:e,action:"created",name:a,category:f})}async function et(t,e){t||(g(e,"push",new Error("Pass a component id (cueframe push <id>), or use `cueframe sync` to push all."),l.UserError),m(l.UserError)),v(t,"id",e),w(e.target);let r=L(process.cwd());r||(g(e,"push",new Error("No CueFrame workspace found (no cueframe.json up-tree). Run `cueframe init` first."),l.UserError),m(l.UserError)),p(e,"push_prepare",{id:t,target:e.target});try{let o=await Xe(r,t,e);E(e,{ok:!0,target:e.target,...o},()=>{console.log(`\u2713 ${o.action} component ${o.id} (${o.name}) on CueFrame`)})}catch(o){let n=o.code==="no_component_source"?l.UserError:h(o);g(e,"push",o,n),e.json||console.error(`Push failed: ${b(o)}`),m(n)}}async function tt(t){w(t.target);let e=L(process.cwd());e||(g(t,"sync",new Error("No CueFrame workspace found (no cueframe.json up-tree). Run `cueframe init` first."),l.UserError),m(l.UserError));let r=Je(e);p(t,"sync_prepare",{target:t.target,count:r.length,ids:r});let o=[],n=[];for(let i of r)try{o.push(await Xe(e,i,t))}catch(s){let a=s.code==="no_component_source"?l.UserError:h(s);g(t,"sync",s,a),n.push({id:i,error:s})}E(t,{ok:n.length===0,target:t.target,pushed:o,failed:n.map(i=>({id:i.id,error:b(i.error)}))},()=>{if(o.length===0&&n.length===0){console.log("No components to sync. Scaffold one with `cueframe eject` or `cueframe new component`.");return}for(let i of o)console.log(`\u2713 ${i.action} ${i.id} (${i.name})`);for(let i of n)console.error(`\u2716 ${i.id}: ${b(i.error)}`);console.log(`Synced ${o.length}/${r.length} component(s) to CueFrame.`)}),n.length>0&&m(h(n[0].error))}var ce=["media","sessions","projects","renders","webhooks"],rt={readonly:Object.fromEntries(ce.map(t=>[t,["read"]])),editall:Object.fromEntries(ce.map(t=>[t,["read","write"]]))};function Mr(t){if(t.preset){let e=rt[t.preset];return e||(console.error(`Unknown --preset "${t.preset}". Valid: ${Object.keys(rt).join(", ")}.`),m(l.UserError)),e}if(t.scope)return Dr(t.scope);console.error("Provide --scope or --preset (e.g. --preset readonly | editall)."),m(l.UserError)}function Dr(t){let e={};for(let r of t.split(",").map(o=>o.trim()).filter(Boolean)){let[o,n]=r.split(":");(!o||!n)&&(console.error(`Invalid --scope "${r}". Use resource:rw (e.g. renders:rw,media:r).`),m(l.UserError)),ce.includes(o)||(console.error(`--scope "${r}": unknown resource "${o}". Valid: ${ce.join(", ")}.`),m(l.UserError));let i=[];/r/i.test(n)&&i.push("read"),/w/i.test(n)&&i.push("write"),i.length===0&&(console.error(`--scope "${r}": actions must include r and/or w.`),m(l.UserError)),e[o]=i}return Object.keys(e).length===0&&(console.error("--scope is required, e.g. --scope media:rw,projects:rw,renders:rw"),m(l.UserError)),e}async function ot(t){w("dev");let e=Mr(t),r={name:t.name,permissions:e};if(t.expires){let o=Number(t.expires);(!Number.isFinite(o)||o<=0)&&(console.error("--expires must be a positive number of days."),m(l.UserError)),r.expiresIn=Math.round(o*86400)}try{let o=await fe("/v1/api-keys",{method:"POST",body:JSON.stringify(r),headers:{"content-type":"application/json"}});E(t,{ok:!0,id:o.id,prefix:o.prefix,start:o.start,key:o.key},()=>{let n=Object.entries(e).map(([i,s])=>`${i}:${s.map(a=>a[0]).join("")}`).join(" ");console.log(`\u2713 API key "${t.name}" created \u2014 scopes: ${n}`),console.log(`
|
|
3
46
|
${o.key}
|
|
4
|
-
`),console.log(" Shown ONCE \u2014 store it now (paste it into your integration; not saved here).")})}catch(o){g(t,"keys_create",o,
|
|
5
|
-
Media item ID: ${
|
|
6
|
-
Media item ID: ${
|
|
7
|
-
Run \`cueframe analyze ${
|
|
8
|
-
`),
|
|
9
|
-
`))}function
|
|
47
|
+
`),console.log(" Shown ONCE \u2014 store it now (paste it into your integration; not saved here).")})}catch(o){g(t,"keys_create",o,l.ApiClientError),m(l.ApiClientError)}}async function nt(t){w("dev");let e=await _("/api/auth/api-key/list",{method:"GET"});e.status>=400&&(g(t,"keys_list",new Error(`list failed (${e.status})`),l.ApiClientError),m(l.ApiClientError));let r=Array.isArray(e.body)?e.body:[];E(t,{ok:!0,keys:r},()=>{if(r.length===0){console.log("No API keys.");return}for(let o of r)console.log(` ${o.id} ${o.name??"(unnamed)"} ${o.start??""}\u2026`)})}async function it(t,e){w("dev");let r=await _("/api/auth/api-key/delete",{method:"POST",body:JSON.stringify({keyId:t}),headers:{"content-type":"application/json"}});r.status>=400&&(g(e,"keys_revoke",new Error(`revoke failed (${r.status})`),l.ApiClientError),m(l.ApiClientError)),E(e,{ok:!0,revoked:t},()=>console.log(`\u2713 Revoked ${t}`))}async function st(t){w(t.target),p(t,"list_prepare",{target:t.target});try{let e=await J.listProjects(),r=t.limit,o=typeof r=="number"&&Number.isFinite(r)&&r>0?e.data.slice(0,r):e.data;if(p(t,"list_complete",{count:o.length,total:e.data.length,truncated:o.length<e.data.length,projects:o}),!t.json){if(e.data.length===0){console.log("No projects found.");return}for(let n of o){let i=new Date(n.createdAt).toLocaleDateString();console.log(` ${n.id} ${n.name} (${i})`)}o.length<e.data.length&&console.log(` \u2026 ${e.data.length-o.length} more (raise --limit to see all)`)}}catch(e){g(t,"list",e,h(e)),t.json||console.error(`Failed to list projects: ${b(e)}`),m(h(e))}}import{readFile as Nr,stat as Lr}from"fs/promises";import{existsSync as Wr}from"fs";import{basename as zr,extname as Jr}from"path";async function at(t){let{file:e,signingUrl:r,token:o,metadata:n,onProgress:i}=t,s=r.replace(/\/$/,""),a={"uppy-auth-token":o,"Content-Type":"application/json"},f=await fetch(`${s}/s3/multipart`,{method:"POST",headers:a,body:JSON.stringify({filename:e.name,type:e.type||"video/mp4",metadata:{uploadToken:o,...n}})});if(!f.ok)throw new Error(`Upload init failed: ${f.status}`);let{uploadId:u}=await f.json(),c=Math.ceil(e.size/10485760),d=[];for(let x=0;x<c;x++){let j=x*10485760,k=Math.min(j+10485760,e.size),F=e.slice(j,k),R=x+1,T=await fetch(`${s}/s3/multipart/${u}/${R}`,{headers:{"uppy-auth-token":o}});if(!T.ok)throw new Error(`Presign failed for part ${R}: ${T.status}`);let{url:z}=await T.json(),K=await fetch(z,{method:"PUT",body:F});if(!K.ok)throw new Error(`Part ${R} upload failed: ${K.status}`);d.push({PartNumber:R,ETag:K.headers.get("ETag")??""}),i?.(k/e.size)}let y=await fetch(`${s}/s3/multipart/${u}/complete`,{method:"POST",headers:a,body:JSON.stringify({parts:d})});if(!y.ok)throw new Error(`Complete failed: ${y.status}`);return await y.json()}var qr=4e3,ct=.1;function Br(t){switch(Jr(t).toLowerCase()){case".mp4":return"video/mp4";case".mov":return"video/quicktime";case".webm":return"video/webm";case".mkv":return"video/x-matroska";default:return"video/mp4"}}async function dt(t,e){Wr(t)||(g(e,"preflight",new Error(`File not found: ${t}`),l.UserError),e.json||console.error(`File not found: ${t}`),m(l.UserError));let r=zr(t),o=(await Lr(t)).size,n=Br(r);if(e.dryRun){p(e,"upload_dry_run",{file:r,sizeBytes:o,contentType:n,target:e.target,analyzeAfter:!!e.analyze,wouldCall:"POST /v1/media \u2192 multipart upload \u2192 poll processing"}),e.json||console.log(`[dry-run] Would upload ${r} (${(o/1024/1024).toFixed(1)} MB, ${n}) to ${e.target}.`);return}w(e.target),p(e,"upload_prepare",{file:r,sizeBytes:o,contentType:n,target:e.target});let i=C("Preparing upload\u2026",e.json),s,a,f;try{let j=await N.createMediaUpload({filename:r,contentType:n,size:o});s=j.id,a=j.upload.endpoint,f=j.upload.token,i.succeed("Ready"),p(e,"upload_ready",{mediaItemId:s,endpoint:a,tokenIssued:!0})}catch(j){i.fail(`Failed: ${b(j)}`),g(e,"upload_prepare",j,h(j)),m(h(j))}let u=Date.now(),c=C(`Uploading ${r} (${(o/1024/1024).toFixed(1)} MB)\u2026`,e.json);try{let j=await Nr(t),k=new File([j],r,{type:n}),F=0;await at({file:k,signingUrl:a,token:f,metadata:{convexMediaItemId:s},onProgress:R=>{c.text=`Uploading ${r} \xB7 ${(R*100).toFixed(0)}%`;let T=Math.floor(R/ct)*ct;T>F&&T<1&&(F=T,p(e,"upload_progress",{percent:Math.round(T*100)}))}}),c.succeed(`Uploaded: ${r}`),p(e,"upload_complete",{mediaItemId:s,bytesUploaded:o,durationMs:Date.now()-u})}catch(j){c.fail(`Upload failed: ${b(j)}`),g(e,"upload",j,l.NetworkError),m(l.NetworkError)}let d=C("Processing (transcription)\u2026",e.json),y=Date.now(),x=null;for(;;){await new Promise(j=>setTimeout(j,qr));try{let j=await N.getMedia(s),k=j?.processingStatus?.phase??"unknown",F=j?.processingStatus?.percent??null,R=Math.round((Date.now()-y)/1e3),T=F!=null?` \xB7 ${Math.round(F*100)}%`:"";if(d.text=`Processing\u2026 ${k}${T} (${R}s)`,p(e,"process_progress",{phase:k,percent:F,elapsedSec:R}),k==="complete"||k==="completed"){d.succeed(`Ready: ${s}`),x=j,p(e,"process_complete",{mediaItemId:s,durationMs:Date.now()-y,item:j});break}if(k==="error"){let z=j?.processingStatus?.error??"unknown error";d.fail(`Processing failed: ${z}`),g(e,"process",new Error(z),l.ApiServerError),m(l.ApiServerError)}}catch{}}if(e.analyze){P(`
|
|
48
|
+
Media item ID: ${s}`,e);let{analyzeCommand:j}=await import("./analyze-CN3MXICA.js");await j(s,{target:e.target,wait:!0,json:e.json});return}e.json||E(e,{mediaItemId:s,item:x},()=>{console.log(`
|
|
49
|
+
Media item ID: ${s}`),console.log(`
|
|
50
|
+
Run \`cueframe analyze ${s} --target ${e.target}\` to generate clip suggestions.`)})}function Se(t){let e=Math.floor(t/60),r=Math.floor(t%60);return`${e}:${String(r).padStart(2,"0")}`}async function mt(t,e){t!==void 0&&v(t,"mediaItemId",e),w(e.target),p(e,"clips_prepare",{target:e.target,mode:t?"suggestions":"media-list",...t?{mediaItemId:t}:{}});try{if(!t){let o=await N.listMedia();if(p(e,"clips_complete",{mode:"media-list",count:o.data.length,media:o.data}),!e.json){if(o.data.length===0){console.log("No media items found. Import a video first.");return}console.log("Media items (pass the ID to `cueframe clips <id>`):"),console.log("");for(let n of o.data){let i=n.duration?`${Se(n.duration)} `:"",s=n.processingStatus?.phase??"unknown",a=s==="complete"?"":` [${s}]`;console.log(` ${n.id} ${n.name} ${i}${a}`)}}return}let r=await Fe.listMediaSuggestions(t);if(p(e,"clips_complete",{mode:"suggestions",mediaItemId:t,count:r.suggestions.length,suggestions:r.suggestions}),!e.json){if(r.suggestions.length===0){console.log("No clip suggestions found for this media item."),console.log("If analysis is still running, try again shortly. Otherwise the video may not have suitable clips.");return}console.log(`${r.suggestions.length} clip suggestion${r.suggestions.length!==1?"s":""}:`),console.log("");for(let o of r.suggestions){let n=`${Se(o.trim.start)} \u2192 ${Se(o.trim.end)}`,i=(o.trim.end-o.trim.start).toFixed(1);console.log(` ${o.id}`),console.log(` ${o.title}`),console.log(` ${n} (${i}s) ${o.format} score: ${o.score}`),o.reason&&console.log(` ${o.reason}`),console.log("")}}}catch(r){g(e,"clips",r,h(r)),e.json||console.error(`Failed: ${b(r)}`),m(h(r))}}import{readFileSync as Kr}from"fs";async function lt(t,e){v(t,"projectId",e),w(e.target),p(e,"composition_get_prepare",{projectId:t});let r=await V(`/v1/projects/${t}/composition`,{method:"GET"},e,"composition_get");r.status>=400&&(Z(r,e,"composition_get"),m(M(r.status)));let o=r.headers.etag,n=o&&ft(o);p(e,"composition_get_complete",{projectId:t,etag:n,status:r.status}),E(e,{etag:n,composition:r.body},()=>{n&&console.log(`# ETag: ${n}`),console.log(JSON.stringify(r.body,null,2))})}async function ut(t,e){v(t,"projectId",e),e.body||(console.error("--body is required (inline JSON, @file.json, or @- for stdin)"),m(l.UserError)),w(e.target);let r=await Ce(t,e),o=await gt(e.body);p(e,"composition_put_prepare",{projectId:t,ifMatch:r});let n=new Headers({"content-type":"application/json","if-match":r}),i=await V(`/v1/projects/${t}/composition`,{method:"PUT",headers:n,body:o},e,"composition_put");i.status>=400&&(Z(i,e,"composition_put"),m(M(i.status)));let s=i.headers.etag;p(e,"composition_put_complete",{projectId:t,etag:s,status:i.status,response:i.body}),E(e,{etag:s,response:i.body},()=>{console.log(`Composition saved (ETag: ${s??"unknown"}).`),i.body&&typeof i.body=="object"&&console.log(JSON.stringify(i.body,null,2))})}async function de(t,e){v(t,"projectId",e),e.suggestionId||(console.error("--suggestion <suggestionId> is required"),m(l.UserError)),w(e.target);let r=await Ce(t,e),o;if(e.format)try{o=JSON.parse(e.format)}catch{console.error(`--format must be JSON, e.g. '{"aspectRatio":"9:16","fps":30,"resolution":"fhd"}'`),m(l.UserError)}let n=JSON.stringify({suggestionId:e.suggestionId,...o?{format:o}:{}});p(e,"composition_from_suggestion_prepare",{projectId:t,suggestionId:e.suggestionId,ifMatch:r});let i,s=r,a=0,f=3;for(;;){a++;let c=new Headers({"content-type":"application/json","if-match":s});if(i=await V(`/v1/projects/${t}/composition/from-suggestion`,{method:"POST",headers:c,body:n},e,"composition_from_suggestion"),!(i.status===409&&typeof i.body=="object"&&i.body!==null&&i.body.code==="stale_etag"))break;if(a>f){p(e,"composition_from_suggestion_etag_retries_exhausted",{projectId:t,attempts:f});break}p(e,"composition_from_suggestion_etag_retry",{projectId:t,attempt:a,reason:"stale_etag"}),s=await Ce(t,e)}i.status>=400&&(Z(i,e,"composition_from_suggestion"),m(M(i.status)));let u=i.headers.etag;p(e,"composition_from_suggestion_complete",{projectId:t,suggestionId:e.suggestionId,etag:u,status:i.status,response:i.body,attempts:a}),E(e,{etag:u,response:i.body},()=>{console.log(`Composition built from suggestion ${e.suggestionId} (ETag: ${u??"unknown"}).`),i.body&&typeof i.body=="object"&&console.log(JSON.stringify(i.body,null,2))})}async function pt(t){t.body||(console.error("--body is required (inline JSON, @file.json, or @- for stdin)"),m(l.UserError)),w(t.target);let e=await gt(t.body);p(t,"composition_validate_prepare",{});let r=new Headers({"content-type":"application/json"}),o=await V("/v1/composition/validate",{method:"POST",headers:r,body:e},t,"composition_validate");o.status>=400&&(Z(o,t,"composition_validate"),m(M(o.status))),p(t,"composition_validate_complete",{status:o.status}),E(t,{valid:!0},()=>{console.log("\u2713 Composition is valid.")})}function ft(t){return t.replace(/^W\//,"").replace(/-gzip(?=("?)$)/,"")}async function Ce(t,e){if(e.force)return"*";if(e.ifMatch)return e.ifMatch;let r=await V(`/v1/projects/${t}/composition`,{method:"GET"},e,"composition_etag_fetch");if(r.status===404)return P("No existing composition; treating as create (If-Match: *).",e),"*";r.status>=400&&(Z(r,e,"composition_etag_fetch"),m(M(r.status)));let o=r.headers.etag;return o||(g(e,"composition_etag_fetch",new Error("Server returned no ETag on composition GET; cannot safely thread If-Match. Pass --force to overwrite anyway."),l.ApiServerError),m(l.ApiServerError)),ft(o)}async function gt(t){if(t.startsWith("@")){let e=t.slice(1);if(e==="-"){let r=[];for await(let o of process.stdin)r.push(Buffer.isBuffer(o)?o:Buffer.from(o));return Buffer.concat(r).toString("utf-8")}return Kr(e,"utf-8")}return t}async function V(t,e,r,o){try{return await _(t,e)}catch(n){g(r,o,n,l.NetworkError),m(l.NetworkError)}}function Z(t,e,r){let o=t.body?.error,n=o?.code??"unknown",i=o?.message??t.statusText,s=Gr(n,t.status,o?.details),a=M(t.status);e.json?g(e,r,Object.assign(new Error(i),{code:n,status:t.status,details:o?.details,advice:s}),a):(process.stderr.write(`\u2716 ${r}: ${i} (${n}, ${t.status})
|
|
51
|
+
`),s&&process.stderr.write(` \u2192 ${s}
|
|
52
|
+
`))}function Gr(t,e,r){let o=new I(e,{code:t,message:"",details:r&&typeof r=="object"?r:void 0}),n=Te(o);return n||(e===404?"Project or composition not found. Run `cueframe list` to confirm the project ID; the composition may not yet exist.":null)}async function me(t,e){let r=e.map(s=>s==null?"null":String(s)).join(":"),o=`${t}:${r}`,n=new TextEncoder().encode(o),i=await crypto.subtle.digest("SHA-256",n);return Array.from(new Uint8Array(i),s=>s.toString(16).padStart(2,"0")).join("")}var yt=1500,Hr=1800*1e3;async function*Vr(t,e,r){let o=Re();if(!o?.baseUrl)throw new Error("cueframe: createClient({baseUrl}) must be called before iterComposeEvents.");let n=o.baseUrl.replace(/\/$/,"")+`/v1/projects/${encodeURIComponent(t)}/compose/jobs/${encodeURIComponent(e)}/stream`,i=new Headers({accept:"text/event-stream",...o.headers??{}});o.apiKey&&!i.has("authorization")&&i.set("authorization",`Bearer ${o.apiKey}`);let a=await(o.fetch??fetch)(n,{method:"GET",headers:i,signal:r});if(!a.ok){let d;try{d=(await a.json()).error??{code:"unknown",message:a.statusText}}catch{d={code:"unknown",message:a.statusText}}throw new I(a.status,d,a.headers.get("x-request-id")??void 0)}if(!a.body)throw new Error("SSE response has no body");let f=a.body.getReader(),u=new TextDecoder("utf-8"),c="";try{for(;;){let{value:d,done:y}=await f.read();if(y)break;c+=u.decode(d,{stream:!0});let x=c.indexOf(`
|
|
10
53
|
|
|
11
|
-
`);for(;
|
|
54
|
+
`);for(;x!==-1;){let j=c.slice(0,x);c=c.slice(x+2);let k=Zr(j);k&&(yield k),x=c.indexOf(`
|
|
12
55
|
|
|
13
|
-
`)}}}finally{try{f.releaseLock()}catch{}}}function
|
|
14
|
-
`)){if(!n||n.startsWith(":"))continue;let i=n.indexOf(":");if(i===-1)continue;let
|
|
15
|
-
`))}catch{return null}return{event:e,data:o}}async function
|
|
16
|
-
Project: ${e.project}`),
|
|
17
|
-
\u2192 Render with: cueframe render ${e.project} -t ${e.target}`));return}
|
|
18
|
-
${o.id} ${o.name}`),t.fromSuggestion&&(t.cheap?await
|
|
19
|
-
${o.id} ${o.name} \u2190 composed from ${t.fromSuggestion} ${n}`)}}async function
|
|
20
|
-
Re-fetch via GET /v1/projects/${e.project}/renders/${t}.`),l
|
|
21
|
-
MP4 URL (1-hour TTL): ${t}`),l
|
|
56
|
+
`)}}}finally{try{f.releaseLock()}catch{}}}function Zr(t){if(!t.trim())return null;let e=null,r=[];for(let n of t.split(`
|
|
57
|
+
`)){if(!n||n.startsWith(":"))continue;let i=n.indexOf(":");if(i===-1)continue;let s=n.slice(0,i),a=n.slice(i+1).replace(/^ /,"");s==="event"?e=a:s==="data"&&r.push(a)}if(!e||r.length===0||e!=="compose_event")return null;let o;try{o=JSON.parse(r.join(`
|
|
58
|
+
`))}catch{return null}return{event:e,data:o}}async function Yr(t,e,r={}){let o=Date.now()+(r.timeoutMs??Hr);for(;Date.now()<o;){let n=null;try{for await(let i of Vr(t,e)){if(r.onTypedEvent?.(i.data),i.data.event==="job_completed"){let s=i.data.data;n={kind:"complete",compositionId:s.compositionId??null,etag:s.etag??null};break}if(i.data.event==="job_failed"){let a=i.data.data.reason??"compose_failed";a==="cancelled"||a.startsWith("cancelled")?n={kind:"cancelled",reason:a}:n={kind:"error",message:a};break}}}catch(i){if(i instanceof I)throw i;await new Promise(s=>setTimeout(s,yt));continue}if(n)return n;await new Promise(i=>setTimeout(i,yt))}return{kind:"error",message:`Timed out waiting for compose to reach terminal state. ComposeJob: ${e}, Project: ${t}.`}}async function ht(t,e){v(t,"suggestionId",e),v(e.project,"projectId",e),w(e.target),p(e,"compose_prepare",{suggestionId:t,projectId:e.project,target:e.target});let r=e.idempotencyKey??await me("compose",[e.project,t,e.seed??"auto"]);p(e,"idempotency_key",{scope:"compose",key:r,overridden:!!e.idempotencyKey,seed:e.seed??null});let o=C(`Starting compose for ${t}\u2026`,e.json),n,i=!1;try{let u={suggestionId:t};if(e.critique!==void 0&&(u.critique=e.critique),e.clientNonce!==void 0&&(u.clientNonce=e.clientNonce),e.baseline!==void 0&&(u.parentJobId=e.baseline),e.maxCandidates!==void 0&&(u.maxCandidates=e.maxCandidates),e.seed!==void 0){let d=Number(e.seed);Number.isInteger(d)&&(u.seed=d)}let c=await _(`/v1/projects/${e.project}/compose`,{method:"POST",headers:{"content-type":"application/json","idempotency-key":r},body:JSON.stringify(u)});if(c.status>=400){let d=c.body?.error,y=d?.message??c.statusText;throw new I(c.status,{code:d?.code??"unknown",message:y})}n=c.body,i=c.headers["idempotency-replay"]==="true"}catch(u){o.fail(`Failed to start compose: ${b(u)}`),g(e,"compose_create",u,h(u)),m(h(u))}i?(p(e,"compose_idempotent_replay",{composeJobId:n.composeJobId,projectId:n.projectId,workflowId:n.workflowId,idempotencyKey:r}),o.text=`Compose already in flight or complete (job ${n.composeJobId})\u2026`):(p(e,"compose_started",{composeJobId:n.composeJobId,projectId:n.projectId,workflowId:n.workflowId}),o.text=`Composing (job ${n.composeJobId})\u2026`);let s,a,f=Date.now();try{s=await Yr(e.project,n.composeJobId,{onTypedEvent:u=>{if(p(e,`compose:${u.event}`,{composeJobId:n.composeJobId,...u.data,timestamp:u.timestamp}),e.json)return;let c=Math.round((Date.now()-f)/1e3),d=u.data;if(u.event==="job_started")o.text=`Composing\u2026 ${d.candidateCount??"?"} candidates (${c}s)`;else if(u.event==="candidate_phase_started")o.text=`Candidate ${d.candidateIndex} starting (${c}s)`;else if(u.event==="candidate_phase_evaluated"){let y=d.perCriterion,x=y?`E${(y.editorial?.score??0).toFixed(1)} S${(y.spatial?.score??0).toFixed(1)} B${(y.brand?.score??0).toFixed(1)} C${(y.caption?.score??0).toFixed(1)}`:"";o.text=`Candidate ${d.candidateIndex} evaluated \xB7 ${x} (${c}s)`}else if(u.event==="critique_retry_started")o.text=`Candidate ${d.candidateIndex} retry ${d.attemptNumber} (${c}s)`;else if(u.event==="candidate_completed"){let y=typeof d.composite=="number"?d.composite.toFixed(2):"?";o.text=`Candidate ${d.candidateIndex} done \xB7 composite ${y} (${c}s)`}else if(u.event==="winner_selected"){let y=typeof d.composite=="number"?d.composite.toFixed(2):"?";(d.route==="publish"||d.route==="handoff")&&(a=d.route);let x=a==="handoff"?" \xB7 \u2691 handoff":a==="publish"?" \xB7 \u2713 publish":"";o.text=`Picked candidate ${d.candidateIndex} \xB7 composite ${y}${x} (${c}s)`}}})}catch(u){o.fail(`Compose watch failed: ${b(u)}`),g(e,"compose_watch",u,h(u)),m(h(u))}if(s.kind==="complete"){o.succeed(`Compose complete (job ${n.composeJobId})`),p(e,"compose_complete",{composeJobId:n.composeJobId,projectId:e.project,compositionId:s.compositionId,etag:s.etag}),e.json||(console.log(`
|
|
59
|
+
Project: ${e.project}`),s.compositionId&&console.log(` Composition: ${s.compositionId}`),s.etag&&console.log(` ETag: ${s.etag}`),a==="publish"?console.log(" Verdict: \u2713 publish \u2014 clears the quality bar, ships autonomously"):a==="handoff"&&console.log(" Verdict: \u2691 handoff \u2014 below bar / floor violation; review in your NLE before publishing"),console.log(`
|
|
60
|
+
\u2192 Render with: cueframe render ${e.project} -t ${e.target}`));return}s.kind==="cancelled"&&(o.warn(`Compose cancelled: ${s.reason}`),p(e,"compose_cancelled",{composeJobId:n.composeJobId,reason:s.reason}),m(l.UserError)),o.fail(`Compose failed: ${s.message}`),g(e,"compose_run",new Error(s.message),l.ApiServerError),m(l.ApiServerError)}async function wt(t){let e=t.aspect?{aspectRatio:t.aspect,fps:t.fps??30,resolution:t.resolution??"fhd"}:void 0;if(t.dryRun){if(p(t,"project_create_dry_run",{name:t.name,target:t.target,format:e??null,fromSuggestion:t.fromSuggestion??null,cheap:t.cheap??!1,wouldCall:t.fromSuggestion?t.cheap?"POST /v1/projects \u2192 POST /v1/projects/:id/composition/from-suggestion (sync translator)":"POST /v1/projects \u2192 POST /v1/projects/:id/compose (async) \u2192 SSE watch":"POST /v1/projects"}),!t.json){let n=t.cheap?" (--cheap: deterministic translator)":"";console.log(`[dry-run] Would create project "${t.name}" on ${t.target}`+(t.fromSuggestion?` from suggestion ${t.fromSuggestion}${n}.`:"."))}return}w(t.target),p(t,"project_create_prepare",{name:t.name,target:t.target,fromSuggestion:t.fromSuggestion??null,cheap:t.cheap??!1});let r=C(`Creating project "${t.name}"\u2026`,t.json),o;try{o=await J.createProject({name:t.name,format:e}),r.succeed(`Created: ${o.id}`)}catch(n){r.fail(`Failed: ${b(n)}`),g(t,"project_create",n,h(n)),m(h(n))}if(p(t,"project_create_complete",{project:o}),!t.json&&!t.fromSuggestion&&console.log(`
|
|
61
|
+
${o.id} ${o.name}`),t.fromSuggestion&&(t.cheap?await de(o.id,{target:t.target,suggestionId:t.fromSuggestion,json:t.json,force:!0}):await ht(t.fromSuggestion,{target:t.target,project:o.id,json:t.json}),!t.json)){let n=t.cheap?"(--cheap)":"(composed)";console.log(`
|
|
62
|
+
${o.id} ${o.name} \u2190 composed from ${t.fromSuggestion} ${n}`)}}async function jt(t,e){v(t,"projectId",e),w(e.target),p(e,"project_delete_prepare",{projectId:t});try{await J.deleteProject(t),p(e,"project_delete_complete",{projectId:t}),E(e,{ok:!0,projectId:t},()=>{console.log(`Project ${t} deleted.`)})}catch(r){g(e,"project_delete",r,h(r)),e.json||console.error(`\u2716 project delete: ${b(r)}`),m(h(r))}}async function bt(t,e){v(t,"mediaId",e),w(e.target),p(e,"media_delete_prepare",{mediaId:t});try{await N.deleteMedia(t),p(e,"media_delete_complete",{mediaId:t}),E(e,{ok:!0,mediaId:t},()=>{console.log(`Media ${t} deleted.`)})}catch(r){g(e,"media_delete",r,h(r)),e.json||console.error(`\u2716 media delete: ${b(r)}`),m(h(r))}}import{writeFileSync as Qr}from"fs";import{resolve as Xr}from"path";var Et=1500,eo=4e3,to=1800*1e3;async function le(t,e,r={}){let o=Date.now()+(r.timeoutMs??to),n=Date.now();for(;Date.now()<o;){let i=null,s=!1;try{for await(let a of Oe(t,e)){if(a.event==="progress"){let f=Math.round((Date.now()-n)/1e3);if(r.spinner){let u=Math.round(a.data.percent*100);r.spinner.text=`Rendering\u2026 ${a.data.phase}${u>0?` \xB7 ${u}%`:""} (${f}s)`}r.onProgress?.({phase:a.data.phase,percent:a.data.percent,elapsedSec:f});continue}a.event==="complete"?i={kind:"complete",outputUrl:a.data.outputUrl,outputExpiresAt:a.data.outputExpiresAt,duration:a.data.duration}:i={kind:"failed",message:a.data.message};break}}catch(a){if(a instanceof I)s=!0;else{await new Promise(f=>setTimeout(f,Et));continue}}if(i)return i;if(s){let a=await ro(t,e);if(a)return a;await new Promise(f=>setTimeout(f,eo));continue}await new Promise(a=>setTimeout(a,Et))}return{kind:"failed",message:`Timed out waiting for render to reach a terminal state. Render ID: ${e}, Project ID: ${t}.`}}async function ro(t,e){let r=await Pe.getRender(t,e);return r.status==="complete"?{kind:"complete",outputUrl:r.outputUrl??null,outputExpiresAt:null,duration:null}:r.status==="error"||r.status==="cancelled"?{kind:"failed",message:r.error??`Render ${r.status}`}:null}async function xt(t,e){v(t,"renderId",e),v(e.project,"projectId",e),w(e.target),p(e,"render_watch_prepare",{renderId:t,projectId:e.project,target:e.target});let r=C(`Watching render ${t}\u2026`,e.json),o;try{o=await le(e.project,t,{spinner:r,onProgress:i=>p(e,"render_progress",{renderId:t,...i})})}catch(i){r.fail(`Failed to watch render: ${b(i)}`),g(e,"render_watch",i,h(i)),m(h(i))}o.kind==="failed"&&(r.fail(o.message),g(e,"render_watch",new Error(o.message),l.ApiServerError),m(l.ApiServerError)),r.succeed(`Render complete (${t})`);let n=null;e.download&&o.outputUrl?n=await ue(o.outputUrl,e.out??`./reel-${t.slice(0,8)}.mp4`,e.json):o.outputUrl||(g(e,"render_watch",new Error("Render reported complete but no outputUrl was returned."),l.ApiServerError),e.json||console.error(`Render reported complete but no outputUrl was returned.
|
|
63
|
+
Re-fetch via GET /v1/projects/${e.project}/renders/${t}.`),m(l.ApiServerError)),p(e,"render_complete",{renderId:t,projectId:e.project,outputUrl:o.outputUrl,outputExpiresAt:o.outputExpiresAt,duration:o.duration,outputPath:n}),e.json||(o.outputUrl&&console.log(`MP4 URL: ${o.outputUrl}`),o.outputExpiresAt&&console.log(`Expires: ${o.outputExpiresAt}`),o.duration!=null&&console.log(`Duration: ${o.duration}s`))}async function ue(t,e,r){let o=Xr(e),n=C(`Downloading \u2192 ${o}\u2026`,r);try{let i=await fetch(t);if(!i.ok)throw new Error(`HTTP ${i.status}`);let s=Buffer.from(await i.arrayBuffer());return Qr(o,s),n.succeed(`Saved: ${o} (${(s.length/1024/1024).toFixed(1)} MB)`),o}catch(i){n.fail(`Download failed: ${b(i)}`),console.error(`
|
|
64
|
+
MP4 URL (1-hour TTL): ${t}`),m(l.NetworkError)}}async function oo(t){let e=await _(`/v1/projects/${t}/composition`,{method:"GET"});return e.status===404?"no-composition":e.status>=400?"unknown-composition":e.headers.etag??"no-etag"}async function vt(t,e){if(v(t,"projectId",e),e.dryRun){p(e,"render_dry_run",{projectId:t,target:e.target,wouldCall:"POST /v1/projects/:id/renders \u2192 SSE watch \u2192 download MP4"}),e.json||console.log(`[dry-run] Would render project ${t} on ${e.target}.`);return}w(e.target),p(e,"render_prepare",{projectId:t,target:e.target});let r;try{r=e.idempotencyKey??await me("render",[t,await oo(t)]),p(e,"idempotency_key",{scope:"render",key:r,overridden:!!e.idempotencyKey})}catch(c){r="",p(e,"idempotency_key",{scope:"render",key:null,skipped:!0,reason:c instanceof Error?c.message:String(c)})}let o=C("Queuing render\u2026",e.json),n,i,s=!1;try{let c=e.aspect?{format:{aspectRatio:e.aspect,fps:e.fps??30,resolution:e.resolution??"fhd"}}:{};e.priority&&(c.priority=e.priority),e.intent&&(c.intent=e.intent);let d={"content-type":"application/json"};r&&(d["idempotency-key"]=r);let y=await _(`/v1/projects/${t}/renders`,{method:"POST",headers:d,body:JSON.stringify(c)});if(y.status>=400){let j=y.body?.error;throw new I(y.status,{code:j?.code??"unknown",message:j?.message??y.statusText})}let x=y.body;n=x.id,i=x.status,s=y.headers["idempotency-replay"]==="true",o.succeed(s?`Render replayed: ${n} (status: ${i})`:`Render queued: ${n}`)}catch(c){o.fail(`Failed: ${b(c)}`),g(e,"render_prepare",c,h(c)),m(h(c))}s?p(e,"render_idempotent_replay",{renderId:n,projectId:t,status:i,idempotencyKey:r}):p(e,"render_queued",{renderId:n,projectId:t});let a=C("Rendering\u2026",e.json),f;try{f=await le(t,n,{spinner:a,onProgress:c=>p(e,"render_progress",{renderId:n,...c})})}catch(c){a.fail(`Failed to watch render: ${b(c)}`),g(e,"render_progress",c,h(c)),m(h(c))}f.kind==="failed"&&(a.fail(f.message),g(e,"render_progress",new Error(f.message),l.ApiServerError),m(l.ApiServerError)),a.succeed("Render complete"),f.outputUrl||(g(e,"render_complete",new Error("Render reported complete but no outputUrl was returned."),l.ApiServerError),e.json||console.error(`Render reported complete but no outputUrl was returned.
|
|
22
65
|
Render ID: ${n}
|
|
23
66
|
Project ID: ${t}
|
|
24
|
-
Re-fetch via GET /v1/projects/${t}/renders/${n} once the URL is signed.`),l
|
|
25
|
-
`);for(let[i,
|
|
67
|
+
Re-fetch via GET /v1/projects/${t}/renders/${n} once the URL is signed.`),m(l.ApiServerError));let u=await ue(f.outputUrl,e.out??`./reel-${n.slice(0,8)}.mp4`,e.json);p(e,"render_complete",{renderId:n,projectId:t,outputUrl:f.outputUrl,outputExpiresAt:f.outputExpiresAt,duration:f.duration,outputPath:u})}import no from"open";import{createAuthClient as io}from"better-auth/client";import{deviceAuthorizationClient as so}from"better-auth/client/plugins";async function St(t){let e=X(t.target),r=io({baseURL:e,plugins:[so()]}),o=C("Requesting device code\u2026",t.json),n;try{let{data:u,error:c}=await r.device.code({client_id:"cueframe-cli",scope:"openid profile email"});(c||!u)&&(o.fail(`Could not start login: ${c?.message??"unknown error"}`),m(l.NetworkError)),n=u}catch(u){o.fail(`Could not start login: ${u instanceof Error?u.message:String(u)}`),m(l.NetworkError)}o.succeed("Device code issued");let i=n.verification_uri_complete??n.verification_uri;P("",t),P(` Visit: ${i}`,t),P(` Code: ${n.user_code}`,t),P("",t);try{await no(i)}catch{}let s=C("Waiting for approval in browser\u2026",t.json),a=Date.now()+n.expires_in*1e3,f=n.interval*1e3;for(;Date.now()<a;){await new Promise(u=>setTimeout(u,f));try{let{data:u,error:c}=await r.device.token({grant_type:"urn:ietf:params:oauth:grant-type:device_code",device_code:n.device_code,client_id:"cueframe-cli"});if(u?.access_token){let x=u.access_token;ee(x,t.target,t.url),s.succeed(`Logged in. Token saved for target "${t.target}".`),E(t,{ok:!0,target:t.target,apiUrl:e},()=>{});return}let d=u??c,y=d?.error;y==="authorization_pending"||(y==="slow_down"?f+=5e3:y==="access_denied"?(s.fail("Access denied in the browser."),m(l.UserError)):y==="expired_token"?(s.fail("Device code expired before approval. Run `cueframe login` again."),m(l.UserError)):y&&(s.fail(`Login failed: ${d?.error_description??y}`),m(l.ApiClientError)))}catch{}}s.fail("Timed out waiting for approval. Run `cueframe login` again."),m(l.UserError)}import{readFileSync as ao}from"fs";var Ct=new Set(["GET","POST","PUT","PATCH","DELETE"]);async function _t(t,e,r){let o=t.toUpperCase();Ct.has(o)||(console.error(`Unsupported method: ${t}. Allowed: ${[...Ct].join(", ")}.`),m(l.UserError)),e.startsWith("/")||(console.error(`Path must start with '/'. Got: ${e}`),m(l.UserError)),w(r.target);let n=lo(r.header??[]),i=await uo(r.body,o);i!==void 0&&!n.has("content-type")&&n.set("content-type","application/json");let s;try{s=await _(e,{method:o,headers:n,body:i})}catch(a){console.error(a instanceof Error?a.message:String(a)),m(l.NetworkError)}mo(s,{include:!!r.include,json:!!r.json,method:o,path:e}),m(co(s.status))}function co(t){return t===402?l.PaymentRequired:t>=500?l.ApiServerError:t>=400?l.ApiClientError:l.Success}function mo(t,{include:e,json:r,method:o,path:n}){if(r){console.log(JSON.stringify({status:t.status,statusText:t.statusText,headers:t.headers,body:t.body}));return}if(e){process.stdout.write(`HTTP/1.1 ${t.status} ${t.statusText}
|
|
68
|
+
`);for(let[i,s]of Object.entries(t.headers))process.stdout.write(`${i}: ${s}
|
|
26
69
|
`);process.stdout.write(`
|
|
27
|
-
`)
|
|
70
|
+
`),$t(t.body,t.status>=400);return}if(t.status===204||t.body===null){console.error(`${o} ${n} \u2192 ${t.status} (no content)`);return}$t(t.body,t.status>=400)}function $t(t,e){let r=e?process.stderr:process.stdout;t!=null&&(typeof t=="string"?r.write(t.endsWith(`
|
|
28
71
|
`)?t:t+`
|
|
29
72
|
`):r.write(JSON.stringify(t,null,2)+`
|
|
30
|
-
`))}function
|
|
73
|
+
`))}function lo(t){let e=new Headers;for(let r of t){let o=r.indexOf(":");o===-1&&(console.error(`Invalid --header (expected "Name: value"): ${r}`),m(l.UserError));let n=r.slice(0,o).trim(),i=r.slice(o+1).trim();e.set(n,i)}return e}async function uo(t,e){if(t){if(t.startsWith("@")){let r=t.slice(1);return r==="-"?kt():ao(r,"utf-8")}return t}if(!(e==="GET"||e==="DELETE")&&!process.stdin.isTTY)return kt()}async function kt(){let t=[];for await(let e of process.stdin)t.push(Buffer.isBuffer(e)?e:Buffer.from(e));return Buffer.concat(t).toString("utf-8")}var It=/#\/components\/schemas\/([A-Za-z0-9_]+)/g;function po(t,e){let r=new Set,o=[e];for(;o.length;){let n=o.pop();if(r.has(n)||!(n in t))continue;r.add(n);let i=JSON.stringify(t[n]),s;for(It.lastIndex=0;s=It.exec(i);)o.push(s[1])}return r}function fo(t){let e=t?.paths??{};for(let r of Object.values(e))if(r?.put?.operationId==="putComposition"){let o=r.put?.requestBody?.content?.["application/json"]?.schema?.$ref,n=typeof o=="string"?o.match(/#\/components\/schemas\/(.+)$/):null;if(n)return n[1]}return null}async function Rt(t,e){t!=="composition"&&(console.error(`Unknown schema '${t}'. Supported: composition.`),m(l.UserError)),Ie({baseUrl:X(e.target)});let r;try{r=await _("/v1/openapi.json",{method:"GET"})}catch(u){g(e,"schema",u,l.NetworkError),m(l.NetworkError)}if(r.status>=400){let u=M(r.status);g(e,"schema",new Error(`Failed to fetch /v1/openapi.json (${r.status})`),u),m(u)}let o=r.body,n=o?.components?.schemas??{},i=fo(o);(!i||!(i in n))&&(g(e,"schema",new Error("Could not locate the composition schema in openapi.json (putComposition body ref not found)."),l.ApiServerError),m(l.ApiServerError));let s=po(n,i);s.delete(i);let a={};for(let u of[...s].sort())a[u]=n[u];let f={name:i,schema:n[i],referenced:a};E(e,f,()=>{console.log(JSON.stringify(f,null,2))})}import{writeFileSync as go}from"fs";import{resolve as yo}from"path";var Tt=1800*1e3,ho=5e3;async function $e(t,e){v(e.projectId,"projectId",e),v(e.suggestionId,"suggestionId",e),w(e.target),p(e,"export_prepare",{projectId:e.projectId,suggestionId:e.suggestionId,format:t,target:e.target});let r=C(`Queuing ${t} export\u2026`,e.json),o;try{o=(await(t==="fcpxml"?Y.createFcpxmlExport:Y.createPremiereExport)(e.projectId,{clipSuggestionId:e.suggestionId})).id,r.succeed(`Export queued: ${o}`)}catch(f){r.fail(`Failed: ${b(f)}`),g(e,"export_prepare",f,h(f)),m(h(f))}if(p(e,"export_queued",{exportId:o,projectId:e.projectId,format:t}),!e.wait){P(`Poll status with: cueframe api GET /v1/projects/${e.projectId}/exports/${o}`,e);return}let n=await wo(e.projectId,o,e);n.kind==="failed"&&(g(e,"export_progress",Object.assign(new Error(n.message),n.error?{details:n.error}:{}),l.ApiServerError),e.json||console.error(`Export failed: ${n.message}`),m(l.ApiServerError)),n.outputUrl||(g(e,"export_complete",new Error("Export reported complete but no outputUrl was returned."),l.ApiServerError),e.json||console.error(`Export reported complete but no outputUrl was returned.
|
|
31
74
|
Export ID: ${o}
|
|
32
|
-
Re-fetch via GET /v1/projects/${e.projectId}/exports/${o}.`),l
|
|
33
|
-
Export URL (7-day TTL): ${t}`),l
|
|
34
|
-
${o.name} \u2014 ${o.description}`);for(let n of o.subcommands??[])console.log(` ${o.name} ${n.name} \u2014 ${n.description}`)}}function ze(t){if(!t)return;let e=parseInt(t,10);if(e===24||e===30||e===60)return e;console.error(`Invalid --fps ${t}. Allowed: 24, 30, 60.`),l(m.UserError)}function Ke(){let t=new Mt;t.name("cueframe").description("AI-assisted video editing and rendering").version("0.1.2").option("--json","Emit a structured JSON document on stdout instead of human-formatted output. Spinners are silenced.").option("--dry-run","Validate inputs and report what would happen \u2014 no API calls, no mutations. Applies to upload, analyze, project create, render.");let e=()=>t.opts();t.command("install").description("Install the cueframe-cli agent skill into ~/.claude/skills (Claude Code / Cursor / Codex).").action(()=>ae({json:e().json}));let r=t.command("keys").description("Manage API keys. `keys create` mints a scoped key \u2014 run `cueframe login` first (keys cannot mint keys).");r.command("create").description("Mint a scoped API key (cf_*). Requires a logged-in session.").requiredOption("-n, --name <name>","Key name, e.g. vaaya-staging").option("-s, --scope <scope>","Comma-separated resource:actions, e.g. media:rw,projects:rw,renders:rw (resources: media,sessions,projects,renders,webhooks; actions r/w)").option("--preset <preset>","Scope preset instead of --scope: readonly (read all) | editall (read+write all)").option("--expires <days>","Expire after N days (default: never)").action(s=>de({...s,json:e().json})),r.command("list").description("List your API keys.").action(()=>le({json:e().json})),r.command("revoke").description("Revoke an API key by id.").argument("<keyId>","Key id (from `cueframe keys list`)").action(s=>me(s,{json:e().json})),t.command("login").description("Browser-mediated sign-in (OAuth device flow). Recommended for laptops.").option("-t, --target <target>","Target environment: prod (default), dev, local").option("--url <url>","Override the API base URL (advanced)").action(s=>Re({...s,json:e().json})),t.command("auth").description("Save a static API key (cf_live_\u2026 / cf_test_\u2026). Use for CI / headless environments.").argument("<api-key>","Your CueFrame API key").option("-t, --target <target>","Target environment: prod (default), dev, local").option("--url <url>","Override the API base URL (advanced)").action((s,a)=>{J(s,a.target,a.url),e().json?console.log(JSON.stringify({ok:!0,target:a.target},null,2)):console.log(`API key saved for target "${a.target}"`)}),t.command("list").description("List projects").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-n, --limit <n>","Max projects to show","10").action(s=>ue({target:s.target,limit:parseInt(s.limit),json:e().json}));let o=t.command("project").description("Project commands");if(o.command("create").description("Create a new project. With --from-suggestion, atomically build the composition from a clip suggestion in one round-trip \u2014 the project is then immediately renderable via `cueframe render <projectId>`.").requiredOption("-n, --name <name>","Project name").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-a, --aspect <ratio>","Aspect ratio: 9:16, 1:1, 16:9","9:16").option("--fps <n>","Frame rate","30").option("--resolution <r>","Resolution: hd, fhd, 2k, 4k","fhd").option("--from-suggestion <sug>","Clip suggestion publicId (sug_\u2026) \u2014 atomically builds the composition after project creation so render needs no further args").option("--cheap","With --from-suggestion: use the deterministic translator (no LLM, ~100ms) instead of the AI authoring pass. Useful for tests / programmatic flows.").action(s=>xe({name:s.name,target:s.target,aspect:s.aspect,fps:ze(s.fps),resolution:s.resolution,fromSuggestion:s.fromSuggestion,cheap:s.cheap,json:e().json,dryRun:e().dryRun})),o.command("delete").description("Permanently delete a project (irreversible cleanup \u2014 removes the composition and render history).").argument("<projectId>","Project ID (from `cueframe list` or `project create`)").option("-t, --target <target>","Target environment: prod (default), dev, local").action((s,a)=>Ce(s,{target:a.target,json:e().json})),t.command("media").description("Media library cleanup commands").command("delete").description("Permanently delete a media item (irreversible cleanup \u2014 removes the source and its derived assets).").argument("<mediaId>","Media item ID (from `cueframe clips`)").option("-t, --target <target>","Target environment: prod (default), dev, local").action((s,a)=>ke(s,{target:a.target,json:e().json})),t.command("upload").description("Upload a video file and process it").argument("<file>","Path to video file").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-a, --analyze","Run clip analysis after processing completes").action((s,a)=>fe(s,{target:a.target,analyze:a.analyze,json:e().json,dryRun:e().dryRun})),t.command("analyze").description("Trigger AI clip suggestion analysis for a media item").argument("<mediaItemId>","Media item ID (from `cueframe clips`)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-w, --wait","Wait for analysis to complete and show results").action((s,a)=>ne(s,{target:a.target,wait:a.wait,json:e().json,dryRun:e().dryRun})),t.command("clips").description("List AI-generated clip suggestions for a media item").argument("[mediaItemId]","Media item ID (omit to list your media library)").option("-t, --target <target>","Target environment: prod (default), dev, local").action((s,a)=>he(s,{target:a.target,json:e().json})),0)var p;let i=t.command("render").description("Render a project to MP4 (queues, watches via SSE, downloads). Renders whatever composition is saved on the project \u2014 build it first via `project create --from-suggestion` or `composition from-suggestion`.").allowExcessArguments(!1).argument("<projectId>","Project ID (from `cueframe list` or `project create`)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-a, --aspect <ratio>","Aspect ratio: 9:16, 1:1, 16:9 (defaults to project format)").option("--fps <n>","Frame rate","30").option("--resolution <r>","Resolution: hd, fhd, 2k, 4k","fhd").option("-o, --out <path>","Output file path (default: ./reel-<id>.mp4)").option("--idempotency-key <key>",'Override the auto-derived Idempotency-Key. Default: sha256("render:<projectId>:<currentCompositionETag>"). Pass a fresh UUID to force a new render row.').option("--priority <p>","Routing intent: interactive (low-latency, access-gated), standard (default), background (lowest queue weight).").option("--intent <i>","Encode-quality hint: preview (fast / low-bitrate, for editor scrubbing) or final (default, full Format).").action((s,a)=>Ie(s,{target:a.target,aspect:a.aspect,fps:ze(a.fps),resolution:a.resolution,out:a.out,json:e().json,dryRun:e().dryRun,...a.idempotencyKey?{idempotencyKey:a.idempotencyKey}:{},...a.priority?{priority:a.priority}:{},...a.intent?{intent:a.intent}:{}}));t.command("api").description("Raw API call against the v1 surface (escape hatch \u2014 like `gh api`).").argument("<method>","HTTP method: GET, POST, PUT, PATCH, DELETE").argument("<path>","API path starting with /v1, e.g. /v1/projects").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-b, --body <body>","Request body (inline JSON, @file.json to read from disk, @- for stdin)").option("-H, --header <header...>",'Additional header (repeatable), e.g. -H "If-Match: \\"abc\\""').option("-i, --include","Include response status line + headers in stdout (curl-style). Lets agents read ETag etc.").action((s,a,h)=>Ae(s,a,{target:h.target,body:h.body,header:h.header,include:h.include,json:e().json})),t.command("schema").description("Print the live composition wire schema from the server (/v1/openapi.json) \u2014 discover the contract instead of guessing.").argument("[resource]","Schema to print (currently: composition)","composition").option("-t, --target <target>","Target environment: prod (default), dev, local").action((s,a)=>Me(s,{target:a.target,json:e().json}));let c=t.command("composition").description("Read/write a project's composition. ETag is threaded automatically.");c.command("validate").description("Validate a composition body against the live schema WITHOUT saving or rendering. Returns per-field errors \u2014 author safely before paying for a render.").requiredOption("-b, --body <body>","Composition JSON (inline, @file.json, or @- for stdin)").option("-t, --target <target>","Target environment: prod (default), dev, local").action(s=>be({target:s.target,body:s.body,json:e().json})),c.command("get").description("Fetch the project's composition and ETag").argument("<projectId>","Project ID").option("-t, --target <target>","Target environment: prod (default), dev, local").action((s,a)=>ye(s,{target:a.target,json:e().json})),c.command("put").description("Replace the composition. GETs ETag first for safe concurrent writes.").argument("<projectId>","Project ID").option("-t, --target <target>","Target environment: prod (default), dev, local").requiredOption("-b, --body <body>","Composition JSON (inline, @file.json, or @- for stdin)").option("--if-match <etag>","Use this ETag (skip the GET round-trip)").option("--force","Use If-Match: * (unconditional overwrite \u2014 may clobber concurrent edits)").action((s,a)=>we(s,{target:a.target,body:a.body,ifMatch:a.ifMatch,force:a.force,json:e().json})),c.command("from-suggestion").description("Build the composition from an existing clip suggestion").argument("<projectId>","Project ID").requiredOption("-s, --suggestion <suggestionId>","Clip suggestion publicId (sug_\u2026)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("--format <json>",`Optional format override, e.g. '{"aspectRatio":"9:16","fps":30,"resolution":"fhd"}'`).option("--if-match <etag>","Use this ETag (skip the GET round-trip)").option("--force","Use If-Match: * (unconditional overwrite)").action((s,a)=>z(s,{target:a.target,suggestionId:a.suggestion,format:a.format,ifMatch:a.ifMatch,force:a.force,json:e().json})),i.command("watch").description("Reattach to an existing render and stream progress to terminal state").argument("<renderId>","Render ID (from `cueframe render` output)").requiredOption("-p, --project <projectId>","Project the render belongs to").option("-t, --target <target>","Target environment: prod (default), dev, local").option("--no-download","Print the MP4 URL but skip downloading the file").option("-o, --out <path>","Output file path when downloading (default: ./reel-<id>.mp4)").action((s,a,h)=>{let E=h?.parent?.opts?.()?.target??a.target;return _e(s,{target:E,project:a.project,out:a.out,download:a.download!==!1,json:e().json})});let d=t.command("export").description("Hand the timeline off to a real NLE (Final Cut Pro / Premiere Pro)");d.command("fcpxml").description("Build a Final Cut Pro export zip and (optionally) wait to download it").argument("<projectId>","Project ID").requiredOption("-s, --suggestion <suggestionId>","Clip suggestion publicId (sug_\u2026)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-o, --out <path>","Output zip path (default: ./export-<id>.zip)").option("--wait","Block until the export finishes, then download the artifact").action((s,a)=>Y("fcpxml",{target:a.target,projectId:s,suggestionId:a.suggestion,out:a.out,wait:a.wait,json:e().json})),d.command("premiere").description("Build a Premiere Pro export zip and (optionally) wait to download it").argument("<projectId>","Project ID").requiredOption("-s, --suggestion <suggestionId>","Clip suggestion publicId (sug_\u2026)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-o, --out <path>","Output zip path (default: ./export-<id>.zip)").option("--wait","Block until the export finishes, then download the artifact").action((s,a)=>Y("premiere",{target:a.target,projectId:s,suggestionId:a.suggestion,out:a.out,wait:a.wait,json:e().json}));let f=t.command("sessions").description("Multi-file ingest sessions (camera A/B/screen \u2192 muxed composition)");f.command("list").description("List sessions in this org").option("-t, --target <target>","Target environment: prod (default), dev, local").action(s=>Ne({target:s.target,json:e().json})),f.command("get").description("Get a single session by ID").argument("<sessionId>","Session ID").option("-t, --target <target>","Target environment: prod (default), dev, local").action((s,a)=>Le(s,{target:a.target,json:e().json})),f.command("create").description("Create a session from one or more media files").requiredOption("-n, --name <name>","Session name").requiredOption("-f, --file <entry...>","Each entry is <mediaId>:<role> (repeatable)").option("-t, --target <target>","Target environment: prod (default), dev, local").action(s=>qe({target:s.target,name:s.name,files:s.file,json:e().json})),f.command("delete").description("Delete a session (server checks for compositions referencing it)").argument("<sessionId>","Session ID").option("-t, --target <target>","Target environment: prod (default), dev, local").action((s,a)=>Je(s,{target:a.target,json:e().json})),t.command("describe").description("Print the CLI command tree as structured JSON (agent introspection)").action(()=>Be(t,{json:e().json})),t.parse()}Ke();
|
|
75
|
+
Re-fetch via GET /v1/projects/${e.projectId}/exports/${o}.`),m(l.ApiServerError));let s=e.out??`./export-${o.slice(0,8)}.zip`,a=await jo(n.outputUrl,s,e.json);p(e,"export_complete",{exportId:o,projectId:e.projectId,format:t,outputUrl:n.outputUrl,outputExpiresAt:n.outputExpiresAt,outputSizeBytes:n.outputSizeBytes,trimCacheHit:n.trimCacheHit,outputPath:a})}async function wo(t,e,r){let o=Date.now()+(r.timeoutMs??Tt),n=r.pollIntervalMs??ho,i=Date.now(),s=C("Exporting\u2026",r.json);try{for(;Date.now()<o;){let a;try{a=await Y.getExport(t,e)}catch(c){if(c instanceof I&&c.status>=500){await Ot(n);continue}throw s.fail(`Failed to poll: ${b(c)}`),c}let f=Math.round((Date.now()-i)/1e3),u=Math.round((a.progress??0)*100);if(s.text=`Exporting\u2026 ${a.status}${u>0?` \xB7 ${u}%`:""} (${f}s)`,p(r,"export_progress",{exportId:e,status:a.status,progress:a.progress,elapsedSec:f}),a.status==="complete")return s.succeed(`Export complete (${f}s)`),{kind:"complete",outputUrl:a.outputUrl??null,outputExpiresAt:a.outputExpiresAt??null,outputSizeBytes:a.outputSizeBytes??null,trimCacheHit:a.trimCacheHit??null};if(a.status==="error"||a.status==="cancelled"){let c=a.error?.message??`Export ${a.status}`;return s.fail(c),{kind:"failed",message:c,error:a.error?{phase:a.error.phase,retryable:!!a.error.retryable}:null}}await Ot(n)}return s.fail(`Timed out waiting for export (${Math.round((Date.now()-i)/1e3)}s)`),{kind:"failed",message:`Timed out after ${Math.round((r.timeoutMs??Tt)/1e3)}s`,error:null}}catch(a){g(r,"export_progress",a,h(a)),m(h(a))}}async function jo(t,e,r){let o=yo(e),n=C(`Downloading \u2192 ${o}\u2026`,r);try{let i=await fetch(t);if(!i.ok)throw new Error(`HTTP ${i.status}`);let s=Buffer.from(await i.arrayBuffer());return go(o,s),n.succeed(`Saved: ${o} (${(s.length/1024/1024).toFixed(1)} MB)`),o}catch(i){n.fail(`Download failed: ${b(i)}`),r||console.error(`
|
|
76
|
+
Export URL (7-day TTL): ${t}`),m(l.NetworkError)}}function Ot(t){return new Promise(e=>setTimeout(e,t))}async function Pt(t){w(t.target),p(t,"sessions_list_prepare",{});try{let e=await G.listSessions();p(t,"sessions_list_complete",{count:e.data.length}),E(t,e,()=>{if(e.data.length===0){console.log("No sessions yet. Create one with `cueframe sessions create --name <n> --files <mediaId:role> \u2026`");return}for(let r of e.data){let o=r.muxStatus??"pending";console.log(`${r.id} ${r.fileCount} files mux:${o} ${r.name}`)}e.pagination.hasMore&&console.log(`# more \u2014 pass --after ${e.pagination.nextCursor} (raw via \`cueframe api\`)`)})}catch(e){g(t,"sessions_list",e,h(e)),t.json||console.error(`\u2716 sessions list: ${b(e)}`),m(h(e))}}async function Ft(t,e){v(t,"sessionId",e),w(e.target),p(e,"sessions_get_prepare",{sessionId:t});try{let r=await G.getSession(t);p(e,"sessions_get_complete",{sessionId:t}),E(e,r,()=>{console.log(JSON.stringify(r,null,2))})}catch(r){g(e,"sessions_get",r,h(r)),e.json||console.error(`\u2716 sessions get: ${b(r)}`),m(h(r))}}async function At(t){t.name||(console.error("--name is required"),m(l.UserError)),(!t.files||t.files.length===0)&&(console.error("At least one --file <mediaId:role> is required"),m(l.UserError));let e=t.files.map((r,o)=>{let n=r.indexOf(":");return n===-1&&(console.error(`--file[${o}] must be <mediaId>:<role>. Got: ${r}`),m(l.UserError)),{mediaId:r.slice(0,n).trim(),role:r.slice(n+1).trim()}});w(t.target),p(t,"sessions_create_prepare",{name:t.name,fileCount:e.length});try{let r=await G.createSession({name:t.name,files:e});p(t,"sessions_create_complete",{sessionId:r.id}),E(t,r,()=>{console.log(`Session created: ${r.id}`),console.log(JSON.stringify(r,null,2))})}catch(r){g(t,"sessions_create",r,h(r)),t.json||console.error(`\u2716 sessions create: ${b(r)}`),m(h(r))}}async function Ut(t,e){v(t,"sessionId",e),w(e.target),p(e,"sessions_delete_prepare",{sessionId:t});try{await G.deleteSession(t),p(e,"sessions_delete_complete",{sessionId:t}),E(e,{ok:!0,sessionId:t},()=>{console.log(`Session ${t} deleted.`)})}catch(r){g(e,"sessions_delete",r,h(r)),e.json||console.error(`\u2716 sessions delete: ${b(r)}`),m(h(r))}}function Mt(t){let e=t.commands.map(Mt);return{name:t.name(),description:t.description(),arguments:t.registeredArguments.map(r=>({name:r.name(),required:r.required,description:r.description})),options:t.options.map(r=>({flags:r.flags,description:r.description,...r.defaultValue!==void 0?{default:r.defaultValue}:{}})),...e.length>0?{subcommands:e}:{}}}function Dt(t,e){let r=t.commands.map(Mt);if(e.json){p(e,"describe_complete",{name:t.name(),version:t.version()??null,commands:r});return}console.log(`${t.name()} ${t.version()??""}`.trim());for(let o of r){console.log(`
|
|
77
|
+
${o.name} \u2014 ${o.description}`);for(let n of o.subcommands??[])console.log(` ${o.name} ${n.name} \u2014 ${n.description}`)}}function Nt(t){if(!t)return;let e=parseInt(t,10);if(e===24||e===30||e===60)return e;console.error(`Invalid --fps ${t}. Allowed: 24, 30, 60.`),m(l.UserError)}function Lt(){let t=new bo;t.name("cueframe").description("AI-assisted video editing and rendering").version("0.1.2").option("--json","Emit a structured JSON document on stdout instead of human-formatted output. Spinners are silenced.").option("--dry-run","Validate inputs and report what would happen \u2014 no API calls, no mutations. Applies to upload, analyze, project create, render.");let e=()=>t.opts();t.command("install").description("Install the cueframe-cli agent skill into ~/.claude/skills (Claude Code / Cursor / Codex).").action(()=>De({json:e().json})),t.command("init").description("Scaffold a CueFrame component workspace (cueframe.json + components dir) in the current repo, so `eject` / `new component` have a home and the render harness can discover them.").action(()=>We({json:e().json,dryRun:e().dryRun})),t.command("eject").description("Fork a built-in CueFrame primitive into cueframe/components/<id> as owned, editable source (renders on CueFrame).").argument("<primitive>","Primitive id (e.g. cinematic-title, heroText)").option("-f, --force","Overwrite an existing ejected component dir").action((c,d)=>He(c,{json:e().json,dryRun:e().dryRun,force:d.force})),t.command("new").description("Scaffold new owned source in the component workspace").command("component").description("Scaffold a blank custom component in cueframe/components/<name>").argument("<name>","Component id (e.g. my-badge)").option("-f, --force","Overwrite an existing non-empty component dir").action((c,d)=>Ye(c,{json:e().json,dryRun:e().dryRun,force:d.force})),t.command("push").description("Publish a locally-authored component (cueframe/components/<id>/index.tsx) to CueFrame so it renders server-side. Upserts by id.").argument("[id]","Component id to push (omit and use `cueframe sync` to push all)").option("-t, --target <target>","Target environment: prod (default), dev, local").action((c,d)=>et(c,{target:d.target,json:e().json})),t.command("sync").description("Publish EVERY component in the workspace (cueframe/components/*) to CueFrame. Upserts each by id.").option("-t, --target <target>","Target environment: prod (default), dev, local").action(c=>tt({target:c.target,json:e().json}));let o=t.command("keys").description("Manage API keys. `keys create` mints a scoped key \u2014 run `cueframe login` first (keys cannot mint keys).");o.command("create").description("Mint a scoped API key (cf_*). Requires a logged-in session.").requiredOption("-n, --name <name>","Key name, e.g. vaaya-staging").option("-s, --scope <scope>","Comma-separated resource:actions, e.g. media:rw,projects:rw,renders:rw (resources: media,sessions,projects,renders,webhooks; actions r/w)").option("--preset <preset>","Scope preset instead of --scope: readonly (read all) | editall (read+write all)").option("--expires <days>","Expire after N days (default: never)").action(c=>ot({...c,json:e().json})),o.command("list").description("List your API keys.").action(()=>nt({json:e().json})),o.command("revoke").description("Revoke an API key by id.").argument("<keyId>","Key id (from `cueframe keys list`)").action(c=>it(c,{json:e().json})),t.command("login").description("Browser-mediated sign-in (OAuth device flow). Recommended for laptops.").option("-t, --target <target>","Target environment: prod (default), dev, local").option("--url <url>","Override the API base URL (advanced)").action(c=>St({...c,json:e().json})),t.command("auth").description("Save a static API key (cf_live_\u2026 / cf_test_\u2026). Use for CI / headless environments.").argument("<api-key>","Your CueFrame API key").option("-t, --target <target>","Target environment: prod (default), dev, local").option("--url <url>","Override the API base URL (advanced)").action((c,d)=>{ee(c,d.target,d.url),e().json?console.log(JSON.stringify({ok:!0,target:d.target},null,2)):console.log(`API key saved for target "${d.target}"`)}),t.command("list").description("List projects").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-n, --limit <n>","Max projects to show","10").action(c=>st({target:c.target,limit:parseInt(c.limit),json:e().json}));let n=t.command("project").description("Project commands");n.command("create").description("Create a new project. With --from-suggestion, atomically build the composition from a clip suggestion in one round-trip \u2014 the project is then immediately renderable via `cueframe render <projectId>`.").requiredOption("-n, --name <name>","Project name").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-a, --aspect <ratio>","Aspect ratio: 9:16, 1:1, 16:9","9:16").option("--fps <n>","Frame rate","30").option("--resolution <r>","Resolution: hd, fhd, 2k, 4k","fhd").option("--from-suggestion <sug>","Clip suggestion publicId (sug_\u2026) \u2014 atomically builds the composition after project creation so render needs no further args").option("--cheap","With --from-suggestion: use the deterministic translator (no LLM, ~100ms) instead of the AI authoring pass. Useful for tests / programmatic flows.").action(c=>wt({name:c.name,target:c.target,aspect:c.aspect,fps:Nt(c.fps),resolution:c.resolution,fromSuggestion:c.fromSuggestion,cheap:c.cheap,json:e().json,dryRun:e().dryRun})),n.command("delete").description("Permanently delete a project (irreversible cleanup \u2014 removes the composition and render history).").argument("<projectId>","Project ID (from `cueframe list` or `project create`)").option("-t, --target <target>","Target environment: prod (default), dev, local").action((c,d)=>jt(c,{target:d.target,json:e().json})),t.command("media").description("Media library cleanup commands").command("delete").description("Permanently delete a media item (irreversible cleanup \u2014 removes the source and its derived assets).").argument("<mediaId>","Media item ID (from `cueframe clips`)").option("-t, --target <target>","Target environment: prod (default), dev, local").action((c,d)=>bt(c,{target:d.target,json:e().json})),t.command("upload").description("Upload a video file and process it").argument("<file>","Path to video file").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-a, --analyze","Run clip analysis after processing completes").action((c,d)=>dt(c,{target:d.target,analyze:d.analyze,json:e().json,dryRun:e().dryRun})),t.command("analyze").description("Trigger AI clip suggestion analysis for a media item").argument("<mediaItemId>","Media item ID (from `cueframe clips`)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-w, --wait","Wait for analysis to complete and show results").action((c,d)=>Ae(c,{target:d.target,wait:d.wait,json:e().json,dryRun:e().dryRun})),t.command("clips").description("List AI-generated clip suggestions for a media item").argument("[mediaItemId]","Media item ID (omit to list your media library)").option("-t, --target <target>","Target environment: prod (default), dev, local").action((c,d)=>mt(c,{target:d.target,json:e().json}));let s=t.command("render").description("Render a project to MP4 (queues, watches via SSE, downloads). Renders whatever composition is saved on the project \u2014 build it first via `project create --from-suggestion` or `composition from-suggestion`.").allowExcessArguments(!1).argument("<projectId>","Project ID (from `cueframe list` or `project create`)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-a, --aspect <ratio>","Aspect ratio: 9:16, 1:1, 16:9 (defaults to project format)").option("--fps <n>","Frame rate","30").option("--resolution <r>","Resolution: hd, fhd, 2k, 4k","fhd").option("-o, --out <path>","Output file path (default: ./reel-<id>.mp4)").option("--idempotency-key <key>",'Override the auto-derived Idempotency-Key. Default: sha256("render:<projectId>:<currentCompositionETag>"). Pass a fresh UUID to force a new render row.').option("--priority <p>","Routing intent: interactive (low-latency, access-gated), standard (default), background (lowest queue weight).").option("--intent <i>","Encode-quality hint: preview (fast / low-bitrate, for editor scrubbing) or final (default, full Format).").action((c,d)=>vt(c,{target:d.target,aspect:d.aspect,fps:Nt(d.fps),resolution:d.resolution,out:d.out,json:e().json,dryRun:e().dryRun,...d.idempotencyKey?{idempotencyKey:d.idempotencyKey}:{},...d.priority?{priority:d.priority}:{},...d.intent?{intent:d.intent}:{}}));t.command("api").description("Raw API call against the v1 surface (escape hatch \u2014 like `gh api`).").argument("<method>","HTTP method: GET, POST, PUT, PATCH, DELETE").argument("<path>","API path starting with /v1, e.g. /v1/projects").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-b, --body <body>","Request body (inline JSON, @file.json to read from disk, @- for stdin)").option("-H, --header <header...>",'Additional header (repeatable), e.g. -H "If-Match: \\"abc\\""').option("-i, --include","Include response status line + headers in stdout (curl-style). Lets agents read ETag etc.").action((c,d,y)=>_t(c,d,{target:y.target,body:y.body,header:y.header,include:y.include,json:e().json})),t.command("schema").description("Print the live composition wire schema from the server (/v1/openapi.json) \u2014 discover the contract instead of guessing.").argument("[resource]","Schema to print (currently: composition)","composition").option("-t, --target <target>","Target environment: prod (default), dev, local").action((c,d)=>Rt(c,{target:d.target,json:e().json}));let a=t.command("composition").description("Read/write a project's composition. ETag is threaded automatically.");a.command("validate").description("Validate a composition body against the live schema WITHOUT saving or rendering. Returns per-field errors \u2014 author safely before paying for a render.").requiredOption("-b, --body <body>","Composition JSON (inline, @file.json, or @- for stdin)").option("-t, --target <target>","Target environment: prod (default), dev, local").action(c=>pt({target:c.target,body:c.body,json:e().json})),a.command("get").description("Fetch the project's composition and ETag").argument("<projectId>","Project ID").option("-t, --target <target>","Target environment: prod (default), dev, local").action((c,d)=>lt(c,{target:d.target,json:e().json})),a.command("put").description("Replace the composition. GETs ETag first for safe concurrent writes.").argument("<projectId>","Project ID").option("-t, --target <target>","Target environment: prod (default), dev, local").requiredOption("-b, --body <body>","Composition JSON (inline, @file.json, or @- for stdin)").option("--if-match <etag>","Use this ETag (skip the GET round-trip)").option("--force","Use If-Match: * (unconditional overwrite \u2014 may clobber concurrent edits)").action((c,d)=>ut(c,{target:d.target,body:d.body,ifMatch:d.ifMatch,force:d.force,json:e().json})),a.command("from-suggestion").description("Build the composition from an existing clip suggestion").argument("<projectId>","Project ID").requiredOption("-s, --suggestion <suggestionId>","Clip suggestion publicId (sug_\u2026)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("--format <json>",`Optional format override, e.g. '{"aspectRatio":"9:16","fps":30,"resolution":"fhd"}'`).option("--if-match <etag>","Use this ETag (skip the GET round-trip)").option("--force","Use If-Match: * (unconditional overwrite)").action((c,d)=>de(c,{target:d.target,suggestionId:d.suggestion,format:d.format,ifMatch:d.ifMatch,force:d.force,json:e().json})),s.command("watch").description("Reattach to an existing render and stream progress to terminal state").argument("<renderId>","Render ID (from `cueframe render` output)").requiredOption("-p, --project <projectId>","Project the render belongs to").option("-t, --target <target>","Target environment: prod (default), dev, local").option("--no-download","Print the MP4 URL but skip downloading the file").option("-o, --out <path>","Output file path when downloading (default: ./reel-<id>.mp4)").action((c,d,y)=>{let x=y?.parent?.opts?.()?.target??d.target;return xt(c,{target:x,project:d.project,out:d.out,download:d.download!==!1,json:e().json})});let f=t.command("export").description("Hand the timeline off to a real NLE (Final Cut Pro / Premiere Pro)");f.command("fcpxml").description("Build a Final Cut Pro export zip and (optionally) wait to download it").argument("<projectId>","Project ID").requiredOption("-s, --suggestion <suggestionId>","Clip suggestion publicId (sug_\u2026)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-o, --out <path>","Output zip path (default: ./export-<id>.zip)").option("--wait","Block until the export finishes, then download the artifact").action((c,d)=>$e("fcpxml",{target:d.target,projectId:c,suggestionId:d.suggestion,out:d.out,wait:d.wait,json:e().json})),f.command("premiere").description("Build a Premiere Pro export zip and (optionally) wait to download it").argument("<projectId>","Project ID").requiredOption("-s, --suggestion <suggestionId>","Clip suggestion publicId (sug_\u2026)").option("-t, --target <target>","Target environment: prod (default), dev, local").option("-o, --out <path>","Output zip path (default: ./export-<id>.zip)").option("--wait","Block until the export finishes, then download the artifact").action((c,d)=>$e("premiere",{target:d.target,projectId:c,suggestionId:d.suggestion,out:d.out,wait:d.wait,json:e().json}));let u=t.command("sessions").description("Multi-file ingest sessions (camera A/B/screen \u2192 muxed composition)");u.command("list").description("List sessions in this org").option("-t, --target <target>","Target environment: prod (default), dev, local").action(c=>Pt({target:c.target,json:e().json})),u.command("get").description("Get a single session by ID").argument("<sessionId>","Session ID").option("-t, --target <target>","Target environment: prod (default), dev, local").action((c,d)=>Ft(c,{target:d.target,json:e().json})),u.command("create").description("Create a session from one or more media files").requiredOption("-n, --name <name>","Session name").requiredOption("-f, --file <entry...>","Each entry is <mediaId>:<role> (repeatable)").option("-t, --target <target>","Target environment: prod (default), dev, local").action(c=>At({target:c.target,name:c.name,files:c.file,json:e().json})),u.command("delete").description("Delete a session (server checks for compositions referencing it)").argument("<sessionId>","Session ID").option("-t, --target <target>","Target environment: prod (default), dev, local").action((c,d)=>Ut(c,{target:d.target,json:e().json})),t.command("describe").description("Print the CLI command tree as structured JSON (agent introspection)").action(()=>Dt(t,{json:e().json})),t.parse()}Lt();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cueframe",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
@@ -36,19 +36,21 @@
|
|
|
36
36
|
"commander": "^13.1.0",
|
|
37
37
|
"open": "^10.1.0",
|
|
38
38
|
"ora": "^8.2.0",
|
|
39
|
-
"pino": "^10.3.1"
|
|
40
|
-
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@types/node": "^22.15.0",
|
|
43
|
-
"tsup": "^8.5.1",
|
|
44
|
-
"typescript": "^5.8.3",
|
|
45
|
-
"vitest": "^4.1.0",
|
|
46
|
-
"@cueframe/cloud-import": "0.1.0",
|
|
47
|
-
"@cueframe/sdk": "0.0.0"
|
|
39
|
+
"pino": "^10.3.1",
|
|
40
|
+
"typescript": "^5.8.3"
|
|
48
41
|
},
|
|
49
42
|
"scripts": {
|
|
50
43
|
"build": "tsup",
|
|
51
44
|
"check": "tsc --noEmit",
|
|
52
|
-
"test": "vitest run"
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"prepublishOnly": "tsup"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@cueframe/cloud-import": "workspace:*",
|
|
50
|
+
"@cueframe/primitive-registry": "workspace:*",
|
|
51
|
+
"@cueframe/sdk": "workspace:*",
|
|
52
|
+
"@types/node": "^22.15.0",
|
|
53
|
+
"tsup": "^8.5.1",
|
|
54
|
+
"vitest": "^4.1.0"
|
|
53
55
|
}
|
|
54
|
-
}
|
|
56
|
+
}
|