@conceptcraft/mindframes 0.3.16 → 0.3.18
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 +207 -48
- package/dist/index.js +580 -1533
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
3
|
-
var f={id:"mindframes",name:"mindframes",displayName:"Mindframes",description:"CLI tool for Mindframes presentation generation",commands:["mf","mindframes"],apiUrl:"https://www.mindframes.app",docsUrl:"https://docs.mindframes.ai",packageName:"@conceptcraft/mindframes",configDir:".mindframes",apiKeyEnvVar:"MINDFRAMES_API_KEY",apiUrlEnvVar:"MINDFRAMES_API_URL",codexSelfConnectorAppId:"asdk_app_6a2b9629c1608191970dcb3ab374ac55"};import me from"chalk";import{Command as Vp}from"commander";import{Command as ic}from"commander";import{createHash as Js,randomUUID as Ge}from"crypto";import{readFileSync as zs,statSync as Xs}from"fs";import{basename as Yr}from"path";import{spawnSync as ks}from"child_process";import{existsSync as Ts,mkdtempSync as Es,rmSync as As,writeFileSync as $s}from"fs";import{tmpdir as Os}from"os";import{join as Nr}from"path";import In from"ffmpeg-static";var Ps=/Duration:\s*(\d+):(\d+):(\d+(?:\.\d+)?)/;function Ns(e){let t=Ps.exec(e);if(!t)return;let n=Number(t[1])*3600+Number(t[2])*60+Number(t[3]);return Number.isFinite(n)&&n>0?n:void 0}function _r(e,t="audio"){if(e.length===0)return;let n=In&&Ts(In)?In:"ffmpeg",r=/^[a-z0-9]+$/i.test(t)?t:"audio",o=Es(Nr(Os(),"mf-tts-duration-")),i=Nr(o,`speech.${r}`);try{$s(i,e);let a=ks(n,["-hide_banner","-i",i],{encoding:"utf8"});return a.error?void 0:Ns(a.stderr??"")}finally{As(o,{force:!0,recursive:!0})}}import Nt from"chalk";import _s from"conf";function Lr(e={}){let t=(s,l)=>{let p=e[s];return p&&p.trim().length>0?p.trim():l},n=t("NEXT_PUBLIC_PRODUCT_SHORT_NAME","ConceptCraft"),r=t("NEXT_PUBLIC_CLI_COMMAND","ccr"),o=t("NEXT_PUBLIC_CLI_COMMAND_FULL","conceptcraft"),i=t("NEXT_PUBLIC_CLI_CONFIG_DIR",`.${o}`),a=n.toUpperCase().replace(/[^A-Z0-9]/g,"_");return{id:o,name:i.replace(/^\./,""),displayName:n,description:`CLI tool for ${n} presentation generation`,commands:[r,o],apiUrl:t("NEXT_PUBLIC_API_URL","http://localhost:3000"),docsUrl:t("NEXT_PUBLIC_DOCS_URL",`https://docs.${n.toLowerCase()}.ai`),packageName:t("NEXT_PUBLIC_CLI_PACKAGE_NAME","@conceptcraft/cli"),configDir:i,apiKeyEnvVar:`${a}_API_KEY`,apiUrlEnvVar:`${a}_API_URL`,codexSelfConnectorAppId:e.NEXT_PUBLIC_CODEX_SELF_CONNECTOR_APP_ID?.trim()||void 0}}var b=typeof f<"u"&&f?f:Lr(process.env);var Dr=b.apiUrl,Ls={apiKey:{type:"string"},apiUrl:{type:"string",default:Dr},defaultTeamId:{type:"string"},accessToken:{type:"string"},refreshToken:{type:"string"},tokenExpiresAt:{type:"number"},clientId:{type:"string"},clientSecret:{type:"string"}},_=new _s({projectName:b.name,schema:Ls});function Ur(){return{apiKey:xe(),apiUrl:V(),defaultTeamId:_.get("defaultTeamId"),accessToken:_.get("accessToken"),refreshToken:_.get("refreshToken"),tokenExpiresAt:_.get("tokenExpiresAt"),clientId:_.get("clientId"),clientSecret:_.get("clientSecret")}}function xe(){let e=process.env[b.apiKeyEnvVar]??process.env.CC_SLIDES_API_KEY;return e||_.get("apiKey")}function Rn(e){_.set("apiKey",e)}function V(){let e=process.env[b.apiUrlEnvVar]??process.env.CC_SLIDES_API_URL;return e||(_.get("apiUrl")??Dr)}function Cn(e){_.set("apiUrl",e)}function At(){return _.get("defaultTeamId")}function Me(e){_.set("defaultTeamId",e)}function $t(){_.clear()}function vt(){return _.path}function Be(){return!!xe()}function Fr(){return _.get("accessToken")}function qr(){return _.get("refreshToken")}function Ot(e,t,n){_.set("accessToken",e),_.set("refreshToken",t),_.set("tokenExpiresAt",Date.now()+(n-60)*1e3)}function Pt(){_.delete("accessToken"),_.delete("refreshToken"),_.delete("tokenExpiresAt")}function Vr(){let e=_.get("tokenExpiresAt");return e?Date.now()>=e:!0}function Ne(){return!!_.get("accessToken")&&!!_.get("refreshToken")}function jr(){return _.get("clientId")}function Mr(){return _.get("clientSecret")}function Br(e,t){_.set("clientId",e),_.set("clientSecret",t)}var d={SUCCESS:0,GENERAL_ERROR:1,AUTH_ERROR:2,NOT_FOUND:3,RATE_LIMIT:4,NETWORK_ERROR:5,INVALID_INPUT:6};async function Ds(){let e=qr(),t=jr(),n=V();if(!e||!t)return null;try{let r=await fetch(`${n}/.well-known/oauth-authorization-server`);if(!r.ok)return null;let o=await r.json(),i=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:t}),a=Mr();a&&i.set("client_secret",a);let s=await fetch(o.token_endpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()});if(!s.ok)return Pt(),null;let l=await s.json();return Ot(l.access_token,l.refresh_token,l.expires_in),l.access_token}catch{return null}}async function Gr(){return Ne()?Vr()?Ds():Fr()||null:null}function ce(){return Ne()||Be()}async function N(){if(!ce()){let e=b.commands[0],t=b.apiKeyEnvVar;console.error(Nt.red("\u2717 Not authenticated.")),console.error(),console.error(Nt.bold("To authenticate, run:")),console.error(Nt.cyan(` ${e} login`)),console.error(),console.error(Nt.gray(`Or set ${t} environment variable.`)),process.exit(d.AUTH_ERROR)}}function Us(e){return e.length<=14?"****":`${e.slice(0,10)}...${e.slice(-4)}`}function Wr(){let e=xe();if(e)return Us(e)}function kn(e){return["cc_slides_","mcp_","cc_sk_","sk_"].some(n=>e.startsWith(n))}import{mkdirSync as Fs,readFileSync as qs,writeFileSync as Vs}from"fs";import{homedir as js}from"os";import{join as Kr}from"path";import Ms from"ajv/dist/2020.js";var Tn=new Map;function Jr(){return Kr(js(),".conceptcraft","schemas")}function zr(e){return Kr(Jr(),`${e}.json`)}function Bs(e){try{let t=JSON.parse(qs(zr(e),"utf8"));return typeof t?.catalogVersion=="number"&&t.schema?t:null}catch{return null}}function Gs(e,t){try{Fs(Jr(),{recursive:!0}),Vs(zr(e),JSON.stringify(t))}catch{}}async function Ws(e){let t=Tn.get(e);if(t!==void 0)return t;try{let n=await Xr(e),r={catalogVersion:n.catalogVersion,schema:n.schema};return Gs(e,r),Tn.set(e,r),r}catch{let n=Bs(e);return Tn.set(e,n),n}}function Ks(e){let t=e.instancePath.replace(/^\//,"").replace(/\//g,"."),n=t.length>0?t:"(root)",r=e.keyword==="additionalProperties"?`: unknown field "${e.params.additionalProperty}"`:`: ${e.message??"invalid value"}`;return`${n}${r}`}async function _t(e,t){let n=await Ws(e);if(!n)return;let r;try{r=new Ms({allErrors:!0,strict:!1}).compile(n.schema)}catch{return}if(r(t))return;let o=(r.errors??[]).slice(0,10).map(Ks);throw new Error(`Request does not match the ${e} contract \u2014 ${o.join("; ")}`)}async function de(){let e=await Gr();if(e)return{Authorization:`Bearer ${e}`};let t=xe();return t?{"x-api-key":t}:{}}var P=class extends Error{constructor(n,r,o=1,i="operation_failed",a=!1){super(n);this.statusCode=r;this.exitCode=o;this.errorCode=i;this.retryable=a;this.name="ApiError"}statusCode;exitCode;errorCode;retryable},En="Idempotency-Key";function Hs(e){return e&&e!=="GET"?{[En]:Ge()}:{}}async function M(e,t={}){let n=V();if(!ce())throw new P(`Not authenticated. Run '${b.commands[0]} login' or set ${b.apiKeyEnvVar} environment variable.`,401,2,"authentication_required");let r=await de(),o=`${n}${e}`,i={...r,...Hs(t.method),...t.headers};t.body&&!t.stream&&(i["Content-Type"]="application/json");let a={method:t.method??"GET",headers:i};t.body&&(a.body=JSON.stringify(t.body));let s;try{s=await fetch(o,a)}catch(p){throw new P(`Network error: ${p instanceof Error?p.message:"Unknown error"}`,0,5,"service_unavailable",!0)}if(!s.ok){let p=await s.text().catch(()=>""),u=Qs(p,s.status),w=1;switch(s.status){case 401:w=2;break;case 403:w=2;break;case 404:w=3;break;case 429:w=4;break;case 500:w=1;break}let R=`[${u.code}] ${u.message}`;throw new P(R,s.status,w,u.code,u.retryable)}return t.stream?s:s.headers.get("content-type")?.includes("application/json")?s.json():s.text()}function Ys(e){switch(e){case 400:return"invalid_input";case 401:return"authentication_required";case 402:return"insufficient_credits";case 403:return"access_denied";case 404:return"not_found";case 429:return"rate_limited";case 503:return"service_unavailable";default:return"operation_failed"}}function Qs(e,t){let n=Ys(t),r=e.trim()||`HTTP ${t}: The request could not be completed.`;try{let o=JSON.parse(e),i=typeof o.error=="object"&&o.error!==null?o.error:void 0;if(o.ok===!1&&typeof i?.code=="string"&&typeof i.message=="string")return{code:i.code,message:i.message,retryable:i.retryable===!0};if(typeof o.error=="string")return{code:/^[a-z][a-z0-9_]+$/.test(o.error)?o.error:n,message:typeof o.message=="string"?o.message:o.error,retryable:t===429||t>=500}}catch{}return{code:n,message:r,retryable:t===429||t>=500}}async function Xr(e){return M(`/api/cli/tools/${encodeURIComponent(e)}/schema`)}async function Qr(){return M("/api/cli/capabilities")}async function A(e,t,n){let r=n?`?teamId=${encodeURIComponent(n)}`:"";return M(`/api/cli/tool-runs/${encodeURIComponent(e)}/${encodeURIComponent(t)}${r}`)}async function h(e,t,n){return M(`/api/cli/tools/${encodeURIComponent(e)}`,{body:t,headers:n?{[En]:n}:void 0,method:"POST"})}async function Zr(e,t,n,r=120){let o=0;for(let i=0;i<r;i++){let a;try{a=await A(e,t,n),o=0}catch(s){if(s instanceof P&&s.retryable&&o<2){o+=1,await new Promise(l=>setTimeout(l,2e3));continue}throw s instanceof P?new P(`${s.message} Request ID: ${t}.`,s.statusCode,s.exitCode,s.errorCode,s.retryable):new P(`${s instanceof Error?s.message:"Unknown error"} Request ID: ${t}.`,0,5,"poll_failed",!0)}if(a.done){if(a.error)throw new P(`[${a.error.code??"operation_failed"}] ${a.error.message} Request ID: ${t}.`,500,1,a.error.code??"operation_failed",a.error.retryable);return a}await new Promise(s=>setTimeout(s,(a.nextPollAfterSeconds??2)*1e3))}throw new P(`[operation_timed_out] The background operation timed out. Request ID: ${t}.`,408,1,"operation_timed_out",!0)}function Lt(e){return e.error||e.status==="failed"||e.status==="errored"||e.status==="cancelled"?"failed":e.done||e.status==="completed"?"completed":e.status==="in_progress"?"processing":"pending"}function Zs(e){try{let n=new URL(e).pathname.split("/").pop()?.split(".").pop()?.toLowerCase();return n&&/^[a-z0-9]+$/.test(n)?n:void 0}catch{return}}function ec(e){let t=e.toLowerCase().split(".").pop();return{pdf:"application/pdf",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",mp4:"video/mp4",mov:"video/quicktime",mpeg:"video/mpeg",mpg:"video/mpeg",webm:"video/webm",aac:"audio/aac",m4a:"audio/mp4",mp3:"audio/mpeg",oga:"audio/ogg",ogg:"audio/ogg",wav:"audio/wav",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",html:"text/html",css:"text/css",js:"application/javascript",ts:"text/x-typescript",py:"text/x-python"}[t||""]||"application/octet-stream"}async function he(e,t={}){let n=V();if(!ce())throw new P(`Not authenticated. Run '${b.commands[0]} login' or set ${b.apiKeyEnvVar} environment variable.`,401,2);let r=await de(),o=Xs(e),i=Yr(e),a=ec(e),s=zs(e),l=Js("sha256").update(s).digest("hex"),p=await fetch(`${n}/api/cli/files/upload`,{method:"POST",headers:{"Content-Type":"application/json",...r},body:JSON.stringify({fileMetadata:{lastModified:o.mtimeMs,name:i,type:a,size:o.size},...t.idempotencyKey?{uploadIdentity:{contentSha256:l,idempotencyKey:t.idempotencyKey}}:{}})});if(!p.ok){let S=await p.text();throw new P(S||"Failed to get upload URL",p.status,1)}let u=await p.json(),w=new FormData;for(let[S,v]of Object.entries(u.fields||{}))w.append(S,v);let R=new Blob([s],{type:a});w.append("file",R,i);let y=await fetch(u.url,{method:"POST",body:w});if(!y.ok)throw new P("Failed to upload file to storage",y.status,1);return{id:Ge(),name:i,url:u.fileUrl,size:o.size,type:a,selected:!0}}async function eo(e,t){let n=[];for(let r=0;r<e.length;r++){let o=e[r],i=Yr(o);t?.(r,e.length,i);let a=await he(o);n.push(a)}return t?.(e.length,e.length,""),n}async function to(e){let t={};for(let[r,o]of Object.entries(e))o!==void 0&&(t[r]=o);let n=t.designBrandReference??t.brandId??t.brandingId;return n!==void 0&&(t.designBrandReference=n,delete t.brandId,delete t.brandingId),h("createSlides",{...t,approvalReceipt:"cli:create"})}async function no(e,t=20){let n=new URLSearchParams;return n.set("limit",String(t)),e&&n.set("teamId",e),(await M(`/api/cli/presentations?${n}`)).presentations}async function Te(e){return M(`/api/cli/presentation/${encodeURIComponent(e)}`)}async function ro(e,t){return M(`/api/cli/presentation/${encodeURIComponent(e)}/speaker-notes`,{body:t,method:"POST"})}async function oo(e,t){return M(`/api/cli/presentation-video/${encodeURIComponent(e)}/narration`,{body:t,method:"POST"})}async function An(e){let t=V(),n=await fetch(`${t}/api/durable-runs/${encodeURIComponent(e)}/events`,{headers:await de()});if(!n.ok)throw new P(await n.text(),n.status);return n}async function io(e){await M(`/api/cli/presentation/${encodeURIComponent(e)}`,{method:"DELETE"})}async function ao(e,t={}){let n=V();if(!ce())throw new P(`Not authenticated. Run '${b.commands[0]} login' or set ${b.apiKeyEnvVar} environment variable.`,401,2);let r=await de(),o=await fetch(`${n}/api/presentations/export`,{method:"POST",headers:{"Content-Type":"application/json",...r},body:JSON.stringify({presentationId:e,options:{includeImages:t.includeImages??!0,includeBranding:t.includeBranding??!0,downloadExternalImages:t.includeExternal??!0}})});if(!o.ok)throw new P(await o.text(),o.status,o.status===404?3:1);return o.arrayBuffer()}async function so(e,t,n={}){let r=V();if(!ce())throw new P(`Not authenticated. Run '${b.commands[0]} login' or set ${b.apiKeyEnvVar} environment variable.`,401,2);let o=await de(),i=new FormData,a=new Blob([e],{type:"application/zip"});i.append("file",a,t),i.append("options",JSON.stringify({dryRun:n.dryRun??!1,remapIds:!0}));let s=await fetch(`${r}/api/presentations/import`,{method:"POST",headers:o,body:i}),l=await s.json();if(!s.ok)throw new P(l.errors?.[0]?.message??"Import failed",s.status,1);return l}async function $n(){return M("/api/cli/branding")}async function co(e,t=25){let n=new URLSearchParams;return n.set("limit",String(t)),e&&n.set("search",e),M(`/api/cli/templates?${n}`)}async function lo(e){return M(`/api/branding/${e}`)}async function uo(e,t,n){return h("extractBranding",{approvalReceipt:n,teamId:t,url:e})}async function Se(){return M("/api/cli/whoami")}async function mo(){return M("/api/cli/features")}async function po(e,t,n={}){let r=V();if(!ce())throw new P(`Not authenticated. Run '${b.commands[0]} login' or set ${b.apiKeyEnvVar} environment variable.`,401,2);let o=await de(),i=await fetch(`${r}/api/presentations/${e}/generate-blog`,{method:"POST",headers:{"Content-Type":"application/json",...o},body:JSON.stringify({documentCreatedAt:t,targetWordCount:n.targetWordCount??300,tone:n.tone??"professional",language:n.language??"en",targetAudience:n.targetAudience??"General Audience",blogFormat:n.blogFormat??"narrative",customInstructions:n.customInstructions??"",includeImages:n.includeImages??!0,includeTableOfContents:n.includeTableOfContents??!1})});if(!i.ok)throw new P(await i.text(),i.status,i.status===404?3:1);return i}async function tc(e){let t=new URLSearchParams;e&&t.set("teamId",e);let n=t.toString();return M(`/api/cli/limits${n?`?${n}`:""}`)}async function go(e,t,n){let r=await tc(n);if(!r.canGenerate[e]){let o=r.remaining[e]??0,i=r.limits[e]??0;throw i===0?new P(`The "${e}" mode is not available on your ${r.planName} plan. Available modes: ${r.availableModes.join(", ")||"none"}`,403,2):new P(`You have reached your ${e} generation limit for this billing cycle (${r.usage[e]??0}/${i} used). Try a different mode: ${r.availableModes.join(", ")||"none available"}`,403,4)}if(r.maxSlidesPerPresentation!==-1&&t>r.maxSlidesPerPresentation)throw new P(`Maximum ${r.maxSlidesPerPresentation} slides allowed on your ${r.planName} plan (requested: ${t}). Try a different mode: ${r.availableModes.join(", ")||"none available"}`,403,6);return r}function fo(e){return e.replace(/[\u2014\u2013]/g,"-").replace(/[\u201C\u201D]/g,'"').replace(/[\u2018\u2019]/g,"'").replace(/\u2026/g,"...").replace(/\u00A0/g," ").replace(/[\u2010-\u2015]/g,"-").replace(/[\u2032\u2033]/g,"'")}async function Dt(e){let t={...e,text:fo(e.text)};await _t("generateSpeech",t);let n=await h("generateSpeech",{approvalReceipt:"cli:tts-generate",...t}),r=await Zr("generateSpeech",n.runId),o=r.entity?.url;if(!o)throw new P(`Speech completed without an audio URL. Request ID: ${n.runId}.`,500);let i;try{i=await fetch(o)}catch(w){throw new P(`${w instanceof Error?w.message:"Failed to download speech audio"} Request ID: ${n.runId}.`,0,5,"download_failed",!0)}if(!i.ok)throw new P(`Failed to download speech audio. Request ID: ${n.runId}.`,i.status,5,"download_failed",i.status===429||i.status>=500);let a=r.output??{},s=e.options??{},l=fe(a.format)??Zs(o)??"mp3",p=Buffer.from(await i.arrayBuffer()),u=ho(a.durationSec,a.duration,a.duration_s)??_r(p,l);if(u===void 0)throw new P(`Speech completed without a measurable duration. Request ID: ${n.runId}.`,500,1,"invalid_audio_metadata");return{audioData:p,audioUrl:o,duration:u,cost:_e(a.costUsd)??_e(a.cost)??0,provider:fe(a.provider)??s.provider??"gemini",format:l,requestId:n.runId,voiceId:fe(a.voice_id)??fe(a.voiceId),voice:fe(a.voice),model:fe(a.model),speed:_e(a.speed)??s.speed,language:fe(a.lang)??fe(a.language)??s.language,langApplied:Hr(a.langApplied)??Hr(a.lang_applied),timestamps:nc(a.timestamps),wordTimings:rc(a.timestamps??a.timings)}}function ho(...e){for(let t of e){let n=typeof t=="number"?t:typeof t=="string"&&t.trim()?Number(t):Number.NaN;if(Number.isFinite(n)&&n>0)return n}}function nc(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=t.characters,r=t.characterStartTimesSeconds??t.character_start_times_seconds,o=t.characterEndTimesSeconds??t.character_end_times_seconds;if(!(!Array.isArray(n)||!Array.isArray(r)||!Array.isArray(o))&&!(n.length===0||n.length!==r.length||n.length!==o.length||!n.every(i=>typeof i=="string")||!r.every(i=>typeof i=="number"&&Number.isFinite(i))||!o.every(i=>typeof i=="number"&&Number.isFinite(i))))return{characters:n,characterStartTimesSeconds:r,characterEndTimesSeconds:o}}function rc(e){if(!Array.isArray(e))return;let t=e.flatMap(n=>{if(!n||typeof n!="object"||Array.isArray(n))return[];let r=n,o=fe(r.text)??fe(r.word),i=_e(r.start),a=_e(r.end);return o&&i!==void 0&&a!==void 0&&a>=i?[{text:o,start:i,end:a}]:[]});return t.length>0?t:void 0}function fe(e){return typeof e=="string"&&e.trim()?e.trim():void 0}function _e(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function Hr(e){return typeof e=="boolean"?e:void 0}async function yo(e){let t=V();if(!ce())throw new P(`Not authenticated. Run '${b.commands[0]} login' or set ${b.apiKeyEnvVar} environment variable.`,401,2);let n=await de(),r=Ge(),o={...e,texts:e.texts.map(s=>({...s,text:fo(s.text)}))},i;try{i=await fetch(`${t}/api/cli/tts/batch`,{method:"POST",headers:{"Content-Type":"application/json",[En]:r,...n},body:JSON.stringify(o)})}catch(s){throw new P(`Network error: ${s instanceof Error?s.message:"Unknown error"}`,0,5)}if(!i.ok){let s=await i.text().catch(()=>"Unknown error"),l;try{let p=JSON.parse(s);l=p.error||p.message||s}catch{l=s}throw new P(l,i.status,i.status===401?2:1)}let a=await i.json();return a.results=a.results.map(s=>({...s,audioData:Buffer.from(s.audioData,"base64")})),a}async function bo(e){let t=e?`/api/cli/tts?provider=${e}`:"/api/cli/tts";return M(t)}async function wo(e){let t={...e},n=typeof t.idempotency_key=="string"?t.idempotency_key:void 0;return delete t.idempotency_key,await _t("generateMusic",t),{requestId:(await h("generateMusic",{...t,approvalReceipt:"cli:music-generate"},n)).runId,status:"pending"}}async function On(e){let t=await A("generateMusic",e);return{requestId:e,status:Lt(t),audioUrl:t.entity?.url,duration:ho(t.output?.durationSec,t.output?.duration,t.output?.duration_s),cost:_e(t.output?.costUsd)??_e(t.output?.cost),error:t.error?.message}}async function vo(e){return{requestId:(await h("mixAudio",{...e,approvalReceipt:"cli:mix-create"})).runId,status:"pending"}}async function Pn(e){let t=await A("mixAudio",e);return{requestId:e,status:Lt(t),outputUrl:t.entity?.url,error:t.error?.message}}async function Ut(e){return M("/api/cli/images/search",{method:"POST",body:e})}async function xo(e){return M("/api/cli/images/generate",{method:"POST",body:e})}async function Nn(e){return M("/api/cli/videos/search",{method:"POST",body:e})}async function re(e,t=60,n=2e3){let r=0;for(let o=0;o<t;o++){let i;try{i=await e(),r=0}catch(a){if(a instanceof P&&a.retryable&&r<2){r+=1,await new Promise(s=>setTimeout(s,n));continue}throw a}if(i.status==="completed"||i.status==="failed")return i;await new Promise(a=>setTimeout(a,n))}throw new P("Operation timed out",408,1)}async function So(e,t,n){return h("getYoutubeTranscript",{...n?{language:n}:{},teamId:t,url:e})}async function Io(e,t){let n=await h("scrape",{teamId:t,url:e});return{data:{content:n.content,cost:n.totalCost??0,metadata:{description:"",format:"markdown",tokenUsage:Math.ceil(n.content.length/4)},title:n.title??"",url:n.url},success:!0}}async function Ro(e){let t={...e},n=typeof t.idempotency_key=="string"?t.idempotency_key:void 0;delete t.idempotency_key,await _t("generateSfx",t);let r=await h("generateSfx",{...t,approvalReceipt:"cli:sfx-generate"},n);return{id:r.runId,requestId:r.runId,status:"pending"}}async function _n(e){let t=await A("generateSfx",e);return{id:t.entity?.id??e,requestId:e,status:Lt(t),audioUrl:t.entity?.url,error:t.error?.message}}async function Co(e){return h("listPresenterOptions",{...e?{voiceLanguage:e}:{}})}async function ko(e){return{requestId:(await h("generateAvatarVideo",{...e,approvalReceipt:"cli:avatar-generate"})).runId,status:"pending"}}async function Ln(e){let t=await A("generateAvatarVideo",e);return{durationSec:typeof t.output?.durationSec=="number"?t.output.durationSec:null,error:t.error?.message,requestId:e,status:Lt(t),videoUrl:t.entity?.url}}async function To(e){return h("lipsyncVideo",{...e,approvalReceipt:"cli:video-lipsync"})}async function Eo(){return(await h("listTranslationLanguages",{})).languages}async function Ao(e){return h("translateVideo",{...e,approvalReceipt:"cli:video-translate"})}async function Dn(e){return h("generateVideoPlan",{...e,approvalReceipt:"cli:video-plan"})}async function $o(e,t,n){return h("createVideo",{...e,approvalReceipt:t},n)}async function Oo(e){let{idempotencyKey:t,...n}=e;return h("analyzeVideoReference",n,t)}async function Po(e,t){return Zr("analyzeVideoReference",e,t)}async function No(e){return M("/api/cli/video/plan",{body:e,method:"POST"})}async function _o(e){return h("listCastPresets",e)}async function Lo(e){return h("createCastPreset",e,Ge())}async function Do(e){return h("updateCastPreset",e,Ge())}async function Uo(e){return h("deleteCastPreset",{presetId:e},Ge())}import U from"chalk";import Un from"cli-table3";function I(e,t="human"){t!=="quiet"&&t!=="json"&&console.log(U.green("\u2713"),e)}function c(e,t="human"){if(t!=="quiet"){if(t==="json"){console.error(JSON.stringify({error:e}));return}console.error(U.red("\u2717"),e)}}function q(e,t="human"){t!=="quiet"&&t!=="json"&&console.warn(U.yellow("\u26A0"),e)}function m(e,t="human"){t!=="quiet"&&t!=="json"&&console.log(U.blue("\u2139"),e)}function We(e,t="en"){return e?`${V()}/${t}/view/presentations/${e}`:"N/A"}function Fo(e,t={}){let{showLinks:n=!1,language:r="en"}=t,o=(s,l)=>s.length>l?s.slice(0,l-1)+"\u2026":s,i=s=>{try{return new Date(s).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0}).replace(" at ",", ")}catch{return"-"}};if(n){let s=new Un({head:[U.cyan("Slug"),U.cyan("Title"),U.cyan("Slides"),U.cyan("Created"),U.cyan("URL")]});for(let l of e)s.push([o(l.slug||l.id.slice(0,8),30),o(l.title||"Untitled",22),String(l.numberOfSlides||"-"),i(l.createdAt),We(l.slug,r)]);return s.toString()}let a=new Un({head:[U.cyan("Slug"),U.cyan("Title"),U.cyan("Slides"),U.cyan("Mode"),U.cyan("Created")],colWidths:[32,28,8,11,18]});for(let s of e)a.push([s.slug||s.id.slice(0,8),s.title||"Untitled",String(s.numberOfSlides||"-"),s.mode||"-",i(s.createdAt)]);return a.toString()}function qo(e){return e.map(t=>t.slug||t.id).join(`
|
|
4
|
-
`)}function Vo(e){let t=[...e].sort((o,i)=>{if(o.isDefault&&!i.isDefault)return-1;if(!o.isDefault&&i.isDefault)return 1;let a=o.createdAt?new Date(o.createdAt).getTime():0;return(i.createdAt?new Date(i.createdAt).getTime():0)-a}),n=o=>{if(!o)return"-";try{return new URL(o).href}catch{return o.startsWith("http")?o:`https://${o}`}},r=new Un({head:[U.cyan("Name"),U.cyan("Source"),U.cyan("Color"),U.cyan("Logo"),U.cyan("Default"),U.cyan("ID")]});for(let o of t){let i=o.primaryColor?`${U.bgHex(o.primaryColor)(" ")} ${o.primaryColor}`:"-";r.push([o.name,n(o.sourceUrl),i,o.logoUrl?U.green("\u2713"):U.gray("\u2717"),o.isDefault?U.green("\u2605"):"",o.id])}return r.toString()}function Fn(e){try{return new Date(e).toLocaleString()}catch{return e}}function ye(e){console.log(),console.log(U.bold(e)),console.log(U.gray("\u2500".repeat(e.length)))}function T(e,t){console.log(` ${U.gray(e+":")} ${t??"-"}`)}function oc(e){return JSON.stringify(e,null,2)}function g(e){console.log(oc(e))}var jo=new ic("capabilities").description("List callable capabilities and their parity state").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{try{let t=await Qr();if(e.format==="json"){g(t);return}for(let n of t.capabilities){if(e.format==="quiet"){console.log(n.name);continue}console.log(`${n.name} ${n.state} ${n.milestone}`)}}catch(t){c(t instanceof Error?t.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}});import{mkdirSync as dc,writeFileSync as lc}from"fs";import{dirname as Wo,join as uc}from"path";import{Command as Ee}from"commander";import xt from"chalk";import{input as mc,password as pc,confirm as Ko,select as gc}from"@inquirer/prompts";import qt from"ora";import ac from"conf";var Ft=new ac({projectName:"conceptcraft",configName:"feature-cache"}),sc=3600*1e3,cc=1440*60*1e3;function Mo(e){let t=0;for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);t=(t<<5)-t+r,t=t&t}return t.toString(16)}function Bo(){if(!Be())return null;let e=Ft.get("featureFlags");if(!e)return null;let t=Mo(xe());if(e.apiKeyHash&&e.apiKeyHash!==t)return De(),null;let n=Date.now()-e.fetchedAt;return n>cc?null:(n>sc&&qn(),e.flags)}function qn(){Le().catch(()=>{})}async function Le(){let e=await mo(),t=xe();return Ft.set("featureFlags",{flags:e,fetchedAt:Date.now(),apiKeyHash:t?Mo(t):void 0}),e}function De(){Ft.delete("featureFlags")}function Go(){return Ft.path}var Jo=new Ee("config").description("Manage CLI configuration").addCommand(new Ee("init").description("Initialize configuration interactively").action(async()=>{console.log(),console.log(xt.bold(`${b.displayName} CLI Configuration`)),console.log(xt.gray("\u2500".repeat(35))),console.log();try{let e=await pc({message:"Enter your API key:",mask:"*",validate:r=>!r||r.trim().length===0?"API key is required":kn(r.trim())?!0:"Invalid API key format. Keys should start with 'cc_slides_' or 'mcp_'"});if(Rn(e.trim()),await Ko({message:`Use a custom API URL? (default: ${b.apiUrl.replace("https://","")})`,default:!1})){let r=await mc({message:"Enter API URL:",default:V(),validate:o=>{try{return new URL(o),!0}catch{return"Invalid URL format"}}});Cn(r)}console.log();let n=qt("Verifying API key...").start();try{let r=await Se();if(n.succeed("API key verified!"),console.log(),m(`Logged in as: ${r.user.email}`),r.teams.length===0)q("No teams found for this user.");else if(r.teams.length===1){let o=r.teams[0];Me(o.id),m(`Team: ${o.name} (${o.planName})`)}else{console.log(),m(`You have access to ${r.teams.length} teams.`);let o=await gc({message:"Select your default team:",choices:r.teams.map(a=>({name:`${a.name} (${a.planName}) - ${a.role}${a.isCurrent?" [current]":""}`,value:a.id})),default:r.currentTeam?.id});Me(o);let i=r.teams.find(a=>a.id===o);I(`Selected team: ${i?.name}`)}try{await Le()}catch{}}catch(r){n.fail("Failed to verify API key"),q(`Could not verify API key: ${r instanceof Error?r.message:String(r)}`),q(`You may need to set the team ID manually: ${b.commands[0]} config set team-id <id>`)}console.log(),I("Configuration saved!"),m(`Config file: ${vt()}`),console.log()}catch(e){if(e.name==="ExitPromptError"){console.log(),m("Configuration cancelled.");return}throw e}})).addCommand(new Ee("show").description("Show current configuration").option("--verify","Verify API key and show team details").action(async e=>{let t=Ur();if(ye("Current Configuration"),console.log(),T("API Key",Wr()??xt.red("Not set")),T("API URL",t.apiUrl),T("Default Team ID",t.defaultTeamId??xt.gray("Not set")),console.log(),T("Config file",vt()),e.verify&&t.apiKey){console.log();let n=qt("Verifying...").start();try{let r=await Se();n.succeed("Verified"),console.log(),T("User",r.user.email),r.currentTeam&&T("Current Team",`${r.currentTeam.name} (${r.currentTeam.planName})`),r.teams.length>1&&T("Total Teams",String(r.teams.length))}catch(r){n.fail("Verification failed"),q(r instanceof Error?r.message:String(r))}}console.log()})).addCommand(new Ee("set").description("Set a configuration value").argument("<key>","Configuration key (api-key, api-url, team-id)").argument("<value>","Value to set").action(async(e,t)=>{switch(e){case"api-key":kn(t)||(c("Invalid API key format. Keys should start with 'cc_slides_' or 'mcp_'"),process.exit(6)),Rn(t),De(),I("API key updated"),Le().catch(()=>{});break;case"api-url":try{new URL(t),Cn(t),I(`API URL set to: ${t}`)}catch{c("Invalid URL format"),process.exit(6)}break;case"team-id":Me(t),I(`Default team ID set to: ${t}`);break;default:c(`Unknown config key: ${e}`),console.log(xt.gray("Valid keys: api-key, api-url, team-id")),process.exit(6)}})).addCommand(new Ee("clear").description("Clear all configuration").action(async()=>{try{await Ko({message:"Are you sure you want to clear all configuration?",default:!1})?($t(),De(),I("Configuration cleared")):m("Cancelled")}catch{m("Cancelled")}})).addCommand(new Ee("refresh").description("Refresh cached feature flags").action(async()=>{let e=qt("Refreshing feature flags...").start();try{await Le(),e.succeed("Feature flags refreshed"),m(`Run '${b.commands[0]} --help' to see updated commands`)}catch(t){e.fail("Failed to refresh"),c(t instanceof Error?t.message:String(t)),process.exit(1)}})).addCommand(new Ee("path").description("Show configuration file path").option("--cache","Show feature cache file path").action(e=>{e.cache?console.log(Go()):console.log(vt())})).addCommand(new Ee("pull").description("Pull your account settings (memory, brand, prefs) into a local config.toml").action(async()=>{let e=qt("Pulling settings...").start();try{let t=await de(),n=await fetch(`${V()}/api/cli/settings`,{headers:t});if(!n.ok)throw new Error(`Request failed (HTTP ${n.status})`);let r=await n.text(),o=uc(Wo(vt()),"settings.toml");dc(Wo(o),{recursive:!0}),lc(o,r,"utf8"),e.succeed("Settings pulled"),m(`Written to ${o}`)}catch(t){e.fail("Failed to pull settings"),c(t instanceof Error?t.message:String(t)),process.exit(1)}}));import{Command as fc}from"commander";import{confirm as hc}from"@inquirer/prompts";var zo=new fc("delete").description("Delete a presentation").argument("<slug>","Presentation slug (e.g., my-presentation-v1-abc123)").option("-f, --force","Skip confirmation prompt").option("-q, --quiet","Suppress output").action(async(e,t)=>{if(await N(),!t.force)try{if(!await hc({message:`Are you sure you want to delete presentation "${e}"?`,default:!1})){t.quiet||q("Deletion cancelled");return}}catch{t.quiet||q("Deletion cancelled");return}try{await io(e),t.quiet||I(`Presentation "${e}" deleted`)}catch(n){c(n instanceof Error?n.message:String(n)),process.exit(n.exitCode??1)}});import{Command as Ke}from"commander";import Ho from"chalk";import yc from"ora";import{createParser as If}from"eventsource-parser";import Cf from"chalk";import Tf from"ora";async function Xo(e,t={}){let n=e.body?.getReader();if(!n)throw new Error("Response body is not readable");let r=new TextDecoder,o="",i=!1;for(;!i;){let a=await n.read();if(i=a.done,a.value){let s=r.decode(a.value,{stream:!0});o+=s,t.quiet||process.stdout.write(s)}}return t.quiet||console.log(),o}function bc(){return new Ke("blog").description("Generate a blog post from a presentation").argument("<slug>","Presentation slug").option("--words <count>","Target word count (50-2000)","300").option("--tone <tone>","Writing tone (professional, casual, educational)","professional").option("--audience <text>","Target audience description").option("-f, --format <format>","Output format (human, json, markdown)","human").option("--instructions <text>","Custom instructions for the blog").option("--no-images","Exclude image references").option("--toc","Include table of contents").action(async(e,t)=>{await N();let n=parseInt(t.words,10);(isNaN(n)||n<50||n>2e3)&&(c("Word count must be between 50 and 2000"),process.exit(6));let r=["professional","casual","educational"];r.includes(t.tone)||(c(`Invalid tone. Valid options: ${r.join(", ")}`),process.exit(6));let o=yc("Fetching presentation...").start();try{let i=await Te(e);o.text="Generating blog post...";let a=await po(i.id,i.documentCreatedAt||i.createdAt,{targetWordCount:n,tone:t.tone,targetAudience:t.audience??"General Audience",customInstructions:t.instructions??"",includeImages:!t.noImages,includeTableOfContents:t.toc??!1}),s=await Xo(a,{quiet:!0});o.succeed("Blog generated"),t.format==="json"?console.log(JSON.stringify({presentationId:e,title:i.title,wordCount:n,tone:t.tone,content:s},null,2)):t.format==="markdown"?console.log(s):(console.log(),console.log(Ho.bold(`Blog: ${i.title}`)),console.log(Ho.gray("\u2500".repeat(40))),console.log(),console.log(s),console.log())}catch(i){o.fail("Generation failed"),c(i instanceof Error?i.message:String(i)),process.exit(i.exitCode??1)}})}function wc(){return new Ke("tweets").description("Generate tweets from a presentation").argument("<slug>","Presentation slug").option("--count <n>","Number of tweets to generate","5").option("-f, --format <format>","Output format (human, json)","human").action(async(e,t)=>{await N(),m("Tweet generation coming soon.")})}function vc(){return new Ke("linkedin").description("Generate a LinkedIn carousel from a presentation").argument("<slug>","Presentation slug").option("-f, --format <format>","Output format (human, json)","human").action(async e=>{await N(),m("LinkedIn carousel generation coming soon.")})}function xc(){return new Ke("questions").description("Generate questions for a presentation").argument("<slug>","Presentation slug").option("--count <n>","Number of questions","10").option("-f, --format <format>","Output format (human, json)","human").action(async(e,t)=>{await N(),m("Question generation coming soon.")})}function Sc(){return new Ke("cheatsheet").description("Generate a presenter cheat sheet").argument("<slug>","Presentation slug").option("--mode <mode>","Cheat sheet mode (qa-prep, talking-points)","qa-prep").option("-f, --format <format>","Output format (human, json, markdown)","human").action(async(e,t)=>{await N(),m("Cheat sheet generation coming soon.")})}function Yo(){let e=new Ke("derive").description("Generate derivative content from a presentation"),t=Bo();return t&&(t.deckToBlog&&e.addCommand(bc()),t.deckToTweets&&e.addCommand(wc()),t.linkedInCarousel&&e.addCommand(vc()),t.redTeamQuestions&&e.addCommand(xc()),t.presenterCheatSheet&&e.addCommand(Sc())),e}import{Command as Ic}from"commander";import{writeFile as Rc}from"fs/promises";import{resolve as Qo}from"path";import Cc from"ora";var Zo=new Ic("export").description("Export a presentation to ZIP").argument("<slug>","Presentation slug (e.g., my-presentation-v1-abc123)").option("-o, --output <path>","Output file path").option("--include-images","Include images (default: true)",!0).option("--include-branding","Include branding (default: true)",!0).option("--no-external","Skip external image downloads").action(async(e,t)=>{await N();let n=Cc("Fetching presentation...").start();try{let r=await Te(e),o=r.title||e,i=r.id,a=e.replace(/[^a-z0-9-]/gi,"_").toLowerCase().substring(0,50),s=new Date().toISOString().split("T")[0],l=`${a}_${s}.zip`,p=t.output?Qo(t.output):Qo(process.cwd(),l);n.text=`Exporting "${o}"...`;let u=await ao(i,{includeImages:t.includeImages,includeBranding:t.includeBranding,includeExternal:!t.noExternal});n.text="Saving file...",await Rc(p,Buffer.from(u)),n.succeed("Export complete!"),console.log(),m(`File saved: ${p}`),m(`Size: ${kc(u.byteLength)}`),console.log()}catch(r){n.fail("Export failed"),c(r instanceof Error?r.message:String(r)),process.exit(r.exitCode??1)}});function kc(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}import{Command as Tc}from"commander";import Vn from"chalk";var ei=new Tc("ideas").description("Generate presentation topic ideas").option("--context <text>","Custom context for idea generation").option("--count <n>","Number of ideas to generate","5").option("-f, --format <format>","Output format (human, json)","human").action(async e=>{await N(),m("Idea generation is available in the web dashboard."),console.log(),console.log(Vn.gray("The CLI will support idea generation in a future release.")),console.log(),console.log("For now, try these approaches:"),console.log(Vn.gray("` 1. Visit the ${brand.displayName} dashboard and use the idea generator`")),console.log(Vn.gray(" 2. Create a presentation with a broad topic and refine it")),console.log()});import{Command as Ec}from"commander";import{readFile as Ac}from"fs/promises";import{resolve as $c,basename as Oc}from"path";import Pc from"ora";var ti=new Ec("import").description("Import a presentation from ZIP (admin only)").argument("<file>","Path to ZIP file");ti._hidden=!0;var ni=ti.option("--dry-run","Validate without importing").option("--overwrite","Overwrite existing presentations").option("--remap-ids","Generate new IDs (default: true)",!0).action(async(e,t)=>{await N();try{(await Se()).user.role!=="admin"&&(c("This command is only available to administrators"),process.exit(2))}catch{c("Failed to verify permissions"),process.exit(2)}let n=$c(e),r=Oc(n);r.endsWith(".zip")||(c("File must be a ZIP archive"),process.exit(6));let o=Pc("Reading file...").start();try{let i=await Ac(n);t.dryRun?o.text="Validating...":o.text="Importing presentation...";let a=await so(i,r,{dryRun:t.dryRun});if(a.success){if(t.dryRun)o.succeed("Validation passed!"),console.log(),m("The file is valid and can be imported."),a.statistics&&(console.log(),T("Slides",String(a.statistics.slidesImported)),T("Images",String(a.statistics.imagesUploaded)));else if(o.succeed("Import complete!"),console.log(),T("Presentation ID",a.presentationId??"N/A"),T("Document ID",a.documentId??"N/A"),a.statistics&&(T("Slides imported",String(a.statistics.slidesImported)),T("Images uploaded",String(a.statistics.imagesUploaded)),T("Time",`${a.statistics.totalTimeMs}ms`)),a.presentationId){let s=V();T("View URL",`${s}/p/${a.presentationId}`)}if(a.warnings&&a.warnings.length>0){console.log();for(let s of a.warnings)q(s)}console.log()}else{if(o.fail("Import failed"),console.log(),a.errors)for(let s of a.errors)c(`${s.type}: ${s.message}`);process.exit(1)}}catch(i){o.fail("Import failed"),i.code==="ENOENT"&&(c(`File not found: ${n}`),process.exit(3)),c(i instanceof Error?i.message:String(i)),process.exit(i.exitCode??1)}});import{Command as Nc}from"commander";var ri=new Nc("list").description("List presentations").option("-n, --limit <count>","Number of results to return","20").option("-f, --format <format>","Output format (table, json, ids)","table").option("--sort <field>","Sort by field (created, updated, title)").option("--team-id <id>","Team ID (uses default if not specified)").option("-d, --detail","Show detailed output including URLs").option("-l, --language <lang>","Language for URLs","en").action(async e=>{await N();let t=parseInt(e.limit,10);(isNaN(t)||t<1)&&(c("Invalid limit value"),process.exit(6));let n=e.teamId??At();n||(c("`Team ID required. Set a default with '${brand.commands[0]} config set team-id <id>' or use --team-id`"),process.exit(6));try{let r=await no(n,t);if(r.length===0){e.format==="json"?console.log(JSON.stringify([])):e.format!=="ids"&&m("No presentations found");return}switch(e.sort&&r.sort((o,i)=>{switch(e.sort){case"created":return new Date(i.createdAt).getTime()-new Date(o.createdAt).getTime();case"title":return(o.title||"").localeCompare(i.title||"");default:return 0}}),e.format){case"json":console.log(JSON.stringify(r,null,2));break;case"ids":console.log(qo(r));break;default:console.log(Fo(r,{showLinks:e.detail,language:e.language}));break}}catch(r){c(r instanceof Error?r.message:String(r)),process.exit(r.exitCode??1)}});import{createHash as _c,randomBytes as ai}from"crypto";import si from"http";import oi from"chalk";import{Command as Lc}from"commander";import Dc from"open";import ii from"ora";var Uc=`${b.displayName} CLI`,Fc=8765,qc=8775,Vc=["presentations:read","presentations:write","media:read","media:write"];function jc(){return ai(32).toString("base64url")}function Mc(e){return _c("sha256").update(e).digest("base64url")}function Bc(){return ai(16).toString("hex")}async function Gc(e,t){for(let n=e;n<=t;n++)try{return await new Promise((r,o)=>{let i=si.createServer();i.listen(n,()=>{i.close(()=>r())}),i.on("error",o)}),n}catch{}throw new Error(`No available port found between ${e} and ${t}`)}async function Wc(e){let t=await fetch(`${e}/.well-known/oauth-authorization-server`);if(!t.ok)throw new Error(`Failed to fetch OAuth metadata: ${t.status}`);return t.json()}function Kc(e){let t=e.scopes_supported?.filter(n=>typeof n=="string"&&n.trim().length>0);return(t?.length?t:Vc).join(" ")}async function Jc(e,t,n){let r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_name:Uc,redirect_uris:[t],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none",scope:n})});if(!r.ok){let o=await r.text();throw new Error(`Client registration failed: ${o}`)}return r.json()}async function zc(e,t,n,r,o){let i=new URLSearchParams({grant_type:"authorization_code",code:t,redirect_uri:r,client_id:o,code_verifier:n}),a=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()});if(!a.ok){let s=await a.text();throw new Error(`Token exchange failed: ${s}`)}return a.json()}function Xc(e,t){return new Promise((n,r)=>{let o,i=!1,a=()=>{i||(i=!0,clearTimeout(o),process.off("SIGINT",s),process.off("SIGTERM",s),l.close())},s=()=>{a(),r(new Error("Login cancelled"))},l=si.createServer((p,u)=>{let w=new URL(p.url||"",`http://localhost:${e}`);if(w.pathname!=="/callback"){u.writeHead(404),u.end("Not found");return}let R=w.searchParams.get("code"),y=w.searchParams.get("state"),S=w.searchParams.get("error"),v=w.searchParams.get("error_description");if(u.writeHead(200,{"Content-Type":"text/html"}),S){u.end(`
|
|
3
|
+
var h={id:"mindframes",name:"mindframes",displayName:"Mindframes",description:"CLI tool for Mindframes presentation generation",commands:["mf","mindframes"],apiUrl:"https://www.mindframes.app",docsUrl:"https://docs.mindframes.ai",packageName:"@conceptcraft/mindframes",configDir:".mindframes",apiKeyEnvVar:"MINDFRAMES_API_KEY",apiUrlEnvVar:"MINDFRAMES_API_URL",codexSelfConnectorAppId:"asdk_app_6a2b9629c1608191970dcb3ab374ac55"};import F from"chalk";import{Command as Ap}from"commander";import rt from"chalk";import{Command as Bs}from"commander";function vr(e={}){let t=(s,l)=>{let g=e[s];return g&&g.trim().length>0?g.trim():l},n=t("NEXT_PUBLIC_PRODUCT_SHORT_NAME","ConceptCraft"),r=t("NEXT_PUBLIC_CLI_COMMAND","ccr"),o=t("NEXT_PUBLIC_CLI_COMMAND_FULL","conceptcraft"),i=t("NEXT_PUBLIC_CLI_CONFIG_DIR",`.${o}`),a=n.toUpperCase().replace(/[^A-Z0-9]/g,"_");return{id:o,name:i.replace(/^\./,""),displayName:n,description:`CLI tool for ${n} presentation generation`,commands:[r,o],apiUrl:t("NEXT_PUBLIC_API_URL","http://localhost:3000"),docsUrl:t("NEXT_PUBLIC_DOCS_URL",`https://docs.${n.toLowerCase()}.ai`),packageName:t("NEXT_PUBLIC_CLI_PACKAGE_NAME","@conceptcraft/cli"),configDir:i,apiKeyEnvVar:`${a}_API_KEY`,apiUrlEnvVar:`${a}_API_URL`,codexSelfConnectorAppId:e.NEXT_PUBLIC_CODEX_SELF_CONNECTOR_APP_ID?.trim()||void 0}}var u=typeof h<"u"&&h?h:vr(process.env);import N from"chalk";import pn from"cli-table3";import cs from"conf";var xr=u.apiUrl,ls={apiKey:{type:"string"},apiUrl:{type:"string",default:xr},defaultTeamId:{type:"string"},accessToken:{type:"string"},refreshToken:{type:"string"},tokenExpiresAt:{type:"number"},clientId:{type:"string"},clientSecret:{type:"string"}},P=new cs({projectName:u.name,schema:ls});function kr(){return{apiKey:ue(),apiUrl:V(),defaultTeamId:P.get("defaultTeamId"),accessToken:P.get("accessToken"),refreshToken:P.get("refreshToken"),tokenExpiresAt:P.get("tokenExpiresAt"),clientId:P.get("clientId"),clientSecret:P.get("clientSecret")}}function ue(){let e=process.env[u.apiKeyEnvVar]??process.env.CC_SLIDES_API_KEY;return e||P.get("apiKey")}function un(e){P.set("apiKey",e)}function V(){let e=process.env[u.apiUrlEnvVar]??process.env.CC_SLIDES_API_URL;return e||(P.get("apiUrl")??xr)}function mn(e){P.set("apiUrl",e)}function bt(){return P.get("defaultTeamId")}function Oe(e){P.set("defaultTeamId",e)}function wt(){P.clear()}function et(){return P.path}function Pe(){return!!ue()}function Cr(){return P.get("accessToken")}function Ir(){return P.get("refreshToken")}function vt(e,t,n){P.set("accessToken",e),P.set("refreshToken",t),P.set("tokenExpiresAt",Date.now()+(n-60)*1e3)}function xt(){P.delete("accessToken"),P.delete("refreshToken"),P.delete("tokenExpiresAt")}function Rr(){let e=P.get("tokenExpiresAt");return e?Date.now()>=e:!0}function ke(){return!!P.get("accessToken")&&!!P.get("refreshToken")}function Sr(){return P.get("clientId")}function $r(){return P.get("clientSecret")}function Tr(e,t){P.set("clientId",e),P.set("clientSecret",t)}function C(e,t="human"){t!=="quiet"&&t!=="json"&&console.log(N.green("\u2713"),e)}function d(e,t="human"){if(t!=="quiet"){if(t==="json"){console.error(JSON.stringify({error:e}));return}console.error(N.red("\u2717"),e)}}function M(e,t="human"){t!=="quiet"&&t!=="json"&&console.warn(N.yellow("\u26A0"),e)}function f(e,t="human"){t!=="quiet"&&t!=="json"&&console.log(N.blue("\u2139"),e)}function _e(e,t="en"){return e?`${V()}/${t}/view/presentations/${e}`:"N/A"}function Er(e,t={}){let{showLinks:n=!1,language:r="en"}=t,o=(s,l)=>s.length>l?`${s.slice(0,l-1)}\u2026`:s,i=s=>{try{return new Date(s).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0}).replace(" at ",", ")}catch{return"-"}};if(n){let s=new pn({head:[N.cyan("Slug"),N.cyan("Title"),N.cyan("Slides"),N.cyan("Created"),N.cyan("URL")]});for(let l of e)s.push([o(l.slug||l.id.slice(0,8),30),o(l.title||"Untitled",22),String(l.numberOfSlides||"-"),i(l.createdAt),_e(l.slug,r)]);return s.toString()}let a=new pn({head:[N.cyan("Slug"),N.cyan("Title"),N.cyan("Slides"),N.cyan("Mode"),N.cyan("Created")],colWidths:[32,28,8,11,18]});for(let s of e)a.push([s.slug||s.id.slice(0,8),s.title||"Untitled",String(s.numberOfSlides||"-"),s.mode||"-",i(s.createdAt)]);return a.toString()}function Ar(e){return e.map(t=>t.slug||t.id).join(`
|
|
4
|
+
`)}function Or(e){let t=[...e].sort((o,i)=>{if(o.isDefault&&!i.isDefault)return-1;if(!o.isDefault&&i.isDefault)return 1;let a=o.createdAt?new Date(o.createdAt).getTime():0;return(i.createdAt?new Date(i.createdAt).getTime():0)-a}),n=o=>{if(!o)return"-";try{return new URL(o).href}catch{return o.startsWith("http")?o:`https://${o}`}},r=new pn({head:[N.cyan("Name"),N.cyan("Source"),N.cyan("Color"),N.cyan("Logo"),N.cyan("Default"),N.cyan("ID")]});for(let o of t){let i=o.primaryColor?`${N.bgHex(o.primaryColor)(" ")} ${o.primaryColor}`:"-";r.push([o.name,n(o.sourceUrl),i,o.logoUrl?N.green("\u2713"):N.gray("\u2717"),o.isDefault?N.green("\u2605"):"",o.id])}return r.toString()}function gn(e){try{return new Date(e).toLocaleString()}catch{return e}}function oe(e){console.log(),console.log(N.bold(e)),console.log(N.gray("\u2500".repeat(e.length)))}function S(e,t){console.log(` ${N.gray(`${e}:`)} ${t??"-"}`)}function us(e){return JSON.stringify(e,null,2)}function p(e){console.log(us(e))}import{createHash as Os,randomUUID as tt}from"crypto";import{readFileSync as Ps,statSync as _s}from"fs";import{basename as Vr}from"path";import{spawnSync as ms}from"child_process";import{existsSync as ps,mkdtempSync as gs,rmSync as fs,writeFileSync as hs}from"fs";import{tmpdir as ys}from"os";import{join as Pr}from"path";import fn from"ffmpeg-static";var bs=/Duration:\s*(\d+):(\d+):(\d+(?:\.\d+)?)/;function ws(e){let t=bs.exec(e);if(!t)return;let n=Number(t[1])*3600+Number(t[2])*60+Number(t[3]);return Number.isFinite(n)&&n>0?n:void 0}function _r(e,t="audio"){if(e.length===0)return;let n=fn&&ps(fn)?fn:"ffmpeg",r=/^[a-z0-9]+$/i.test(t)?t:"audio",o=gs(Pr(ys(),"mf-tts-duration-")),i=Pr(o,`speech.${r}`);try{hs(i,e);let a=ms(n,["-hide_banner","-i",i],{encoding:"utf8"});return a.error?void 0:ws(a.stderr??"")}finally{fs(o,{force:!0,recursive:!0})}}import kt from"chalk";var c={SUCCESS:0,GENERAL_ERROR:1,AUTH_ERROR:2,NOT_FOUND:3,RATE_LIMIT:4,NETWORK_ERROR:5,INVALID_INPUT:6};async function vs(){let e=Ir(),t=Sr(),n=V();if(!e||!t)return null;try{let r=await fetch(`${n}/.well-known/oauth-authorization-server`);if(!r.ok)return null;let o=await r.json(),i=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:t}),a=$r();a&&i.set("client_secret",a);let s=await fetch(o.token_endpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()});if(!s.ok)return xt(),null;let l=await s.json();return vt(l.access_token,l.refresh_token,l.expires_in),l.access_token}catch{return null}}async function Lr(){return ke()?Rr()?vs():Cr()||null:null}function ie(){return ke()||Pe()}async function A(){if(!ie()){let e=u.commands[0],t=u.apiKeyEnvVar;console.error(kt.red("\u2717 Not authenticated.")),console.error(),console.error(kt.bold("To authenticate, run:")),console.error(kt.cyan(` ${e} login`)),console.error(),console.error(kt.gray(`Or set ${t} environment variable.`)),process.exit(c.AUTH_ERROR)}}function xs(e){return e.length<=14?"****":`${e.slice(0,10)}...${e.slice(-4)}`}function Dr(){let e=ue();if(e)return xs(e)}function hn(e){return["cc_slides_","mcp_","cc_sk_","sk_"].some(n=>e.startsWith(n))}import{mkdirSync as ks,readFileSync as Cs,writeFileSync as Is}from"fs";import{homedir as Rs}from"os";import{join as Nr}from"path";import Ss from"ajv/dist/2020.js";var yn=new Map;function Ur(){return Nr(Rs(),".conceptcraft","schemas")}function qr(e){return Nr(Ur(),`${e}.json`)}function $s(e){try{let t=JSON.parse(Cs(qr(e),"utf8"));return typeof t?.catalogVersion=="number"&&t.schema?t:null}catch{return null}}function Ts(e,t){try{ks(Ur(),{recursive:!0}),Is(qr(e),JSON.stringify(t))}catch{}}async function Es(e){let t=yn.get(e);if(t!==void 0)return t;try{let n=await Mr(e),r={catalogVersion:n.catalogVersion,schema:n.schema};return Ts(e,r),yn.set(e,r),r}catch{let n=$s(e);return yn.set(e,n),n}}function As(e){let t=e.instancePath.replace(/^\//,"").replace(/\//g,"."),n=t.length>0?t:"(root)",r=e.keyword==="additionalProperties"?`: unknown field "${e.params.additionalProperty}"`:`: ${e.message??"invalid value"}`;return`${n}${r}`}async function Ct(e,t){let n=await Es(e);if(!n)return;let r;try{r=new Ss({allErrors:!0,strict:!1}).compile(n.schema)}catch{return}if(r(t))return;let o=(r.errors??[]).slice(0,10).map(As);throw new Error(`Request does not match the ${e} contract \u2014 ${o.join("; ")}`)}async function se(){let e=await Lr();if(e)return{Authorization:`Bearer ${e}`};let t=ue();return t?{"x-api-key":t}:{}}var O=class extends Error{constructor(n,r,o=1,i="operation_failed",a=!1){super(n);this.statusCode=r;this.exitCode=o;this.errorCode=i;this.retryable=a;this.name="ApiError"}statusCode;exitCode;errorCode;retryable},jr="Idempotency-Key";function Ls(e){return e&&e!=="GET"?{[jr]:tt()}:{}}async function W(e,t={}){let n=V();if(!ie()&&!t.allowUnauthenticated)throw new O(`Not authenticated. Run '${u.commands[0]} login' or set ${u.apiKeyEnvVar} environment variable.`,401,2,"authentication_required");let r=await se(),o=`${n}${e}`,i={...r,...Ls(t.method),...t.headers};t.body&&!t.stream&&(i["Content-Type"]="application/json");let a={method:t.method??"GET",headers:i};t.body&&(a.body=JSON.stringify(t.body));let s;try{s=await fetch(o,a)}catch(g){throw new O(`Network error: ${g instanceof Error?g.message:"Unknown error"}`,0,5,"service_unavailable",!0)}if(!s.ok){let g=await s.text().catch(()=>""),m=Ns(g,s.status),w=1;switch(s.status){case 401:w=2;break;case 403:w=2;break;case 404:w=3;break;case 429:w=4;break;case 500:w=1;break}let v=`[${m.code}] ${m.message}`;throw new O(v,s.status,w,m.code,m.retryable)}return t.stream?s:s.headers.get("content-type")?.includes("application/json")?s.json():s.text()}function Ds(e){switch(e){case 400:return"invalid_input";case 401:return"authentication_required";case 402:return"insufficient_credits";case 403:return"access_denied";case 404:return"not_found";case 429:return"rate_limited";case 503:return"service_unavailable";default:return"operation_failed"}}function Ns(e,t){let n=Ds(t),r=e.trim()||`HTTP ${t}: The request could not be completed.`;try{let o=JSON.parse(e),i=typeof o.error=="object"&&o.error!==null?o.error:void 0;if(o.ok===!1&&typeof i?.code=="string"&&typeof i.message=="string")return{code:i.code,message:i.message,retryable:i.retryable===!0};if(typeof o.error=="string")return{code:/^[a-z][a-z0-9_]+$/.test(o.error)?o.error:n,message:typeof o.message=="string"?o.message:o.error,retryable:t===429||t>=500}}catch{}return{code:n,message:r,retryable:t===429||t>=500}}async function Mr(e){return W(`/api/cli/tools/${encodeURIComponent(e)}/schema`)}async function Gr(){return W("/api/cli/capabilities",{allowUnauthenticated:!0})}async function T(e,t,n){let r=n?`?teamId=${encodeURIComponent(n)}`:"";return W(`/api/cli/tool-runs/${encodeURIComponent(e)}/${encodeURIComponent(t)}${r}`)}async function y(e,t,n){return W(`/api/cli/tools/${encodeURIComponent(e)}`,{body:t,headers:n?{[jr]:n}:void 0,method:"POST"})}async function Br(e,t,n,r=120){let o=0;for(let i=0;i<r;i++){let a;try{a=await T(e,t,n),o=0}catch(s){if(s instanceof O&&s.retryable&&o<2){o+=1,await new Promise(l=>setTimeout(l,2e3));continue}throw s instanceof O?new O(`${s.message} Request ID: ${t}.`,s.statusCode,s.exitCode,s.errorCode,s.retryable):new O(`${s instanceof Error?s.message:"Unknown error"} Request ID: ${t}.`,0,5,"poll_failed",!0)}if(a.done){if(a.error)throw new O(`[${a.error.code??"operation_failed"}] ${a.error.message} Request ID: ${t}.`,500,1,a.error.code??"operation_failed",a.error.retryable);return a}await new Promise(s=>setTimeout(s,(a.nextPollAfterSeconds??2)*1e3))}throw new O(`[operation_timed_out] The background operation timed out. Request ID: ${t}.`,408,1,"operation_timed_out",!0)}function It(e){return e.error||e.status==="failed"||e.status==="errored"||e.status==="cancelled"?"failed":e.done||e.status==="completed"?"completed":e.status==="in_progress"?"processing":"pending"}function Us(e){try{let n=new URL(e).pathname.split("/").pop()?.split(".").pop()?.toLowerCase();return n&&/^[a-z0-9]+$/.test(n)?n:void 0}catch{return}}function qs(e){let t=e.toLowerCase().split(".").pop();return{pdf:"application/pdf",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",mp4:"video/mp4",mov:"video/quicktime",mpeg:"video/mpeg",mpg:"video/mpeg",webm:"video/webm",aac:"audio/aac",m4a:"audio/mp4",mp3:"audio/mpeg",oga:"audio/ogg",ogg:"audio/ogg",wav:"audio/wav",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",html:"text/html",css:"text/css",js:"application/javascript",ts:"text/x-typescript",py:"text/x-python"}[t||""]||"application/octet-stream"}async function de(e,t={}){let n=V();if(!ie())throw new O(`Not authenticated. Run '${u.commands[0]} login' or set ${u.apiKeyEnvVar} environment variable.`,401,2);let r=await se(),o=_s(e),i=Vr(e),a=qs(e),s=Ps(e),l=Os("sha256").update(s).digest("hex"),g=await fetch(`${n}/api/cli/files/upload`,{method:"POST",headers:{"Content-Type":"application/json",...r},body:JSON.stringify({fileMetadata:{lastModified:o.mtimeMs,name:i,type:a,size:o.size},...t.idempotencyKey?{uploadIdentity:{contentSha256:l,idempotencyKey:t.idempotencyKey}}:{}})});if(!g.ok){let $=await g.text();throw new O($||"Failed to get upload URL",g.status,1)}let m=await g.json(),w=new FormData;for(let[$,x]of Object.entries(m.fields||{}))w.append($,x);let v=new Blob([s],{type:a});w.append("file",v,i);let b=await fetch(m.url,{method:"POST",body:w});if(!b.ok)throw new O("Failed to upload file to storage",b.status,1);return{id:tt(),name:i,url:m.fileUrl,size:o.size,type:a,selected:!0}}async function Wr(e,t){let n=[];for(let r=0;r<e.length;r++){let o=e[r],i=Vr(o);t?.(r,e.length,i);let a=await de(o);n.push(a)}return t?.(e.length,e.length,""),n}async function Kr(e){let t={};for(let[r,o]of Object.entries(e))o!==void 0&&(t[r]=o);let n=t.designBrandReference??t.brandId??t.brandingId;return n!==void 0&&(t.designBrandReference=n,delete t.brandId,delete t.brandingId),y("createSlides",{...t,approvalReceipt:"cli:create"})}async function Jr(e,t=20){let n=new URLSearchParams;return n.set("limit",String(t)),e&&n.set("teamId",e),(await W(`/api/cli/presentations?${n}`)).presentations}async function fe(e){return W(`/api/cli/presentation/${encodeURIComponent(e)}`)}async function zr(e,t){return W(`/api/cli/presentation/${encodeURIComponent(e)}/speaker-notes`,{body:t,method:"POST"})}async function Xr(e,t){return W(`/api/cli/presentation-video/${encodeURIComponent(e)}/narration`,{body:t,method:"POST"})}async function bn(e){let t=V(),n=await fetch(`${t}/api/durable-runs/${encodeURIComponent(e)}/events`,{headers:await se()});if(!n.ok)throw new O(await n.text(),n.status);return n}async function Hr(e){await W(`/api/cli/presentation/${encodeURIComponent(e)}`,{method:"DELETE"})}async function Yr(e,t={}){let n=V();if(!ie())throw new O(`Not authenticated. Run '${u.commands[0]} login' or set ${u.apiKeyEnvVar} environment variable.`,401,2);let r=await se(),o=await fetch(`${n}/api/presentations/export`,{method:"POST",headers:{"Content-Type":"application/json",...r},body:JSON.stringify({presentationId:e,options:{includeImages:t.includeImages??!0,includeBranding:t.includeBranding??!0,downloadExternalImages:t.includeExternal??!0}})});if(!o.ok)throw new O(await o.text(),o.status,o.status===404?3:1);return o.arrayBuffer()}async function Qr(e,t,n={}){let r=V();if(!ie())throw new O(`Not authenticated. Run '${u.commands[0]} login' or set ${u.apiKeyEnvVar} environment variable.`,401,2);let o=await se(),i=new FormData,a=new Blob([e],{type:"application/zip"});i.append("file",a,t),i.append("options",JSON.stringify({dryRun:n.dryRun??!1,remapIds:!0}));let s=await fetch(`${r}/api/presentations/import`,{method:"POST",headers:o,body:i}),l=await s.json();if(!s.ok)throw new O(l.errors?.[0]?.message??"Import failed",s.status,1);return l}async function wn(){return W("/api/cli/branding")}async function Zr(e,t=25){let n=new URLSearchParams;return n.set("limit",String(t)),e&&n.set("search",e),W(`/api/cli/templates?${n}`)}async function eo(e){return W(`/api/branding/${e}`)}async function to(e,t,n){return y("extractBranding",{approvalReceipt:n,teamId:t,url:e})}async function me(){return W("/api/cli/whoami")}async function no(){return W("/api/cli/features")}async function ro(e,t,n={}){let r=V();if(!ie())throw new O(`Not authenticated. Run '${u.commands[0]} login' or set ${u.apiKeyEnvVar} environment variable.`,401,2);let o=await se(),i=await fetch(`${r}/api/presentations/${e}/generate-blog`,{method:"POST",headers:{"Content-Type":"application/json",...o},body:JSON.stringify({documentCreatedAt:t,targetWordCount:n.targetWordCount??300,tone:n.tone??"professional",language:n.language??"en",targetAudience:n.targetAudience??"General Audience",blogFormat:n.blogFormat??"narrative",customInstructions:n.customInstructions??"",includeImages:n.includeImages??!0,includeTableOfContents:n.includeTableOfContents??!1})});if(!i.ok)throw new O(await i.text(),i.status,i.status===404?3:1);return i}async function Ms(e){let t=new URLSearchParams;e&&t.set("teamId",e);let n=t.toString();return W(`/api/cli/limits${n?`?${n}`:""}`)}async function oo(e,t,n){let r=await Ms(n);if(!r.canGenerate[e]){let o=r.remaining[e]??0,i=r.limits[e]??0;throw i===0?new O(`The "${e}" mode is not available on your ${r.planName} plan. Available modes: ${r.availableModes.join(", ")||"none"}`,403,2):new O(`You have reached your ${e} generation limit for this billing cycle (${r.usage[e]??0}/${i} used). Try a different mode: ${r.availableModes.join(", ")||"none available"}`,403,4)}if(r.maxSlidesPerPresentation!==-1&&t>r.maxSlidesPerPresentation)throw new O(`Maximum ${r.maxSlidesPerPresentation} slides allowed on your ${r.planName} plan (requested: ${t}). Try a different mode: ${r.availableModes.join(", ")||"none available"}`,403,6);return r}function Fs(e){return e.replace(/[\u2014\u2013]/g,"-").replace(/[\u201C\u201D]/g,'"').replace(/[\u2018\u2019]/g,"'").replace(/\u2026/g,"...").replace(/\u00A0/g," ").replace(/[\u2010-\u2015]/g,"-").replace(/[\u2032\u2033]/g,"'")}async function io(e){let t={...e,text:Fs(e.text)};await Ct("generateSpeech",t);let n=await y("generateSpeech",{approvalReceipt:"cli:tts-generate",...t}),r=await Br("generateSpeech",n.runId),o=r.entity?.url;if(!o)throw new O(`Speech completed without an audio URL. Request ID: ${n.runId}.`,500);let i;try{i=await fetch(o)}catch(w){throw new O(`${w instanceof Error?w.message:"Failed to download speech audio"} Request ID: ${n.runId}.`,0,5,"download_failed",!0)}if(!i.ok)throw new O(`Failed to download speech audio. Request ID: ${n.runId}.`,i.status,5,"download_failed",i.status===429||i.status>=500);let a=r.output??{},s=e.options??{},l=ae(a.format)??Us(o)??"mp3",g=Buffer.from(await i.arrayBuffer()),m=ao(a.durationSec,a.duration,a.duration_s)??_r(g,l);if(m===void 0)throw new O(`Speech completed without a measurable duration. Request ID: ${n.runId}.`,500,1,"invalid_audio_metadata");return{audioData:g,audioUrl:o,duration:m,cost:Ce(a.costUsd)??Ce(a.cost)??0,provider:ae(a.provider)??s.provider??"gemini",format:l,requestId:n.runId,voiceId:ae(a.voice_id)??ae(a.voiceId),voice:ae(a.voice),model:ae(a.model),speed:Ce(a.speed)??s.speed,language:ae(a.lang)??ae(a.language)??s.language,langApplied:Fr(a.langApplied)??Fr(a.lang_applied),timestamps:Vs(a.timestamps),wordTimings:js(a.timestamps??a.timings)}}function ao(...e){for(let t of e){let n=typeof t=="number"?t:typeof t=="string"&&t.trim()?Number(t):Number.NaN;if(Number.isFinite(n)&&n>0)return n}}function Vs(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=t.characters,r=t.characterStartTimesSeconds??t.character_start_times_seconds,o=t.characterEndTimesSeconds??t.character_end_times_seconds;if(!(!Array.isArray(n)||!Array.isArray(r)||!Array.isArray(o))&&!(n.length===0||n.length!==r.length||n.length!==o.length||!n.every(i=>typeof i=="string")||!r.every(i=>typeof i=="number"&&Number.isFinite(i))||!o.every(i=>typeof i=="number"&&Number.isFinite(i))))return{characters:n,characterStartTimesSeconds:r,characterEndTimesSeconds:o}}function js(e){if(!Array.isArray(e))return;let t=e.flatMap(n=>{if(!n||typeof n!="object"||Array.isArray(n))return[];let r=n,o=ae(r.text)??ae(r.word),i=Ce(r.start),a=Ce(r.end);return o&&i!==void 0&&a!==void 0&&a>=i?[{text:o,start:i,end:a}]:[]});return t.length>0?t:void 0}function ae(e){return typeof e=="string"&&e.trim()?e.trim():void 0}function Ce(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function Fr(e){return typeof e=="boolean"?e:void 0}async function so(e){let t=e?`/api/cli/tts?provider=${e}`:"/api/cli/tts";return W(t)}async function co(e){let t={...e},n=typeof t.idempotency_key=="string"?t.idempotency_key:void 0;return delete t.idempotency_key,await Ct("generateMusic",t),{requestId:(await y("generateMusic",{...t,approvalReceipt:"cli:music-generate"},n)).runId,status:"pending"}}async function vn(e){let t=await T("generateMusic",e);return{requestId:e,status:It(t),audioUrl:t.entity?.url,duration:ao(t.output?.durationSec,t.output?.duration,t.output?.duration_s),cost:Ce(t.output?.costUsd)??Ce(t.output?.cost),error:t.error?.message}}async function lo(e){return{requestId:(await y("mixAudio",{...e,approvalReceipt:"cli:mix-create"})).runId,status:"pending"}}async function xn(e){let t=await T("mixAudio",e);return{requestId:e,status:It(t),outputUrl:t.entity?.url,error:t.error?.message}}async function uo(e){return W("/api/cli/images/search",{method:"POST",body:e})}async function mo(e){return W("/api/cli/videos/search",{method:"POST",body:e})}async function Q(e,t=60,n=2e3){let r=0;for(let o=0;o<t;o++){let i;try{i=await e(),r=0}catch(a){if(a instanceof O&&a.retryable&&r<2){r+=1,await new Promise(s=>setTimeout(s,n));continue}throw a}if(i.status==="completed"||i.status==="failed")return i;await new Promise(a=>setTimeout(a,n))}throw new O("Operation timed out",408,1)}async function po(e,t,n){return y("getYoutubeTranscript",{...n?{language:n}:{},teamId:t,url:e})}async function go(e,t){let n=await y("scrape",{teamId:t,url:e});return{data:{content:n.content,cost:n.totalCost??0,metadata:{description:"",format:"markdown",tokenUsage:Math.ceil(n.content.length/4)},title:n.title??"",url:n.url},success:!0}}async function fo(e){let t={...e},n=typeof t.idempotency_key=="string"?t.idempotency_key:void 0;delete t.idempotency_key,await Ct("generateSfx",t);let r=await y("generateSfx",{...t,approvalReceipt:"cli:sfx-generate"},n);return{id:r.runId,requestId:r.runId,status:"pending"}}async function kn(e){let t=await T("generateSfx",e);return{id:t.entity?.id??e,requestId:e,status:It(t),audioUrl:t.entity?.url,error:t.error?.message}}async function ho(e){return y("listPresenterOptions",{...e?{voiceLanguage:e}:{}})}async function yo(e){return{requestId:(await y("generateAvatarVideo",{...e,approvalReceipt:"cli:avatar-generate"})).runId,status:"pending"}}async function Cn(e){let t=await T("generateAvatarVideo",e);return{durationSec:typeof t.output?.durationSec=="number"?t.output.durationSec:null,error:t.error?.message,requestId:e,status:It(t),videoUrl:t.entity?.url}}async function bo(e){return y("lipsyncVideo",{...e,approvalReceipt:"cli:video-lipsync"})}async function wo(){return(await y("listTranslationLanguages",{})).languages}async function vo(e){return y("translateVideo",{...e,approvalReceipt:"cli:video-translate"})}async function xo(e,t,n){return y("createVideo",{...e,approvalReceipt:t},n)}async function ko(e){let{idempotencyKey:t,...n}=e;return y("analyzeVideoReference",n,t)}async function Co(e,t){return Br("analyzeVideoReference",e,t)}async function Io(e){return y("listCastPresets",e)}async function Ro(e){return y("createCastPreset",e,tt())}async function So(e){return y("updateCastPreset",e,tt())}async function $o(e){return y("deleteCastPreset",{presetId:e},tt())}var To=[{billing:"free",category:"branding",description:"Attach a social handle to an accessible brand profile.",id:"addBrandSocialHandle",lifecycle:{mode:"bounded"},milestone:"M2",name:"branding social add",operation:"mutation",scopes:["brands:write"],state:"available"},{billing:"free",category:"branding",description:"Add a competitor to an accessible brand profile.",id:"addCompetitor",lifecycle:{mode:"bounded"},milestone:"M2",name:"branding competitor add",operation:"mutation",scopes:["brands:write"],state:"available"},{billing:"delegated",category:"media",description:"Transcribe an uploaded recording and publish the transcript for generation.",id:"analyzeMediaAsset",lifecycle:{mode:"background",status:{cliCommand:"media analyze status",pollAfterSeconds:10}},milestone:"M8",name:"media analyze",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"media",description:"Analyze an uploaded video before using it as a source, motion, blocking, or pacing reference in a video storyboard. The completed status output includes the analysisId required by the storyboard's source-video binding.",id:"analyzeVideoReference",lifecycle:{mode:"background",status:{cliCommand:"video reference status",pollAfterSeconds:10}},milestone:"M8",name:"video reference analyze",operation:"generation",scopes:["media:write"],state:"available"},{billing:"delegated",category:"artifacts",description:"Generate and persist an ad variation set.",id:"createAdSet",lifecycle:{mode:"background",status:{cliCommand:"ad-set status",pollAfterSeconds:10}},milestone:"M6",name:"ad-set create",operation:"generation",scopes:["ad-sets:write"],state:"available"},{billing:"free",category:"agents",description:"Create a scheduled agent from a validated configuration.",id:"createAgent",lifecycle:{mode:"bounded"},milestone:"M3",name:"agent create",operation:"mutation",scopes:["agents:write"],state:"available"},{billing:"free",category:"media",description:"Save one way a cast member appears: a personal presenter (pp_* id), a backdrop, and optionally a voice, under a name unique to that person.",id:"createCastPreset",lifecycle:{mode:"bounded"},milestone:"M8",name:"cast preset create",operation:"mutation",scopes:["media:write"],state:"available"},{billing:"free",category:"knowledge",description:"Save a markdown, CSV or plain-text document into the Library.",id:"createDocument",lifecycle:{mode:"bounded"},milestone:"M8",name:"document create",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"fixed",category:"media",description:"Generate and persist an image.",id:"createImage",lifecycle:{mode:"background",status:{cliCommand:"image status",pollAfterSeconds:10}},milestone:"M5",name:"image generate",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"knowledge",description:"Create a Library folder, optionally inside another.",id:"createLibraryFolder",lifecycle:{mode:"bounded"},milestone:"M1",name:"library folders create",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"dynamic",category:"artifacts",description:"Generate and persist a microsite.",id:"createMicrosite",lifecycle:{mode:"background",status:{cliCommand:"microsite status",pollAfterSeconds:10}},milestone:"M6",name:"microsite create",operation:"generation",scopes:["microsites:write"],state:"available"},{billing:"free",category:"artifacts",description:"Create a standalone poll.",id:"createPoll",lifecycle:{mode:"bounded"},milestone:"M3",name:"poll create",operation:"mutation",scopes:["polls:write"],state:"available"},{billing:"delegated",category:"presentations",description:"Create an MP4 video from a finished presentation, or render an existing presentation-video project.",id:"createPresentationVideo",lifecycle:{mode:"background",status:{cliCommand:"presentation video status",pollAfterSeconds:10}},milestone:"M4",name:"presentation video",operation:"generation",scopes:["presentations:write"],state:"available"},{billing:"fixed",category:"presentations",description:"Generate a presentation and persist the resulting deck.",id:"createSlides",lifecycle:{mode:"background",status:{cliCommand:"presentation status",pollAfterSeconds:20}},milestone:"M4",name:"create",operation:"generation",scopes:["presentations:write"],state:"available"},{billing:"delegated",category:"media",description:"Generate one video from the opaque text and explicit metadata stored in a pinned storyboard revision.",id:"createVideo",lifecycle:{mode:"background",status:{cliCommand:"video status",pollAfterSeconds:20}},milestone:"M8",name:"video create",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"media",description:"Create a versioned Markdown video storyboard.",id:"createVideoStoryboard",lifecycle:{mode:"bounded"},milestone:"M8",name:"video storyboard create",operation:"mutation",scopes:["media:write"],state:"available"},{billing:"fixed",category:"artifacts",description:"Generate a persisted standalone visual abstract.",id:"createVisualAbstract",lifecycle:{mode:"background",status:{cliCommand:"visual-abstract status",pollAfterSeconds:10}},milestone:"M5",name:"visual-abstract create",operation:"generation",scopes:["visual-abstracts:write"],state:"available"},{billing:"free",category:"media",description:"Retire a saved cast preset. Existing videos keep working; the preset stops being offered.",id:"deleteCastPreset",lifecycle:{mode:"bounded"},milestone:"M8",name:"cast preset delete",operation:"mutation",scopes:["media:write"],state:"available"},{billing:"free",category:"knowledge",description:"Remove a document from the Library.",id:"deleteDocument",lifecycle:{mode:"bounded"},milestone:"M8",name:"document delete",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"free",category:"knowledge",description:"Delete a Library folder and its subfolders. Items inside are unfiled, never deleted.",id:"deleteLibraryFolder",lifecycle:{mode:"bounded"},milestone:"M1",name:"library folders delete",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"free",category:"artifacts",description:"Edit a standalone poll.",id:"editPoll",lifecycle:{mode:"bounded"},milestone:"M3",name:"poll edit",operation:"mutation",scopes:["polls:write"],state:"available"},{billing:"free",category:"presentations",description:"Apply one atomic edit to a presentation-video draft.",id:"editPresentationVideoDraft",lifecycle:{mode:"bounded"},milestone:"M4",name:"presentation video edit",operation:"mutation",scopes:["presentations:write","media:read"],state:"available"},{billing:"quota",category:"branding",description:"Extract and persist a reusable brand profile from a URL.",id:"extractBranding",lifecycle:{mode:"background",status:{cliCommand:"branding status",pollAfterSeconds:10}},milestone:"M2",name:"branding extract",operation:"generation",scopes:["brands:write"],state:"available"},{billing:"dynamic",category:"research",description:"Extract normalized text from uploaded or remote content.",id:"extractContent",lifecycle:{mode:"bounded"},milestone:"M1",name:"content extract",operation:"query",scopes:["research:read"],state:"available"},{billing:"free",category:"knowledge",description:"File an artifact into a Library folder, or unfile it.",id:"fileArtifactIntoFolder",lifecycle:{mode:"bounded"},milestone:"M1",name:"library file",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"free",category:"agents",description:"Erase every stored version of a scheduled agent's memory. Irreversible.",id:"forgetAgentMemory",lifecycle:{mode:"bounded"},milestone:"M3",name:"agent memory forget",operation:"mutation",scopes:["agents:write"],state:"available"},{billing:"delegated",category:"media",description:"Generate one reusable avatar/presenter clip for composition into a video. Accepts a stock avatar id or a personal presenter's pp_* id (from listPresenterOptions' `personal` group) as avatarId.",id:"generateAvatarVideo",lifecycle:{mode:"background",status:{cliCommand:"avatar status",pollAfterSeconds:10}},milestone:"M8",name:"avatar generate",operation:"generation",scopes:["media:write"],state:"available"},{billing:"dynamic",category:"media",description:"Generate music from final video facts or a trusted direct prompt.",id:"generateMusic",lifecycle:{mode:"background",status:{cliCommand:"music status",pollAfterSeconds:10}},milestone:"M7",name:"music generate",operation:"generation",scopes:["media:write"],state:"available"},{billing:"delegated",category:"media",description:"Generate an additional look for an existing personal presenter while preserving the same Cast identity.",id:"generatePresenterLook",lifecycle:{mode:"background",status:{cliCommand:"avatar look status",pollAfterSeconds:10}},milestone:"M8",name:"avatar look generate",operation:"generation",scopes:["media:write"],state:"available"},{billing:"dynamic",category:"media",description:"Generate SFX from final video facts or a trusted direct prompt.",id:"generateSfx",lifecycle:{mode:"background",status:{cliCommand:"sfx status",pollAfterSeconds:10}},milestone:"M7",name:"sfx generate",operation:"generation",scopes:["media:write"],state:"available"},{billing:"dynamic",category:"presentations",description:"Generate persisted speaker notes for a presentation version.",id:"generateSpeakerNotes",lifecycle:{mode:"background",status:{cliCommand:"presentation speaker-notes-status",pollAfterSeconds:5}},milestone:"M4",name:"presentation speaker-notes",operation:"generation",scopes:["presentations:write"],state:"available"},{billing:"dynamic",category:"media",description:"Generate speech audio from text.",id:"generateSpeech",lifecycle:{mode:"background",status:{cliCommand:"tts status",pollAfterSeconds:10}},milestone:"M7",name:"tts generate",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"agents",description:"Read what a scheduled agent has recorded about its own work: learned facts, standing instructions, things it never does, entities it tracks, and freeform notes.",id:"getAgentMemory",lifecycle:{mode:"bounded"},milestone:"M3",name:"agent memory show",operation:"query",scopes:["agents:read"],state:"available"},{billing:"free",category:"branding",description:"List competitors saved for an accessible brand profile.",id:"getBrandCompetitors",lifecycle:{mode:"bounded"},milestone:"M1",name:"branding competitor list",operation:"query",scopes:["brands:read"],state:"available"},{billing:"free",category:"branding",description:"Read a brand's design tokens, the keys it is missing, or its design.md document.",id:"getBrandProfile",lifecycle:{mode:"bounded"},milestone:"M2",name:"branding profile",operation:"query",scopes:["brands:read"],state:"available"},{billing:"free",category:"knowledge",description:"Read a bounded workspace Knowledge Hub bundle.",id:"getKnowledgeBundle",lifecycle:{mode:"bounded"},milestone:"M1",name:"knowledge bundle",operation:"query",scopes:["knowledge:read"],state:"available"},{billing:"dynamic",category:"research",description:"Search recent news sources.",id:"getNews",lifecycle:{mode:"bounded"},milestone:"M1",name:"search news",operation:"query",scopes:["research:read"],state:"available"},{billing:"free",category:"presentations",description:"Read a presentation and its slides.",id:"getPresentation",lifecycle:{mode:"bounded"},milestone:"M1",name:"get",operation:"query",scopes:["presentations:read"],state:"available"},{billing:"free",category:"presentations",description:"Read an owned presentation-video project and its ordered scene timeline.",id:"getPresentationVideoProject",lifecycle:{mode:"bounded"},milestone:"M4",name:"presentation video inspect",operation:"query",scopes:["presentations:read"],state:"available"},{billing:"dynamic",category:"research",description:"Fetch a YouTube video's timestamped transcript with its title, channel, and duration.",id:"getYoutubeTranscript",lifecycle:{mode:"bounded"},milestone:"M1",name:"transcript",operation:"query",scopes:["research:read"],state:"available"},{billing:"free",category:"media",description:"Import a recording from a connected account into the media library.",id:"importConnectorRecording",lifecycle:{mode:"background",status:{cliCommand:"media import status",pollAfterSeconds:5}},milestone:"M8",name:"media import",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"presentations",description:"Import an attached PowerPoint as an auditable, review-gated Mindframes presentation.",id:"importPowerPoint",lifecycle:{mode:"background",status:{cliCommand:"presentation import status",pollAfterSeconds:5}},milestone:"M4",name:"presentation import",operation:"generation",scopes:["presentations:write"],state:"available"},{billing:"delegated",category:"media",description:"Re-voice a ready video with new audio via provider lip-sync (one sibling re-voiced video artifact).",id:"lipsyncVideo",lifecycle:{mode:"background",status:{cliCommand:"video lipsync status",pollAfterSeconds:20}},milestone:"M8",name:"video lipsync",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"media",description:"List saved ways a cast member appears \u2014 a trained presenter, a backdrop, and optionally a voice, under one name. Filtered to what the caller may actually use.",id:"listCastPresets",lifecycle:{mode:"bounded"},milestone:"M8",name:"cast preset list",operation:"query",scopes:["media:read"],state:"available"},{billing:"free",category:"knowledge",description:"List the workspace's documents, newest first.",id:"listDocuments",lifecycle:{mode:"bounded"},milestone:"M8",name:"document list",operation:"query",scopes:["knowledge:read"],state:"available"},{billing:"free",category:"knowledge",description:"List a document's append-only version history.",id:"listDocumentVersions",lifecycle:{mode:"bounded"},milestone:"M8",name:"document versions",operation:"query",scopes:["knowledge:read"],state:"available"},{billing:"free",category:"knowledge",description:"List the workspace Library folder tree.",id:"listLibraryFolders",lifecycle:{mode:"bounded"},milestone:"M1",name:"library folders list",operation:"query",scopes:["knowledge:read"],state:"available"},{billing:"free",category:"presentations",description:"List saved presentation-video draft versions.",id:"listPresentationVideoVersions",lifecycle:{mode:"bounded"},milestone:"M4",name:"presentation video versions",operation:"query",scopes:["presentations:read"],state:"available"},{billing:"free",category:"media",description:"List provider-neutral presenter avatars, voices, and voice languages, plus the workspace's trained personal presenters (`personal` roster group, pp_* ids).",id:"listPresenterOptions",lifecycle:{mode:"bounded"},milestone:"M8",name:"avatar options",operation:"query",scopes:["media:read"],state:"available"},{billing:"free",category:"media",description:"List ready generated clips and uploaded MediaAssets while excluding final presentation-to-MP4 renders.",id:"listReusableVideoClips",lifecycle:{mode:"bounded"},milestone:"M8",name:"video library list",operation:"query",scopes:["media:read","presentations:read"],state:"available"},{billing:"free",category:"media",description:"List provider-neutral target languages for video translation.",id:"listTranslationLanguages",lifecycle:{mode:"bounded"},milestone:"M8",name:"video translate-languages",operation:"query",scopes:["media:read"],state:"available"},{billing:"dynamic",category:"media",description:"Mix voice, music, and sound-effect tracks.",id:"mixAudio",lifecycle:{mode:"background",status:{cliCommand:"mix status",pollAfterSeconds:10}},milestone:"M7",name:"mix create",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"knowledge",description:"Move a Library folder under a new parent, or to the top level.",id:"moveLibraryFolder",lifecycle:{mode:"bounded"},milestone:"M1",name:"library folders move",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"free",category:"presentations",description:"Create or reuse a presentation-video project for a finished presentation.",id:"preparePresentationVideo",lifecycle:{mode:"bounded"},milestone:"M4",name:"presentation video prepare",operation:"mutation",scopes:["presentations:write"],state:"available"},{billing:"free",category:"knowledge",description:"Read one document's full body and metadata.",id:"readDocument",lifecycle:{mode:"bounded"},milestone:"M8",name:"document read",operation:"query",scopes:["knowledge:read"],state:"available"},{billing:"free",category:"media",description:"Read a video storyboard and its immutable revision history.",id:"readVideoStoryboard",lifecycle:{mode:"bounded"},milestone:"M8",name:"video storyboard show",operation:"query",scopes:["media:read"],state:"available"},{billing:"free",category:"knowledge",description:"Set or clear the colour of a Library folder.",id:"recolorLibraryFolder",lifecycle:{mode:"bounded"},milestone:"M1",name:"library folders recolor",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"free",category:"agents",description:"Create a reviewable scheduled-agent recommendation.",id:"recommendAgent",lifecycle:{mode:"bounded"},milestone:"M3",name:"agent recommend",operation:"generation",scopes:["agents:write"],state:"available"},{billing:"free",category:"presentations",description:"Refresh deck-backed video scenes from the latest presentation version.",id:"refreshPresentationVideoSlides",lifecycle:{mode:"bounded"},milestone:"M4",name:"presentation video refresh",operation:"mutation",scopes:["presentations:write"],state:"available"},{billing:"dynamic",category:"presentations",description:"Regenerate one narration override in a presentation video draft.",id:"regeneratePresentationVideoNarration",lifecycle:{mode:"background",status:{cliCommand:"presentation video-narration-status",pollAfterSeconds:5}},milestone:"M4",name:"presentation video-narration",operation:"generation",scopes:["presentations:write"],state:"available"},{billing:"free",category:"knowledge",description:"Rename a Library folder.",id:"renameLibraryFolder",lifecycle:{mode:"bounded"},milestone:"M1",name:"library folders rename",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"free",category:"knowledge",description:"Restore an earlier version by appending it as the current one.",id:"restoreDocumentVersion",lifecycle:{mode:"bounded"},milestone:"M8",name:"document restore",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"free",category:"presentations",description:"Restore a saved presentation-video draft version as a new version.",id:"restorePresentationVideoVersion",lifecycle:{mode:"bounded"},milestone:"M4",name:"presentation video restore",operation:"mutation",scopes:["presentations:write"],state:"available"},{billing:"delegated",category:"media",description:"Create a new presenter Look by editing an existing Look without changing its source.",id:"revisePresenterLook",lifecycle:{mode:"background",status:{cliCommand:"avatar look revision-status",pollAfterSeconds:10}},milestone:"M8",name:"avatar look revise",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"media",description:"Create a new immutable revision of a Markdown video storyboard.",id:"reviseVideoStoryboard",lifecycle:{mode:"bounded"},milestone:"M8",name:"video storyboard revise",operation:"mutation",scopes:["media:write"],state:"available"},{billing:"dynamic",category:"research",description:"Read and normalize content from a public URL.",id:"scrape",lifecycle:{mode:"bounded"},milestone:"M1",name:"scrape",operation:"query",scopes:["research:read"],state:"available"},{billing:"dynamic",category:"research",description:"Search licensed image providers.",id:"searchImages",lifecycle:{mode:"bounded"},milestone:"M1",name:"search images",operation:"query",scopes:["research:read"],state:"available"},{billing:"dynamic",category:"research",description:"Search the public web for current sources.",id:"searchInternet",lifecycle:{mode:"bounded"},milestone:"M1",name:"search web",operation:"query",scopes:["research:read"],state:"available"},{billing:"free",category:"knowledge",description:"Search workspace Knowledge Hub entries.",id:"searchKnowledge",lifecycle:{mode:"bounded"},milestone:"M1",name:"knowledge search",operation:"query",scopes:["knowledge:read"],state:"available"},{billing:"dynamic",category:"media",description:"Find videos: licensed stock clips to download, or web videos to embed.",id:"searchStockVideo",lifecycle:{mode:"bounded"},milestone:"M1",name:"search video",operation:"query",scopes:["media:read"],state:"available"},{billing:"free",category:"branding",description:"Point a brand at an already-uploaded logo, dark-background logo variant, or favicon.",id:"setBrandLogo",lifecycle:{mode:"bounded"},milestone:"M2",name:"branding logo set",operation:"mutation",scopes:["brands:write"],state:"available"},{billing:"free",category:"presentations",description:"Set one presentation-video scene to a specific avatar/look or saved preset, including the preset voice by default, or restore inheritance from the project default.",id:"setPresentationVideoScenePresenter",lifecycle:{mode:"bounded"},milestone:"M4",name:"presentation video presenter",operation:"mutation",scopes:["presentations:write"],state:"available"},{billing:"free",category:"presentations",description:"Transform an uploaded PowerPoint source into an auditable presentation review.",id:"transformPresentationImport",lifecycle:{mode:"background",status:{cliCommand:"presentation import status",pollAfterSeconds:5}},milestone:"M4",name:"presentation import transform",operation:"generation",scopes:["presentations:write"],state:"available"},{billing:"delegated",category:"media",description:"Translate a ready video into sibling language versions (one video per target language).",id:"translateVideo",lifecycle:{mode:"background",status:{cliCommand:"video translate status",pollAfterSeconds:20}},milestone:"M8",name:"video translate",operation:"generation",scopes:["media:write"],state:"available"},{billing:"free",category:"agents",description:"Add to or remove from what a scheduled agent remembers. Owner or workspace admin only.",id:"updateAgentMemory",lifecycle:{mode:"bounded"},milestone:"M3",name:"agent memory set",operation:"mutation",scopes:["agents:write"],state:"available"},{billing:"free",category:"branding",description:"Write a brand's design tokens from a key patch or a whole design.md document.",id:"updateBrandTokens",lifecycle:{mode:"bounded"},milestone:"M2",name:"branding set",operation:"mutation",scopes:["brands:write"],state:"available"},{billing:"free",category:"media",description:"Change a saved cast preset's name, presenter, backdrop, or voice. Sends the complete preset, not a patch.",id:"updateCastPreset",lifecycle:{mode:"bounded"},milestone:"M8",name:"cast preset update",operation:"mutation",scopes:["media:write"],state:"available"},{billing:"free",category:"knowledge",description:"Edit a document's body, title or summary under an optimistic lock.",id:"updateDocument",lifecycle:{mode:"bounded"},milestone:"M8",name:"document update",operation:"mutation",scopes:["knowledge:write"],state:"available"},{billing:"free",category:"presentations",description:"Update avatar, voice, music, captions, format, and renderer settings for a presentation-video project.",id:"updatePresentationVideoSettings",lifecycle:{mode:"bounded"},milestone:"M4",name:"presentation video settings",operation:"mutation",scopes:["presentations:write"],state:"available"}];function Rt(e){return{capabilities:To,catalogVersion:1,reason:e,source:"snapshot"}}async function nt(e=Gr){try{let{capabilities:t,catalogVersion:n}=await e();return!Array.isArray(t)||t.length===0?Rt("the live catalog returned no capabilities"):{capabilities:t,catalogVersion:n,source:"live"}}catch(t){let n=t instanceof Error?t.message:String(t);return Rt(n)}}var Eo=["presentations","media","branding","knowledge","research","artifacts","agents"],Ws=80,Ks=30;function Js(e){return e.billing!==void 0&&e.billing!=="free"&&e.billing!=="quota"}function zs(e){return e.lifecycle?.mode==="background"}function Xs(e,t){if(t<=0)return[e];let n=[],r="";for(let o of e.split(/\s+/).filter(Boolean))r===""?r=o:r.length+1+o.length<=t?r=`${r} ${o}`:(n.push(r),r=o);return r!==""&&n.push(r),n.length>0?n:[""]}function Ao(e){let t=Eo.indexOf(e);return t===-1?Eo.length:t}function Hs(e,t){let n=t.search?.trim().toLowerCase(),r=t.category?.trim().toLowerCase();return e.filter(o=>r&&(o.category??"").toLowerCase()!==r?!1:n?`${o.name} ${o.description??""}`.toLowerCase().includes(n):!0)}function Ys(e){let t=new Map;for(let i of e){let a=i.category??"other",s=t.get(a);s?s.push(i):t.set(a,[i])}let n=[...t.entries()].sort(([i],[a])=>{let s=Ao(i)-Ao(a);return s!==0?s:i.localeCompare(a)}),r=Math.min(Ks,Math.max(...e.map(i=>i.name.length))),o=[];for(let[i,a]of n){a.sort((s,l)=>s.name.localeCompare(l.name)),o.length>0&&o.push(""),o.push(rt.bold(`${i} (${a.length})`));for(let s of a){let l=`${Js(s)?"$":" "}${zs(s)?"*":" "}`,g=s.name.padEnd(r),m=2+r+1+l.length+1,w=Math.max(24,Ws-m),v=Xs(s.description??"",w);o.push(` ${rt.cyan(g)} ${rt.yellow(l)} ${v[0]??""}`.trimEnd());for(let b of v.slice(1))o.push(`${" ".repeat(m)}${b}`)}}return o}function Qs(e){return e.map(t=>`${t.name} ${t.state} ${t.milestone}`)}function Zs(e,t={filtered:!1}){if(e.length===0)return[t.filtered?"No capabilities match that filter.":"No capabilities available."];if(!e.some(i=>i.description||i.category))return Qs(e);let r=u.commands[0],o=Ys(e);return o.push(""),o.push(rt.dim(`${e.length} capabilities \xB7 $ = paid \xB7 * = background (returns a run id)`)),o.push(rt.dim(`Filter with: ${r} capabilities --search <query>`)),o}var Oo=new Bs("capabilities").description("Index of everything this CLI can do \u2014 every command, grouped and described").option("-f, --format <format>","Output format: human, json, quiet","human").option("-s, --search <query>","Only show capabilities matching a query").option("--category <name>","Only show one category").action(async e=>{try{let t=await nt(),n=Hs(t.capabilities,e),r=!!(e.search||e.category);if(e.format==="json"){p({capabilities:n,catalogVersion:t.catalogVersion,source:t.source,...t.reason?{sourceReason:t.reason}:{}});return}if(e.format==="quiet"){for(let o of n)console.log(o.name);return}t.source==="snapshot"&&M(`Showing the capability list baked into this CLI build (${t.reason}); the deployed catalog may have moved on.`);for(let o of Zs(n,{filtered:r}))console.log(o)}catch(t){d(t instanceof Error?t.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}});import{mkdirSync as rd,writeFileSync as od}from"fs";import{dirname as Do,join as id}from"path";import{Command as he}from"commander";import ot from"chalk";import{input as ad,password as sd,confirm as No,select as dd}from"@inquirer/prompts";import $t from"ora";import ed from"conf";var St=new ed({projectName:"conceptcraft",configName:"feature-cache"}),td=3600*1e3,nd=1440*60*1e3;function Po(e){let t=0;for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);t=(t<<5)-t+r,t=t&t}return t.toString(16)}function _o(){if(!Pe())return null;let e=St.get("featureFlags");if(!e)return null;let t=Po(ue());if(e.apiKeyHash&&e.apiKeyHash!==t)return Re(),null;let n=Date.now()-e.fetchedAt;return n>nd?null:(n>td&&In(),e.flags)}function In(){Ie().catch(()=>{})}async function Ie(){let e=await no(),t=ue();return St.set("featureFlags",{flags:e,fetchedAt:Date.now(),apiKeyHash:t?Po(t):void 0}),e}function Re(){St.delete("featureFlags")}function Lo(){return St.path}var Uo=new he("config").description("Manage CLI configuration: account settings, and the cached feature flags that decide which commands appear").addCommand(new he("init").description("Initialize configuration interactively").action(async()=>{console.log(),console.log(ot.bold(`${u.displayName} CLI Configuration`)),console.log(ot.gray("\u2500".repeat(35))),console.log();try{let e=await sd({message:"Enter your API key:",mask:"*",validate:r=>!r||r.trim().length===0?"API key is required":hn(r.trim())?!0:"Invalid API key format. Keys should start with 'cc_slides_' or 'mcp_'"});if(un(e.trim()),await No({message:`Use a custom API URL? (default: ${u.apiUrl.replace("https://","")})`,default:!1})){let r=await ad({message:"Enter API URL:",default:V(),validate:o=>{try{return new URL(o),!0}catch{return"Invalid URL format"}}});mn(r)}console.log();let n=$t("Verifying API key...").start();try{let r=await me();if(n.succeed("API key verified!"),console.log(),f(`Logged in as: ${r.user.email}`),r.teams.length===0)M("No teams found for this user.");else if(r.teams.length===1){let o=r.teams[0];Oe(o.id),f(`Team: ${o.name} (${o.planName})`)}else{console.log(),f(`You have access to ${r.teams.length} teams.`);let o=await dd({message:"Select your default team:",choices:r.teams.map(a=>({name:`${a.name} (${a.planName}) - ${a.role}${a.isCurrent?" [current]":""}`,value:a.id})),default:r.currentTeam?.id});Oe(o);let i=r.teams.find(a=>a.id===o);C(`Selected team: ${i?.name}`)}try{await Ie()}catch{}}catch(r){n.fail("Failed to verify API key"),M(`Could not verify API key: ${r instanceof Error?r.message:String(r)}`),M(`You may need to set the team ID manually: ${u.commands[0]} config set team-id <id>`)}console.log(),C("Configuration saved!"),f(`Config file: ${et()}`),console.log()}catch(e){if(e.name==="ExitPromptError"){console.log(),f("Configuration cancelled.");return}throw e}})).addCommand(new he("show").description("Show current configuration").option("--verify","Verify API key and show team details").action(async e=>{let t=kr();if(oe("Current Configuration"),console.log(),S("API Key",Dr()??ot.red("Not set")),S("API URL",t.apiUrl),S("Default Team ID",t.defaultTeamId??ot.gray("Not set")),console.log(),S("Config file",et()),e.verify&&t.apiKey){console.log();let n=$t("Verifying...").start();try{let r=await me();n.succeed("Verified"),console.log(),S("User",r.user.email),r.currentTeam&&S("Current Team",`${r.currentTeam.name} (${r.currentTeam.planName})`),r.teams.length>1&&S("Total Teams",String(r.teams.length))}catch(r){n.fail("Verification failed"),M(r instanceof Error?r.message:String(r))}}console.log()})).addCommand(new he("set").description("Set a configuration value").argument("<key>","Configuration key (api-key, api-url, team-id)").argument("<value>","Value to set").action(async(e,t)=>{switch(e){case"api-key":hn(t)||(d("Invalid API key format. Keys should start with 'cc_slides_' or 'mcp_'"),process.exit(6)),un(t),Re(),C("API key updated"),Ie().catch(()=>{});break;case"api-url":try{new URL(t),mn(t),C(`API URL set to: ${t}`)}catch{d("Invalid URL format"),process.exit(6)}break;case"team-id":Oe(t),C(`Default team ID set to: ${t}`);break;default:d(`Unknown config key: ${e}`),console.log(ot.gray("Valid keys: api-key, api-url, team-id")),process.exit(6)}})).addCommand(new he("clear").description("Clear all configuration").action(async()=>{try{await No({message:"Are you sure you want to clear all configuration?",default:!1})?(wt(),Re(),C("Configuration cleared")):f("Cancelled")}catch{f("Cancelled")}})).addCommand(new he("refresh").description("Refresh cached feature flags").action(async()=>{let e=$t("Refreshing feature flags...").start();try{await Ie(),e.succeed("Feature flags refreshed"),f(`Run '${u.commands[0]} --help' to see updated commands`)}catch(t){e.fail("Failed to refresh"),d(t instanceof Error?t.message:String(t)),process.exit(1)}})).addCommand(new he("path").description("Show configuration file path").option("--cache","Show feature cache file path").action(e=>{e.cache?console.log(Lo()):console.log(et())})).addCommand(new he("pull").description("Pull your account settings (memory, brand, prefs) into a local config.toml").action(async()=>{let e=$t("Pulling settings...").start();try{let t=await se(),n=await fetch(`${V()}/api/cli/settings`,{headers:t});if(!n.ok)throw new Error(`Request failed (HTTP ${n.status})`);let r=await n.text(),o=id(Do(et()),"settings.toml");rd(Do(o),{recursive:!0}),od(o,r,"utf8"),e.succeed("Settings pulled"),f(`Written to ${o}`)}catch(t){e.fail("Failed to pull settings"),d(t instanceof Error?t.message:String(t)),process.exit(1)}}));import{Command as cd}from"commander";import{confirm as ld}from"@inquirer/prompts";var qo=new cd("delete").description("Delete a presentation").argument("<slug>","Presentation slug (e.g., my-presentation-v1-abc123)").option("-f, --force","Skip confirmation prompt").option("-q, --quiet","Suppress output").action(async(e,t)=>{if(await A(),!t.force)try{if(!await ld({message:`Are you sure you want to delete presentation "${e}"?`,default:!1})){t.quiet||M("Deletion cancelled");return}}catch{t.quiet||M("Deletion cancelled");return}try{await Hr(e),t.quiet||C(`Presentation "${e}" deleted`)}catch(n){d(n instanceof Error?n.message:String(n)),process.exit(n.exitCode??1)}});import{Command as Le}from"commander";import Fo from"chalk";import ud from"ora";import{createParser as vf}from"eventsource-parser";import kf from"chalk";import If from"ora";async function Mo(e,t={}){let n=e.body?.getReader();if(!n)throw new Error("Response body is not readable");let r=new TextDecoder,o="",i=!1;for(;!i;){let a=await n.read();if(i=a.done,a.value){let s=r.decode(a.value,{stream:!0});o+=s,t.quiet||process.stdout.write(s)}}return t.quiet||console.log(),o}function md(){return new Le("blog").description("Generate a blog post from a presentation").argument("<slug>","Presentation slug").option("--words <count>","Target word count (50-2000)","300").option("--tone <tone>","Writing tone (professional, casual, educational)","professional").option("--audience <text>","Target audience description").option("-f, --format <format>","Output format (human, json, markdown)","human").option("--instructions <text>","Custom instructions for the blog").option("--no-images","Exclude image references").option("--toc","Include table of contents").action(async(e,t)=>{await A();let n=parseInt(t.words,10);(Number.isNaN(n)||n<50||n>2e3)&&(d("Word count must be between 50 and 2000"),process.exit(6));let r=["professional","casual","educational"];r.includes(t.tone)||(d(`Invalid tone. Valid options: ${r.join(", ")}`),process.exit(6));let o=ud("Fetching presentation...").start();try{let i=await fe(e);o.text="Generating blog post...";let a=await ro(i.id,i.documentCreatedAt||i.createdAt,{targetWordCount:n,tone:t.tone,targetAudience:t.audience??"General Audience",customInstructions:t.instructions??"",includeImages:!t.noImages,includeTableOfContents:t.toc??!1}),s=await Mo(a,{quiet:!0});o.succeed("Blog generated"),t.format==="json"?console.log(JSON.stringify({presentationId:e,title:i.title,wordCount:n,tone:t.tone,content:s},null,2)):t.format==="markdown"?console.log(s):(console.log(),console.log(Fo.bold(`Blog: ${i.title}`)),console.log(Fo.gray("\u2500".repeat(40))),console.log(),console.log(s),console.log())}catch(i){o.fail("Generation failed"),d(i instanceof Error?i.message:String(i)),process.exit(i.exitCode??1)}})}function pd(){return new Le("tweets").description("Generate tweets from a presentation").argument("<slug>","Presentation slug").option("--count <n>","Number of tweets to generate","5").option("-f, --format <format>","Output format (human, json)","human").action(async(e,t)=>{await A(),f("Tweet generation coming soon.")})}function gd(){return new Le("linkedin").description("Generate a LinkedIn carousel from a presentation").argument("<slug>","Presentation slug").option("-f, --format <format>","Output format (human, json)","human").action(async e=>{await A(),f("LinkedIn carousel generation coming soon.")})}function fd(){return new Le("questions").description("Generate questions for a presentation").argument("<slug>","Presentation slug").option("--count <n>","Number of questions","10").option("-f, --format <format>","Output format (human, json)","human").action(async(e,t)=>{await A(),f("Question generation coming soon.")})}function hd(){return new Le("cheatsheet").description("Generate a presenter cheat sheet").argument("<slug>","Presentation slug").option("--mode <mode>","Cheat sheet mode (qa-prep, talking-points)","qa-prep").option("-f, --format <format>","Output format (human, json, markdown)","human").action(async(e,t)=>{await A(),f("Cheat sheet generation coming soon.")})}function Vo(){let e=new Le("derive").description("Generate derivative content from a presentation"),t=_o();return t&&(t.deckToBlog&&e.addCommand(md()),t.deckToTweets&&e.addCommand(pd()),t.linkedInCarousel&&e.addCommand(gd()),t.redTeamQuestions&&e.addCommand(fd()),t.presenterCheatSheet&&e.addCommand(hd())),e}import Tt from"chalk";import{Command as yd}from"commander";import{writeFile as bd}from"fs/promises";import{resolve as jo}from"path";import wd from"ora";var Rn=new yd("export").description("Export a presentation to a ZIP of HTML and its assets").argument("<slug>","Presentation slug (e.g., my-presentation-v1-abc123)").option("-o, --output <path>","Output file path").option("--include-images","Include images (default: true)",!0).option("--include-branding","Include branding (default: true)",!0).option("--no-external","Skip external image downloads").action(async(e,t)=>{await A();let n=wd("Fetching presentation...").start();try{let r=await fe(e),o=r.title||e,i=r.id,a=e.replace(/[^a-z0-9-]/gi,"_").toLowerCase().substring(0,50),s=new Date().toISOString().split("T")[0],l=`${a}_${s}.zip`,g=t.output?jo(t.output):jo(process.cwd(),l);n.text=`Exporting "${o}"...`;let m=await Yr(i,{includeImages:t.includeImages,includeBranding:t.includeBranding,includeExternal:!t.noExternal});n.text="Saving file...",await bd(g,Buffer.from(m)),n.succeed("Export complete!"),console.log(),f(`File saved: ${g}`),f(`Size: ${vd(m.byteLength)}`),console.log()}catch(r){n.fail("Export failed"),d(r instanceof Error?r.message:String(r)),process.exit(r.exitCode??1)}});function vd(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}Rn.addHelpText("after",`
|
|
5
|
+
${Tt.bold("Examples:")}
|
|
6
|
+
${Tt.gray("# HTML + assets ZIP of a deck")}
|
|
7
|
+
$ ${u.commands[0]} export <slug> -o deck.zip
|
|
8
|
+
|
|
9
|
+
${Tt.bold("See also:")}
|
|
10
|
+
${Tt.gray("# For an MP4 instead of a ZIP")}
|
|
11
|
+
$ ${u.commands[0]} presentation video <slug>
|
|
12
|
+
`);import{Command as xd}from"commander";import Sn from"chalk";var Go=new xd("ideas").description("Generate presentation topic ideas").option("--context <text>","Custom context for idea generation").option("--count <n>","Number of ideas to generate","5").option("-f, --format <format>","Output format (human, json)","human").action(async e=>{await A(),f("Idea generation is available in the web dashboard."),console.log(),console.log(Sn.gray("The CLI will support idea generation in a future release.")),console.log(),console.log("For now, try these approaches:"),console.log(Sn.gray("` 1. Visit the ${brand.displayName} dashboard and use the idea generator`")),console.log(Sn.gray(" 2. Create a presentation with a broad topic and refine it")),console.log()});import{Command as kd}from"commander";import{readFile as Cd}from"fs/promises";import{resolve as Id,basename as Rd}from"path";import Sd from"ora";var Bo=new kd("import").description("Import a presentation from ZIP (admin only)").argument("<file>","Path to ZIP file");Bo._hidden=!0;var Wo=Bo.option("--dry-run","Validate without importing").option("--overwrite","Overwrite existing presentations").option("--remap-ids","Generate new IDs (default: true)",!0).action(async(e,t)=>{await A();try{(await me()).user.role!=="admin"&&(d("This command is only available to administrators"),process.exit(2))}catch{d("Failed to verify permissions"),process.exit(2)}let n=Id(e),r=Rd(n);r.endsWith(".zip")||(d("File must be a ZIP archive"),process.exit(6));let o=Sd("Reading file...").start();try{let i=await Cd(n);t.dryRun?o.text="Validating...":o.text="Importing presentation...";let a=await Qr(i,r,{dryRun:t.dryRun});if(a.success){if(t.dryRun)o.succeed("Validation passed!"),console.log(),f("The file is valid and can be imported."),a.statistics&&(console.log(),S("Slides",String(a.statistics.slidesImported)),S("Images",String(a.statistics.imagesUploaded)));else if(o.succeed("Import complete!"),console.log(),S("Presentation ID",a.presentationId??"N/A"),S("Document ID",a.documentId??"N/A"),a.statistics&&(S("Slides imported",String(a.statistics.slidesImported)),S("Images uploaded",String(a.statistics.imagesUploaded)),S("Time",`${a.statistics.totalTimeMs}ms`)),a.presentationId){let s=V();S("View URL",`${s}/p/${a.presentationId}`)}if(a.warnings&&a.warnings.length>0){console.log();for(let s of a.warnings)M(s)}console.log()}else{if(o.fail("Import failed"),console.log(),a.errors)for(let s of a.errors)d(`${s.type}: ${s.message}`);process.exit(1)}}catch(i){o.fail("Import failed"),i.code==="ENOENT"&&(d(`File not found: ${n}`),process.exit(3)),d(i instanceof Error?i.message:String(i)),process.exit(i.exitCode??1)}});import{Command as $d}from"commander";var Ko=new $d("list").description("List presentations").option("-n, --limit <count>","Number of results to return","20").option("-f, --format <format>","Output format (table, json, ids)","table").option("--sort <field>","Sort by field (created, updated, title)").option("--team-id <id>","Team ID (uses default if not specified)").option("-d, --detail","Show detailed output including URLs").option("-l, --language <lang>","Language for URLs","en").action(async e=>{await A();let t=parseInt(e.limit,10);(Number.isNaN(t)||t<1)&&(d("Invalid limit value"),process.exit(6));let n=e.teamId??bt();n||(d("`Team ID required. Set a default with '${brand.commands[0]} config set team-id <id>' or use --team-id`"),process.exit(6));try{let r=await Jr(n,t);if(r.length===0){e.format==="json"?console.log(JSON.stringify([])):e.format!=="ids"&&f("No presentations found");return}switch(e.sort&&r.sort((o,i)=>{switch(e.sort){case"created":return new Date(i.createdAt).getTime()-new Date(o.createdAt).getTime();case"title":return(o.title||"").localeCompare(i.title||"");default:return 0}}),e.format){case"json":console.log(JSON.stringify(r,null,2));break;case"ids":console.log(Ar(r));break;default:console.log(Er(r,{showLinks:e.detail,language:e.language}));break}}catch(r){d(r instanceof Error?r.message:String(r)),process.exit(r.exitCode??1)}});import{createHash as Td,randomBytes as Xo}from"crypto";import Ho from"http";import Jo from"chalk";import{Command as Ed}from"commander";import Ad from"open";import zo from"ora";var Od=`${u.displayName} CLI`,Pd=8765,_d=8775,Ld=["presentations:read","presentations:write","media:read","media:write"];function Dd(){return Xo(32).toString("base64url")}function Nd(e){return Td("sha256").update(e).digest("base64url")}function Ud(){return Xo(16).toString("hex")}async function qd(e,t){for(let n=e;n<=t;n++)try{return await new Promise((r,o)=>{let i=Ho.createServer();i.listen(n,()=>{i.close(()=>r())}),i.on("error",o)}),n}catch{}throw new Error(`No available port found between ${e} and ${t}`)}async function Md(e){let t=await fetch(`${e}/.well-known/oauth-authorization-server`);if(!t.ok)throw new Error(`Failed to fetch OAuth metadata: ${t.status}`);return t.json()}function Fd(e){let t=e.scopes_supported?.filter(n=>typeof n=="string"&&n.trim().length>0);return(t?.length?t:Ld).join(" ")}async function Vd(e,t,n){let r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_name:Od,redirect_uris:[t],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none",scope:n})});if(!r.ok){let o=await r.text();throw new Error(`Client registration failed: ${o}`)}return r.json()}async function jd(e,t,n,r,o){let i=new URLSearchParams({grant_type:"authorization_code",code:t,redirect_uri:r,client_id:o,code_verifier:n}),a=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()});if(!a.ok){let s=await a.text();throw new Error(`Token exchange failed: ${s}`)}return a.json()}function Gd(e,t){return new Promise((n,r)=>{let o,i=!1,a=()=>{i||(i=!0,clearTimeout(o),process.off("SIGINT",s),process.off("SIGTERM",s),l.close())},s=()=>{a(),r(new Error("Login cancelled"))},l=Ho.createServer((g,m)=>{let w=new URL(g.url||"",`http://localhost:${e}`);if(w.pathname!=="/callback"){m.writeHead(404),m.end("Not found");return}let v=w.searchParams.get("code"),b=w.searchParams.get("state"),$=w.searchParams.get("error"),x=w.searchParams.get("error_description");if(m.writeHead(200,{"Content-Type":"text/html"}),$){m.end(`
|
|
5
13
|
<!DOCTYPE html>
|
|
6
14
|
<html>
|
|
7
15
|
<head><title>Login Failed</title></head>
|
|
8
16
|
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
9
17
|
<h1 style="color: #dc2626;">Login Failed</h1>
|
|
10
|
-
<p>${
|
|
18
|
+
<p>${x||$}</p>
|
|
11
19
|
<p>You can close this window.</p>
|
|
12
20
|
</body>
|
|
13
21
|
</html>
|
|
14
|
-
`),a(),r(new Error(
|
|
22
|
+
`),a(),r(new Error(x||$));return}if(!v||!b){m.end(`
|
|
15
23
|
<!DOCTYPE html>
|
|
16
24
|
<html>
|
|
17
25
|
<head><title>Login Failed</title></head>
|
|
@@ -21,7 +29,7 @@ var f={id:"mindframes",name:"mindframes",displayName:"Mindframes",description:"C
|
|
|
21
29
|
<p>You can close this window.</p>
|
|
22
30
|
</body>
|
|
23
31
|
</html>
|
|
24
|
-
`),a(),r(new Error("Missing authorization code or state"));return}if(
|
|
32
|
+
`),a(),r(new Error("Missing authorization code or state"));return}if(b!==t){m.end(`
|
|
25
33
|
<!DOCTYPE html>
|
|
26
34
|
<html>
|
|
27
35
|
<head><title>Login Failed</title></head>
|
|
@@ -31,7 +39,7 @@ var f={id:"mindframes",name:"mindframes",displayName:"Mindframes",description:"C
|
|
|
31
39
|
<p>You can close this window.</p>
|
|
32
40
|
</body>
|
|
33
41
|
</html>
|
|
34
|
-
`),a(),r(new Error("State mismatch"));return}
|
|
42
|
+
`),a(),r(new Error("State mismatch"));return}m.end(`
|
|
35
43
|
<!DOCTYPE html>
|
|
36
44
|
<html>
|
|
37
45
|
<head><title>Login Successful</title></head>
|
|
@@ -40,591 +48,541 @@ var f={id:"mindframes",name:"mindframes",displayName:"Mindframes",description:"C
|
|
|
40
48
|
<p>You can close this window and return to the terminal.</p>
|
|
41
49
|
</body>
|
|
42
50
|
</html>
|
|
43
|
-
`),a(),n({code:
|
|
44
|
-
`)[0]??"";n(a?{bin:t,version:a}:null)})})}var
|
|
51
|
+
`),a(),n({code:v,state:b})});l.listen(e),process.once("SIGINT",s),process.once("SIGTERM",s),o=setTimeout(()=>{a(),r(new Error("Login timed out - no callback received within 5 minutes"))},300*1e3)})}async function Bd(e,t){let n=await fetch(`${e}/api/cli/whoami`,{headers:{Authorization:`Bearer ${t}`}});if(!n.ok)throw new Error(`Failed to fetch user info: ${n.status}`);return n.json()}async function Wd(e){let t=V(),n=zo("Discovering OAuth server...").start();try{let r=await Md(t),o=Fd(r);n.succeed(`Connecting to ${t}`);let i=await qd(Pd,_d),a=`http://localhost:${i}/callback`;console.log(`Redirect URI: ${a}`),console.log(`Authorization endpoint: ${r.authorization_endpoint}`),console.log(`Token endpoint: ${r.token_endpoint}`),console.log(`Registration endpoint: ${r.registration_endpoint}`);let s=await Vd(r.registration_endpoint,a,o),l=s.client_id;Tr(l,s.client_secret);let g=Dd(),m=Nd(g),w=Ud(),v=new URLSearchParams({client_id:l,redirect_uri:a,response_type:"code",scope:o,state:w,code_challenge:m,code_challenge_method:"S256"}),b=`${r.authorization_endpoint}?${v}`;console.log(`Authorization URL: ${b}`),e.browser?(f("Opening browser..."),await Ad(b)):(console.log(Jo.bold("Open this URL in your browser:")),console.log(Jo.cyan(b)));let $=Gd(i,w),{code:x}=await $;n=zo("Completing login...").start();let k=await jd(r.token_endpoint,x,g,a,l);vt(k.access_token,k.refresh_token,k.expires_in);let R=await Bd(t,k.access_token);n.succeed("Logged in!"),console.log(),S("Logged in as",R.user.email),R.currentTeam&&(Oe(R.currentTeam.id),S("Team",`${R.currentTeam.name} (${R.currentTeam.planName})`));try{await Ie()}catch{}console.log(),C("You're all set!"),console.log()}catch(r){throw n.fail("Login failed"),d(r instanceof Error?r.message:String(r)),r}}var Yo=new Ed("login").description(`Authenticate with ${u.displayName} (opens browser)`).option("--no-browser","Print URL instead of opening browser").action(async e=>{console.log(),ke()&&(M("You are already logged in."),f(`Run '${u.commands[0]} logout' to log out first, or continue to re-authenticate.`),console.log());try{await Wd(e),process.exit(0)}catch{process.exit(1)}});import{Command as Kd}from"commander";import{confirm as Jd}from"@inquirer/prompts";var Qo=new Kd("logout").description("Log out and clear authentication").option("--all","Clear all config including API key").action(async e=>{console.log();let t=ke(),n=Pe();if(!t&&!n){M("You are not logged in."),console.log();return}if(e.all)try{await Jd({message:"Clear all configuration including API key?",default:!1})?(wt(),Re(),C("All configuration cleared.")):f("Cancelled.")}catch{f("Cancelled.")}else xt(),Re(),C("Logged out successfully."),n&&f("Note: Your API key is still configured. Use --all to clear everything.");console.log()});import{homedir as Pc}from"os";import{join as _c}from"path";import ne from"chalk";import{Command as Lc}from"commander";import{spawn as zd}from"child_process";import{existsSync as Xd,readdirSync as Hd}from"fs";import{homedir as ti}from"os";import{delimiter as Zo}from"path";import{query as Yd}from"@anthropic-ai/claude-agent-sdk";function Z(e){if(e===void 0)return null;if(typeof e=="string")return e.length>12e3?`${e.slice(0,12e3)}\u2026`:e;try{let t=JSON.stringify(e);return t.length<=12e3?e:{preview:`${t.slice(0,12e3)}\u2026`,truncated:!0}}catch{return String(e).slice(0,12e3)}}function Et(e){let t=typeof e=="string"?e:String(e??"");return t.length>12e3?`${t.slice(0,12e3)}\u2026`:t}function ee(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function _(e){return typeof e=="string"?e:""}function ei(e,t){let n=e.replace(/[/.]/g,"-");return`${ti()}/.claude/projects/${n}/${t}.jsonl`}function Qd(e){try{return Hd(`${e}/.nvm/versions/node`).map(t=>`${e}/.nvm/versions/node/${t}/bin`).sort().reverse()}catch{return[]}}function At(){let e=ti(),t=[`${e}/.local/bin`,`${e}/.claude/local`,`${e}/.bun/bin`,`${e}/bin`,"/opt/homebrew/bin","/usr/local/bin",...Qd(e)],r=(process.env.PATH??"").split(Zo);for(let o of t)r.includes(o)||r.push(o);return r.join(Zo)}async function Ot(e){let t=e??"claude";return new Promise(n=>{let r=zd(t,["--version"],{env:{...process.env,PATH:At()},stdio:["ignore","pipe","ignore"]}),o="";r.stdout.on("data",i=>{o+=i.toString("utf8")}),r.on("error",()=>n(null)),r.on("close",i=>{if(i!==0)return n(null);let a=o.trim().split(`
|
|
52
|
+
`)[0]??"";n(a?{bin:t,version:a}:null)})})}var Zd=new Set(["","default","best"]);function $n(e){let t=e.filter(i=>i.text.trim());if(t.length===0)return"";let n=t[t.length-1],r=t.slice(0,-1);return r.length===0?n.text:`<conversation-history>
|
|
45
53
|
${r.map(i=>`${i.role==="assistant"?"Assistant":"User"}: ${i.text}`).join(`
|
|
46
54
|
|
|
47
55
|
`)}
|
|
48
56
|
</conversation-history>
|
|
49
57
|
|
|
50
|
-
${n.text}`}function
|
|
51
|
-
`)[0]??"";t(i||null)})})}var
|
|
52
|
-
`)}catch{return[]}let n=new Set,r=new Map,o=null;for(let i of t){if(!i.trim())continue;let a;try{a=JSON.parse(i)}catch{continue}let s=
|
|
53
|
-
`);return a===-1?"":i.slice(a+1)}finally{
|
|
54
|
-
`):e&&typeof e=="object"?Object.values(e).map(
|
|
55
|
-
`):""}function
|
|
58
|
+
${n.text}`}function Tn(e,t){return t?e.messages.filter(r=>r.text.trim()).at(-1)?.text??"":$n(e.messages)}function ec({abortController:e,input:t,resumeSessionId:n,workspace:r}){let o=(t.modelId??"").trim(),i=t.thinking??null,a={abortController:e,allowedTools:["WebSearch","WebFetch"],cwd:r,env:{...process.env,PATH:At()},includePartialMessages:!0,maxTurns:16,permissionMode:"dontAsk"};return Zd.has(o)||(a.model=o),t.effort&&(a.effort=t.effort),i&&i!=="disabled"&&(a.thinking={display:"summarized",type:i==="enabled"?"enabled":"adaptive"}),n?a.resume=n:t.system?.trim()&&(a.systemPrompt=t.system),a}async function ni(e){let t=Tn(e.input,e.resumeSessionId);if(!t)throw new Error("Chat turn has no user text to send.");let n=new AbortController,r=()=>n.abort();e.signal?.addEventListener("abort",r,{once:!0});let o=null,i=null,a=null,s=null,l=null,g=0,m=new Set;try{let b=Yd({prompt:t,options:ec({abortController:n,input:e.input,resumeSessionId:e.resumeSessionId,workspace:e.workspace})});for await(let $ of b){let x=$;if(x.type==="system"&&x.subtype==="init"){typeof x.session_id=="string"&&(i=x.session_id,e.onEvent({eventType:"chat.session",payload:{sessionId:i,sessionPath:ei(e.workspace,i)}})),typeof x.model=="string"&&(o=x.model,e.onEvent({eventType:"chat.model",payload:{modelId:x.model}}));continue}if(x.type==="stream_event"){let k=x.event;if(k?.type!=="content_block_delta")continue;let R=k.delta;if(!R)continue;R.type==="text_delta"&&typeof R.text=="string"?(g+=R.text.length,e.onEvent({eventType:"chat.text.delta",payload:{text:R.text}})):R.type==="thinking_delta"&&typeof R.thinking=="string"&&e.onEvent({eventType:"chat.reasoning.delta",payload:{text:R.thinking}});continue}if(x.type==="assistant"){let k=ee(x.message),R=Array.isArray(k.content)?k.content:[];for(let D of R){let E=ee(D);if(E.type!=="tool_use")continue;let U=_(E.id),I=_(E.name);!U||!I||m.has(U)||(m.add(U),e.onEvent({eventType:"chat.tool.started",payload:{input:Z(E.input),toolCallId:U,toolName:I}}))}continue}if(x.type==="user"){let k=ee(x.message),R=Array.isArray(k.content)?k.content:[];for(let D of R){let E=ee(D);if(E.type!=="tool_result")continue;let U=_(E.tool_use_id);U&&e.onEvent({eventType:"chat.tool.completed",payload:{...E.is_error===!0?{errorText:typeof E.content=="string"?Et(E.content):"Claude tool call failed."}:{},output:Z(E.content),toolCallId:U}})}continue}x.type==="result"&&(typeof x.result=="string"&&(s=x.result),x.usage&&typeof x.usage=="object"&&(a=x.usage),typeof x.session_id=="string"&&(i=x.session_id),(x.is_error===!0||x.subtype!=="success")&&(l=typeof x.result=="string"&&x.result||`Claude turn failed (${String(x.subtype)}).`,e.onEvent({eventType:"chat.error",message:l,payload:{message:l}})))}}finally{e.signal?.removeEventListener("abort",r)}if(e.signal?.aborted)throw new Error("Chat turn cancelled.");if(l)throw new Error(l);let w=s??"";g===0&&w&&e.onEvent({eventType:"chat.text.delta",payload:{text:w}});let v=i?ei(e.workspace,i):null;return{modelId:o,sessionId:i,sessionPath:v&&Xd(v)?v:null,text:w,usage:a}}import{spawn as tc}from"child_process";import{closeSync as nc,fstatSync as rc,openSync as oc,readdirSync as ic,readSync as ac}from"fs";import{homedir as sc}from"os";import{join as ri}from"path";import{Codex as dc}from"@openai/codex-sdk";var cc=["/Applications/Codex.app/Contents/Resources/codex"];async function Pt(e){let t=e?[e]:["codex",...cc];for(let n of t){let r=await lc(n);if(r)return{bin:n,version:r}}return null}function lc(e){return new Promise(t=>{let n=tc(e,["--version"],{env:{...process.env,PATH:At()},stdio:["ignore","pipe","ignore"]}),r="";n.stdout.on("data",o=>{r+=o.toString("utf8")}),n.on("error",()=>t(null)),n.on("close",o=>{if(o!==0)return t(null);let i=r.trim().split(`
|
|
59
|
+
`)[0]??"";t(i||null)})})}var uc=24,mc=4*1024*1024,ai=new Set(["custom_tool_call","custom_tool_call_output","dynamic_tool_call","function_call"]);function pc(e){let t;try{t=gc(e).split(`
|
|
60
|
+
`)}catch{return[]}let n=new Set,r=new Map,o=null;for(let i of t){if(!i.trim())continue;let a;try{a=JSON.parse(i)}catch{continue}let s=ee(a.payload);if(a.type==="response_item"&&s.type==="custom_tool_call"){let l=_(s.call_id),g=_(s.input);l&&s.name==="exec"&&/\btools\.web__run\s*\(/.test(g)&&(n.add(l),o=l);continue}if(a.type==="event_msg"&&s.type==="web_search_end"){let l=_(s.call_id);if(!l||!o)continue;let g=_(s.query),m=ee(s.action),v={action:{...m,...g&&!_(m.query)?{query:g}:{}},query:g,sources:oi(s.results),toolCallId:l},b=r.get(o)??[];b.push(v),r.set(o,b);continue}if(a.type==="response_item"&&s.type==="custom_tool_call_output"){let l=_(s.call_id);if(!l||!n.has(l))continue;let g=oi(s.output);for(let m of r.get(l)??[])m.sources.length===0&&(m.sources=g);o===l&&(o=null)}}return Array.from(r.values()).flat()}function gc(e){let t=oc(e,"r");try{let n=rc(t).size,r=Math.max(0,n-mc),o=Buffer.alloc(n-r);ac(t,o,0,o.length,r);let i=o.toString("utf8");if(r===0)return i;let a=i.indexOf(`
|
|
61
|
+
`);return a===-1?"":i.slice(a+1)}finally{nc(t)}}function oi(e){let n=On(e).match(/https?:\/\/[^\s<>"'()[\]{}\\]+/g)??[],r=new Set,o=[];for(let i of n){let a=i.replace(/[.,;:!?]+$/,"");try{let s=new URL(a);if(s.protocol!=="http:"&&s.protocol!=="https:"||r.has(s.href))continue;if(r.add(s.href),o.push({type:"url",url:s.href}),o.length>=uc)break}catch{}}return o}function On(e){return typeof e=="string"?e:Array.isArray(e)?e.map(On).join(`
|
|
62
|
+
`):e&&typeof e=="object"?Object.values(e).map(On).join(`
|
|
63
|
+
`):""}function En(e,t){let n=ee(e),r=_(n.type),o=_(n.id),i=_(n.call_id)||_(n.tool_call_id),a=ai.has(r)?i||o:o||i;if(!a)return null;if(r==="command_execution"){let s=_(n.command),l=_(n.status),g={exitCode:typeof n.exit_code=="number"?n.exit_code:null,output:Z(n.aggregated_output)};return{...t&&l==="failed"?{errorText:Et(n.aggregated_output)||"Command failed."}:{},input:{command:s},...t?{output:g}:{},toolCallId:a,toolName:"Bash"}}if(r==="mcp_tool_call"){let s=ee(n.error);return{...t&&_(s.message)?{errorText:_(s.message)}:{},input:Z(n.arguments),...t?{output:Z(n.result??{status:n.status??"completed"})}:{},toolCallId:a,toolName:_(n.tool)||_(n.name)||"Codex tool"}}if(r==="web_search"){let s=_(n.query);return{input:{query:s},...t?{output:{query:s}}:{},toolCallId:a,toolName:"web_search"}}if(r==="file_change"){let s=_(n.status);return{...t&&s==="failed"?{errorText:"Codex could not apply the file changes."}:{},input:{changes:Z(n.changes)},...t?{output:{changes:Z(n.changes),status:s}}:{},toolCallId:a,toolName:"ApplyPatch"}}if(r==="custom_tool_call_output")return{input:{},...t?{output:Z(n.output)}:{},toolCallId:a,toolName:"Codex tool"};if(r==="custom_tool_call"||r==="dynamic_tool_call"||r==="function_call"){let s=_(n.name)||_(n.tool)||"Codex tool",l=n.input??n.arguments??{};return{input:s==="exec"&&typeof l=="string"?{command:l}:Z(l),...t?{output:Z(n.output??n.result??{status:n.status??"completed"})}:{},toolCallId:a,toolName:s}}return null}function An(e,t=ri(sc(),".codex","sessions")){try{let r=ic(t,{recursive:!0}).find(o=>typeof o=="string"&&o.endsWith(`${e}.jsonl`));return r?ri(t,r):null}catch{return null}}function fc(e,t){return t?Tn(e,t):$n(e.messages)}function hc(e,t,n=u.codexSelfConnectorAppId){let r=t.system?.trim(),o={...r?{developer_instructions:r}:{},...n?{apps:{[n]:{enabled:!1}}}:{}};return{codexPathOverride:e,...Object.keys(o).length>0?{config:o}:{}}}var yc=new Set(["minimal","low","medium","high","xhigh"]);function bc(e,t){let n={networkAccessEnabled:!0,sandboxMode:"workspace-write",skipGitRepoCheck:!0,webSearchEnabled:!0,workingDirectory:t},r=(e.modelId??"").trim();return r&&r!=="default"&&(n.model=r),e.effort&&yc.has(e.effort)&&(n.modelReasoningEffort=e.effort),n}async function si(e){let t=fc(e.input,e.resumeSessionId);if(!t)throw new Error("Chat turn has no user text to send.");let n=new dc(hc(e.bin,e.input)),r=bc(e.input,e.workspace),o=e.resumeSessionId?n.resumeThread(e.resumeSessionId,r):n.startThread(r),i=e.resumeSessionId??null,a=null,s=null,l=[],g=new Set,m=new Set,w=null,v=x=>{w??=An(x,e.sessionsRoot),e.onEvent({eventType:"chat.session",payload:{sessionId:x,sessionPath:w}})},b=x=>{i&&(w??=An(i,e.sessionsRoot));let k=w?pc(w):[],R=_(ee(x.input).query),D=ii(R),E=k.find(U=>!g.has(U.toolCallId)&&ii(U.query)===D)??k.find(U=>!g.has(U.toolCallId));E&&g.add(E.toolCallId),e.onEvent({eventType:"chat.tool.completed",payload:{...x,output:E?{action:E.action,query:E.query,sources:E.sources}:{action:{query:R,type:"search"},sourceListUnavailable:!0,sources:[]}}})},{events:$}=await o.runStreamed(t);i&&v(i);for await(let x of $){if(e.signal?.aborted)throw await $.return?.(void 0),new Error("Chat turn cancelled.");let k=x;if(k.type==="thread.started"){typeof k.thread_id=="string"&&(i=k.thread_id,v(k.thread_id));continue}if(k.type==="item.started"){let R=En(k.item,!1);if(R){e.onEvent({eventType:"chat.tool.started",payload:R});let D=ee(k.item);if(ai.has(_(D.type))&&_(D.status)==="completed"){let E=En(D,!0);E&&(m.add(E.toolCallId),e.onEvent({eventType:"chat.tool.completed",payload:E}))}}continue}if(k.type==="item.completed"){let R=k.item;if(!R)continue;if(R.type==="agent_message"&&typeof R.text=="string")l.push(R.text),e.onEvent({eventType:"chat.text.delta",payload:{text:R.text}});else if(R.type==="reasoning"&&typeof R.text=="string")e.onEvent({eventType:"chat.reasoning.delta",payload:{text:R.text}});else{let D=En(R,!0);D&&(R.type==="web_search"?b(D):R.type==="custom_tool_call"&&m.has(D.toolCallId)||e.onEvent({eventType:"chat.tool.completed",payload:D}))}continue}if(k.type==="turn.completed"){k.usage&&typeof k.usage=="object"&&(a=k.usage);continue}if(k.type==="turn.failed"){let R=k.error;s=typeof R?.message=="string"&&R.message||"Codex turn failed.",e.onEvent({eventType:"chat.error",message:s,payload:{message:s}})}}if(s)throw new Error(s);return i=o.id??i,i&&(w??=An(i,e.sessionsRoot)),{modelId:e.input.modelId??null,sessionId:i,sessionPath:w,text:l.join(`
|
|
64
|
+
|
|
65
|
+
`),usage:a}}function ii(e){return e.trim().replace(/\s+/g," ").toLocaleLowerCase()}import{randomUUID as Cc}from"crypto";import{mkdir as Ic}from"fs/promises";import{hostname as Lt,platform as Rc}from"os";import{join as Sc}from"path";var De=class extends Error{constructor(n,r,o=!1){super(n);this.statusCode=r;this.reRegister=o;this.name="RunnerApiError"}statusCode;reRegister};async function Ne(e,t={}){let n=await se();t.body!==void 0&&(n["Content-Type"]="application/json");let r=new AbortController,o=setTimeout(()=>r.abort(),t.timeoutMs??3e4),i=()=>r.abort();t.signal?.addEventListener("abort",i,{once:!0});try{let a=await fetch(`${V()}${e}`,{body:t.body!==void 0?JSON.stringify(t.body):void 0,headers:n,method:t.method??"GET",signal:r.signal}),s=await a.text(),l={};try{l=s?JSON.parse(s):{}}catch{l={error:s.slice(0,300)}}if(!a.ok)throw new De(typeof l.error=="string"?l.error:`Runner API returned ${a.status}`,a.status,l.reRegister===!0);return l}finally{clearTimeout(o),t.signal?.removeEventListener("abort",i)}}async function di(e){await Ne("/api/runner/register",{body:e,method:"POST",timeoutMs:15e3})}async function ci(e,t){return(await Ne(`/api/runner/work?runnerId=${encodeURIComponent(e)}`,{signal:t,timeoutMs:28e3})).job??null}async function li(e){await Ne("/api/runner/heartbeat",{body:{runnerId:e},method:"POST",timeoutMs:1e4})}async function ui(e,t){return{cancelRequested:(await Ne(`/api/runner/jobs/${encodeURIComponent(e)}/events`,{body:{events:t},method:"POST",timeoutMs:15e3})).cancelRequested===!0}}async function Pn(e,t,n){await Ne(`/api/runner/jobs/${encodeURIComponent(e)}/complete`,{body:{result:n,runnerId:t},method:"POST",timeoutMs:15e3})}async function _t(e,t,n,r=!0){await Ne(`/api/runner/jobs/${encodeURIComponent(e)}/fail`,{body:{message:n,retryable:r,runnerId:t},method:"POST",timeoutMs:15e3})}import{mkdirSync as wc,readFileSync as vc,writeFileSync as mi}from"fs";import{dirname as xc,join as kc}from"path";function _n(e){return kc(e,"sessions.json")}function Ln(e){try{let t=vc(_n(e),"utf8"),n=JSON.parse(t);if(n&&typeof n=="object"&&!Array.isArray(n))return n}catch{}return{}}function pi(e,t,n){return t?Ln(e)[t]?.[n]??null:null}function gi(e,t,n,r){if(!(!t||!r))try{let o=_n(e);wc(xc(o),{recursive:!0});let i=Ln(e);i[t]={...i[t],[n]:r},mi(o,`${JSON.stringify(i,null," ")}
|
|
66
|
+
`,"utf8")}catch{}}function fi(e,t,n){if(t)try{let r=Ln(e);if(!r[t])return;delete r[t][n],mi(_n(e),`${JSON.stringify(r,null," ")}
|
|
67
|
+
`,"utf8")}catch{}}var $c=300,Tc=3e4;async function hi(e){let[t,n]=await Promise.all([Ot(e.claudeBin),Pt(e.codexBin)]);if(!t&&!n)throw new Error("Neither Claude Code nor Codex was found on this machine. Install one and log in, then retry.");t?e.log(`Claude Code detected: ${t.version} (${t.bin})`):e.log("Claude Code not found \u2014 Claude turns will be declined."),n?e.log(`Codex detected: ${n.version} (${n.bin})`):e.log("Codex not found \u2014 Codex turns will be declined.");let r=[...t?["claude-code"]:[],...n?["codex"]:[]],o=`${Lt().split(".")[0]}-${Cc().slice(0,8)}`,i=()=>di({capabilities:["chat_turn","proof"],claudeCodeVersion:t?.version??null,cliVersion:e.version,codexVersion:n?.version??null,hostname:Lt(),platform:Rc(),runnerId:o,runtimes:r});for(await i(),e.log(`Registered as ${o} (runtimes: ${r.join(", ")}). Waiting for work\u2026`);!e.signal?.aborted;){let a=null;try{a=await ci(o,e.signal)}catch(s){if(e.signal?.aborted)break;if(s instanceof De&&s.reRegister){e.log("Server asked for re-registration."),await i().catch(()=>{});continue}e.log(`Work poll failed (${s instanceof Error?s.message:"unknown"}); retrying\u2026`),await Oc(e.pollBackoffMs??5e3,e.signal);continue}if(!a){if(e.once)continue;continue}if(e.log(`Claimed ${a.jobType} job ${a.runId}.`),await Ec({claude:t,codex:n,job:a,options:e,runnerId:o}),e.once)break}}async function Ec({claude:e,codex:t,job:n,options:r,runnerId:o}){let i=new AbortController,a=()=>i.abort();r.signal?.addEventListener("abort",a,{once:!0});let s=setInterval(()=>{li(o).catch(()=>{})},Tc),l=[],g=!1,m=async()=>{if(g||l.length===0)return;g=!0;let b=l.splice(0,100);try{let{cancelRequested:$}=await ui(n.runId,b);$&&i.abort()}catch($){$ instanceof De&&$.statusCode===409&&i.abort()}finally{g=!1}},w=setInterval(()=>{m()},$c),v=b=>{l.push(b)};try{if(n.jobType==="runner.proof"){let b=typeof n.input.message=="string"?n.input.message:"proof";v({eventType:"proof.echo",payload:{message:b}}),await Dn(m,l),await Pn(n.runId,o,{echo:b}),r.log(`Proof job ${n.runId} completed.`);return}if(n.jobType==="runner.chat_turn"){let b=n.input.runtime==="codex"?"codex":"claude-code",$=b==="codex"?t&&{detection:t,run:si}:e&&{detection:e,run:ni};if(!$){await _t(n.runId,o,b==="codex"?"Codex is not available on this machine. Install the Codex app or CLI and log in, then retry.":"Claude Code is not available on this machine. Install it and log in, then retry.",!1);return}let x=typeof n.input.chatId=="string"&&n.input.chatId?n.input.chatId:null,k=Sc(r.workspaceRoot,"chats",x??n.runId);await Ic(k,{recursive:!0});let R=Ac(n.input),D=I=>v({eventType:I.eventType,message:I.message??null,payload:I.eventType==="chat.session"?{host:Lt(),runtime:b,runtimeVersion:$.detection.version,...I.payload??{}}:I.payload??{}}),E=pi(r.workspaceRoot,x,b),U;try{U=await $.run({bin:$.detection.bin,input:R,onEvent:D,resumeSessionId:E,signal:i.signal,workspace:k})}catch(I){if(!E||i.signal.aborted)throw I;r.log(`Resume of ${b} session failed (${I instanceof Error?I.message:"unknown"}); retrying fresh.`),fi(r.workspaceRoot,x,b),U=await $.run({bin:$.detection.bin,input:R,onEvent:D,resumeSessionId:null,signal:i.signal,workspace:k})}gi(r.workspaceRoot,x,b,U.sessionId),v({eventType:"chat.session",payload:{host:Lt(),runtime:b,runtimeVersion:$.detection.version,sessionId:U.sessionId,sessionPath:U.sessionPath}}),await Dn(m,l),await Pn(n.runId,o,{modelId:U.modelId,runtime:b,runtimeVersion:$.detection.version,sessionId:U.sessionId,sessionPath:U.sessionPath,text:U.text,usage:U.usage??{}}),r.log(`Chat turn ${n.runId} (${b}) completed.`);return}await _t(n.runId,o,`This runner does not support ${n.jobType} jobs. Update the desktop app / CLI.`,!1)}catch(b){let $=b instanceof Error?b.message:"Local runner job crashed.";r.log(`Job ${n.runId} failed: ${$}`),await Dn(m,l).catch(()=>{}),await _t(n.runId,o,$).catch(()=>{})}finally{clearInterval(w),clearInterval(s),r.signal?.removeEventListener("abort",a)}}function Ac(e){let t=Array.isArray(e.messages)?e.messages.map(n=>{if(!n||typeof n!="object")return null;let r=n;return{role:typeof r.role=="string"?r.role:"user",text:typeof r.text=="string"?r.text:""}}).filter(n=>!!n):[];return{effort:typeof e.effort=="string"?e.effort:null,messages:t,modelId:typeof e.modelId=="string"?e.modelId:null,system:typeof e.system=="string"?e.system:null,thinking:typeof e.thinking=="string"?e.thinking:null}}async function Dn(e,t){for(;t.length>0;)await e(),t.length>0&&await new Promise(n=>setTimeout(n,25))}function Oc(e,t){return e<=0||t?.aborted?Promise.resolve():new Promise(n=>{let r=setTimeout(o,e);function o(){clearTimeout(r),t?.removeEventListener("abort",o),n()}t?.addEventListener("abort",o,{once:!0})})}function Dc(){return _c(Pc(),`.${u.commands[0]}-runner`,"runs")}var Dt=new Lc("runner").description("Run agent work locally with your own Claude Code / Codex login (desktop lane)");Dt.command("status").description("Check local Claude Code / Codex detection and account auth").action(async()=>{let[e,t]=await Promise.all([Ot(),Pt()]);if(console.log(e?ne.green(`\u2713 Claude Code ${e.version}`):ne.red("\u2717 Claude Code not found on PATH")),console.log(t?ne.green(`\u2713 Codex ${t.version} (${t.bin})`):ne.red("\u2717 Codex not found (PATH or Codex.app)")),!ie()){console.log(ne.red(`\u2717 Not logged in. Run '${u.commands[0]} login' first.`)),process.exitCode=2;return}try{let n=await me();console.log(ne.green(`\u2713 Logged in as ${n.user.email}`))}catch(n){console.log(ne.red(`\u2717 Auth check failed: ${n instanceof Error?n.message:"unknown"}`)),process.exitCode=2}});Dt.command("start").description("Start the local runner (register + wait for work)").option("--once","Execute a single job, then exit").option("--workspace <dir>","Workspace root for job scratch directories").option("--claude-bin <path>","Path to the claude binary").option("--codex-bin <path>","Path to the codex binary").action(async e=>{ie()||(console.error(ne.red(`Not authenticated. Run '${u.commands[0]} login' first.`)),process.exit(2));let t=new AbortController,n=()=>{console.log(ne.gray(`
|
|
68
|
+
Stopping runner\u2026`)),t.abort()};process.on("SIGINT",n),process.on("SIGTERM",n);try{await hi({claudeBin:e.claudeBin,codexBin:e.codexBin,log:r=>console.log(ne.gray(`[runner] ${r}`)),once:e.once,signal:t.signal,version:"0.3.18",workspaceRoot:e.workspace??Dc()})}catch(r){console.error(ne.red(r instanceof Error?r.message:String(r))),process.exit(1)}});import q from"chalk";import{Command as $p}from"commander";import{Command as Nn}from"commander";function Nc(e,t){return[...t,e]}var Uc=new Nn("create").description("Start durable ad-set generation").argument("<title>","Ad-set title").requiredOption("--brief <brief>","Campaign brief").option("--hook <text>","Opening hook (repeat)",Nc,[]).option("--style <style>","ugc, clean, cinematic, or testimonial","ugc").option("--branding-id <id>","Branding ID").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(e,t)=>{if(!t.yes)return d("Pass --yes to approve generation");try{p(await y("createAdSet",{approvalReceipt:"cli:--yes",brandingId:t.brandingId,brief:t.brief,hooks:t.hook.length?t.hook:[t.brief],style:t.style,teamId:t.teamId,title:e}))}catch(n){d(n instanceof Error?n.message:"Generation failed"),process.exit(c.GENERAL_ERROR)}}),qc=new Nn("status").description("Read ad-set generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await T("createAdSet",e,t.teamId))}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),yi=new Nn("ad-set").description("Generate a set of ad creatives (hooks, copy, visuals) from a campaign brief").addCommand(Uc).addCommand(qc);import{randomUUID as Mc}from"crypto";import{Command as Ue}from"commander";function bi(e){return new Ue(e).description(e==="create"?"Create and activate a scheduled agent":"Create a reviewable agent recommendation").argument("<name>","Agent name").requiredOption("--instructions <markdown>","Agent instructions").option("--description <text>","Agent description","").option("--schedule <frequency>","manual, daily, weekdays, weekly, or monthly","weekly").option("--time <hh:mm>","Local run time","09:00").option("--timezone <zone>","IANA timezone","UTC").option("--output <adapter>","Output adapter","digest").option("--team-id <id>","Workspace ID").option("--yes","Approve saving the agent").action(async(t,n)=>{if(!n.yes)return d("Pass --yes to approve this operation");try{p(await y(e==="create"?"createAgent":"recommendAgent",{approvalReceipt:"cli:--yes",description:n.description,instructionsMarkdown:n.instructions,name:t,output:n.output,schedule:n.schedule,scheduleTime:n.time,teamId:n.teamId,timezone:n.timezone}))}catch(r){d(r instanceof Error?r.message:"Agent operation failed"),process.exit(c.GENERAL_ERROR)}})}function qn(e,t){d(e instanceof Error?e.message:t),process.exit(c.GENERAL_ERROR)}function wi(e){return`cli:agent-memory:${e}:${Mc()}`}function Fc(e,t){let n=e.indexOf("="),r=n===-1?e:e.slice(0,n),o=n===-1?"":e.slice(n+1);return[...t,{name:r.trim(),note:o.trim()}]}function Un(e,t){return[...t,e]}var Vc=new Ue("show").description("Show what a scheduled agent remembers").argument("<agent-id>","Agent ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await y("getAgentMemory",{agentId:e,teamId:t.teamId}))}catch(n){qn(n,"Could not read the agent's memory")}}),jc=new Ue("set").description("Add to or remove from what an agent remembers. Owner or workspace admin only.").argument("<agent-id>","Agent ID").option("--learned <text>","Durable fact to add (repeatable)",Un,[]).option("--always <text>","Standing instruction to add (repeatable)",Un,[]).option("--never <text>","Thing it must not do, to add (repeatable)",Un,[]).option("--track <name=note>","Entity to track, as name=note (repeatable)",Fc,[]).option("--notes <text>","Replace the freeform notes").option("--remove <field>","Remove instead of add: learned, recurringInstructions, negativePreferences, entities, or notes").option("--value <text>","Item to remove; not needed when removing notes").option("--expected-version <n>","Refuse the write unless the stored memory is still at this version").option("--team-id <id>","Workspace ID").action(async(e,t)=>{let n=typeof t.remove=="string";try{p(await y("updateAgentMemory",{agentId:e,entities:t.track.length?t.track:void 0,expectedVersion:t.expectedVersion===void 0?void 0:Number.parseInt(t.expectedVersion,10),learned:t.learned.length?t.learned:void 0,negativePreferences:t.never.length?t.never:void 0,notes:t.notes,operation:n?"remove":"add",recurringInstructions:t.always.length?t.always:void 0,removeField:t.remove,removeValue:t.value,teamId:t.teamId},wi(n?"remove":"add")))}catch(r){qn(r,"Could not update the agent's memory")}}),Gc=new Ue("forget").description("Erase every stored version of an agent's memory. This cannot be undone.").argument("<agent-id>","Agent ID").option("--team-id <id>","Workspace ID").option("--yes","Confirm the erase").action(async(e,t)=>{if(!t.yes)return d("Pass --yes to erase this agent's memory");try{p(await y("forgetAgentMemory",{agentId:e,teamId:t.teamId},wi("forget")))}catch(n){qn(n,"Could not erase the agent's memory")}}),Bc=new Ue("memory").description("Read, edit, and erase what a scheduled agent remembers").addCommand(Vc).addCommand(jc).addCommand(Gc),vi=new Ue("agent").description("Create scheduled agents and read, edit, or erase what they remember").addCommand(bi("create")).addCommand(bi("recommend")).addCommand(Bc);import{randomUUID as Ci}from"crypto";import{existsSync as Jc}from"fs";import{resolve as zc}from"path";import it from"chalk";import{Command as pe}from"commander";import Xc from"ora";import{mkdir as Wc,writeFile as xi}from"fs/promises";import{dirname as Kc}from"path";async function z(e,t){if(await Wc(Kc(t),{recursive:!0}),e.startsWith("data:")){let o=e.match(/^data:[^;]+;base64,(.+)$/);if(!o)throw new Error("Invalid data URL format");let i=Buffer.from(o[1],"base64");if(i.length===0)throw new Error("Downloaded media was empty");await xi(t,i);return}let n=await fetch(e);if(!n.ok)throw new Error(`Failed to download media: ${n.status}`);let r=Buffer.from(await n.arrayBuffer());if(r.length===0)throw new Error("Downloaded media was empty");await xi(t,r)}var Hc=15e3,Yc=40,ki=3;async function Ii(e,t){if(e?.length){if(e.length>ki)throw new Error(`A presenter Look accepts at most ${ki} reference images`);return Promise.all(e.map(async(n,r)=>{if(Jc(n))return{url:(await de(zc(n),{idempotencyKey:`${t}:reference:${r+1}`})).url};let o;try{o=new URL(n)}catch(i){throw new Error(`Reference image ${r+1} must be an existing local file or an HTTP(S) URL.`,{cause:i})}if(o.protocol!=="http:"&&o.protocol!=="https:")throw new Error(`Reference image ${r+1} must use an HTTP(S) URL.`);return{url:o.toString()}}))}}function Mn(e,t){if(t==="json"){p(e);return}if(t==="quiet"){console.log(e.videoUrl??e.requestId);return}f(`Request ID: ${e.requestId}`),f(`Status: ${e.status}`),e.durationSec!=null&&f(`Duration: ${e.durationSec}s`),e.videoUrl&&C(`Video URL: ${e.videoUrl}`),e.error&&d(`Error: ${e.error}`)}var Qc=new pe("options").description("List available presenter avatars and voices").option("--language <language>","Filter voices by language").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{try{let t=await ho(e.language);if(e.format==="json"){p(t);return}let n=t.personal??[];if(e.format==="quiet"){for(let r of n)console.log(r.id);for(let r of t.avatars)console.log(r.id);for(let r of t.voices)console.log(r.id);return}if(n.length>0){f("Your presenters:");for(let r of n)console.log(` ${r.id} ${r.name}`)}f("Avatars:");for(let r of t.avatars){let o=r.renderTiers?.length?` [${r.renderTiers.join(", ")}]`:"";console.log(` ${r.id} ${r.name}${o}`)}f("Voices:");for(let r of t.voices)console.log(` ${r.id} ${r.name}${r.language?` (${r.language})`:""}`)}catch(t){d(t instanceof Error?t.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Zc=new pe("generate").description("Generate a reusable presenter clip").option("--avatar-id <id>","Avatar id from 'avatar options'; sandbox runs use the host presenter policy").option("--audio-url <url>","Public narration audio URL for avatar lip sync").option("--voice-id <id>","Voice id from 'avatar options'").option("--script <text>","Exact words for the presenter to speak").requiredOption("-o, --output <path>","Local output path").option("--aspect <aspect>","Aspect: 1:1, 9:16, 16:9","9:16").option("--duration <seconds>","Maximum requested duration (1-300)").option("--transparent","Render a transparent-background WebM presenter clip").option("--background-color <hex>","Solid presenter background color, for example #0F172A").option("--background-image-url <url>","Public image URL used as the presenter background").option("--fit <mode>","Background image fit: contain or cover").option("--output-format <container>","Container: mp4 (default) or webm \u2014 webm renders a transparent-background clip (single scene) for compositing").option("--render-tier <tier>","Render cost/quality: basic, standard, premium","basic").option("--title <title>","Clip title","Presenter clip").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.transparent?"webm":e.outputFormat;!!e.audioUrl==!!e.script&&(d("Provide exactly one avatar speech input: --audio-url or --script"),process.exit(c.INVALID_INPUT)),["1:1","9:16","16:9"].includes(e.aspect)||(d("Aspect must be one of 1:1, 9:16, or 16:9"),process.exit(c.INVALID_INPUT)),e.outputFormat!==void 0&&!["mp4","webm"].includes(e.outputFormat)&&(d("Output format must be mp4 or webm"),process.exit(c.INVALID_INPUT)),e.backgroundColor!==void 0&&!/^#[0-9a-fA-F]{6}$/.test(e.backgroundColor)&&(d("Background color must be a six-digit hex value"),process.exit(c.INVALID_INPUT)),e.backgroundColor&&e.backgroundImageUrl&&(d("Provide only one presenter background"),process.exit(c.INVALID_INPUT)),e.fit!==void 0&&!["contain","cover"].includes(e.fit)&&(d("Fit must be contain or cover"),process.exit(c.INVALID_INPUT)),e.fit&&!e.backgroundImageUrl&&(d("Fit requires --background-image-url"),process.exit(c.INVALID_INPUT)),e.transparent&&e.outputFormat==="mp4"&&(d("--transparent cannot be combined with --output-format mp4"),process.exit(c.INVALID_INPUT)),t==="webm"&&(e.backgroundColor||e.backgroundImageUrl)&&(d("Transparent WebM output cannot be combined with a background"),process.exit(c.INVALID_INPUT)),["basic","standard","premium"].includes(e.renderTier)||(d("Render tier must be basic, standard, or premium"),process.exit(c.INVALID_INPUT));let n=e.duration?Number.parseInt(e.duration,10):void 0;n!==void 0&&(!Number.isFinite(n)||n<1||n>300)&&(d("Duration must be between 1 and 300 seconds"),process.exit(c.INVALID_INPUT));let r=e.format==="human"?Xc("Generating presenter clip...").start():null;try{let o=e.audioUrl?{audioUrl:e.audioUrl}:{script:e.script??"",...e.voiceId?{voiceId:e.voiceId}:{}},i=await yo({...o,aspect:e.aspect,...e.avatarId?{avatarId:e.avatarId}:{},...e.backgroundColor?{backgroundHex:e.backgroundColor}:{},...e.backgroundImageUrl?{backgroundImageUrl:e.backgroundImageUrl}:{},...n!==void 0?{durationSec:n}:{},...e.fit?{fit:e.fit}:{},...t?{outputFormat:t}:{},renderTier:e.renderTier,title:e.title});if(!e.wait){r?.stop(),Mn(i,e.format);return}let a=await Q(()=>Cn(i.requestId),Yc,Hc);if(a.status==="failed")throw new Error(a.error||"Presenter generation failed");if(!a.videoUrl)throw new Error("Presenter generation completed without a video URL");r&&(r.text="Downloading presenter clip..."),await z(a.videoUrl,e.output),r?.stop(),Mn(a,e.format),e.format==="human"&&C(`Saved to: ${e.output}`)}catch(o){r?.stop(),d(o instanceof Error?o.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),el=new pe("status").description("Check a presenter clip generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{Mn(await Cn(e),t.format)}catch(n){d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),tl=new pe("generate").description("Generate another look for an existing personal presenter").argument("<base-profile-id>","Base personal presenter id (pp_*)").requiredOption("--name <name>","Look name").requiredOption("--description <description>","Short look description").requiredOption("--prompt <prompt>","Exact appearance generation prompt").requiredOption("--look-id <id>","Stable template/look id").option("--reference-image <path-or-url...>","Up to three local images or URLs for location, outfit, or style guidance").option("--idempotency-key <key>","Stable retry key for this look request").action(async(e,t)=>{try{let n=t.idempotencyKey??Ci(),r=await Ii(t.referenceImage,n),o=await y("generatePresenterLook",{baseProfileId:e,description:t.description,id:t.lookId,prompt:t.prompt,...r?{referenceImages:r}:{},title:t.name},n);p(o)}catch(n){d(n instanceof Error?n.message:"Look generation failed"),process.exit(c.GENERAL_ERROR)}}),nl=new pe("status").description("Read presenter look generation status").argument("<run-id>","Durable run ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await T("generatePresenterLook",e);if(t.format==="json")return p(n);if(t.format==="quiet")return console.log(n.status);f(`Run ID: ${e}`),f(`Status: ${n.status}`),n.error?.message&&d(n.error.message)}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),rl=new pe("revise").description("Create a new Look by editing an existing presenter Look").argument("<look-id>","Ready presenter Look id (pp_*)").requiredOption("--instruction <instruction>","What to add, remove, or change").option("--reference-image <path-or-url...>","Up to three local images or URLs for location, outfit, or style guidance").option("--idempotency-key <key>","Stable retry key for this revision").action(async(e,t)=>{try{let n=t.idempotencyKey??Ci(),r=await Ii(t.referenceImage,n),o=await y("revisePresenterLook",{instruction:t.instruction,...r?{referenceImages:r}:{},sourceLookId:e},n);p(o)}catch(n){d(n instanceof Error?n.message:"Look revision failed"),process.exit(c.GENERAL_ERROR)}}),ol=new pe("revision-status").description("Read presenter Look revision status").argument("<run-id>","Durable run ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await T("revisePresenterLook",e);if(t.format==="json")return p(n);if(t.format==="quiet")return console.log(n.status);f(`Run ID: ${e}`),f(`Status: ${n.status}`),n.error?.message&&d(n.error.message)}catch(n){d(n instanceof Error?n.message:"Revision status failed"),process.exit(c.GENERAL_ERROR)}}),il=new pe("look").description("Manage looks under an existing personal presenter").addCommand(tl).addCommand(rl).addCommand(ol).addCommand(nl),Fn=new pe("avatar").description("Generate presenter clips and looks; avatar options lists the voices and looks used by presentation video presenter and cast preset").addCommand(Qc).addCommand(Zc).addCommand(il).addCommand(el);Fn.addHelpText("after",`
|
|
69
|
+
${it.bold("Examples:")}
|
|
70
|
+
${it.gray("# Discover the presenter avatars and voices you can use")}
|
|
71
|
+
$ ${u.commands[0]} avatar options
|
|
72
|
+
|
|
73
|
+
${it.bold("See also:")}
|
|
74
|
+
${it.gray("# Save a presenter + backdrop + voice combination under a name")}
|
|
75
|
+
$ ${u.commands[0]} cast preset --help
|
|
76
|
+
|
|
77
|
+
${it.gray("# Apply an avatar, look, or preset to one presentation-video scene")}
|
|
78
|
+
$ ${u.commands[0]} presentation video presenter <project-id>
|
|
79
|
+
`);import{writeFile as Ai}from"fs/promises";import K from"chalk";import{Command as te}from"commander";import yl from"ora";import{createHash as al}from"crypto";import{existsSync as sl}from"fs";import{basename as dl}from"path";import{Command as Ri}from"commander";import cl from"ora";var Si=new Ri("logo").description("Brand logo files").addCommand(new Ri("set").description("Set a brand's logo, dark variant, or favicon").argument("[brand-id]","Brand ID (defaults to this workspace's default brand)").requiredOption("--file <path>","Local image file to upload and use").option("--variant <variant>","primary | dark | favicon. 'dark' is the reversed lock-up for dark backgrounds","primary").option("--team-id <id>","Workspace ID").action(async(e,t)=>{await A();let n=t.variant??"primary";["primary","dark","favicon"].includes(n)||(d(`--variant must be primary, dark, or favicon (got "${n}")`),process.exit(c.INVALID_INPUT)),sl(t.file)||(d(`File not found: ${t.file}`),process.exit(c.INVALID_INPUT));let r=cl({text:`Uploading ${dl(t.file)}...`,stream:process.stdout}).start(),o;try{o=(await de(t.file)).url,r.succeed("Uploaded")}catch(i){r.fail("Upload failed"),d(i instanceof Error?i.message:String(i)),process.exit(c.GENERAL_ERROR)}try{p(await y("setBrandLogo",{brandId:e,fileUrl:o,teamId:t.teamId,variant:n},al("sha256").update(`setBrandLogo:${e??"default"}:${n}:${o}`).digest("hex").slice(0,32)))}catch(i){d(i instanceof Error?i.message:"Write failed"),process.exit(c.GENERAL_ERROR)}}));import{readFile as ll}from"fs/promises";import{randomUUID as ul}from"crypto";import{Command as $i}from"commander";var ml=["color.primary","color.background","color.text","color.secondary","color.accent","color.surface","color.link","typography.primary","typography.secondary","typography.h1","typography.h2","typography.h3","typography.h4","typography.h5","typography.h6","typography.paragraph","typography.button","typography.small","typography.label","typography.caption","asset.logo","asset.logo.dark","asset.favicon"];function pl(e){let t=e.split(".").filter(Boolean);for(let n=t.length;n>=2;n--){let r=t.slice(0,n).join(".");if(ml.includes(r))return{fieldPath:t.slice(n),nodeKey:r}}return{fieldPath:t.slice(2),nodeKey:t.slice(0,2).join(".")}}function gl(e){let t={};for(let n of e){let r=n.indexOf("=");if(r<=0)throw new Error(`"${n}" is not a token assignment. Use key=value, e.g. color.primary=#114b97`);let o=n.slice(0,r).trim(),i=n.slice(r+1);if(o.split(".").filter(Boolean).length<2)throw new Error(`"${o}" is not a brand token key. Expected something like color.primary or typography.h1.size`);let{fieldPath:s,nodeKey:l}=pl(o),g=fl(i);if(s.length===0){t[l]=g;continue}let m=t[l],w=m&&typeof m=="object"&&!Array.isArray(m)?m:{},v=w;for(let b of s.slice(0,-1)){let $=v[b],x=$&&typeof $=="object"&&!Array.isArray($)?$:{};v[b]=x,v=x}v[s[s.length-1]]=g,t[l]=w}return t}function fl(e){return e==="true"?!0:e==="false"?!1:/^-?\d+(?:\.\d+)?$/.test(e)?Number(e):e}function hl(e,t){return e===void 0?{assignments:[...t]}:e.includes("=")?{assignments:[e,...t]}:{assignments:[...t],brandId:e}}var Ti=new $i("profile").description("Read a brand's design tokens, the keys it is missing, or its design.md").argument("[brand-id]","Brand ID (defaults to this workspace's brand)").option("--format <format>","tokens (default) or md").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{let n=await y("getBrandProfile",{brandId:e,format:t.format,teamId:t.teamId});if(t.format==="md"&&typeof n.designMd=="string"){console.log(n.designMd);return}p(n)}catch(n){d(n instanceof Error?n.message:"Profile read failed"),process.exit(c.GENERAL_ERROR)}}),Ei=new $i("set").description("Write brand design tokens (key=value pairs or a design.md)").argument("[brand-id]","Brand ID (defaults to this workspace's brand)").argument("[tokens...]","Token assignments, e.g. color.primary=#114b97 typography.h1.size=32px").option("--design-md <path>","Apply a whole design.md file instead").option("--no-publish","Stage the change without publishing a new active brand version").option("--team-id <id>","Workspace ID").option("--yes","Confirm writing brand data").action(async(e,t,n)=>{n.yes||(d("Pass --yes to confirm writing brand tokens."),process.exit(c.INVALID_INPUT));let{assignments:r,brandId:o}=hl(e,t),i;try{i=r.length>0?gl(r):void 0}catch(s){d(s instanceof Error?s.message:"Invalid token pair"),process.exit(c.INVALID_INPUT)}let a;if(n.designMd)try{a=await ll(n.designMd,"utf8")}catch(s){d(`Could not read ${n.designMd}: ${s instanceof Error?s.message:String(s)}`),process.exit(c.INVALID_INPUT)}!i&&!a&&(d("Nothing to write. Pass token assignments (color.primary=#114b97) or --design-md <file>."),process.exit(c.INVALID_INPUT));try{p(await y("updateBrandTokens",{brandId:o,designMd:a,publish:n.publish===!1?!1:void 0,teamId:n.teamId,tokens:i},ul()))}catch(s){d(s instanceof Error?s.message:"Write failed"),process.exit(c.GENERAL_ERROR)}});var Oi=new te("branding").description("Manage brand profiles, design tokens, logos, competitors, and social handles").addCommand(Ti).addCommand(Ei).addCommand(new te("list").description("List brand profiles").option("-f, --format <format>","Output format (table, json)","table").action(async e=>{await A();try{let t=await wn();if(t.brandings.length===0){e.format==="json"?console.log(JSON.stringify([])):(f("No brand profiles found"),console.log(),console.log(K.gray(`Create one with: ${u.commands[0]} branding extract <url>`)));return}e.format==="json"?console.log(JSON.stringify(t.brandings,null,2)):console.log(Or(t.brandings))}catch(t){d(t instanceof Error?t.message:String(t)),process.exit(t.exitCode??1)}})).addCommand(new te("get").description("Get brand profile details").argument("<id>","Brand profile ID").option("-f, --format <format>","Output format (summary, json)","summary").option("-o, --output <path>","Save JSON to file").action(async(e,t)=>{await A();try{let n=await eo(e);if(t.output){await Ai(t.output,JSON.stringify(n,null,2)),C(`Brand profile saved to ${t.output}`);return}if(t.format==="json")console.log(JSON.stringify(n,null,2));else{if(oe("Brand Profile"),console.log(),S("ID",n.id),S("Name",n.name),S("Source URL",n.sourceUrl??K.gray("None")),S("Default",n.isDefault?K.green("Yes"):"No"),n.createdAt&&S("Created",new Date(n.createdAt).toLocaleString()),console.log(),n.primaryColor||n.colors.length>0){if(console.log(K.bold(" Colors")),n.primaryColor){let o=K.bgHex(n.primaryColor)(" ");console.log(` Primary: ${o} ${n.primaryColor}`)}if(n.colors.length>0){console.log(" Palette:");for(let o of n.colors.slice(0,10)){let i=K.bgHex(o.hex)(" "),a=o.role?K.gray(` (${o.role})`):"";console.log(` ${i} ${o.hex}${a}`)}}console.log()}n.logoUrl&&(console.log(K.bold(" Logo")),console.log(` ${n.logoUrl}`),console.log());let r=n.typography;r&&Object.keys(r).length>0&&(console.log(K.bold(" Typography")),(r.primary||r.headings)&&console.log(` Headings: ${r.primary||r.headings||"-"}`),(r.secondary||r.body)&&console.log(` Body: ${r.secondary||r.body||"-"}`),console.log()),(n.confidence||n.extractionMethod)&&(console.log(K.bold(" Extraction")),n.confidence&&console.log(` Confidence: ${Math.round(n.confidence*100)}%`),n.extractionMethod&&console.log(` Method: ${n.extractionMethod}`),console.log()),console.log(K.gray(" Use --format json for full details")),console.log()}}catch(n){d(n instanceof Error?n.message:String(n)),process.exit(n.exitCode??1)}})).addCommand(new te("find").description("Find existing brand profile by name or URL").argument("<query>","Brand name or website URL to search for").action(async e=>{await A();try{let t=await wn(),n=e.toLowerCase().replace(/^https?:\/\//,"").replace(/\/$/,""),r=t.brandings.filter(o=>{let i=(o.name||"").toLowerCase(),a=(o.sourceUrl||"").toLowerCase().replace(/^https?:\/\//,"").replace(/\/$/,"");return i.includes(n)||a.includes(n)||i.length>0&&n.includes(i)||a.length>0&&n.includes(a)});if(r.length===0){f(`No brand profiles matching "${e}"`),console.log(K.gray(` Extract one with: ${u.commands[0]} branding extract <url>`));return}console.log(`Found ${r.length} brand${r.length>1?"s":""}:
|
|
80
|
+
`);for(let o of r){let i=o.primaryColor?`${K.bgHex(o.primaryColor)(" ")} ${o.primaryColor}`:"";console.log(` ${K.bold(o.name)} ${K.gray(o.id)}`),o.sourceUrl&&console.log(` ${K.gray(o.sourceUrl)}`),i&&console.log(` ${i}`),console.log()}console.log(K.gray(` Export with: ${u.commands[0]} branding get <id> -o branding.json`))}catch(t){d(t instanceof Error?t.message:String(t)),process.exit(t.exitCode??1)}})).addCommand(new te("extract").description("Extract brand profile from a website").argument("<url>","Website URL to extract branding from").option("--team-id <id>","Team ID").option("--yes","Confirm starting a paid/external extraction").option("--no-save","Extract without saving").option("-o, --output <path>","Save JSON to file").action(async(e,t)=>{await A(),t.yes||(d("Pass --yes to confirm brand extraction."),process.exit(c.INVALID_INPUT));try{new URL(e.startsWith("http")?e:`https://${e}`)}catch{d("Invalid URL format"),process.exit(6)}let n=e.startsWith("http")?e:`https://${e}`,r=t.teamId??bt(),o=yl({text:`Extracting branding from ${n}...`,stream:process.stdout}).start();try{let i=await to(n,r,"cli:--yes");o.succeed("Brand extraction started");let a=JSON.stringify(i,null,2);t.output?(await Ai(t.output,a),C(`Brand profile saved to ${t.output}`)):console.log(a),f(`Check progress with: ${u.commands[0]} branding status ${i.runId}`),console.log()}catch(i){o.fail("Extraction failed"),d(i instanceof Error?i.message:String(i)),process.exit(i.exitCode??1)}})).addCommand(new te("status").description("Check a brand extraction run").argument("<run-id>","Run ID returned by branding extract").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await T("extractBranding",e,t.teamId))}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}})).addCommand(new te("set-default").description("Set a brand profile as default").argument("<id>","Brand profile ID").action(async e=>{await A(),f(`To set brand ${e} as default, use the web dashboard.`),console.log(),console.log(K.gray("API support for setting default brand is coming soon."))})).addCommand(new te("competitor").description("Brand competitor commands").addCommand(new te("list").description("List competitors for a brand").option("--branding-id <id>","Brand ID").option("--team-id <id>","Workspace ID").action(async e=>{try{console.log(JSON.stringify(await y("getBrandCompetitors",{brandingId:e.brandingId,teamId:e.teamId}),null,2))}catch(t){d(t instanceof Error?t.message:"Lookup failed"),process.exit(1)}})).addCommand(new te("add").description("Add or update a brand competitor").argument("<name>","Competitor name").option("--branding-id <id>","Brand ID").option("--team-id <id>","Workspace ID").option("--website <url>","Competitor website").option("--x-handle <handle>","Competitor X handle").option("--yes","Confirm writing brand data").action(async(e,t)=>{t.yes||(d("Pass --yes to confirm adding a competitor."),process.exit(c.INVALID_INPUT));try{p(await y("addCompetitor",{approvalReceipt:"cli:--yes",brandingId:t.brandingId,name:e,teamId:t.teamId,website:t.website,xHandle:t.xHandle}))}catch(n){d(n instanceof Error?n.message:"Write failed"),process.exit(c.GENERAL_ERROR)}}))).addCommand(Si).addCommand(new te("social").description("Brand social handle commands").addCommand(new te("add").description("Add or update a brand social handle").argument("<platform>","Social platform").argument("<handle>","Account handle").option("--branding-id <id>","Brand ID").option("--team-id <id>","Workspace ID").option("--yes","Confirm writing brand data").action(async(e,t,n)=>{n.yes||(d("Pass --yes to confirm adding a social handle."),process.exit(c.INVALID_INPUT));try{p(await y("addBrandSocialHandle",{approvalReceipt:"cli:--yes",brandingId:n.brandingId,handle:t,platform:e,teamId:n.teamId}))}catch(r){d(r instanceof Error?r.message:"Write failed"),process.exit(c.GENERAL_ERROR)}})));import at from"chalk";import{Command as qe}from"commander";function bl(e){switch(e.background){case"color":{if(!e.backgroundColor)throw new Error("--background color needs --background-color <hex>, e.g. #1d4ed8");return{hex:e.backgroundColor,kind:"color"}}case"image":{if(!e.backgroundImage)throw new Error("--background image needs --background-image <url>");return{fit:e.fit,kind:"image",url:e.backgroundImage}}default:return{kind:e.background}}}function Pi(e,t){return{background:bl(t),name:e,presenterProfileId:t.presenter,...t.voiceId?{voiceId:t.voiceId}:{}}}function Nt(e,t){if(t==="json"){p(e);return}if(t==="quiet"){console.log(e.id);return}C(`${e.name} (${e.id})`),f(` Presenter: ${e.presenterProfileId}`),f(` Backdrop: ${wl(e.background)}`),e.voiceId&&f(` Voice: ${e.voiceId}`)}function wl(e){switch(e.kind){case"color":return`color ${e.hex}`;case"image":return`image ${e.url} (${e.fit})`;default:return e.kind}}function _i(e){return e.requiredOption("--presenter <profileId>","Trained personal presenter id (pp_*), from `mf avatar options`").option("--background <kind>","Backdrop: original, transparent, color, or image","original").option("--background-color <hex>","Backdrop color, e.g. #1d4ed8").option("--background-image <url>","Backdrop image URL").option("--fit <fit>","How an image backdrop fills the frame","cover").option("--voice-id <voiceId>","Workspace voice to save with this preset").option("-f, --format <format>","Output format: human, json, quiet","human")}var vl=new qe("list").description("List saved cast presets you can use").option("--cast-member-id <castMemberId>","Only this person's presets").option("--branding-id <brandingId>","Narrow to one brand").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{try{let t=await Io({...e.brandingId?{brandingId:e.brandingId}:{},...e.castMemberId?{castMemberId:e.castMemberId}:{}});if(e.format==="json"){p(t);return}if(e.format==="quiet"){for(let n of t.presets)console.log(n.id);return}if(t.presets.length===0){f("No presets saved yet.");return}for(let n of t.presets)Nt(n,"human")}catch(t){d(t instanceof Error?t.message:String(t)),process.exit(c.API_ERROR)}}),xl=_i(new qe("create").description("Save one way a cast member appears").argument("<name>",'What to call this preset, e.g. "In the cafe"').requiredOption("--cast-member-id <castMemberId>","The person this preset is a way of showing")).action(async(e,t)=>{try{let n=await Ro({castMemberId:t.castMemberId,...Pi(e,t)});Nt(n.preset,t.format)}catch(n){d(n instanceof Error?n.message:String(n)),process.exit(c.API_ERROR)}}),kl=_i(new qe("update").description("Change a saved preset (sends the complete preset)").argument("<presetId>","The preset to change").requiredOption("--name <name>","What to call it")).action(async(e,t)=>{try{let n=await So({presetId:e,...Pi(t.name,t)});Nt(n.preset,t.format)}catch(n){d(n instanceof Error?n.message:String(n)),process.exit(c.API_ERROR)}}),Cl=new qe("delete").description("Retire a saved preset. Existing videos keep working.").argument("<presetId>","The preset to retire").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await $o(e);if(t.format==="human"){C(`Retired "${n.preset.name}".`);return}Nt(n.preset,t.format)}catch(n){d(n instanceof Error?n.message:String(n)),process.exit(c.API_ERROR)}}),Il=new qe("preset").description("Saved ways a cast member appears").addCommand(vl).addCommand(xl).addCommand(kl).addCommand(Cl),Vn=new qe("cast").description("The faces and voices your videos can use").addCommand(Il);Vn.addHelpText("after",`
|
|
81
|
+
${at.bold("Examples:")}
|
|
82
|
+
${at.gray("# List the saved presenter/backdrop/voice combinations")}
|
|
83
|
+
$ ${u.commands[0]} cast preset list
|
|
84
|
+
|
|
85
|
+
${at.bold("See also:")}
|
|
86
|
+
${at.gray("# The presenter avatars, looks, and voices a preset is built from")}
|
|
87
|
+
$ ${u.commands[0]} avatar options
|
|
88
|
+
|
|
89
|
+
${at.gray("# Use a preset on one presentation-video scene")}
|
|
90
|
+
$ ${u.commands[0]} presentation video presenter <project-id>
|
|
91
|
+
`);import{readFile as Rl}from"fs/promises";import st from"chalk";import{Command as Li}from"commander";var Sl=new Li("extract").description("Extract normalized text from a URL or local file").argument("<source>","URL, encoded source, or local file path").option("--team-id <id>","Workspace ID").option("--mime-type <type>","Source MIME type").option("--max-length <count>","Maximum output characters","30000").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=e,r;e.includes("://")||(n=await Rl(e,"utf8"),r=e);let o=await y("extractContent",{filename:r,maxLength:Number.parseInt(t.maxLength,10),mimeType:t.mimeType,source:n,teamId:t.teamId});t.format!=="quiet"&&p(o)}catch(n){d(n instanceof Error?n.message:"Extraction failed"),process.exit(c.GENERAL_ERROR)}}),jn=new Li("content").description("Extract normalized text from a URL, a local file, or an encoded document").addCommand(Sl);jn.addHelpText("after",`
|
|
92
|
+
${st.bold("Examples:")}
|
|
93
|
+
${st.gray("# Normalized text from a local document")}
|
|
94
|
+
$ ${u.commands[0]} content extract ./paper.pdf
|
|
95
|
+
|
|
96
|
+
${st.bold("See also:")}
|
|
97
|
+
${st.gray("# Web pages, with page metadata and cost reporting")}
|
|
98
|
+
$ ${u.commands[0]} scrape https://example.com/post
|
|
99
|
+
|
|
100
|
+
${st.gray("# Spoken words out of audio or video")}
|
|
101
|
+
$ ${u.commands[0]} transcript --help
|
|
102
|
+
`);import J from"chalk";import{Command as $l}from"commander";import{existsSync as Tl,readFileSync as El}from"fs";import Al,{resolve as Di}from"path";var Ni=new $l("create").description("Create a new presentation").argument("<topic>","The topic or title for the presentation").option("-n, --slides <count>","Number of slides (1-20)").option("-m, --mode <mode>","Generation quality (best, balanced, fast, ultrafast, instant, image-min, image, image-max) \u2014 default: fast. image-min/image/image-max render each slide as a full-bleed AI image (text not editable); slower + pricier, require image-mode access, and cap at 25 slides. image-min is cheapest (brand colors only, no exact fonts).").option("--image-tier <tier>","Embedded-image quality for HTML modes. Each tier is a per-image credit ceiling as well as its price: quick (+20/slide), smart (default, +60/slide), balanced (+250/slide), best (+500/slide, slow). Ignored for image-*/instant modes.").option("-t, --tone <tone>","Tone (creative, professional, educational, formal, casual)").option("--amount <amount>","Content density (minimal, concise, detailed, extensive)").option("--audience <text>","Target audience description").option("-l, --language <lang>","Output language").option("--branding-id <id>","Branding ID (from `ccr branding list` or `ccr branding extract`)").option("--brand-domain <url>","Brand the deck by website/domain (e.g. https://www.pfizer.com). Errors if no matching saved brand exists.").option("--template <id>","Slide template ID to style the deck after (from `ccr templates`). A default template is applied automatically when omitted.").option("-c, --context <text>","Inline text context (research notes, key facts)").option("--context-file <path>","Path to a context file (text, markdown, JSON)").option("--stdin","Read context from stdin").option("-f, --file <paths...>","Files to upload (PDF, PPTX, DOCX, images)").option("-g, --goal <type>","Goal (inform, persuade, train, learn, entertain, report)").option("--styling <mode>","Styling mode (freeform, brand-only, brand-plus-style, style-only, no-styling)").option("--reference-url <url>","Image URL to use as visual style reference").option("--thinking-depth <depth>","AI thinking depth (quick, moderate, deep, profound)").option("--theme <preset>","[instant mode only] Color theme preset (blue, violet, rose, orange, green)").option("--primary-color <hex>","[instant mode only] Primary color (e.g., #0066CC)").option("--secondary-color <hex>","[instant mode only] Secondary color").option("--accent-color <hex>","[instant mode only] Accent color").option("--background-color <hex>","[instant mode only] Background color").option("--foreground-color <hex>","[instant mode only] Foreground/text color").option("--decorations <style>","[instant mode only] Background decoration (none, waves-bottom-left, waves-top-right, blob-corners, minimal)").option("--team-id <id>","Team ID (switch teams)").option("-o, --output <format>","Output format (human, json, quiet)").option("--no-stream","Wait for completion without streaming progress").option("--wait","Poll until generation completes").option("--debug","Enable debug logging").option("--open","Open in browser after creation").option("--id <uuid>","Use a specific UUID for the presentation").option("--studio","Studio mode: use current directory name as presentation ID (auto-links to Studio preview)").action(async(e,t)=>{await A();let n;t.slides!==void 0&&(n=parseInt(t.slides,10),(Number.isNaN(n)||n<1||n>20)&&(d("Slide count must be between 1 and 20"),process.exit(6)));let r=["best","balanced","fast","ultrafast","instant","image-min","image","image-max"];t.mode&&!r.includes(t.mode)&&(d(`Invalid mode: ${t.mode}. Valid: ${r.join(", ")}`),process.exit(6));let o=["smart","fast","balanced","best"];t.imageTier&&!o.includes(t.imageTier)&&(d(`Invalid image tier: ${t.imageTier}. Valid: ${o.join(", ")}`),process.exit(6));let i=["creative","professional","educational","formal","casual"];t.tone&&!i.includes(t.tone)&&(d(`Invalid tone: ${t.tone}. Valid: ${i.join(", ")}`),process.exit(6));let a=["minimal","concise","detailed","extensive"];t.amount&&!a.includes(t.amount)&&(d(`Invalid amount: ${t.amount}. Valid: ${a.join(", ")}`),process.exit(6));let s=["freeform","brand-only","brand-plus-style","style-only","no-styling"];t.styling&&!s.includes(t.styling)&&(d(`Invalid styling: ${t.styling}. Valid: ${s.join(", ")}`),process.exit(6));let l=["quick","moderate","deep","profound"];t.thinkingDepth&&!l.includes(t.thinkingDepth)&&(d(`Invalid thinking-depth: ${t.thinkingDepth}. Valid: ${l.join(", ")}`),process.exit(6));let g=["inform","persuade","train","learn","entertain","report"];t.goal&&!g.includes(t.goal)&&(d(`Invalid goal: ${t.goal}. Valid: ${g.join(", ")}`),process.exit(6));let m=["blue","violet","rose","orange","green"];t.theme&&!m.includes(t.theme)&&(d(`Invalid theme: ${t.theme}. Valid: ${m.join(", ")}`),process.exit(6));let w=["none","waves-bottom-left","waves-top-right","blob-corners","minimal"];t.decorations&&!w.includes(t.decorations)&&(d(`Invalid decorations: ${t.decorations}. Valid: ${w.join(", ")}`),process.exit(6));let v=t.theme||t.primaryColor||t.secondaryColor||t.accentColor||t.backgroundColor||t.foregroundColor||t.decorations;v&&t.mode&&t.mode!=="instant"&&(d("--theme, --primary-color, --decorations and other color options are only supported with --mode instant"),process.exit(6));let b=t.mode??(v?"instant":"fast"),$=/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;for(let[I,B]of[["primary-color",t.primaryColor],["secondary-color",t.secondaryColor],["accent-color",t.accentColor],["background-color",t.backgroundColor],["foreground-color",t.foregroundColor]])B&&!$.test(B)&&(d(`Invalid ${I}: ${B}. Must be hex (e.g., #0066CC)`),process.exit(6));try{t.output!=="json"&&t.output!=="quiet"&&process.stdout.write(J.gray("Checking generation limits... "));let I=await oo(b,n??10,t.teamId);if(t.output!=="json"&&t.output!=="quiet"){console.log(J.green("\u2713"));let B=I.remaining[b],X=I.limits[b],xe=B===void 0||X===void 0?"":` | ${b}: ${B}/${X} remaining`;console.log(J.gray(` Plan: ${I.planName}${xe}`))}}catch(I){t.output==="json"?console.log(JSON.stringify({success:!1,error:I instanceof Error?I.message:String(I)})):(console.log(J.red("\u2717")),d(I instanceof Error?I.message:String(I))),process.exit(I.exitCode??1)}let x=[];if(t.file&&t.file.length>0){for(let I of t.file)Tl(Di(I))||(d(`File not found: ${I}`),process.exit(6));t.output!=="json"&&t.output!=="quiet"&&console.log(J.gray(`
|
|
103
|
+
Uploading ${t.file.length} file(s)...`));try{x=await Wr(t.file.map(I=>Di(I)),(I,B,X)=>{t.output!=="json"&&t.output!=="quiet"&&X&&process.stdout.write(`\r ${J.cyan("\u2B06")} Uploading: ${X} (${I+1}/${B})`)}),t.output!=="json"&&t.output!=="quiet"&&console.log(`\r ${J.green("\u2713")} Uploaded ${x.length} file(s) `)}catch(I){d(`Failed to upload files: ${I instanceof Error?I.message:String(I)}`),process.exit(1)}}let k;(t.stdin||!process.stdin.isTTY)&&(k=await Ol(),t.stdin&&!k&&(d("No content received from stdin"),process.exit(6)));let R=t.context;if(t.contextFile)try{R=El(t.contextFile,"utf-8"),R.trim()||(d(`Context file is empty: ${t.contextFile}`),process.exit(6))}catch{d(`Failed to read context file: ${t.contextFile}`),process.exit(6)}!k&&!R&&x.length===0&&(d("Context is required to create a presentation."),console.log(),console.log(J.gray(" -f, --file <paths...> Upload files (PDF, PPTX, images)")),console.log(J.gray(" -c, --context <text> Inline text context")),console.log(J.gray(" --context-file <path> Read from a file")),console.log(J.gray(` cat file | ${u.commands[0]} Pipe content`)),process.exit(6));let D=[...x];k&&D.push({id:`stdin-${Date.now()}`,content:k,title:"Piped Content",mime:"text/plain",selected:!0}),R&&D.push({id:`context-${Date.now()}`,content:R,title:"Context",mime:"text/plain",selected:!0});let E,U=t.primaryColor||t.secondaryColor||t.accentColor||t.backgroundColor||t.foregroundColor;(t.theme||U||t.decorations)&&(E={},t.theme&&(E.preset=t.theme),U&&(E.custom={},t.primaryColor&&(E.custom.primary=t.primaryColor),t.secondaryColor&&(E.custom.secondary=t.secondaryColor),t.accentColor&&(E.custom.accent=t.accentColor),t.backgroundColor&&(E.custom.background=t.backgroundColor),t.foregroundColor&&(E.custom.foreground=t.foregroundColor)),t.decorations&&(E.decorations=t.decorations));try{let I=await Kr({topic:e,...n!==void 0&&{slideCount:n},mode:b,...t.imageTier&&{imageTier:t.imageTier},...t.tone&&{tone:t.tone},...t.amount&&{amount:t.amount},...t.audience&&{audience:t.audience},...t.language&&{language:t.language},...t.brandingId&&{brandingId:t.brandingId},...t.brandDomain&&{brandDomain:t.brandDomain},...t.template&&{templateId:t.template},...t.styling&&{stylingMode:t.styling},...t.referenceUrl&&{referenceUrl:t.referenceUrl},...t.thinkingDepth&&{thinkingDepth:t.thinkingDepth},...t.goal&&{goal:t.goal},...t.teamId&&{teamId:t.teamId},...E&&{theme:E},...t.studio?{documentId:Al.basename(process.cwd())}:t.id?{documentId:t.id}:{},...D.length>0&&{contentSources:{uploadedFiles:D,contextFiles:[]}}});if(!t.wait){t.output==="json"?console.log(JSON.stringify({success:!0,run:I},null,2)):t.output!=="quiet"&&(C("Presentation generation queued"),S("Run ID",I.runId),S("Status",I.status),console.log(J.gray(` Check later with presentation status ${I.runId}`)));return}let B=await T("createSlides",I.runId,t.teamId);for(;!B.done;)t.output!=="json"&&t.output!=="quiet"&&console.log(J.gray(` ${B.message}`)),await new Promise(xe=>setTimeout(xe,(B.nextPollAfterSeconds??5)*1e3)),B=await T("createSlides",I.runId,t.teamId);if(B.error)throw new Error(B.error.message);let X=B.entity?await fe(B.entity.id):void 0;if(t.output==="json")console.log(JSON.stringify({success:!0,presentation:X,run:B,viewUrl:X?_e(X.slug,t.language??"en"):void 0},null,2));else{console.log(),C("Presentation created successfully"),console.log(),S("Title",X?.title??e),S("Slides",String(X?.numberOfSlides??n??"\u2014")),console.log();let xe=X?_e(X.slug,t.language??"en"):"N/A";xe!=="N/A"&&(console.log(J.bold(" Open: ")+J.cyan.underline(xe)),t.open&&await(await import("open")).default(xe)),console.log()}}catch(I){t.output==="json"?console.log(JSON.stringify({success:!1,error:I instanceof Error?I.message:String(I)})):d(I instanceof Error?I.message:String(I)),process.exit(I.exitCode??1)}});async function Ol(){return new Promise(e=>{let t="";if(process.stdin.setEncoding("utf8"),process.stdin.isTTY){e("");return}process.stdin.on("data",n=>{t+=n}),process.stdin.on("end",()=>{e(t.trim())}),setTimeout(()=>{e(t.trim())},100)})}import{randomUUID as Pl}from"crypto";import{readFile as _l}from"fs/promises";import{Command as ye}from"commander";var Gn=["text/markdown","text/csv","text/plain"];async function Ui(e){let t=e.content;if(t!==void 0&&t!=="-"&&!t.startsWith("@"))return t;if(t?.startsWith("@"))return _l(t.slice(1),"utf8");if(t==="-"||!process.stdin.isTTY)return Ll();d("No document body. Pass --content <text>, --content @<path>, or pipe it on stdin."),process.exit(c.GENERAL_ERROR)}async function Ll(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8")}function Ut(e){return`cli:document:${e}:${Pl()}`}function Se(e,t){d(e instanceof Error?e.message:t),process.exit(c.GENERAL_ERROR)}function Dl(e){if(!e)return;let t=e.split(".").pop()?.toLowerCase();if(t==="md"||t==="markdown")return"text/markdown";if(t==="csv")return"text/csv";if(t==="txt")return"text/plain"}var Nl=new ye("create").description("Save a document into the Library").argument("<title>","Document title").option("--team-id <id>","Workspace ID").option("--content <text>","Document body: literal text, @<path> to read a file, or '-' for stdin").option("--media-type <type>",`One of ${Gn.join(", ")}; inferred from an @<path> body when omitted`).option("--summary <text>","One line for the Library card").action(async(e,t)=>{try{let n=t.mediaType??Dl(t.content?.startsWith("@")?t.content.slice(1):void 0);n||(d(`Cannot tell what this document is. Pass --media-type (${Gn.join(", ")}).`),process.exit(c.GENERAL_ERROR)),p(await y("createDocument",{content:await Ui(t),mediaType:n,summary:t.summary,teamId:t.teamId,title:e},Ut("create")))}catch(n){Se(n,"Could not save the document")}}),Ul=new ye("list").description("List the workspace's documents, newest first").option("--team-id <id>","Workspace ID").option("--media-type <type>",`Filter to one of ${Gn.join(", ")}`).option("--limit <n>","Maximum documents to return (default 50)").action(async e=>{try{p(await y("listDocuments",{limit:e.limit?Number(e.limit):void 0,mediaType:e.mediaType,teamId:e.teamId}))}catch(t){Se(t,"Could not list documents")}}),ql=new ye("read").description("Print a document's body").argument("<document-id>","Document id").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{let n=await y("readDocument",{documentId:e,teamId:t.teamId}),{document:r}=n;process.stderr.write(`${r.title} \xB7 ${r.mediaType} \xB7 v${r.version} \xB7 ${r.byteLength} bytes
|
|
104
|
+
`),process.stdout.write(r.content)}catch(n){Se(n,"Could not read the document")}}),Ml=new ye("update").description("Edit a document under an optimistic lock").argument("<document-id>","Document id").requiredOption("--expected-version <n>","The version you last read; the edit is rejected if it has moved on").option("--team-id <id>","Workspace ID").option("--content <text>","New body: literal text, @<path> to read a file, or '-' for stdin").option("--title <text>","New title").option("--summary <text>","New Library-card summary").option("--change-summary <text>","What changed, recorded on the version").action(async(e,t)=>{try{let n=t.content!==void 0;p(await y("updateDocument",{changeSummary:t.changeSummary,content:n?await Ui(t):void 0,documentId:e,expectedVersion:Number(t.expectedVersion),summary:t.summary,teamId:t.teamId,title:t.title},Ut("update")))}catch(n){Se(n,"Could not update the document")}}),Fl=new ye("delete").description("Remove a document from the Library").argument("<document-id>","Document id").option("--team-id <id>","Workspace ID").option("--yes","Skip the confirmation prompt").action(async(e,t)=>{try{!t.yes&&process.stdin.isTTY&&(d("Refusing to delete without confirmation. Re-run with --yes to remove this document."),process.exit(c.GENERAL_ERROR)),p(await y("deleteDocument",{documentId:e,teamId:t.teamId},Ut("delete")))}catch(n){Se(n,"Could not delete the document")}}),Vl=new ye("versions").description("List a document's version history").argument("<document-id>","Document id").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await y("listDocumentVersions",{documentId:e,teamId:t.teamId}))}catch(n){Se(n,"Could not list versions")}}),jl=new ye("restore").description("Restore an earlier version by appending it as the current one").argument("<document-id>","Document id").requiredOption("--version-number <n>","The historical version to restore").requiredOption("--expected-version <n>","The version you last read; the restore is rejected if it has moved on").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await y("restoreDocumentVersion",{documentId:e,expectedVersion:Number(t.expectedVersion),teamId:t.teamId,versionNumber:Number(t.versionNumber)},Ut("restore")))}catch(n){Se(n,"Could not restore the version")}}),qi=new ye("document").description("Create, read and edit Library documents").addCommand(Nl).addCommand(Ul).addCommand(ql).addCommand(Ml).addCommand(Fl).addCommand(Vl).addCommand(jl);import{Command as Gl}from"commander";import qt from"chalk";var Mi=new Gl("get").description("Get presentation details").argument("<slug>","Presentation slug (e.g., my-presentation-v1-abc123)").option("-f, --format <format>","Output format (summary, full, json)","summary").option("--include <fields>","Fields to include (comma-separated: slides,styling)").option("-l, --language <lang>","Language for view URL","en").action(async(e,t)=>{await A();try{let n=await fe(e);if(t.format==="json"){console.log(JSON.stringify(n,null,2));return}let r=_e(n.slug,t.language);oe("Presentation Details"),console.log(),S("Slug",n.slug),S("Title",n.title||"Untitled"),S("Description",n.description||qt.gray("None")),S("Slides",String(n.numberOfSlides||"-")),S("Mode",n.mode||"-"),S("Created",gn(n.createdAt)),n.updatedAt&&S("Updated",gn(n.updatedAt)),console.log(),r!=="N/A"&&console.log(qt.bold(" Open: ")+qt.cyan.underline(r)),console.log(),t.format==="full"&&t.include?.includes("slides")&&(console.log(qt.gray("(Full slide content available in JSON format)")),console.log())}catch(n){d(n instanceof Error?n.message:String(n)),process.exit(n.exitCode??1)}});import{Command as Ft}from"commander";import Bl from"ora";function re(e,t){let n=e instanceof Error?e.message:"Unknown error";return!t||n.includes(t)?n:`${n} Request ID: ${t}. Resume with the matching status command; do not start a duplicate run.`}function Wl(e){let t=e?.trim().toLowerCase();if(!(!t||t==="any"||t==="all"))return t==="web"||t==="stock"?[t]:void 0}function Kl(e){let t=e.assetType??"image";if(t!=="image"&&t!=="icon")throw new Error("Asset type must be image or icon");let n;if(e.maxResults&&(n=Number.parseInt(e.maxResults,10),Number.isNaN(n)||n<1))throw new Error("Max results must be a positive number");return{query:t==="icon"?`${e.query.trim()} transparent icon isolated symbol`:e.query.trim(),options:{maxResults:n||10,size:e.size,safeSearch:e.safeSearch,assetType:t,sources:Wl(e.source),transparentBackground:t==="icon"}}}function Jl(e){let t=e.url||e.imageUrl;if(!t)throw new Error("Image search returned a result without a URL");return t}function zl(e){return e.transparent??e.isTransparent??e.metadata?.transparent??e.metadata?.isTransparent}function Xl(e){let t=[],n=e.reduce((r,o)=>Math.max(r,o.length),0);for(let r=0;r<n;r+=1)for(let o of e){let i=o[r];i!==void 0&&t.push(i)}return t}function Hl(e,t){let n=e.data.results.map(r=>r.results.map(o=>{let i=o,a=zl(i);if(t==="icon"&&a!==!0)throw new Error("Icon search returned a result without a verified transparent background");return{...i,url:Jl(i),provider:r.providerName,assetType:t,...a!==void 0?{transparent:a}:{}}}));return Xl(n)}function Yl(e){return e.error||e.status==="failed"||e.status==="errored"||e.status==="cancelled"?"failed":e.done||e.status==="completed"?"completed":e.status==="in_progress"?"processing":"pending"}function Mt(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function Ql(e){return{requestId:e.runId,status:Yl(e),...e.entity?.url?{url:e.entity.url}:{},...e.error?.message?{error:e.error.message}:{},...Mt(e.output?.width)!==void 0?{width:Mt(e.output?.width)}:{},...Mt(e.output?.height)!==void 0?{height:Mt(e.output?.height)}:{}}}async function Fi(e,t){return Ql(await T("createImage",e,t))}function Bn(e,t){if(t==="json"){p(e);return}if(t==="quiet"){console.log(e.output??e.url??e.requestId);return}f(`Request ID: ${e.requestId}`),f(`Status: ${e.status}`),e.url&&C(`Image URL: ${e.url}`),e.output&&C(`Saved to: ${e.output}`),e.error&&d(`Error: ${e.error}`)}var Zl=new Ft("search").description("Search images across the web and licensed stock").requiredOption("-q, --query <query>","Search query").option("-n, --max-results <number>","Maximum number of results (default: 10)").option("-s, --size <size>","Image size: small, medium, large, any","large").option("--asset-type <type>","Asset intent: image or transparent icon","image").option("--source <source>","Catalog: web, stock, or any (default: any)","any").option("--safe-search","Enable safe search (default: true)",!0).option("--no-safe-search","Disable safe search").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format,n=t==="human"?Bl("Searching for images...").start():null,r;try{r=Kl(e)}catch(o){n?.stop(),d(o instanceof Error?o.message:"Invalid image search request"),process.exit(c.INVALID_INPUT);return}try{let o=await uo(r);n?.stop(),o.success||(d("Search failed"),process.exit(c.GENERAL_ERROR));let i=r.options?.assetType??"image",a=Hl(o,i);if(t==="json"){p({success:!0,query:e.query,assetType:i,totalResults:a.length,images:a});return}if(t==="quiet"){for(let s of a)console.log(s.url);return}if(a.length===0){f("No images found");return}C(`Found ${a.length} images for "${e.query}"`),console.log();for(let s=0;s<a.length;s++){let l=a[s];console.log(`[${s+1}] ${l.title||"Untitled"}`),console.log(` URL: ${l.url}`),console.log(` Size: ${l.width}x${l.height}`),l.author&&console.log(` Author: ${l.author}`),console.log(` Provider: ${l.provider}`),console.log()}f(`Total cost: $${o.data.totalCost.toFixed(4)}`)}catch(o){n?.stop(),d(o instanceof Error?o.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),eu=new Ft("generate").description("Generate an image and optionally save it locally").argument("[prompt]","Image prompt").option("-p, --prompt <text>","Image prompt (named compatibility form)").option("--provider <provider>","Provider override").option("--model <model>","Model override").option("--width <pixels>","Width").option("--height <pixels>","Height").option("--team-id <id>","Workspace ID").option("-o, --output <path>","Local output path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").option("--yes","Approve generation").action(async(e,t)=>{let n,r=e??t.prompt;r?.trim()||(d("Provide an image prompt as an argument or with --prompt"),process.exit(c.INVALID_INPUT)),t.yes||(d("Pass --yes to approve image generation"),process.exit(c.INVALID_INPUT)),!t.wait&&t.output&&(d("--output requires waiting for completion; remove --no-wait"),process.exit(c.INVALID_INPUT));try{let o=await y("createImage",{approvalReceipt:"cli:--yes",height:t.height?Number.parseInt(t.height,10):void 0,model:t.model,prompt:r.trim(),provider:t.provider,teamId:t.teamId,width:t.width?Number.parseInt(t.width,10):void 0});if(n=o.runId,!t.wait){Bn({requestId:o.runId,status:"pending"},t.format);return}let i=await Q(()=>Fi(o.runId,t.teamId),180,2e3);if(i.status==="failed")throw new Error(i.error||"Image generation failed");if(!i.url)throw new Error("Image generation completed without an image URL");let a=t.output?{...i,output:t.output}:i;t.output&&await z(i.url,t.output),Bn(a,t.format)}catch(o){d(re(o,n),t.format),process.exit(c.GENERAL_ERROR)}}),tu=new Ft("status").description("Read image generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").option("-o, --output <path>","Download a completed image").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await Fi(e,t.teamId);if(t.output){if(n.status!=="completed"||!n.url)throw new Error("Image generation is not complete");await z(n.url,t.output),n.output=t.output}Bn(n,t.format)}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),Vi=new Ft("image").description("Image search and generation commands").addCommand(Zl).addCommand(eu).addCommand(tu);import{Command as Wn}from"commander";var nu=new Wn("search").description("Search the workspace Knowledge Hub").argument("[query]","Optional search query").option("--team-id <id>","Workspace ID").option("--kind <kind>","Knowledge kind").option("--limit <count>","Maximum results","10").action(async(e,t)=>{try{p(await y("searchKnowledge",{kind:t.kind,limit:Number.parseInt(t.limit,10),query:e,teamId:t.teamId}))}catch(n){d(n instanceof Error?n.message:"Knowledge search failed"),process.exit(c.GENERAL_ERROR)}}),ru=new Wn("bundle").description("Read a bounded Open Knowledge Format bundle").option("--team-id <id>","Workspace ID").option("--kind <kind>","Knowledge kind").action(async e=>{try{p(await y("getKnowledgeBundle",{kind:e.kind,teamId:e.teamId}))}catch(t){d(t instanceof Error?t.message:"Knowledge bundle failed"),process.exit(c.GENERAL_ERROR)}}),ji=new Wn("knowledge").description("Search the workspace Knowledge Hub and read bounded knowledge bundles").addCommand(nu).addCommand(ru);import{randomUUID as ou}from"crypto";import Vt from"chalk";import{Command as ge}from"commander";function Kn(e){if(e!==void 0)return e==="root"?null:e}function Me(e){return`cli:library:${e}:${ou()}`}function $e(e,t){d(e instanceof Error?e.message:t),process.exit(c.GENERAL_ERROR)}var iu=new ge("list").description("List the Library folder tree").option("--team-id <id>","Workspace ID").option("--counts","Include how many items are in each folder").action(async e=>{try{p(await y("listLibraryFolders",{includeEntryCounts:!!e.counts,teamId:e.teamId}))}catch(t){$e(t,"Could not list folders")}}),au=new ge("create").description("Create a Library folder").argument("<name>","Folder name").option("--team-id <id>","Workspace ID").option("--parent <id>","Parent folder id; omit for a top-level folder").option("--color <color>","Colour token (slate, blue, green, amber, red, violet)").action(async(e,t)=>{try{p(await y("createLibraryFolder",{color:t.color,name:e,parentId:Kn(t.parent),teamId:t.teamId},Me("create")))}catch(n){$e(n,"Could not create the folder")}}),su=new ge("rename").description("Rename a Library folder").argument("<folder-id>","Folder id").argument("<name>","New name").option("--team-id <id>","Workspace ID").action(async(e,t,n)=>{try{p(await y("renameLibraryFolder",{folderId:e,name:t,teamId:n.teamId},Me("rename")))}catch(r){$e(r,"Could not rename the folder")}}),du=new ge("recolor").description("Set or clear a Library folder's colour").argument("<folder-id>","Folder id").requiredOption("--color <color>","Colour token (slate, blue, green, amber, red, violet), or 'none' to clear").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await y("recolorLibraryFolder",{color:t.color==="none"?null:t.color,folderId:e,teamId:t.teamId},Me("recolor")))}catch(n){$e(n,"Could not recolour the folder")}}),cu=new ge("move").description("Move a Library folder under a new parent").argument("<folder-id>","Folder id").requiredOption("--to <id>","New parent folder id, or 'root' for top level").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await y("moveLibraryFolder",{folderId:e,newParentId:Kn(t.to),teamId:t.teamId},Me("move")))}catch(n){$e(n,"Could not move the folder")}}),lu=new ge("delete").description("Delete a folder and its subfolders; items inside are unfiled").argument("<folder-id>","Folder id").requiredOption("--yes","Confirm deletion").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await y("deleteLibraryFolder",{folderId:e,teamId:t.teamId},Me("delete")))}catch(n){$e(n,"Could not delete the folder")}}),uu=new ge("folders").description("Manage Library folders").addCommand(iu).addCommand(au).addCommand(su).addCommand(du).addCommand(cu).addCommand(lu),mu=new ge("file").description("File an artifact into a Library folder").argument("<kind>","Artifact kind, e.g. presentation or microsite").argument("<target-id>","Id of the artifact itself").requiredOption("--folder <id>","Destination folder id, or 'root' to unfile the artifact").option("--team-id <id>","Workspace ID").action(async(e,t,n)=>{try{p(await y("fileArtifactIntoFolder",{folderId:Kn(n.folder),kind:e,targetId:t,teamId:n.teamId},Me("file")))}catch(r){$e(r,"Could not file that item")}}),Jn=new ge("library").description("Library folders and filing").addCommand(uu).addCommand(mu);Jn.addHelpText("after",`
|
|
105
|
+
${Vt.bold("Examples:")}
|
|
106
|
+
${Vt.gray("# Browse the folder tree and file an artifact into a folder")}
|
|
107
|
+
$ ${u.commands[0]} library folders list
|
|
108
|
+
$ ${u.commands[0]} library file presentation <id> --folder <folder-id>
|
|
109
|
+
|
|
110
|
+
${Vt.bold("See also:")}
|
|
111
|
+
${Vt.gray("# A different thing: reusable video clips, not Library folders")}
|
|
112
|
+
$ ${u.commands[0]} video library --help
|
|
113
|
+
`);import{Command as dt}from"commander";function j(e,t){let n=e.parent?.opts?.();if(!n)return t;let r={...t};for(let[o,i]of Object.entries(n))r[o]===void 0&&i!==void 0&&(r[o]=i);return r}var Gi=new dt("analyze").description("Transcribe an uploaded recording and publish it for generation").argument("<asset-id>","Recording ID").option("--language <code>","BCP-47 language hint for the spoken audio").option("--no-diarize","Skip speaker attribution (cheaper, but a multi-speaker recording becomes much weaker as generation input)").option("--team-id <id>","Workspace ID").option("--yes","Confirm the charge \u2014 billed per source-minute").action(async(e,t)=>{if(!t.yes)return d("Pass --yes to confirm. Analysis is billed per minute of source audio.");try{p(await y("analyzeMediaAsset",{assetId:e,diarize:t.diarize,language:t.language,teamId:t.teamId}))}catch(n){d(n instanceof Error?n.message:"Analysis failed"),process.exit(c.GENERAL_ERROR)}}),pu=new dt("status").description("Read an analysis run's status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async function(e,t){let n=j(this,t);try{p(await T("analyzeMediaAsset",e,n.teamId))}catch(r){d(r instanceof Error?r.message:"Status failed"),process.exit(c.GENERAL_ERROR)}});Gi.addCommand(pu);var Bi=new dt("import").description("Import a recording from a connected account").argument("<remote-file-id>","The provider's own ID for the recording").requiredOption("--provider <id>","Provider id of the connected account, e.g. google").requiredOption("--connector <id>","Connected account ID to read from").option("--title <title>","Name for the imported recording").option("--chat <id>","Chat this import was requested from").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await y("importConnectorRecording",{connectorId:t.connector,providerId:t.provider,remoteFileId:e,sourceChatId:t.chat,teamId:t.teamId,title:t.title}))}catch(n){d(n instanceof Error?n.message:"Import failed"),process.exit(c.GENERAL_ERROR)}}),gu=new dt("status").description("Read an import run's status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async function(e,t){let n=j(this,t);try{p(await T("importConnectorRecording",e,n.teamId))}catch(r){d(r instanceof Error?r.message:"Status failed"),process.exit(c.GENERAL_ERROR)}});Bi.addCommand(gu);var Wi=new dt("media").description("Ingest and transcribe recordings you already own").addCommand(Gi).addCommand(Bi);import{Command as zn}from"commander";var fu=new zn("create").description("Start background microsite generation").argument("<title>","Microsite title").requiredOption("--description <brief>","Microsite brief").option("--audience <audience>","Target audience").option("--tone <tone>","Tone").option("--language <language>","Content language","en").option("--branding-id <id>","Branding ID").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(e,t)=>{if(!t.yes)return d("Pass --yes to approve generation");try{p(await y("createMicrosite",{approvalReceipt:"cli:--yes",audience:t.audience,brandingId:t.brandingId,description:t.description,language:t.language,teamId:t.teamId,title:e,tone:t.tone}))}catch(n){d(n instanceof Error?n.message:"Generation failed"),process.exit(c.GENERAL_ERROR)}}),hu=new zn("status").description("Read microsite generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await T("createMicrosite",e,t.teamId))}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),Ki=new zn("microsite").description("Generate a shareable multi-section web microsite from a brief").addCommand(fu).addCommand(hu);import{Command as Yn}from"commander";import Xn from"ora";function Hn(e,t){if(t==="json"){let{cost:n,...r}=e;p(r);return}if(t==="quiet"){e.outputUrl?console.log(e.outputUrl):console.log(e.requestId);return}f(`Request ID: ${e.requestId}`),f(`Status: ${e.status}`),e.duration&&f(`Duration: ${e.duration}s`),e.outputUrl&&C(`Output URL: ${e.outputUrl}`),e.cost!==void 0&&f(`Cost: $${e.cost.toFixed(4)}`),e.error&&d(`Error: ${e.error}`)}var yu=new Yn("create").description("Mix audio tracks into a video (music will loop to match video duration)").requiredOption("--video <url>","Input video file/URL").option("--music <url>","Background music file/URL (will loop if shorter than video)").option("--voice <url>","Voiceover file/URL").option("--music-volume <percent>","Music volume 0-100 (default: 30, recommended for mix with voice)","30").option("--voice-volume <percent>","Voice volume 0-100","100").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{!e.music&&!e.voice&&(d("At least one of --music or --voice must be provided"),process.exit(c.INVALID_INPUT));let t=parseInt(e.musicVolume,10)/100,n=parseInt(e.voiceVolume,10)/100;(Number.isNaN(t)||t<0||t>1)&&(d("Music volume must be between 0 and 100"),process.exit(c.INVALID_INPUT)),(Number.isNaN(n)||n<0||n>2)&&(d("Voice volume must be between 0 and 200"),process.exit(c.INVALID_INPUT));let r=e.format,o=r==="human"?Xn("Mixing audio...").start():null,i=[{url:e.video,role:"video"}];e.music&&i.push({url:e.music,role:"background",volume:t*5}),e.voice&&i.push({url:e.voice,role:"voice",volume:n*2});try{let a=await lo({operation:"add-to-video",inputs:i,options:{musicVolume:t,voiceVolume:n}});if(!e.wait){o?.stop(),Hn(a,r);return}o&&(o.text=`Processing (ID: ${a.requestId})...`);let s=await Q(()=>xn(a.requestId),120,3e3);if(o?.stop(),s.status==="failed"&&(d(s.error||"Audio mixing failed"),process.exit(c.GENERAL_ERROR)),Hn(s,r),e.output){if(!s.outputUrl)throw new Error("Audio mix completed without an output URL");let l=r==="human"?Xn("Downloading...").start():null;await z(s.outputUrl,e.output),l?.stop(),r==="human"&&C(`Saved to: ${e.output}`)}}catch(a){o?.stop(),d(a instanceof Error?a.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),bu=new Yn("status").description("Check status of an audio mix request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let n=t.format==="human"?Xn("Checking status...").start():null;try{let r=await xn(e);n?.stop(),Hn(r,t.format)}catch(r){n?.stop(),d(r instanceof Error?r.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Ji=new Yn("mix").description("Audio mixing commands").addCommand(yu).addCommand(bu);import{Command as Zn}from"commander";import Ru from"ora";import{readFileSync as wu}from"fs";var zi=500,Xi=1e4,jt=100,Hi=1e4,Yi=1e3,Zi=["duration_s","idempotency_key","narration","schema_version","video_description"],vu=["end_s","id","start_s","text","words"],xu=["end","id","start","text"],ku=["description","end_s","id","start_s"];function ea(e){let t=na(e);if(ra(t),ct(t,Zi,""),t.duration_s<3)throw new Error("Music request duration_s must be between 3 and 600");return t}function ta(e){let t=na(e);ra(t),ct(t,[...Zi,"visual_event"],"");let n=t.visual_event;if(!Gt(n))throw new Error("SFX request visual_event is required");if(ct(n,ku,"visual_event"),!be(n.description))throw new Error("SFX request visual_event.description is required");if(!be(n.id)||!we(n.start_s))throw new Error("SFX request visual_event requires id and start_s");if(n.end_s!==void 0&&(!we(n.end_s)||n.end_s<n.start_s))throw new Error("SFX request visual_event.end_s must be after start_s");return t}function na(e){try{let t=JSON.parse(wu(e,"utf8"));if(!Gt(t))throw new Error("request must be a JSON object");return t}catch(t){throw new Error(`Cannot read audio request ${e}: ${t instanceof Error?t.message:String(t)}`)}}function ra(e){if(e.schema_version!==1)throw new Error("Audio request schema_version must be 1");if(!be(e.video_description))throw new Error("Audio request video_description is required");if(!we(e.duration_s)||e.duration_s<.5||e.duration_s>600)throw new Error("Audio request duration_s must be between 0.5 and 600");if(!Array.isArray(e.narration))throw new Error("Audio request narration must be an array");if(e.narration.length>zi)throw new Error(`Audio request narration holds at most ${zi} lines`);if(e.narration.forEach(Cu),e.idempotency_key!==void 0&&!be(e.idempotency_key))throw new Error("Audio request idempotency_key must be a non-empty string")}function Cu(e,t){let n=`narration.${t}`;if(!Gt(e))throw new Error(`Audio request ${n} must be an object`);if(ct(e,vu,n),!be(e.id)||e.id.length>jt)throw new Error(`Audio request ${n}.id is required (1-${jt} characters)`);if(!be(e.text)||e.text.trim().length>Hi)throw new Error(`Audio request ${n}.text is required (1-${Hi} characters)`);if(Qi(e.start_s,`${n}.start_s`),Qi(e.end_s,`${n}.end_s`),we(e.start_s)&&we(e.end_s)&&e.end_s<e.start_s)throw new Error(`Audio request ${n}.end_s must be greater than or equal to ${n}.start_s`);if(e.words!==void 0){if(!Array.isArray(e.words)||e.words.length>Xi)throw new Error(`Audio request ${n}.words must be an array of at most ${Xi} entries`);e.words.forEach((r,o)=>{Iu(r,`${n}.words.${o}`)})}}function Iu(e,t){if(!Gt(e))throw new Error(`Audio request ${t} must be an object`);if(ct(e,xu,t),!be(e.text)||e.text.trim().length>Yi)throw new Error(`Audio request ${t}.text is required (1-${Yi} characters)`);if(!we(e.start)||!we(e.end))throw new Error(`Audio request ${t} requires numeric start and end seconds`);if(e.end<e.start)throw new Error(`Audio request ${t}.end must be greater than or equal to ${t}.start`);if(e.id!==void 0&&(!be(e.id)||e.id.length>jt))throw new Error(`Audio request ${t}.id must be 1-${jt} characters`)}function Qi(e,t){if(e!==void 0&&!we(e))throw new Error(`Audio request ${t} must be a non-negative number`)}function ct(e,t,n){let r=Object.keys(e).filter(i=>!t.includes(i));if(r.length===0)return;let o=n?`${n} `:"";throw new Error(`Audio request ${o}has unknown field(s): ${r.join(", ")}. Allowed: ${t.join(", ")}`)}function Gt(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function be(e){return typeof e=="string"&&e.trim().length>0}function we(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0}function Qn(e,t){if(t==="json"){let{cost:n,...r}=e;p(r);return}if(t==="quiet"){console.log(e.audioUrl??e.requestId);return}f(`Request ID: ${e.requestId}`),f(`Status: ${e.status}`),e.duration&&f(`Duration: ${e.duration}s`),e.audioUrl&&C(`Audio URL: ${e.audioUrl}`),e.cost!==void 0&&f(`Cost: $${e.cost.toFixed(4)}`),e.error&&d(`Error: ${e.error}`)}var Su=new Zn("generate").description("Generate music that follows a video's narration and final timing").requiredOption("--request <path>","JSON file with video description, narration, and final timing").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{!e.wait&&e.output&&(d("--output requires waiting for completion; remove --no-wait"),process.exit(c.INVALID_INPUT));let t;try{t=ea(e.request)}catch(i){d(i instanceof Error?i.message:String(i)),process.exit(c.INVALID_INPUT)}let n=e.format,r=n==="human"?Ru("Generating music for video...").start():null,o;try{let i=await co(t);if(o=i.requestId,!e.wait){r?.stop(),Qn(i,n);return}r&&(r.text=`Processing (ID: ${i.requestId})...`);let a=i.status==="completed"||i.status==="failed"?i:await Q(()=>vn(i.requestId),180,2e3);if(r?.stop(),a.status==="failed"&&(d(a.error||"Music generation failed"),process.exit(c.GENERAL_ERROR)),e.output){if(!a.audioUrl)throw new Error("Music completed without an audio URL");await z(a.audioUrl,e.output),n==="human"&&C(`Saved to: ${e.output}`),a.output=e.output}Qn(a,n)}catch(i){r?.stop(),d(re(i,o),n),process.exit(c.GENERAL_ERROR)}}),$u=new Zn("status").description("Check status of a music generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{Qn(await vn(e),t.format)}catch(n){d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),oa=new Zn("music").description("Video-aware music generation commands").addCommand(Su).addCommand($u);import{Command as er}from"commander";function ia(e,t){return[...t,e]}var Tu=new er("create").description("Create a standalone poll").argument("<question>","Poll question").option("--option <text>","Choice (repeat 2-10 times)",ia,[]).option("--type <type>","single, multiple, or word-cloud","single").option("--title <title>","Poll title").option("--team-id <id>","Workspace ID").option("--yes","Approve creation").action(async(e,t)=>{if(!t.yes)return d("Pass --yes to approve poll creation");try{p(await y("createPoll",{approvalReceipt:"cli:--yes",options:t.option.map(n=>({text:n})),pollType:t.type,question:e,teamId:t.teamId,title:t.title}))}catch(n){d(n instanceof Error?n.message:"Poll creation failed"),process.exit(c.GENERAL_ERROR)}}),Eu=new er("edit").description("Edit a standalone poll").argument("<poll-id>","Poll UUID").option("--question <question>","New question").option("--option <text>","Replacement choice (repeat)",ia).option("--type <type>","single, multiple, or word-cloud").option("--title <title>","Poll title").option("--team-id <id>","Workspace ID").option("--yes","Approve update").action(async(e,t)=>{if(!t.yes)return d("Pass --yes to approve poll update");try{p(await y("editPoll",{approvalReceipt:"cli:--yes",options:t.option?.map(n=>({text:n})),pollId:e,pollType:t.type,question:t.question,teamId:t.teamId,title:t.title}))}catch(n){d(n instanceof Error?n.message:"Poll update failed"),process.exit(c.GENERAL_ERROR)}}),aa=new er("poll").description("Standalone poll commands").addCommand(Tu).addCommand(Eu);import{randomUUID as qu}from"crypto";import{readFile as Mu}from"fs/promises";import Te from"chalk";import{Command as H}from"commander";import{randomUUID as nr}from"crypto";import{readFile as Au}from"fs/promises";import{Command as Bt}from"commander";var sa=new Bt("edit").description("Apply one atomic draft edit: narration, scene patch/move/remove, composition, or intro/outro card").argument("<project-id>","Presentation-video project UUID or editor slug").requiredOption("--edit <json-or-path>","Edit object as inline JSON or a JSON file path").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key").action(async function(e,t){let n=j(this,t);await Wt("Draft edit failed",async()=>y("editPresentationVideoDraft",{edit:await Ou(n.edit),projectId:e,teamId:n.teamId},n.idempotencyKey??nr()))}),da=new Bt("versions").description("List saved presentation-video draft versions").argument("<project-id>","Presentation-video project UUID or editor slug").option("--before-version <number>","Continue below this version number",tr(1)).option("--limit <number>","Maximum versions",tr(1,100)).option("--team-id <id>","Workspace ID override").action(async function(e,t){let n=j(this,t);await Wt("Version list failed",()=>y("listPresentationVideoVersions",{beforeVersion:n.beforeVersion,limit:n.limit,projectId:e,teamId:n.teamId}))}),ca=new Bt("restore").description("Restore a saved draft version as a new version").argument("<project-id>","Presentation-video project UUID or editor slug").argument("<version-number>","Version number",tr(1)).option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key").action(async function(e,t,n){let r=j(this,n);await Wt("Version restore failed",()=>y("restorePresentationVideoVersion",{projectId:e,teamId:r.teamId,versionNumber:t},r.idempotencyKey??nr()))}),la=new Bt("refresh").description("Refresh deck-backed scenes from the latest presentation version").argument("<project-id>","Presentation-video project UUID or editor slug").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key").action(async function(e,t){let n=j(this,t);await Wt("Slide refresh failed",()=>y("refreshPresentationVideoSlides",{projectId:e,teamId:n.teamId},n.idempotencyKey??nr()))});async function Ou(e){let t=e.trim(),n=t.startsWith("{")?t:await Au(t,"utf8"),r=JSON.parse(n);if(!r||typeof r!="object"||Array.isArray(r))throw new Error("--edit must contain one JSON object.");return r}function tr(e,t=Number.MAX_SAFE_INTEGER){return n=>{let r=Number(n);if(!Number.isInteger(r)||r<e||r>t)throw new Error(`Expected an integer between ${e} and ${t}.`);return r}}async function Wt(e,t){try{p(await t())}catch(n){d(n instanceof Error?n.message:e),process.exit(c.GENERAL_ERROR)}}import{randomUUID as Pu}from"crypto";import{Command as _u}from"commander";var Lu=["basic","standard","premium"],ua=new _u("presenter").description("Set one scene to a different avatar/look or preset, or restore the project default").argument("<project-id>","Presentation video project ID").argument("<slide-id>","Slide or intro/outro scene ID").option("--avatar-id <id>","Exact avatar/look ID from presenter list").option("--cast-preset-id <id>","Saved cast preset ID").option("--no-apply-preset-voice","Preserve this scene's current voice instead of applying the preset voice").option("--inherit","Remove the override and inherit the project default").option("--render-tier <tier>","Avatar render tier: basic, standard, or premium").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key for this update").action(async function(e,t,n){let r=j(this,n);try{if(+!!r.avatarId+ +!!r.castPresetId+ +(r.inherit===!0)!==1)throw new Error("Pass exactly one of --avatar-id, --cast-preset-id, or --inherit.");if(r.renderTier&&!r.avatarId)throw new Error("--render-tier can only be used with --avatar-id.");if(r.renderTier&&!Lu.includes(r.renderTier))throw new Error("--render-tier must be basic, standard, or premium.");p(await y("setPresentationVideoScenePresenter",{applyPresetVoice:r.castPresetId?r.applyPresetVoice:void 0,avatarId:r.avatarId,castPresetId:r.castPresetId,inherit:r.inherit,projectId:e,renderTier:r.renderTier,slideId:t,teamId:r.teamId},Du(r.idempotencyKey)))}catch(o){d(o instanceof Error?o.message:"Scene presenter update failed"),process.exit(c.GENERAL_ERROR)}});function Du(e){return e||Pu()}import{Command as rr}from"commander";var ma=new rr("inspect").description("Read a presentation-video project and its ordered scene timeline").argument("<project-id>","Presentation-video project UUID or editor slug").option("--team-id <id>","Workspace ID override").action(async function(e,t){let n=j(this,t);try{p(await y("getPresentationVideoProject",{projectId:e,teamId:n.teamId}))}catch(r){d(r instanceof Error?r.message:"Project read failed"),process.exit(c.GENERAL_ERROR)}}),Nu=new rr("list").description("List generated clips and uploaded recordings usable in a presentation video").argument("<project-id>","Target presentation-video project UUID or slug").option("--cursor <cursor>","Opaque cursor from the previous page").option("--limit <number>","Maximum clips to return",Uu).option("--query <text>","Filter clips by title").option("--team-id <id>","Workspace ID override").action(async function(e,t){let n=j(this,t);try{p(await y("listReusableVideoClips",{cursor:n.cursor,limit:n.limit,projectId:e,query:n.query,teamId:n.teamId}))}catch(r){d(r instanceof Error?r.message:"Clip list failed"),process.exit(c.GENERAL_ERROR)}}),pa=new rr("library").description("Reusable generated clips and uploaded recordings").addCommand(Nu);function Uu(e){let t=Number(e);if(!Number.isInteger(t)||t<1||t>100)throw new Error("--limit must be an integer between 1 and 100.");return t}var Fu=new H("status").description("Check a presentation generation run").argument("<run-id>","Run ID returned by presentation generation").option("--team-id <id>","Workspace ID override").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await T("createSlides",e,t.teamId);if(t.format==="json"){p(n);return}if(t.format==="quiet"){console.log(n.status);return}f(`Status: ${n.status}`),f(n.message),n.progress!==null&&f(`Progress: ${n.progress}%`),n.error&&d(n.error.message)}catch(n){d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Vu=new H("transform").description("Transform an uploaded PowerPoint import for review").argument("<import-id>","PowerPoint import ID returned by Mindframes").option("--idempotency-key <key>","Stable retry key").action(async function(e,t){let n=j(this,t);try{let r=await y("transformPresentationImport",{importId:e},n.idempotencyKey);p(r)}catch(r){d(r instanceof Error?r.message:"Transform failed"),process.exit(c.GENERAL_ERROR)}}),ju=new H("status").description("Read a PowerPoint import run").argument("<run-id>","Run ID returned by PowerPoint import").option("--team-id <id>","Workspace ID override").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await T("importPowerPoint",e,t.teamId);t.format==="json"?p(n):t.format==="quiet"?console.log(n.status):(f(`Status: ${n.status}`),f(n.message),n.error&&d(n.error.message))}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),ga=new H("import").description("Import a PowerPoint through the review-gated reconstruction pipeline").option("--file <path>","Path to the .pptx to import").option("--filename <name>","Override the uploaded filename").option("--mode <mode>","Import mode: preserve or rebrand","preserve").option("--branding-id <id>","Saved brand ID (required for rebrand mode)").option("--source-chat-id <id>","Associate the import with an existing chat").option("--title <title>","Presentation title").option("--idempotency-key <key>","Stable retry key").action(async e=>{if(!e.file){ga.help();return}e.file.toLowerCase().endsWith(".pptx")||(d("PowerPoint import requires a .pptx file."),process.exit(c.INVALID_INPUT));try{let t=await de(e.file),n=await y("importPowerPoint",{brandingId:e.brandingId,filename:e.filename??t.name,mode:e.mode,sourceChatId:e.sourceChatId,sourceUrl:t.url,title:e.title},e.idempotencyKey??qu());p(n)}catch(t){d(t instanceof Error?t.message:"Import failed"),process.exit(c.GENERAL_ERROR)}}).addCommand(Vu).addCommand(ju),Gu=new H("speaker-notes-status").description("Check a speaker-notes generation run").argument("<run-id>","Run ID returned by speaker-notes generation").option("--team-id <id>","Workspace ID override").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await T("generateSpeakerNotes",e,t.teamId);t.format==="json"?p(n):t.format==="quiet"?console.log(n.status):(f(`Status: ${n.status}`),f(n.message))}catch(n){d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Bu=new H("video-narration-status").description("Check a presentation-video narration regeneration run").argument("<run-id>","Run ID returned by video-narration").option("--team-id <id>","Workspace ID override").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await T("regeneratePresentationVideoNarration",e,t.teamId);t.format==="json"?p(n):t.format==="quiet"?console.log(n.status):(f(`Status: ${n.status}`),f(n.message))}catch(n){d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Wu=new H("status").description("Read a presentation-video MP4 render run").argument("<run-id>","Run ID returned by presentation video").option("--team-id <id>","Workspace ID override").action(async function(e,t){let n=j(this,t);try{p(await T("createPresentationVideo",e,n.teamId))}catch(r){d(r instanceof Error?r.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),Ku=new H("prepare").description("Create or reuse a presentation-video project for a presentation").argument("<presentation-id>","Presentation ID, document ID, or slug").option("--force-new","Create a fresh video project even if the current presentation version already has one").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key for this prepare request").option("--yes","Confirm creating or reusing the presentation video project").action(async function(e,t){let n=j(this,t);if(!n.yes)return d("Pass --yes to confirm preparing a presentation video.");try{p(await y("preparePresentationVideo",{forceNew:n.forceNew,presentationId:e,teamId:n.teamId},n.idempotencyKey))}catch(r){d(r instanceof Error?r.message:"Prepare failed"),process.exit(c.GENERAL_ERROR)}}),Ju=new H("settings").description("Update saved presentation-video render settings").argument("<project-id>","Presentation video project ID").option("--settings <json-or-path>","Settings patch as JSON or a path to a JSON file").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key for this settings update").option("--yes","Confirm updating presentation video settings").action(async function(e,t){let n=j(this,t);if(n.settings||(d("Pass --settings <json-or-path> with the patch to apply."),process.exit(c.INVALID_INPUT)),!n.yes)return d("Pass --yes to confirm updating presentation video settings.");try{p(await y("updatePresentationVideoSettings",{projectId:e,settings:await fa(n.settings),teamId:n.teamId},n.idempotencyKey))}catch(r){d(r instanceof Error?r.message:"Settings update failed"),process.exit(c.GENERAL_ERROR)}}),zu=new H("video").description("Create an MP4 video from a presentation").argument("[presentation-id]","Presentation ID, document ID, or slug").option("--project-id <id>","Advanced: render an already-configured presentation video project").option("--settings <json-or-path>","Settings patch as JSON or a path to a JSON file; only valid with presentation-id").option("--force-new","Create a fresh video project even if the current presentation version already has one").option("--force-rerender","Start a new MP4 render even when the current project fingerprint has a completed render").option("--render-engine <engine>","Renderer for this MP4 only: remotion (Standard) or hyperframes (Premium)").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key for this render request").option("--yes","Confirm starting the MP4 render").action(async(e,t)=>{if(!t.yes)return d("Pass --yes to confirm presentation video rendering.");try{if(!e&&!t.projectId)throw new Error("Pass a presentation id or --project-id.");if(e&&t.projectId)throw new Error("Pass either presentation id or --project-id, not both.");if(t.projectId&&(t.settings||t.forceNew))throw new Error("--settings and --force-new require a presentation id, not --project-id.");if(t.renderEngine&&t.renderEngine!=="remotion"&&t.renderEngine!=="hyperframes")throw new Error("--render-engine must be remotion or hyperframes.");p(await y("createPresentationVideo",{forceNew:t.forceNew,forceRerender:t.forceRerender,presentationId:e,projectId:t.projectId,renderEngine:t.renderEngine,settings:t.settings?await fa(t.settings):void 0,teamId:t.teamId},t.idempotencyKey))}catch(n){d(n instanceof Error?n.message:"Render failed"),process.exit(c.GENERAL_ERROR)}}).addCommand(Ku).addCommand(ma).addCommand(sa).addCommand(ua).addCommand(la).addCommand(ca).addCommand(Ju).addCommand(Wu).addCommand(da);async function fa(e){let t=e.trim(),n=t.startsWith("{")?t:await Mu(t,"utf-8"),r=JSON.parse(n);if(!r||typeof r!="object"||Array.isArray(r))throw new Error("--settings must be a JSON object.");return r}function Xu(e,t){return[...t,e]}var Hu=new H("speaker-notes").description("Generate or regenerate persisted presentation speaker notes").argument("<presentation-id>","Presentation ID or slug").option("--mode <mode>","ensure or regenerate","ensure").option("--style <style>","concise or detailed","concise").option("--instructions <text>","Instructions for every selected slide").option("--language <language>","Output language").option("--slide-id <id>","Target one slide (repeatable)",Xu,[]).option("--no-follow","Return immediately with the durable run ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{if(!["ensure","regenerate"].includes(t.mode))throw new Error("--mode must be ensure or regenerate");if(!["concise","detailed"].includes(t.style))throw new Error("--style must be concise or detailed");let n=await zr(e,{instructions:t.instructions,language:t.language,mode:t.mode,slideIds:t.slideId.length?t.slideId:void 0,style:t.style});if(t.format==="json"||!t.follow){p(n);return}f(`Speaker notes run: ${n.runId}`);let r=await bn(n.runId);await ha(r)}catch(n){d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Yu=new H("video-narration").description("Regenerate one slide narration in a presentation video draft").argument("<project-id>","Presentation video project ID").requiredOption("--slide-id <id>","Slide ID in the video draft").option("--instructions <text>","Optional regeneration instructions").option("--no-follow","Return immediately with the durable run ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await Xr(e,{instructions:t.instructions,slideId:t.slideId});if(t.format==="json"||!t.follow){p(n);return}f(`Narration run: ${n.runId}`),await ha(await bn(n.runId))}catch(n){d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}});async function ha(e){let t=e.body?.getReader();if(!t)throw new Error("Speaker notes event stream is unavailable");let n=new TextDecoder,r="";for(;;){let{done:o,value:i}=await t.read();r+=n.decode(i,{stream:!o});let a=r.split(`
|
|
56
114
|
|
|
57
|
-
`)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
115
|
+
`);r=a.pop()??"";for(let s of a){let l=s.split(`
|
|
116
|
+
`).find(m=>m.startsWith("data:"))?.slice(5).trim();if(!l)continue;let g=JSON.parse(l);g.eventType?.startsWith("slide.")&&f(`${g.eventType}${g.payload?.slideId?` \xB7 ${g.payload.slideId}`:""}`)}if(o)return}}var or=new H("presentation").description("Create an MP4 video from a finished presentation, plus speaker notes, PowerPoint import, and run status").addCommand(Fu).addCommand(ga).addCommand(Hu).addCommand(Gu).addCommand(zu).addCommand(Yu).addCommand(Bu);or.addHelpText("after",`
|
|
117
|
+
${Te.bold("Deck to MP4:")}
|
|
118
|
+
$ ${u.commands[0]} presentation video prepare <slug> ${Te.gray("# create/reuse the video project")}
|
|
119
|
+
$ ${u.commands[0]} presentation video inspect <project-id> ${Te.gray("# review the scene timeline")}
|
|
120
|
+
$ ${u.commands[0]} presentation video <slug> --yes ${Te.gray("# start the MP4 render")}
|
|
121
|
+
$ ${u.commands[0]} presentation video status <run-id> ${Te.gray("# follow the render")}
|
|
122
|
+
|
|
123
|
+
${Te.bold("See also:")}
|
|
124
|
+
${Te.gray("# Building a video from scratch instead of from a deck")}
|
|
125
|
+
$ ${u.commands[0]} video --help
|
|
126
|
+
`);import{writeFile as Qu}from"fs/promises";import lt from"chalk";import{Command as Zu}from"commander";import em from"ora";var ir=new Zu("scrape").description("Extract content from a URL").argument("<url>","URL to scrape").option("--team-id <id>","Workspace ID").option("-o, --output <path>","Save content to file").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let n=t.format,r=n==="human"?em("Scraping URL...").start():null;try{let o=await go(e,t.teamId);r?.stop(),(!o.success||!o.data)&&(d(o.error||"Failed to scrape URL"),process.exit(c.GENERAL_ERROR));let i=o.data;if(t.output&&(await Qu(t.output,i.content,"utf-8"),n==="human"&&C(`Content saved to: ${t.output}`)),n==="json"){let{cost:a,...s}=i;p(s);return}if(n==="quiet"){console.log(i.content);return}i.title&&(console.log(),console.log(`Title: ${i.title}`)),i.metadata?.description&&console.log(`Description: ${i.metadata.description}`),console.log(`URL: ${i.url}`),console.log(`Tokens: ~${i.metadata?.tokenUsage?.toLocaleString()||"unknown"}`),i.warning&&M(i.warning),t.output||(console.log(),console.log("--- Content ---"),console.log(i.content)),i.cost&&i.cost>0&&f(`Cost: $${i.cost.toFixed(6)}`)}catch(o){r?.stop(),d(o instanceof Error?o.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}});ir.addHelpText("after",`
|
|
127
|
+
${lt.bold("Examples:")}
|
|
128
|
+
${lt.gray("# Pull the readable content of a web page")}
|
|
129
|
+
$ ${u.commands[0]} scrape https://example.com/post
|
|
130
|
+
|
|
131
|
+
${lt.bold("See also:")}
|
|
132
|
+
${lt.gray("# Local files and documents, not just URLs")}
|
|
133
|
+
$ ${u.commands[0]} content extract ./paper.pdf
|
|
134
|
+
|
|
135
|
+
${lt.gray("# Spoken words out of audio or video")}
|
|
136
|
+
$ ${u.commands[0]} transcript --help
|
|
137
|
+
`);import ar from"chalk";import{Command as ut}from"commander";function Kt(e,t){t!=="quiet"&&p(e)}var tm=new ut("web").description("Search current public web sources").argument("<query>","Search query").option("--team-id <id>","Workspace ID").option("--max-results <count>","Maximum results","5").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await y("searchInternet",{maxResults:Number.parseInt(t.maxResults,10),query:e,teamId:t.teamId});Kt(n,t.format)}catch(n){d(n instanceof Error?n.message:"Search failed"),process.exit(c.GENERAL_ERROR)}}),nm=new ut("news").description("Search recent news").argument("<topic>","News topic").option("--team-id <id>","Workspace ID").option("--timeframe <value>","Timeframe hint").option("--max-results <count>","Maximum results","6").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await y("getNews",{maxResults:Number.parseInt(t.maxResults,10),teamId:t.teamId,timeframe:t.timeframe,topic:e});Kt(n,t.format)}catch(n){d(n instanceof Error?n.message:"News search failed"),process.exit(c.GENERAL_ERROR)}});function ya(e){let t=e?.trim().toLowerCase();if(!(!t||t==="any"||t==="all"))return t==="web"||t==="stock"?[t]:void 0}var rm=new ut("images").description("Search images across the web and licensed stock").argument("<query>","Image search query").option("--team-id <id>","Workspace ID").option("--max-results <count>","Maximum results","10").option("--aspect <aspect>","Aspect: any, square, wide, tall","any").option("--source <source>","Catalog: web, stock, or any (default: any)","any").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await y("searchImages",{aspect:t.aspect,maxResults:Number.parseInt(t.maxResults,10),query:e,sources:ya(t.source),teamId:t.teamId});Kt(n,t.format)}catch(n){d(n instanceof Error?n.message:"Image search failed"),process.exit(c.GENERAL_ERROR)}}),om=new ut("video").description("Search video across the web and licensed stock").argument("<query>","Video search query").option("--team-id <id>","Workspace ID").option("--max-results <count>","Maximum results","10").option("--source <source>","Catalog: web, stock, or any (default: any)","any").option("--orientation <value>","Orientation: landscape, portrait, square, any","any").option("--license <value>","License: free, premium, any","any").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await y("searchStockVideo",{license:t.license,maxResults:Number.parseInt(t.maxResults,10),orientation:t.orientation,query:e,sources:ya(t.source),teamId:t.teamId});Kt(n,t.format)}catch(n){d(n instanceof Error?n.message:"Video search failed"),process.exit(c.GENERAL_ERROR)}}),sr=new ut("search").description("Web, news, image, and stock-video search").addCommand(tm).addCommand(nm).addCommand(rm).addCommand(om);sr.addHelpText("after",`
|
|
138
|
+
${ar.bold("Examples:")}
|
|
139
|
+
$ ${u.commands[0]} search web "state of AI video 2026"
|
|
140
|
+
$ ${u.commands[0]} search news "vaccine approvals"
|
|
141
|
+
|
|
142
|
+
${ar.bold("See also:")}
|
|
143
|
+
${ar.gray("# `search images` and `search video` overlap with these")}
|
|
144
|
+
$ ${u.commands[0]} image search --help
|
|
145
|
+
$ ${u.commands[0]} video find --help
|
|
146
|
+
`);import{Command as cr}from"commander";import im from"ora";function dr(e,t){if(t==="json"){let{cost:n,...r}=e;p(r);return}if(t==="quiet"){console.log(e.audioUrl??e.requestId??e.id);return}f(`Request ID: ${e.requestId||e.id}`),f(`Status: ${e.status}`),e.duration&&f(`Duration: ${e.duration}s`),e.audioUrl&&C(`Audio URL: ${e.audioUrl}`),e.cost!==void 0&&f(`Cost: $${e.cost.toFixed(4)}`),e.error&&d(`Error: ${e.error}`)}var am=new cr("generate").description("Generate one SFX cue from a video's final facts and visual event").requiredOption("--request <path>","JSON file with video description, narration timings, and visual_event").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{!e.wait&&e.output&&(d("--output requires waiting for completion; remove --no-wait"),process.exit(c.INVALID_INPUT));let t;try{t=ta(e.request)}catch(i){d(i instanceof Error?i.message:String(i)),process.exit(c.INVALID_INPUT)}let n=e.format,r=n==="human"?im("Generating sound effect for video...").start():null,o;try{let i=await fo(t);if(o=i.requestId||i.id,!e.wait){r?.stop(),dr(i,n);return}let a=i.requestId||i.id;r&&(r.text=`Processing (ID: ${a})...`);let s=i.status==="completed"||i.status==="failed"?i:await Q(()=>kn(a),60,2e3);if(r?.stop(),s.status==="failed"&&(d(s.error||"Sound effects generation failed"),process.exit(c.GENERAL_ERROR)),e.output){if(!s.audioUrl)throw new Error("Sound effect completed without an audio URL");await z(s.audioUrl,e.output),n==="human"&&C(`Saved to: ${e.output}`)}dr(s,n)}catch(i){r?.stop(),d(re(i,o),n),process.exit(c.GENERAL_ERROR)}}),sm=new cr("status").description("Check status of a sound effects generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{dr(await kn(e),t.format)}catch(n){d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),ba=new cr("sfx").description("Video-aware sound effects generation commands").addCommand(am).addCommand(sm);import{writeFile as dm}from"fs/promises";import{Command as cm}from"commander";import lm from"ora";var wa=new cm("transcript").description("Fetch a YouTube video's transcript").argument("<url>","YouTube URL or 11-character video id").option("--team-id <id>","Workspace ID").option("--language <codes>","Comma-separated language priority list, e.g. 'en,de' or 'asr-en'").option("--no-timestamps","Print plain text without [m:ss] prefixes").option("-o, --output <path>","Save the transcript to a file").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let n=t.format,r=n==="human"?lm("Fetching transcript...").start():null;try{let o=await po(e,t.teamId,t.language);r?.stop();let i=t.timestamps?o.cues.map(a=>`[${a.timestamp}] ${a.text}`).join(`
|
|
147
|
+
`):o.cues.map(a=>a.text).join(`
|
|
148
|
+
`);if(t.output&&(await dm(t.output,i,"utf-8"),n==="human"&&C(`Transcript saved to: ${t.output}`)),n==="json"){p(o);return}if(n==="quiet"){console.log(i);return}console.log(),console.log(`Title: ${o.title}`),o.channelName&&console.log(`Channel: ${o.channelName}`),o.durationFormatted&&console.log(`Duration: ${o.durationFormatted}`),console.log(`Language: ${o.language??"unknown"}${o.isAutoGenerated?" (auto-generated)":""}`),console.log(`Words: ${o.wordCount.toLocaleString()}`),console.log(`URL: ${o.url}`),o.truncated&&M("Transcript was truncated to fit the response budget."),t.output||(console.log(),console.log("--- Transcript ---"),console.log(i)),o.totalCost&&o.totalCost>0&&f(`Cost: $${o.totalCost.toFixed(6)}`)}catch(o){r?.stop(),d(o instanceof Error?o.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}});import{mkdir as um,writeFile as va}from"fs/promises";import{dirname as mm}from"path";import{Command as Jt}from"commander";import Ca from"ora";var xa="en-US-Neural2-C",ka="gemini",pm=new Jt("generate").description("Generate speech from text").requiredOption("-t, --text <text>","Text to convert to speech").requiredOption("-o, --output <path>","Output file path").option("--voice-id <voiceId>","Voice ID (e.g., ElevenLabs: 21m00Tcm4TlvDq8ikWAM)").option("--voice <voice>",`Voice name (default: ${xa})`).option("-p, --provider <provider>",`Provider: gemini, elevenlabs, openai (default: ${ka})`).option("-m, --model <model>","Model (provider-specific)").option("-s, --speed <speed>","Speech speed 0.1-4.0 (default: 1.0)").option("-l, --lang <lang>","Language or locale hint (for example en-US)").option("--speakers <json>",`Multi-speaker (Gemini, max 2): '[{"name":"Joe","voice":"Kore"},{"name":"Jane","voice":"Puck"}]'`).option("-i, --instructions <text>","Style prompt (OpenAI gpt-4o-mini-tts): tone, accent, speed, emotion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format,n=t==="human"?Ca("Generating speech...").start():null,r;e.speed&&(r=parseFloat(e.speed),(Number.isNaN(r)||r<.1||r>4)&&(n?.stop(),d("Speed must be between 0.1 and 4.0"),process.exit(c.INVALID_INPUT)));let o;if(e.speakers)try{o=JSON.parse(e.speakers),(!Array.isArray(o)||o.length===0||o.length>2||o.some(a=>!a||typeof a!="object"||typeof a.name!="string"||!a.name.trim()||typeof a.voice!="string"||!a.voice.trim()))&&(n?.stop(),d("Speakers must be an array with 1-2 name/voice items"),process.exit(c.INVALID_INPUT))}catch{n?.stop(),d(`Invalid --speakers JSON. Example: '[{"name":"Joe","voice":"Kore"}]'`),process.exit(c.INVALID_INPUT)}let i;try{let a=e.voiceId,s=e.voice||xa,l=e.provider||ka,g={};o&&(g.gemini={speakers:o}),e.instructions&&(g.openai={instructions:e.instructions});let m=await io({text:e.text,options:{language:e.lang,provider:l,voiceId:a,voice:s,model:e.model,speed:r,providerOptions:Object.keys(g).length>0?g:void 0}});i=m.requestId,n?.stop();let w=e.output.endsWith(`.${m.format}`)?e.output:`${e.output}.${m.format}`;await um(mm(w),{recursive:!0}),await va(w,m.audioData);let v=w.replace(/\.[^.]+$/,".txt");if(await va(v,e.text,"utf-8"),t==="json"){p({status:"completed",output:w,audioUrl:m.audioUrl,duration:m.duration,provider:m.provider,format:m.format,voice_id:m.voiceId??null,voice:m.voice??s,model:m.model??e.model??null,speed:m.speed??r??1,lang:m.language??e.lang??null,langApplied:m.langApplied??!1,timestamps:m.timestamps??null,words:m.wordTimings??null});return}if(t==="quiet"){console.log(w);return}C(`Saved to: ${w}`),f(`Duration: ${m.duration.toFixed(2)}s`),f(`Provider: ${m.provider}`),f(`Cost: $${m.cost.toFixed(6)}`)}catch(a){n?.stop(),d(re(a,i),t),process.exit(c.GENERAL_ERROR)}}),gm=new Jt("voices").description("List available voices").option("-p, --provider <provider>","Filter by provider: gemini, elevenlabs, openai").option("-f, --format <format>","Output format: human, json","human").action(async e=>{let t=e.format==="human"?Ca("Fetching voices...").start():null;try{let n=await so(e.provider);if(t?.stop(),e.format==="json"){p(n.voices);return}if(Array.isArray(n.voices)){let r=e.provider?.toUpperCase()||"VOICES";console.log(),console.log(`${r} Voices:`),console.log("-".repeat(50));for(let o of n.voices)console.log(` ${o.name} (${o.id})`),console.log(` ${o.description}`)}else for(let[r,o]of Object.entries(n.voices))if(!(!o||o.length===0)){console.log(),console.log(`${r.toUpperCase()} Voices:`),console.log("-".repeat(50));for(let i of o)console.log(` ${i.name} (${i.id})`),console.log(` ${i.description}`)}}catch(n){t?.stop(),d(n instanceof Error?n.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),fm=new Jt("status").description("Read speech generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").option("-f, --format <format>","Output format: json","json").action(async(e,t)=>{try{p(await T("generateSpeech",e,t.teamId))}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),Ia=new Jt("tts").description("Text-to-speech commands").addCommand(pm).addCommand(gm).addCommand(fm);import{execSync as Yt,spawnSync as Vm}from"child_process";import{access as ur,readFile as jm,rm as Ua}from"fs/promises";import{resolve as gt}from"path";import pt from"chalk";import{Command as Y}from"commander";import Gm from"ffmpeg-static";import qa from"ora";import{Command as hm}from"commander";import ym from"ora";import{execSync as bm,spawnSync as Fe}from"child_process";import{mkdirSync as Ra,existsSync as Xt,readFileSync as $a,writeFileSync as Sa}from"fs";import{join as Ee,basename as wm}from"path";import{tmpdir as vm}from"os";function Ht(e,t){try{return bm(t,{stdio:"ignore"}),!0}catch{return!1}}function zt(e){let t=Math.floor(e/60),n=Math.floor(e%60),r=Math.round(e%1*10);return`${t}:${String(n).padStart(2,"0")}.${r}`}function xm(e){let t=Fe("ffprobe",["-v","error","-show_entries","format=duration","-of","default=noprint_wrappers=1:nokey=1",e],{encoding:"utf-8"});return parseFloat(t.stdout.trim())}function km(e,t){let n=Fe("ffmpeg",["-i",e,"-vf",`select='gt(scene,${t})',showinfo`,"-vsync","vfr","-f","null","-"],{encoding:"utf-8",timeout:12e4}),r=[0],o=(n.stderr||"").split(`
|
|
149
|
+
`);for(let i of o){let a=i.match(/pts_time:([\d.]+)/);if(a){let s=parseFloat(a[1]);s-r[r.length-1]>=1.5&&r.push(s)}}return r}function Cm(e,t,n){let r=[];for(let o=0;o<t.length;o++){let i=t[o]+.5,a=Ee(n,`scene_${String(o+1).padStart(2,"0")}.jpg`);Fe("ffmpeg",["-ss",String(i),"-i",e,"-vframes","1","-q:v","2","-y",a],{stdio:"ignore",timeout:3e4}),r.push(a)}return r}function Im(e,t){let n=Ee(t,"audio.wav");if(Fe("ffmpeg",["-i",e,"-vn","-acodec","pcm_s16le","-ar","16000","-ac","1","-y",n],{stdio:"ignore",timeout:6e4}),!Xt(n)||!Ht("whisper","which whisper")&&!Ht("whisper-cpp","which whisper-cpp"))return null;let o=Fe("whisper",[n,"--model","base","--output_format","json","--output_dir",t,"--language","en"],{encoding:"utf-8",timeout:3e5}),i=Ee(t,"audio.json");if(!Xt(i))return null;try{let a=JSON.parse($a(i,"utf-8"));return{text:a.text||"",segments:(a.segments||[]).map(s=>({start:s.start,end:s.end,text:s.text?.trim()||""}))}}catch{return null}}function Rm(e,t){let n=$a(e).toString("base64"),r=JSON.stringify({model:t,prompt:"Describe this image in one short sentence (max 15 words). Focus on: what is shown, the setting, key objects or people.",images:[n],stream:!1}),o=Fe("curl",["-s","-X","POST","http://localhost:11434/api/generate","-H","Content-Type: application/json","-d",r],{encoding:"utf-8",timeout:6e4});try{return(JSON.parse(o.stdout).response||"").replace(/!!!IMAGE!!!/gi,"").replace(/\[image[^\]]*\]/gi,"").trim()||"Unable to describe frame"}catch{return"Unable to describe frame"}}var Ta=new hm("analyze").description("Analyze a video file: detect scenes, transcribe audio, describe frames").argument("<file>","Path to video file").option("-o, --output <path>","Output directory for analysis results").option("-t, --threshold <number>","Scene detection threshold (0-1, lower = more scenes)","0.3").option("-m, --model <name>","Ollama vision model","moondream").option("--no-vision","Skip frame description (faster)").option("--no-transcribe","Skip audio transcription").option("-f, --output-format <format>","Output format: human, json","human").action(async(e,t)=>{let n=ym();Xt(e)||(console.error(`File not found: ${e}`),process.exit(1)),Ht("ffmpeg","which ffmpeg")||(console.error("ffmpeg is required. Install: brew install ffmpeg"),process.exit(1)),t.vision!==!1&&!Ht("ollama","which ollama")&&(console.error("ollama is required for vision. Install: brew install ollama"),process.exit(1));let r=t.output||Ee(vm(),`video-analyze-${Date.now()}`);Ra(r,{recursive:!0});let o=Ee(r,"frames");Ra(o,{recursive:!0});let i=parseFloat(t.threshold),a=t.model;n.start("Reading video metadata...");let s=xm(e);n.succeed(`Video: ${zt(s)} (${s.toFixed(1)}s)`),n.start("Detecting scenes...");let l=km(e,i);n.succeed(`Detected ${l.length} scenes`),n.start("Extracting key frames...");let g=Cm(e,l,o);n.succeed(`Extracted ${g.length} key frames`);let m=null;t.transcribe!==!1&&(n.start("Transcribing audio..."),m=Im(e,r),m?n.succeed(`Transcribed: ${m.segments.length} segments`):n.warn("Transcription skipped (whisper not installed: pip install openai-whisper)"));let w=[];for(let b=0;b<l.length;b++){let $=l[b],x=b<l.length-1?l[b+1]:s,k="";t.vision!==!1&&Xt(g[b])&&(n.start(`Describing scene ${b+1}/${l.length}...`),k=Rm(g[b],a),n.succeed(`Scene ${b+1}: ${k}`)),w.push({id:b+1,startTime:$,endTime:x,duration:x-$,framePath:g[b],description:k})}let v={source:e,duration:s,scenes:w,transcript:m};if(t.outputFormat==="json"){let b=JSON.stringify(v,null,2),$=Ee(r,"analysis.json");Sa($,b),console.log(b)}else{let b=["# Video Analysis","",`**Source:** ${wm(e)}`,`**Duration:** ${zt(s)} (${s.toFixed(1)}s)`,`**Scenes:** ${w.length}`,"","## Scenes",""];for(let k of w){if(b.push(`### Scene ${k.id} (${zt(k.startTime)} - ${zt(k.endTime)}) [${k.duration.toFixed(1)}s]`),k.description&&b.push(`**Visual:** ${k.description}`),m?.segments){let R=m.segments.filter(D=>D.end>k.startTime&&D.start<k.endTime);R.length>0&&b.push(`**Audio:** ${R.map(D=>D.text).join(" ").trim()}`)}b.push(`**Frame:** ${k.framePath}`),b.push("")}m?.text&&(b.push("## Full Transcript"),b.push(""),b.push(m.text.trim()),b.push(""));let $=b.join(`
|
|
150
|
+
`),x=Ee(r,"analysis.md");Sa(x,$),console.log($),console.log(`
|
|
151
|
+
Results saved to: ${r}`)}});import{mkdir as Sm}from"fs/promises";import{join as $m}from"path";import{Command as mt}from"commander";import Aa from"ora";var Tm=5e3,Em=120,Ea=5;function Am(e,t){return[...t,e]}function Om(e,t){try{let n=new URL(e);if(n.protocol!=="https:"&&n.protocol!=="http:")throw new Error;return n.toString()}catch{throw new Error(`${t} must be a public HTTP(S) URL`)}}function Oa(e){if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e))throw new Error("Source video id must be a UUID");return e}function Pm(e){if(e.mode&&!["speed","precision"].includes(e.mode))throw new Error("Lip-sync mode must be speed or precision");return{audioUrl:Om(e.audioUrl,"Audio URL"),...e.mode?{mode:e.mode}:{},sourceVideoId:Oa(e.sourceVideoId)}}function _m(e){let t=[],n=new Set;for(let o of e.targetLanguage){let i=o.trim(),a=i.toLocaleLowerCase("en-US");!i||n.has(a)||(n.add(a),t.push(i))}if(t.length===0)throw new Error("Provide at least one target language");if(t.length>Ea)throw new Error(`Provide at most ${Ea} target languages`);if(t.find(o=>o.length<2||o.length>40))throw new Error("Target languages must be between 2 and 40 characters");return{...e.captions?{captions:!0}:{},sourceVideoId:Oa(e.sourceVideoId),targetLanguages:t}}function Lm(e){return e.error||e.status==="failed"||e.status==="cancelled"?"failed":e.done||e.status==="completed"?"completed":e.status==="in_progress"||e.status==="processing"?"processing":"pending"}function lr(e){return{requestId:e.runId,status:Lm(e),...e.entity?.url?{videoUrl:e.entity.url}:{},...e.output?{output:e.output}:{},...e.error?.message?{error:e.error.message}:{}}}function Pa(e){return{requestId:e.runId,status:"pending"}}async function _a(e,t){return Q(async()=>lr(await T(e,t)),Em,Tm)}function Ve(e,t){if(t==="json"){p(e);return}if(t==="quiet"){console.log(e.videoUrl??e.requestId);return}f(`Request ID: ${e.requestId}`),f(`Status: ${e.status}`),e.videoUrl&&C(`Video URL: ${e.videoUrl}`),e.error&&d(`Error: ${e.error}`)}function Dm(e){return Array.isArray(e?.languages)?e.languages.filter(t=>{if(!t||typeof t!="object"||Array.isArray(t))return!1;let n=t;return typeof n.language=="string"&&(n.status==="completed"||n.status==="failed")&&typeof n.videoId=="string"&&(n.videoUrl===null||typeof n.videoUrl=="string")}):[]}function Nm(e){return`${e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"translation"}.mp4`}function Um(e,t){let n=Nm(e);if(!t.has(n))return t.add(n),n;let r=n.slice(0,-4),o=2;for(;t.has(`${r}-${o}.mp4`);)o+=1;let i=`${r}-${o}.mp4`;return t.add(i),i}async function qm(e,t){let n=Dm(e.output).filter(i=>i.status==="completed"&&!!i.videoUrl);if(n.length===0)throw new Error("Video translation completed without downloadable results");await Sm(t,{recursive:!0});let r=[],o=new Set;for(let i of n){let a=$m(t,Um(i.language,o));await z(i.videoUrl,a),r.push({language:i.language,path:a})}return r}var Mm=new mt("status").description("Check a video lip-sync request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet").action(async function(e,t){let r=j(this,t).format??"human";try{Ve(lr(await T("lipsyncVideo",e)),r)}catch(o){d(o instanceof Error?o.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),La=new mt("lipsync").description("Re-voice a ready workspace video with a public audio URL").requiredOption("--source-video-id <uuid>","READY workspace video UUID").requiredOption("--audio-url <url>","Public replacement voice audio URL").option("--mode <mode>","Quality mode: speed or precision","speed").requiredOption("-o, --output <path>","Local output video path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format==="human"?Aa("Starting video lip-sync...").start():null,n;try{let r=await bo(Pm(e));if(n=r.runId,!e.wait){t?.stop(),Ve(Pa(r),e.format);return}t&&(t.text="Waiting for video lip-sync...");let o=await _a("lipsyncVideo",r.runId);if(o.status==="failed")throw new Error(o.error??"Video lip-sync failed");if(!o.videoUrl)throw new Error("Video lip-sync completed without a video URL");t&&(t.text="Downloading lip-synced video..."),await z(o.videoUrl,e.output),t?.stop(),Ve(o,e.format),e.format==="human"&&C(`Saved to: ${e.output}`)}catch(r){t?.stop(),d(re(r,n),e.format),process.exit(c.GENERAL_ERROR)}}).addCommand(Mm),Da=new mt("translate-languages").description("List supported video translation target languages").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{try{let t=await wo();if(e.format==="json")p({languages:t});else for(let n of t)console.log(n)}catch(t){d(t instanceof Error?t.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Fm=new mt("status").description("Check a video translation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet").action(async function(e,t){let r=j(this,t).format??"human";try{Ve(lr(await T("translateVideo",e)),r)}catch(o){d(o instanceof Error?o.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Na=new mt("translate").description("Translate a ready workspace video into one or more languages").requiredOption("--source-video-id <uuid>","READY workspace video UUID").option("--target-language <language>","Target language (repeatable)",Am,[]).option("--captions","Burn provider-generated captions into translated videos").requiredOption("--output-dir <path>","Directory for translated videos").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format==="human"?Aa("Starting video translation...").start():null,n;try{let r=await vo(_m(e));if(n=r.runId,!e.wait){t?.stop(),Ve(Pa(r),e.format);return}t&&(t.text="Waiting for video translations...");let o=await _a("translateVideo",r.runId);if(o.status==="failed")throw new Error(o.error??"Video translation failed");t&&(t.text="Downloading translated videos...");let i=await qm(o,e.outputDir);if(t?.stop(),e.format==="json")p({...o,downloads:i});else if(e.format==="quiet")for(let a of i)console.log(a.path);else{Ve(o,e.format);for(let a of i)C(`${a.language}: ${a.path}`)}}catch(r){t?.stop(),d(re(r,n),e.format),process.exit(c.GENERAL_ERROR)}}).addCommand(Fm);async function Bm(){return process.stdin.isTTY?null:new Promise(e=>{let t="",n=!1;process.stdin.setEncoding("utf-8"),process.stdin.on("data",r=>{t+=r}),process.stdin.on("end",()=>{n||(n=!0,e(t.trim()||null))}),process.stdin.on("error",()=>{n||(n=!0,e(null))}),setTimeout(()=>{!n&&!t&&(n=!0,e(null))},500)})}function Wm(e){let t=e?.trim().toLowerCase();if(!(!t||t==="any"||t==="all"))return t==="web"||t==="stock"?[t]:void 0}var Km=new Y("find").description("Find video clips across the web and licensed stock").argument("<query>","Search query").option("-n, --max-results <count>","Maximum number of results","10").option("-o, --orientation <type>","Video orientation: landscape, portrait, square, any","any").option("-l, --license <type>","License type: free, premium, any","free").option("--source <source>","Catalog: web, stock, or any (default: any)","any").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let{maxResults:n,orientation:r,license:o,format:i}=t,a=i==="human"?qa("Finding videos...").start():null;try{let s=await mo({query:e,options:{maxResults:parseInt(n,10),orientation:r,license:o,sources:Wm(t.source)}});a?.stop();let l=s.data.results.flatMap(g=>g.results);if(i==="json"){let{totalCost:g,...m}=s.data;p({...s,data:m});return}if(i==="quiet"){l.forEach(g=>{console.log(g.embedUrl||g.previewUrl||g.thumbnailUrl)});return}if(l.length===0){f("No videos found");return}C(`Found ${l.length} videos for "${e}"`),console.log(),l.forEach((g,m)=>{let w=g.kind==="embed";console.log(`[${m+1}] ${g.title}`),console.log(` ${w?"Embed":"URL"}: ${g.embedUrl||g.previewUrl||g.thumbnailUrl}`),console.log(w?` Duration: ${g.duration}s | Embed only \u2014 plays in the host player, do not download`:` Duration: ${g.duration}s | Size: ${g.width}x${g.height}`),console.log(` Provider: ${g.provider}`),console.log()}),f(`Total cost: $${s.data.totalCost.toFixed(4)}`)}catch(s){a?.stop(),d(s instanceof Error?s.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),Jm=new Y("create").description("Generate one video from a pinned storyboard revision").requiredOption("--storyboard-revision <id>","Immutable storyboard revision UUID").option("--source-video <id>","Ready video from an earlier create call to use as base footage").option("--draft","Generate at 480p for a faster draft").option("--team-id <id>","Workspace ID").option("--approval-receipt <receipt>","Receipt returned by the browser confirmation URL; omit on the first call to get that URL").requiredOption("--idempotency-key <key>","Stable invocation key bound into the approval receipt").option("--yes","Approve paid video generation").action(async e=>{if(!e.yes)return d("Pass --yes to approve video generation");try{p(await xo({quality:e.draft?"draft":void 0,sourceVideoId:e.sourceVideo,storyboardRevisionId:e.storyboardRevision,teamId:e.teamId},e.approvalReceipt,e.idempotencyKey))}catch(t){d(t instanceof Error?t.message:"Generation failed"),process.exit(c.GENERAL_ERROR)}});async function Ma(e){let t=e==="-"?await Bm():await jm(e,"utf-8");if(!t?.trim())throw new Error(e==="-"?"No storyboard Markdown was received on stdin":`Storyboard file is empty: ${e}`);return t}async function Fa(e,t={}){if(e)return e.length===1&&e[0]==="none"?[]:Promise.all(e.map(async n=>{let r;try{let v=JSON.parse(n);if(!v||typeof v!="object"||Array.isArray(v))throw new Error("Media binding must be a JSON object");r=v}catch(v){throw new Error(`Invalid --media binding. Pass one JSON object per binding, for example '{"handle":"actress-photo","path":"./actress.jpg","purpose":"identity"}'.`,{cause:v})}let o=ce(r.handle),i=ce(r.path),a=ce(r.fileUrl)??ce(r.url),s=ce(r.purpose),l=ce(r.videoRole),g=ce(r.analysisId)??ce(r.analysis);if(!o||!i&&!a||!s)throw new Error("Invalid --media binding. handle, purpose, and either path or url are required strings.");if(i&&a)throw new Error("A media binding cannot contain both path and url.");if(i&&s==="source"&&g)throw new Error("A source analysis is bound to the original uploaded URL. Reuse the url/name/mime/size binding printed by 'video reference analyze' instead of uploading path again.");if(i&&!t.uploadIdempotencyKey)throw new Error("Local storyboard media requires the command idempotency key.");let m=i?await de(gt(i),{idempotencyKey:`${t.uploadIdempotencyKey}:media:${o}`}):{name:ce(r.name),size:Number(r.size),type:ce(r.mime),url:a};if(!m.name||!m.type||!m.url||!Number.isSafeInteger(m.size)||m.size<=0)throw new Error(`URL media binding "${n}" requires name, mime, and positive integer size.`);let w=m.type.startsWith("video/")?"video":m.type.startsWith("audio/")?"audio":m.type.startsWith("image/")?"image":null;if(!w)throw new Error(`Unsupported media type for ${i??a}: ${m.type}`);if(!["source","style","identity","required"].includes(s))throw new Error(`Invalid media purpose: ${s}`);if(l&&!["reference","first-frame","last-frame"].includes(l))throw new Error(`Invalid videoRole: ${l}`);if(l&&w!=="image")throw new Error("videoRole is supported only for image bindings.");return{...g?{analysisId:g}:{},fileUrl:m.url,handle:o,kind:w,mime:m.type,name:m.name,purpose:s,size:m.size,...l?{videoRole:l}:{}}}))}function ce(e){return typeof e=="string"&&e.length>0?e:void 0}var zm=new Y("create").description("Create a versioned Markdown video storyboard").requiredOption("--file <path>","Markdown file path, or - for stdin").requiredOption("--duration <seconds>","Exact total duration in seconds").requiredOption("--aspect <ratio>","Output aspect: 9:16, 16:9, or 1:1").option("--title <text>","Artifact label").requiredOption("--idempotency-key <key>","Stable key to reuse when retrying this exact create").option("--media <binding...>",'One JSON binding per asset: {"handle":"actress-photo","path":"./actress.jpg","purpose":"identity"}').option("--team-id <id>","Workspace ID").action(async e=>{try{p(await y("createVideoStoryboard",{aspectRatio:e.aspect,durationSec:Number(e.duration),mediaBindings:await Fa(e.media,{uploadIdempotencyKey:e.idempotencyKey}),storyboardMarkdown:await Ma(e.file),teamId:e.teamId,title:e.title},e.idempotencyKey))}catch(t){d(t instanceof Error?t.message:"Storyboard creation failed"),process.exit(c.GENERAL_ERROR)}}),Xm=new Y("revise").description("Append an immutable revision to a video storyboard").argument("<storyboard-id>","Storyboard UUID").requiredOption("--base-revision <id>","Revision UUID that was edited").requiredOption("--file <path>","Complete Markdown file path, or - for stdin").requiredOption("--duration <seconds>","Exact total duration in seconds").requiredOption("--aspect <ratio>","Output aspect: 9:16, 16:9, or 1:1").option("--title <text>","Replacement artifact label").requiredOption("--idempotency-key <key>","Stable key to reuse when retrying this exact revision").option("--change-summary <text>","Short description of this revision").option("--media <binding...>",'Replace media bindings with one JSON object per asset; use the JSON printed by "video reference analyze" for analyzed source video').option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await y("reviseVideoStoryboard",{aspectRatio:t.aspect,baseRevisionId:t.baseRevision,changeSummary:t.changeSummary,durationSec:Number(t.duration),mediaBindings:await Fa(t.media,{uploadIdempotencyKey:t.idempotencyKey}),storyboardId:e,storyboardMarkdown:await Ma(t.file),teamId:t.teamId,title:t.title},t.idempotencyKey))}catch(n){d(n instanceof Error?n.message:"Storyboard revision failed"),process.exit(c.GENERAL_ERROR)}}),Hm=new Y("show").description("Read a storyboard; print Markdown unless --json is used").argument("<storyboard-id>","Storyboard UUID").option("--revision <id>","Read one immutable revision UUID").option("--team-id <id>","Workspace ID").option("--json","Print the complete structured response").action(async(e,t)=>{try{let n=await y("readVideoStoryboard",{revisionId:t.revision,storyboardId:e,teamId:t.teamId});if(t.json){p(n);return}console.error(`${n.storyboard.title} \xB7 revision ${n.storyboard.revision.versionNumber} (${n.storyboard.revision.id})`),console.log(n.storyboard.revision.storyboardMarkdown)}catch(n){d(n instanceof Error?n.message:"Storyboard read failed"),process.exit(c.GENERAL_ERROR)}}),Ym=new Y("storyboard").description("Create, revise, and read Markdown video storyboards").addCommand(zm).addCommand(Xm).addCommand(Hm),Qm=new Y("analyze").description("Upload and analyze a reference video").requiredOption("--file <path>","Local reference video").requiredOption("--idempotency-key <key>","Stable key to reuse when retrying this exact analysis").option("--user-request <text>","What the analysis should focus on").option("--team-id <id>","Workspace ID").option("--no-wait","Return the durable run immediately").option("--json","Print the complete structured result").action(async e=>{try{let t=await de(gt(e.file),{idempotencyKey:`video-reference-analysis:${e.idempotencyKey}:source`});if(!t.type.startsWith("video/"))throw new Error(`Reference analysis requires a video, got ${t.type}`);let n=await ko({idempotencyKey:e.idempotencyKey,sourceUrl:t.url,teamId:e.teamId,userRequest:e.userRequest}),r={analysisId:n.runId,fileUrl:t.url,handle:"source-video",kind:"video",mime:t.type,name:t.name,purpose:"source",size:t.size};if(e.wait===!1){p({...n,binding:r,source:t});return}let o=await Co(n.runId,e.teamId);if(e.json){p({...o,binding:r,source:t});return}let i=o.output?.analysis??{};console.log(typeof i.promptContext=="string"?i.promptContext:JSON.stringify(o.output,null,2)),console.error(`Analysis run: ${n.runId}`),console.error(`Storyboard media JSON: ${JSON.stringify(r)}`)}catch(t){d(t instanceof Error?t.message:"Reference analysis failed"),process.exit(c.GENERAL_ERROR)}}),Zm=new Y("status").description("Read a video-reference analysis run").argument("<run-id>","Analysis run UUID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await T("analyzeVideoReference",e,t.teamId))}catch(n){d(n instanceof Error?n.message:"Status read failed"),process.exit(c.GENERAL_ERROR)}}),ep=new Y("reference").description("Upload and inspect reusable video references").addCommand(Qm).addCommand(Zm),tp=new Y("status").description("Read video background status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await T("createVideo",e,t.teamId))}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),np=new Y("inject").description("Inject thumbnail into video (first frame + file icon)").requiredOption("-i, --video <path>","Input video file").requiredOption("-t, --thumbnail <path>","Thumbnail image").option("-o, --output <path>","Output video (default: video-with-thumb.mp4)").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format,n=t==="human"?qa("Injecting thumbnail...").start():null;try{let r=gt(e.video),o=gt(e.thumbnail),i=e.output?gt(e.output):r.replace(/\.mp4$/,"-with-thumb.mp4");await ur(r),await ur(o);let a=Gm;if(a)try{await ur(a)}catch{a=null}if(!a)try{a=Yt("which ffmpeg",{encoding:"utf8"}).trim()}catch{throw new Error("ffmpeg not found. Install ffmpeg or ffmpeg-static.")}let s=Vm(a,["-i",r],{encoding:"utf8"});if(s.error)throw new Error(`Failed to probe video: ${s.error.message}`);let l=(s.stderr??"").match(/(\d{2,5})x(\d{2,5})/);if(!l)throw new Error("Could not detect video dimensions");let[,g,m]=l;n&&(n.text="Creating thumbnail frame...");let w="/tmp/cc-thumb-frame.mp4";Yt(`${a} -y -loop 1 -i "${o}" -vf "scale=${g}:${m}:force_original_aspect_ratio=decrease,pad=${g}:${m}:(ow-iw)/2:(oh-ih)/2,setsar=1" -t 0.033 -r 30 -c:v libx264 -pix_fmt yuv420p "${w}"`,{stdio:"pipe"}),n&&(n.text="Concatenating with video...");let v="/tmp/cc-concat-video.mp4";if(Yt(`${a} -y -i "${w}" -i "${r}" -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" -map 1:a -c:v libx264 -preset ultrafast -crf 18 -c:a copy "${v}"`,{stdio:"pipe"}),n&&(n.text="Adding file icon thumbnail..."),Yt(`${a} -y -i "${v}" -i "${o}" -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic "${i}"`,{stdio:"pipe"}),await Ua(w,{force:!0}),await Ua(v,{force:!0}),n?.stop(),t==="json"){p({success:!0,output:i});return}if(t==="quiet"){console.log(i);return}C(`Thumbnail injected: ${i}`)}catch(r){n?.stop(),d(r instanceof Error?r.message:"Unknown error"),process.exit(c.GENERAL_ERROR)}}),rp=new Y("thumbnail").description("Thumbnail operations").addCommand(np),Qt=new Y("video").description("Build a video from scratch \u2014 brief or storyboard to stock and generated footage, translation, lipsync, analysis (for a finished deck, use presentation video)").addCommand(Km).addCommand(rp).addCommand(La).addCommand(Da).addCommand(Na).addCommand(pa).addCommand(Ta);Qt.addCommand(Jm).addCommand(Ym).addCommand(ep).addCommand(tp);Qt.addHelpText("after",`
|
|
152
|
+
${pt.bold("Examples:")}
|
|
153
|
+
${pt.gray("# Storyboard-first video from a brief")}
|
|
154
|
+
$ ${u.commands[0]} video storyboard create --file brief.md --duration 60 --aspect 16:9 --idempotency-key launch-1
|
|
67
155
|
|
|
68
|
-
|
|
156
|
+
${pt.gray("# Find licensed stock footage")}
|
|
157
|
+
$ ${u.commands[0]} video find "city timelapse"
|
|
69
158
|
|
|
70
|
-
|
|
159
|
+
${pt.bold("See also:")}
|
|
160
|
+
${pt.gray("# Already have a deck? Render it to MP4 (narration, avatar, captions)")}
|
|
161
|
+
$ ${u.commands[0]} presentation video <slug>
|
|
162
|
+
`);import{Command as mr}from"commander";var op=new mr("create").description("Start background visual-abstract generation").argument("<brief>","Dense source brief").option("--title <title>","Artifact title").option("--aspect <ratio>","1:1, 16:9, 9:16, 4:3, or 3:4","1:1").option("--quality <tier>","fast, best, or enterprise","best").option("--preset <preset>","Output preset","executive-one-pager").option("--output <type>","raster or vector","raster").option("--branding-id <id>","Branding ID").option("--reference-url <url...>","Up to three image URLs to use as visual references").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(e,t)=>{if(!t.yes)return d("Pass --yes to approve visual-abstract generation");try{let n=Array.isArray(t.referenceUrl)?t.referenceUrl.slice(0,3).map((r,o)=>({label:`visual-reference-${o+1}`,url:r})):void 0;p(await y("createVisualAbstract",{approvalReceipt:"cli:--yes",aspectRatio:t.aspect,brandingId:t.brandingId,brief:e,outputPreset:t.preset,outputType:t.output,qualityTier:t.quality,referenceImages:n,teamId:t.teamId,title:t.title}))}catch(n){d(n instanceof Error?n.message:"Generation failed"),process.exit(c.GENERAL_ERROR)}}),ip=new mr("status").description("Read visual-abstract generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{p(await T("createVisualAbstract",e,t.teamId))}catch(n){d(n instanceof Error?n.message:"Status failed"),process.exit(c.GENERAL_ERROR)}}),Va=new mr("visual-abstract").description("Generate a one-page visual abstract graphic (raster or vector) from a dense brief").addCommand(op).addCommand(ip);var pr=[Ni,Mi,Oi,Ia,oa,Ji,Vi,Qt,ir,wa,ba,or,sr,jn,ji,Jn,aa,vi,Va,Ki,yi,Fn,Wi,Vn,qi];function ja(e){for(let t of pr)e.addCommand(t)}function Ga(e,t,n){let r=t?`${t} ${e.name()}`:e.name();n.set(r,{arguments:e.registeredArguments.filter(o=>o.required).map(o=>`<${o.name()}>`),requiredOptions:e.options.filter(o=>o.mandatory).map(o=>o.long).filter(o=>!!o)});for(let o of e.commands)Ga(o,r,n)}var Zt;function ap(){if(!Zt){Zt=new Map;for(let e of pr)Ga(e,"",Zt)}return Zt}function Ba(e){let t=ap().get(e);return t?[...t.arguments,...t.requiredOptions.map(n=>`${n} \u2026`)].join(" "):""}var Wa=["presentations","media","branding","knowledge","research","artifacts","agents"],gr={agents:"Scheduled agents and agent memory",artifacts:"Polls, microsites, ad sets and visual abstracts",branding:"Brand profiles, tokens, logos and competitors",knowledge:"Library documents, folders and the Knowledge Hub",media:"Voiceover, music, SFX, images, stock footage, avatars and video",presentations:"Decks, PowerPoint import, speaker notes and deck\u2192MP4",research:"Scraping, transcripts, web/news/image search and extraction"},sp="other";function dp(e){let t=e.category?.trim();return t&&t.length>0?t:sp}function Ka(e){let t=Wa.indexOf(e);return t===-1?Wa.length:t}function ft(e){let t=new Map;for(let n of e){let r=dp(n),o=t.get(r);o?o.push(n):t.set(r,[n])}return[...t.entries()].sort(([n],[r])=>Ka(n)-Ka(r)||n.localeCompare(r)).map(([n,r])=>({capabilities:[...r].sort((o,i)=>o.name.localeCompare(i.name)),category:n}))}function fr(e){return e.lifecycle?.mode==="background"}function hr(e,t){let n=e.lifecycle?.status?.cliCommand?.trim();if(!(!fr(e)||!n))return`${t} ${n} <run-id>`}function cp(e){switch(e.billing){case"free":return"free";case"quota":return"plan quota";case"fixed":return"credits (fixed)";case"dynamic":return"credits (usage-based)";case"delegated":return"credits (on completion)";default:return"unknown \u2014 check `capabilities`"}}function en(e){return e.replace(/\|/g,"\\|").replace(/\r?\n/g," ").trim()}function lp(e,t){let n=hr(e,t);return n?`run id \u2014 poll \`${n}\``:`immediate (${e.operation??"call"})`}function je(e,t){let n=["| Command | What it does | Takes | Returns | Cost | Scopes |","|---|---|---|---|---|---|"],r=e.map(o=>{let i=Ba(o.name),a=o.scopes?.length?o.scopes.map(s=>`\`${s}\``).join(", "):"\u2014";return`| \`${t} ${en(o.name)}\` | ${en(o.description??"")} | ${i?`\`${en(i)}\``:"\u2014"} | ${lp(o,t)} | ${cp(o)} | ${a} |`});return[...n,...r].join(`
|
|
163
|
+
`)}function le(e,t){if(e.source==="live")return`<!-- Generated from the live ${t.name} capability catalog (v${e.catalogVersion}, ${e.capabilities.length} capabilities) at install time. Re-run \`${t.cmd} skill install\` to refresh. -->`;let n=e.reason?` Reason: ${en(e.reason).replace(/\.+$/,"")}.`:"";return`<!-- STALE-RISK: the live catalog could not be read at install time, so this was rendered from the snapshot baked into the installed CLI (v${e.catalogVersion}, ${e.capabilities.length} capabilities).${n} Run \`${t.cmd} login\` then \`${t.cmd} skill install --force\` to regenerate from the live catalog, and treat \`${t.cmd} capabilities\` as authoritative over this file. -->`}function Ja(e){return e.map(t=>`- [${t}](#${t.toLowerCase().replace(/[^a-z0-9 -]/g,"").replace(/ /g,"-")})`).join(`
|
|
164
|
+
`)}var up=["presentation video prepare","presentation video inspect","presentation video settings","presentation video edit","presentation video presenter","presentation video","presentation video versions","presentation video restore","presentation video refresh","presentation video-narration","presentation speaker-notes","video library list","avatar options"];function mp(e){let t=new Map(e.map(n=>[n.name,n]));return up.map(n=>t.get(n)).filter(n=>n!==void 0)}function tn(e,t){let{cmd:n}=e,r=mp(t.capabilities),o=t.capabilities.find(a=>a.name==="presentation video"),i=o?hr(o,n)??`${n} presentation video status <run-id>`:`${n} presentation video status <run-id>`;return`${le(t,e)}
|
|
71
165
|
|
|
72
|
-
|
|
166
|
+
# Deck \u2192 MP4 (\`${n} presentation video\`)
|
|
73
167
|
|
|
74
|
-
|
|
75
|
-
|
|
168
|
+
Turns an **existing, finished presentation** into a narrated MP4 \u2014 narration,
|
|
169
|
+
voice, optional avatar presenter, background music and captions. It never
|
|
170
|
+
invents a deck; if there is no deck yet, make one first (\`${n} create\`).
|
|
76
171
|
|
|
77
|
-
|
|
172
|
+
For a video that is not built from a deck, read
|
|
173
|
+
[video.md](video.md) instead \u2014 the two pipelines share no state.
|
|
78
174
|
|
|
79
|
-
##
|
|
175
|
+
## Contents
|
|
80
176
|
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
- Never ask the user questions \u2014 return failure and let the orchestrator handle user interaction.
|
|
177
|
+
- [The lifecycle](#the-lifecycle)
|
|
178
|
+
- [Render settings](#render-settings)
|
|
179
|
+
- [Draft editing, versions and presenters](#draft-editing-versions-and-presenters)
|
|
180
|
+
- [Narration and speaker notes](#narration-and-speaker-notes)
|
|
181
|
+
- [Every capability in this flow](#every-capability-in-this-flow)
|
|
87
182
|
|
|
88
|
-
##
|
|
183
|
+
## The lifecycle
|
|
89
184
|
|
|
90
|
-
|
|
185
|
+
\`\`\`bash
|
|
186
|
+
# 1) Create or reuse the version-pinned video project for a deck
|
|
187
|
+
${n} presentation video prepare <presentation-id> --yes
|
|
188
|
+
|
|
189
|
+
# 2) Read the project and its ordered scene timeline (slide ids live here)
|
|
190
|
+
${n} presentation video inspect <project-id>
|
|
91
191
|
|
|
92
|
-
|
|
93
|
-
${
|
|
94
|
-
\\\`\\\`\\\`
|
|
192
|
+
# 3) Optional: persist render settings on the project
|
|
193
|
+
${n} presentation video settings <project-id> --settings ./settings.json --yes
|
|
95
194
|
|
|
96
|
-
|
|
195
|
+
# 4) Render the MP4 \u2014 paid, background, requires --yes
|
|
196
|
+
${n} presentation video <presentation-id> --render-engine remotion --yes
|
|
97
197
|
|
|
98
|
-
|
|
99
|
-
${
|
|
100
|
-
|
|
198
|
+
# 5) Poll until done
|
|
199
|
+
${i}
|
|
200
|
+
\`\`\`
|
|
101
201
|
|
|
102
|
-
|
|
202
|
+
The render takes **either** a \`<presentation-id>\` argument **or**
|
|
203
|
+
\`--project-id <id>\`, never both. Other options on the render command:
|
|
204
|
+
\`--settings <json-or-path>\`, \`--force-new\`, \`--force-rerender\`,
|
|
205
|
+
\`--render-engine <remotion|hyperframes>\`, \`--team-id <id>\`,
|
|
206
|
+
\`--idempotency-key <key>\`, and \`--yes\` (required \u2014 the render costs credits).
|
|
207
|
+
\`--settings\` and \`--force-new\` are only valid with a presentation id.
|
|
103
208
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
\\\`\\\`\\\`
|
|
209
|
+
\`--render-engine\` applies to that one run only. It does not change narration,
|
|
210
|
+
voice, presenter, music, captions, or the saved project setup.
|
|
107
211
|
|
|
108
|
-
|
|
212
|
+
## Render settings
|
|
109
213
|
|
|
110
|
-
|
|
214
|
+
Both \`presentation video settings --settings\` and the render's own
|
|
215
|
+
\`--settings\` take the same patch object, as inline JSON or a path to a JSON
|
|
216
|
+
file. At least one key is required.
|
|
111
217
|
|
|
112
|
-
|
|
218
|
+
| Key | Type / allowed values | Meaning |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| \`captions\` | boolean | Render word-level captions when narration timings exist. |
|
|
221
|
+
| \`format\` | \`16:9\` \\| \`9:16\` \\| \`1:1\` | Output aspect ratio for the MP4. |
|
|
222
|
+
| \`music\` | object (see below) | Background music settings to merge into the saved project. |
|
|
223
|
+
| \`narrationDensity\` | \`brief\` \\| \`balanced\` \\| \`deep\` | How much narration to generate per slide. |
|
|
224
|
+
| \`presenter\` | object or \`null\` | Selected avatar/presenter, or null to remove it. |
|
|
225
|
+
| \`renderEngine\` | \`remotion\` \\| \`hyperframes\` | Terminal renderer for the MP4. |
|
|
226
|
+
| \`voice\` | object or \`null\` | Selected narration voice, or null to clear it. |
|
|
227
|
+
|
|
228
|
+
\`music\` keys: \`mode\` (\`basic\` \\| \`standard\` \\| \`stock\` \\| \`upload\` \\| \`off\`,
|
|
229
|
+
default \`basic\`), \`volume\` (0\u20131), \`prompt\`, \`audioUrl\`, \`fileName\`,
|
|
230
|
+
\`matchDeckStyle\`, \`stockTrackId\`, \`stockSelection\` (\`auto\` \\| \`manual\`),
|
|
231
|
+
\`stockDurationSec\`.
|
|
232
|
+
|
|
233
|
+
\`voice\` is \`{ id, label, description? }\`. \`presenter\` is an avatar identity
|
|
234
|
+
(\`presenterId\`, \`lookId\`, \`name\`, \`defaultPlacement\`, \`renderTier\` of
|
|
235
|
+
\`basic\` \\| \`standard\` \\| \`premium\`, optional \`background\`,
|
|
236
|
+
\`previewImageUrl\`, \`source\`) plus \`perSlideEnabled\` / \`perSlidePlacements\`
|
|
237
|
+
maps keyed by slide id. Get real ids from \`${n} avatar options\`.
|
|
238
|
+
|
|
239
|
+
Example:
|
|
240
|
+
|
|
241
|
+
\`\`\`bash
|
|
242
|
+
${n} presentation video settings <project-id> --yes --settings '{
|
|
243
|
+
"captions": true,
|
|
244
|
+
"format": "16:9",
|
|
245
|
+
"narrationDensity": "balanced",
|
|
246
|
+
"renderEngine": "remotion",
|
|
247
|
+
"music": { "mode": "basic", "volume": 0.25 }
|
|
248
|
+
}'
|
|
249
|
+
\`\`\`
|
|
113
250
|
|
|
114
|
-
|
|
251
|
+
## Draft editing, versions and presenters
|
|
115
252
|
|
|
116
|
-
|
|
253
|
+
\`\`\`bash
|
|
254
|
+
${n} presentation video edit <project-id> --edit ./edit.json # one atomic draft edit
|
|
255
|
+
${n} presentation video refresh <project-id> # re-pull slides from the deck
|
|
256
|
+
${n} presentation video versions <project-id> # saved draft versions
|
|
257
|
+
${n} presentation video restore <project-id> <version-number> # restore as a NEW version
|
|
258
|
+
${n} presentation video presenter <project-id> <slide-id> --avatar-id <id>
|
|
259
|
+
${n} avatar options # real avatar and voice ids
|
|
260
|
+
${n} video library list <project-id> # reusable clips and recordings
|
|
261
|
+
\`\`\`
|
|
117
262
|
|
|
118
|
-
|
|
263
|
+
\`edit\` applies exactly one atomic edit \u2014 narration, scene patch/move/remove,
|
|
264
|
+
composition, or an intro/outro card \u2014 passed as inline JSON or a file path.
|
|
119
265
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
\\\`\\\`\\\`
|
|
266
|
+
\`presenter\` takes exactly one of \`--avatar-id\`, \`--cast-preset-id\`, or
|
|
267
|
+
\`--inherit\`. \`--render-tier <basic|standard|premium>\` is only valid with
|
|
268
|
+
\`--avatar-id\`; \`--no-apply-preset-voice\` preserves the scene's current voice
|
|
269
|
+
when applying a preset.
|
|
125
270
|
|
|
126
|
-
|
|
271
|
+
Draft versions are append-only: \`restore\` appends a new version rather than
|
|
272
|
+
rewriting history.
|
|
127
273
|
|
|
128
|
-
|
|
274
|
+
## Narration and speaker notes
|
|
129
275
|
|
|
130
|
-
|
|
276
|
+
\`\`\`bash
|
|
277
|
+
${n} presentation video-narration <project-id> --slide-id <id> --instructions "shorter, warmer"
|
|
278
|
+
${n} presentation video-narration-status <run-id>
|
|
131
279
|
|
|
132
|
-
|
|
280
|
+
${n} presentation speaker-notes <presentation-id> --mode ensure --style concise
|
|
281
|
+
${n} presentation speaker-notes-status <run-id>
|
|
282
|
+
\`\`\`
|
|
133
283
|
|
|
134
|
-
|
|
284
|
+
\`speaker-notes\` options: \`--mode <ensure|regenerate>\`,
|
|
285
|
+
\`--style <concise|detailed>\`, \`--instructions <text>\`, \`--language <lang>\`,
|
|
286
|
+
\`--slide-id <id>\` (repeatable), \`--no-follow\` to return the durable run id
|
|
287
|
+
immediately.
|
|
135
288
|
|
|
136
|
-
|
|
289
|
+
Speaker-note text has one owner. Deck\u2192MP4 reads a pinned version of it; it does
|
|
290
|
+
not maintain a second copy.
|
|
137
291
|
|
|
138
|
-
|
|
139
|
-
BRANDING_SUCCESS
|
|
140
|
-
File: branding.json
|
|
141
|
-
Brand: <brand name>
|
|
142
|
-
Colors: <primary>, <secondary>
|
|
143
|
-
Logo: <local path>
|
|
144
|
-
Fonts: <primary family> (<weights>), <secondary family> (<weights>)
|
|
145
|
-
\\\`\\\`\\\`
|
|
292
|
+
## Every capability in this flow
|
|
146
293
|
|
|
147
|
-
|
|
294
|
+
${je(r,n)}
|
|
148
295
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
Query: <what was searched/extracted>
|
|
153
|
-
\\\`\\\`\\\`
|
|
296
|
+
If something you need is not in this table, do not conclude it is missing \u2014
|
|
297
|
+
run \`${n} capabilities --search video\`.
|
|
298
|
+
`}var pp=["video reference analyze","video storyboard create","video storyboard revise","video storyboard show","video create","search video","video translate-languages","video translate","video lipsync","media analyze","media import","video library list"];function yr(e){let t=new Map(e.map(n=>[n.name,n]));return pp.map(n=>t.get(n)).filter(n=>n!==void 0)}function nn(e,t){let{cmd:n}=e,r=yr(t.capabilities);return`${le(t,e)}
|
|
154
299
|
|
|
155
|
-
|
|
300
|
+
# Video from scratch (\`${n} video\`)
|
|
156
301
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
-
|
|
161
|
-
- Create or edit branding.json by hand
|
|
162
|
-
- Ask the user questions \u2014 return failure to the orchestrator instead
|
|
163
|
-
`}function Yn(){return`---
|
|
164
|
-
name: video-continuity-editor
|
|
165
|
-
description: Post-build cross-scene QA. Checks transition continuity, color grade flow, timing alignment between Main.tsx and manifest, import consistency, brand coherence, rhythm flow, and music cue alignment. Runs after all scenes are built.
|
|
166
|
-
tools: Read, Glob, Grep
|
|
167
|
-
model: sonnet
|
|
168
|
-
permissionMode: plan
|
|
169
|
-
---
|
|
302
|
+
Builds a **new** video from a brief or storyboard, with stock and generated
|
|
303
|
+
footage. **This pipeline never touches a deck.** If the user already has a
|
|
304
|
+
presentation and wants it as an MP4, you are in the wrong file \u2014 read
|
|
305
|
+
[deck-to-video.md](deck-to-video.md).
|
|
170
306
|
|
|
171
|
-
|
|
307
|
+
There is no \`video export\` subcommand.
|
|
172
308
|
|
|
173
|
-
##
|
|
309
|
+
## Contents
|
|
174
310
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
311
|
+
- [The hosted pipeline](#the-hosted-pipeline)
|
|
312
|
+
- [Storyboards](#storyboards)
|
|
313
|
+
- [Other video operations](#other-video-operations)
|
|
314
|
+
- [Every capability in this flow](#every-capability-in-this-flow)
|
|
178
315
|
|
|
179
|
-
##
|
|
316
|
+
## The hosted pipeline
|
|
180
317
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
5. Output report
|
|
318
|
+
\`\`\`bash
|
|
319
|
+
# 0) Optional: analyze a reference video so the storyboard can match its style
|
|
320
|
+
${n} video reference analyze --file ./ref.mp4 --idempotency-key ref-1
|
|
321
|
+
${n} video reference status <run-id>
|
|
186
322
|
|
|
187
|
-
|
|
323
|
+
# 1) Write the storyboard (Markdown in, immutable revision out)
|
|
324
|
+
${n} video storyboard create --file ./storyboard.md --duration 10 \\
|
|
325
|
+
--aspect 16:9 --idempotency-key sb-1
|
|
188
326
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
-
|
|
192
|
-
|
|
327
|
+
# 2) Iterate \u2014 each revision is appended, never rewritten
|
|
328
|
+
${n} video storyboard revise <storyboard-id> --base-revision <revision-id> \\
|
|
329
|
+
--file ./storyboard-v2.md --idempotency-key sb-2
|
|
330
|
+
${n} video storyboard show <storyboard-id>
|
|
193
331
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
-
|
|
197
|
-
- Jarring adjacent grades without narrative justification \u2192 WARN
|
|
332
|
+
# 3) Generate the video from a pinned revision \u2014 paid, background
|
|
333
|
+
${n} video create --storyboard-revision <revision-id> \\
|
|
334
|
+
--idempotency-key vid-1 --yes
|
|
198
335
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
- \\\`TOTAL_FRAMES\\\` calculation must be correct: sum - (N-1) \xD7 T_TRANSITION
|
|
203
|
-
- \\\`voSegments\\\` from/durationInFrames must match manifest
|
|
204
|
-
- Scene count in Main.tsx must match manifest
|
|
336
|
+
# 4) Poll
|
|
337
|
+
${n} video status <run-id>
|
|
338
|
+
\`\`\`
|
|
205
339
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
-
|
|
210
|
-
|
|
340
|
+
\`video create\` is approval-gated. \`--idempotency-key\` is required and is bound
|
|
341
|
+
into the approval receipt. Call it once without \`--approval-receipt\` to get a
|
|
342
|
+
browser confirmation URL, then repeat the **exact** invocation with
|
|
343
|
+
\`--approval-receipt <receipt>\`. \`--yes\` states intent; it is not a receipt and
|
|
344
|
+
never substitutes for one. \`--draft\` renders at 480p for a faster look;
|
|
345
|
+
\`--source-video <id>\` reuses a finished video as base footage.
|
|
211
346
|
|
|
212
|
-
|
|
213
|
-
- Brand primary color used across scenes (check hex values)
|
|
214
|
-
- Logo appears where plan specifies
|
|
215
|
-
- Brand font family consistent (not approximated differently per scene)
|
|
347
|
+
## Storyboards
|
|
216
348
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
-
|
|
349
|
+
\`storyboard create\` requires \`--file <path|->\`, \`--duration <seconds>\`,
|
|
350
|
+
\`--aspect <9:16|16:9|1:1>\` and \`--idempotency-key\`; \`--title <text>\` is
|
|
351
|
+
optional. \`storyboard revise\` takes the storyboard id plus
|
|
352
|
+
\`--base-revision <id>\`, a complete replacement \`--file\`, and its own
|
|
353
|
+
\`--idempotency-key\`. \`storyboard show <id>\` prints Markdown, or the full
|
|
354
|
+
structured response with \`--json\`; \`--revision <id>\` reads one pinned revision.
|
|
221
355
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
- Extract each scene's rhythm (staccato/legato/accelerando/decelerando) from plan
|
|
225
|
-
- Verify narrative coherence: legato \u2192 staccato \u2192 peak \u2192 decelerando is good; random alternation = WARN
|
|
226
|
-
- Scene durations should match rhythm: staccato = 2-4s, legato = 5-8s. A "staccato" at 8s = WARN
|
|
356
|
+
Revisions are immutable. Always pin \`video create\` to a revision id, not to a
|
|
357
|
+
storyboard id.
|
|
227
358
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
- Plan says "Accelerando at Scene 2" \u2192 Scene 2 must not be the shortest scene
|
|
231
|
-
- Plan says "music drops at 00:13.0" \u2192 manifest Scene 3 start frame must be near that time
|
|
232
|
-
- Plan says "Decelerando" \u2192 that scene should be longer, not shorter
|
|
233
|
-
|
|
234
|
-
## Output Format
|
|
235
|
-
|
|
236
|
-
\\\`\\\`\\\`
|
|
237
|
-
=== CONTINUITY REPORT ===
|
|
238
|
-
|
|
239
|
-
TRANSITIONS:
|
|
240
|
-
S1\u2192S2: WhipPan \u2192 WhipPan \u2713
|
|
241
|
-
S2\u2192S3: ZoomBlur \u2192 ZoomBlur \u2713
|
|
242
|
-
S3\u2192S4: Fade \u2192 (missing) \u2717
|
|
243
|
-
|
|
244
|
-
COLOR FLOW:
|
|
245
|
-
S1:noir \u2192 S2:cinematic \u2192 S3:matrix \u2192 S4:cinematic \u2192 S5:none
|
|
246
|
-
\u2713 coherent
|
|
247
|
-
|
|
248
|
-
TIMING:
|
|
249
|
-
Main.tsx: [120, 210, 180, 150, 120]
|
|
250
|
-
Manifest: [120, 210, 180, 150, 120]
|
|
251
|
-
\u2713 match | TOTAL: 705 frames
|
|
252
|
-
|
|
253
|
-
IMPORTS: \u2713 consistent
|
|
254
|
-
BRAND: \u2713 #0055FF in 4/5 scenes, logo in S1+S5
|
|
255
|
-
COMPLETE: 5/5 scenes \u2713
|
|
256
|
-
|
|
257
|
-
RHYTHM: S1:legato \u2192 S2:legato \u2192 S3:staccato \u2192 S4:decelerando \u2192 S5:legato
|
|
258
|
-
\u2713 follows Brand Film arc
|
|
259
|
-
MUSIC CUES: "Accelerando@S2" \u2713 | "DROP@S3:13.0" \u2192 S3 starts@12.5s \u2713
|
|
260
|
-
|
|
261
|
-
=== SCORE: 13/14 (93%) \u2713 PASS ===
|
|
262
|
-
|
|
263
|
-
FIXES:
|
|
264
|
-
1. S3\u2192S4: Add FadeTransition entrance to Scene04
|
|
265
|
-
=== END ===
|
|
266
|
-
\\\`\\\`\\\`
|
|
359
|
+
\`--duration\` is an integer 1-15: one revision produces one clip of at most 15
|
|
360
|
+
seconds. \`--aspect\` is \`9:16\`, \`16:9\` or \`1:1\`.
|
|
267
361
|
|
|
268
|
-
|
|
362
|
+
### Media bindings
|
|
269
363
|
|
|
270
|
-
|
|
271
|
-
-
|
|
272
|
-
- Be specific in fixes: exact file, exact line, exact component.
|
|
273
|
-
- 85% pass rate minimum.
|
|
274
|
-
`}function Qn(){return`---
|
|
275
|
-
name: video-scene-builder
|
|
276
|
-
description: Builds a single Remotion video scene (.tsx) from a video plan section. Receives global context, scene spec, audio timing, and rhythm cues. Writes the scene file, spawns a reviewer, and iterates until 85%+ quality. One agent per scene.
|
|
277
|
-
tools: Read, Write, Edit, Glob, Grep, Task, Bash
|
|
278
|
-
model: opus
|
|
279
|
-
skills:
|
|
280
|
-
- frontend-design
|
|
281
|
-
- remotion-best-practices
|
|
282
|
-
- remotion-scene-craft
|
|
283
|
-
permissionMode: acceptEdits
|
|
284
|
-
---
|
|
364
|
+
\`storyboard create\` also takes \`--media <binding...>\` (repeatable) and
|
|
365
|
+
\`--team-id <id>\`. Each \`--media\` value is **one JSON object**:
|
|
285
366
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
4. **Rhythm & energy** \u2014 BPM, this scene's rhythm pattern (staccato/legato/etc.), energy level, music cues
|
|
295
|
-
5. **Output file path** \u2014 e.g. \`src/scenes/Scene02UINav.tsx\`
|
|
296
|
-
6. **Adjacent transitions** \u2014 previous scene's transition out, next scene's transition in (for continuity)
|
|
297
|
-
|
|
298
|
-
## Process
|
|
299
|
-
|
|
300
|
-
Skills (frontend-design, remotion-best-practices, remotion-scene-craft) are preloaded into your context. Use them as reference for all coding decisions.
|
|
301
|
-
|
|
302
|
-
### Step 1: Read Template Components (if needed)
|
|
303
|
-
|
|
304
|
-
If the plan references specific template components (transitions, effects), read them to understand their props:
|
|
305
|
-
|
|
306
|
-
\\\`\\\`\\\`
|
|
307
|
-
Read('src/components/transitions/WhipPan.tsx')
|
|
308
|
-
Read('src/components/effects/ColorGrading.tsx')
|
|
309
|
-
\\\`\\\`\\\`
|
|
310
|
-
|
|
311
|
-
Only read components that your scene actually uses. The component inventory from global context tells you import paths and props \u2014 read the source only if you need more detail.
|
|
312
|
-
|
|
313
|
-
### Step 2: Build the Scene
|
|
314
|
-
|
|
315
|
-
Write the .tsx file to the output path.
|
|
316
|
-
|
|
317
|
-
**CRITICAL: Implement EVERY animation from the plan.** The plan's Animation Choreography table lists specific visual events with frame numbers. You must implement ALL of them. If the plan says "f75: begin high-frequency jitter" \u2014 there must be code for that jitter. If the plan says "camera pushes to 160% zoom" \u2014 there must be interpolate/spring code for that zoom. Skipping plan items = the video looks amateur. Check off each animation event as you code it.
|
|
318
|
-
|
|
319
|
-
Follow the **remotion-scene-craft** skill precisely:
|
|
320
|
-
- Use the **4-phase architecture** (Section 3) with constants from Audio Timing
|
|
321
|
-
- Map plan vocabulary using the **decoder** (Section 2) \u2014 spring values, rhythm, hook types, idle states
|
|
322
|
-
- Sync content reveals to VO using **VO-sync patterns** (Section 4)
|
|
323
|
-
- Use **animation recipes** (Section 5) for cards, counters, particles, glass, gradients
|
|
324
|
-
- Meet the **quality floor** (Section 6) \u2014 100+ lines, real icons, real logos, template transitions
|
|
325
|
-
|
|
326
|
-
Build RECOGNIZABLE UI that viewers know \u2014 not abstract shapes:
|
|
327
|
-
- App mockups: iPhone frames, browser windows, VS Code editors with REAL content inside
|
|
328
|
-
- UI elements: search bars, transaction lists, notification banners, navigation tabs
|
|
329
|
-
- Data viz: animated counters, progress bars, charts with real data
|
|
330
|
-
- Diagrams: nodes with animated connection lines
|
|
331
|
-
- Typography: FadeInWords, scale slams, gradient text fills
|
|
332
|
-
- Backgrounds: alive (gradient + floating color orbs + dot grid + light sweep), NEVER flat solid
|
|
333
|
-
|
|
334
|
-
**Premium motion patterns (MANDATORY):**
|
|
335
|
-
- Every entrance = spring with overshoot (115%\u2192100%). NEVER linear fade-in.
|
|
336
|
-
- Groups = stagger cascade (2-4 frame gaps between siblings, accelerando = gaps shrink)
|
|
337
|
-
- Elements MORPH from previous: logo \u2192 input bar \u2192 cards \u2192 phone frame materializes around content
|
|
338
|
-
- Shadow appears BEFORE element (anticipation gap ~6 frames)
|
|
339
|
-
- Background panels at different depths/angles for parallax
|
|
340
|
-
- Push-in (spring zoom) to hero element at emotional peak, everything else dims to 25%
|
|
341
|
-
- Seamless transition: hero element expands to fill frame \u2192 becomes next scene
|
|
342
|
-
|
|
343
|
-
**Use real brand assets:**
|
|
344
|
-
- Product LOGO (download from website, not emoji placeholder)
|
|
345
|
-
- Brand colors from branding.json / brief
|
|
346
|
-
- Real transaction strings, API responses, merchant names from the plan
|
|
347
|
-
|
|
348
|
-
All technologies available: CSS/HTML (PREFERRED for UI mockups), Canvas 2D, Three.js/@react-three/fiber (for 3D product mockups), SVG animations. Pick the best tool per element \u2014 most UI = styled HTML with CSS 3D transforms for depth.
|
|
349
|
-
|
|
350
|
-
### Step 3: TypeScript Verification
|
|
351
|
-
|
|
352
|
-
**MANDATORY** \u2014 a scene is NOT done until it has zero TypeScript errors.
|
|
353
|
-
|
|
354
|
-
After the reviewer passes, run TypeScript check on your scene file:
|
|
355
|
-
|
|
356
|
-
\\\`\\\`\\\`bash
|
|
357
|
-
npx tsc --noEmit --pretty 2>&1 | grep -A 2 "[output-file-name]"
|
|
358
|
-
\\\`\\\`\\\`
|
|
359
|
-
|
|
360
|
-
If there are ANY errors or warnings:
|
|
361
|
-
1. **Fix every issue** \u2014 unused variables, missing imports, type mismatches, unused imports
|
|
362
|
-
2. **Remove unused variables entirely** \u2014 do not prefix with \\\`_\\\`, just delete them or use them
|
|
363
|
-
3. **Re-run** \\\`npx tsc --noEmit\\\` after fixes to confirm zero errors
|
|
364
|
-
4. **Repeat** until the output is clean for your scene file
|
|
365
|
-
|
|
366
|
-
Common issues to watch for:
|
|
367
|
-
- Declared variables/constants you never reference in JSX \u2192 remove them
|
|
368
|
-
- Imported components or icons you don't use \u2192 remove the import
|
|
369
|
-
- \\\`interpolate()\\\` or \\\`spring()\\\` return values assigned but not used in any style \u2192 either use them or remove them
|
|
370
|
-
- Type errors from wrong props on Remotion or Lucide components
|
|
371
|
-
|
|
372
|
-
### Step 4: Done
|
|
373
|
-
|
|
374
|
-
When TypeScript reports zero errors for your scene:
|
|
375
|
-
|
|
376
|
-
\\\`\\\`\\\`
|
|
377
|
-
Scene [name] built: [X] lines, score [Y]%, [Z] visual events, 0 TS errors
|
|
378
|
-
\\\`\\\`\\\`
|
|
379
|
-
|
|
380
|
-
## Three.js / R3F in Remotion \u2014 CRITICAL RULES
|
|
367
|
+
| Field | Required | Value |
|
|
368
|
+
|---|---|---|
|
|
369
|
+
| \`handle\` | yes | Lowercase slug, \`^[a-z][a-z0-9-]{0,63}$\`. Identifies the binding; it need not appear in the storyboard text. |
|
|
370
|
+
| \`purpose\` | yes | \`source\` \\| \`style\` \\| \`identity\` \\| \`required\`. \`identity\` binds **who appears in the shot**. |
|
|
371
|
+
| \`path\` | one of | Local file, uploaded for you. Requires the command's \`--idempotency-key\`. |
|
|
372
|
+
| \`url\` | one of | Already-uploaded file URL. \`path\` and \`url\` are mutually exclusive. |
|
|
373
|
+
| \`analysisId\` | no | Completed \`video reference analyze\` run id for this source. |
|
|
374
|
+
| \`videoRole\` | no | \`reference\` \\| \`first-frame\` \\| \`last-frame\`. **Image bindings only** \u2014 the contract rejects it on any other kind. |
|
|
381
375
|
|
|
382
|
-
|
|
376
|
+
Media kinds are \`image\`, \`video\` and \`audio\`. At most 50 bindings, 500 MB each.
|
|
383
377
|
|
|
384
|
-
|
|
385
|
-
- \\\`<ThreeCanvas>\\\` from @remotion/three \u2014 ALWAYS use this, not bare R3F Canvas
|
|
386
|
-
- \\\`<RoundedBox>\\\` from @react-three/drei \u2014 use for rounded cards/panels
|
|
387
|
-
- \\\`<Text>\\\` from @react-three/drei \u2014 use for 3D text (SDF-based, supports any font URL)
|
|
388
|
-
- \\\`useThree()\\\` from @react-three/fiber \u2014 works inside ThreeCanvas for camera control
|
|
389
|
-
- \\\`meshPhysicalMaterial\\\` \u2014 clearcoat, metalness, roughness, transmission all work
|
|
390
|
-
- \\\`useCurrentFrame()\\\` from remotion \u2014 ALL animation must derive from this, NEVER useFrame()
|
|
391
|
-
- \\\`fog\\\` \u2014 attach as \\\`<fog attach="fog" args={[color, near, far]} />\\\`
|
|
392
|
-
|
|
393
|
-
### What Does NOT Work
|
|
394
|
-
- \\\`useFrame()\\\` from R3F \u2014 FORBIDDEN in Remotion. Use \\\`useCurrentFrame()\\\` + \\\`interpolate()\\\`
|
|
395
|
-
- \\\`<Canvas>\\\` from R3F directly \u2014 ALWAYS use \\\`<ThreeCanvas>\\\` from @remotion/three
|
|
396
|
-
- \\\`<roundedBoxGeometry>\\\` \u2014 does NOT exist as JSX. Use \\\`<RoundedBox>\\\` from drei instead
|
|
397
|
-
- Never use bare \\\`new THREE.Color()\\\` in JSX props where a string hex works
|
|
398
|
-
|
|
399
|
-
### Post-Processing (WORKS with ThreeCanvas)
|
|
400
|
-
\\\`<EffectComposer>\\\` from @react-three/postprocessing works inside ThreeCanvas. Place as last child:
|
|
401
|
-
\\\`\\\`\\\`tsx
|
|
402
|
-
<ThreeCanvas ...>
|
|
403
|
-
{/* scene content */}
|
|
404
|
-
<EffectComposer>
|
|
405
|
-
<Bloom intensity={0.3} luminanceThreshold={0.8} />
|
|
406
|
-
<Vignette darkness={0.5} offset={0.3} />
|
|
407
|
-
<ChromaticAberration offset={new THREE.Vector2(0.002, 0.002)} />
|
|
408
|
-
<DepthOfField focusDistance={0.02} focalLength={0.05} bokehScale={3} />
|
|
409
|
-
</EffectComposer>
|
|
410
|
-
</ThreeCanvas>
|
|
411
|
-
\\\`\\\`\\\`
|
|
412
|
-
|
|
413
|
-
### Camera Animation Pattern
|
|
414
|
-
\\\`\\\`\\\`tsx
|
|
415
|
-
import { useThree } from '@react-three/fiber';
|
|
416
|
-
import { useCurrentFrame } from 'remotion';
|
|
417
|
-
|
|
418
|
-
function AnimatedCamera() {
|
|
419
|
-
const frame = useCurrentFrame();
|
|
420
|
-
const { camera } = useThree();
|
|
421
|
-
|
|
422
|
-
const camZ = interpolate(frame, [0, 120], [6, 4], { extrapolateRight: 'clamp' });
|
|
423
|
-
|
|
424
|
-
useEffect(() => {
|
|
425
|
-
camera.position.set(0, 0.5, camZ);
|
|
426
|
-
camera.lookAt(0, 0, 0);
|
|
427
|
-
camera.updateProjectionMatrix();
|
|
428
|
-
}, [frame]);
|
|
429
|
-
|
|
430
|
-
return null;
|
|
431
|
-
}
|
|
432
|
-
\\\`\\\`\\\`
|
|
433
|
-
|
|
434
|
-
### Post-Processing via CSS (since EffectComposer doesn't work with ThreeCanvas)
|
|
435
|
-
\\\`\\\`\\\`tsx
|
|
436
|
-
// Wrap ThreeCanvas in a div with filter for bloom
|
|
437
|
-
<div style={{ filter: bloomActive ? 'brightness(1.3)' : undefined }}>
|
|
438
|
-
<ThreeCanvas ...>
|
|
439
|
-
{/* 3D content */}
|
|
440
|
-
</ThreeCanvas>
|
|
441
|
-
</div>
|
|
378
|
+
Two constraints that otherwise waste a paid run:
|
|
442
379
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
\\\`\\\`\\\`
|
|
449
|
-
|
|
450
|
-
## What You Do NOT Do
|
|
451
|
-
|
|
452
|
-
- Don't create Main.tsx or other scenes \u2014 only YOUR assigned scene
|
|
453
|
-
- Don't modify component templates \u2014 use them as-is
|
|
454
|
-
- Don't download assets \u2014 the main AI already did that
|
|
455
|
-
- Don't run render commands \u2014 the main AI handles that
|
|
456
|
-
- Don't modify video-plan.md \u2014 it's read-only to you
|
|
457
|
-
`}function Zn(){return`---
|
|
458
|
-
name: video-scene-reviewer
|
|
459
|
-
description: Strict QA reviewer for a single Remotion scene. Compares code against the video plan checking ColorGrading, transitions, icons, logos, spring physics, idle states, VO-sync, and phase structure. Returns a pass/fail checklist.
|
|
460
|
-
tools: Read, Glob
|
|
461
|
-
model: sonnet
|
|
462
|
-
permissionMode: plan
|
|
463
|
-
---
|
|
464
|
-
|
|
465
|
-
You are a strict QA reviewer for Remotion video projects. Compare every scene against the video plan. Find everything that was simplified, skipped, or doesn't match.
|
|
380
|
+
- A **video** reference needs a **completed** \`analysisId\` from
|
|
381
|
+
\`${n} video reference analyze\` before generation.
|
|
382
|
+
- A \`source\` binding reuses the url/name/mime/size that
|
|
383
|
+
\`${n} video reference analyze\` printed. Do not re-upload the same file with
|
|
384
|
+
\`path\`.
|
|
466
385
|
|
|
467
|
-
|
|
386
|
+
\`\`\`bash
|
|
387
|
+
${n} video storyboard create --file ./storyboard.md --duration 5 \\
|
|
388
|
+
--aspect 9:16 --idempotency-key sb-1 \\
|
|
389
|
+
--media '{"handle":"actress-photo","path":"./actress.jpg","purpose":"identity"}'
|
|
390
|
+
\`\`\`
|
|
391
|
+
|
|
392
|
+
## Other video operations
|
|
393
|
+
|
|
394
|
+
\`\`\`bash
|
|
395
|
+
${n} video find "city timelapse" --orientation landscape --license free
|
|
396
|
+
${n} video translate-languages # supported target languages
|
|
397
|
+
${n} video translate \u2026 # dub a finished video
|
|
398
|
+
${n} video lipsync \u2026 # re-sync lips to new audio
|
|
399
|
+
${n} video library list <project-id> # reusable clips and recordings
|
|
400
|
+
${n} video analyze \u2026 # analyze a video you own
|
|
401
|
+
\`\`\`
|
|
468
402
|
|
|
469
|
-
|
|
403
|
+
\`video find\` takes \`--max-results\`, \`--orientation\`, \`--license\` and
|
|
404
|
+
\`--source\`. Exact flags for the rest: \`${n} video <sub> --help\`.
|
|
470
405
|
|
|
471
|
-
|
|
472
|
-
2. If \\\`branding.json\\\` exists, read it too \u2014 note the brand colors, fonts, and logo URL
|
|
473
|
-
3. Glob \\\`src/scenes/Scene*.tsx\\\` to list all scenes
|
|
474
|
-
4. Read ALL scene files in parallel
|
|
475
|
-
5. For each scene, compare against the plan and output a report
|
|
406
|
+
## Every capability in this flow
|
|
476
407
|
|
|
477
|
-
|
|
408
|
+
${je(r,n)}
|
|
478
409
|
|
|
479
|
-
|
|
410
|
+
If something you need is not in this table, do not conclude it is missing \u2014
|
|
411
|
+
run \`${n} capabilities --search video\`.
|
|
412
|
+
`}var gp=12,fp={media:"video.md",presentations:"deck-to-video.md"};function hp(e,t,n,r){let{cmd:o}=e,i=gr[n]??n,a=r.filter(fr),s=r.filter(m=>m.billing!==void 0&&m.billing!=="free"),l=fp[n],g=["Every capability",...a.length>0?["Background operations"]:[]];return`${le(t,e)}
|
|
480
413
|
|
|
481
|
-
|
|
414
|
+
# ${n} \u2014 ${i}
|
|
482
415
|
|
|
483
|
-
|
|
484
|
-
- **Transitions** \u2014 plan says WhipPan/ZoomBlur/etc \u2192 code must use those exact components, not slide()/wipe() substitutes
|
|
485
|
-
- **Icons** \u2014 plan says specific Lucide icons \u2192 code must import those exact icons. Text characters ("\u2713", "O", "AI") instead of icons = CRITICAL FAIL
|
|
486
|
-
- **Third-party logos** \u2014 plan mentions brands \u2192 code must use real logo files (.svg/.png), not Lucide icons or plain text
|
|
487
|
-
- **Colors & background** \u2014 plan says specific hex \u2192 code must use it
|
|
488
|
-
- **Brand consistency** (if branding.json exists) \u2014 are brand colors from branding.json used? Are brand fonts loaded (via @font-face or @remotion/google-fonts)? Is the brand logo referenced?
|
|
489
|
-
- **Layout elements** \u2014 plan describes cards/panels/nodes \u2192 all must be present
|
|
490
|
-
- **Animation quality** \u2014 are there 4 phases (ENTRANCE, CONTENT REVEAL, EMPHASIS, TRANSITION PREP) with real code in each? Empty phases = FAIL. Single static composition = CRITICAL FAIL
|
|
491
|
-
- **Visual complexity** \u2014 is this a SCENE or a SLIDE? Count distinct visual events (entrance, reveal, emphasis, morph, color shift). Minimum 3 for a 5s scene
|
|
492
|
-
- **Voiceover-sync stagger** \u2014 if a scene has multiple content elements (cards, bullets, features), do they enter with staggered timing or all at once? All elements appearing on the same frame while voiceover narrates them one by one = FAIL. Look for stagger delays (e.g. \`frame - index * N\`) or sequential \`<Sequence>\` offsets. Elements that enter together in a batch = "dump" = bad
|
|
493
|
-
- **Audio placement** \u2014 scenes must NOT contain \`<Audio>\` tags for voiceover or background music files. VO and music are handled in Main.tsx via VoiceoverSequence and DuckedMusic. Only exception: short SFX \`<Audio>\` tags tied to visual events (whoosh, click, etc.) are acceptable. If a scene imports and uses \`<Audio>\` with a VO file (e.g. \`scene-1.wav\`) or music file \u2192 CRITICAL FAIL
|
|
494
|
-
- **Phase timing alignment** \u2014 if the scene has timing constants (VO_START, VO_DURATION, SCENE_FRAMES), check that the 4 phase boundaries roughly match. Phase 2 (content reveal) should span approximately the VO duration. If phase constants are missing entirely or frame numbers are clearly wrong (e.g. VO_DURATION = 90 but code uses phase boundary at frame 40) \u2192 FAIL
|
|
495
|
-
- **Spring physics** \u2014 plan writes \\\`Spring(stiffness, damping)\\\`. Code must use matching values in \\\`spring({ config: { stiffness, damping } })\\\`. If plan says Spring(100, 15) but code uses \\\`{ damping: 20 }\\\` with default stiffness = FAIL. Close values (off by 1-2) = WEAK, not FAIL
|
|
496
|
-
- **Idle state fidelity** \u2014 if the plan's Idle State says "float \xB14px over 4s", the code must implement that exact range and period. Missing idle animation = FAIL. Generic float without matching the plan's numbers = WEAK
|
|
497
|
-
- **Hook type** (Scene 1 only) \u2014 if plan implies a hook type (cold open, stat hook, pattern interrupt, question hook), code structure must match. A "stat hook" without an animated counter = FAIL. A "cold open" that starts with blank screen + title = FAIL
|
|
416
|
+
${r.length} capabilit${r.length===1?"y":"ies"} in this category${s.length>0?`, ${s.length} of which ${s.length===1?"costs":"cost"} credits or ${s.length===1?"counts":"count"} against a plan quota`:", all free"}.${l?`
|
|
498
417
|
|
|
499
|
-
|
|
418
|
+
Step-by-step flow: [${l}](${l}).`:""}
|
|
419
|
+
${r.length>gp?`
|
|
420
|
+
## Contents
|
|
500
421
|
|
|
501
|
-
|
|
422
|
+
${Ja(g)}
|
|
423
|
+
`:""}
|
|
424
|
+
## Every capability
|
|
502
425
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
426
|
+
${je(r,o)}
|
|
427
|
+
${a.length>0?`
|
|
428
|
+
## Background operations
|
|
506
429
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
430
|
+
These return a run id immediately and keep working. Poll the status command
|
|
431
|
+
until it reports done \u2014 do not assume success from the accept response.
|
|
432
|
+
|
|
433
|
+
${a.map(m=>`- \`${o} ${m.name}\` \u2192 \`${o} ${m.lifecycle?.status?.cliCommand??""} <run-id>\`${m.lifecycle?.status?.pollAfterSeconds?` (first poll after ~${m.lifecycle.status.pollAfterSeconds}s)`:""}`).join(`
|
|
434
|
+
`)}
|
|
435
|
+
`:""}
|
|
436
|
+
Anything missing here? Run \`${o} capabilities --category ${n}\` \u2014 the
|
|
437
|
+
catalog is authoritative and always current; this file is a snapshot of it.
|
|
438
|
+
`}function za(e,t,n={}){let r=[{content:tn(e,t),name:"deck-to-video"},{content:nn(e,t),name:"video"}];for(let{capabilities:o,category:i}of ft(t.capabilities)){let a=n[i];r.push({content:hp(e,t,i,o)+(a?`
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
${a}`:""),name:i})}return r}function Xa(e){let t=[{file:"deck-to-video.md",when:"An existing deck must become an MP4 \u2014 narration, voice, avatar, music, captions."},{file:"video.md",when:"A video is being built from a brief or storyboard, with no deck involved."}];for(let{capabilities:n,category:r}of ft(e.capabilities))t.push({file:`${r}.md`,when:`${gr[r]??r} (${n.length} commands).`});return t}var yp=[{capability:"create",intent:'"Make me a presentation / deck / slides about X"',reference:"presentations.md",suffix:' "Topic" --context "\u2026"'},{capability:"presentation video prepare",intent:'"Turn my deck into a video / MP4 / narrated video"',reference:"deck-to-video.md",suffix:" <presentation-id> --yes"},{capability:"video storyboard create",intent:'"Make me a video about X" (no deck exists)',reference:"video.md",suffix:" --file ./storyboard.md \u2026"},{capability:"presentation import",intent:'"Import this PowerPoint / .pptx"',reference:"presentations.md",suffix:" --file deck.pptx --mode preserve"},{capability:"presentation speaker-notes",intent:'"Write speaker notes for this deck"',reference:"deck-to-video.md",suffix:" <presentation-id>"},{capability:"presentation video-narration",intent:'"Rewrite the narration on one slide"',reference:"deck-to-video.md",suffix:" <project-id> --slide-id <id>"},{capability:"tts generate",intent:'"Voiceover / narration / read this aloud / TTS"',reference:"media.md",suffix:' -t "Text" -o out.mp3'},{capability:"music generate",intent:'"Background music / a soundtrack"',reference:"media.md"},{capability:"sfx generate",intent:'"Sound effects"',reference:"media.md"},{capability:"mix create",intent:'"Mix this voiceover and music into the video"',reference:"media.md"},{capability:"image generate",intent:'"Generate an image"',reference:"media.md"},{capability:"search images",intent:'"Find a stock photo"',reference:"research.md",suffix:' -q "\u2026"'},{capability:"search video",intent:'"Find stock footage / b-roll"',reference:"video.md",suffix:' "ocean waves"'},{capability:"avatar options",intent:'"Which avatars / presenters / voices can I use?"',reference:"media.md"},{capability:"avatar generate",intent:'"Make a talking-head / avatar clip"',reference:"media.md"},{capability:"video translate",intent:'"Dub / translate this video"',reference:"video.md"},{capability:"video lipsync",intent:'"Re-sync the lips to this new audio"',reference:"video.md"},{capability:"branding extract",intent:'"Match my brand" / "pull branding from this site"',reference:"branding.md",suffix:" <url>"},{capability:"scrape",intent:'"Read this web page"',reference:"research.md",suffix:" https://example.com"},{capability:"transcript",intent:'"Get the transcript of this YouTube video"',reference:"research.md",suffix:" <url>"},{capability:"search web",intent:'"Search the web / find news about X"',reference:"research.md",suffix:' "query"'},{capability:"knowledge search",intent:'"What do we already have on X?"',reference:"knowledge.md"},{capability:"document create",intent:'"Save this as a document" / library filing',reference:"knowledge.md"},{capability:"microsite create",intent:'"Build a landing page / microsite"',reference:"artifacts.md"},{capability:"poll create",intent:'"Make a poll"',reference:"artifacts.md"},{capability:"visual-abstract create",intent:'"One-page visual summary"',reference:"artifacts.md"},{capability:"ad-set create",intent:'"Ad creatives for this campaign"',reference:"artifacts.md"},{capability:"agent create",intent:'"Schedule this to run every week"',reference:"agents.md"}],bp=["capabilities","config","delete","derive","export","ideas","import","list","login","logout","runner","skill","templates","whoami"];function rn(e,t=Rt("rendered without a catalog fetch")){let{name:n,cmd:r,pkg:o}=e,i=n.toUpperCase().replace(/[^A-Z0-9]/g,"_"),a=new Map(t.capabilities.map(v=>[v.name,v])),s=yp.filter(v=>a.has(v.capability)).map(v=>`| ${v.intent} | \`${r} ${v.capability}${v.suffix??""}\` | [${v.reference}](references/${v.reference}) |`).join(`
|
|
442
|
+
`),g=ft(t.capabilities).map(({capabilities:v,category:b})=>`${b} (${v.length})`).join(", "),m=Xa(t).map(({file:v,when:b})=>`| [references/${v}](references/${v}) | ${b} |`).join(`
|
|
443
|
+
`),w=[...new Set([...t.capabilities.map(v=>v.name.split(" ")[0]),...bp])].sort().map(v=>`\`${v}\``).join(", ");return`---
|
|
444
|
+
name: ${n}
|
|
445
|
+
description: >-
|
|
446
|
+
Use for ANY request involving presentations, decks, slides, pitch decks, PowerPoint,
|
|
447
|
+
speaker notes, or turning a deck into a video \u2014 plus video, MP4 export, narration,
|
|
448
|
+
voiceover, TTS, AI music, sound effects, audio mixing, avatars and talking heads,
|
|
449
|
+
lipsync, dubbing, stock images and stock footage, brand extraction and design tokens,
|
|
450
|
+
microsites, polls, ad creatives, web scraping, YouTube transcripts, and the document
|
|
451
|
+
library. Trigger it even when the user never names the product: "make me slides",
|
|
452
|
+
"turn this into a video", "add a voiceover", "find b-roll", "match our brand".
|
|
453
|
+
${t.capabilities.length} CLI capabilities, listed in references/.
|
|
454
|
+
---
|
|
523
455
|
|
|
524
|
-
|
|
456
|
+
${le(t,e)}
|
|
525
457
|
|
|
526
|
-
|
|
527
|
-
1. [CRITICAL] Wrong icons \u2014 use Bot, BrainCircuit not GitBranch
|
|
528
|
-
2. [CRITICAL] Download real OpenAI/Claude logos to public/
|
|
529
|
-
3. Add WhipPan transition out
|
|
530
|
-
4. Add code to EMPHASIS and TRANSITION PREP phases
|
|
531
|
-
\\\`\\\`\\\`
|
|
458
|
+
# ${n} CLI
|
|
532
459
|
|
|
533
|
-
|
|
460
|
+
**Install:** \`npm install -g ${o}\`
|
|
534
461
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
462
|
+
This file routes; it does not enumerate. The per-capability detail \u2014
|
|
463
|
+
command paths, cost, background/poll behaviour, required scopes \u2014 lives in
|
|
464
|
+
\`references/\`, generated from the same capability catalog the CLI itself
|
|
465
|
+
serves.
|
|
538
466
|
|
|
539
|
-
|
|
467
|
+
---
|
|
540
468
|
|
|
541
|
-
|
|
542
|
-
1. [Scenes 02, 05] Download real brand logos
|
|
543
|
-
2. [Scenes 02, 03] Wrong Lucide icons
|
|
544
|
-
3. [Scenes 04, 06] Missing animation phases
|
|
545
|
-
\\\`\\\`\\\`
|
|
469
|
+
## Task \u2192 command
|
|
546
470
|
|
|
547
|
-
|
|
471
|
+
| The user asks for\u2026 | Start here | Detail |
|
|
472
|
+
|---|---|---|
|
|
473
|
+
${s}
|
|
548
474
|
|
|
549
|
-
- "Close enough" is not passing
|
|
550
|
-
- Lucide icon for a brand logo = ALWAYS critical fail
|
|
551
|
-
- Scene under 80 lines with 5+ seconds duration = flag as too simple
|
|
552
|
-
- Be specific in fixes: exact icon name, exact component, exact file
|
|
553
|
-
- 85% pass rate minimum. Below = not ready for render
|
|
554
|
-
`}function Jt(e){let{name:t,cmd:n}=e,r=t.toUpperCase().replace(/[^A-Z0-9]/g,"_");return`---
|
|
555
|
-
name: ${t}
|
|
556
|
-
description: CLI with TTS (text-to-speech), AI music generation, sound effects, stock image/video search, audio mixing, URL scraping, and branding. Use when user asks for voiceover, narration, background music, sound effects, stock footage, or needs to scrape a website.
|
|
557
475
|
---
|
|
558
476
|
|
|
559
|
-
|
|
477
|
+
## \`presentation video\` vs \`video\` \u2014 the one confusion that costs money
|
|
560
478
|
|
|
561
|
-
|
|
479
|
+
\`${r} presentation video\` renders an **existing deck** to MP4 (narration,
|
|
480
|
+
voice, avatar, music, captions). \`${r} video\` builds a **new** video from a
|
|
481
|
+
brief or storyboard and never touches a deck.
|
|
562
482
|
|
|
563
|
-
|
|
483
|
+
They share no state, and picking the wrong one wastes a paid render. There is
|
|
484
|
+
no \`video export\` subcommand.
|
|
564
485
|
|
|
565
486
|
---
|
|
566
487
|
|
|
567
|
-
##
|
|
488
|
+
## Composing commands
|
|
568
489
|
|
|
569
|
-
|
|
490
|
+
Every table in \`references/\` carries a **Takes** and a **Returns** column. That
|
|
491
|
+
is enough to build a chain nobody wrote down. Derive the steps from the data:
|
|
570
492
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
493
|
+
- An id one command returns is the id the next command accepts.
|
|
494
|
+
- A **Returns** cell reading \`run id\` means the command works in the
|
|
495
|
+
background: poll the exact status command named in that cell until it reports
|
|
496
|
+
done, and take the resulting id from there. Never assume success from the
|
|
497
|
+
accept response.
|
|
498
|
+
- A **Takes** cell shows the required positionals and mandatory flags only. For
|
|
499
|
+
the optional ones, run \`${r} <group> <sub> --help\`.
|
|
500
|
+
- If you cannot find a step, \`${r} capabilities --search <topic>\` is
|
|
501
|
+
authoritative \u2014 not this file.
|
|
574
502
|
|
|
575
|
-
|
|
503
|
+
---
|
|
576
504
|
|
|
577
|
-
|
|
578
|
-
export ${r}_API_KEY="your-key-here"
|
|
579
|
-
\`\`\`
|
|
505
|
+
## When you are not sure a capability exists
|
|
580
506
|
|
|
581
|
-
|
|
582
|
-
When running \`${n} login\` via Bash tool, use the default behavior (opens browser).
|
|
583
|
-
Do NOT use \`--no-browser\` flag. Correct: \`Bash(${n} login)\` \u2713
|
|
507
|
+
Run this before telling the user something is unsupported:
|
|
584
508
|
|
|
585
|
-
**Verify authentication:**
|
|
586
509
|
\`\`\`bash
|
|
587
|
-
${
|
|
510
|
+
${r} capabilities --search <topic> # search every callable capability
|
|
511
|
+
${r} capabilities --category media # one category at a time
|
|
512
|
+
${r} capabilities # all of them, with exact command paths
|
|
513
|
+
${r} <group> --help # flags for one group
|
|
588
514
|
\`\`\`
|
|
589
515
|
|
|
516
|
+
\`${r} capabilities\` reads the server's live capability catalog \u2014 the same
|
|
517
|
+
source this skill was generated from, and the same one the API and MCP surfaces
|
|
518
|
+
are built on. It is therefore **more current than this file can ever be**: it
|
|
519
|
+
reflects capabilities added after this skill was installed. A capability missing
|
|
520
|
+
from these references is not evidence that it does not exist; \`capabilities\`
|
|
521
|
+
returning nothing is.
|
|
522
|
+
|
|
523
|
+
When it cannot reach the catalog it says so and prints the list baked into the
|
|
524
|
+
CLI build instead (\`--format json\` reports \`"source": "snapshot"\`). Treat that
|
|
525
|
+
output as possibly behind the deployment, not as proof a capability is gone.
|
|
526
|
+
|
|
527
|
+
Two things it does not cover: commands gated behind account feature flags (the
|
|
528
|
+
\`${r} derive\` subcommands) appear only after \`${r} login\` and
|
|
529
|
+
\`${r} config refresh\`, and commands the CLI owns without a catalog entry are
|
|
530
|
+
listed by \`${r} --help\`.
|
|
531
|
+
|
|
532
|
+
Categories: ${g}.
|
|
533
|
+
|
|
590
534
|
---
|
|
591
535
|
|
|
592
|
-
##
|
|
536
|
+
## Which reference file
|
|
593
537
|
|
|
594
|
-
|
|
595
|
-
|
|
538
|
+
| File | Read it when |
|
|
539
|
+
|---|---|
|
|
540
|
+
${m}
|
|
596
541
|
|
|
597
|
-
|
|
598
|
-
\`${n} video generate new my-video\` \u2192 Load \`${t}-video\` skill
|
|
542
|
+
---
|
|
599
543
|
|
|
600
|
-
|
|
601
|
-
\`${n} tts generate -t "Text" -o out.mp3\` \u2192 [references/tts.md](references/tts.md)
|
|
544
|
+
## Authentication and feature flags
|
|
602
545
|
|
|
603
|
-
|
|
604
|
-
|
|
546
|
+
\`\`\`bash
|
|
547
|
+
${r} login # opens a browser (do NOT pass --no-browser from an agent)
|
|
548
|
+
${r} whoami # verify
|
|
549
|
+
${r} config refresh
|
|
550
|
+
\`\`\`
|
|
605
551
|
|
|
606
|
-
|
|
607
|
-
\`${n} sfx generate --request sfx-request.json\` \u2192 [references/sfx.md](references/sfx.md)
|
|
552
|
+
Or set \`${i}_API_KEY\` in the environment.
|
|
608
553
|
|
|
609
|
-
|
|
610
|
-
\`${
|
|
554
|
+
Some command groups appear only **after** \`${r} login\` followed by
|
|
555
|
+
\`${r} config refresh\`. If a command documented here is missing from
|
|
556
|
+
\`--help\`, refresh before concluding it was removed.
|
|
611
557
|
|
|
612
|
-
|
|
613
|
-
|
|
558
|
+
Background operations return a run id \u2014 poll the status command named in the
|
|
559
|
+
reference file rather than assuming success.
|
|
614
560
|
|
|
615
|
-
|
|
616
|
-
|
|
561
|
+
Some commands take \`--yes\` to confirm a paid or irreversible action, and some
|
|
562
|
+
also require \`--idempotency-key\`. Which ones is a property of the command, not
|
|
563
|
+
of its cost: check \`${r} <command> --help\` instead of adding flags a command
|
|
564
|
+
does not declare.
|
|
617
565
|
|
|
618
|
-
|
|
619
|
-
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Every command group
|
|
569
|
+
|
|
570
|
+
Run \`${r} <group> --help\` for any of these:
|
|
620
571
|
|
|
621
|
-
|
|
622
|
-
\`${n} branding list\` \u2192 [references/branding.md](references/branding.md)
|
|
572
|
+
${w}
|
|
623
573
|
|
|
624
|
-
|
|
625
|
-
\`${
|
|
574
|
+
The groups the catalog owns are documented in \`references/\`. The rest are
|
|
575
|
+
CLI-local and never appear in \`${r} capabilities\`: auth and config
|
|
576
|
+
(\`login\`, \`logout\`, \`whoami\`, \`config\`), the legacy direct deck commands
|
|
577
|
+
(\`list\`, \`get\`, \`delete\`, \`export\`, \`import\`, \`templates\`, \`ideas\`),
|
|
578
|
+
\`derive\` (needs \`config refresh\` first), plus \`runner\` and \`skill\`.
|
|
626
579
|
|
|
627
|
-
|
|
580
|
+
## Deeper skills
|
|
581
|
+
|
|
582
|
+
- \`${r} create "Topic"\` \u2192 load the \`${n}-presentation\` skill
|
|
583
|
+
- editing an existing deck \u2192 load the \`${n}-presentation-edit\` skill
|
|
584
|
+
- building a video from a brief or storyboard, with no deck \u2192 load the \`${n}-video\` skill
|
|
585
|
+
`}function on(e){let{name:t,cmd:n}=e;return`---
|
|
628
586
|
name: ${t}-presentation-edit
|
|
629
587
|
description: >
|
|
630
588
|
Use ONLY when editing an already-created presentation that has real slides:
|
|
@@ -909,7 +867,7 @@ Do NOT render or export the presentation \u2014 that's handled by the app.
|
|
|
909
867
|
| Create brand-new presentation (5+ slides, new topic) | \`${n} create ...\` via CLI |
|
|
910
868
|
| Redesign the whole deck | \`${n} create ...\` via CLI, then sync |
|
|
911
869
|
| User provides a URL to base the presentation on | \`${n} scrape <url>\` \u2192 \`${n} create ...\` |
|
|
912
|
-
`}function
|
|
870
|
+
`}function an(e){let{name:t,cmd:n}=e;return`---
|
|
913
871
|
name: ${t}-presentation
|
|
914
872
|
description: Use when user asks to create presentations, slides, decks, pitch decks.
|
|
915
873
|
Researches the topic, gathers context, and generates AI-powered presentations via CLI.
|
|
@@ -1129,814 +1087,78 @@ ${n} import ./presentation.zip
|
|
|
1129
1087
|
- **Auth fails** \u2192 \`${n} logout && ${n} login\` (browser required)
|
|
1130
1088
|
- **"Context required"** \u2192 add \`--context\`, \`--file\`, or \`--context-file\`
|
|
1131
1089
|
- **Rate limit (429)** \u2192 check plan with \`${n} whoami\`
|
|
1132
|
-
`}function
|
|
1133
|
-
|
|
1134
|
-
|
|
1090
|
+
`}var wp=[{capability:"video reference analyze",when:"Optional \u2014 match the style of a reference video."},{capability:"video storyboard create",when:"Write the storyboard. Markdown in, immutable revision out."},{capability:"video storyboard revise",when:"Iterate. Each revision is appended, never rewritten."},{capability:"video create",when:"Render a pinned revision. Paid, approval-gated, background."}];function sn(e,t){let{cmd:n,name:r,pkg:o}=e,i=new Set(t.capabilities.map(l=>l.name)),a=wp.filter(l=>i.has(l.capability)).map((l,g)=>`${g+1}. \`${n} ${l.capability}\` \u2014 ${l.when}`).join(`
|
|
1091
|
+
`),s=yr(t.capabilities).length;return`---
|
|
1092
|
+
name: ${r}-video
|
|
1093
|
+
description: >-
|
|
1094
|
+
Use when a video must be built from scratch \u2014 a brief, a script, a storyboard,
|
|
1095
|
+
a promo, an explainer, a product demo, social/vertical content \u2014 with no deck
|
|
1096
|
+
involved. Trigger it even when the user never names the product: "make me a
|
|
1097
|
+
video about X", "turn this script into a video", "I need a 30s promo".
|
|
1098
|
+
For turning an EXISTING presentation into an MP4, use the ${r} skill's
|
|
1099
|
+
deck\u2192MP4 flow instead. ${s} CLI capabilities, listed in references/video.md.
|
|
1135
1100
|
---
|
|
1136
1101
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
\`\`\`
|
|
1140
|
-
useCurrentFrame() -> number
|
|
1141
|
-
useVideoConfig() -> { fps, width, height, durationInFrames }
|
|
1142
|
-
spring({ frame, fps, config: { stiffness, damping } }) -> 0..1
|
|
1143
|
-
interpolate(value, [in0, in1], [out0, out1], { extrapolateRight: 'clamp' }) -> number
|
|
1144
|
-
<Sequence from={N} durationInFrames={M}> -- time-based grouping
|
|
1145
|
-
<AbsoluteFill> -- root container, layers via z-index
|
|
1146
|
-
staticFile('path') -- reference public/ assets
|
|
1147
|
-
<Img src={staticFile('image.png')} /> -- images
|
|
1148
|
-
<Audio src={staticFile('sfx.mp3')} startFrom={N} /> -- SFX ONLY (not VO/music)
|
|
1149
|
-
\`\`\`
|
|
1150
|
-
|
|
1151
|
-
## 2. Plan Vocabulary Decoder
|
|
1152
|
-
|
|
1153
|
-
The video plan uses production vocabulary. Map to Remotion code exactly.
|
|
1154
|
-
|
|
1155
|
-
### Spring Values
|
|
1156
|
-
|
|
1157
|
-
Plan writes \`Spring(100, 15)\` -> first = stiffness, second = damping:
|
|
1158
|
-
\`\`\`tsx
|
|
1159
|
-
spring({ frame, fps, config: { stiffness: 100, damping: 15 } })
|
|
1160
|
-
\`\`\`
|
|
1161
|
-
Use the plan's exact numbers. Do not invent values.
|
|
1162
|
-
|
|
1163
|
-
### Rhythm Patterns
|
|
1164
|
-
|
|
1165
|
-
| Pattern | Spring config | Stagger delay | Use when |
|
|
1166
|
-
|---------|--------------|---------------|----------|
|
|
1167
|
-
| Staccato | stiffness 120+, damping 15+ | 0.05-0.1s (2-3 frames) | Energy, urgency, feature montage |
|
|
1168
|
-
| Legato | stiffness 60-80, damping 8-10 | 0.3-0.5s (9-15 frames) | Emotional weight, breathing room |
|
|
1169
|
-
| Accelerando | Each interval shorter than previous | 0.4s down to 0.1s | Building toward climax |
|
|
1170
|
-
| Decelerando | Each interval longer than previous | 0.1s up to 0.4s | Resolution, outro |
|
|
1171
|
-
|
|
1172
|
-
### Hook Types (Scene 1 only)
|
|
1173
|
-
|
|
1174
|
-
- **Cold open**: Hero visual fills frame at frame 0. Context/text enters after VO_START.
|
|
1175
|
-
- **Pattern interrupt**: Scale 200%->100% slam or color flash in first 10 frames.
|
|
1176
|
-
- **Stat hook**: Large counter (\`interpolate() + Math.floor()\`) dominates frame. Context enters after.
|
|
1177
|
-
- **Question hook**: Text question via FadeInWords, 1s pause, then answer elements enter.
|
|
1102
|
+
${le(t,e)}
|
|
1178
1103
|
|
|
1179
|
-
|
|
1104
|
+
# ${r} video
|
|
1180
1105
|
|
|
1181
|
-
|
|
1182
|
-
\`\`\`tsx
|
|
1183
|
-
<ColorGrading preset="matrix">
|
|
1184
|
-
<div style={{ background: 'rgba(220,0,0,0.1)', mixBlendMode: 'multiply', position: 'absolute', inset: 0 }} />
|
|
1185
|
-
{/* scene content */}
|
|
1186
|
-
</ColorGrading>
|
|
1187
|
-
\`\`\`
|
|
1188
|
-
|
|
1189
|
-
### Idle State Formulas
|
|
1190
|
-
|
|
1191
|
-
Copy the plan's exact values. Common patterns:
|
|
1192
|
-
|
|
1193
|
-
"breathes +-4px over 4s":
|
|
1194
|
-
\`\`\`tsx
|
|
1195
|
-
const breathe = Math.sin(frame / fps * Math.PI * 2 / 4) * 4;
|
|
1196
|
-
// style: { transform: \`translateY(\${breathe}px)\` }
|
|
1197
|
-
\`\`\`
|
|
1198
|
-
|
|
1199
|
-
"pulses opacity 0.7 to 1.0":
|
|
1200
|
-
\`\`\`tsx
|
|
1201
|
-
const pulse = interpolate(Math.sin(frame / fps * Math.PI * 2 / 3), [-1, 1], [0.7, 1.0]);
|
|
1202
|
-
// style: { opacity: pulse }
|
|
1203
|
-
\`\`\`
|
|
1204
|
-
|
|
1205
|
-
"drifts right 20px over 6s":
|
|
1206
|
-
\`\`\`tsx
|
|
1207
|
-
const drift = interpolate(frame, [0, fps * 6], [0, 20], { extrapolateRight: 'clamp' });
|
|
1208
|
-
// style: { transform: \`translateX(\${drift}px)\` }
|
|
1209
|
-
\`\`\`
|
|
1210
|
-
|
|
1211
|
-
### Transition Emotional Weight
|
|
1212
|
-
|
|
1213
|
-
Match the plan's described energy to spring intensity:
|
|
1214
|
-
- "violent snap" / "smash" -> stiffness 150+, damping 18+, fast exit
|
|
1215
|
-
- "let that sink in" / "closure" -> slow fade 1s+ (30+ frames)
|
|
1216
|
-
- "entering a world" -> blur clears gradually over 20+ frames
|
|
1217
|
-
- "and then!" / "surprise" -> instant WhipPan, stiffness 200+
|
|
1218
|
-
|
|
1219
|
-
## 3. 4-Phase Scene Architecture
|
|
1220
|
-
|
|
1221
|
-
Every scene MUST follow this structure. Constants come from Audio Timing in the Task prompt.
|
|
1222
|
-
|
|
1223
|
-
\`\`\`tsx
|
|
1224
|
-
import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate, spring, Img, Audio } from 'remotion';
|
|
1225
|
-
import { staticFile } from 'remotion';
|
|
1226
|
-
|
|
1227
|
-
export const SceneNNName: React.FC = () => {
|
|
1228
|
-
const frame = useCurrentFrame();
|
|
1229
|
-
const { fps } = useVideoConfig();
|
|
1230
|
-
|
|
1231
|
-
// === CONSTANTS (from Audio Timing) ===
|
|
1232
|
-
const VO_START = 10;
|
|
1233
|
-
const VO_DURATION = 156;
|
|
1234
|
-
const VO_END = VO_START + VO_DURATION;
|
|
1235
|
-
const SCENE_FRAMES = 210;
|
|
1236
|
-
|
|
1237
|
-
// === PHASE 1: ENTRANCE (frames 0 - VO_START) ===
|
|
1238
|
-
// Background fades in, hero elements begin entrance.
|
|
1239
|
-
// VO has NOT started -- purely visual setup.
|
|
1240
|
-
const bgOpacity = interpolate(frame, [0, 10], [0, 1], { extrapolateRight: 'clamp' });
|
|
1241
|
-
|
|
1242
|
-
// === PHASE 2: CONTENT REVEAL (frames VO_START - VO_END) ===
|
|
1243
|
-
// Progressive reveals SYNCED to voiceover.
|
|
1244
|
-
// Elements enter when the narrator mentions them.
|
|
1245
|
-
// If 4 items over VO_DURATION: item[i] at VO_START + i * (VO_DURATION / 4)
|
|
1246
|
-
|
|
1247
|
-
// === PHASE 3: EMPHASIS (frames VO_END - SCENE_FRAMES - 15) ===
|
|
1248
|
-
// VO finished. Highlight key element, camera push, color shift.
|
|
1249
|
-
|
|
1250
|
-
// === PHASE 4: TRANSITION PREP (last 15 frames) ===
|
|
1251
|
-
// Exit animation toward transition direction.
|
|
1252
|
-
const exitProgress = interpolate(frame, [SCENE_FRAMES - 15, SCENE_FRAMES], [0, 1], { extrapolateRight: 'clamp' });
|
|
1253
|
-
|
|
1254
|
-
return (
|
|
1255
|
-
<AbsoluteFill>
|
|
1256
|
-
{/* Layer A: Background (gradient, image, motion) */}
|
|
1257
|
-
{/* Layer B: Hero Content (cards, text, data viz) */}
|
|
1258
|
-
{/* Layer C: Texture (ColorGrading, GrainOverlay, Vignette) */}
|
|
1259
|
-
</AbsoluteFill>
|
|
1260
|
-
);
|
|
1261
|
-
};
|
|
1262
|
-
\`\`\`
|
|
1106
|
+
**Install:** \`npm install -g ${o}\`
|
|
1263
1107
|
|
|
1264
|
-
|
|
1108
|
+
Builds a **new** video from a brief or storyboard. This pipeline never touches a
|
|
1109
|
+
deck: \`${n} presentation video\` is the deck\u2192MP4 path, and picking the wrong
|
|
1110
|
+
one wastes a paid render. There is no \`video export\` subcommand.
|
|
1265
1111
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
The technique that separates premium from amateur:
|
|
1269
|
-
|
|
1270
|
-
### Stagger N items across VO duration
|
|
1271
|
-
|
|
1272
|
-
\`\`\`tsx
|
|
1273
|
-
const items = ['Security', 'Speed', 'Scale', 'Support'];
|
|
1274
|
-
const staggerFrames = Math.floor(VO_DURATION / items.length);
|
|
1275
|
-
|
|
1276
|
-
const itemProgress = (index: number) => spring({
|
|
1277
|
-
frame: frame - (VO_START + index * staggerFrames),
|
|
1278
|
-
fps,
|
|
1279
|
-
config: { stiffness: 100, damping: 15 },
|
|
1280
|
-
});
|
|
1281
|
-
|
|
1282
|
-
// In JSX:
|
|
1283
|
-
{items.map((item, i) => (
|
|
1284
|
-
<div key={i} style={{
|
|
1285
|
-
opacity: itemProgress(i),
|
|
1286
|
-
transform: \`translateY(\${interpolate(itemProgress(i), [0, 1], [30, 0])}px)\`,
|
|
1287
|
-
}}>
|
|
1288
|
-
{item}
|
|
1289
|
-
</div>
|
|
1290
|
-
))}
|
|
1291
|
-
\`\`\`
|
|
1292
|
-
|
|
1293
|
-
### Rhythm cues from timing manifest
|
|
1294
|
-
|
|
1295
|
-
- **BPM provided**: Align stagger to beat intervals (\`const beatFrames = Math.round(fps * 60 / bpm)\`)
|
|
1296
|
-
- **Energy = low**: Gentle springs (stiffness 60-80)
|
|
1297
|
-
- **Energy = peak**: Aggressive springs (stiffness 120+), tight staggers
|
|
1298
|
-
- **Music cue "Accelerando @00:11.0"**: Decrease stagger intervals after that frame
|
|
1299
|
-
|
|
1300
|
-
### Scenes without VO
|
|
1301
|
-
|
|
1302
|
-
No VO_START/VO_DURATION. Use SCENE_FRAMES directly:
|
|
1303
|
-
- Entrance: frames 0-15
|
|
1304
|
-
- Content: frames 15 to SCENE_FRAMES - 30
|
|
1305
|
-
- Emphasis: last 30-15 frames
|
|
1306
|
-
- Exit: last 15 frames
|
|
1307
|
-
|
|
1308
|
-
## 5. Animation Recipes
|
|
1309
|
-
|
|
1310
|
-
### Staggered card grid
|
|
1311
|
-
\`\`\`tsx
|
|
1312
|
-
const cardProgress = (i: number) => spring({
|
|
1313
|
-
frame: frame - (VO_START + i * Math.floor(VO_DURATION / cardCount)),
|
|
1314
|
-
fps,
|
|
1315
|
-
config: { stiffness: 120, damping: 12 },
|
|
1316
|
-
});
|
|
1317
|
-
// opacity: cardProgress(i)
|
|
1318
|
-
// translateY: interpolate(cardProgress(i), [0, 1], [30, 0])
|
|
1319
|
-
\`\`\`
|
|
1320
|
-
|
|
1321
|
-
### Counter animation
|
|
1322
|
-
\`\`\`tsx
|
|
1323
|
-
const count = Math.floor(interpolate(frame, [startFrame, endFrame], [0, target], {
|
|
1324
|
-
extrapolateRight: 'clamp',
|
|
1325
|
-
}));
|
|
1326
|
-
// Display: {count.toLocaleString()}
|
|
1327
|
-
\`\`\`
|
|
1328
|
-
|
|
1329
|
-
### Typing effect
|
|
1330
|
-
\`\`\`tsx
|
|
1331
|
-
const chars = Math.floor(Math.max(0, (frame - startFrame) * charsPerFrame));
|
|
1332
|
-
const text = fullText.slice(0, chars);
|
|
1333
|
-
// Cursor: opacity via Math.floor(frame / 20) % 2
|
|
1334
|
-
\`\`\`
|
|
1335
|
-
|
|
1336
|
-
### Floating particles
|
|
1337
|
-
\`\`\`tsx
|
|
1338
|
-
{Array.from({ length: 12 }).map((_, i) => {
|
|
1339
|
-
const delay = i * 8;
|
|
1340
|
-
const x = 10 + (i * 7) % 80;
|
|
1341
|
-
const y = interpolate(Math.sin((frame + delay) / 40), [-1, 1], [20, 80]);
|
|
1342
|
-
const opacity = interpolate(frame - delay, [0, 30], [0, 0.3], { extrapolateRight: 'clamp' });
|
|
1343
|
-
return <div key={i} style={{ position: 'absolute', left: \`\${x}%\`, top: \`\${y}%\`, opacity,
|
|
1344
|
-
width: 4, height: 4, borderRadius: '50%', background: 'rgba(255,255,255,0.5)' }} />;
|
|
1345
|
-
})}
|
|
1346
|
-
\`\`\`
|
|
1347
|
-
|
|
1348
|
-
### Glass card
|
|
1349
|
-
\`\`\`tsx
|
|
1350
|
-
style={{
|
|
1351
|
-
background: 'rgba(255,255,255,0.05)',
|
|
1352
|
-
backdropFilter: 'blur(20px)',
|
|
1353
|
-
border: '1px solid rgba(255,255,255,0.1)',
|
|
1354
|
-
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.15), 0 8px 32px rgba(0,0,0,0.3)',
|
|
1355
|
-
borderRadius: 24,
|
|
1356
|
-
padding: 32,
|
|
1357
|
-
}}
|
|
1358
|
-
\`\`\`
|
|
1359
|
-
|
|
1360
|
-
### Camera push (Ken Burns)
|
|
1361
|
-
\`\`\`tsx
|
|
1362
|
-
const scale = interpolate(frame, [pushStart, pushEnd], [1.0, 1.05], { extrapolateRight: 'clamp' });
|
|
1363
|
-
// Apply to background layer: transform: \`scale(\${scale})\`
|
|
1364
|
-
\`\`\`
|
|
1365
|
-
|
|
1366
|
-
### Gradient text
|
|
1367
|
-
\`\`\`tsx
|
|
1368
|
-
style={{
|
|
1369
|
-
backgroundImage: 'linear-gradient(135deg, #FFFFFF 0%, #9CA3AF 100%)',
|
|
1370
|
-
WebkitBackgroundClip: 'text',
|
|
1371
|
-
WebkitTextFillColor: 'transparent',
|
|
1372
|
-
}}
|
|
1373
|
-
\`\`\`
|
|
1374
|
-
|
|
1375
|
-
### Pulsing glow
|
|
1376
|
-
\`\`\`tsx
|
|
1377
|
-
const glow = interpolate(Math.sin(frame / fps * Math.PI * 2 / 2), [-1, 1], [0.1, 0.3]);
|
|
1378
|
-
// boxShadow: \`0 0 \${glow * 60}px rgba(59, 130, 246, \${glow})\`
|
|
1379
|
-
\`\`\`
|
|
1380
|
-
|
|
1381
|
-
## 6. Quality Floor and Anti-patterns
|
|
1382
|
-
|
|
1383
|
-
### Minimum requirements (reviewer checks at 85%+ pass rate):
|
|
1384
|
-
|
|
1385
|
-
1. 100+ lines of code per scene
|
|
1386
|
-
2. Real Lucide React icons only (\`import { Shield, Lock } from 'lucide-react'\`). Never text/emoji substitutes.
|
|
1387
|
-
3. Real logo files from public/. Never Lucide icons as brand logos.
|
|
1388
|
-
4. Brand fonts via loadFont() or @font-face. Never "closest Google Font match".
|
|
1389
|
-
5. ColorGrading wrapper when plan specifies a preset.
|
|
1390
|
-
6. Template transitions: use exact components from plan. Never generic substitutes.
|
|
1391
|
-
7. Background motion: gradients, drift, particles. Never flat solid color.
|
|
1392
|
-
8. Idle animations after entrance: float, breathe, pulse, drift.
|
|
1393
|
-
9. SFX via \`<Audio>\` at correct frames when files available.
|
|
1394
|
-
10. VO-sync stagger: elements enter when narrator mentions them. Never all at once.
|
|
1395
|
-
11. Audio timing constants (VO_START, VO_DURATION, VO_END, SCENE_FRAMES) at top of component.
|
|
1396
|
-
|
|
1397
|
-
### Anti-patterns (will cause reviewer FAIL):
|
|
1398
|
-
|
|
1399
|
-
- setTimeout / useEffect -> Remotion is declarative, frame-based. Use \`frame\` directly.
|
|
1400
|
-
- Hardcoded frame numbers -> Use VO_START / VO_END / SCENE_FRAMES constants.
|
|
1401
|
-
- \`<Audio>\` for VO or music -> Main.tsx handles via VoiceoverSequence + DuckedMusic. Scenes are visual only.
|
|
1402
|
-
- All elements entering on frame 0 while VO narrates them one by one -> Stagger to VO timing.
|
|
1403
|
-
- Flat solid backgrounds -> Always gradient, noise, or moving texture.
|
|
1404
|
-
- Static scenes with no visual change for 2+ seconds -> Every 2s needs a visual event.
|
|
1405
|
-
- Generic component substitution -> Plan says WhipPan, use WhipPan. Not slide() or wipe().
|
|
1406
|
-
- Unused variables or imports -> Every \`const\`, \`import\`, and \`interpolate()\` call must be used in JSX/return. Remove what you don't use. Do NOT prefix with \`_\` \u2014 delete entirely.
|
|
1407
|
-
`}function Ht(e){let{name:t,cmd:n,mode:r="standalone"}=e,o=r==="studio";return`---
|
|
1408
|
-
name: ${t}-video
|
|
1409
|
-
description: Orchestrates video creation by coordinating specialized agents for branding, audio, and scene building. Gathers context, writes briefs, generates plans, and assembles Remotion videos. Use when user says "create a video", "make a promo", "product demo", "explainer video", "marketing video", "gather video assets", "video for my project", or "TikTok/Reels content".
|
|
1410
|
-
---
|
|
1411
|
-
|
|
1412
|
-
Follow the steps in order. Some steps delegate work to specialized agents (branding, audio, scenes) \u2014 spawn them and wait for results before continuing. In YOLO mode, make decisions autonomously instead of asking the user. In other modes, use AskUserQuestion when a step offers it.
|
|
1112
|
+
---
|
|
1413
1113
|
|
|
1414
|
-
##
|
|
1114
|
+
## The flow
|
|
1415
1115
|
|
|
1416
|
-
|
|
1116
|
+
${a}
|
|
1417
1117
|
|
|
1418
|
-
|
|
1419
|
-
${n} whoami
|
|
1420
|
-
\`\`\`
|
|
1118
|
+
Then poll \`${n} video status <run-id>\` until it reports done.
|
|
1421
1119
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
- Wait for user to authenticate before continuing
|
|
1120
|
+
Exact flags, the storyboard/revision rules, the approval-receipt handshake and
|
|
1121
|
+
the other video operations (\`find\`, \`translate\`, \`lipsync\`, \`library list\`,
|
|
1122
|
+
\`analyze\`) are in [references/video.md](references/video.md), with a capability
|
|
1123
|
+
table generated from the same catalog the CLI serves.
|
|
1427
1124
|
|
|
1428
|
-
|
|
1125
|
+
---
|
|
1429
1126
|
|
|
1430
1127
|
## References
|
|
1431
1128
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
## Related Commands (use \`--help\` for details)
|
|
1437
|
-
|
|
1438
|
-
| Command | Purpose |
|
|
1439
|
-
|---------|---------|
|
|
1440
|
-
| \`${n} scrape <url>\` | Extract content from URLs (websites, YouTube, Twitter) |
|
|
1441
|
-
| \`${n} image search\` | Search stock images |
|
|
1442
|
-
| \`${n} video find\` | Search stock video clips |
|
|
1443
|
-
| \`${n} video analyze <file>\` | Analyze video: scene detection, transcription, frame descriptions |
|
|
1444
|
-
|
|
1445
|
-
Branding commands (\`branding find/extract/get\`) are used by the \`video-branding-director\` \u2014 see Step 3.
|
|
1446
|
-
Audio is generated in Step 8. The backend, not the sandbox, chooses music and SFX from final video facts.
|
|
1447
|
-
|
|
1448
|
-
## Step 1: Initialize Project
|
|
1449
|
-
|
|
1450
|
-
\`\`\`bash
|
|
1451
|
-
${n} video generate new my-video # Run in BACKGROUND, continue to Step 2 immediately
|
|
1452
|
-
\`\`\`
|
|
1453
|
-
|
|
1454
|
-
**MANDATORY:** Run project creation in background and proceed to Step 2 while dependencies install.
|
|
1455
|
-
|
|
1456
|
-
Projects are stored in \`~/.conceptcraft/projects/\`. Use \`${n} video projects\` to list all.
|
|
1457
|
-
|
|
1458
|
-
**Revisions:** If user requests changes (new music, different voice, etc.), render a new output file (e.g., \`out/video_v2.mp4\`) \u2014 do not overwrite the original.
|
|
1459
|
-
|
|
1460
|
-
## Step 2: Explore Source & Gather Context
|
|
1461
|
-
|
|
1462
|
-
Understand user's request and identify source type. Gather all relevant information for the video.
|
|
1463
|
-
|
|
1464
|
-
**For URLs/websites:** \`${n} scrape <url>\` - extracts content and page structure.
|
|
1465
|
-
**For codebases:** Explore repo, find UI components, README, docs.
|
|
1466
|
-
**For topics:** Research via web search, gather key points.
|
|
1467
|
-
|
|
1468
|
-
If the scraped content contains useful images (hero shots, product photos, screenshots, diagrams), download them to \`./public/\` \u2014 real assets from the source look better than stock photos:
|
|
1469
|
-
\`\`\`bash
|
|
1470
|
-
curl -L -o ./public/hero-image.jpg "https://example.com/blog/wp-content/uploads/hero.jpg"
|
|
1471
|
-
curl -L -o ./public/screenshot-1.png "https://example.com/images/feature-screenshot.png"
|
|
1472
|
-
\`\`\`
|
|
1473
|
-
If branding is needed (logos, colors, fonts), there's a dedicated \`video-branding-director\` for that in Step 3. It uses the CLI's branding extraction pipeline which handles logo resolution, color palette extraction, font file downloads, and S3-hosted assets \u2014 things that are tricky to do manually from a scraped page.
|
|
1474
|
-
|
|
1475
|
-
Check \`references/\` folder for detailed guidance.
|
|
1476
|
-
|
|
1477
|
-
## Step 3: Choose Creation Mode
|
|
1478
|
-
|
|
1479
|
-
After gathering context, ask user to choose a creation mode using AskUserQuestion:
|
|
1480
|
-
|
|
1481
|
-
### Mode Options:
|
|
1482
|
-
|
|
1483
|
-
**1. YOLO (Full Auto)**
|
|
1484
|
-
- Fully autonomous \u2014 NO questions, NO brief review, NO approvals
|
|
1485
|
-
- AI makes all decisions and executes end-to-end
|
|
1486
|
-
- Best for: Quick videos, trust AI judgment, time-sensitive
|
|
1487
|
-
|
|
1488
|
-
**2. Quick Setup**
|
|
1489
|
-
- AI asks 4-5 basic questions before proceeding
|
|
1490
|
-
- Best for: Most use cases, balanced control
|
|
1491
|
-
|
|
1492
|
-
**3. Interview Mode**
|
|
1493
|
-
- Detailed interview to capture comprehensive video requirements
|
|
1494
|
-
- AI asks in-depth questions about every aspect
|
|
1495
|
-
- Best for: Important videos, specific vision, brand-critical content
|
|
1129
|
+
| File | Read it when |
|
|
1130
|
+
|---|---|
|
|
1131
|
+
| [references/video.md](references/video.md) | A video is being built from a brief or storyboard, with no deck involved. |
|
|
1132
|
+
| [references/deck-to-video.md](references/deck-to-video.md) | The user already has a deck and wants it as an MP4. |
|
|
1496
1133
|
|
|
1497
1134
|
---
|
|
1498
1135
|
|
|
1499
|
-
|
|
1500
|
-
Proceed directly to Step 5, then **skip Step 6** (no brief review) and continue to Step 7. Do not ask for any approvals.
|
|
1501
|
-
|
|
1502
|
-
### If user selects Quick Setup:
|
|
1503
|
-
Ask 4-5 essential questions using AskUserQuestion (batch into 1-2 calls). Based on gathered context, determine what's most important to clarify. Then proceed to Step 5.
|
|
1136
|
+
## Composing commands
|
|
1504
1137
|
|
|
1505
|
-
|
|
1506
|
-
|
|
1138
|
+
The capability table in [references/video.md](references/video.md) carries a
|
|
1139
|
+
**Takes** and a **Returns** column; derive the chain from those rather than
|
|
1140
|
+
looking for a scripted walkthrough. An id one command returns is the id the
|
|
1141
|
+
next one accepts, and a \`run id\` return means poll the status command named in
|
|
1142
|
+
that cell until it reports done.
|
|
1507
1143
|
|
|
1508
1144
|
---
|
|
1509
1145
|
|
|
1510
|
-
##
|
|
1511
|
-
|
|
1512
|
-
Spawn the \`video-branding-director\` agent when the user's request involves a brand or company. If no brand is involved: in YOLO mode skip this step, otherwise ask the user via AskUserQuestion.
|
|
1146
|
+
## When you are not sure a capability exists
|
|
1513
1147
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
\`\`\`
|
|
1517
|
-
## Branding Director Task
|
|
1518
|
-
|
|
1519
|
-
**Brand query:** [company name or URL from user's request, e.g. "alfaromeousa.com" or "Nike"]
|
|
1520
|
-
**Project directory:** [absolute path to current project]
|
|
1521
|
-
|
|
1522
|
-
Search for existing brand profiles, extract if needed, download all assets locally, and write branding.json.
|
|
1523
|
-
\`\`\`
|
|
1524
|
-
|
|
1525
|
-
**Wait for the agent to complete before proceeding to Step 5.**
|
|
1526
|
-
|
|
1527
|
-
If the agent succeeds \u2014 \`branding.json\` is ready. Use it in Step 5 (brief) and Step 7 (plan generation via \`--branding\` flag).
|
|
1528
|
-
|
|
1529
|
-
If the agent fails \u2014 stop and ask the user via AskUserQuestion with options: "Continue without branding" / "Try a different URL". This applies in every mode, including YOLO. If the user provides a different URL, spawn the agent again with the new URL. You do not have enough information to make branding decisions on your own \u2014 colors, fonts, and logos from a scraped page are not the same as a proper brand profile.
|
|
1530
|
-
|
|
1531
|
-
## Step 5: Prepare Brief
|
|
1532
|
-
|
|
1533
|
-
Following [references/brief-guide.md](references/brief-guide.md), prepare a comprehensive brief covering:
|
|
1534
|
-
|
|
1535
|
-
1. **Subject** \u2014 What this video is about (product, service, brand, etc.). Go deep on mechanics, value, audience.
|
|
1536
|
-
2. **Brand** \u2014 Follow the rules in [references/brief-guide.md](references/brief-guide.md) \xA7 2. Do NOT invent colors, fonts, or visual direction. One line if \`branding.json\` exists. One line ("No brand provided.") if no brand and user hasn't specified style.
|
|
1537
|
-
3. **Key Messages** \u2014 Priority-ordered list of what must be communicated.
|
|
1538
|
-
4. **Available Assets** \u2014 List ALL real assets downloaded in Step 2 and Step 3 with descriptions:
|
|
1539
|
-
- Images from source (hero-image.jpg \u2014 blog header showing two people collaborating, screenshot-1.png \u2014 product UI screenshot, etc.)
|
|
1540
|
-
- Logo files (microsoft-logo.png \u2014 brand wordmark)
|
|
1541
|
-
- Font files (.woff2 in public/fonts/)
|
|
1542
|
-
- Mark each asset: **Use as background/overlay** vs **Reference only**
|
|
1543
|
-
- **Asset priority:** (1) Real images/screenshots from the source \u2014 always more authentic than stock. (2) Code-built React components for anything that benefits from being animated or dynamic (cards, grids, data visualizations, UI mockups). (3) Stock photos only when the plan requires real photography (people, places, objects) that wasn't available from the source.
|
|
1544
|
-
5. **UI Components** \u2014 (Only for codebase sources) Path + visual description of key components.
|
|
1545
|
-
6. **Parameters** \u2014 Desired duration, platform, voiceover yes/no, music yes/no.
|
|
1546
|
-
|
|
1547
|
-
Write the brief to \`video-brief.md\`.
|
|
1548
|
-
|
|
1549
|
-
## Step 6: Review Brief with User
|
|
1550
|
-
|
|
1551
|
-
Show the brief to user and ask using AskUserQuestion:
|
|
1552
|
-
- "Here's the video brief I've prepared. Is this good to proceed, or would you like to adjust anything?"
|
|
1553
|
-
- Options: "Looks good, proceed" / "I want to adjust something"
|
|
1554
|
-
|
|
1555
|
-
If user wants adjustments \u2014 make changes and ask again.
|
|
1556
|
-
If user approves \u2014 proceed to Step 7.
|
|
1557
|
-
|
|
1558
|
-
## Step 7: Generate Plan
|
|
1559
|
-
|
|
1560
|
-
Once brief is approved, generate the video plan. The \`--branding\` flag sends brand identity JSON to the server \u2014 the plan generator uses it to make accurate color, logo, and typography decisions across all scenes. Always pass it when \`branding.json\` exists.
|
|
1148
|
+
Run this before telling the user something is unsupported:
|
|
1561
1149
|
|
|
1562
1150
|
\`\`\`bash
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
# With branding (always use when branding.json exists)
|
|
1567
|
-
cat video-brief.md | ${n} video generate plan -o video-plan.md --branding branding.json
|
|
1151
|
+
${n} capabilities --search <topic>
|
|
1152
|
+
${n} video <sub> --help
|
|
1568
1153
|
\`\`\`
|
|
1569
1154
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
The returned \`video-plan.md\` is your source of truth for everything that follows:
|
|
1573
|
-
- Scene breakdown with scripts (use for voiceover generation)
|
|
1574
|
-
- Visual strategy per scene (use for building Remotion scenes)
|
|
1575
|
-
- The video's subject and narration (send as facts for backend music/SFX planning)
|
|
1576
|
-
- Assets needed and timing
|
|
1577
|
-
|
|
1578
|
-
Follow the plan precisely for all subsequent steps.
|
|
1579
|
-
|
|
1580
|
-
## Step 8: Generate Audio Assets & Timing
|
|
1581
|
-
|
|
1582
|
-
Generate TTS first, capture its actual durations/word timings, and reconcile the final storyboard/timeline. Only after those timings are final, create factual JSON requests for music and SFX. Do not write a music prompt, SFX prompt, provider plan, style list, or provider options: Gemini plans them on the backend.
|
|
1583
|
-
|
|
1584
|
-
Music request:
|
|
1585
|
-
|
|
1586
|
-
\`\`\`json
|
|
1587
|
-
{"schema_version":1,"video_description":"What the finished video is about","duration_s":30,"narration":[{"id":"scene-1","text":"Exact narration","start_s":0,"end_s":4.2}]}
|
|
1588
|
-
\`\`\`
|
|
1589
|
-
|
|
1590
|
-
\`\`\`bash
|
|
1591
|
-
${n} music generate --request music-request.json --output ./public/audio/music.mp3
|
|
1592
|
-
\`\`\`
|
|
1593
|
-
|
|
1594
|
-
For each meaningful visual cue that needs a generated sound, reuse the same facts and add one \`visual_event\`. Include TTS word timings when available so the backend can align the sound with narration:
|
|
1595
|
-
|
|
1596
|
-
\`\`\`json
|
|
1597
|
-
{"schema_version":1,"video_description":"What the finished video is about","duration_s":30,"narration":[{"id":"scene-1","text":"Exact narration","start_s":0,"end_s":4.2,"words":[{"text":"launch","start":2.1,"end":2.5}]}],"visual_event":{"id":"logo-lock","description":"The logo tiles lock into place","start_s":2.1,"end_s":2.9}}
|
|
1598
|
-
\`\`\`
|
|
1599
|
-
|
|
1600
|
-
\`\`\`bash
|
|
1601
|
-
${n} sfx generate --request sfx-logo-lock.json --output ./public/audio/logo-lock.mp3
|
|
1602
|
-
\`\`\`
|
|
1603
|
-
|
|
1604
|
-
**Available voices:** \`${n} tts voices -p <provider>\`
|
|
1605
|
-
- **Gemini (default):** Puck, Kore, Charon, Zephyr, Fenrir, Aoede, Enceladus...
|
|
1606
|
-
- **OpenAI:** alloy, coral, nova, onyx, sage, shimmer, ash, ballad...
|
|
1607
|
-
- **ElevenLabs:** Use \`--voice-id\` with ID from \`${n} tts voices -p elevenlabs\`
|
|
1608
|
-
|
|
1609
|
-
### Stock Media (only if plan requires it)
|
|
1610
|
-
|
|
1611
|
-
Search for stock media if the plan references real photos/footage. CSS-built visuals do NOT need stock media.
|
|
1612
|
-
|
|
1613
|
-
\`\`\`bash
|
|
1614
|
-
${n} image search -q "exact description from plan" --max-results 3
|
|
1615
|
-
${n} video find "exact description from plan" --max-results 3
|
|
1616
|
-
\`\`\`
|
|
1617
|
-
|
|
1618
|
-
### Final Audio Checks
|
|
1619
|
-
|
|
1620
|
-
Verify the outputs:
|
|
1621
|
-
- \`timing-manifest.md\` records actual TTS durations, final scene timing, and word timings when available
|
|
1622
|
-
- All VO files exist in \`./public/audio/\`
|
|
1623
|
-
- Music file exists
|
|
1624
|
-
- Read \`timing-manifest.md\` \u2014 you'll need it for Step 9b (Main.tsx) and Step 9c (scene builders)
|
|
1625
|
-
|
|
1626
|
-
**Audio mixing is handled automatically** by the template's \`DuckedMusic\` component (see Step 9b).
|
|
1627
|
-
|
|
1628
|
-
## Step 9: Build Scenes and Render Video
|
|
1629
|
-
|
|
1630
|
-
**File structure:** Create \`src/scenes/\` folder with one \`.tsx\` file per scene.
|
|
1631
|
-
|
|
1632
|
-
### 9a. Component Discovery (BEFORE writing ANY code)
|
|
1633
|
-
|
|
1634
|
-
List available template components. Do NOT read every file \u2014 just get the directory listing:
|
|
1635
|
-
|
|
1636
|
-
\`\`\`bash
|
|
1637
|
-
ls src/components/transitions/ src/components/effects/
|
|
1638
|
-
\`\`\`
|
|
1639
|
-
|
|
1640
|
-
Then read ONLY the barrel exports to get component names and props:
|
|
1641
|
-
\`\`\`bash
|
|
1642
|
-
cat src/components/transitions/index.ts src/components/effects/index.ts 2>/dev/null
|
|
1643
|
-
\`\`\`
|
|
1644
|
-
|
|
1645
|
-
Output a brief **Component Inventory** listing component names and import paths. Scene builders have the \`remotion-scene-craft\` skill preloaded and will read individual component files themselves if they need prop details.
|
|
1646
|
-
|
|
1647
|
-
### 9b. Shared Setup
|
|
1648
|
-
|
|
1649
|
-
Before spawning scene agents, create the shared infrastructure they depend on:
|
|
1650
|
-
|
|
1651
|
-
**1. Font Registration Component (if brand fonts exist):**
|
|
1652
|
-
|
|
1653
|
-
Read \`branding.json\` \u2192 check \`brandData.enhancedTypography.fontStack.primary.source\`:
|
|
1654
|
-
|
|
1655
|
-
**If \`source: "google-fonts"\`** \u2014 note the Google Font name and \`loadFont()\` import for agents.
|
|
1656
|
-
|
|
1657
|
-
**If \`source: "web-font"\` or \`"custom-upload"\`** \u2014 create a shared font face string that agents can import:
|
|
1658
|
-
\`\`\`tsx
|
|
1659
|
-
// src/lib/fonts.ts
|
|
1660
|
-
import { staticFile } from 'remotion';
|
|
1661
|
-
|
|
1662
|
-
export const brandFontFace = \`
|
|
1663
|
-
@font-face {
|
|
1664
|
-
font-family: 'BrandFont';
|
|
1665
|
-
src: url('\${staticFile("fonts/abc123.woff2")}') format('woff2');
|
|
1666
|
-
font-weight: 400; font-style: normal;
|
|
1667
|
-
}
|
|
1668
|
-
@font-face {
|
|
1669
|
-
font-family: 'BrandFont';
|
|
1670
|
-
src: url('\${staticFile("fonts/def456.woff2")}') format('woff2');
|
|
1671
|
-
font-weight: 700; font-style: normal;
|
|
1672
|
-
}
|
|
1673
|
-
\`;
|
|
1674
|
-
export const brandFontFamily = "'BrandFont', sans-serif";
|
|
1675
|
-
\`\`\`
|
|
1676
|
-
|
|
1677
|
-
Use the EXACT font family name from \`fontStack.primary.family\`. Font files should already be in \`./public/fonts/\` (downloaded by the branding agent in Step 3).
|
|
1678
|
-
|
|
1679
|
-
**2. Parse video-plan.md into per-scene sections:**
|
|
1680
|
-
|
|
1681
|
-
Read \`video-plan.md\` and split it into sections:
|
|
1682
|
-
- **Video Overview** \u2014 everything before the first \`## Scene\` header
|
|
1683
|
-
- **Per-scene sections** \u2014 split by \`## Scene\` headers. Each section = everything from one \`## Scene\` header to the next (or EOF)
|
|
1684
|
-
- Count the scenes and note each scene's title, duration, transition in, and transition out
|
|
1685
|
-
|
|
1686
|
-
**3. Create Main.tsx using the template's audio components:**
|
|
1687
|
-
|
|
1688
|
-
The template provides \`VoiceoverSequence\`, \`DuckedMusic\`, and timing helpers in \`src/audio/\`.
|
|
1689
|
-
|
|
1690
|
-
**Key principle: visual Sequences and audio components are SIBLINGS, not nested.** Voiceover lives in its own layer \u2014 immune to scene transition overlap. Music auto-ducks during VO.
|
|
1691
|
-
|
|
1692
|
-
Build \`src/Main.tsx\` following this exact pattern. Use **timing-manifest.md** from Step 8 for all durations and frame positions:
|
|
1693
|
-
|
|
1694
|
-
\`\`\`tsx
|
|
1695
|
-
import { AbsoluteFill, Sequence, staticFile } from 'remotion';
|
|
1696
|
-
import { VoiceoverSequence, DuckedMusic, T_TRANSITION } from './audio';
|
|
1697
|
-
import type { VoSegment } from './audio';
|
|
1698
|
-
// Import scenes (agents will create these files)
|
|
1699
|
-
import { Scene01Hook } from './scenes/Scene01Hook';
|
|
1700
|
-
import { Scene02Feature } from './scenes/Scene02Feature';
|
|
1701
|
-
// ... one import per scene
|
|
1702
|
-
|
|
1703
|
-
// \u2500\u2500 Scene timing (from Timing Manifest \u2014 use ADJUSTED durations) \u2500\u2500
|
|
1704
|
-
const SCENE_DURATIONS = [120, 210, 180, 150]; // frames per scene
|
|
1705
|
-
|
|
1706
|
-
// TOTAL_FRAMES is CALCULATED, never hardcoded
|
|
1707
|
-
const TOTAL_FRAMES =
|
|
1708
|
-
SCENE_DURATIONS.reduce((a, b) => a + b, 0) -
|
|
1709
|
-
(SCENE_DURATIONS.length - 1) * T_TRANSITION;
|
|
1710
|
-
|
|
1711
|
-
// \u2500\u2500 VO segments (from Timing Manifest \u2014 absolute frame positions) \u2500\u2500
|
|
1712
|
-
const voSegments: VoSegment[] = [
|
|
1713
|
-
{ src: staticFile('audio/scene-1.wav'), from: 10, durationInFrames: 69 },
|
|
1714
|
-
{ src: staticFile('audio/scene-2.wav'), from: 115, durationInFrames: 156 },
|
|
1715
|
-
// ... one per scene with VO
|
|
1716
|
-
];
|
|
1717
|
-
|
|
1718
|
-
// \u2500\u2500 Helper: scene start frame accounting for transition overlaps \u2500\u2500
|
|
1719
|
-
function sceneStart(index: number): number {
|
|
1720
|
-
let start = 0;
|
|
1721
|
-
for (let i = 0; i < index; i++) {
|
|
1722
|
-
start += SCENE_DURATIONS[i] - T_TRANSITION;
|
|
1723
|
-
}
|
|
1724
|
-
return start;
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
export const Main: React.FC = () => {
|
|
1728
|
-
return (
|
|
1729
|
-
<AbsoluteFill style={{ background: '#000' }}>
|
|
1730
|
-
{/* \u2500\u2500 VISUAL LAYER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1731
|
-
<Sequence from={sceneStart(0)} durationInFrames={SCENE_DURATIONS[0]}>
|
|
1732
|
-
<Scene01Hook />
|
|
1733
|
-
</Sequence>
|
|
1734
|
-
<Sequence from={sceneStart(1)} durationInFrames={SCENE_DURATIONS[1]}>
|
|
1735
|
-
<Scene02Feature />
|
|
1736
|
-
</Sequence>
|
|
1737
|
-
{/* ... one Sequence per scene */}
|
|
1738
|
-
|
|
1739
|
-
{/* \u2500\u2500 AUDIO LAYER (siblings, NOT nested in scenes) \u2500\u2500 */}
|
|
1740
|
-
<VoiceoverSequence segments={voSegments} />
|
|
1741
|
-
<DuckedMusic
|
|
1742
|
-
src={staticFile('audio/music.mp3')}
|
|
1743
|
-
durationInFrames={TOTAL_FRAMES}
|
|
1744
|
-
voSegments={voSegments}
|
|
1745
|
-
/>
|
|
1746
|
-
</AbsoluteFill>
|
|
1747
|
-
);
|
|
1748
|
-
};
|
|
1749
|
-
\`\`\`
|
|
1750
|
-
|
|
1751
|
-
**Also update \`src/Root.tsx\`** to use the adjusted TOTAL_FRAMES:
|
|
1752
|
-
\`\`\`tsx
|
|
1753
|
-
import { DEFAULT_FPS } from './audio/constants';
|
|
1754
|
-
// ... in Composition: fps={DEFAULT_FPS}, durationInFrames={TOTAL_FRAMES}
|
|
1755
|
-
\`\`\`
|
|
1756
|
-
|
|
1757
|
-
**Key rules:**
|
|
1758
|
-
- \`SCENE_DURATIONS\` comes from \`timing-manifest.md\`, NOT from the plan's original estimates
|
|
1759
|
-
- \`TOTAL_FRAMES\` is CALCULATED: sum minus (N-1) \xD7 T_TRANSITION
|
|
1760
|
-
- VO \`from\` values are ABSOLUTE composition frame positions from the manifest
|
|
1761
|
-
- Audio is NEVER nested inside scene Sequences \u2014 \`VoiceoverSequence\` and \`DuckedMusic\` are siblings
|
|
1762
|
-
- \`DuckedMusic\` auto-ducks during VO \u2014 no manual volume math needed
|
|
1763
|
-
- Scenes are VISUAL ONLY \u2014 they do not contain \`<Audio>\` for VO/music (SFX is the exception)
|
|
1764
|
-
|
|
1765
|
-
### 9c. Spawn Per-Scene Builder Agents
|
|
1766
|
-
|
|
1767
|
-
**For EACH scene** from the parsed plan, spawn a \`video-scene-builder\` agent using the Task tool (model: opus).
|
|
1768
|
-
|
|
1769
|
-
**Construct the Task prompt for each scene with these sections:**
|
|
1770
|
-
|
|
1771
|
-
\`\`\`
|
|
1772
|
-
## Global Context
|
|
1773
|
-
|
|
1774
|
-
**Video Overview:**
|
|
1775
|
-
[Paste the Video Overview section \u2014 duration, FPS, core feeling, arc, global texture]
|
|
1776
|
-
|
|
1777
|
-
**Brand Colors:**
|
|
1778
|
-
- Primary: [hex]
|
|
1779
|
-
- Accent: [hex]
|
|
1780
|
-
- Background: [hex]
|
|
1781
|
-
|
|
1782
|
-
**Font Setup:**
|
|
1783
|
-
[Either: "Use loadFont() from '@remotion/google-fonts/FontName'"
|
|
1784
|
-
Or: "Import { brandFontFace, brandFontFamily } from '../lib/fonts' \u2014 use @font-face with brandFontFace string and brandFontFamily for CSS"]
|
|
1785
|
-
|
|
1786
|
-
**Logo:** [path, e.g. staticFile('logo.png')]
|
|
1787
|
-
|
|
1788
|
-
**Component Inventory:**
|
|
1789
|
-
[Paste the inventory from step 9a]
|
|
1790
|
-
|
|
1791
|
-
## Scene Section
|
|
1792
|
-
|
|
1793
|
-
[Paste the VERBATIM scene section from video-plan.md \u2014 everything between two ## Scene headers]
|
|
1794
|
-
|
|
1795
|
-
## Audio Timing (from Timing Manifest)
|
|
1796
|
-
|
|
1797
|
-
**Scene duration:** [Y.Ys] ([M] frames at 30fps)
|
|
1798
|
-
**Actual VO duration:** [X.Xs] ([N] frames at 30fps)
|
|
1799
|
-
**VO text:** "[exact text from Voiceover Script section]"
|
|
1800
|
-
**Phase breakdown (use these exact frame numbers):**
|
|
1801
|
-
- Phase 1 ENTRANCE: frames 0\u201310 (before VO starts)
|
|
1802
|
-
- Phase 2 CONTENT+VO: frames 10\u2013[10+N] (progressive reveals synced to VO)
|
|
1803
|
-
- Phase 3 EMPHASIS: frames [10+N]\u2013[M-15] (post-VO highlight, camera push)
|
|
1804
|
-
- Phase 4 TRANSITION PREP: frames [M-15]\u2013[M] (exit animation)
|
|
1805
|
-
|
|
1806
|
-
## Rhythm & Energy (from Timing Manifest)
|
|
1807
|
-
|
|
1808
|
-
**BPM:** [e.g. 115]
|
|
1809
|
-
**This scene's rhythm:** [e.g. staccato / legato / accelerando / decelerando]
|
|
1810
|
-
**Energy level:** [e.g. low / building / peak / resolving]
|
|
1811
|
-
**Music cue:** [e.g. "Accelerando @00:11.0" or "none"]
|
|
1812
|
-
|
|
1813
|
-
Use rhythm to set spring configs and stagger delays \u2014 see Step 2b vocabulary table.
|
|
1814
|
-
|
|
1815
|
-
**NOTE:** Audio (VO + music) is handled in Main.tsx. Your scene is VISUAL ONLY. Do NOT add \`<Audio>\` for voiceover or music. SFX \`<Audio>\` for short sound effects is OK.
|
|
1816
|
-
|
|
1817
|
-
## Output
|
|
1818
|
-
|
|
1819
|
-
Write the scene to: src/scenes/Scene[NN][Name].tsx
|
|
1820
|
-
|
|
1821
|
-
## Adjacent Transitions
|
|
1822
|
-
|
|
1823
|
-
- Previous scene's transition out: [e.g. "WhipPan left-to-right" or "N/A \u2014 this is Scene 1"]
|
|
1824
|
-
- Next scene's transition in: [e.g. "BlurTransition in" or "N/A \u2014 this is the last scene"]
|
|
1825
|
-
\`\`\`
|
|
1826
|
-
|
|
1827
|
-
**Launch ALL scene agents in parallel** \u2014 scenes are independent files. Use the Task tool with multiple calls in one message. Each agent builds its scene, reviews it internally with the \`video-scene-reviewer\` agent, and fixes issues until 85%+ quality.
|
|
1828
|
-
|
|
1829
|
-
**Wait for all agents to complete** before proceeding.
|
|
1830
|
-
|
|
1831
|
-
### 9d. Continuity Check
|
|
1832
|
-
|
|
1833
|
-
After all scene builders complete, spawn the \`video-continuity-editor\` agent to catch cross-scene issues:
|
|
1834
|
-
|
|
1835
|
-
\`\`\`
|
|
1836
|
-
Task('video-continuity-editor') with prompt:
|
|
1837
|
-
"Check cross-scene consistency.
|
|
1838
|
-
Plan: video-plan.md
|
|
1839
|
-
Manifest: timing-manifest.md
|
|
1840
|
-
Branding: branding.json"
|
|
1841
|
-
\`\`\`
|
|
1842
|
-
|
|
1843
|
-
This checks transition flow (Scene N out = Scene N+1 in), timing alignment (Main.tsx matches manifest), color grade progression, import consistency, and brand coherence.
|
|
1844
|
-
|
|
1845
|
-
If score is below 85%, fix the flagged issues before ${o?"previewing":"rendering"}.
|
|
1846
|
-
|
|
1847
|
-
### 9e. Verify and ${o?"Preview":"Render"}
|
|
1848
|
-
|
|
1849
|
-
After all agents finish:
|
|
1850
|
-
|
|
1851
|
-
1. **Verify all scene files exist:** \`ls src/scenes/Scene*.tsx\` \u2014 count must match number of scenes in plan
|
|
1852
|
-
2. **If any scene is missing**, re-spawn its builder agent
|
|
1853
|
-
|
|
1854
|
-
**Checklist before ${o?"preview":"render"}:**
|
|
1855
|
-
- [ ] All scene files exist and match the plan's scene count
|
|
1856
|
-
- [ ] Main.tsx/Root.tsx imports all scenes correctly
|
|
1857
|
-
- [ ] Fonts loaded via \`loadFont()\` or \`@font-face\` \u2014 no raw fontFamily
|
|
1858
|
-
- [ ] Logos/icons are real images or Lucide components \u2014 no text/emoji substitutes
|
|
1859
|
-
- [ ] Assets use \`staticFile()\` or \`<Img/>\` properly
|
|
1860
|
-
- [ ] Transitions use template components as specified in plan \u2014 no generic substitutions
|
|
1861
|
-
- [ ] SFX files from Step 8 are used at correct timestamps
|
|
1862
|
-
- [ ] Layout fills frame \u2014 not clustered in corners
|
|
1863
|
-
${o?"\n**Studio Preview auto-reloads** when scene files are saved. After verifying the checklist, tell the user to check the Studio Preview in the app.\n\n**Do NOT run `npx remotion render` unless the user explicitly asks to render the final video.**\nIf the user requests a render, use:\n\n```bash\nnpx remotion render Main out/video.mp4\n```\n\nFor WebGL/Three.js scenes, use `--gl=angle`:\n```bash\nnpx remotion render Main out/video.mp4 --gl=angle\n```\n":`
|
|
1864
|
-
**Then render:**
|
|
1865
|
-
|
|
1866
|
-
\`\`\`bash
|
|
1867
|
-
npx remotion render Main out/video.mp4
|
|
1868
|
-
\`\`\`
|
|
1869
|
-
|
|
1870
|
-
For WebGL/Three.js scenes, use \`--gl=angle\`:
|
|
1871
|
-
\`\`\`bash
|
|
1872
|
-
npx remotion render Main out/video.mp4 --gl=angle
|
|
1873
|
-
\`\`\`
|
|
1874
|
-
|
|
1875
|
-
After render:
|
|
1876
|
-
- [ ] Thumbnail injected via \`${n} video thumbnail inject\`
|
|
1877
|
-
|
|
1878
|
-
## Step 10: Inject Thumbnail
|
|
1879
|
-
|
|
1880
|
-
\`\`\`bash
|
|
1881
|
-
${n} video thumbnail inject -i out/video.mp4 -t ./public/thumbnail.jpg
|
|
1882
|
-
\`\`\`
|
|
1883
|
-
|
|
1884
|
-
Injects thumbnail as first frame (QuickLook preview) + attached_pic (file icons).
|
|
1885
|
-
`}
|
|
1886
|
-
---
|
|
1887
|
-
|
|
1888
|
-
## Troubleshooting
|
|
1889
|
-
|
|
1890
|
-
### Authentication failed / Unauthorized
|
|
1891
|
-
|
|
1892
|
-
**CRITICAL:** If you see "Unauthorized" or authentication errors:
|
|
1893
|
-
- **STOP the entire workflow immediately**
|
|
1894
|
-
- **Do NOT proceed to build scenes or implement code**
|
|
1895
|
-
- Tell user: "Authentication is required. Please run: \`${n} login\`"
|
|
1896
|
-
- Wait for user to authenticate before restarting from Step 1
|
|
1897
|
-
|
|
1898
|
-
**Why:** Audio assets cannot be generated without authentication. Building Remotion scenes without audio is wasted work that cannot be completed later.
|
|
1899
|
-
|
|
1900
|
-
### Audio generation fails (TTS, music, SFX)
|
|
1901
|
-
|
|
1902
|
-
If TTS fails, use its request ID to recover rather than paying for a duplicate. Generate music/SFX only after final timing exists, and retry the same unchanged request instead of inventing a new prompt.
|
|
1903
|
-
${o?"":"\n### Render fails with WebGL components\nUse `--gl=angle` flag: `npx remotion render Main out/video.mp4 --gl=angle`\n"}
|
|
1904
|
-
${o?"":`
|
|
1905
|
-
### Video too long to render
|
|
1906
|
-
Add \`--concurrency=2\` to reduce memory usage, or render scenes separately.
|
|
1907
|
-
`}
|
|
1908
|
-
### Preview/build confusion
|
|
1909
|
-
\`npm run build\` may run Next.js instead of Remotion. Use Remotion-specific commands:
|
|
1910
|
-
- Preview: \`npx remotion studio\` or \`npm run dev\` (if configured)
|
|
1911
|
-
- Type check: \`npx tsc --noEmit\`${o?"":"\n- Render: `npx remotion render Main out/video.mp4`"}
|
|
1912
|
-
|
|
1913
|
-
## CLI Quick Reference
|
|
1914
|
-
|
|
1915
|
-
The \`${n}\` CLI has tools for working with individual assets directly. Run \`${n} --help\` for the full list.
|
|
1916
|
-
|
|
1917
|
-
- **Voiceover:** \`${n} tts generate --text "..." --voice Puck --output file.wav\` \u2014 generate speech from text. \`${n} tts voices\` lists available voices.
|
|
1918
|
-
- **Music:** \`${n} music generate --request music-request.json --output file.mp3\` \u2014 backend-planned music from final video facts.
|
|
1919
|
-
- **Sound effects:** \`${n} sfx generate --request sfx-request.json --output file.wav\` \u2014 backend-planned SFX from final video facts and one visual event.
|
|
1920
|
-
- **Stock images:** \`${n} image search "query"\` \u2014 search stock photo libraries.
|
|
1921
|
-
- **Stock video:** \`${n} video find "query"\` \u2014 search stock video clips.
|
|
1922
|
-
- **Scraping:** \`${n} scrape <url>\` \u2014 extract text content from a URL.
|
|
1923
|
-
- **Branding:** \`${n} branding find <name>\`, \`${n} branding extract <url>\`, \`${n} branding get <id>\` \u2014 search, extract, or export brand profiles.
|
|
1924
|
-
- **Audio mixing:** \`${n} mix\` \u2014 combine voice, music, and video tracks.
|
|
1925
|
-
|
|
1926
|
-
During the main workflow, branding uses its dedicated agent. Audio generation follows the final timeline and the backend owns music/SFX creative planning.
|
|
1927
|
-
`}import{existsSync as Xe,mkdirSync as Yt,rmSync as Pi,writeFileSync as Qt}from"fs";import{homedir as Zt}from"os";import{basename as Ni,join as ee,relative as Md,resolve as Ue}from"path";var be=[{name:"Claude Code",dir:".claude",provider:"claude"},{name:"GitHub Copilot",dir:".github",agentSuffix:".agent.md",provider:"copilot"},{name:"Cursor",dir:".cursor"},{name:"Codex",dir:".codex",agentSuffix:".toml",provider:"codex"},{name:"OpenCode",dir:".opencode"},{name:"Windsurf",dir:".windsurf"},{name:"Agent",dir:".agent"}];var Bd={sonnet:"claude-sonnet-4.6",haiku:"claude-haiku-4.5",opus:"claude-opus-4.6"},Gd=["permissionMode","skills","subagentType"];function Wd(e){let t=e.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!t)return`developer_instructions = """
|
|
1928
|
-
${e.replace(/"""/g,'\\"\\"\\"').trim()}
|
|
1929
|
-
"""
|
|
1930
|
-
`;let n=t[1],r=t[2].trim(),o=p=>n.match(new RegExp(`^${p}:\\s*(.+)$`,"m"))?.[1]?.trim().replace(/^["']|["']$/g,""),i=o("name")??"",a=o("description")??"",s=r.replace(/"""/g,'\\"\\"\\"');return`${[`name = ${JSON.stringify(i)}`,`description = ${JSON.stringify(a)}`,`developer_instructions = """
|
|
1931
|
-
${s}
|
|
1932
|
-
"""`].join(`
|
|
1933
|
-
`)}
|
|
1934
|
-
`}function _i(e,t){if(t!=="copilot")return e;let n=e.match(/^---\n([\s\S]*?)\n---\n/);if(!n)return $i(e);let r=n[1],o=e.slice((n.index??0)+n[0].length);r=r.replace(/^(model:\s*)(\S+)([ \t]*)$/m,(a,s,l,p)=>`${s}${Bd[l]??l}${p}`);for(let a of Gd)r=r.replace(new RegExp(`^${a}:[^
|
|
1155
|
+
The catalog is authoritative and always current; this file is a snapshot of it.
|
|
1156
|
+
`}function Ha(e,t){return[{content:nn(e,t),name:"video"},{content:tn(e,t),name:"deck-to-video"}]}import{existsSync as br,mkdirSync as Ya,rmSync as vp,writeFileSync as Qa}from"fs";import{homedir as ts}from"os";import{basename as xp,join as ve,relative as kp,resolve as ht}from"path";var Ae=[{name:"Claude Code",dir:".claude",provider:"claude"},{name:"GitHub Copilot",dir:".github",agentSuffix:".agent.md",provider:"copilot"},{name:"Cursor",dir:".cursor"},{name:"Codex",dir:".codex",agentSuffix:".toml",provider:"codex"},{name:"OpenCode",dir:".opencode"},{name:"Windsurf",dir:".windsurf"},{name:"Agent",dir:".agent"}];var Cp={sonnet:"claude-sonnet-4.6",haiku:"claude-haiku-4.5",opus:"claude-opus-4.6"},Ip=["permissionMode","skills","subagentType"];function Rp(e,t){if(t!=="copilot")return e;let n=e.match(/^---\n([\s\S]*?)\n---\n/);if(!n)return Za(e);let r=n[1],o=e.slice((n.index??0)+n[0].length);r=r.replace(/^(model:\s*)(\S+)([ \t]*)$/m,(a,s,l,g)=>`${s}${Cp[l]??l}${g}`);for(let a of Ip)r=r.replace(new RegExp(`^${a}:[^
|
|
1935
1157
|
]*
|
|
1936
|
-
?`,"m"),"");r=r.replace(/\n+$/,"");let i
|
|
1158
|
+
?`,"m"),"");r=r.replace(/\n+$/,"");let i=Za(o);return`---
|
|
1937
1159
|
${r}
|
|
1938
1160
|
---
|
|
1939
|
-
${i}`}function
|
|
1161
|
+
${i}`}function Za(e){return e.replace(/\bAskUserQuestion\b/g,"ask the user directly in your response").replace(/\*\*Construct the Task prompt:\*\*/g,"**Construct the Task prompt (must be fully self-contained \u2014 sub-agents cannot read this conversation's history):**").replace(/\*\*Wait for all agents to complete\*\*/g,"**Wait for all agents to complete** (monitor with `/tasks` in Copilot CLI)").replace("**Launch ALL scene agents in parallel**","**Launch ALL scene agents in parallel** \u2014 each agent must write to its own `Scene[NN][Name].tsx` file. Two agents writing to the same file will silently overwrite each other (no error). Verify unique filenames before spawning.")}function Sp(e,t){let n=ht(e),r=ht(e,t);if(kp(n,r).startsWith("..")||ht(r)!==r.replace(/\.\./g,""))throw new Error(`Invalid path: "${t}" would escape base directory`);return r}function es(e,t,n,r){let o=ve(e,"SKILL.md");if(Ya(e,{recursive:!0}),Qa(o,Rp(t,r),"utf-8"),n&&n.length>0){let i=ve(e,"references");Ya(i,{recursive:!0});for(let a of n){let s=ve(i,`${a.name}.md`);Qa(s,a.content,"utf-8")}}}function dn(e,t,n={},r){let o={installed:[],skipped:[],errors:[]},i=n.local?process.cwd():ts();if(n.dir){let a=xp(ht(n.dir)),s=Ae.find(l=>l.dir===a)?.provider;try{let l=ht(n.dir),g=Sp(l,ve("skills",e));es(g,t,r,s),o.installed.push(n.dir)}catch(l){o.errors.push(`${n.dir}: ${l instanceof Error?l.message:String(l)}`)}}else for(let a of Ae){let s=ve(i,a.dir),l=ve(s,"skills",e),g=ve(l,"SKILL.md");if(br(s)){if(br(g)&&!n.force){o.skipped.push(a.name);continue}try{es(l,t,r,a.provider),o.installed.push(a.name)}catch(m){o.errors.push(`${a.name}: ${m instanceof Error?m.message:String(m)}`)}}}return o}function cn(e,t={}){let n={removed:[],errors:[]},r=t.local?process.cwd():ts();for(let o of Ae){let i=ve(r,o.dir,"skills",e);if(br(i))try{vp(i,{recursive:!0}),n.removed.push(o.name)}catch(a){n.errors.push(`${o.name}: ${a instanceof Error?a.message:String(a)}`)}}return n}function ns(){return Ae.map(e=>e.name)}function Ge(e){let{name:t,cmd:n}=e;return`---
|
|
1940
1162
|
name: ${t}-tts
|
|
1941
1163
|
description: Text-to-speech generation. Convert text to natural speech with multiple providers (Gemini, ElevenLabs, OpenAI) and voices. Use for voiceovers, narration, accessibility.
|
|
1942
1164
|
---
|
|
@@ -2135,8 +1357,8 @@ ${n} tts generate \\
|
|
|
2135
1357
|
1. **Gemini style control** - Include delivery instructions in text: "Say excitedly:", "Speak slowly and clearly:"
|
|
2136
1358
|
2. **Voice matching** - Match voice style to content: Puck for upbeat, Charon for serious
|
|
2137
1359
|
3. **Speed adjustment** - Use 0.9-0.95 for narration, 1.0-1.1 for casual
|
|
2138
|
-
4. **
|
|
2139
|
-
`}function
|
|
1360
|
+
4. **Multiple voiceovers** - Run \`${n} tts generate\` once per line and poll each run with \`${n} tts status <run-id>\`
|
|
1361
|
+
`}function Be(e){let{name:t,cmd:n}=e;return`---
|
|
2140
1362
|
name: ${t}-music
|
|
2141
1363
|
description: Generate music that follows a video's narration and final timing. Gemini plans the provider prompt on the backend.
|
|
2142
1364
|
---
|
|
@@ -2162,7 +1384,7 @@ ${n} music status <request-id>
|
|
|
2162
1384
|
\`\`\`
|
|
2163
1385
|
|
|
2164
1386
|
The backend sends these facts to Gemini, receives the provider-ready musical direction, and makes the paid music-provider request. Retrying the same unchanged request is idempotent.
|
|
2165
|
-
`}function
|
|
1387
|
+
`}function We(e){let{name:t,cmd:n}=e;return`---
|
|
2166
1388
|
name: ${t}-sfx
|
|
2167
1389
|
description: Generate one sound effect aligned with a final visual event and narration. Gemini plans the provider prompt on the backend.
|
|
2168
1390
|
---
|
|
@@ -2200,7 +1422,7 @@ ${n} sfx status <request-id>
|
|
|
2200
1422
|
\`\`\`
|
|
2201
1423
|
|
|
2202
1424
|
The backend sends these facts to Gemini, receives one provider-ready SFX description, and makes the paid SFX-provider request. Retrying the same unchanged request is idempotent.
|
|
2203
|
-
`}function
|
|
1425
|
+
`}function Ke(e){let{name:t,cmd:n}=e;return`---
|
|
2204
1426
|
name: ${t}-mix
|
|
2205
1427
|
description: Audio mixing for video production. Combine video with voiceover and background music. Supports volume control, fade effects, and multiple audio tracks.
|
|
2206
1428
|
---
|
|
@@ -2350,7 +1572,7 @@ ${n} mix status <request-id>
|
|
|
2350
1572
|
2. **Music selection** - Choose music that doesn't compete with voice frequency.
|
|
2351
1573
|
3. **Consistent levels** - Keep voice at 100%, adjust music only.
|
|
2352
1574
|
4. **File sizes** - Use \`--no-wait\` for large files to avoid timeout.
|
|
2353
|
-
`}function
|
|
1575
|
+
`}function Je(e){let{name:t,cmd:n}=e;return`---
|
|
2354
1576
|
name: ${t}-image
|
|
2355
1577
|
description: Image search across the web and licensed stock, plus AI image generation. Find licensed photos to publish, web images for specific real-world subjects, or generate a custom image. For videos, presentations, marketing.
|
|
2356
1578
|
---
|
|
@@ -2547,7 +1769,7 @@ Found 10 images for "mountain landscape"
|
|
|
2547
1769
|
3. **Size matters** - Use \`--size large\` for hero images
|
|
2548
1770
|
4. **JSON for automation** - Parse results programmatically
|
|
2549
1771
|
5. **Generation prompts** - Be detailed, describe style, lighting, composition
|
|
2550
|
-
`}function
|
|
1772
|
+
`}function ze(e){let{name:t,cmd:n}=e;return`---
|
|
2551
1773
|
name: ${t}-video-search
|
|
2552
1774
|
description: Video search across the web and licensed stock. Find downloadable stock clips for b-roll and backgrounds, or web videos (YouTube, Vimeo) to embed in a slide or microsite.
|
|
2553
1775
|
---
|
|
@@ -2721,10 +1943,9 @@ Found 5 videos for "ocean waves"
|
|
|
2721
1943
|
# 1. Search for b-roll
|
|
2722
1944
|
${n} video find "tech startup office" -f json > broll.json
|
|
2723
1945
|
|
|
2724
|
-
# 2.
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
# 3. Or download manually and use in Remotion
|
|
1946
|
+
# 2. Reference the chosen clips from a storyboard, then render
|
|
1947
|
+
${n} video storyboard create --file ./storyboard.md --duration 10 \\
|
|
1948
|
+
--aspect 16:9 --idempotency-key sb-1
|
|
2728
1949
|
\`\`\`
|
|
2729
1950
|
|
|
2730
1951
|
### Social Media Content
|
|
@@ -2746,7 +1967,7 @@ ${n} video find "city timelapse" --orientation landscape -n 10
|
|
|
2746
1967
|
3. **Preview first** - Use preview URLs to check quality
|
|
2747
1968
|
4. **License** - Verify license for commercial use
|
|
2748
1969
|
5. **JSON export** - Save searches for batch processing
|
|
2749
|
-
`}function
|
|
1970
|
+
`}function Xe(e){let{name:t,cmd:n}=e;return`---
|
|
2750
1971
|
name: ${t}-scrape
|
|
2751
1972
|
description: Scrape any URL the user provides. Use when user says "check this site", "look at this URL", "use this website", "here's the link", or shares any URL for context. Supports websites, YouTube, Twitter/X.
|
|
2752
1973
|
---
|
|
@@ -2936,7 +2157,7 @@ ${n} scrape https://blog.example.com -f quiet | ${n} create "Blog Summary"
|
|
|
2936
2157
|
3. **Use JSON** - For programmatic processing of results
|
|
2937
2158
|
4. **Combine sources** - Scrape multiple URLs for comprehensive context
|
|
2938
2159
|
5. **Token count** - Check token usage for large documents
|
|
2939
|
-
`}function
|
|
2160
|
+
`}function He(e){let{name:t,cmd:n}=e;return`---
|
|
2940
2161
|
name: ${t}-branding
|
|
2941
2162
|
description: Brand profile management. Extract branding from websites, manage saved brands, apply consistent styling to presentations.
|
|
2942
2163
|
---
|
|
@@ -3046,7 +2267,7 @@ ${n} branding set-default abc123
|
|
|
3046
2267
|
2. **If extraction fails** \u2014 Proceed without branding. Do not manually create brand assets.
|
|
3047
2268
|
3. **Set default** \u2014 If you mostly work with one brand, set it as default.
|
|
3048
2269
|
4. **Override** \u2014 Use \`--brand\` flag to override default for specific presentations.
|
|
3049
|
-
`}function
|
|
2270
|
+
`}function Ye(e){let{name:t,cmd:n}=e,r=t.toUpperCase().replace(/[^A-Z0-9]/g,"_");return`---
|
|
3050
2271
|
name: ${t}-config
|
|
3051
2272
|
description: CLI configuration management. Set API keys, team IDs, custom API URLs. Verify authentication status.
|
|
3052
2273
|
---
|
|
@@ -3207,261 +2428,87 @@ ${n} config show --verify # Test API key
|
|
|
3207
2428
|
\`\`\`bash
|
|
3208
2429
|
${n} config set team-id <correct-team-id>
|
|
3209
2430
|
\`\`\`
|
|
3210
|
-
`}var
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
${
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
#
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
description: How to write video brief for server-side plan generation.
|
|
3284
|
-
---
|
|
3285
|
-
|
|
3286
|
-
# Video Brief Guide
|
|
3287
|
-
|
|
3288
|
-
Write a free-form brief that gives the plan generator everything needed to make smart creative decisions. Be descriptive, not prescriptive \u2014 describe the context richly, let the plan generator draw conclusions.
|
|
3289
|
-
|
|
3290
|
-
## Structure
|
|
3291
|
-
|
|
3292
|
-
### 1. Subject
|
|
3293
|
-
**What is this video about?** Could be a product, service, person, brand, event \u2014 anything. Go deep:
|
|
3294
|
-
- What is it? What does it do / offer?
|
|
3295
|
-
- Who is it for? What problem does it solve?
|
|
3296
|
-
- How does it work? What's the experience?
|
|
3297
|
-
- What makes it special? What's the "aha moment"?
|
|
3298
|
-
- How is it different from alternatives?
|
|
3299
|
-
|
|
3300
|
-
The more the plan generator understands the subject, the better the video.
|
|
3301
|
-
|
|
3302
|
-
### 2. Brand
|
|
3303
|
-
**Visual identity.** Three cases \u2014 follow exactly:
|
|
3304
|
-
|
|
3305
|
-
- **\`branding.json\` exists:** Write one line only: \`Brand: [company name] \u2014 see branding.json\`. Do NOT re-specify colors, fonts, aesthetics, or visual direction \u2014 the plan generator receives \`branding.json\` directly via \`--branding\` and makes its own decisions.
|
|
3306
|
-
- **No brand, user has NOT specified a style:** Write one line only: \`No brand provided.\` Stop there. Do NOT invent colors, typography, visual direction, mood, or aesthetic \u2014 the plan generator decides everything.
|
|
3307
|
-
- **No brand, but user explicitly described a style in their request:** Write what the user said, as they said it. No additions.
|
|
3308
|
-
|
|
3309
|
-
### 3. Key Messages
|
|
3310
|
-
**Priority-ordered list of what must be communicated.** For each message:
|
|
3311
|
-
- The core point
|
|
3312
|
-
- Why it matters to the audience
|
|
3313
|
-
- How it differentiates from alternatives
|
|
3314
|
-
|
|
3315
|
-
These become the backbone of the video. 3-5 messages, best ones first.
|
|
3316
|
-
|
|
3317
|
-
### 4. Available Assets
|
|
3318
|
-
**List assets with brief descriptions** so plan generator knows what it's working with:
|
|
3319
|
-
- logo.png \u2014 company wordmark, dark version
|
|
3320
|
-
- ui-dashboard.png \u2014 main dashboard showing all metrics
|
|
3321
|
-
- hero-photo.jpg \u2014 founder portrait for intro
|
|
3322
|
-
|
|
3323
|
-
The plan generator will reference these by name (e.g., "animate logo.png to center").
|
|
3324
|
-
|
|
3325
|
-
### 5. UI Components (only when source is a codebase)
|
|
3326
|
-
**List key components that can be rebuilt for video.** Path + visual description:
|
|
3327
|
-
- src/components/Chat \u2014 chat interface: message bubbles, typing indicator, input field
|
|
3328
|
-
- src/components/Dashboard \u2014 metrics grid: balance card, usage chart, activity list
|
|
3329
|
-
- src/components/PricingCard \u2014 pricing tier: title, price, features list, CTA button
|
|
3330
|
-
|
|
3331
|
-
These will be rebuilt as lightweight animated versions for Remotion. Focus on visual structure, not implementation details.
|
|
3332
|
-
|
|
3333
|
-
### 6. Parameters
|
|
3334
|
-
- Desired duration (e.g., ~60 seconds)
|
|
3335
|
-
- Platform (YouTube, TikTok, LinkedIn, website)
|
|
3336
|
-
- Voiceover: yes/no
|
|
3337
|
-
- Music: yes/no
|
|
3338
|
-
|
|
3339
|
-
---
|
|
3340
|
-
|
|
3341
|
-
**IMPORTANT:** The brief contains ONLY the 6 sections above. Do NOT add "Suggested Scenes", "Scene Breakdown", or any other sections \u2014 scene planning happens server-side via \`video generate plan\`.
|
|
3342
|
-
`};var nr={name:"video-ref-source-scraping",description:"Extract content from various sources (YouTube, websites, Twitter).",render:e=>`---
|
|
3343
|
-
name: ${e.name}-video-ref-source-scraping
|
|
3344
|
-
description: Extract content from various sources (YouTube, websites, Twitter).
|
|
3345
|
-
---
|
|
3346
|
-
|
|
3347
|
-
# Source Scraping
|
|
3348
|
-
|
|
3349
|
-
The \`${e.cmd} scrape\` command automatically detects source type and extracts content.
|
|
3350
|
-
|
|
3351
|
-
## YouTube Videos
|
|
3352
|
-
|
|
3353
|
-
\`\`\`bash
|
|
3354
|
-
${e.cmd} scrape <youtube-url>
|
|
3355
|
-
\`\`\`
|
|
3356
|
-
|
|
3357
|
-
Extracts: transcript, title, description, channel info
|
|
3358
|
-
|
|
3359
|
-
## Websites
|
|
3360
|
-
|
|
3361
|
-
\`\`\`bash
|
|
3362
|
-
${e.cmd} scrape <website-url>
|
|
3363
|
-
\`\`\`
|
|
3364
|
-
|
|
3365
|
-
Extracts: content, headings, metadata
|
|
3366
|
-
|
|
3367
|
-
## Twitter/X Posts
|
|
3368
|
-
|
|
3369
|
-
\`\`\`bash
|
|
3370
|
-
${e.cmd} scrape <twitter-url>
|
|
3371
|
-
\`\`\`
|
|
3372
|
-
|
|
3373
|
-
Extracts: tweet text, author info, media URLs
|
|
3374
|
-
|
|
3375
|
-
---
|
|
3376
|
-
|
|
3377
|
-
All scraped content is returned in markdown format.
|
|
3378
|
-
`};var St=["main","video","presentation","presentation-edit","tts","music","sfx","mix","image","video-search","scrape","branding","config"],nn=["standalone","studio"],Fi={cmd:b.commands[0],pkg:b.packageName,url:b.apiUrl,name:b.name},it=new Jd("skill").description(`Manage ${b.displayName} skills for AI coding assistants`).addHelpText("after",`
|
|
3379
|
-
${F.bold("Skill Types:")}
|
|
3380
|
-
${F.cyan("main")} Main CLI skill with all capabilities overview
|
|
3381
|
-
${F.cyan("video")} Detailed video creation workflow with Remotion/R3F patterns
|
|
3382
|
-
${F.cyan("presentation")} Detailed presentation creation workflow
|
|
3383
|
-
${F.cyan("presentation-edit")} In-app presentation editing (Studio)
|
|
3384
|
-
|
|
3385
|
-
${F.bold("Reference Skills:")}
|
|
3386
|
-
${F.cyan("tts")} Text-to-speech generation (voices, providers, options)
|
|
3387
|
-
${F.cyan("music")} AI music generation (prompts, styles, duration)
|
|
3388
|
-
${F.cyan("sfx")} Sound effects generation (categories, looping, formats)
|
|
3389
|
-
${F.cyan("mix")} Audio mixing (video + voice + music)
|
|
3390
|
-
${F.cyan("image")} Image search and AI generation
|
|
3391
|
-
${F.cyan("video-search")} Stock video search
|
|
3392
|
-
${F.cyan("scrape")} URL content extraction
|
|
3393
|
-
${F.cyan("branding")} Brand profile management
|
|
3394
|
-
${F.cyan("config")} CLI configuration management
|
|
3395
|
-
|
|
3396
|
-
${F.bold("Examples:")}
|
|
3397
|
-
${F.gray("# Install main CLI skill (comprehensive overview)")}
|
|
3398
|
-
$ ${b.commands[0]} skill install main
|
|
3399
|
-
|
|
3400
|
-
${F.gray("# Install video skill")}
|
|
3401
|
-
$ ${b.commands[0]} skill install video
|
|
3402
|
-
|
|
3403
|
-
${F.gray("# Install presentation skill")}
|
|
3404
|
-
$ ${b.commands[0]} skill install presentation
|
|
3405
|
-
|
|
3406
|
-
${F.gray("# Install all skills")}
|
|
3407
|
-
$ ${b.commands[0]} skill install
|
|
3408
|
-
|
|
3409
|
-
${F.gray("# Install to specific directory")}
|
|
3410
|
-
$ ${b.commands[0]} skill install main --dir ~/.claude
|
|
3411
|
-
|
|
3412
|
-
${F.gray("# Show skill content")}
|
|
3413
|
-
$ ${b.commands[0]} skill show main
|
|
3414
|
-
`);it.command("install").description(`Install ${b.displayName} skills for AI coding assistants`).argument("[type]","Skill type: main, video, presentation, or omit for all").option("-d, --dir <path>","Install to specific directory").option("-g, --global","Install globally (to home directory)",!0).option("-l, --local","Install locally (to current directory)").option("-f, --force","Overwrite existing skill files").option("-m, --mode <mode>","Skill mode: standalone (render to file) or studio (Studio Preview)","standalone").action(async(e,t)=>{t.mode&&!nn.includes(t.mode)&&(c(`Invalid mode: ${t.mode}. Must be one of: ${nn.join(", ")}`),process.exit(1));let n={...Fi,mode:t.mode},r=[];e&&!St.includes(e)&&(c(`Invalid skill type: ${e}. Must be one of: ${St.join(", ")}`),process.exit(1));let o=[{name:"tts",content:He(n)},{name:"music",content:Ye(n)},{name:"sfx",content:Qe(n)},{name:"mix",content:Ze(n)},{name:"image",content:et(n)},{name:"video-search",content:tt(n)},{name:"scrape",content:nt(n)},{name:"branding",content:rt(n)},{name:"config",content:ot(n)}];(!e||e==="main")&&r.push({name:b.name,content:Jt(n),references:o});let i=[{name:"brief-guide",content:tr.render(n)},{name:"website-assets",content:er.render(n)},{name:"source-scraping",content:nr.render(n)}];(!e||e==="video")&&(r.push({name:`${b.name}-video`,content:Ht(n),references:i}),r.push({name:"remotion-scene-craft",content:Ei(n)})),(!e||e==="presentation")&&r.push({name:`${b.name}-presentation`,content:Xt(n)}),(!e||e==="presentation-edit")&&r.push({name:`${b.name}-presentation-edit`,content:zt(n)}),e==="tts"&&r.push({name:`${b.name}-tts`,content:He(n)}),e==="music"&&r.push({name:`${b.name}-music`,content:Ye(n)}),e==="sfx"&&r.push({name:`${b.name}-sfx`,content:Qe(n)}),e==="mix"&&r.push({name:`${b.name}-mix`,content:Ze(n)}),e==="image"&&r.push({name:`${b.name}-image`,content:et(n)}),e==="video-search"&&r.push({name:`${b.name}-video-search`,content:tt(n)}),e==="scrape"&&r.push({name:`${b.name}-scrape`,content:nt(n)}),e==="branding"&&r.push({name:`${b.name}-branding`,content:rt(n)}),e==="config"&&r.push({name:`${b.name}-config`,content:ot(n)}),console.log();for(let a of r){m(`Installing ${a.name}...`);let s=en(a.name,a.content,{dir:t.dir,local:t.local,force:t.force},a.references);if(s.installed.length>0&&(I(`${a.name} installed successfully`),T(" Installed to",s.installed.join(", "))),s.skipped.length>0&&(m(` Skipped (already exists): ${s.skipped.join(", ")}`),console.log(F.gray(" Use --force to overwrite"))),s.errors.length>0)for(let l of s.errors)c(` ${l}`);s.installed.length===0&&s.skipped.length===0&&s.errors.length===0&&(m(" No supported AI coding assistants detected"),console.log(F.gray(" Supported editors: "+Li().join(", "))),console.log(F.gray(" Use --dir <path> to install to a specific directory"))),console.log()}if(!e||e==="video"){for(let a of[{name:"video-branding-director",generate:()=>Hn(n)},{name:"video-scene-builder",generate:Qn},{name:"video-scene-reviewer",generate:Zn},{name:"video-continuity-editor",generate:Yn}]){m(`Installing ${a.name} agent...`);let s=Di(a.name,a.generate(),{force:t.force,dir:t.dir});if(s.installed.length>0){I(`${a.name} agent installed`);let l={"Claude Code":"~/.claude/agents/","GitHub Copilot":"~/.github/agents/"};T(" Installed to",s.installed.map(p=>t.dir?p:`${p} (${l[p]??"~/.<editor>/agents/"})`).join(", "))}s.skipped.length>0&&m(` Agent skipped (already exists): ${s.skipped.join(", ")}`)}console.log()}});it.command("show").description("Display skill content").argument("[type]","Skill type (default: main)").option("-m, --mode <mode>","Skill mode: standalone or studio","standalone").action((e="main",t)=>{t.mode&&!nn.includes(t.mode)&&(c(`Invalid mode: ${t.mode}. Must be one of: ${nn.join(", ")}`),process.exit(1));let n={...Fi,mode:t.mode},o={main:()=>Jt(n),video:()=>Ht(n),presentation:()=>Xt(n),"presentation-edit":()=>zt(n),tts:()=>He(n),music:()=>Ye(n),sfx:()=>Qe(n),mix:()=>Ze(n),image:()=>et(n),"video-search":()=>tt(n),scrape:()=>nt(n),branding:()=>rt(n),config:()=>ot(n),"video-branding-director":()=>Hn(n),"video-scene-builder":()=>Qn(),"video-scene-reviewer":()=>Zn(),"video-continuity-editor":()=>Yn()}[e];if(o)console.log(o());else{let i=[...St,"video-branding-director","video-scene-builder","video-scene-reviewer","video-continuity-editor"];c(`Invalid skill type: ${e}. Must be one of: ${i.join(", ")}`),process.exit(1)}});it.command("uninstall").description(`Remove ${b.displayName} skills from AI coding assistants`).argument("[type]","Skill type: main, video, presentation, or omit for all").option("-g, --global","Uninstall globally (from home directory)",!0).option("-l, --local","Uninstall locally (from current directory)").action(async(e,t)=>{let n=[];e&&!St.includes(e)&&(c(`Invalid skill type: ${e}. Must be one of: ${St.join(", ")}`),process.exit(1)),(!e||e==="main")&&n.push(b.name),(!e||e==="video")&&(n.push(`${b.name}-video`),n.push("remotion-scene-craft")),(!e||e==="presentation")&&n.push(`${b.name}-presentation`),(!e||e==="presentation-edit")&&n.push(`${b.name}-presentation-edit`),(!e||e==="tts")&&n.push(`${b.name}-tts`),(!e||e==="music")&&n.push(`${b.name}-music`),(!e||e==="sfx")&&n.push(`${b.name}-sfx`),(!e||e==="mix")&&n.push(`${b.name}-mix`),(!e||e==="image")&&n.push(`${b.name}-image`),(!e||e==="video-search")&&n.push(`${b.name}-video-search`),(!e||e==="scrape")&&n.push(`${b.name}-scrape`),(!e||e==="branding")&&n.push(`${b.name}-branding`),(!e||e==="config")&&n.push(`${b.name}-config`),console.log();for(let r of n){let o=tn(r,{local:t.local});if(o.removed.length>0?(I(`${r} uninstalled`),T(" Removed from",o.removed.join(", "))):m(` ${r} not found`),o.errors.length>0)for(let i of o.errors)q(` Failed to remove: ${i}`)}if(!e||e==="video")for(let r of["video-branding-director","video-scene-builder","video-scene-reviewer","video-continuity-editor"])Ui(r).removed.length>0&&I(`${r} agent uninstalled`);console.log()});import{Command as zd}from"commander";import It from"chalk";var qi=new zd("templates").description("List slide design templates (use with `create --template <id>`)").argument("[search]","Filter templates by name, description, or category").option("-f, --format <format>","Output format (table, json)","table").option("--limit <n>","Maximum templates to list","25").action(async(e,t)=>{await N();try{let n=Number.parseInt(t.limit??"25",10),{templates:r}=await co(e,Number.isFinite(n)?n:25);if(r.length===0){t.format==="json"?console.log(JSON.stringify([])):m("No templates found");return}if(t.format==="json"){console.log(JSON.stringify(r,null,2));return}for(let o of r){let i=[o.isDefault?It.green("default"):null,o.category??null,o.recommendedModes?.length?`modes: ${o.recommendedModes.join("/")}`:null].filter(Boolean).join(" \xB7 ");console.log(`${It.bold(o.name)} ${It.gray(o.id)}${i?`
|
|
3415
|
-
${It.gray(i)}`:""}`)}console.log(),console.log(It.gray(`Use one with: ${b.commands[0]} create "<topic>" --template <id>`))}catch(n){c(n instanceof Error?n.message:String(n)),process.exit(n.exitCode??1)}});import{Command as Xd}from"commander";import rr from"chalk";var Vi=new Xd("whoami").description("Show current user and team information").option("-f, --format <format>","Output format (human, json)","human").action(async e=>{await N();try{let t=await Se();if(qn(),e.format==="json"){console.log(JSON.stringify(t,null,2));return}ye("User"),console.log(),T("Email",t.user.email),t.user.username&&T("Username",t.user.username),(t.user.firstName||t.user.lastName)&&T("Name",[t.user.firstName,t.user.lastName].filter(Boolean).join(" "));let n=t.authType==="apiKey"?"API Key":t.authType==="oauth"?"OAuth":"Session";if(T("Auth Type",n),t.currentTeam&&(console.log(),ye("Current Team"),console.log(),T("ID",t.currentTeam.id),T("Name",t.currentTeam.name),T("Plan",t.currentTeam.planName),T("Role",t.currentTeam.isOwner?"Owner":"Member")),t.teams.length>1){console.log(),ye("All Teams"),console.log();for(let r of t.teams){let o=r.isCurrent?rr.green(" (current)"):"";console.log(` ${rr.bold(r.name)}${o}`),console.log(rr.gray(` ID: ${r.id} | Plan: ${r.planName} | Role: ${r.role}`))}}console.log()}catch(t){c(t instanceof Error?t.message:String(t)),process.exit(t.exitCode??1)}});function ji(e){return e[0]==="--"?e.slice(1):e}import{Command as or}from"commander";function Hd(e,t){return[...t,e]}var Yd=new or("create").description("Start durable ad-set generation").argument("<title>","Ad-set title").requiredOption("--brief <brief>","Campaign brief").option("--hook <text>","Opening hook (repeat)",Hd,[]).option("--style <style>","ugc, clean, cinematic, or testimonial","ugc").option("--branding-id <id>","Branding ID").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to approve generation");try{g(await h("createAdSet",{approvalReceipt:"cli:--yes",brandingId:t.brandingId,brief:t.brief,hooks:t.hook.length?t.hook:[t.brief],style:t.style,teamId:t.teamId,title:e}))}catch(n){c(n instanceof Error?n.message:"Generation failed"),process.exit(d.GENERAL_ERROR)}}),Qd=new or("status").description("Read ad-set generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A("createAdSet",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),Mi=new or("ad-set").description("Ad set commands").addCommand(Yd).addCommand(Qd);import{randomUUID as Zd}from"crypto";import{Command as at}from"commander";function Bi(e){return new at(e).description(e==="create"?"Create and activate a scheduled agent":"Create a reviewable agent recommendation").argument("<name>","Agent name").requiredOption("--instructions <markdown>","Agent instructions").option("--description <text>","Agent description","").option("--schedule <frequency>","manual, daily, weekdays, weekly, or monthly","weekly").option("--time <hh:mm>","Local run time","09:00").option("--timezone <zone>","IANA timezone","UTC").option("--output <adapter>","Output adapter","digest").option("--team-id <id>","Workspace ID").option("--yes","Approve saving the agent").action(async(t,n)=>{if(!n.yes)return c("Pass --yes to approve this operation");try{g(await h(e==="create"?"createAgent":"recommendAgent",{approvalReceipt:"cli:--yes",description:n.description,instructionsMarkdown:n.instructions,name:t,output:n.output,schedule:n.schedule,scheduleTime:n.time,teamId:n.teamId,timezone:n.timezone}))}catch(r){c(r instanceof Error?r.message:"Agent operation failed"),process.exit(d.GENERAL_ERROR)}})}function ar(e,t){c(e instanceof Error?e.message:t),process.exit(d.GENERAL_ERROR)}function Gi(e){return`cli:agent-memory:${e}:${Zd()}`}function el(e,t){let n=e.indexOf("="),r=n===-1?e:e.slice(0,n),o=n===-1?"":e.slice(n+1);return[...t,{name:r.trim(),note:o.trim()}]}function ir(e,t){return[...t,e]}var tl=new at("show").description("Show what a scheduled agent remembers").argument("<agent-id>","Agent ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await h("getAgentMemory",{agentId:e,teamId:t.teamId}))}catch(n){ar(n,"Could not read the agent's memory")}}),nl=new at("set").description("Add to or remove from what an agent remembers. Owner or workspace admin only.").argument("<agent-id>","Agent ID").option("--learned <text>","Durable fact to add (repeatable)",ir,[]).option("--always <text>","Standing instruction to add (repeatable)",ir,[]).option("--never <text>","Thing it must not do, to add (repeatable)",ir,[]).option("--track <name=note>","Entity to track, as name=note (repeatable)",el,[]).option("--notes <text>","Replace the freeform notes").option("--remove <field>","Remove instead of add: learned, recurringInstructions, negativePreferences, entities, or notes").option("--value <text>","Item to remove; not needed when removing notes").option("--expected-version <n>","Refuse the write unless the stored memory is still at this version").option("--team-id <id>","Workspace ID").action(async(e,t)=>{let n=typeof t.remove=="string";try{g(await h("updateAgentMemory",{agentId:e,entities:t.track.length?t.track:void 0,expectedVersion:t.expectedVersion===void 0?void 0:Number.parseInt(t.expectedVersion,10),learned:t.learned.length?t.learned:void 0,negativePreferences:t.never.length?t.never:void 0,notes:t.notes,operation:n?"remove":"add",recurringInstructions:t.always.length?t.always:void 0,removeField:t.remove,removeValue:t.value,teamId:t.teamId},Gi(n?"remove":"add")))}catch(r){ar(r,"Could not update the agent's memory")}}),rl=new at("forget").description("Erase every stored version of an agent's memory. This cannot be undone.").argument("<agent-id>","Agent ID").option("--team-id <id>","Workspace ID").option("--yes","Confirm the erase").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to erase this agent's memory");try{g(await h("forgetAgentMemory",{agentId:e,teamId:t.teamId},Gi("forget")))}catch(n){ar(n,"Could not erase the agent's memory")}}),ol=new at("memory").description("Read, edit, and erase what a scheduled agent remembers").addCommand(tl).addCommand(nl).addCommand(rl),Wi=new at("agent").description("Scheduled agent commands").addCommand(Bi("create")).addCommand(Bi("recommend")).addCommand(ol);import{randomUUID as zi}from"crypto";import{existsSync as sl}from"fs";import{resolve as cl}from"path";import{Command as Ie}from"commander";import dl from"ora";import{mkdir as il,writeFile as Ki}from"fs/promises";import{dirname as al}from"path";async function Q(e,t){if(await il(al(t),{recursive:!0}),e.startsWith("data:")){let o=e.match(/^data:[^;]+;base64,(.+)$/);if(!o)throw new Error("Invalid data URL format");let i=Buffer.from(o[1],"base64");if(i.length===0)throw new Error("Downloaded media was empty");await Ki(t,i);return}let n=await fetch(e);if(!n.ok)throw new Error(`Failed to download media: ${n.status}`);let r=Buffer.from(await n.arrayBuffer());if(r.length===0)throw new Error("Downloaded media was empty");await Ki(t,r)}var ll=15e3,ul=40,Ji=3;async function Xi(e,t){if(e?.length){if(e.length>Ji)throw new Error(`A presenter Look accepts at most ${Ji} reference images`);return Promise.all(e.map(async(n,r)=>{if(sl(n))return{url:(await he(cl(n),{idempotencyKey:`${t}:reference:${r+1}`})).url};let o;try{o=new URL(n)}catch(i){throw new Error(`Reference image ${r+1} must be an existing local file or an HTTP(S) URL.`,{cause:i})}if(o.protocol!=="http:"&&o.protocol!=="https:")throw new Error(`Reference image ${r+1} must use an HTTP(S) URL.`);return{url:o.toString()}}))}}function sr(e,t){if(t==="json"){g(e);return}if(t==="quiet"){console.log(e.videoUrl??e.requestId);return}m(`Request ID: ${e.requestId}`),m(`Status: ${e.status}`),e.durationSec!=null&&m(`Duration: ${e.durationSec}s`),e.videoUrl&&I(`Video URL: ${e.videoUrl}`),e.error&&c(`Error: ${e.error}`)}var ml=new Ie("options").description("List available presenter avatars and voices").option("--language <language>","Filter voices by language").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{try{let t=await Co(e.language);if(e.format==="json"){g(t);return}let n=t.personal??[];if(e.format==="quiet"){for(let r of n)console.log(r.id);for(let r of t.avatars)console.log(r.id);for(let r of t.voices)console.log(r.id);return}if(n.length>0){m("Your presenters:");for(let r of n)console.log(` ${r.id} ${r.name}`)}m("Avatars:");for(let r of t.avatars){let o=r.renderTiers?.length?` [${r.renderTiers.join(", ")}]`:"";console.log(` ${r.id} ${r.name}${o}`)}m("Voices:");for(let r of t.voices)console.log(` ${r.id} ${r.name}${r.language?` (${r.language})`:""}`)}catch(t){c(t instanceof Error?t.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),pl=new Ie("generate").description("Generate a reusable presenter clip").option("--avatar-id <id>","Avatar id from 'avatar options'; sandbox runs use the host presenter policy").option("--audio-url <url>","Public narration audio URL for avatar lip sync").option("--voice-id <id>","Voice id from 'avatar options'").option("--script <text>","Exact words for the presenter to speak").requiredOption("-o, --output <path>","Local output path").option("--aspect <aspect>","Aspect: 1:1, 9:16, 16:9","9:16").option("--duration <seconds>","Maximum requested duration (1-300)").option("--transparent","Render a transparent-background WebM presenter clip").option("--background-color <hex>","Solid presenter background color, for example #0F172A").option("--background-image-url <url>","Public image URL used as the presenter background").option("--fit <mode>","Background image fit: contain or cover").option("--output-format <container>","Container: mp4 (default) or webm \u2014 webm renders a transparent-background clip (single scene) for compositing").option("--render-tier <tier>","Render cost/quality: basic, standard, premium","basic").option("--title <title>","Clip title","Presenter clip").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.transparent?"webm":e.outputFormat;!!e.audioUrl==!!e.script&&(c("Provide exactly one avatar speech input: --audio-url or --script"),process.exit(d.INVALID_INPUT)),["1:1","9:16","16:9"].includes(e.aspect)||(c("Aspect must be one of 1:1, 9:16, or 16:9"),process.exit(d.INVALID_INPUT)),e.outputFormat!==void 0&&!["mp4","webm"].includes(e.outputFormat)&&(c("Output format must be mp4 or webm"),process.exit(d.INVALID_INPUT)),e.backgroundColor!==void 0&&!/^#[0-9a-fA-F]{6}$/.test(e.backgroundColor)&&(c("Background color must be a six-digit hex value"),process.exit(d.INVALID_INPUT)),e.backgroundColor&&e.backgroundImageUrl&&(c("Provide only one presenter background"),process.exit(d.INVALID_INPUT)),e.fit!==void 0&&!["contain","cover"].includes(e.fit)&&(c("Fit must be contain or cover"),process.exit(d.INVALID_INPUT)),e.fit&&!e.backgroundImageUrl&&(c("Fit requires --background-image-url"),process.exit(d.INVALID_INPUT)),e.transparent&&e.outputFormat==="mp4"&&(c("--transparent cannot be combined with --output-format mp4"),process.exit(d.INVALID_INPUT)),t==="webm"&&(e.backgroundColor||e.backgroundImageUrl)&&(c("Transparent WebM output cannot be combined with a background"),process.exit(d.INVALID_INPUT)),["basic","standard","premium"].includes(e.renderTier)||(c("Render tier must be basic, standard, or premium"),process.exit(d.INVALID_INPUT));let n=e.duration?Number.parseInt(e.duration,10):void 0;n!==void 0&&(!Number.isFinite(n)||n<1||n>300)&&(c("Duration must be between 1 and 300 seconds"),process.exit(d.INVALID_INPUT));let r=e.format==="human"?dl("Generating presenter clip...").start():null;try{let o=e.audioUrl?{audioUrl:e.audioUrl}:{script:e.script??"",...e.voiceId?{voiceId:e.voiceId}:{}},i=await ko({...o,aspect:e.aspect,...e.avatarId?{avatarId:e.avatarId}:{},...e.backgroundColor?{backgroundHex:e.backgroundColor}:{},...e.backgroundImageUrl?{backgroundImageUrl:e.backgroundImageUrl}:{},...n!==void 0?{durationSec:n}:{},...e.fit?{fit:e.fit}:{},...t?{outputFormat:t}:{},renderTier:e.renderTier,title:e.title});if(!e.wait){r?.stop(),sr(i,e.format);return}let a=await re(()=>Ln(i.requestId),ul,ll);if(a.status==="failed")throw new Error(a.error||"Presenter generation failed");if(!a.videoUrl)throw new Error("Presenter generation completed without a video URL");r&&(r.text="Downloading presenter clip..."),await Q(a.videoUrl,e.output),r?.stop(),sr(a,e.format),e.format==="human"&&I(`Saved to: ${e.output}`)}catch(o){r?.stop(),c(o instanceof Error?o.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),gl=new Ie("status").description("Check a presenter clip generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{sr(await Ln(e),t.format)}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),fl=new Ie("generate").description("Generate another look for an existing personal presenter").argument("<base-profile-id>","Base personal presenter id (pp_*)").requiredOption("--name <name>","Look name").requiredOption("--description <description>","Short look description").requiredOption("--prompt <prompt>","Exact appearance generation prompt").requiredOption("--look-id <id>","Stable template/look id").option("--reference-image <path-or-url...>","Up to three local images or URLs for location, outfit, or style guidance").option("--idempotency-key <key>","Stable retry key for this look request").action(async(e,t)=>{try{let n=t.idempotencyKey??zi(),r=await Xi(t.referenceImage,n),o=await h("generatePresenterLook",{baseProfileId:e,description:t.description,id:t.lookId,prompt:t.prompt,...r?{referenceImages:r}:{},title:t.name},n);g(o)}catch(n){c(n instanceof Error?n.message:"Look generation failed"),process.exit(d.GENERAL_ERROR)}}),hl=new Ie("status").description("Read presenter look generation status").argument("<run-id>","Durable run ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await A("generatePresenterLook",e);if(t.format==="json")return g(n);if(t.format==="quiet")return console.log(n.status);m(`Run ID: ${e}`),m(`Status: ${n.status}`),n.error?.message&&c(n.error.message)}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),yl=new Ie("revise").description("Create a new Look by editing an existing presenter Look").argument("<look-id>","Ready presenter Look id (pp_*)").requiredOption("--instruction <instruction>","What to add, remove, or change").option("--reference-image <path-or-url...>","Up to three local images or URLs for location, outfit, or style guidance").option("--idempotency-key <key>","Stable retry key for this revision").action(async(e,t)=>{try{let n=t.idempotencyKey??zi(),r=await Xi(t.referenceImage,n),o=await h("revisePresenterLook",{instruction:t.instruction,...r?{referenceImages:r}:{},sourceLookId:e},n);g(o)}catch(n){c(n instanceof Error?n.message:"Look revision failed"),process.exit(d.GENERAL_ERROR)}}),bl=new Ie("revision-status").description("Read presenter Look revision status").argument("<run-id>","Durable run ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await A("revisePresenterLook",e);if(t.format==="json")return g(n);if(t.format==="quiet")return console.log(n.status);m(`Run ID: ${e}`),m(`Status: ${n.status}`),n.error?.message&&c(n.error.message)}catch(n){c(n instanceof Error?n.message:"Revision status failed"),process.exit(d.GENERAL_ERROR)}}),wl=new Ie("look").description("Manage looks under an existing personal presenter").addCommand(fl).addCommand(yl).addCommand(bl).addCommand(hl),Hi=new Ie("avatar").description("Avatar/presenter media commands").addCommand(ml).addCommand(pl).addCommand(wl).addCommand(gl);import{writeFile as na}from"fs/promises";import K from"chalk";import{Command as ae}from"commander";import Ol from"ora";import{createHash as vl}from"crypto";import{existsSync as xl}from"fs";import{basename as Sl}from"path";import{Command as Yi}from"commander";import Il from"ora";var Qi=new Yi("logo").description("Brand logo files").addCommand(new Yi("set").description("Set a brand's logo, dark variant, or favicon").argument("[brand-id]","Brand ID (defaults to this workspace's default brand)").requiredOption("--file <path>","Local image file to upload and use").option("--variant <variant>","primary | dark | favicon. 'dark' is the reversed lock-up for dark backgrounds","primary").option("--team-id <id>","Workspace ID").action(async(e,t)=>{await N();let n=t.variant??"primary";["primary","dark","favicon"].includes(n)||(c(`--variant must be primary, dark, or favicon (got "${n}")`),process.exit(d.INVALID_INPUT)),xl(t.file)||(c(`File not found: ${t.file}`),process.exit(d.INVALID_INPUT));let r=Il({text:`Uploading ${Sl(t.file)}...`,stream:process.stdout}).start(),o;try{o=(await he(t.file)).url,r.succeed("Uploaded")}catch(i){r.fail("Upload failed"),c(i instanceof Error?i.message:String(i)),process.exit(d.GENERAL_ERROR)}try{g(await h("setBrandLogo",{brandId:e,fileUrl:o,teamId:t.teamId,variant:n},vl("sha256").update(`setBrandLogo:${e??"default"}:${n}:${o}`).digest("hex").slice(0,32)))}catch(i){c(i instanceof Error?i.message:"Write failed"),process.exit(d.GENERAL_ERROR)}}));import{readFile as Rl}from"fs/promises";import{randomUUID as Cl}from"crypto";import{Command as Zi}from"commander";var kl=["color.primary","color.background","color.text","color.secondary","color.accent","color.surface","color.link","typography.primary","typography.secondary","typography.h1","typography.h2","typography.h3","typography.h4","typography.h5","typography.h6","typography.paragraph","typography.button","typography.small","typography.label","typography.caption","asset.logo","asset.logo.dark","asset.favicon"];function Tl(e){let t=e.split(".").filter(Boolean);for(let n=t.length;n>=2;n--){let r=t.slice(0,n).join(".");if(kl.includes(r))return{fieldPath:t.slice(n),nodeKey:r}}return{fieldPath:t.slice(2),nodeKey:t.slice(0,2).join(".")}}function El(e){let t={};for(let n of e){let r=n.indexOf("=");if(r<=0)throw new Error(`"${n}" is not a token assignment. Use key=value, e.g. color.primary=#114b97`);let o=n.slice(0,r).trim(),i=n.slice(r+1);if(o.split(".").filter(Boolean).length<2)throw new Error(`"${o}" is not a brand token key. Expected something like color.primary or typography.h1.size`);let{fieldPath:s,nodeKey:l}=Tl(o),p=Al(i);if(s.length===0){t[l]=p;continue}let u=t[l],w=u&&typeof u=="object"&&!Array.isArray(u)?u:{},R=w;for(let y of s.slice(0,-1)){let S=R[y],v=S&&typeof S=="object"&&!Array.isArray(S)?S:{};R[y]=v,R=v}R[s[s.length-1]]=p,t[l]=w}return t}function Al(e){return e==="true"?!0:e==="false"?!1:/^-?\d+(?:\.\d+)?$/.test(e)?Number(e):e}function $l(e,t){return e===void 0?{assignments:[...t]}:e.includes("=")?{assignments:[e,...t]}:{assignments:[...t],brandId:e}}var ea=new Zi("profile").description("Read a brand's design tokens, the keys it is missing, or its design.md").argument("[brand-id]","Brand ID (defaults to this workspace's brand)").option("--format <format>","tokens (default) or md").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{let n=await h("getBrandProfile",{brandId:e,format:t.format,teamId:t.teamId});if(t.format==="md"&&typeof n.designMd=="string"){console.log(n.designMd);return}g(n)}catch(n){c(n instanceof Error?n.message:"Profile read failed"),process.exit(d.GENERAL_ERROR)}}),ta=new Zi("set").description("Write brand design tokens (key=value pairs or a design.md)").argument("[brand-id]","Brand ID (defaults to this workspace's brand)").argument("[tokens...]","Token assignments, e.g. color.primary=#114b97 typography.h1.size=32px").option("--design-md <path>","Apply a whole design.md file instead").option("--no-publish","Stage the change without publishing a new active brand version").option("--team-id <id>","Workspace ID").option("--yes","Confirm writing brand data").action(async(e,t,n)=>{n.yes||(c("Pass --yes to confirm writing brand tokens."),process.exit(d.INVALID_INPUT));let{assignments:r,brandId:o}=$l(e,t),i;try{i=r.length>0?El(r):void 0}catch(s){c(s instanceof Error?s.message:"Invalid token pair"),process.exit(d.INVALID_INPUT)}let a;if(n.designMd)try{a=await Rl(n.designMd,"utf8")}catch(s){c(`Could not read ${n.designMd}: ${s instanceof Error?s.message:String(s)}`),process.exit(d.INVALID_INPUT)}!i&&!a&&(c("Nothing to write. Pass token assignments (color.primary=#114b97) or --design-md <file>."),process.exit(d.INVALID_INPUT));try{g(await h("updateBrandTokens",{brandId:o,designMd:a,publish:n.publish===!1?!1:void 0,teamId:n.teamId,tokens:i},Cl()))}catch(s){c(s instanceof Error?s.message:"Write failed"),process.exit(d.GENERAL_ERROR)}});var ra=new ae("branding").description("Manage brand profiles").addCommand(ea).addCommand(ta).addCommand(new ae("list").description("List brand profiles").option("-f, --format <format>","Output format (table, json)","table").action(async e=>{await N();try{let t=await $n();if(t.brandings.length===0){e.format==="json"?console.log(JSON.stringify([])):(m("No brand profiles found"),console.log(),console.log(K.gray(`Create one with: ${b.commands[0]} branding extract <url>`)));return}e.format==="json"?console.log(JSON.stringify(t.brandings,null,2)):console.log(Vo(t.brandings))}catch(t){c(t instanceof Error?t.message:String(t)),process.exit(t.exitCode??1)}})).addCommand(new ae("get").description("Get brand profile details").argument("<id>","Brand profile ID").option("-f, --format <format>","Output format (summary, json)","summary").option("-o, --output <path>","Save JSON to file").action(async(e,t)=>{await N();try{let n=await lo(e);if(t.output){await na(t.output,JSON.stringify(n,null,2)),I(`Brand profile saved to ${t.output}`);return}if(t.format==="json")console.log(JSON.stringify(n,null,2));else{if(ye("Brand Profile"),console.log(),T("ID",n.id),T("Name",n.name),T("Source URL",n.sourceUrl??K.gray("None")),T("Default",n.isDefault?K.green("Yes"):"No"),n.createdAt&&T("Created",new Date(n.createdAt).toLocaleString()),console.log(),n.primaryColor||n.colors.length>0){if(console.log(K.bold(" Colors")),n.primaryColor){let o=K.bgHex(n.primaryColor)(" ");console.log(` Primary: ${o} ${n.primaryColor}`)}if(n.colors.length>0){console.log(" Palette:");for(let o of n.colors.slice(0,10)){let i=K.bgHex(o.hex)(" "),a=o.role?K.gray(` (${o.role})`):"";console.log(` ${i} ${o.hex}${a}`)}}console.log()}n.logoUrl&&(console.log(K.bold(" Logo")),console.log(` ${n.logoUrl}`),console.log());let r=n.typography;r&&Object.keys(r).length>0&&(console.log(K.bold(" Typography")),(r.primary||r.headings)&&console.log(` Headings: ${r.primary||r.headings||"-"}`),(r.secondary||r.body)&&console.log(` Body: ${r.secondary||r.body||"-"}`),console.log()),(n.confidence||n.extractionMethod)&&(console.log(K.bold(" Extraction")),n.confidence&&console.log(` Confidence: ${Math.round(n.confidence*100)}%`),n.extractionMethod&&console.log(` Method: ${n.extractionMethod}`),console.log()),console.log(K.gray(" Use --format json for full details")),console.log()}}catch(n){c(n instanceof Error?n.message:String(n)),process.exit(n.exitCode??1)}})).addCommand(new ae("find").description("Find existing brand profile by name or URL").argument("<query>","Brand name or website URL to search for").action(async e=>{await N();try{let t=await $n(),n=e.toLowerCase().replace(/^https?:\/\//,"").replace(/\/$/,""),r=t.brandings.filter(o=>{let i=(o.name||"").toLowerCase(),a=(o.sourceUrl||"").toLowerCase().replace(/^https?:\/\//,"").replace(/\/$/,"");return i.includes(n)||a.includes(n)||i.length>0&&n.includes(i)||a.length>0&&n.includes(a)});if(r.length===0){m(`No brand profiles matching "${e}"`),console.log(K.gray(` Extract one with: ${b.commands[0]} branding extract <url>`));return}console.log(`Found ${r.length} brand${r.length>1?"s":""}:
|
|
3416
|
-
`);for(let o of r){let i=o.primaryColor?`${K.bgHex(o.primaryColor)(" ")} ${o.primaryColor}`:"";console.log(` ${K.bold(o.name)} ${K.gray(o.id)}`),o.sourceUrl&&console.log(` ${K.gray(o.sourceUrl)}`),i&&console.log(` ${i}`),console.log()}console.log(K.gray(` Export with: ${b.commands[0]} branding get <id> -o branding.json`))}catch(t){c(t instanceof Error?t.message:String(t)),process.exit(t.exitCode??1)}})).addCommand(new ae("extract").description("Extract brand profile from a website").argument("<url>","Website URL to extract branding from").option("--team-id <id>","Team ID").option("--yes","Confirm starting a paid/external extraction").option("--no-save","Extract without saving").option("-o, --output <path>","Save JSON to file").action(async(e,t)=>{await N(),t.yes||(c("Pass --yes to confirm brand extraction."),process.exit(d.INVALID_INPUT));try{new URL(e.startsWith("http")?e:`https://${e}`)}catch{c("Invalid URL format"),process.exit(6)}let n=e.startsWith("http")?e:`https://${e}`,r=t.teamId??At(),o=Ol({text:`Extracting branding from ${n}...`,stream:process.stdout}).start();try{let i=await uo(n,r,"cli:--yes");o.succeed("Brand extraction started");let a=JSON.stringify(i,null,2);t.output?(await na(t.output,a),I(`Brand profile saved to ${t.output}`)):console.log(a),m(`Check progress with: ${b.commands[0]} branding status ${i.runId}`),console.log()}catch(i){o.fail("Extraction failed"),c(i instanceof Error?i.message:String(i)),process.exit(i.exitCode??1)}})).addCommand(new ae("status").description("Check a brand extraction run").argument("<run-id>","Run ID returned by branding extract").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A("extractBranding",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}})).addCommand(new ae("set-default").description("Set a brand profile as default").argument("<id>","Brand profile ID").action(async e=>{await N(),m(`To set brand ${e} as default, use the web dashboard.`),console.log(),console.log(K.gray("API support for setting default brand is coming soon."))})).addCommand(new ae("competitor").description("Brand competitor commands").addCommand(new ae("list").description("List competitors for a brand").option("--branding-id <id>","Brand ID").option("--team-id <id>","Workspace ID").action(async e=>{try{console.log(JSON.stringify(await h("getBrandCompetitors",{brandingId:e.brandingId,teamId:e.teamId}),null,2))}catch(t){c(t instanceof Error?t.message:"Lookup failed"),process.exit(1)}})).addCommand(new ae("add").description("Add or update a brand competitor").argument("<name>","Competitor name").option("--branding-id <id>","Brand ID").option("--team-id <id>","Workspace ID").option("--website <url>","Competitor website").option("--x-handle <handle>","Competitor X handle").option("--yes","Confirm writing brand data").action(async(e,t)=>{t.yes||(c("Pass --yes to confirm adding a competitor."),process.exit(d.INVALID_INPUT));try{g(await h("addCompetitor",{approvalReceipt:"cli:--yes",brandingId:t.brandingId,name:e,teamId:t.teamId,website:t.website,xHandle:t.xHandle}))}catch(n){c(n instanceof Error?n.message:"Write failed"),process.exit(d.GENERAL_ERROR)}}))).addCommand(Qi).addCommand(new ae("social").description("Brand social handle commands").addCommand(new ae("add").description("Add or update a brand social handle").argument("<platform>","Social platform").argument("<handle>","Account handle").option("--branding-id <id>","Brand ID").option("--team-id <id>","Workspace ID").option("--yes","Confirm writing brand data").action(async(e,t,n)=>{n.yes||(c("Pass --yes to confirm adding a social handle."),process.exit(d.INVALID_INPUT));try{g(await h("addBrandSocialHandle",{approvalReceipt:"cli:--yes",brandingId:n.brandingId,handle:t,platform:e,teamId:n.teamId}))}catch(r){c(r instanceof Error?r.message:"Write failed"),process.exit(d.GENERAL_ERROR)}})));import{Command as st}from"commander";function Pl(e){switch(e.background){case"color":{if(!e.backgroundColor)throw new Error("--background color needs --background-color <hex>, e.g. #1d4ed8");return{hex:e.backgroundColor,kind:"color"}}case"image":{if(!e.backgroundImage)throw new Error("--background image needs --background-image <url>");return{fit:e.fit,kind:"image",url:e.backgroundImage}}default:return{kind:e.background}}}function oa(e,t){return{background:Pl(t),name:e,presenterProfileId:t.presenter,...t.voiceId?{voiceId:t.voiceId}:{}}}function rn(e,t){if(t==="json"){g(e);return}if(t==="quiet"){console.log(e.id);return}I(`${e.name} (${e.id})`),m(` Presenter: ${e.presenterProfileId}`),m(` Backdrop: ${Nl(e.background)}`),e.voiceId&&m(` Voice: ${e.voiceId}`)}function Nl(e){switch(e.kind){case"color":return`color ${e.hex}`;case"image":return`image ${e.url} (${e.fit})`;default:return e.kind}}function ia(e){return e.requiredOption("--presenter <profileId>","Trained personal presenter id (pp_*), from `mf avatar options`").option("--background <kind>","Backdrop: original, transparent, color, or image","original").option("--background-color <hex>","Backdrop color, e.g. #1d4ed8").option("--background-image <url>","Backdrop image URL").option("--fit <fit>","How an image backdrop fills the frame","cover").option("--voice-id <voiceId>","Workspace voice to save with this preset").option("-f, --format <format>","Output format: human, json, quiet","human")}var _l=new st("list").description("List saved cast presets you can use").option("--cast-member-id <castMemberId>","Only this person's presets").option("--branding-id <brandingId>","Narrow to one brand").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{try{let t=await _o({...e.brandingId?{brandingId:e.brandingId}:{},...e.castMemberId?{castMemberId:e.castMemberId}:{}});if(e.format==="json"){g(t);return}if(e.format==="quiet"){for(let n of t.presets)console.log(n.id);return}if(t.presets.length===0){m("No presets saved yet.");return}for(let n of t.presets)rn(n,"human")}catch(t){c(t instanceof Error?t.message:String(t)),process.exit(d.API_ERROR)}}),Ll=ia(new st("create").description("Save one way a cast member appears").argument("<name>",'What to call this preset, e.g. "In the cafe"').requiredOption("--cast-member-id <castMemberId>","The person this preset is a way of showing")).action(async(e,t)=>{try{let n=await Lo({castMemberId:t.castMemberId,...oa(e,t)});rn(n.preset,t.format)}catch(n){c(n instanceof Error?n.message:String(n)),process.exit(d.API_ERROR)}}),Dl=ia(new st("update").description("Change a saved preset (sends the complete preset)").argument("<presetId>","The preset to change").requiredOption("--name <name>","What to call it")).action(async(e,t)=>{try{let n=await Do({presetId:e,...oa(t.name,t)});rn(n.preset,t.format)}catch(n){c(n instanceof Error?n.message:String(n)),process.exit(d.API_ERROR)}}),Ul=new st("delete").description("Retire a saved preset. Existing videos keep working.").argument("<presetId>","The preset to retire").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await Uo(e);if(t.format==="human"){I(`Retired "${n.preset.name}".`);return}rn(n.preset,t.format)}catch(n){c(n instanceof Error?n.message:String(n)),process.exit(d.API_ERROR)}}),Fl=new st("preset").description("Saved ways a cast member appears").addCommand(_l).addCommand(Ll).addCommand(Dl).addCommand(Ul),aa=new st("cast").description("The faces and voices your videos can use").addCommand(Fl);import{readFile as ql}from"fs/promises";import{Command as sa}from"commander";var Vl=new sa("extract").description("Extract normalized text from a URL or local file").argument("<source>","URL, encoded source, or local file path").option("--team-id <id>","Workspace ID").option("--mime-type <type>","Source MIME type").option("--max-length <count>","Maximum output characters","30000").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=e,r;e.includes("://")||(n=await ql(e,"utf8"),r=e);let o=await h("extractContent",{filename:r,maxLength:Number.parseInt(t.maxLength,10),mimeType:t.mimeType,source:n,teamId:t.teamId});t.format!=="quiet"&&g(o)}catch(n){c(n instanceof Error?n.message:"Extraction failed"),process.exit(d.GENERAL_ERROR)}}),ca=new sa("content").description("Content extraction commands").addCommand(Vl);import Y from"chalk";import{Command as jl}from"commander";import{existsSync as Ml,readFileSync as Bl}from"fs";import Gl,{resolve as da}from"path";var la=new jl("create").description("Create a new presentation").argument("<topic>","The topic or title for the presentation").option("-n, --slides <count>","Number of slides (1-20)").option("-m, --mode <mode>","Generation quality (best, balanced, fast, ultrafast, instant, image-min, image, image-max) \u2014 default: fast. image-min/image/image-max render each slide as a full-bleed AI image (text not editable); slower + pricier, require image-mode access, and cap at 25 slides. image-min is cheapest (brand colors only, no exact fonts).").option("--image-tier <tier>","Embedded-image quality for HTML modes. Each tier is a per-image credit ceiling as well as its price: quick (+20/slide), smart (default, +60/slide), balanced (+250/slide), best (+500/slide, slow). Ignored for image-*/instant modes.").option("-t, --tone <tone>","Tone (creative, professional, educational, formal, casual)").option("--amount <amount>","Content density (minimal, concise, detailed, extensive)").option("--audience <text>","Target audience description").option("-l, --language <lang>","Output language").option("--branding-id <id>","Branding ID (from `ccr branding list` or `ccr branding extract`)").option("--brand-domain <url>","Brand the deck by website/domain (e.g. https://www.pfizer.com). Errors if no matching saved brand exists.").option("--template <id>","Slide template ID to style the deck after (from `ccr templates`). A default template is applied automatically when omitted.").option("-c, --context <text>","Inline text context (research notes, key facts)").option("--context-file <path>","Path to a context file (text, markdown, JSON)").option("--stdin","Read context from stdin").option("-f, --file <paths...>","Files to upload (PDF, PPTX, DOCX, images)").option("-g, --goal <type>","Goal (inform, persuade, train, learn, entertain, report)").option("--styling <mode>","Styling mode (freeform, brand-only, brand-plus-style, style-only, no-styling)").option("--reference-url <url>","Image URL to use as visual style reference").option("--thinking-depth <depth>","AI thinking depth (quick, moderate, deep, profound)").option("--theme <preset>","[instant mode only] Color theme preset (blue, violet, rose, orange, green)").option("--primary-color <hex>","[instant mode only] Primary color (e.g., #0066CC)").option("--secondary-color <hex>","[instant mode only] Secondary color").option("--accent-color <hex>","[instant mode only] Accent color").option("--background-color <hex>","[instant mode only] Background color").option("--foreground-color <hex>","[instant mode only] Foreground/text color").option("--decorations <style>","[instant mode only] Background decoration (none, waves-bottom-left, waves-top-right, blob-corners, minimal)").option("--team-id <id>","Team ID (switch teams)").option("-o, --output <format>","Output format (human, json, quiet)").option("--no-stream","Wait for completion without streaming progress").option("--wait","Poll until generation completes").option("--debug","Enable debug logging").option("--open","Open in browser after creation").option("--id <uuid>","Use a specific UUID for the presentation").option("--studio","Studio mode: use current directory name as presentation ID (auto-links to Studio preview)").action(async(e,t)=>{await N();let n;t.slides!==void 0&&(n=parseInt(t.slides,10),(Number.isNaN(n)||n<1||n>20)&&(c("Slide count must be between 1 and 20"),process.exit(6)));let r=["best","balanced","fast","ultrafast","instant","image-min","image","image-max"];t.mode&&!r.includes(t.mode)&&(c(`Invalid mode: ${t.mode}. Valid: ${r.join(", ")}`),process.exit(6));let o=["smart","fast","balanced","best"];t.imageTier&&!o.includes(t.imageTier)&&(c(`Invalid image tier: ${t.imageTier}. Valid: ${o.join(", ")}`),process.exit(6));let i=["creative","professional","educational","formal","casual"];t.tone&&!i.includes(t.tone)&&(c(`Invalid tone: ${t.tone}. Valid: ${i.join(", ")}`),process.exit(6));let a=["minimal","concise","detailed","extensive"];t.amount&&!a.includes(t.amount)&&(c(`Invalid amount: ${t.amount}. Valid: ${a.join(", ")}`),process.exit(6));let s=["freeform","brand-only","brand-plus-style","style-only","no-styling"];t.styling&&!s.includes(t.styling)&&(c(`Invalid styling: ${t.styling}. Valid: ${s.join(", ")}`),process.exit(6));let l=["quick","moderate","deep","profound"];t.thinkingDepth&&!l.includes(t.thinkingDepth)&&(c(`Invalid thinking-depth: ${t.thinkingDepth}. Valid: ${l.join(", ")}`),process.exit(6));let p=["inform","persuade","train","learn","entertain","report"];t.goal&&!p.includes(t.goal)&&(c(`Invalid goal: ${t.goal}. Valid: ${p.join(", ")}`),process.exit(6));let u=["blue","violet","rose","orange","green"];t.theme&&!u.includes(t.theme)&&(c(`Invalid theme: ${t.theme}. Valid: ${u.join(", ")}`),process.exit(6));let w=["none","waves-bottom-left","waves-top-right","blob-corners","minimal"];t.decorations&&!w.includes(t.decorations)&&(c(`Invalid decorations: ${t.decorations}. Valid: ${w.join(", ")}`),process.exit(6));let R=t.theme||t.primaryColor||t.secondaryColor||t.accentColor||t.backgroundColor||t.foregroundColor||t.decorations;R&&t.mode&&t.mode!=="instant"&&(c("--theme, --primary-color, --decorations and other color options are only supported with --mode instant"),process.exit(6));let y=t.mode??(R?"instant":"fast"),S=/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;for(let[k,G]of[["primary-color",t.primaryColor],["secondary-color",t.secondaryColor],["accent-color",t.accentColor],["background-color",t.backgroundColor],["foreground-color",t.foregroundColor]])G&&!S.test(G)&&(c(`Invalid ${k}: ${G}. Must be hex (e.g., #0066CC)`),process.exit(6));try{t.output!=="json"&&t.output!=="quiet"&&process.stdout.write(Y.gray("Checking generation limits... "));let k=await go(y,n??10,t.teamId);if(t.output!=="json"&&t.output!=="quiet"){console.log(Y.green("\u2713"));let G=k.remaining[y],z=k.limits[y],pe=G===void 0||z===void 0?"":` | ${y}: ${G}/${z} remaining`;console.log(Y.gray(` Plan: ${k.planName}${pe}`))}}catch(k){t.output==="json"?console.log(JSON.stringify({success:!1,error:k instanceof Error?k.message:String(k)})):(console.log(Y.red("\u2717")),c(k instanceof Error?k.message:String(k))),process.exit(k.exitCode??1)}let v=[];if(t.file&&t.file.length>0){for(let k of t.file)Ml(da(k))||(c(`File not found: ${k}`),process.exit(6));t.output!=="json"&&t.output!=="quiet"&&console.log(Y.gray(`
|
|
3417
|
-
Uploading ${t.file.length} file(s)...`));try{v=await eo(t.file.map(k=>da(k)),(k,G,z)=>{t.output!=="json"&&t.output!=="quiet"&&z&&process.stdout.write(`\r ${Y.cyan("\u2B06")} Uploading: ${z} (${k+1}/${G})`)}),t.output!=="json"&&t.output!=="quiet"&&console.log(`\r ${Y.green("\u2713")} Uploaded ${v.length} file(s) `)}catch(k){c(`Failed to upload files: ${k instanceof Error?k.message:String(k)}`),process.exit(1)}}let x;(t.stdin||!process.stdin.isTTY)&&(x=await Wl(),t.stdin&&!x&&(c("No content received from stdin"),process.exit(6)));let C=t.context;if(t.contextFile)try{C=Bl(t.contextFile,"utf-8"),C.trim()||(c(`Context file is empty: ${t.contextFile}`),process.exit(6))}catch{c(`Failed to read context file: ${t.contextFile}`),process.exit(6)}!x&&!C&&v.length===0&&(c("Context is required to create a presentation."),console.log(),console.log(Y.gray(" -f, --file <paths...> Upload files (PDF, PPTX, images)")),console.log(Y.gray(" -c, --context <text> Inline text context")),console.log(Y.gray(" --context-file <path> Read from a file")),console.log(Y.gray(` cat file | ${b.commands[0]} Pipe content`)),process.exit(6));let O=[...v];x&&O.push({id:`stdin-${Date.now()}`,content:x,title:"Piped Content",mime:"text/plain",selected:!0}),C&&O.push({id:`context-${Date.now()}`,content:C,title:"Context",mime:"text/plain",selected:!0});let E,L=t.primaryColor||t.secondaryColor||t.accentColor||t.backgroundColor||t.foregroundColor;(t.theme||L||t.decorations)&&(E={},t.theme&&(E.preset=t.theme),L&&(E.custom={},t.primaryColor&&(E.custom.primary=t.primaryColor),t.secondaryColor&&(E.custom.secondary=t.secondaryColor),t.accentColor&&(E.custom.accent=t.accentColor),t.backgroundColor&&(E.custom.background=t.backgroundColor),t.foregroundColor&&(E.custom.foreground=t.foregroundColor)),t.decorations&&(E.decorations=t.decorations));try{let k=await to({topic:e,...n!==void 0&&{slideCount:n},mode:y,...t.imageTier&&{imageTier:t.imageTier},...t.tone&&{tone:t.tone},...t.amount&&{amount:t.amount},...t.audience&&{audience:t.audience},...t.language&&{language:t.language},...t.brandingId&&{brandingId:t.brandingId},...t.brandDomain&&{brandDomain:t.brandDomain},...t.template&&{templateId:t.template},...t.styling&&{stylingMode:t.styling},...t.referenceUrl&&{referenceUrl:t.referenceUrl},...t.thinkingDepth&&{thinkingDepth:t.thinkingDepth},...t.goal&&{goal:t.goal},...t.teamId&&{teamId:t.teamId},...E&&{theme:E},...t.studio?{documentId:Gl.basename(process.cwd())}:t.id?{documentId:t.id}:{},...O.length>0&&{contentSources:{uploadedFiles:O,contextFiles:[]}}});if(!t.wait){t.output==="json"?console.log(JSON.stringify({success:!0,run:k},null,2)):t.output!=="quiet"&&(I("Presentation generation queued"),T("Run ID",k.runId),T("Status",k.status),console.log(Y.gray(` Check later with presentation status ${k.runId}`)));return}let G=await A("createSlides",k.runId,t.teamId);for(;!G.done;)t.output!=="json"&&t.output!=="quiet"&&console.log(Y.gray(` ${G.message}`)),await new Promise(pe=>setTimeout(pe,(G.nextPollAfterSeconds??5)*1e3)),G=await A("createSlides",k.runId,t.teamId);if(G.error)throw new Error(G.error.message);let z=G.entity?await Te(G.entity.id):void 0;if(t.output==="json")console.log(JSON.stringify({success:!0,presentation:z,run:G,viewUrl:z?We(z.slug,t.language??"en"):void 0},null,2));else{console.log(),I("Presentation created successfully"),console.log(),T("Title",z?.title??e),T("Slides",String(z?.numberOfSlides??n??"\u2014")),console.log();let pe=z?We(z.slug,t.language??"en"):"N/A";pe!=="N/A"&&(console.log(Y.bold(" Open: ")+Y.cyan.underline(pe)),t.open&&await(await import("open")).default(pe)),console.log()}}catch(k){t.output==="json"?console.log(JSON.stringify({success:!1,error:k instanceof Error?k.message:String(k)})):c(k instanceof Error?k.message:String(k)),process.exit(k.exitCode??1)}});async function Wl(){return new Promise(e=>{let t="";if(process.stdin.setEncoding("utf8"),process.stdin.isTTY){e("");return}process.stdin.on("data",n=>{t+=n}),process.stdin.on("end",()=>{e(t.trim())}),setTimeout(()=>{e(t.trim())},100)})}import{randomUUID as Kl}from"crypto";import{readFile as Jl}from"fs/promises";import{Command as Ae}from"commander";var cr=["text/markdown","text/csv","text/plain"];async function ua(e){let t=e.content;if(t!==void 0&&t!=="-"&&!t.startsWith("@"))return t;if(t?.startsWith("@"))return Jl(t.slice(1),"utf8");if(t==="-"||!process.stdin.isTTY)return zl();c("No document body. Pass --content <text>, --content @<path>, or pipe it on stdin."),process.exit(d.GENERAL_ERROR)}async function zl(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8")}function on(e){return`cli:document:${e}:${Kl()}`}function Fe(e,t){c(e instanceof Error?e.message:t),process.exit(d.GENERAL_ERROR)}function Xl(e){if(!e)return;let t=e.split(".").pop()?.toLowerCase();if(t==="md"||t==="markdown")return"text/markdown";if(t==="csv")return"text/csv";if(t==="txt")return"text/plain"}var Hl=new Ae("create").description("Save a document into the Library").argument("<title>","Document title").option("--team-id <id>","Workspace ID").option("--content <text>","Document body: literal text, @<path> to read a file, or '-' for stdin").option("--media-type <type>",`One of ${cr.join(", ")}; inferred from an @<path> body when omitted`).option("--summary <text>","One line for the Library card").action(async(e,t)=>{try{let n=t.mediaType??Xl(t.content?.startsWith("@")?t.content.slice(1):void 0);n||(c(`Cannot tell what this document is. Pass --media-type (${cr.join(", ")}).`),process.exit(d.GENERAL_ERROR)),g(await h("createDocument",{content:await ua(t),mediaType:n,summary:t.summary,teamId:t.teamId,title:e},on("create")))}catch(n){Fe(n,"Could not save the document")}}),Yl=new Ae("list").description("List the workspace's documents, newest first").option("--team-id <id>","Workspace ID").option("--media-type <type>",`Filter to one of ${cr.join(", ")}`).option("--limit <n>","Maximum documents to return (default 50)").action(async e=>{try{g(await h("listDocuments",{limit:e.limit?Number(e.limit):void 0,mediaType:e.mediaType,teamId:e.teamId}))}catch(t){Fe(t,"Could not list documents")}}),Ql=new Ae("read").description("Print a document's body").argument("<document-id>","Document id").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{let n=await h("readDocument",{documentId:e,teamId:t.teamId}),{document:r}=n;process.stderr.write(`${r.title} \xB7 ${r.mediaType} \xB7 v${r.version} \xB7 ${r.byteLength} bytes
|
|
3418
|
-
`),process.stdout.write(r.content)}catch(n){Fe(n,"Could not read the document")}}),Zl=new Ae("update").description("Edit a document under an optimistic lock").argument("<document-id>","Document id").requiredOption("--expected-version <n>","The version you last read; the edit is rejected if it has moved on").option("--team-id <id>","Workspace ID").option("--content <text>","New body: literal text, @<path> to read a file, or '-' for stdin").option("--title <text>","New title").option("--summary <text>","New Library-card summary").option("--change-summary <text>","What changed, recorded on the version").action(async(e,t)=>{try{let n=t.content!==void 0;g(await h("updateDocument",{changeSummary:t.changeSummary,content:n?await ua(t):void 0,documentId:e,expectedVersion:Number(t.expectedVersion),summary:t.summary,teamId:t.teamId,title:t.title},on("update")))}catch(n){Fe(n,"Could not update the document")}}),eu=new Ae("delete").description("Remove a document from the Library").argument("<document-id>","Document id").option("--team-id <id>","Workspace ID").option("--yes","Skip the confirmation prompt").action(async(e,t)=>{try{!t.yes&&process.stdin.isTTY&&(c("Refusing to delete without confirmation. Re-run with --yes to remove this document."),process.exit(d.GENERAL_ERROR)),g(await h("deleteDocument",{documentId:e,teamId:t.teamId},on("delete")))}catch(n){Fe(n,"Could not delete the document")}}),tu=new Ae("versions").description("List a document's version history").argument("<document-id>","Document id").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await h("listDocumentVersions",{documentId:e,teamId:t.teamId}))}catch(n){Fe(n,"Could not list versions")}}),nu=new Ae("restore").description("Restore an earlier version by appending it as the current one").argument("<document-id>","Document id").requiredOption("--version-number <n>","The historical version to restore").requiredOption("--expected-version <n>","The version you last read; the restore is rejected if it has moved on").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await h("restoreDocumentVersion",{documentId:e,expectedVersion:Number(t.expectedVersion),teamId:t.teamId,versionNumber:Number(t.versionNumber)},on("restore")))}catch(n){Fe(n,"Could not restore the version")}}),ma=new Ae("document").description("Create, read and edit Library documents").addCommand(Hl).addCommand(Yl).addCommand(Ql).addCommand(Zl).addCommand(eu).addCommand(tu).addCommand(nu);import{Command as ru}from"commander";import an from"chalk";var pa=new ru("get").description("Get presentation details").argument("<slug>","Presentation slug (e.g., my-presentation-v1-abc123)").option("-f, --format <format>","Output format (summary, full, json)","summary").option("--include <fields>","Fields to include (comma-separated: slides,styling)").option("-l, --language <lang>","Language for view URL","en").action(async(e,t)=>{await N();try{let n=await Te(e);if(t.format==="json"){console.log(JSON.stringify(n,null,2));return}let r=We(n.slug,t.language);ye("Presentation Details"),console.log(),T("Slug",n.slug),T("Title",n.title||"Untitled"),T("Description",n.description||an.gray("None")),T("Slides",String(n.numberOfSlides||"-")),T("Mode",n.mode||"-"),T("Created",Fn(n.createdAt)),n.updatedAt&&T("Updated",Fn(n.updatedAt)),console.log(),r!=="N/A"&&console.log(an.bold(" Open: ")+an.cyan.underline(r)),console.log(),t.format==="full"&&t.include?.includes("slides")&&(console.log(an.gray("(Full slide content available in JSON format)")),console.log())}catch(n){c(n instanceof Error?n.message:String(n)),process.exit(n.exitCode??1)}});import{Command as cn}from"commander";import ou from"ora";function ue(e,t){let n=e instanceof Error?e.message:"Unknown error";return!t||n.includes(t)?n:`${n} Request ID: ${t}. Resume with the matching status command; do not start a duplicate run.`}function iu(e){let t=e?.trim().toLowerCase();if(!(!t||t==="any"||t==="all"))return t==="web"||t==="stock"?[t]:void 0}function au(e){let t=e.assetType??"image";if(t!=="image"&&t!=="icon")throw new Error("Asset type must be image or icon");let n;if(e.maxResults&&(n=Number.parseInt(e.maxResults,10),Number.isNaN(n)||n<1))throw new Error("Max results must be a positive number");return{query:t==="icon"?`${e.query.trim()} transparent icon isolated symbol`:e.query.trim(),options:{maxResults:n||10,size:e.size,safeSearch:e.safeSearch,assetType:t,sources:iu(e.source),transparentBackground:t==="icon"}}}function su(e){let t=e.url||e.imageUrl;if(!t)throw new Error("Image search returned a result without a URL");return t}function cu(e){return e.transparent??e.isTransparent??e.metadata?.transparent??e.metadata?.isTransparent}function du(e){let t=[],n=e.reduce((r,o)=>Math.max(r,o.length),0);for(let r=0;r<n;r+=1)for(let o of e){let i=o[r];i!==void 0&&t.push(i)}return t}function lu(e,t){let n=e.data.results.map(r=>r.results.map(o=>{let i=o,a=cu(i);if(t==="icon"&&a!==!0)throw new Error("Icon search returned a result without a verified transparent background");return{...i,url:su(i),provider:r.providerName,assetType:t,...a!==void 0?{transparent:a}:{}}}));return du(n)}function uu(e){return e.error||e.status==="failed"||e.status==="errored"||e.status==="cancelled"?"failed":e.done||e.status==="completed"?"completed":e.status==="in_progress"?"processing":"pending"}function sn(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function mu(e){return{requestId:e.runId,status:uu(e),...e.entity?.url?{url:e.entity.url}:{},...e.error?.message?{error:e.error.message}:{},...sn(e.output?.width)!==void 0?{width:sn(e.output?.width)}:{},...sn(e.output?.height)!==void 0?{height:sn(e.output?.height)}:{}}}async function ga(e,t){return mu(await A("createImage",e,t))}function dr(e,t){if(t==="json"){g(e);return}if(t==="quiet"){console.log(e.output??e.url??e.requestId);return}m(`Request ID: ${e.requestId}`),m(`Status: ${e.status}`),e.url&&I(`Image URL: ${e.url}`),e.output&&I(`Saved to: ${e.output}`),e.error&&c(`Error: ${e.error}`)}var pu=new cn("search").description("Search images across the web and licensed stock").requiredOption("-q, --query <query>","Search query").option("-n, --max-results <number>","Maximum number of results (default: 10)").option("-s, --size <size>","Image size: small, medium, large, any","large").option("--asset-type <type>","Asset intent: image or transparent icon","image").option("--source <source>","Catalog: web, stock, or any (default: any)","any").option("--safe-search","Enable safe search (default: true)",!0).option("--no-safe-search","Disable safe search").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format,n=t==="human"?ou("Searching for images...").start():null,r;try{r=au(e)}catch(o){n?.stop(),c(o instanceof Error?o.message:"Invalid image search request"),process.exit(d.INVALID_INPUT);return}try{let o=await Ut(r);n?.stop(),o.success||(c("Search failed"),process.exit(d.GENERAL_ERROR));let i=r.options?.assetType??"image",a=lu(o,i);if(t==="json"){g({success:!0,query:e.query,assetType:i,totalResults:a.length,images:a});return}if(t==="quiet"){for(let s of a)console.log(s.url);return}if(a.length===0){m("No images found");return}I(`Found ${a.length} images for "${e.query}"`),console.log();for(let s=0;s<a.length;s++){let l=a[s];console.log(`[${s+1}] ${l.title||"Untitled"}`),console.log(` URL: ${l.url}`),console.log(` Size: ${l.width}x${l.height}`),l.author&&console.log(` Author: ${l.author}`),console.log(` Provider: ${l.provider}`),console.log()}m(`Total cost: $${o.data.totalCost.toFixed(4)}`)}catch(o){n?.stop(),c(o instanceof Error?o.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),gu=new cn("generate").description("Generate an image and optionally save it locally").argument("[prompt]","Image prompt").option("-p, --prompt <text>","Image prompt (named compatibility form)").option("--provider <provider>","Provider override").option("--model <model>","Model override").option("--width <pixels>","Width").option("--height <pixels>","Height").option("--team-id <id>","Workspace ID").option("-o, --output <path>","Local output path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").option("--yes","Approve generation").action(async(e,t)=>{let n,r=e??t.prompt;r?.trim()||(c("Provide an image prompt as an argument or with --prompt"),process.exit(d.INVALID_INPUT)),t.yes||(c("Pass --yes to approve image generation"),process.exit(d.INVALID_INPUT)),!t.wait&&t.output&&(c("--output requires waiting for completion; remove --no-wait"),process.exit(d.INVALID_INPUT));try{let o=await h("createImage",{approvalReceipt:"cli:--yes",height:t.height?Number.parseInt(t.height,10):void 0,model:t.model,prompt:r.trim(),provider:t.provider,teamId:t.teamId,width:t.width?Number.parseInt(t.width,10):void 0});if(n=o.runId,!t.wait){dr({requestId:o.runId,status:"pending"},t.format);return}let i=await re(()=>ga(o.runId,t.teamId),180,2e3);if(i.status==="failed")throw new Error(i.error||"Image generation failed");if(!i.url)throw new Error("Image generation completed without an image URL");let a=t.output?{...i,output:t.output}:i;t.output&&await Q(i.url,t.output),dr(a,t.format)}catch(o){c(ue(o,n),t.format),process.exit(d.GENERAL_ERROR)}}),fu=new cn("status").description("Read image generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").option("-o, --output <path>","Download a completed image").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await ga(e,t.teamId);if(t.output){if(n.status!=="completed"||!n.url)throw new Error("Image generation is not complete");await Q(n.url,t.output),n.output=t.output}dr(n,t.format)}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),fa=new cn("image").description("Image search and generation commands").addCommand(pu).addCommand(gu).addCommand(fu);import{Command as lr}from"commander";var hu=new lr("search").description("Search the workspace Knowledge Hub").argument("[query]","Optional search query").option("--team-id <id>","Workspace ID").option("--kind <kind>","Knowledge kind").option("--limit <count>","Maximum results","10").action(async(e,t)=>{try{g(await h("searchKnowledge",{kind:t.kind,limit:Number.parseInt(t.limit,10),query:e,teamId:t.teamId}))}catch(n){c(n instanceof Error?n.message:"Knowledge search failed"),process.exit(d.GENERAL_ERROR)}}),yu=new lr("bundle").description("Read a bounded Open Knowledge Format bundle").option("--team-id <id>","Workspace ID").option("--kind <kind>","Knowledge kind").action(async e=>{try{g(await h("getKnowledgeBundle",{kind:e.kind,teamId:e.teamId}))}catch(t){c(t instanceof Error?t.message:"Knowledge bundle failed"),process.exit(d.GENERAL_ERROR)}}),ha=new lr("knowledge").description("Knowledge Hub commands").addCommand(hu).addCommand(yu);import{randomUUID as bu}from"crypto";import{Command as Re}from"commander";function ur(e){if(e!==void 0)return e==="root"?null:e}function ct(e){return`cli:library:${e}:${bu()}`}function qe(e,t){c(e instanceof Error?e.message:t),process.exit(d.GENERAL_ERROR)}var wu=new Re("list").description("List the Library folder tree").option("--team-id <id>","Workspace ID").option("--counts","Include how many items are in each folder").action(async e=>{try{g(await h("listLibraryFolders",{includeEntryCounts:!!e.counts,teamId:e.teamId}))}catch(t){qe(t,"Could not list folders")}}),vu=new Re("create").description("Create a Library folder").argument("<name>","Folder name").option("--team-id <id>","Workspace ID").option("--parent <id>","Parent folder id; omit for a top-level folder").option("--color <color>","Colour token (slate, blue, green, amber, red, violet)").action(async(e,t)=>{try{g(await h("createLibraryFolder",{color:t.color,name:e,parentId:ur(t.parent),teamId:t.teamId},ct("create")))}catch(n){qe(n,"Could not create the folder")}}),xu=new Re("rename").description("Rename a Library folder").argument("<folder-id>","Folder id").argument("<name>","New name").option("--team-id <id>","Workspace ID").action(async(e,t,n)=>{try{g(await h("renameLibraryFolder",{folderId:e,name:t,teamId:n.teamId},ct("rename")))}catch(r){qe(r,"Could not rename the folder")}}),Su=new Re("recolor").description("Set or clear a Library folder's colour").argument("<folder-id>","Folder id").requiredOption("--color <color>","Colour token (slate, blue, green, amber, red, violet), or 'none' to clear").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await h("recolorLibraryFolder",{color:t.color==="none"?null:t.color,folderId:e,teamId:t.teamId},ct("recolor")))}catch(n){qe(n,"Could not recolour the folder")}}),Iu=new Re("move").description("Move a Library folder under a new parent").argument("<folder-id>","Folder id").requiredOption("--to <id>","New parent folder id, or 'root' for top level").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await h("moveLibraryFolder",{folderId:e,newParentId:ur(t.to),teamId:t.teamId},ct("move")))}catch(n){qe(n,"Could not move the folder")}}),Ru=new Re("delete").description("Delete a folder and its subfolders; items inside are unfiled").argument("<folder-id>","Folder id").requiredOption("--yes","Confirm deletion").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await h("deleteLibraryFolder",{folderId:e,teamId:t.teamId},ct("delete")))}catch(n){qe(n,"Could not delete the folder")}}),Cu=new Re("folders").description("Manage Library folders").addCommand(wu).addCommand(vu).addCommand(xu).addCommand(Su).addCommand(Iu).addCommand(Ru),ku=new Re("file").description("File an artifact into a Library folder").argument("<kind>","Artifact kind, e.g. presentation or microsite").argument("<target-id>","Id of the artifact itself").requiredOption("--folder <id>","Destination folder id, or 'root' to unfile the artifact").option("--team-id <id>","Workspace ID").action(async(e,t,n)=>{try{g(await h("fileArtifactIntoFolder",{folderId:ur(n.folder),kind:e,targetId:t,teamId:n.teamId},ct("file")))}catch(r){qe(r,"Could not file that item")}}),ya=new Re("library").description("Library folders and filing").addCommand(Cu).addCommand(ku);import{Command as Rt}from"commander";var ba=new Rt("analyze").description("Transcribe an uploaded recording and publish it for generation").argument("<asset-id>","Recording ID").option("--language <code>","BCP-47 language hint for the spoken audio").option("--no-diarize","Skip speaker attribution (cheaper, but a multi-speaker recording becomes much weaker as generation input)").option("--team-id <id>","Workspace ID").option("--yes","Confirm the charge \u2014 billed per source-minute").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to confirm. Analysis is billed per minute of source audio.");try{g(await h("analyzeMediaAsset",{assetId:e,diarize:t.diarize,language:t.language,teamId:t.teamId}))}catch(n){c(n instanceof Error?n.message:"Analysis failed"),process.exit(d.GENERAL_ERROR)}}),Tu=new Rt("status").description("Read an analysis run's status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A("analyzeMediaAsset",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}});ba.addCommand(Tu);var wa=new Rt("import").description("Import a recording from a connected account").argument("<remote-file-id>","The provider's own ID for the recording").requiredOption("--provider <id>","Provider id of the connected account, e.g. google").requiredOption("--connector <id>","Connected account ID to read from").option("--title <title>","Name for the imported recording").option("--chat <id>","Chat this import was requested from").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await h("importConnectorRecording",{connectorId:t.connector,providerId:t.provider,remoteFileId:e,sourceChatId:t.chat,teamId:t.teamId,title:t.title}))}catch(n){c(n instanceof Error?n.message:"Import failed"),process.exit(d.GENERAL_ERROR)}}),Eu=new Rt("status").description("Read an import run's status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A("importConnectorRecording",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}});wa.addCommand(Eu);var va=new Rt("media").description("Ingest and transcribe recordings you already own").addCommand(ba).addCommand(wa);import{Command as mr}from"commander";var Au=new mr("create").description("Start background microsite generation").argument("<title>","Microsite title").requiredOption("--description <brief>","Microsite brief").option("--audience <audience>","Target audience").option("--tone <tone>","Tone").option("--language <language>","Content language","en").option("--branding-id <id>","Branding ID").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to approve generation");try{g(await h("createMicrosite",{approvalReceipt:"cli:--yes",audience:t.audience,brandingId:t.brandingId,description:t.description,language:t.language,teamId:t.teamId,title:e,tone:t.tone}))}catch(n){c(n instanceof Error?n.message:"Generation failed"),process.exit(d.GENERAL_ERROR)}}),$u=new mr("status").description("Read microsite generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A("createMicrosite",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),xa=new mr("microsite").description("Microsite commands").addCommand(Au).addCommand($u);import{Command as fr}from"commander";import pr from"ora";function gr(e,t){if(t==="json"){let{cost:n,...r}=e;g(r);return}if(t==="quiet"){e.outputUrl?console.log(e.outputUrl):console.log(e.requestId);return}m(`Request ID: ${e.requestId}`),m(`Status: ${e.status}`),e.duration&&m(`Duration: ${e.duration}s`),e.outputUrl&&I(`Output URL: ${e.outputUrl}`),e.cost!==void 0&&m(`Cost: $${e.cost.toFixed(4)}`),e.error&&c(`Error: ${e.error}`)}var Ou=new fr("create").description("Mix audio tracks into a video (music will loop to match video duration)").requiredOption("--video <url>","Input video file/URL").option("--music <url>","Background music file/URL (will loop if shorter than video)").option("--voice <url>","Voiceover file/URL").option("--music-volume <percent>","Music volume 0-100 (default: 30, recommended for mix with voice)","30").option("--voice-volume <percent>","Voice volume 0-100","100").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{!e.music&&!e.voice&&(c("At least one of --music or --voice must be provided"),process.exit(d.INVALID_INPUT));let t=parseInt(e.musicVolume,10)/100,n=parseInt(e.voiceVolume,10)/100;(Number.isNaN(t)||t<0||t>1)&&(c("Music volume must be between 0 and 100"),process.exit(d.INVALID_INPUT)),(Number.isNaN(n)||n<0||n>2)&&(c("Voice volume must be between 0 and 200"),process.exit(d.INVALID_INPUT));let r=e.format,o=r==="human"?pr("Mixing audio...").start():null,i=[{url:e.video,role:"video"}];e.music&&i.push({url:e.music,role:"background",volume:t*5}),e.voice&&i.push({url:e.voice,role:"voice",volume:n*2});try{let a=await vo({operation:"add-to-video",inputs:i,options:{musicVolume:t,voiceVolume:n}});if(!e.wait){o?.stop(),gr(a,r);return}o&&(o.text=`Processing (ID: ${a.requestId})...`);let s=await re(()=>Pn(a.requestId),120,3e3);if(o?.stop(),s.status==="failed"&&(c(s.error||"Audio mixing failed"),process.exit(d.GENERAL_ERROR)),gr(s,r),e.output){if(!s.outputUrl)throw new Error("Audio mix completed without an output URL");let l=r==="human"?pr("Downloading...").start():null;await Q(s.outputUrl,e.output),l?.stop(),r==="human"&&I(`Saved to: ${e.output}`)}}catch(a){o?.stop(),c(a instanceof Error?a.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),Pu=new fr("status").description("Check status of an audio mix request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let n=t.format==="human"?pr("Checking status...").start():null;try{let r=await Pn(e);n?.stop(),gr(r,t.format)}catch(r){n?.stop(),c(r instanceof Error?r.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),Sa=new fr("mix").description("Audio mixing commands").addCommand(Ou).addCommand(Pu);import{Command as yr}from"commander";import qu from"ora";import{readFileSync as Nu}from"fs";var Ia=500,Ra=1e4,dn=100,Ca=1e4,ka=1e3,Ea=["duration_s","idempotency_key","narration","schema_version","video_description"],_u=["end_s","id","start_s","text","words"],Lu=["end","id","start","text"],Du=["description","end_s","id","start_s"];function Aa(e){let t=Oa(e);if(Pa(t),Ct(t,Ea,""),t.duration_s<3)throw new Error("Music request duration_s must be between 3 and 600");return t}function $a(e){let t=Oa(e);Pa(t),Ct(t,[...Ea,"visual_event"],"");let n=t.visual_event;if(!ln(n))throw new Error("SFX request visual_event is required");if(Ct(n,Du,"visual_event"),!$e(n.description))throw new Error("SFX request visual_event.description is required");if(!$e(n.id)||!Oe(n.start_s))throw new Error("SFX request visual_event requires id and start_s");if(n.end_s!==void 0&&(!Oe(n.end_s)||n.end_s<n.start_s))throw new Error("SFX request visual_event.end_s must be after start_s");return t}function Oa(e){try{let t=JSON.parse(Nu(e,"utf8"));if(!ln(t))throw new Error("request must be a JSON object");return t}catch(t){throw new Error(`Cannot read audio request ${e}: ${t instanceof Error?t.message:String(t)}`)}}function Pa(e){if(e.schema_version!==1)throw new Error("Audio request schema_version must be 1");if(!$e(e.video_description))throw new Error("Audio request video_description is required");if(!Oe(e.duration_s)||e.duration_s<.5||e.duration_s>600)throw new Error("Audio request duration_s must be between 0.5 and 600");if(!Array.isArray(e.narration))throw new Error("Audio request narration must be an array");if(e.narration.length>Ia)throw new Error(`Audio request narration holds at most ${Ia} lines`);if(e.narration.forEach(Uu),e.idempotency_key!==void 0&&!$e(e.idempotency_key))throw new Error("Audio request idempotency_key must be a non-empty string")}function Uu(e,t){let n=`narration.${t}`;if(!ln(e))throw new Error(`Audio request ${n} must be an object`);if(Ct(e,_u,n),!$e(e.id)||e.id.length>dn)throw new Error(`Audio request ${n}.id is required (1-${dn} characters)`);if(!$e(e.text)||e.text.trim().length>Ca)throw new Error(`Audio request ${n}.text is required (1-${Ca} characters)`);if(Ta(e.start_s,`${n}.start_s`),Ta(e.end_s,`${n}.end_s`),Oe(e.start_s)&&Oe(e.end_s)&&e.end_s<e.start_s)throw new Error(`Audio request ${n}.end_s must be greater than or equal to ${n}.start_s`);if(e.words!==void 0){if(!Array.isArray(e.words)||e.words.length>Ra)throw new Error(`Audio request ${n}.words must be an array of at most ${Ra} entries`);e.words.forEach((r,o)=>{Fu(r,`${n}.words.${o}`)})}}function Fu(e,t){if(!ln(e))throw new Error(`Audio request ${t} must be an object`);if(Ct(e,Lu,t),!$e(e.text)||e.text.trim().length>ka)throw new Error(`Audio request ${t}.text is required (1-${ka} characters)`);if(!Oe(e.start)||!Oe(e.end))throw new Error(`Audio request ${t} requires numeric start and end seconds`);if(e.end<e.start)throw new Error(`Audio request ${t}.end must be greater than or equal to ${t}.start`);if(e.id!==void 0&&(!$e(e.id)||e.id.length>dn))throw new Error(`Audio request ${t}.id must be 1-${dn} characters`)}function Ta(e,t){if(e!==void 0&&!Oe(e))throw new Error(`Audio request ${t} must be a non-negative number`)}function Ct(e,t,n){let r=Object.keys(e).filter(i=>!t.includes(i));if(r.length===0)return;let o=n?`${n} `:"";throw new Error(`Audio request ${o}has unknown field(s): ${r.join(", ")}. Allowed: ${t.join(", ")}`)}function ln(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function $e(e){return typeof e=="string"&&e.trim().length>0}function Oe(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0}function hr(e,t){if(t==="json"){let{cost:n,...r}=e;g(r);return}if(t==="quiet"){console.log(e.audioUrl??e.requestId);return}m(`Request ID: ${e.requestId}`),m(`Status: ${e.status}`),e.duration&&m(`Duration: ${e.duration}s`),e.audioUrl&&I(`Audio URL: ${e.audioUrl}`),e.cost!==void 0&&m(`Cost: $${e.cost.toFixed(4)}`),e.error&&c(`Error: ${e.error}`)}var Vu=new yr("generate").description("Generate music that follows a video's narration and final timing").requiredOption("--request <path>","JSON file with video description, narration, and final timing").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{!e.wait&&e.output&&(c("--output requires waiting for completion; remove --no-wait"),process.exit(d.INVALID_INPUT));let t;try{t=Aa(e.request)}catch(i){c(i instanceof Error?i.message:String(i)),process.exit(d.INVALID_INPUT)}let n=e.format,r=n==="human"?qu("Generating music for video...").start():null,o;try{let i=await wo(t);if(o=i.requestId,!e.wait){r?.stop(),hr(i,n);return}r&&(r.text=`Processing (ID: ${i.requestId})...`);let a=i.status==="completed"||i.status==="failed"?i:await re(()=>On(i.requestId),180,2e3);if(r?.stop(),a.status==="failed"&&(c(a.error||"Music generation failed"),process.exit(d.GENERAL_ERROR)),e.output){if(!a.audioUrl)throw new Error("Music completed without an audio URL");await Q(a.audioUrl,e.output),n==="human"&&I(`Saved to: ${e.output}`),a.output=e.output}hr(a,n)}catch(i){r?.stop(),c(ue(i,o),n),process.exit(d.GENERAL_ERROR)}}),ju=new yr("status").description("Check status of a music generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{hr(await On(e),t.format)}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),Na=new yr("music").description("Video-aware music generation commands").addCommand(Vu).addCommand(ju);import{Command as br}from"commander";function _a(e,t){return[...t,e]}var Mu=new br("create").description("Create a standalone poll").argument("<question>","Poll question").option("--option <text>","Choice (repeat 2-10 times)",_a,[]).option("--type <type>","single, multiple, or word-cloud","single").option("--title <title>","Poll title").option("--team-id <id>","Workspace ID").option("--yes","Approve creation").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to approve poll creation");try{g(await h("createPoll",{approvalReceipt:"cli:--yes",options:t.option.map(n=>({text:n})),pollType:t.type,question:e,teamId:t.teamId,title:t.title}))}catch(n){c(n instanceof Error?n.message:"Poll creation failed"),process.exit(d.GENERAL_ERROR)}}),Bu=new br("edit").description("Edit a standalone poll").argument("<poll-id>","Poll UUID").option("--question <question>","New question").option("--option <text>","Replacement choice (repeat)",_a).option("--type <type>","single, multiple, or word-cloud").option("--title <title>","Poll title").option("--team-id <id>","Workspace ID").option("--yes","Approve update").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to approve poll update");try{g(await h("editPoll",{approvalReceipt:"cli:--yes",options:t.option?.map(n=>({text:n})),pollId:e,pollType:t.type,question:t.question,teamId:t.teamId,title:t.title}))}catch(n){c(n instanceof Error?n.message:"Poll update failed"),process.exit(d.GENERAL_ERROR)}}),La=new br("poll").description("Standalone poll commands").addCommand(Mu).addCommand(Bu);import{randomUUID as Qu}from"crypto";import{readFile as Zu}from"fs/promises";import{Command as te}from"commander";import{randomUUID as vr}from"crypto";import{readFile as Gu}from"fs/promises";import{Command as un}from"commander";var Da=new un("edit").description("Apply one atomic draft edit: narration, scene patch/move/remove, composition, or intro/outro card").argument("<project-id>","Presentation-video project UUID or editor slug").requiredOption("--edit <json-or-path>","Edit object as inline JSON or a JSON file path").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key").action(async(e,t)=>{await mn("Draft edit failed",async()=>h("editPresentationVideoDraft",{edit:await Wu(t.edit),projectId:e,teamId:t.teamId},t.idempotencyKey??vr()))}),Ua=new un("versions").description("List saved presentation-video draft versions").argument("<project-id>","Presentation-video project UUID or editor slug").option("--before-version <number>","Continue below this version number",wr(1)).option("--limit <number>","Maximum versions",wr(1,100)).option("--team-id <id>","Workspace ID override").action(async(e,t)=>{await mn("Version list failed",()=>h("listPresentationVideoVersions",{beforeVersion:t.beforeVersion,limit:t.limit,projectId:e,teamId:t.teamId}))}),Fa=new un("restore").description("Restore a saved draft version as a new version").argument("<project-id>","Presentation-video project UUID or editor slug").argument("<version-number>","Version number",wr(1)).option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key").action(async(e,t,n)=>{await mn("Version restore failed",()=>h("restorePresentationVideoVersion",{projectId:e,teamId:n.teamId,versionNumber:t},n.idempotencyKey??vr()))}),qa=new un("refresh").description("Refresh deck-backed scenes from the latest presentation version").argument("<project-id>","Presentation-video project UUID or editor slug").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key").action(async(e,t)=>{await mn("Slide refresh failed",()=>h("refreshPresentationVideoSlides",{projectId:e,teamId:t.teamId},t.idempotencyKey??vr()))});async function Wu(e){let t=e.trim(),n=t.startsWith("{")?t:await Gu(t,"utf8"),r=JSON.parse(n);if(!r||typeof r!="object"||Array.isArray(r))throw new Error("--edit must contain one JSON object.");return r}function wr(e,t=Number.MAX_SAFE_INTEGER){return n=>{let r=Number(n);if(!Number.isInteger(r)||r<e||r>t)throw new Error(`Expected an integer between ${e} and ${t}.`);return r}}async function mn(e,t){try{g(await t())}catch(n){c(n instanceof Error?n.message:e),process.exit(d.GENERAL_ERROR)}}import{randomUUID as Ku}from"crypto";import{Command as Ju}from"commander";var zu=["basic","standard","premium"],Va=new Ju("presenter").description("Set one scene to a different avatar/look or preset, or restore the project default").argument("<project-id>","Presentation video project ID").argument("<slide-id>","Slide or intro/outro scene ID").option("--avatar-id <id>","Exact avatar/look ID from presenter list").option("--cast-preset-id <id>","Saved cast preset ID").option("--no-apply-preset-voice","Preserve this scene's current voice instead of applying the preset voice").option("--inherit","Remove the override and inherit the project default").option("--render-tier <tier>","Avatar render tier: basic, standard, or premium").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key for this update").action(async(e,t,n)=>{try{if(+!!n.avatarId+ +!!n.castPresetId+ +(n.inherit===!0)!==1)throw new Error("Pass exactly one of --avatar-id, --cast-preset-id, or --inherit.");if(n.renderTier&&!n.avatarId)throw new Error("--render-tier can only be used with --avatar-id.");if(n.renderTier&&!zu.includes(n.renderTier))throw new Error("--render-tier must be basic, standard, or premium.");g(await h("setPresentationVideoScenePresenter",{applyPresetVoice:n.castPresetId?n.applyPresetVoice:void 0,avatarId:n.avatarId,castPresetId:n.castPresetId,inherit:n.inherit,projectId:e,renderTier:n.renderTier,slideId:t,teamId:n.teamId},Xu(n.idempotencyKey)))}catch(r){c(r instanceof Error?r.message:"Scene presenter update failed"),process.exit(d.GENERAL_ERROR)}});function Xu(e){return e||Ku()}import{Command as xr}from"commander";var ja=new xr("inspect").description("Read a presentation-video project and its ordered scene timeline").argument("<project-id>","Presentation-video project UUID or editor slug").option("--team-id <id>","Workspace ID override").action(async(e,t)=>{try{g(await h("getPresentationVideoProject",{projectId:e,teamId:t.teamId}))}catch(n){c(n instanceof Error?n.message:"Project read failed"),process.exit(d.GENERAL_ERROR)}}),Hu=new xr("list").description("List generated clips and uploaded recordings usable in a presentation video").argument("<project-id>","Target presentation-video project UUID or slug").option("--cursor <cursor>","Opaque cursor from the previous page").option("--limit <number>","Maximum clips to return",Yu).option("--query <text>","Filter clips by title").option("--team-id <id>","Workspace ID override").action(async(e,t)=>{try{g(await h("listReusableVideoClips",{cursor:t.cursor,limit:t.limit,projectId:e,query:t.query,teamId:t.teamId}))}catch(n){c(n instanceof Error?n.message:"Clip list failed"),process.exit(d.GENERAL_ERROR)}}),Ma=new xr("library").description("Reusable generated clips and uploaded recordings").addCommand(Hu);function Yu(e){let t=Number(e);if(!Number.isInteger(t)||t<1||t>100)throw new Error("--limit must be an integer between 1 and 100.");return t}var em=new te("status").description("Check a presentation generation run").argument("<run-id>","Run ID returned by presentation generation").option("--team-id <id>","Workspace ID override").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await A("createSlides",e,t.teamId);if(t.format==="json"){g(n);return}if(t.format==="quiet"){console.log(n.status);return}m(`Status: ${n.status}`),m(n.message),n.progress!==null&&m(`Progress: ${n.progress}%`),n.error&&c(n.error.message)}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),tm=new te("transform").description("Transform an uploaded PowerPoint import for review").argument("<import-id>","PowerPoint import ID returned by Mindframes").option("--idempotency-key <key>","Stable retry key").action(async(e,t)=>{try{let n=await h("transformPresentationImport",{importId:e},t.idempotencyKey);g(n)}catch(n){c(n instanceof Error?n.message:"Transform failed"),process.exit(d.GENERAL_ERROR)}}),nm=new te("status").description("Read a PowerPoint import run").argument("<run-id>","Run ID returned by PowerPoint import").option("--team-id <id>","Workspace ID override").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await A("importPowerPoint",e,t.teamId);t.format==="json"?g(n):t.format==="quiet"?console.log(n.status):(m(`Status: ${n.status}`),m(n.message),n.error&&c(n.error.message))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),Ba=new te("import").description("Import a PowerPoint through the review-gated reconstruction pipeline").option("--file <path>","Path to the .pptx to import").option("--filename <name>","Override the uploaded filename").option("--mode <mode>","Import mode: preserve or rebrand","preserve").option("--branding-id <id>","Saved brand ID (required for rebrand mode)").option("--source-chat-id <id>","Associate the import with an existing chat").option("--title <title>","Presentation title").option("--idempotency-key <key>","Stable retry key").action(async e=>{if(!e.file){Ba.help();return}e.file.toLowerCase().endsWith(".pptx")||(c("PowerPoint import requires a .pptx file."),process.exit(d.INVALID_INPUT));try{let t=await he(e.file),n=await h("importPowerPoint",{brandingId:e.brandingId,filename:e.filename??t.name,mode:e.mode,sourceChatId:e.sourceChatId,sourceUrl:t.url,title:e.title},e.idempotencyKey??Qu());g(n)}catch(t){c(t instanceof Error?t.message:"Import failed"),process.exit(d.GENERAL_ERROR)}}).addCommand(tm).addCommand(nm),rm=new te("speaker-notes-status").description("Check a speaker-notes generation run").argument("<run-id>","Run ID returned by speaker-notes generation").option("--team-id <id>","Workspace ID override").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await A("generateSpeakerNotes",e,t.teamId);t.format==="json"?g(n):t.format==="quiet"?console.log(n.status):(m(`Status: ${n.status}`),m(n.message))}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),om=new te("video-narration-status").description("Check a presentation-video narration regeneration run").argument("<run-id>","Run ID returned by video-narration").option("--team-id <id>","Workspace ID override").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await A("regeneratePresentationVideoNarration",e,t.teamId);t.format==="json"?g(n):t.format==="quiet"?console.log(n.status):(m(`Status: ${n.status}`),m(n.message))}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),im=new te("status").description("Read a presentation-video MP4 render run").argument("<run-id>","Run ID returned by presentation video").option("--team-id <id>","Workspace ID override").action(async(e,t)=>{try{g(await A("createPresentationVideo",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),am=new te("prepare").description("Create or reuse a presentation-video project for a presentation").argument("<presentation-id>","Presentation ID, document ID, or slug").option("--force-new","Create a fresh video project even if the current presentation version already has one").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key for this prepare request").option("--yes","Confirm creating or reusing the presentation video project").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to confirm preparing a presentation video.");try{g(await h("preparePresentationVideo",{forceNew:t.forceNew,presentationId:e,teamId:t.teamId},t.idempotencyKey))}catch(n){c(n instanceof Error?n.message:"Prepare failed"),process.exit(d.GENERAL_ERROR)}}),sm=new te("settings").description("Update saved presentation-video render settings").argument("<project-id>","Presentation video project ID").requiredOption("--settings <json-or-path>","Settings patch as JSON or a path to a JSON file").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key for this settings update").option("--yes","Confirm updating presentation video settings").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to confirm updating presentation video settings.");try{g(await h("updatePresentationVideoSettings",{projectId:e,settings:await Ga(t.settings),teamId:t.teamId},t.idempotencyKey))}catch(n){c(n instanceof Error?n.message:"Settings update failed"),process.exit(d.GENERAL_ERROR)}}),cm=new te("video").description("Create an MP4 video from a presentation").argument("[presentation-id]","Presentation ID, document ID, or slug").option("--project-id <id>","Advanced: render an already-configured presentation video project").option("--settings <json-or-path>","Settings patch as JSON or a path to a JSON file; only valid with presentation-id").option("--force-new","Create a fresh video project even if the current presentation version already has one").option("--force-rerender","Start a new MP4 render even when the current project fingerprint has a completed render").option("--render-engine <engine>","Renderer for this MP4 only: remotion (Standard) or hyperframes (Premium)").option("--team-id <id>","Workspace ID override").option("--idempotency-key <key>","Stable retry key for this render request").option("--yes","Confirm starting the MP4 render").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to confirm presentation video rendering.");try{if(!e&&!t.projectId)throw new Error("Pass a presentation id or --project-id.");if(e&&t.projectId)throw new Error("Pass either presentation id or --project-id, not both.");if(t.projectId&&(t.settings||t.forceNew))throw new Error("--settings and --force-new require a presentation id, not --project-id.");if(t.renderEngine&&t.renderEngine!=="remotion"&&t.renderEngine!=="hyperframes")throw new Error("--render-engine must be remotion or hyperframes.");g(await h("createPresentationVideo",{forceNew:t.forceNew,forceRerender:t.forceRerender,presentationId:e,projectId:t.projectId,renderEngine:t.renderEngine,settings:t.settings?await Ga(t.settings):void 0,teamId:t.teamId},t.idempotencyKey))}catch(n){c(n instanceof Error?n.message:"Render failed"),process.exit(d.GENERAL_ERROR)}}).addCommand(am).addCommand(ja).addCommand(Da).addCommand(Va).addCommand(qa).addCommand(Fa).addCommand(sm).addCommand(im).addCommand(Ua);async function Ga(e){let t=e.trim(),n=t.startsWith("{")?t:await Zu(t,"utf-8"),r=JSON.parse(n);if(!r||typeof r!="object"||Array.isArray(r))throw new Error("--settings must be a JSON object.");return r}function dm(e,t){return[...t,e]}var lm=new te("speaker-notes").description("Generate or regenerate persisted presentation speaker notes").argument("<presentation-id>","Presentation ID or slug").option("--mode <mode>","ensure or regenerate","ensure").option("--style <style>","concise or detailed","concise").option("--instructions <text>","Instructions for every selected slide").option("--language <language>","Output language").option("--slide-id <id>","Target one slide (repeatable)",dm,[]).option("--no-follow","Return immediately with the durable run ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{if(!["ensure","regenerate"].includes(t.mode))throw new Error("--mode must be ensure or regenerate");if(!["concise","detailed"].includes(t.style))throw new Error("--style must be concise or detailed");let n=await ro(e,{instructions:t.instructions,language:t.language,mode:t.mode,slideIds:t.slideId.length?t.slideId:void 0,style:t.style});if(t.format==="json"||!t.follow){g(n);return}m(`Speaker notes run: ${n.runId}`);let r=await An(n.runId);await Wa(r)}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),um=new te("video-narration").description("Regenerate one slide narration in a presentation video draft").argument("<project-id>","Presentation video project ID").requiredOption("--slide-id <id>","Slide ID in the video draft").option("--instructions <text>","Optional regeneration instructions").option("--no-follow","Return immediately with the durable run ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await oo(e,{instructions:t.instructions,slideId:t.slideId});if(t.format==="json"||!t.follow){g(n);return}m(`Narration run: ${n.runId}`),await Wa(await An(n.runId))}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}});async function Wa(e){let t=e.body?.getReader();if(!t)throw new Error("Speaker notes event stream is unavailable");let n=new TextDecoder,r="";for(;;){let{done:o,value:i}=await t.read();r+=n.decode(i,{stream:!o});let a=r.split(`
|
|
3419
|
-
|
|
3420
|
-
`);r=a.pop()??"";for(let s of a){let l=s.split(`
|
|
3421
|
-
`).find(u=>u.startsWith("data:"))?.slice(5).trim();if(!l)continue;let p=JSON.parse(l);p.eventType?.startsWith("slide.")&&m(`${p.eventType}${p.payload?.slideId?` \xB7 ${p.payload.slideId}`:""}`)}if(o)return}}var Ka=new te("presentation").description("Presentation background-run commands").addCommand(em).addCommand(Ba).addCommand(lm).addCommand(rm).addCommand(cm).addCommand(um).addCommand(om);import{writeFile as mm}from"fs/promises";import{Command as pm}from"commander";import gm from"ora";var Ja=new pm("scrape").description("Extract content from a URL").argument("<url>","URL to scrape").option("--team-id <id>","Workspace ID").option("-o, --output <path>","Save content to file").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let n=t.format,r=n==="human"?gm("Scraping URL...").start():null;try{let o=await Io(e,t.teamId);r?.stop(),(!o.success||!o.data)&&(c(o.error||"Failed to scrape URL"),process.exit(d.GENERAL_ERROR));let i=o.data;if(t.output&&(await mm(t.output,i.content,"utf-8"),n==="human"&&I(`Content saved to: ${t.output}`)),n==="json"){let{cost:a,...s}=i;g(s);return}if(n==="quiet"){console.log(i.content);return}i.title&&(console.log(),console.log(`Title: ${i.title}`)),i.metadata?.description&&console.log(`Description: ${i.metadata.description}`),console.log(`URL: ${i.url}`),console.log(`Tokens: ~${i.metadata?.tokenUsage?.toLocaleString()||"unknown"}`),i.warning&&q(i.warning),t.output||(console.log(),console.log("--- Content ---"),console.log(i.content)),i.cost&&i.cost>0&&m(`Cost: $${i.cost.toFixed(6)}`)}catch(o){r?.stop(),c(o instanceof Error?o.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}});import{Command as kt}from"commander";function pn(e,t){t!=="quiet"&&g(e)}var fm=new kt("web").description("Search current public web sources").argument("<query>","Search query").option("--team-id <id>","Workspace ID").option("--max-results <count>","Maximum results","5").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await h("searchInternet",{maxResults:Number.parseInt(t.maxResults,10),query:e,teamId:t.teamId});pn(n,t.format)}catch(n){c(n instanceof Error?n.message:"Search failed"),process.exit(d.GENERAL_ERROR)}}),hm=new kt("news").description("Search recent news").argument("<topic>","News topic").option("--team-id <id>","Workspace ID").option("--timeframe <value>","Timeframe hint").option("--max-results <count>","Maximum results","6").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await h("getNews",{maxResults:Number.parseInt(t.maxResults,10),teamId:t.teamId,timeframe:t.timeframe,topic:e});pn(n,t.format)}catch(n){c(n instanceof Error?n.message:"News search failed"),process.exit(d.GENERAL_ERROR)}});function za(e){let t=e?.trim().toLowerCase();if(!(!t||t==="any"||t==="all"))return t==="web"||t==="stock"?[t]:void 0}var ym=new kt("images").description("Search images across the web and licensed stock").argument("<query>","Image search query").option("--team-id <id>","Workspace ID").option("--max-results <count>","Maximum results","10").option("--aspect <aspect>","Aspect: any, square, wide, tall","any").option("--source <source>","Catalog: web, stock, or any (default: any)","any").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await h("searchImages",{aspect:t.aspect,maxResults:Number.parseInt(t.maxResults,10),query:e,sources:za(t.source),teamId:t.teamId});pn(n,t.format)}catch(n){c(n instanceof Error?n.message:"Image search failed"),process.exit(d.GENERAL_ERROR)}}),bm=new kt("video").description("Search video across the web and licensed stock").argument("<query>","Video search query").option("--team-id <id>","Workspace ID").option("--max-results <count>","Maximum results","10").option("--source <source>","Catalog: web, stock, or any (default: any)","any").option("--orientation <value>","Orientation: landscape, portrait, square, any","any").option("--license <value>","License: free, premium, any","any").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{let n=await h("searchStockVideo",{license:t.license,maxResults:Number.parseInt(t.maxResults,10),orientation:t.orientation,query:e,sources:za(t.source),teamId:t.teamId});pn(n,t.format)}catch(n){c(n instanceof Error?n.message:"Video search failed"),process.exit(d.GENERAL_ERROR)}}),Xa=new kt("search").description("Web, news, image, and stock-video search").addCommand(fm).addCommand(hm).addCommand(ym).addCommand(bm);import{Command as Ir}from"commander";import wm from"ora";function Sr(e,t){if(t==="json"){let{cost:n,...r}=e;g(r);return}if(t==="quiet"){console.log(e.audioUrl??e.requestId??e.id);return}m(`Request ID: ${e.requestId||e.id}`),m(`Status: ${e.status}`),e.duration&&m(`Duration: ${e.duration}s`),e.audioUrl&&I(`Audio URL: ${e.audioUrl}`),e.cost!==void 0&&m(`Cost: $${e.cost.toFixed(4)}`),e.error&&c(`Error: ${e.error}`)}var vm=new Ir("generate").description("Generate one SFX cue from a video's final facts and visual event").requiredOption("--request <path>","JSON file with video description, narration timings, and visual_event").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{!e.wait&&e.output&&(c("--output requires waiting for completion; remove --no-wait"),process.exit(d.INVALID_INPUT));let t;try{t=$a(e.request)}catch(i){c(i instanceof Error?i.message:String(i)),process.exit(d.INVALID_INPUT)}let n=e.format,r=n==="human"?wm("Generating sound effect for video...").start():null,o;try{let i=await Ro(t);if(o=i.requestId||i.id,!e.wait){r?.stop(),Sr(i,n);return}let a=i.requestId||i.id;r&&(r.text=`Processing (ID: ${a})...`);let s=i.status==="completed"||i.status==="failed"?i:await re(()=>_n(a),60,2e3);if(r?.stop(),s.status==="failed"&&(c(s.error||"Sound effects generation failed"),process.exit(d.GENERAL_ERROR)),e.output){if(!s.audioUrl)throw new Error("Sound effect completed without an audio URL");await Q(s.audioUrl,e.output),n==="human"&&I(`Saved to: ${e.output}`)}Sr(s,n)}catch(i){r?.stop(),c(ue(i,o),n),process.exit(d.GENERAL_ERROR)}}),xm=new Ir("status").description("Check status of a sound effects generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{Sr(await _n(e),t.format)}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),Ha=new Ir("sfx").description("Video-aware sound effects generation commands").addCommand(vm).addCommand(xm);import{writeFile as Sm}from"fs/promises";import{Command as Im}from"commander";import Rm from"ora";var Ya=new Im("transcript").description("Fetch a YouTube video's transcript").argument("<url>","YouTube URL or 11-character video id").option("--team-id <id>","Workspace ID").option("--language <codes>","Comma-separated language priority list, e.g. 'en,de' or 'asr-en'").option("--no-timestamps","Print plain text without [m:ss] prefixes").option("-o, --output <path>","Save the transcript to a file").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let n=t.format,r=n==="human"?Rm("Fetching transcript...").start():null;try{let o=await So(e,t.teamId,t.language);r?.stop();let i=t.timestamps?o.cues.map(a=>`[${a.timestamp}] ${a.text}`).join(`
|
|
3422
|
-
`):o.cues.map(a=>a.text).join(`
|
|
3423
|
-
`);if(t.output&&(await Sm(t.output,i,"utf-8"),n==="human"&&I(`Transcript saved to: ${t.output}`)),n==="json"){g(o);return}if(n==="quiet"){console.log(i);return}console.log(),console.log(`Title: ${o.title}`),o.channelName&&console.log(`Channel: ${o.channelName}`),o.durationFormatted&&console.log(`Duration: ${o.durationFormatted}`),console.log(`Language: ${o.language??"unknown"}${o.isAutoGenerated?" (auto-generated)":""}`),console.log(`Words: ${o.wordCount.toLocaleString()}`),console.log(`URL: ${o.url}`),o.truncated&&q("Transcript was truncated to fit the response budget."),t.output||(console.log(),console.log("--- Transcript ---"),console.log(i)),o.totalCost&&o.totalCost>0&&m(`Cost: $${o.totalCost.toFixed(6)}`)}catch(o){r?.stop(),c(o instanceof Error?o.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}});import{mkdir as Cm,writeFile as Qa}from"fs/promises";import{dirname as km}from"path";import{Command as gn}from"commander";import ts from"ora";var Za="en-US-Neural2-C",es="gemini",Tm=new gn("generate").description("Generate speech from text").requiredOption("-t, --text <text>","Text to convert to speech").requiredOption("-o, --output <path>","Output file path").option("--voice-id <voiceId>","Voice ID (e.g., ElevenLabs: 21m00Tcm4TlvDq8ikWAM)").option("--voice <voice>",`Voice name (default: ${Za})`).option("-p, --provider <provider>",`Provider: gemini, elevenlabs, openai (default: ${es})`).option("-m, --model <model>","Model (provider-specific)").option("-s, --speed <speed>","Speech speed 0.1-4.0 (default: 1.0)").option("-l, --lang <lang>","Language or locale hint (for example en-US)").option("--speakers <json>",`Multi-speaker (Gemini, max 2): '[{"name":"Joe","voice":"Kore"},{"name":"Jane","voice":"Puck"}]'`).option("-i, --instructions <text>","Style prompt (OpenAI gpt-4o-mini-tts): tone, accent, speed, emotion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format,n=t==="human"?ts("Generating speech...").start():null,r;e.speed&&(r=parseFloat(e.speed),(Number.isNaN(r)||r<.1||r>4)&&(n?.stop(),c("Speed must be between 0.1 and 4.0"),process.exit(d.INVALID_INPUT)));let o;if(e.speakers)try{o=JSON.parse(e.speakers),(!Array.isArray(o)||o.length===0||o.length>2||o.some(a=>!a||typeof a!="object"||typeof a.name!="string"||!a.name.trim()||typeof a.voice!="string"||!a.voice.trim()))&&(n?.stop(),c("Speakers must be an array with 1-2 name/voice items"),process.exit(d.INVALID_INPUT))}catch{n?.stop(),c(`Invalid --speakers JSON. Example: '[{"name":"Joe","voice":"Kore"}]'`),process.exit(d.INVALID_INPUT)}let i;try{let a=e.voiceId,s=e.voice||Za,l=e.provider||es,p={};o&&(p.gemini={speakers:o}),e.instructions&&(p.openai={instructions:e.instructions});let u=await Dt({text:e.text,options:{language:e.lang,provider:l,voiceId:a,voice:s,model:e.model,speed:r,providerOptions:Object.keys(p).length>0?p:void 0}});i=u.requestId,n?.stop();let w=e.output.endsWith(`.${u.format}`)?e.output:`${e.output}.${u.format}`;await Cm(km(w),{recursive:!0}),await Qa(w,u.audioData);let R=w.replace(/\.[^.]+$/,".txt");if(await Qa(R,e.text,"utf-8"),t==="json"){g({status:"completed",output:w,audioUrl:u.audioUrl,duration:u.duration,provider:u.provider,format:u.format,voice_id:u.voiceId??null,voice:u.voice??s,model:u.model??e.model??null,speed:u.speed??r??1,lang:u.language??e.lang??null,langApplied:u.langApplied??!1,timestamps:u.timestamps??null,words:u.wordTimings??null});return}if(t==="quiet"){console.log(w);return}I(`Saved to: ${w}`),m(`Duration: ${u.duration.toFixed(2)}s`),m(`Provider: ${u.provider}`),m(`Cost: $${u.cost.toFixed(6)}`)}catch(a){n?.stop(),c(ue(a,i),t),process.exit(d.GENERAL_ERROR)}}),Em=new gn("voices").description("List available voices").option("-p, --provider <provider>","Filter by provider: gemini, elevenlabs, openai").option("-f, --format <format>","Output format: human, json","human").action(async e=>{let t=e.format==="human"?ts("Fetching voices...").start():null;try{let n=await bo(e.provider);if(t?.stop(),e.format==="json"){g(n.voices);return}if(Array.isArray(n.voices)){let r=e.provider?.toUpperCase()||"VOICES";console.log(),console.log(`${r} Voices:`),console.log("-".repeat(50));for(let o of n.voices)console.log(` ${o.name} (${o.id})`),console.log(` ${o.description}`)}else for(let[r,o]of Object.entries(n.voices))if(!(!o||o.length===0)){console.log(),console.log(`${r.toUpperCase()} Voices:`),console.log("-".repeat(50));for(let i of o)console.log(` ${i.name} (${i.id})`),console.log(` ${i.description}`)}}catch(n){t?.stop(),c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),Am=new gn("status").description("Read speech generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").option("-f, --format <format>","Output format: json","json").action(async(e,t)=>{try{g(await A("generateSpeech",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),ns=new gn("tts").description("Text-to-speech commands").addCommand(Tm).addCommand(Em).addCommand(Am);import{execSync as pt,spawn as op,spawnSync as ip}from"child_process";import{access as kr,mkdir as gt,readFile as ft,rm as Er,writeFile as Ce}from"fs/promises";import{join as J,resolve as je}from"path";import{Command as B}from"commander";import ap from"ffmpeg-static";import Et from"ora";import{homedir as $m}from"os";import{join as rs}from"path";import{readdir as Om}from"fs/promises";var Pm="conceptcraft";function dt(){return rs($m(),`.${Pm}`,"projects")}function fn(e){return rs(dt(),e)}async function Rr(){try{let e=dt();return(await Om(e,{withFileTypes:!0})).filter(n=>n.isDirectory()).map(n=>n.name)}catch{return[]}}function os(e,t){let n=2,r=`${e}-${n}`;for(;t.includes(r);)n++,r=`${e}-${n}`;return r}import{Command as Nm}from"commander";import _m from"ora";import{execSync as Lm,spawnSync as lt}from"child_process";import{mkdirSync as is,existsSync as yn,readFileSync as ss,writeFileSync as as}from"fs";import{join as Ve,basename as Dm}from"path";import{tmpdir as Um}from"os";function bn(e,t){try{return Lm(t,{stdio:"ignore"}),!0}catch{return!1}}function hn(e){let t=Math.floor(e/60),n=Math.floor(e%60),r=Math.round(e%1*10);return`${t}:${String(n).padStart(2,"0")}.${r}`}function Fm(e){let t=lt("ffprobe",["-v","error","-show_entries","format=duration","-of","default=noprint_wrappers=1:nokey=1",e],{encoding:"utf-8"});return parseFloat(t.stdout.trim())}function qm(e,t){let n=lt("ffmpeg",["-i",e,"-vf",`select='gt(scene,${t})',showinfo`,"-vsync","vfr","-f","null","-"],{encoding:"utf-8",timeout:12e4}),r=[0],o=(n.stderr||"").split(`
|
|
3424
|
-
`);for(let i of o){let a=i.match(/pts_time:([\d.]+)/);if(a){let s=parseFloat(a[1]);s-r[r.length-1]>=1.5&&r.push(s)}}return r}function Vm(e,t,n){let r=[];for(let o=0;o<t.length;o++){let i=t[o]+.5,a=Ve(n,`scene_${String(o+1).padStart(2,"0")}.jpg`);lt("ffmpeg",["-ss",String(i),"-i",e,"-vframes","1","-q:v","2","-y",a],{stdio:"ignore",timeout:3e4}),r.push(a)}return r}function jm(e,t){let n=Ve(t,"audio.wav");if(lt("ffmpeg",["-i",e,"-vn","-acodec","pcm_s16le","-ar","16000","-ac","1","-y",n],{stdio:"ignore",timeout:6e4}),!yn(n)||!bn("whisper","which whisper")&&!bn("whisper-cpp","which whisper-cpp"))return null;let o=lt("whisper",[n,"--model","base","--output_format","json","--output_dir",t,"--language","en"],{encoding:"utf-8",timeout:3e5}),i=Ve(t,"audio.json");if(!yn(i))return null;try{let a=JSON.parse(ss(i,"utf-8"));return{text:a.text||"",segments:(a.segments||[]).map(s=>({start:s.start,end:s.end,text:s.text?.trim()||""}))}}catch{return null}}function Mm(e,t){let n=ss(e).toString("base64"),r=JSON.stringify({model:t,prompt:"Describe this image in one short sentence (max 15 words). Focus on: what is shown, the setting, key objects or people.",images:[n],stream:!1}),o=lt("curl",["-s","-X","POST","http://localhost:11434/api/generate","-H","Content-Type: application/json","-d",r],{encoding:"utf-8",timeout:6e4});try{return(JSON.parse(o.stdout).response||"").replace(/!!!IMAGE!!!/gi,"").replace(/\[image[^\]]*\]/gi,"").trim()||"Unable to describe frame"}catch{return"Unable to describe frame"}}var cs=new Nm("analyze").description("Analyze a video file: detect scenes, transcribe audio, describe frames").argument("<file>","Path to video file").option("-o, --output <path>","Output directory for analysis results").option("-t, --threshold <number>","Scene detection threshold (0-1, lower = more scenes)","0.3").option("-m, --model <name>","Ollama vision model","moondream").option("--no-vision","Skip frame description (faster)").option("--no-transcribe","Skip audio transcription").option("-f, --output-format <format>","Output format: human, json","human").action(async(e,t)=>{let n=_m();yn(e)||(console.error(`File not found: ${e}`),process.exit(1)),bn("ffmpeg","which ffmpeg")||(console.error("ffmpeg is required. Install: brew install ffmpeg"),process.exit(1)),t.vision!==!1&&!bn("ollama","which ollama")&&(console.error("ollama is required for vision. Install: brew install ollama"),process.exit(1));let r=t.output||Ve(Um(),`video-analyze-${Date.now()}`);is(r,{recursive:!0});let o=Ve(r,"frames");is(o,{recursive:!0});let i=parseFloat(t.threshold),a=t.model;n.start("Reading video metadata...");let s=Fm(e);n.succeed(`Video: ${hn(s)} (${s.toFixed(1)}s)`),n.start("Detecting scenes...");let l=qm(e,i);n.succeed(`Detected ${l.length} scenes`),n.start("Extracting key frames...");let p=Vm(e,l,o);n.succeed(`Extracted ${p.length} key frames`);let u=null;t.transcribe!==!1&&(n.start("Transcribing audio..."),u=jm(e,r),u?n.succeed(`Transcribed: ${u.segments.length} segments`):n.warn("Transcription skipped (whisper not installed: pip install openai-whisper)"));let w=[];for(let y=0;y<l.length;y++){let S=l[y],v=y<l.length-1?l[y+1]:s,x="";t.vision!==!1&&yn(p[y])&&(n.start(`Describing scene ${y+1}/${l.length}...`),x=Mm(p[y],a),n.succeed(`Scene ${y+1}: ${x}`)),w.push({id:y+1,startTime:S,endTime:v,duration:v-S,framePath:p[y],description:x})}let R={source:e,duration:s,scenes:w,transcript:u};if(t.outputFormat==="json"){let y=JSON.stringify(R,null,2),S=Ve(r,"analysis.json");as(S,y),console.log(y)}else{let y=["# Video Analysis","",`**Source:** ${Dm(e)}`,`**Duration:** ${hn(s)} (${s.toFixed(1)}s)`,`**Scenes:** ${w.length}`,"","## Scenes",""];for(let x of w){if(y.push(`### Scene ${x.id} (${hn(x.startTime)} - ${hn(x.endTime)}) [${x.duration.toFixed(1)}s]`),x.description&&y.push(`**Visual:** ${x.description}`),u?.segments){let C=u.segments.filter(O=>O.end>x.startTime&&O.start<x.endTime);C.length>0&&y.push(`**Audio:** ${C.map(O=>O.text).join(" ").trim()}`)}y.push(`**Frame:** ${x.framePath}`),y.push("")}u?.text&&(y.push("## Full Transcript"),y.push(""),y.push(u.text.trim()),y.push(""));let S=y.join(`
|
|
3425
|
-
`),v=Ve(r,"analysis.md");as(v,S),console.log(S),console.log(`
|
|
3426
|
-
Results saved to: ${r}`)}});import{mkdir as Bm}from"fs/promises";import{join as Gm}from"path";import{Command as Tt}from"commander";import ls from"ora";var Wm=5e3,Km=120,ds=5;function Jm(e,t){return[...t,e]}function zm(e,t){try{let n=new URL(e);if(n.protocol!=="https:"&&n.protocol!=="http:")throw new Error;return n.toString()}catch{throw new Error(`${t} must be a public HTTP(S) URL`)}}function us(e){if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e))throw new Error("Source video id must be a UUID");return e}function Xm(e){if(e.mode&&!["speed","precision"].includes(e.mode))throw new Error("Lip-sync mode must be speed or precision");return{audioUrl:zm(e.audioUrl,"Audio URL"),...e.mode?{mode:e.mode}:{},sourceVideoId:us(e.sourceVideoId)}}function Hm(e){let t=[],n=new Set;for(let o of e.targetLanguage){let i=o.trim(),a=i.toLocaleLowerCase("en-US");!i||n.has(a)||(n.add(a),t.push(i))}if(t.length===0)throw new Error("Provide at least one target language");if(t.length>ds)throw new Error(`Provide at most ${ds} target languages`);if(t.find(o=>o.length<2||o.length>40))throw new Error("Target languages must be between 2 and 40 characters");return{...e.captions?{captions:!0}:{},sourceVideoId:us(e.sourceVideoId),targetLanguages:t}}function Ym(e){return e.error||e.status==="failed"||e.status==="cancelled"?"failed":e.done||e.status==="completed"?"completed":e.status==="in_progress"||e.status==="processing"?"processing":"pending"}function Cr(e){return{requestId:e.runId,status:Ym(e),...e.entity?.url?{videoUrl:e.entity.url}:{},...e.output?{output:e.output}:{},...e.error?.message?{error:e.error.message}:{}}}function ms(e){return{requestId:e.runId,status:"pending"}}async function ps(e,t){return re(async()=>Cr(await A(e,t)),Km,Wm)}function ut(e,t){if(t==="json"){g(e);return}if(t==="quiet"){console.log(e.videoUrl??e.requestId);return}m(`Request ID: ${e.requestId}`),m(`Status: ${e.status}`),e.videoUrl&&I(`Video URL: ${e.videoUrl}`),e.error&&c(`Error: ${e.error}`)}function Qm(e){return Array.isArray(e?.languages)?e.languages.filter(t=>{if(!t||typeof t!="object"||Array.isArray(t))return!1;let n=t;return typeof n.language=="string"&&(n.status==="completed"||n.status==="failed")&&typeof n.videoId=="string"&&(n.videoUrl===null||typeof n.videoUrl=="string")}):[]}function Zm(e){return`${e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"translation"}.mp4`}function ep(e,t){let n=Zm(e);if(!t.has(n))return t.add(n),n;let r=n.slice(0,-4),o=2;for(;t.has(`${r}-${o}.mp4`);)o+=1;let i=`${r}-${o}.mp4`;return t.add(i),i}async function tp(e,t){let n=Qm(e.output).filter(i=>i.status==="completed"&&!!i.videoUrl);if(n.length===0)throw new Error("Video translation completed without downloadable results");await Bm(t,{recursive:!0});let r=[],o=new Set;for(let i of n){let a=Gm(t,ep(i.language,o));await Q(i.videoUrl,a),r.push({language:i.language,path:a})}return r}var np=new Tt("status").description("Check a video lip-sync request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{ut(Cr(await A("lipsyncVideo",e)),t.format)}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),gs=new Tt("lipsync").description("Re-voice a ready workspace video with a public audio URL").requiredOption("--source-video-id <uuid>","READY workspace video UUID").requiredOption("--audio-url <url>","Public replacement voice audio URL").option("--mode <mode>","Quality mode: speed or precision","speed").requiredOption("-o, --output <path>","Local output video path").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format==="human"?ls("Starting video lip-sync...").start():null,n;try{let r=await To(Xm(e));if(n=r.runId,!e.wait){t?.stop(),ut(ms(r),e.format);return}t&&(t.text="Waiting for video lip-sync...");let o=await ps("lipsyncVideo",r.runId);if(o.status==="failed")throw new Error(o.error??"Video lip-sync failed");if(!o.videoUrl)throw new Error("Video lip-sync completed without a video URL");t&&(t.text="Downloading lip-synced video..."),await Q(o.videoUrl,e.output),t?.stop(),ut(o,e.format),e.format==="human"&&I(`Saved to: ${e.output}`)}catch(r){t?.stop(),c(ue(r,n),e.format),process.exit(d.GENERAL_ERROR)}}).addCommand(np),fs=new Tt("translate-languages").description("List supported video translation target languages").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{try{let t=await Eo();if(e.format==="json")g({languages:t});else for(let n of t)console.log(n)}catch(t){c(t instanceof Error?t.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),rp=new Tt("status").description("Check a video translation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{try{ut(Cr(await A("translateVideo",e)),t.format)}catch(n){c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),hs=new Tt("translate").description("Translate a ready workspace video into one or more languages").requiredOption("--source-video-id <uuid>","READY workspace video UUID").option("--target-language <language>","Target language (repeatable)",Jm,[]).option("--captions","Burn provider-generated captions into translated videos").requiredOption("--output-dir <path>","Directory for translated videos").option("--no-wait","Do not wait for completion").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format==="human"?ls("Starting video translation...").start():null,n;try{let r=await Ao(Hm(e));if(n=r.runId,!e.wait){t?.stop(),ut(ms(r),e.format);return}t&&(t.text="Waiting for video translations...");let o=await ps("translateVideo",r.runId);if(o.status==="failed")throw new Error(o.error??"Video translation failed");t&&(t.text="Downloading translated videos...");let i=await tp(o,e.outputDir);if(t?.stop(),e.format==="json")g({...o,downloads:i});else if(e.format==="quiet")for(let a of i)console.log(a.path);else{ut(o,e.format);for(let a of i)I(`${a.language}: ${a.path}`)}}catch(r){t?.stop(),c(ue(r,n),e.format),process.exit(d.GENERAL_ERROR)}}).addCommand(rp);var sp="inizio-inc/remotion-video-template#main",mt=30;async function cp(e){let t=await ft(e,"utf-8");return JSON.parse(t)}async function dp(e){let t=J(e,"template.manifest.json");try{return await cp(t)}catch{return{entry:"src/Root.tsx",compositions:[{id:"Main",type:"youtube",aspect:"16:9",default:!0}]}}}function ys(){try{return pt("bun --version",{stdio:"ignore"}),!0}catch{return!1}}function lp(e){if(e.includes("---")||e.includes("[Section")){let o=e.split(/---|\[Section \d+\]/i).filter(i=>i.trim());if(o.length>1)return o.map(i=>i.trim())}let t=e.split(/(?<=[.!?])\s+/).map(o=>o.trim()).filter(o=>o.length>0),n=[],r="";for(let o of t){let i=o.split(/\s+/).length;r?(n.push(`${r} ${o}`),r=""):i<5&&n.length<t.length-1?r=o:n.push(o)}return r&&(n.length>0?n[n.length-1]+=` ${r}`:n.push(r)),n}function up(e,t,n=mt,r){if(r&&r.characters.length>0)return mp(e,r,n);let o=e.reduce((a,s)=>a+s.split(/\s+/).length,0),i=0;return e.map((a,s)=>{let l=a.split(/\s+/).length,p=l/o,u=t*p,w=Math.round(u*n),R=a.split(""),y=u/R.length,S={characters:R,characterStartTimesSeconds:R.map((x,C)=>C*y),characterEndTimesSeconds:R.map((x,C)=>(C+1)*y)},v={id:s+1,text:a,wordCount:l,startTime:i,endTime:i+u,durationInSeconds:u,durationInFrames:w,timestamps:S};return i+=u,v})}function mp(e,t,n){let{characters:r,characterStartTimesSeconds:o,characterEndTimesSeconds:i}=t,a=r.join(""),s=[],l=0;for(let p=0;p<e.length;p++){let u=e[p],w=u.length;for(;l<r.length&&r[l].match(/^\s*$/);)l++;let R=l,y=o[R]||0;l+=w;let S=l-1;for(;S>R&&r[S]?.match(/^\s*$/);)S--;let v=i[Math.min(S,i.length-1)]||y+1,x=v-y,C=Math.round(x*n),O={characters:r.slice(R,S+1),characterStartTimesSeconds:o.slice(R,S+1).map(E=>E-y),characterEndTimesSeconds:i.slice(R,S+1).map(E=>E-y)};s.push({id:p+1,text:u,wordCount:u.split(/\s+/).length,startTime:y,endTime:v,durationInSeconds:x,durationInFrames:C,timestamps:O})}return s}function pp(e){let t=e==="tiktok"?"vertical":e==="audiogram"?"square":"widescreen";return{scenes:[{name:"Hook",script:"Replace this with a bold opening line that grabs attention.",imageQuery:`cinematic ${t} intro visual, dramatic lighting`,videoQuery:`abstract ${t} motion background`},{name:"Proof",script:"Share the core insight, benefit, or product moment you want viewers to remember.",imageQuery:`people collaborating ${t} office, warm lighting`,videoQuery:`close-up hands using technology ${t}`},{name:"Close",script:"End with a crisp call to action and a confident closing line.",imageQuery:`bold typography ${t} background, high contrast`,videoQuery:`sunrise skyline ${t} timelapse`}],voice:"en-US-Neural2-C",voiceSettings:{speed:1,stability:.4,style:.6}}}async function gp(e,t,n){let r=J(e,"public"),o=J(e,"scripts");await gt(r,{recursive:!0}),await gt(o,{recursive:!0});let i=J(r,"scenes.json");await Ce(i,JSON.stringify(pp(t),null,2),"utf-8");let a=`import { execSync } from "node:child_process";
|
|
3427
|
-
|
|
3428
|
-
execSync("${n} video generate plan --project .", { stdio: "inherit" });
|
|
3429
|
-
`,s=`import { execSync } from "node:child_process";
|
|
3430
|
-
|
|
3431
|
-
const args = process.argv.slice(2).join(" ");
|
|
3432
|
-
execSync("${n} video export --project . " + args, { stdio: "inherit" });
|
|
3433
|
-
`;await Ce(J(o,"plan.ts"),a,"utf-8"),await Ce(J(o,"export.ts"),s,"utf-8")}async function Ar(){return process.stdin.isTTY?null:new Promise(e=>{let t="",n=!1;process.stdin.setEncoding("utf-8"),process.stdin.on("data",r=>{t+=r}),process.stdin.on("end",()=>{n||(n=!0,e(t.trim()||null))}),process.stdin.on("error",()=>{n||(n=!0,e(null))}),setTimeout(()=>{!n&&!t&&(n=!0,e(null))},500)})}function fp(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function hp(e){let t={shortTitle:"Video",elements:[],audio:[],text:[],scenes:[]},n=!0;return e.forEach(r=>{let o=r.startTime*1e3,i=r.endTime*1e3,a=r.durationInSeconds*1e3;t.scenes.push({name:r.name||`Scene ${r.id}`,text:r.text,startMs:o,endMs:i}),r.imagePath?(t.elements.push({startMs:o,endMs:i,imageUrl:r.imagePath,enterTransition:"blur",exitTransition:"blur",animations:[{type:"scale",from:n?1.3:1,to:n?1:1.3,startMs:0,endMs:a}]}),n=!n):r.videoPath&&t.elements.push({startMs:o,endMs:i,videoUrl:r.videoPath,enterTransition:"blur",exitTransition:"blur",animations:[]}),r.audioPath&&t.audio.push({startMs:o,endMs:i,audioUrl:r.audioPath}),r.timestamps&&t.text.push({startMs:o,endMs:i,text:r.text,position:"bottom",animations:[],timestamps:r.timestamps})}),t}async function Tr(e,t){if(e.startsWith("data:")){let o=e.match(/^data:[^;]+;base64,(.+)$/);if(!o)throw new Error("Invalid data URL format");let i=Buffer.from(o[1],"base64");await Ce(t,i);return}let n=await fetch(e);if(!n.ok)throw new Error(`Failed to download: ${n.status}`);let r=await n.arrayBuffer();await Ce(t,Buffer.from(r))}function yp(e){try{let r=new URL(e).pathname.split(".").pop()?.toLowerCase();if(r&&["jpg","jpeg","png","gif","webp"].includes(r))return r}catch{}return"jpg"}var bp=new B("assets").description("Generate early assets (TTS, images, and video; music/SFX follow final timing)").option("-s, --script <text>","Narration script (legacy single-script mode)").option("--script-file <path>","Path to script file (legacy) or scenes JSON").option("--scenes <path>","Path to scenes.json (defaults to <project>/public/scenes.json)").option("-p, --project <dir>","Project directory (defaults to current directory; uses <dir>/public for output)").option("-t, --topic <text>","Topic for image search").option("-v, --voice <name>","TTS voice ID (ElevenLabs: Rachel, Josh, Adam; OpenAI: alloy, nova; Gemini: Kore, Puck)").option("-n, --num-images <number>","Number of images to search/download","5").option("-o, --output <dir>","Output directory","./public").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format,n=t==="human"?Et("Initializing...").start():null,r=e.project?je(process.cwd(),e.project):null;try{let o=await Ar(),i=null;if(e.scenes&&!e.scriptFile&&(e.scriptFile=e.scenes),r&&(e.output=J(r,"public"),e.scriptFile?e.scriptFile=je(r,e.scriptFile):!e.script&&!o&&(e.scriptFile=J(r,"public","scenes.json"))),o)try{let $=JSON.parse(o);$.scenes&&Array.isArray($.scenes)&&(i=$)}catch{}if(!i&&e.scriptFile)try{let $=await ft(e.scriptFile,"utf-8"),X=JSON.parse($);X.scenes&&Array.isArray(X.scenes)&&(i=X)}catch{}let a=i?.voice||e.voice,s=i?.voiceId,l=i?.provider,p=i?.model,u=i?.voiceInstructions,w=J(e.output,"audio"),R=J(e.output,"images"),y=J(e.output,"videos");n&&(n.text="Creating directories..."),await gt(w,{recursive:!0}),await gt(R,{recursive:!0}),await gt(y,{recursive:!0});let S=0,v=[],x=[],C=[];if(i&&i.scenes.length>0){t==="human"&&(n?.stop(),m(`Processing ${i.scenes.length} scenes...`),n?.start());let $=i.scenes.map((ne,j)=>{let ve=ne.script;return ne.voiceStyle&&(ve=`${ne.voiceStyle} ${ve}`),{text:ve,id:`scene-${j}`}});n&&(n.text="Generating speech for all scenes...");let X=await yo({texts:$,options:{provider:l,voice:a,voiceId:s,model:p,voiceSettings:i.voiceSettings,instructions:u}});S+=X.totalCost;let se=0;for(let ne=0;ne<i.scenes.length;ne++){let j=i.scenes[ne],ve=fp(j.name),ge=X.results[ne];n&&(n.text=`[${j.name}] Saving audio...`);let xn=J(w,`${ve}.${ge.format}`);await Ce(xn,ge.audioData);let ht=ge.duration,Rs=Math.round(ht*mt),yt={id:ne+1,name:j.name,text:j.script,wordCount:j.script.split(/\s+/).length,startTime:se,endTime:se+ht,durationInSeconds:ht,durationInFrames:Rs,audioPath:`audio/${ve}.${ge.format}`,timestamps:ge.timestamps};if(j.imageQuery){n&&(n.text=`[${j.name}] Searching image...`);try{let Z=await Ut({query:j.imageQuery,options:{maxResults:1,size:"large",safeSearch:!0,sources:["stock"]}}),bt=Z.data.results.flatMap(H=>H.results);if(S+=Z.data.totalCost,bt.length>0){let H=bt[0],wt=yp(H.url),Pe=`${ve}.${wt}`,Sn=J(R,Pe);await Tr(H.url,Sn),yt.imagePath=`images/${Pe}`,x.push({path:`images/${Pe}`,url:H.url,width:H.width,height:H.height,query:j.imageQuery})}}catch(Z){t==="human"&&(n?.stop(),q(`[${j.name}] Image search failed: ${Z instanceof Error?Z.message:"Unknown"}`),n?.start())}}if(j.videoQuery){n&&(n.text=`[${j.name}] Searching video...`);try{let Z=await Nn({query:j.videoQuery,options:{maxResults:1,license:"free",kinds:["file"]}}),bt=Z.data.results.flatMap(H=>H.results);if(S+=Z.data.totalCost,bt.length>0){let H=bt[0],wt=H.previewUrl||H.downloadUrl;if(wt){let Pe=`${ve}.mp4`,Sn=J(y,Pe);await Tr(wt,Sn),yt.videoPath=`videos/${Pe}`,C.push({path:`videos/${Pe}`,url:wt,width:H.width,height:H.height,duration:H.duration,query:j.videoQuery})}}}catch(Z){t==="human"&&(n?.stop(),q(`[${j.name}] Video search failed: ${Z instanceof Error?Z.message:"Unknown"}`),n?.start())}}if(v.push(yt),se+=ht,t==="human"){n?.stop();let Z=[`audio: ${ht.toFixed(1)}s`,yt.imagePath?"image":null,yt.videoPath?"video":null].filter(Boolean).join(", ");I(` ${j.name}: ${Z}`),n?.start()}}}else{let $=e.script;if(e.scriptFile)try{$=await ft(e.scriptFile,"utf-8")}catch(ge){n?.stop(),c(`Failed to read script file: ${ge instanceof Error?ge.message:"Unknown error"}`),process.exit(d.INVALID_INPUT)}(!$||$.trim().length===0)&&(n?.stop(),c("Provide scenes via stdin JSON, --scenes/--script-file with scenes JSON, or --script for legacy mode"),process.exit(d.INVALID_INPUT)),$=$.trim();let X=e.topic||$.split(".")[0].slice(0,50);n&&(n.text="Generating voiceover...");let se=await Dt({text:$,options:{voice:a}}),ne=J(w,`voiceover.${se.format}`);await Ce(ne,se.audioData),S+=se.cost;let j=lp($);v=up(j,se.duration,mt,se.timestamps).map((ge,xn)=>({...ge,name:`Section${xn+1}`,audioPath:`audio/voiceover.${se.format}`})),t==="human"&&(n?.stop(),I(`Voiceover: ${ne} (${se.duration.toFixed(1)}s)`),n?.start())}n&&(n.text="Creating timeline...");let O=hp(v),L=Math.max(O.audio.length>0?Math.max(...O.audio.map($=>$.endMs)):0,O.text.length>0?Math.max(...O.text.map($=>$.endMs)):0,O.elements.length>0?Math.max(...O.elements.map($=>$.endMs)):0)/1e3,k;n&&(n.text="Generating thumbnail...");let z=`YouTube thumbnail style: BIG BOLD short headline text (like "$50K/MONTH"), dramatic engaging scene, arrows pointing to key elements, high contrast, vibrant colors, clean composition, 16:9. Video context: ${v.map($=>$.text).join(" ")}`,pe=await(async()=>{try{let $=await xo({prompt:z,options:{width:1280,height:720}});if($.success&&$.data.url){let X=J(e.output,"thumbnail.jpg");return await Tr($.data.url,X),S+=$.data.cost||0,X}return null}catch($){return t==="human"&&(n?.stop(),q(`Thumbnail generation failed: ${$.message}`),n?.start()),null}})();pe&&(k=pe,t==="human"&&(n?.stop(),I(`Thumbnail: ${k}`),n?.start())),n&&(n.text="Writing manifest...");let wn=Math.round(L*mt),Pr={thumbnail:k?"thumbnail.jpg":void 0,images:x,videos:C,scenes:v,timeline:O,totalDurationInFrames:wn,fps:mt,totalCost:S,createdAt:new Date().toISOString()},vn=J(e.output,"video-manifest.json");if(await Ce(vn,JSON.stringify(Pr,null,2)),n?.stop(),t==="json"){let{totalCost:$,...X}=Pr;g(X);return}if(t==="quiet"){console.log(vn);return}console.log(),I("Video assets created successfully!"),console.log(),m(`Scenes: ${v.length} (${wn} frames at ${mt}fps)`);for(let $ of v){let X=[$.audioPath?"audio":null,$.imagePath?"image":null,$.videoPath?"video":null].filter(Boolean).join(", ");m(` - ${$.name}: ${$.durationInSeconds.toFixed(1)}s [${X}]`)}m("Music/SFX: generate after final timing with --request"),m(`Manifest: ${vn}`),console.log(),m(`Total cost: $${S.toFixed(4)}`)}catch(o){n?.stop(),c(o instanceof Error?o.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}});function wp(e){let t=e?.trim().toLowerCase();if(!(!t||t==="any"||t==="all"))return t==="web"||t==="stock"?[t]:void 0}var vp=new B("find").description("Find video clips across the web and licensed stock").argument("<query>","Search query").option("-n, --max-results <count>","Maximum number of results","10").option("-o, --orientation <type>","Video orientation: landscape, portrait, square, any","any").option("-l, --license <type>","License type: free, premium, any","free").option("--source <source>","Catalog: web, stock, or any (default: any)","any").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let{maxResults:n,orientation:r,license:o,format:i}=t,a=i==="human"?Et("Finding videos...").start():null;try{let s=await Nn({query:e,options:{maxResults:parseInt(n,10),orientation:r,license:o,sources:wp(t.source)}});a?.stop();let l=s.data.results.flatMap(p=>p.results);if(i==="json"){let{totalCost:p,...u}=s.data;g({...s,data:u});return}if(i==="quiet"){l.forEach(p=>{console.log(p.embedUrl||p.previewUrl||p.thumbnailUrl)});return}if(l.length===0){m("No videos found");return}I(`Found ${l.length} videos for "${e}"`),console.log(),l.forEach((p,u)=>{let w=p.kind==="embed";console.log(`[${u+1}] ${p.title}`),console.log(` ${w?"Embed":"URL"}: ${p.embedUrl||p.previewUrl||p.thumbnailUrl}`),console.log(w?` Duration: ${p.duration}s | Embed only \u2014 plays in the host player, do not download`:` Duration: ${p.duration}s | Size: ${p.width}x${p.height}`),console.log(` Provider: ${p.provider}`),console.log()}),m(`Total cost: $${s.data.totalCost.toFixed(4)}`)}catch(s){a?.stop(),c(s instanceof Error?s.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),xp=new B("new").description("Create a new video project from a template").argument("<name>","Project directory name").option("-t, --template <repo>","GitHub repo (user/repo#commit)",sp).option("--type <type>","Video type: youtube (16:9), tiktok (9:16), or audiogram (1:1)","youtube").option("--no-install","Skip dependency install").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(e,t)=>{let n=t.format,r=n==="human"?Et("Initializing video project...").start():null;try{let o=dt();await gt(o,{recursive:!0});let i=fn(e),a=t.type,s=await Rr();if(s.includes(e)){let y=os(e,s);r?.stop(),c(`Project "${e}" already exists at: ${i}`),m(`Try: ${b.commands[0]} video generate new ${y}`),process.exit(d.INVALID_INPUT)}/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+(#[a-zA-Z0-9_./-]+)?$/.test(t.template)||(r?.stop(),c(`Invalid template format: "${t.template}". Expected format: owner/repo or owner/repo#branch`),process.exit(d.INVALID_INPUT)),["youtube","tiktok","audiogram"].includes(a)||(r?.stop(),c(`Invalid type "${a}". Use youtube, tiktok, or audiogram.`),process.exit(d.INVALID_INPUT)),r&&(r.text=`Cloning template from ${t.template}...`);let[p,u]=t.template.split("#"),w=u?`-b ${u}`:"";try{pt(`git clone ${w} --depth 1 https://github.com/${p}.git "${i}"`,{stdio:"pipe"}),await Er(J(i,".git"),{recursive:!0,force:!0})}catch(y){r?.stop(),c(`Failed to clone template: ${y instanceof Error?y.message:"Unknown error"}`),process.exit(d.GENERAL_ERROR)}n==="human"&&(r?.stop(),I(`Template downloaded to ${e}/`),r?.start());let R;try{R=await dp(i)}catch{r?.stop(),c("template.manifest.json not found or invalid in template repo"),process.exit(d.INVALID_INPUT)}if(R.propsSchema&&R.propsSchema!=="video-plan.v1"&&(r?.stop(),c(`Template propsSchema must be video-plan.v1 (found ${R.propsSchema})`),process.exit(d.INVALID_INPUT)),r&&(r.text="Writing scaffold files..."),await gp(i,a,b.commands[0]),t.install){let y=ys()?"bun":"npm";r&&(r.text=`Installing dependencies with ${y}...`),await new Promise((S,v)=>{let x=op(y,["install"],{cwd:i,stdio:"pipe",shell:!0});x.on("close",C=>{C===0?S():v(new Error(`${y} install failed with code ${C}`))}),x.on("error",v)}),n==="human"&&(r?.stop(),I("Dependencies installed"),r?.start())}if(r?.stop(),n==="json"){g({name:e,path:i,template:t.template,type:a,installed:t.install});return}if(n==="quiet"){console.log(i);return}console.log(),I(`Video project "${e}" created successfully!`),m(`Location: ${i}`),a==="tiktok"?m("Format: TikTok/Reels/Shorts (1080x1920 @ 30fps)"):a==="audiogram"?m("Format: Audiogram (1080x1080 @ 30fps)"):m("Format: YouTube (1920x1080 @ 30fps)"),console.log(),m("Next steps:"),m(` cd ${i}`),t.install||m(` ${ys()?"bun":"npm"} install`),m(" bun run dev # Preview in Remotion Studio"),m(` ${b.commands[0]} video generate assets --scenes public/scenes.json`),m(` ${b.commands[0]} video generate plan`)}catch(o){r?.stop(),c(o instanceof Error?o.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),Sp=new B("plan").description("Generate video plan from context (via stdin or --context)").option("-c, --context <text>","Context for plan generation").option("-o, --output <path>","Save plan to file").option("-b, --branding <path>","Branding JSON file to include").option("--render-target <target>","Plan target: editable composition or remotion","editable-composition").option("--no-wait","Return the background run ID immediately").action(async e=>{let t=Et("Generating video plan...").start();try{let n=e.context;if(!n){let l=await Ar();l&&(n=l)}n||(t.stop(),c("Provide context via stdin or --context flag"),process.exit(d.INVALID_INPUT));let r;if(e.branding)try{r=await ft(e.branding,"utf-8")}catch{t.stop(),c(`Could not read branding file: ${e.branding}`),process.exit(d.INVALID_INPUT)}let o=e.renderTarget==="remotion"?"remotion":"hyperframes",i={context:n,branding:r,renderTarget:o};if(e.wait===!1){t.stop(),g(await Dn(i));return}let a=await No(i);t.stop(),a.success||(c(a.error||"Failed to generate plan"),process.exit(d.GENERAL_ERROR));let s=a.data;e.output?(await Ce(e.output,s),I(`Plan saved to ${e.output}`)):console.log(s)}catch(n){t.stop(),c(n instanceof Error?n.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),Ip=new B("generate").description("Generate parts of a video project").addCommand(xp).addCommand(bp).addCommand(Sp),Rp=new B("create").description("Generate one video from a pinned storyboard revision").requiredOption("--storyboard-revision <id>","Immutable storyboard revision UUID").option("--source-video <id>","Ready video from an earlier create call to use as base footage").option("--draft","Generate at 480p for a faster draft").option("--team-id <id>","Workspace ID").option("--approval-receipt <receipt>","Receipt returned by the browser confirmation URL; omit on the first call to get that URL").requiredOption("--idempotency-key <key>","Stable invocation key bound into the approval receipt").option("--yes","Approve paid video generation").action(async e=>{if(!e.yes)return c("Pass --yes to approve video generation");try{g(await $o({quality:e.draft?"draft":void 0,sourceVideoId:e.sourceVideo,storyboardRevisionId:e.storyboardRevision,teamId:e.teamId},e.approvalReceipt,e.idempotencyKey))}catch(t){c(t instanceof Error?t.message:"Generation failed"),process.exit(d.GENERAL_ERROR)}});async function bs(e){let t=e==="-"?await Ar():await ft(e,"utf-8");if(!t?.trim())throw new Error(e==="-"?"No storyboard Markdown was received on stdin":`Storyboard file is empty: ${e}`);return t}async function ws(e,t={}){if(e)return e.length===1&&e[0]==="none"?[]:Promise.all(e.map(async n=>{let r;try{let R=JSON.parse(n);if(!R||typeof R!="object"||Array.isArray(R))throw new Error("Media binding must be a JSON object");r=R}catch(R){throw new Error(`Invalid --media binding. Pass one JSON object per binding, for example '{"handle":"actress-photo","path":"./actress.jpg","purpose":"identity"}'.`,{cause:R})}let o=we(r.handle),i=we(r.path),a=we(r.fileUrl)??we(r.url),s=we(r.purpose),l=we(r.videoRole),p=we(r.analysisId)??we(r.analysis);if(!o||!i&&!a||!s)throw new Error("Invalid --media binding. handle, purpose, and either path or url are required strings.");if(i&&a)throw new Error("A media binding cannot contain both path and url.");if(i&&s==="source"&&p)throw new Error("A source analysis is bound to the original uploaded URL. Reuse the url/name/mime/size binding printed by 'video reference analyze' instead of uploading path again.");if(i&&!t.uploadIdempotencyKey)throw new Error("Local storyboard media requires the command idempotency key.");let u=i?await he(je(i),{idempotencyKey:`${t.uploadIdempotencyKey}:media:${o}`}):{name:we(r.name),size:Number(r.size),type:we(r.mime),url:a};if(!u.name||!u.type||!u.url||!Number.isSafeInteger(u.size)||u.size<=0)throw new Error(`URL media binding "${n}" requires name, mime, and positive integer size.`);let w=u.type.startsWith("video/")?"video":u.type.startsWith("audio/")?"audio":u.type.startsWith("image/")?"image":null;if(!w)throw new Error(`Unsupported media type for ${i??a}: ${u.type}`);if(!["source","style","identity","required"].includes(s))throw new Error(`Invalid media purpose: ${s}`);if(l&&!["reference","first-frame","last-frame"].includes(l))throw new Error(`Invalid videoRole: ${l}`);if(l&&w!=="image")throw new Error("videoRole is supported only for image bindings.");return{...p?{analysisId:p}:{},fileUrl:u.url,handle:o,kind:w,mime:u.type,name:u.name,purpose:s,size:u.size,...l?{videoRole:l}:{}}}))}function we(e){return typeof e=="string"&&e.length>0?e:void 0}var Cp=new B("create").description("Create a versioned Markdown video storyboard").requiredOption("--file <path>","Markdown file path, or - for stdin").requiredOption("--duration <seconds>","Exact total duration in seconds").requiredOption("--aspect <ratio>","Output aspect: 9:16, 16:9, or 1:1").option("--title <text>","Artifact label").requiredOption("--idempotency-key <key>","Stable key to reuse when retrying this exact create").option("--media <binding...>",'One JSON binding per asset: {"handle":"actress-photo","path":"./actress.jpg","purpose":"identity"}').option("--team-id <id>","Workspace ID").action(async e=>{try{g(await h("createVideoStoryboard",{aspectRatio:e.aspect,durationSec:Number(e.duration),mediaBindings:await ws(e.media,{uploadIdempotencyKey:e.idempotencyKey}),storyboardMarkdown:await bs(e.file),teamId:e.teamId,title:e.title},e.idempotencyKey))}catch(t){c(t instanceof Error?t.message:"Storyboard creation failed"),process.exit(d.GENERAL_ERROR)}}),kp=new B("revise").description("Append an immutable revision to a video storyboard").argument("<storyboard-id>","Storyboard UUID").requiredOption("--base-revision <id>","Revision UUID that was edited").requiredOption("--file <path>","Complete Markdown file path, or - for stdin").requiredOption("--duration <seconds>","Exact total duration in seconds").requiredOption("--aspect <ratio>","Output aspect: 9:16, 16:9, or 1:1").option("--title <text>","Replacement artifact label").requiredOption("--idempotency-key <key>","Stable key to reuse when retrying this exact revision").option("--change-summary <text>","Short description of this revision").option("--media <binding...>",'Replace media bindings with one JSON object per asset; use the JSON printed by "video reference analyze" for analyzed source video').option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await h("reviseVideoStoryboard",{aspectRatio:t.aspect,baseRevisionId:t.baseRevision,changeSummary:t.changeSummary,durationSec:Number(t.duration),mediaBindings:await ws(t.media,{uploadIdempotencyKey:t.idempotencyKey}),storyboardId:e,storyboardMarkdown:await bs(t.file),teamId:t.teamId,title:t.title},t.idempotencyKey))}catch(n){c(n instanceof Error?n.message:"Storyboard revision failed"),process.exit(d.GENERAL_ERROR)}}),Tp=new B("show").description("Read a storyboard; print Markdown unless --json is used").argument("<storyboard-id>","Storyboard UUID").option("--revision <id>","Read one immutable revision UUID").option("--team-id <id>","Workspace ID").option("--json","Print the complete structured response").action(async(e,t)=>{try{let n=await h("readVideoStoryboard",{revisionId:t.revision,storyboardId:e,teamId:t.teamId});if(t.json){g(n);return}console.error(`${n.storyboard.title} \xB7 revision ${n.storyboard.revision.versionNumber} (${n.storyboard.revision.id})`),console.log(n.storyboard.revision.storyboardMarkdown)}catch(n){c(n instanceof Error?n.message:"Storyboard read failed"),process.exit(d.GENERAL_ERROR)}}),Ep=new B("storyboard").description("Create, revise, and read Markdown video storyboards").addCommand(Cp).addCommand(kp).addCommand(Tp),Ap=new B("analyze").description("Upload and analyze a reference video").requiredOption("--file <path>","Local reference video").requiredOption("--idempotency-key <key>","Stable key to reuse when retrying this exact analysis").option("--user-request <text>","What the analysis should focus on").option("--team-id <id>","Workspace ID").option("--no-wait","Return the durable run immediately").option("--json","Print the complete structured result").action(async e=>{try{let t=await he(je(e.file),{idempotencyKey:`video-reference-analysis:${e.idempotencyKey}:source`});if(!t.type.startsWith("video/"))throw new Error(`Reference analysis requires a video, got ${t.type}`);let n=await Oo({idempotencyKey:e.idempotencyKey,sourceUrl:t.url,teamId:e.teamId,userRequest:e.userRequest}),r={analysisId:n.runId,fileUrl:t.url,handle:"source-video",kind:"video",mime:t.type,name:t.name,purpose:"source",size:t.size};if(e.wait===!1){g({...n,binding:r,source:t});return}let o=await Po(n.runId,e.teamId);if(e.json){g({...o,binding:r,source:t});return}let i=o.output?.analysis??{};console.log(typeof i.promptContext=="string"?i.promptContext:JSON.stringify(o.output,null,2)),console.error(`Analysis run: ${n.runId}`),console.error(`Storyboard media JSON: ${JSON.stringify(r)}`)}catch(t){c(t instanceof Error?t.message:"Reference analysis failed"),process.exit(d.GENERAL_ERROR)}}),$p=new B("status").description("Read a video-reference analysis run").argument("<run-id>","Analysis run UUID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A("analyzeVideoReference",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status read failed"),process.exit(d.GENERAL_ERROR)}}),Op=new B("reference").description("Upload and inspect reusable video references").addCommand(Ap).addCommand($p),vs=new B("plan").description("Start background video-plan generation").requiredOption("--context <brief>","Video brief (minimum 100 characters)").option("--branding <path>","Branding JSON file").option("--render-target <target>","editable composition or remotion","editable-composition").option("--yes","Approve generation").action(async e=>{if(!e.yes)return c("Pass --yes to approve plan generation");try{let t=e.branding?await ft(e.branding,"utf-8"):void 0;g(await Dn({branding:t,context:e.context,renderTarget:e.renderTarget==="remotion"?"remotion":"hyperframes"}))}catch(t){c(t instanceof Error?t.message:"Plan generation failed"),process.exit(d.GENERAL_ERROR)}}),Pp=new B("status").description("Read video or video-plan background status").argument("<run-id>","Run ID").option("--plan","Read a video-plan run").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A(t.plan?"generateVideoPlan":"createVideo",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),Np=new B("status").description("Read video-plan background status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A("generateVideoPlan",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}});vs.addCommand(Np);var _p=new B("projects").description("List all video projects").option("-f, --format <format>","Output format: human, json","human").action(async e=>{let t=await Rr();if(e.format==="json"){g({projectsDir:dt(),projects:t.map(n=>({name:n,path:fn(n)}))});return}if(t.length===0){m("No video projects found"),m(`Projects are stored in: ${dt()}`);return}console.log(`
|
|
3434
|
-
Video Projects:`),console.log("-".repeat(50));for(let n of t)console.log(` ${n}`),console.log(` ${fn(n)}`);console.log()}),Lp=new B("inject").description("Inject thumbnail into video (first frame + file icon)").requiredOption("-i, --video <path>","Input video file").requiredOption("-t, --thumbnail <path>","Thumbnail image").option("-o, --output <path>","Output video (default: video-with-thumb.mp4)").option("-f, --format <format>","Output format: human, json, quiet","human").action(async e=>{let t=e.format,n=t==="human"?Et("Injecting thumbnail...").start():null;try{let r=je(e.video),o=je(e.thumbnail),i=e.output?je(e.output):r.replace(/\.mp4$/,"-with-thumb.mp4");await kr(r),await kr(o);let a=ap;if(a)try{await kr(a)}catch{a=null}if(!a)try{a=pt("which ffmpeg",{encoding:"utf8"}).trim()}catch{throw new Error("ffmpeg not found. Install ffmpeg or ffmpeg-static.")}let s=ip(a,["-i",r],{encoding:"utf8"});if(s.error)throw new Error(`Failed to probe video: ${s.error.message}`);let l=(s.stderr??"").match(/(\d{2,5})x(\d{2,5})/);if(!l)throw new Error("Could not detect video dimensions");let[,p,u]=l;n&&(n.text="Creating thumbnail frame...");let w="/tmp/cc-thumb-frame.mp4";pt(`${a} -y -loop 1 -i "${o}" -vf "scale=${p}:${u}:force_original_aspect_ratio=decrease,pad=${p}:${u}:(ow-iw)/2:(oh-ih)/2,setsar=1" -t 0.033 -r 30 -c:v libx264 -pix_fmt yuv420p "${w}"`,{stdio:"pipe"}),n&&(n.text="Concatenating with video...");let R="/tmp/cc-concat-video.mp4";if(pt(`${a} -y -i "${w}" -i "${r}" -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" -map 1:a -c:v libx264 -preset ultrafast -crf 18 -c:a copy "${R}"`,{stdio:"pipe"}),n&&(n.text="Adding file icon thumbnail..."),pt(`${a} -y -i "${R}" -i "${o}" -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic "${i}"`,{stdio:"pipe"}),await Er(w,{force:!0}),await Er(R,{force:!0}),n?.stop(),t==="json"){g({success:!0,output:i});return}if(t==="quiet"){console.log(i);return}I(`Thumbnail injected: ${i}`)}catch(r){n?.stop(),c(r instanceof Error?r.message:"Unknown error"),process.exit(d.GENERAL_ERROR)}}),Dp=new B("thumbnail").description("Thumbnail operations").addCommand(Lp),$r=new B("video").description("Video generation commands").addCommand(Ip).addCommand(vp).addCommand(_p).addCommand(Dp).addCommand(gs).addCommand(fs).addCommand(hs).addCommand(Ma).addCommand(cs);$r.addCommand(Rp).addCommand(Ep).addCommand(Op).addCommand(vs).addCommand(Pp);import{Command as Or}from"commander";var Up=new Or("create").description("Start background visual-abstract generation").argument("<brief>","Dense source brief").option("--title <title>","Artifact title").option("--aspect <ratio>","1:1, 16:9, 9:16, 4:3, or 3:4","1:1").option("--quality <tier>","fast, best, or enterprise","best").option("--preset <preset>","Output preset","executive-one-pager").option("--output <type>","raster or vector","raster").option("--branding-id <id>","Branding ID").option("--reference-url <url...>","Up to three image URLs to use as visual references").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(e,t)=>{if(!t.yes)return c("Pass --yes to approve visual-abstract generation");try{let n=Array.isArray(t.referenceUrl)?t.referenceUrl.slice(0,3).map((r,o)=>({label:`visual-reference-${o+1}`,url:r})):void 0;g(await h("createVisualAbstract",{approvalReceipt:"cli:--yes",aspectRatio:t.aspect,brandingId:t.brandingId,brief:e,outputPreset:t.preset,outputType:t.output,qualityTier:t.quality,referenceImages:n,teamId:t.teamId,title:t.title}))}catch(n){c(n instanceof Error?n.message:"Generation failed"),process.exit(d.GENERAL_ERROR)}}),Fp=new Or("status").description("Read visual-abstract generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(e,t)=>{try{g(await A("createVisualAbstract",e,t.teamId))}catch(n){c(n instanceof Error?n.message:"Status failed"),process.exit(d.GENERAL_ERROR)}}),xs=new Or("visual-abstract").description("Visual abstract commands").addCommand(Up).addCommand(Fp);var qp=[la,pa,ra,ns,Na,Sa,fa,$r,Ja,Ya,Ha,Ka,Xa,ca,ha,ya,La,Wi,xs,xa,Mi,Hi,va,aa,ma];function Ss(e){for(let t of qp)e.addCommand(t)}var jp="0.3.16",W=new Vp,ke=b.commands[0];W.name(ke).description(b.description).version(jp,"-v, --version","Show version number").option("--debug","Enable debug logging").option("--no-color","Disable colored output").configureOutput({outputError:(e,t)=>{t(me.red(e))}});W.addCommand(ci);W.addCommand(di);W.addCommand(Jo);W.addCommand(ri);W.addCommand(zo);W.addCommand(Zo);W.addCommand(ni);W.addCommand(qi);W.addCommand(ei);W.addCommand(Vi);W.addCommand(it);W.addCommand(Kt);W.addCommand(jo);Ss(W);var Is=Yo();Is.commands.length>0&&W.addCommand(Is);W.on("command:*",e=>{console.error(me.red(`Error: Unknown command '${e[0]}'`)),console.error(),console.error(`Run '${ke} --help' to see available commands.`),process.exit(1)});W.addHelpText("after",`
|
|
3435
|
-
${me.bold("Examples:")}
|
|
3436
|
-
${me.gray("# Log in (opens browser)")}
|
|
3437
|
-
$ ${ke} login
|
|
3438
|
-
|
|
3439
|
-
${me.gray("# Create a presentation (recommended pattern)")}
|
|
3440
|
-
$ ${ke} create "Q4 Business Review" \\
|
|
3441
|
-
-n 12 -m best \\
|
|
3442
|
-
--audience "Executive leadership team" \\
|
|
3443
|
-
--context "Revenue: $50M (+25% YoY), New customers: 150"
|
|
3444
|
-
|
|
3445
|
-
${me.gray("# Create from a file")}
|
|
3446
|
-
$ ${ke} create "Product Launch" \\
|
|
3447
|
-
-n 10 -m fast \\
|
|
3448
|
-
--audience "Sales team and partners" \\
|
|
3449
|
-
--context-file ./launch-brief.md
|
|
3450
|
-
|
|
3451
|
-
${me.gray("# Create from URLs")}
|
|
3452
|
-
$ ${ke} create "Market Analysis" \\
|
|
3453
|
-
-n 15 -m best \\
|
|
3454
|
-
--audience "Strategy team" \\
|
|
3455
|
-
--sources https://example.com/report.pdf
|
|
3456
|
-
|
|
3457
|
-
${me.gray("# List and export")}
|
|
3458
|
-
$ ${ke} list --format table
|
|
3459
|
-
$ ${ke} export <slug> -o backup.zip
|
|
3460
|
-
|
|
3461
|
-
${me.bold("Authentication:")}
|
|
3462
|
-
Run '${ke} login' to authenticate (recommended).
|
|
2431
|
+
`}var Qe=["main","video","presentation","presentation-edit","tts","music","sfx","mix","image","video-search","scrape","branding","config"],ln=["standalone","studio"],rs={cmd:u.commands[0],pkg:u.packageName,url:u.apiUrl,name:u.name},Ze=new $p("skill").description(`Manage ${u.displayName} skills for AI coding assistants`).addHelpText("after",`
|
|
2432
|
+
${q.bold("Skill Types:")}
|
|
2433
|
+
${q.cyan("main")} Main CLI skill with all capabilities overview
|
|
2434
|
+
${q.cyan("video")} Video-from-scratch pipeline (storyboard \u2192 render)
|
|
2435
|
+
${q.cyan("presentation")} Detailed presentation creation workflow
|
|
2436
|
+
${q.cyan("presentation-edit")} In-app presentation editing (Studio)
|
|
2437
|
+
|
|
2438
|
+
${q.bold("Reference Skills:")}
|
|
2439
|
+
${q.cyan("tts")} Text-to-speech generation (voices, providers, options)
|
|
2440
|
+
${q.cyan("music")} AI music generation (prompts, styles, duration)
|
|
2441
|
+
${q.cyan("sfx")} Sound effects generation (categories, looping, formats)
|
|
2442
|
+
${q.cyan("mix")} Audio mixing (video + voice + music)
|
|
2443
|
+
${q.cyan("image")} Image search and AI generation
|
|
2444
|
+
${q.cyan("video-search")} Stock video search
|
|
2445
|
+
${q.cyan("scrape")} URL content extraction
|
|
2446
|
+
${q.cyan("branding")} Brand profile management
|
|
2447
|
+
${q.cyan("config")} CLI configuration management
|
|
2448
|
+
|
|
2449
|
+
${q.bold("Examples:")}
|
|
2450
|
+
${q.gray("# Install main CLI skill (comprehensive overview)")}
|
|
2451
|
+
$ ${u.commands[0]} skill install main
|
|
2452
|
+
|
|
2453
|
+
${q.gray("# Install video skill")}
|
|
2454
|
+
$ ${u.commands[0]} skill install video
|
|
2455
|
+
|
|
2456
|
+
${q.gray("# Install presentation skill")}
|
|
2457
|
+
$ ${u.commands[0]} skill install presentation
|
|
2458
|
+
|
|
2459
|
+
${q.gray("# Install all skills")}
|
|
2460
|
+
$ ${u.commands[0]} skill install
|
|
2461
|
+
|
|
2462
|
+
${q.gray("# Install to specific directory")}
|
|
2463
|
+
$ ${u.commands[0]} skill install main --dir ~/.claude
|
|
2464
|
+
|
|
2465
|
+
${q.gray("# Show skill content")}
|
|
2466
|
+
$ ${u.commands[0]} skill show main
|
|
2467
|
+
`);Ze.command("install").description(`Install ${u.displayName} skills for AI coding assistants`).argument("[type]","Skill type: main, video, presentation, or omit for all").option("-d, --dir <path>","Install to specific directory").option("-g, --global","Install globally (to home directory)",!0).option("-l, --local","Install locally (to current directory)").option("-f, --force","Overwrite existing skill files").option("-m, --mode <mode>","Skill mode: standalone (render to file) or studio (Studio Preview)","standalone").action(async(e,t)=>{t.mode&&!ln.includes(t.mode)&&(d(`Invalid mode: ${t.mode}. Must be one of: ${ln.join(", ")}`),process.exit(1));let n={...rs,mode:t.mode},r=[];e&&!Qe.includes(e)&&(d(`Invalid skill type: ${e}. Must be one of: ${Qe.join(", ")}`),process.exit(1));let o=await nt();o.source==="snapshot"&&(M(`Live capability catalog unavailable (${o.reason??"unknown reason"}) \u2014 generating from the snapshot baked into this CLI.`),console.log(q.gray(` Run \`${u.commands[0]} login\` then re-run with --force to regenerate from the live catalog.`)));let i=[...za(n,o,{branding:He(n)}),{name:"tts",content:Ge(n)},{name:"music",content:Be(n)},{name:"sfx",content:We(n)},{name:"mix",content:Ke(n)},{name:"image",content:Je(n)},{name:"video-search",content:ze(n)},{name:"scrape",content:Xe(n)},{name:"config",content:Ye(n)}];(!e||e==="main")&&r.push({name:u.name,content:rn(n,o),references:i}),(!e||e==="video")&&r.push({content:sn(n,o),name:`${u.name}-video`,references:Ha(n,o)}),(!e||e==="presentation")&&r.push({name:`${u.name}-presentation`,content:an(n)}),(!e||e==="presentation-edit")&&r.push({name:`${u.name}-presentation-edit`,content:on(n)}),e==="tts"&&r.push({name:`${u.name}-tts`,content:Ge(n)}),e==="music"&&r.push({name:`${u.name}-music`,content:Be(n)}),e==="sfx"&&r.push({name:`${u.name}-sfx`,content:We(n)}),e==="mix"&&r.push({name:`${u.name}-mix`,content:Ke(n)}),e==="image"&&r.push({name:`${u.name}-image`,content:Je(n)}),e==="video-search"&&r.push({name:`${u.name}-video-search`,content:ze(n)}),e==="scrape"&&r.push({name:`${u.name}-scrape`,content:Xe(n)}),e==="branding"&&r.push({name:`${u.name}-branding`,content:He(n)}),e==="config"&&r.push({name:`${u.name}-config`,content:Ye(n)}),console.log();for(let a of r){f(`Installing ${a.name}...`);let s=dn(a.name,a.content,{dir:t.dir,local:t.local,force:t.force},a.references);if(s.installed.length>0&&(C(`${a.name} installed successfully`),S(" Installed to",s.installed.join(", "))),s.skipped.length>0&&(f(` Skipped (already exists): ${s.skipped.join(", ")}`),console.log(q.gray(" Use --force to overwrite"))),s.errors.length>0)for(let l of s.errors)d(` ${l}`);s.installed.length===0&&s.skipped.length===0&&s.errors.length===0&&(f(" No supported AI coding assistants detected"),console.log(q.gray(` Supported editors: ${ns().join(", ")}`)),console.log(q.gray(" Use --dir <path> to install to a specific directory"))),console.log()}});Ze.command("show").description("Display skill content").argument("[type]","Skill type (default: main)").option("-m, --mode <mode>","Skill mode: standalone or studio","standalone").action(async(e="main",t)=>{t.mode&&!ln.includes(t.mode)&&(d(`Invalid mode: ${t.mode}. Must be one of: ${ln.join(", ")}`),process.exit(1));let n={...rs,mode:t.mode},r=await nt(),i={main:()=>rn(n,r),video:()=>sn(n,r),presentation:()=>an(n),"presentation-edit":()=>on(n),tts:()=>Ge(n),music:()=>Be(n),sfx:()=>We(n),mix:()=>Ke(n),image:()=>Je(n),"video-search":()=>ze(n),scrape:()=>Xe(n),branding:()=>He(n),config:()=>Ye(n)}[e];i?console.log(i()):(d(`Invalid skill type: ${e}. Must be one of: ${Qe.join(", ")}`),process.exit(1))});Ze.command("uninstall").description(`Remove ${u.displayName} skills from AI coding assistants`).argument("[type]","Skill type: main, video, presentation, or omit for all").option("-g, --global","Uninstall globally (from home directory)",!0).option("-l, --local","Uninstall locally (from current directory)").action(async(e,t)=>{let n=[];e&&!Qe.includes(e)&&(d(`Invalid skill type: ${e}. Must be one of: ${Qe.join(", ")}`),process.exit(1)),(!e||e==="main")&&n.push(u.name),(!e||e==="video")&&n.push(`${u.name}-video`),(!e||e==="presentation")&&n.push(`${u.name}-presentation`),(!e||e==="presentation-edit")&&n.push(`${u.name}-presentation-edit`),(!e||e==="tts")&&n.push(`${u.name}-tts`),(!e||e==="music")&&n.push(`${u.name}-music`),(!e||e==="sfx")&&n.push(`${u.name}-sfx`),(!e||e==="mix")&&n.push(`${u.name}-mix`),(!e||e==="image")&&n.push(`${u.name}-image`),(!e||e==="video-search")&&n.push(`${u.name}-video-search`),(!e||e==="scrape")&&n.push(`${u.name}-scrape`),(!e||e==="branding")&&n.push(`${u.name}-branding`),(!e||e==="config")&&n.push(`${u.name}-config`),console.log();for(let r of n){let o=cn(r,{local:t.local});if(o.removed.length>0?(C(`${r} uninstalled`),S(" Removed from",o.removed.join(", "))):f(` ${r} not found`),o.errors.length>0)for(let i of o.errors)M(` Failed to remove: ${i}`)}console.log()});import{Command as Tp}from"commander";import yt from"chalk";var os=new Tp("templates").description("List slide design templates (use with `create --template <id>`)").argument("[search]","Filter templates by name, description, or category").option("-f, --format <format>","Output format (table, json)","table").option("--limit <n>","Maximum templates to list","25").action(async(e,t)=>{await A();try{let n=Number.parseInt(t.limit??"25",10),{templates:r}=await Zr(e,Number.isFinite(n)?n:25);if(r.length===0){t.format==="json"?console.log(JSON.stringify([])):f("No templates found");return}if(t.format==="json"){console.log(JSON.stringify(r,null,2));return}for(let o of r){let i=[o.isDefault?yt.green("default"):null,o.category??null,o.recommendedModes?.length?`modes: ${o.recommendedModes.join("/")}`:null].filter(Boolean).join(" \xB7 ");console.log(`${yt.bold(o.name)} ${yt.gray(o.id)}${i?`
|
|
2468
|
+
${yt.gray(i)}`:""}`)}console.log(),console.log(yt.gray(`Use one with: ${u.commands[0]} create "<topic>" --template <id>`))}catch(n){d(n instanceof Error?n.message:String(n)),process.exit(n.exitCode??1)}});import{Command as Ep}from"commander";import wr from"chalk";var is=new Ep("whoami").description("Show current user and team information").option("-f, --format <format>","Output format (human, json)","human").action(async e=>{await A();try{let t=await me();if(In(),e.format==="json"){console.log(JSON.stringify(t,null,2));return}oe("User"),console.log(),S("Email",t.user.email),t.user.username&&S("Username",t.user.username),(t.user.firstName||t.user.lastName)&&S("Name",[t.user.firstName,t.user.lastName].filter(Boolean).join(" "));let n=t.authType==="apiKey"?"API Key":t.authType==="oauth"?"OAuth":"Session";if(S("Auth Type",n),t.currentTeam&&(console.log(),oe("Current Team"),console.log(),S("ID",t.currentTeam.id),S("Name",t.currentTeam.name),S("Plan",t.currentTeam.planName),S("Role",t.currentTeam.isOwner?"Owner":"Member")),t.teams.length>1){console.log(),oe("All Teams"),console.log();for(let r of t.teams){let o=r.isCurrent?wr.green(" (current)"):"";console.log(` ${wr.bold(r.name)}${o}`),console.log(wr.gray(` ID: ${r.id} | Plan: ${r.planName} | Role: ${r.role}`))}}console.log()}catch(t){d(t instanceof Error?t.message:String(t)),process.exit(t.exitCode??1)}});function as(e){return e[0]==="--"?e.slice(1):e}var Op="0.3.18",G=new Ap,L=u.commands[0];G.name(L).description(`${u.displayName} CLI \u2014 presentations, videos, avatars, audio, branding, media and research`).version(Op,"-v, --version","Show version number").option("--debug","Enable debug logging").option("--no-color","Disable colored output").configureOutput({outputError:(e,t)=>{t(F.red(e))}});G.addCommand(Oo);ja(G);G.addCommand(Ko);G.addCommand(Rn);G.addCommand(Wo);G.addCommand(os);G.addCommand(Go);G.addCommand(qo);G.addCommand(is);G.addCommand(Yo);G.addCommand(Qo);G.addCommand(Uo);G.addCommand(Ze);G.addCommand(Dt);var ss=Vo();ss.commands.length>0&&G.addCommand(ss);function Pp(e,t){if(e===t)return 0;if(e.length===0)return t.length;if(t.length===0)return e.length;let n=Array.from({length:t.length+1},(r,o)=>o);for(let r=1;r<=e.length;r++){let o=[r];for(let i=1;i<=t.length;i++){let a=e[r-1]===t[i-1]?0:1;o[i]=Math.min(o[i-1]+1,n[i]+1,n[i-1]+a)}n=o}return n[t.length]}function _p(e){let t=e.toLowerCase(),n=t.length<=4?1:2,r,o=Number.POSITIVE_INFINITY;for(let i of G.commands)for(let a of[i.name(),...i.aliases()]){let s=Pp(t,a.toLowerCase());s<o&&(o=s,r=i.name())}return r!==void 0&&o<=n?r:void 0}G.on("command:*",e=>{let t=String(e[0]);console.error(F.red(`Error: Unknown command '${t}'`)),console.error();let n=_p(t);n&&console.error(`Did you mean '${n}'?`),console.error(`Run '${L} --help' to see available commands.`),console.error(`Run '${L} capabilities' for the full list of callable capabilities.`),process.exit(1)});G.addHelpText("after",`
|
|
2469
|
+
${F.bold("Start here:")}
|
|
2470
|
+
$ ${L} capabilities ${F.gray("# every capability + its exact command path")}
|
|
2471
|
+
$ ${L} <group> --help ${F.gray("# drill into any command group above")}
|
|
2472
|
+
|
|
2473
|
+
${F.bold("Common tasks:")}
|
|
2474
|
+
${F.gray("Make a presentation")}
|
|
2475
|
+
$ ${L} create "<topic>" --context "..."
|
|
2476
|
+
${F.gray("Turn a finished presentation into an MP4 (narration + avatar)")}
|
|
2477
|
+
$ ${L} presentation video <slug>
|
|
2478
|
+
${F.gray("Export a presentation as a ZIP of HTML + assets")}
|
|
2479
|
+
$ ${L} export <slug> -o deck.zip
|
|
2480
|
+
${F.gray("Make a video from scratch (no deck)")}
|
|
2481
|
+
$ ${L} video storyboard create ${F.gray("then")} $ ${L} video create
|
|
2482
|
+
${F.gray("Regenerate one slide's narration in a video draft")}
|
|
2483
|
+
$ ${L} presentation video-narration <project-id>
|
|
2484
|
+
${F.gray("Generate speaker notes")}
|
|
2485
|
+
$ ${L} presentation speaker-notes <presentation-id>
|
|
2486
|
+
${F.gray("Import a PowerPoint")}
|
|
2487
|
+
$ ${L} presentation import
|
|
2488
|
+
${F.gray("Extract a brand identity from a URL")}
|
|
2489
|
+
$ ${L} branding extract <url>
|
|
2490
|
+
${F.gray("Voiceover, music, sound effects")}
|
|
2491
|
+
$ ${L} tts generate | ${L} music generate | ${L} sfx generate
|
|
2492
|
+
${F.gray("Find stock footage / images")}
|
|
2493
|
+
$ ${L} video find | ${L} image search
|
|
2494
|
+
${F.gray("Read a web page or a YouTube transcript")}
|
|
2495
|
+
$ ${L} scrape <url> | ${L} transcript <url>
|
|
2496
|
+
|
|
2497
|
+
${F.bold("Two different video pipelines:")}
|
|
2498
|
+
'${L} presentation video ...' renders an EXISTING deck to MP4.
|
|
2499
|
+
'${L} video ...' builds a video from a storyboard or brief with stock
|
|
2500
|
+
and generated footage \u2014 no deck involved.
|
|
2501
|
+
|
|
2502
|
+
${F.bold("Authentication:")}
|
|
2503
|
+
Run '${L} login' to authenticate (recommended).
|
|
3463
2504
|
Or set CC_SLIDES_API_KEY environment variable for API key auth.
|
|
3464
2505
|
|
|
3465
|
-
${
|
|
3466
|
-
|
|
3467
|
-
|
|
2506
|
+
${F.bold("Note:")}
|
|
2507
|
+
The command list above is not always complete: feature-flag-gated commands
|
|
2508
|
+
(the '${L} derive' subcommands) appear only after '${L} login'
|
|
2509
|
+
and '${L} config refresh' have cached your feature access. Run
|
|
2510
|
+
'${L} capabilities' for the authoritative list.
|
|
2511
|
+
|
|
2512
|
+
${F.bold("More Info:")}
|
|
2513
|
+
${F.blue(u.docsUrl)}
|
|
2514
|
+
`);G.parse(as(process.argv.slice(2)),{from:"user"});
|