@vivipilot/cli 0.1.1 → 0.1.4
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/dist/chunk-FGUKEVKT.js +1 -0
- package/dist/chunk-JJDAKVXA.js +8 -0
- package/dist/{chunk-RB2L6BY5.js → chunk-ZIATVRTF.js} +2 -2
- package/dist/cli.js +28 -26
- package/dist/index.js +1 -1
- package/dist/mcp-DTGU64NI.js +1 -0
- package/dist/render-CVXYAUBY.js +1 -0
- package/package.json +1 -1
- package/dist/chunk-RW7OWVPD.js +0 -5
- package/dist/chunk-TZJPBE3B.js +0 -1
- package/dist/mcp-MRADYIPQ.js +0 -1
- package/dist/render-TJGWLKPC.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{f as a,g as p,i}from"./chunk-L3C7EOPY.js";var u=class{config;env;fetchImpl;constructor(e){this.config=e.config,this.env=e.env??process.env,this.fetchImpl=e.fetchImpl??fetch}apiUrl(){return a(this.config,this.env)}apiKey(){let e=p(this.config,this.env);if(!e)throw new i("Vivipilot CLI/MCP generation is paid-only. Set VIVIPILOT_API_KEY or run `vivipilot login --api-key <key>`.",2);return e}async requestJson(e,t={}){let s=new Headers(t.headers);s.set("accept","application/json"),s.set("authorization",`Bearer ${this.apiKey()}`),s.set("x-vivipilot-client","@vivipilot/cli"),t.body&&!s.has("content-type")&&s.set("content-type","application/json");let n=await this.fetchImpl(`${this.apiUrl()}${e}`,{...t,headers:s}),o=await n.text(),r={};try{r=o.length>0?JSON.parse(o):{}}catch{throw n.ok?new i("Failed to parse response from Vivipilot API as JSON.",1):new i(`Vivipilot API request failed with status ${n.status}. The server did not return a valid JSON response. Ensure your API URL is correct.`,1)}if(!n.ok){let l=r&&typeof r=="object"&&"error"in r?String(r.error):`Vivipilot API request failed with status ${n.status}`,g=n.status===402?" Run `vivipilot topup` to buy credits.":"";throw new i(`${l}${g}`,n.status===401?2:1)}return r}async whoami(){return this.requestJson("/api/v1/me")}async balance(){return this.requestJson("/api/v1/credits/balance")}async estimate(e){return this.requestJson("/api/v1/layouts/estimate",{method:"POST",body:JSON.stringify(e)})}async generate(e,t){return this.requestJson("/api/v1/layouts/generate",{method:"POST",headers:{"idempotency-key":t},body:JSON.stringify({...e,outputFormat:"manifest"})})}async startGenerate(e,t){return this.requestJson("/api/v1/layouts/generate/start",{method:"POST",headers:{"idempotency-key":t},body:JSON.stringify({...e,outputFormat:"manifest"})})}async generationProgress(e){return this.requestJson(`/api/v1/layouts/${encodeURIComponent(e)}?progress=true`)}async generationStatus(e){return this.requestJson(`/api/v1/layouts/${encodeURIComponent(e)}`)}};export{u as a};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{b as U,c as D,d as F,e as j}from"./chunk-FN7FHZ3D.js";import{f as _,i as w}from"./chunk-L3C7EOPY.js";import{spawn as le}from"child_process";import{createServer as K}from"http";import{readFile as k,writeFile as I,access as W,stat as he,mkdir as de}from"fs/promises";import{constants as ue,openSync as me,writeSync as V,closeSync as fe}from"fs";import{join as C}from"path";import{createHash as pe}from"crypto";import{existsSync as ne}from"fs";import{access as oe,constants as se}from"fs/promises";import{homedir as E}from"os";import{join as g}from"path";var O=class extends Error{constructor(e){super(e),this.name="BrowserNotFoundError"}};async function G(t=process.env){let e=t.VIVIPILOT_HEADLESS_BROWSER;if(e&&await S(e))return{executablePath:e,source:"env"};let r=await ae();if(r)return{executablePath:r,source:"chrome-headless-shell"};let a=await ie();if(a)return{executablePath:a,source:"system-chrome"};let o=await ce();if(o)return{executablePath:o,source:"system-chromium"};throw new O(`No headless browser found. To install a lightweight headless browser automatically, run:
|
|
2
|
+
npx @puppeteer/browsers install chrome-headless-shell
|
|
3
|
+
|
|
4
|
+
Alternatively, install Chrome/Chromium on your system, or set VIVIPILOT_HEADLESS_BROWSER to your binary path.`)}async function S(t){try{return await oe(t,se.X_OK),!0}catch{return!1}}async function ae(){let t=[process.env.PUPPETEER_EXECUTABLE_PATH,g(E(),".cache","puppeteer","chrome-headless-shell"),"/usr/bin/chrome-headless-shell","/usr/local/bin/chrome-headless-shell","/snap/bin/chrome-headless-shell",g(E(),".local","share","chrome-headless-shell","chrome-headless-shell"),"/opt/chrome-headless-shell/chrome-headless-shell","/Applications/Google Chrome Headless Shell.app/Contents/MacOS/Google Chrome Headless Shell",g(E(),"AppData","Local","chrome-headless-shell","chrome-headless-shell.exe")];for(let e of t)if(e){if(await S(e))return e;if(ne(e)&&!e.endsWith("chrome-headless-shell")&&!e.endsWith(".exe")){let{readdirSync:r}=await import("fs");try{let a=r(e,{withFileTypes:!0});for(let o of a){if(!o.isDirectory())continue;let h=g(e,o.name,"chrome-headless-shell");if(await S(h))return h;let i=g(e,o.name,"chrome-headless-shell.exe");if(await S(i))return i}}catch{}}}return null}async function ie(){let t=["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/usr/bin/google-chrome-unstable","/usr/local/bin/google-chrome","/opt/google/chrome/chrome","/snap/bin/google-chrome","/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",g(E(),"AppData","Local","Google","Chrome","Application","chrome.exe"),"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"];for(let e of t)if(await S(e))return e;return null}async function ce(){let t=["/usr/bin/chromium","/usr/bin/chromium-browser","/usr/local/bin/chromium","/snap/bin/chromium","/usr/bin/brave-browser","/usr/bin/brave","/Applications/Chromium.app/Contents/MacOS/Chromium",g(E(),"AppData","Local","Chromium","Application","chromium.exe"),"C:\\Program Files\\Chromium\\Application\\chromium.exe"];for(let e of t)if(await S(e))return e;return null}function J(t,e){let r=e?.windowSize?.width??1920,a=e?.windowSize?.height??1080;return["--headless=new","--no-sandbox","--disable-gpu-sandbox","--disable-dev-shm-usage","--disable-extensions","--disable-background-networking","--disable-sync","--disable-translate","--disable-default-apps","--disable-popup-blocking","--disable-component-update","--disable-metrics","--no-first-run","--mute-audio","--enable-logging=stderr","--log-level=0","--v=1",`--window-size=${r},${a}`,"--enable-features=Vulkan","--use-gl=angle","--use-angle=swiftshader","--enable-unsafe-swiftshader","--ignore-gpu-blocklist",...e?.extraArgs??[],t]}var we=3e5;function ge(t){return new Promise((e,r)=>{let a=null,o=null,h=K(async(i,c)=>{if(c.setHeader("Access-Control-Allow-Origin","*"),c.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),c.setHeader("Access-Control-Allow-Headers","Content-Type"),i.method==="OPTIONS"){c.writeHead(204),c.end();return}let p=new URL(i.url??"/","http://localhost");if(p.pathname==="/manifest.json"&&i.method==="GET"){try{let u=await k(t.replace(/\.mp4$|\.webm$|\.gif$|\.mov$/,".vivi.json"),"utf8");c.writeHead(200,{"content-type":"application/json"}),c.end(u)}catch{c.writeHead(404),c.end("Manifest not found")}return}if(p.pathname==="/result"&&i.method==="POST"){let u=[];i.on("data",m=>u.push(m)),i.on("end",async()=>{a=Buffer.concat(u);try{await I(t,a),c.writeHead(200,{"content-type":"application/json"}),c.end(JSON.stringify({ok:!0,size:a.length}))}catch(m){c.writeHead(500,{"content-type":"application/json"}),c.end(JSON.stringify({ok:!1,error:m instanceof Error?m.message:String(m)}))}});return}if(p.pathname==="/error"&&i.method==="POST"){let u=[];i.on("data",m=>u.push(m)),i.on("end",()=>{try{o=JSON.parse(Buffer.concat(u).toString()).message??"Unknown render error"}catch{o=Buffer.concat(u).toString()||"Unknown render error"}c.writeHead(200,{"content-type":"application/json"}),c.end(JSON.stringify({ok:!0}))});return}if(p.pathname==="/status"&&i.method==="GET"){c.writeHead(200,{"content-type":"application/json"}),c.end(JSON.stringify({done:a!==null||o!==null,error:o,hasBlob:a!==null,blobSize:a?.length??0}));return}c.writeHead(404),c.end("Not found")});h.on("error",r),h.listen(0,"127.0.0.1",()=>{let i=h.address();if(!i||typeof i=="string"){r(new Error("Failed to bind callback server"));return}e({server:h,port:i.port})})})}async function be(t,e){let r=Date.now()+e;for(;Date.now()<r;){try{let o=await(await fetch(`http://127.0.0.1:${t}/status`)).json();if(o.done)return o.error?{size:0,error:o.error}:{size:o.blobSize,error:null}}catch{}await new Promise(a=>setTimeout(a,1e3))}return{size:0,error:`Render timed out after ${e/1e3}s`}}async function Ne(t,e,r=process.env){let{manifestPath:a,outPath:o}=t,h=await U(a,e,r);if(!h.ok)throw new w(`Manifest verification failed: ${h.message}`,1);let i=await D(h.manifest,e,r);F(i);let c={...t.format?{format:t.format}:{},...t.width?{width:t.width}:{},...t.height?{height:t.height}:{}},p=j(h.manifest,c);if(!p.ok)throw new w(p.message,1);if(t.verifyOnly)return{ok:!0,outPath:o,size:0,manifestId:h.manifest.manifestId};let u;try{u=await G(r)}catch(n){throw n instanceof O?new w(n.message,1):n}let m=await k(a,"utf8"),b=JSON.parse(m),$=b.render.canvas,B="";if(b.render.assets&&Array.isArray(b.render.assets))try{B=await ye(b.render.assets)}catch(n){throw new w(`Failed to cache assets: ${n instanceof Error?n.message:String(n)}`,1)}let{server:X}=await ge(o);X.close();let P=null,x=!1,N=0,v=K((n,s)=>{if(s.setHeader("Access-Control-Allow-Origin","*"),s.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),s.setHeader("Access-Control-Allow-Headers","Content-Type"),n.method==="OPTIONS"){s.writeHead(204),s.end();return}let f=new URL(n.url??"/","http://localhost");if(B&&f.pathname.startsWith("/assets/")&&n.method==="GET"){let d=f.pathname.slice(8),l=C(B,d);k(l).then(y=>{let te=b.render.assets?.find(re=>re.sha256===d);s.writeHead(200,{"content-type":te?.mimeType||"application/octet-stream"}),s.end(y)}).catch(()=>{s.writeHead(404),s.end("Asset not found")});return}if(f.pathname==="/manifest.json"&&n.method==="GET"){let d={...b};d.render?.assets&&(d.render.assets=d.render.assets.map(l=>({...l,url:`http://127.0.0.1:${A}/assets/${l.sha256}`}))),s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify(d));return}if(f.pathname==="/result"&&n.method==="POST"){let d=[];n.on("data",l=>d.push(l)),n.on("end",async()=>{let l=Buffer.concat(d);try{await I(o,l),x=!0,N=l.length,s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({ok:!0,size:l.length}))}catch(y){P=y instanceof Error?y.message:String(y),s.writeHead(500,{"content-type":"application/json"}),s.end(JSON.stringify({ok:!1,error:P}))}});return}if(f.pathname==="/error"&&n.method==="POST"){let d=[];n.on("data",l=>d.push(l)),n.on("end",()=>{let l="Unknown render error";try{l=JSON.parse(Buffer.concat(d).toString()).message??l}catch{l=Buffer.concat(d).toString()||l}P=l,s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({ok:!0,error:l}))});return}if(f.pathname==="/status"&&n.method==="GET"){s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({done:x||P!==null,error:P,hasBlob:x,blobSize:N}));return}s.writeHead(404),s.end("Not found")}),A=await new Promise((n,s)=>{v.on("error",s),v.listen(0,"127.0.0.1",()=>{let f=v.address();if(!f||typeof f=="string"){s(new Error("Failed to bind manifest server"));return}n(f.port)})}),Y=r.VIVIPILOT_RENDER_URL??`${_(e,r)}/headless-render`,R=new URLSearchParams({manifest:`http://127.0.0.1:${A}/manifest.json`,format:t.format??"mp4",headless:"true",callback:`http://127.0.0.1:${A}/result`});t.scale&&R.set("scale",String(t.scale)),t.transparent&&R.set("transparent","true"),t.fps&&R.set("fps",String(t.fps));let Q=`${Y}?${R.toString()}`,Z=J(Q,{windowSize:{width:Math.max($.width,1280),height:Math.max($.height,720)}}),T=le(u.executablePath,Z,{stdio:["ignore","pipe","pipe"],env:{...r,DISPLAY:r.DISPLAY??":99"}}),q=C(process.cwd(),"host","chrome_browser.log"),H=me(q,"w"),L="",z="";T.stderr?.on("data",n=>{L+=n.toString();try{V(H,n)}catch{}}),T.stdout?.on("data",n=>{z+=n.toString();try{V(H,n)}catch{}});let ee=parseInt(r.VIVIPILOT_RENDER_TIMEOUT_MS??String(we),10),M=await be(A,ee);T.kill("SIGTERM"),setTimeout(()=>{try{T.kill("SIGKILL")}catch{}},5e3),v.close();try{fe(H)}catch{}if(M.error)throw new w(`Render failed: ${M.error}
|
|
5
|
+
Browser stderr (last 2KB):
|
|
6
|
+
${L.slice(-2e3)}
|
|
7
|
+
Browser stdout (last 1KB):
|
|
8
|
+
${z.slice(-1e3)}`,1);try{await W(o,ue.R_OK);let n=await he(o);return{ok:!0,outPath:o,size:n.size,manifestId:h.manifest.manifestId}}catch{throw new w(`Render completed but output file not found at ${o}`,1)}}async function ye(t){let e=C(process.env.HOME||process.env.USERPROFILE||process.cwd(),".cache","vivipilot","assets");await de(e,{recursive:!0});for(let r of t){if(!r.sha256||!r.url)continue;let a=C(e,r.sha256);try{await W(a);continue}catch{}let o=await fetch(r.url);if(!o.ok)throw new Error(`Failed to download asset ${r.url}: HTTP ${o.status}`);let h=Buffer.from(await o.arrayBuffer()),i=pe("sha256").update(h).digest("hex");if(i!==r.sha256)throw new Error(`Asset hash mismatch for ${r.url}: expected ${r.sha256}, got ${i}`);await I(a,h)}return e}export{Ne as a};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{a as k}from"./chunk-
|
|
2
|
-
`,"utf8")}async function T(e,o,n){let{verifyManifestFile:l}=await import("./manifest-TDCIHZ46.js");return l(e,o,n)}function N(){return[{name:"vivipilot_get_balance",title:"Get Vivipilot paid credit balance",description:"Returns Vivipilot account balance and paid top-up balance. CLI/MCP generation is paid-only and never grants trial credits.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"vivipilot_estimate_motion_layout",title:"Estimate Vivipilot motion layout credits",description:"Estimates paid credits for a cloud layout generation. This does not burn credits.",inputSchema:{type:"object",properties:{prompt:{type:"string"},width:{type:"number"},height:{type:"number"},fps:{type:"number"},durationSeconds:{type:"number"},engine:{type:"string",enum:["pixi","three","auto"]}},required:["prompt"],additionalProperties:!1}},{name:"vivipilot_generate_motion_layout",title:"Generate signed Vivipilot render manifest",description:"Paid-only cloud generation. Burns paid credits only after cloud orchestration is wired; local rendering uses the client's hardware.",inputSchema:{type:"object",properties:{prompt:{type:"string"},out:{type:"string"},idempotencyKey:{type:"string"},width:{type:"number"},height:{type:"number"},fps:{type:"number"},durationSeconds:{type:"number"},engine:{type:"string",enum:["pixi","three","auto"]}},required:["prompt"],additionalProperties:!1}},{name:"vivipilot_verify_manifest",title:"Verify Vivipilot render manifest",description:"Verifies a signed Vivipilot manifest locally using public keys. This does not burn credits.",inputSchema:{type:"object",properties:{manifestPath:{type:"string"}},required:["manifestPath"],additionalProperties:!1}},{name:"vivipilot_render_video",title:"Render Vivipilot video locally",description:"Renders a signed manifest on local hardware after signature verification. No cloud rendering is used.",inputSchema:{type:"object",properties:{manifestPath:{type:"string"},out:{type:"string"},verifyOnly:{type:"boolean"}},required:["manifestPath"],additionalProperties:!1}},{name:"vivipilot_get_generation_status",title:"Get Vivipilot generation status",description:"Checks cloud status for a Vivipilot layout generation. This does not burn credits.",inputSchema:{type:"object",properties:{generationId:{type:"string"}},required:["generationId"],additionalProperties:!1}}]}async function O(e,o,n,l,m){let r=S(o)?o:{};try{switch(e){case"vivipilot_get_balance":{let t=await n.balance(),a=(typeof t.paidBalance=="number"?t.paidBalance:0)>0?[{tool:"vivipilot_estimate_motion_layout",reason:"Estimate credits for a generation before spending."},{tool:"vivipilot_generate_motion_layout",reason:"Generate a signed manifest (paid credits will be burned)."}]:[{action:"topup",reason:`Buy credits at ${b(l,m)}/edit?billing=topup before generating. CLI/MCP generation is paid-only.`}];return u({ok:!0,...t,nextActions:a})}case"vivipilot_estimate_motion_layout":{let t=await n.estimate(P(r)),i=typeof t.estimatedCredits=="number"?t.estimatedCredits:0;return u({ok:!0,...t,nextActions:[{tool:"vivipilot_generate_motion_layout",reason:`Generate now for ~${i} credits (paid-only).`},{tool:"vivipilot_get_balance",reason:"Check balance before generating."}]})}case"vivipilot_generate_motion_layout":{let t=p(r,"out")??"scene.vivi.json",i=p(r,"idempotencyKey")??`mcp_${q()}`,a=P(r),f=await n.startGenerate({...a,outputFormat:"manifest"},i),d=f.generationId;if(f.idempotentReplay&&f.status==="completed"){let s=await n.generationProgress(d);if(s.manifestData)return await R(t,s.manifestData),u({ok:!0,generationId:d,manifestPath:t,creditsCharged:s.creditsCharged??null,status:"completed",nextActions:[{tool:"vivipilot_verify_manifest",args:{manifestPath:t},reason:"Verify the signed manifest locally before rendering."},{tool:"vivipilot_render_video",args:{manifestPath:t},reason:"Render the manifest to video on local hardware."}]})}let v=3e5,_=2e3,w=Date.now(),c="";for(;Date.now()-w<v;){await new Promise(I=>setTimeout(I,_));let s;try{s=await n.generationProgress(d)}catch{continue}if(s.progressMessage&&s.progressMessage!==c&&(c=s.progressMessage),s.status==="completed"&&s.manifestData)return await R(t,s.manifestData),u({ok:!0,generationId:d,manifestPath:t,creditsCharged:s.creditsCharged??null,engine:s.engine,status:"completed",progressLog:c,nextActions:[{tool:"vivipilot_verify_manifest",args:{manifestPath:t},reason:"Verify the signed manifest locally before rendering."},{tool:"vivipilot_render_video",args:{manifestPath:t},reason:"Render the manifest to video on local hardware."}]});if(s.status==="failed")return u({ok:!1,generationId:d,error:s.error??"Generation failed.",progressLog:c,nextActions:[{tool:"vivipilot_generate_motion_layout",reason:"Retry with a new prompt or idempotency key."},{tool:"vivipilot_get_balance",reason:"Check balance before retrying."}]},!0)}return u({ok:!1,generationId:d,error:"Generation timed out after 5 minutes.",nextActions:[{tool:"vivipilot_get_generation_status",args:{generationId:d},reason:"Check if generation completed after timeout."}]},!0)}case"vivipilot_verify_manifest":{let t=p(r,"manifestPath");if(!t)throw new Error("manifestPath is required");let i=await T(t,l,m);if(!i.ok)throw new Error(i.message);return u({ok:!0,manifestId:i.manifest.manifestId,generationId:i.manifest.generationId,canonicalPayloadHash:i.canonicalPayloadHash,nextActions:[{tool:"vivipilot_render_video",args:{manifestPath:t},reason:"Render the verified manifest to video."}]})}case"vivipilot_render_video":{let t=p(r,"manifestPath");if(!t)throw new Error("manifestPath is required");let i=p(r,"out")??"video.mp4",a=p(r,"format"),f=g(r,"scale"),d=g(r,"fps"),v=r.transparent===!0,_=r.verifyOnly===!0,{renderManifest:w}=await import("./render-
|
|
1
|
+
import{a as k}from"./chunk-FGUKEVKT.js";import{f as b}from"./chunk-L3C7EOPY.js";import{mkdir as V,writeFile as E}from"fs/promises";import{dirname as x}from"path";import{createInterface as A}from"readline";import{randomUUID as q}from"crypto";var J="2025-06-18";function S(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function g(e,o){let n=e[o];return typeof n=="number"&&Number.isFinite(n)?n:void 0}function p(e,o){let n=e[o];return typeof n=="string"&&n.trim()?n.trim():void 0}function P(e){let o=p(e,"prompt");if(!o)throw new Error("prompt is required");let n=p(e,"engine"),l=n==="pixi"||n==="three"||n==="auto"?n:void 0,m=g(e,"width"),r=g(e,"height"),t=g(e,"fps"),i=g(e,"durationSeconds")??g(e,"duration");return{prompt:o,...m||r||t?{canvas:{width:m,height:r,fps:t}}:{},...i?{durationSeconds:i}:{},...l?{enginePreference:l}:{}}}function u(e,o=!1){return{content:[{type:"text",text:JSON.stringify(e,null,2)}],structuredContent:e,isError:o}}function M(e){let o=e instanceof Error?e.message:String(e);return u({ok:!1,error:o},!0)}async function R(e,o){await V(x(e),{recursive:!0}),await E(e,`${JSON.stringify(o,null,2)}
|
|
2
|
+
`,"utf8")}async function T(e,o,n){let{verifyManifestFile:l}=await import("./manifest-TDCIHZ46.js");return l(e,o,n)}function N(){return[{name:"vivipilot_get_balance",title:"Get Vivipilot paid credit balance",description:"Returns Vivipilot account balance and paid top-up balance. CLI/MCP generation is paid-only and never grants trial credits.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"vivipilot_estimate_motion_layout",title:"Estimate Vivipilot motion layout credits",description:"Estimates paid credits for a cloud layout generation. This does not burn credits.",inputSchema:{type:"object",properties:{prompt:{type:"string"},width:{type:"number"},height:{type:"number"},fps:{type:"number"},durationSeconds:{type:"number"},engine:{type:"string",enum:["pixi","three","auto"]}},required:["prompt"],additionalProperties:!1}},{name:"vivipilot_generate_motion_layout",title:"Generate signed Vivipilot render manifest",description:"Paid-only cloud generation. Burns paid credits only after cloud orchestration is wired; local rendering uses the client's hardware.",inputSchema:{type:"object",properties:{prompt:{type:"string"},out:{type:"string"},idempotencyKey:{type:"string"},width:{type:"number"},height:{type:"number"},fps:{type:"number"},durationSeconds:{type:"number"},engine:{type:"string",enum:["pixi","three","auto"]}},required:["prompt"],additionalProperties:!1}},{name:"vivipilot_verify_manifest",title:"Verify Vivipilot render manifest",description:"Verifies a signed Vivipilot manifest locally using public keys. This does not burn credits.",inputSchema:{type:"object",properties:{manifestPath:{type:"string"}},required:["manifestPath"],additionalProperties:!1}},{name:"vivipilot_render_video",title:"Render Vivipilot video locally",description:"Renders a signed manifest on local hardware after signature verification. No cloud rendering is used.",inputSchema:{type:"object",properties:{manifestPath:{type:"string"},out:{type:"string"},verifyOnly:{type:"boolean"}},required:["manifestPath"],additionalProperties:!1}},{name:"vivipilot_get_generation_status",title:"Get Vivipilot generation status",description:"Checks cloud status for a Vivipilot layout generation. This does not burn credits.",inputSchema:{type:"object",properties:{generationId:{type:"string"}},required:["generationId"],additionalProperties:!1}}]}async function O(e,o,n,l,m){let r=S(o)?o:{};try{switch(e){case"vivipilot_get_balance":{let t=await n.balance(),a=(typeof t.paidBalance=="number"?t.paidBalance:0)>0?[{tool:"vivipilot_estimate_motion_layout",reason:"Estimate credits for a generation before spending."},{tool:"vivipilot_generate_motion_layout",reason:"Generate a signed manifest (paid credits will be burned)."}]:[{action:"topup",reason:`Buy credits at ${b(l,m)}/edit?billing=topup before generating. CLI/MCP generation is paid-only.`}];return u({ok:!0,...t,nextActions:a})}case"vivipilot_estimate_motion_layout":{let t=await n.estimate(P(r)),i=typeof t.estimatedCredits=="number"?t.estimatedCredits:0;return u({ok:!0,...t,nextActions:[{tool:"vivipilot_generate_motion_layout",reason:`Generate now for ~${i} credits (paid-only).`},{tool:"vivipilot_get_balance",reason:"Check balance before generating."}]})}case"vivipilot_generate_motion_layout":{let t=p(r,"out")??"scene.vivi.json",i=p(r,"idempotencyKey")??`mcp_${q()}`,a=P(r),f=await n.startGenerate({...a,outputFormat:"manifest"},i),d=f.generationId;if(f.idempotentReplay&&f.status==="completed"){let s=await n.generationProgress(d);if(s.manifestData)return await R(t,s.manifestData),u({ok:!0,generationId:d,manifestPath:t,creditsCharged:s.creditsCharged??null,status:"completed",nextActions:[{tool:"vivipilot_verify_manifest",args:{manifestPath:t},reason:"Verify the signed manifest locally before rendering."},{tool:"vivipilot_render_video",args:{manifestPath:t},reason:"Render the manifest to video on local hardware."}]})}let v=3e5,_=2e3,w=Date.now(),c="";for(;Date.now()-w<v;){await new Promise(I=>setTimeout(I,_));let s;try{s=await n.generationProgress(d)}catch{continue}if(s.progressMessage&&s.progressMessage!==c&&(c=s.progressMessage),s.status==="completed"&&s.manifestData)return await R(t,s.manifestData),u({ok:!0,generationId:d,manifestPath:t,creditsCharged:s.creditsCharged??null,engine:s.engine,status:"completed",progressLog:c,nextActions:[{tool:"vivipilot_verify_manifest",args:{manifestPath:t},reason:"Verify the signed manifest locally before rendering."},{tool:"vivipilot_render_video",args:{manifestPath:t},reason:"Render the manifest to video on local hardware."}]});if(s.status==="failed")return u({ok:!1,generationId:d,error:s.error??"Generation failed.",progressLog:c,nextActions:[{tool:"vivipilot_generate_motion_layout",reason:"Retry with a new prompt or idempotency key."},{tool:"vivipilot_get_balance",reason:"Check balance before retrying."}]},!0)}return u({ok:!1,generationId:d,error:"Generation timed out after 5 minutes.",nextActions:[{tool:"vivipilot_get_generation_status",args:{generationId:d},reason:"Check if generation completed after timeout."}]},!0)}case"vivipilot_verify_manifest":{let t=p(r,"manifestPath");if(!t)throw new Error("manifestPath is required");let i=await T(t,l,m);if(!i.ok)throw new Error(i.message);return u({ok:!0,manifestId:i.manifest.manifestId,generationId:i.manifest.generationId,canonicalPayloadHash:i.canonicalPayloadHash,nextActions:[{tool:"vivipilot_render_video",args:{manifestPath:t},reason:"Render the verified manifest to video."}]})}case"vivipilot_render_video":{let t=p(r,"manifestPath");if(!t)throw new Error("manifestPath is required");let i=p(r,"out")??"video.mp4",a=p(r,"format"),f=g(r,"scale"),d=g(r,"fps"),v=r.transparent===!0,_=r.verifyOnly===!0,{renderManifest:w}=await import("./render-CVXYAUBY.js"),c=await w({manifestPath:t,outPath:i,...a?{format:a}:{},...f?{scale:f}:{},...d?{fps:d}:{},...v?{transparent:v}:{},verifyOnly:_},l,m);return u({ok:!0,manifestId:c.manifestId,out:c.outPath,size:c.size,nextActions:[{action:"file_ready",reason:`Video written to ${c.outPath} (${(c.size/1024/1024).toFixed(2)} MB).`}]})}case"vivipilot_get_generation_status":{let t=p(r,"generationId");if(!t)throw new Error("generationId is required");let i=await n.generationProgress(t),a=[];return i.status==="completed"&&i.manifest?(a.push({tool:"vivipilot_verify_manifest",reason:"Verify the completed manifest."}),a.push({tool:"vivipilot_render_video",reason:"Render the completed manifest to video."})):i.status==="failed"?(a.push({tool:"vivipilot_generate_motion_layout",reason:"Retry with a new idempotency key."}),a.push({tool:"vivipilot_get_balance",reason:"Check balance before retrying."})):a.push({tool:"vivipilot_get_generation_status",args:{generationId:t},reason:"Poll again \u2014 generation still in progress."}),u({ok:!0,...i,nextActions:a})}default:throw new Error(`Unknown tool: ${e}`)}}catch(t){return M(t)}}function C(e,o){e.write(`${JSON.stringify(o)}
|
|
3
3
|
`)}function y(e,o,n){C(e,{jsonrpc:"2.0",id:o,result:n})}function h(e,o,n,l){C(e,{jsonrpc:"2.0",id:o??null,error:{code:n,message:l}})}async function F(e){let o=e.env??process.env,n=new k({config:e.config,env:o}),l=A({input:e.stdin});for await(let m of l){let r=m.trim();if(!r)continue;let t;try{t=JSON.parse(r)}catch{h(e.stdout,null,-32700,"Parse error");continue}let i=t.id;try{switch(t.method){case"initialize":y(e.stdout,i??null,{protocolVersion:J,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"vivipilot",title:"Vivipilot MCP",version:"0.1.0"},instructions:"Vivipilot generation tools are paid-only and require VIVIPILOT_API_KEY or `vivipilot login` configuration. Local render tools use the client's hardware."});break;case"notifications/initialized":break;case"ping":y(e.stdout,i??null,{});break;case"tools/list":y(e.stdout,i??null,{tools:N()});break;case"tools/call":{if(!S(t.params)||typeof t.params.name!="string"){h(e.stdout,i,-32602,"tools/call requires params.name");break}let a=await O(t.params.name,t.params.arguments,n,e.config,o);y(e.stdout,i??null,a);break}default:i!==void 0&&h(e.stdout,i,-32601,`Method not found: ${t.method??""}`)}}catch(a){let f=a instanceof Error?a.message:String(a);i!==void 0&&h(e.stdout,i,-32603,f)}}}export{F as a};
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as R,b as
|
|
2
|
+
import{a as R,b as v,c as $,d as p}from"./chunk-ZZ72PPC3.js";import{a as E}from"./chunk-JJDAKVXA.js";import{b as M}from"./chunk-FN7FHZ3D.js";import{a as G}from"./chunk-FGUKEVKT.js";import{b as S,c as A,d as O,e as N,f as U,g as V,i as u,j as T}from"./chunk-L3C7EOPY.js";import{randomUUID as F}from"crypto";import{createInterface as H}from"readline";import{mkdir as X,writeFile as Q}from"fs/promises";import{dirname as Z}from"path";var _=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],L=class{io;frame=0;timer=null;lastMessage="";lastStage="";constructor(e){this.io=e}start(e){this.lastMessage=e,this.io.stderr.write(`\r${_[0]} ${e}`),this.timer=setInterval(()=>{this.frame=(this.frame+1)%_.length,this.io.stderr.write(`\r${_[this.frame]} ${this.lastMessage}`)},80)}update(e,n){e!==this.lastStage&&(this.lastStage&&this.io.stderr.write(`\r\x1B[32m\u2713\x1B[0m ${this.lastMessage}
|
|
3
3
|
`),this.lastStage=e),this.lastMessage=n}succeed(e){this.timer&&clearInterval(this.timer),this.timer=null,this.io.stderr.write(`\r\x1B[32m\u2713\x1B[0m ${e}
|
|
4
4
|
`)}fail(e){this.timer&&clearInterval(this.timer),this.timer=null,this.io.stderr.write(`\r\x1B[31m\u2717\x1B[0m ${e}
|
|
5
5
|
`)}stop(){this.timer&&clearInterval(this.timer),this.timer=null}},q=`Vivipilot CLI (paid-only)
|
|
@@ -11,14 +11,14 @@ Usage:
|
|
|
11
11
|
vivipilot balance
|
|
12
12
|
vivipilot topup
|
|
13
13
|
vivipilot estimate --prompt "..." [--width 1280 --height 720 --fps 30 --duration 6 --engine auto]
|
|
14
|
-
vivipilot generate --prompt "..." --out scene.vivi.json [--idempotency-key <key>]
|
|
14
|
+
vivipilot generate --prompt "..." --out video.mp4|scene.vivi.json [--idempotency-key <key>]
|
|
15
15
|
vivipilot chat [--out-dir ./scenes] [--width 1280 --height 720 --fps 30 --duration 6]
|
|
16
16
|
vivipilot verify scene.vivi.json
|
|
17
17
|
vivipilot render scene.vivi.json --out video.mp4
|
|
18
18
|
vivipilot mcp
|
|
19
19
|
|
|
20
20
|
Commands:
|
|
21
|
-
generate Create a
|
|
21
|
+
generate Create a scene (outputs video or signed vivi.json manifest)
|
|
22
22
|
chat Interactive multi-turn session (like the browser editor)
|
|
23
23
|
render Render a signed manifest to video locally
|
|
24
24
|
verify Verify a manifest's signature
|
|
@@ -32,9 +32,11 @@ Environment:
|
|
|
32
32
|
VIVIPILOT_MANIFEST_PUBLIC_KEYS JSON map of keyId to Ed25519 public key
|
|
33
33
|
|
|
34
34
|
CLI/MCP generation is paid-only: no trial credits and no free generations.
|
|
35
|
-
`;function
|
|
36
|
-
`)}function ee(t){let e=
|
|
37
|
-
`,"utf8")}function
|
|
35
|
+
`;function f(t,e){t.stdout.write(`${JSON.stringify(e,null,2)}
|
|
36
|
+
`)}function ee(t){let e=v(t,["prompt","p"])??t.positionals.join(" ");if(!e.trim())throw new u('Missing prompt. Pass --prompt "...".',2);return e.trim()}function K(t){let e=p(t,["width","w"]),n=p(t,["height","h"]),i=p(t,["fps"]),c=p(t,["duration","duration-seconds"]),r=v(t,["engine"]),a=r==="pixi"||r==="three"||r==="auto"?r:void 0;return{prompt:ee(t),...e||n||i?{canvas:{width:e,height:n,fps:i}}:{},...c?{durationSeconds:c}:{},...a?{enginePreference:a}:{}}}function W(t,e){return{prompt:t,...e.width||e.height||e.fps?{canvas:{width:e.width,height:e.height,fps:e.fps}}:{},...e.durationSeconds?{durationSeconds:e.durationSeconds}:{}}}async function j(t,e){await X(Z(t),{recursive:!0}),await Q(t,`${JSON.stringify(e,null,2)}
|
|
37
|
+
`,"utf8")}function D(t){let e=t.positionals[0];if(!e)throw new u("Missing manifest path.",2);return e}async function te(t,e,n){let i=S(e),c=await A(i),r=v(t,["api-key"])??e.VIVIPILOT_API_KEY;if(!r)throw new u("Missing paid API key. Pass --api-key or set VIVIPILOT_API_KEY.",2);let a={...c,apiKey:r,apiUrl:v(t,["api-url"])??e.VIVIPILOT_API_URL??c.apiUrl};await O(a,i),f(n,{ok:!0,configPath:i,paidOnly:!0})}async function ie(t,e){let n=S(t);await N(n),f(e,{ok:!0,configPath:n})}function P(t,e){return new G({config:t,env:e})}async function ne(t,e,n){let i=U(t,e);f(n,{paidOnly:!0,topupUrl:`${i}/edit?billing=topup`,message:"CLI/MCP generation requires paid top-up credits."})}var re=1500,se=3e5;async function B(t,e,n,i,c){let r=new L(c);r.start("Starting generation...");try{let a=await t.startGenerate({...e,outputFormat:"manifest"},n),{generationId:d}=a;if(r.update("start",`Generation ${d} started (est. ${a.estimatedCredits} credits, engine: ${a.engine})`),a.idempotentReplay&&a.status==="completed"){let s=await t.generationProgress(d);if(s.manifestData)return await j(i,s.manifestData),r.succeed(`Scene saved to ${i} (${s.creditsCharged??0} credits)`),{generationId:d,manifestPath:i,creditsCharged:s.creditsCharged}}let b=Date.now(),l="";for(;Date.now()-b<se;){await new Promise(m=>setTimeout(m,re));let s;try{s=await t.generationProgress(d)}catch{continue}if(s.progressMessage&&s.progressMessage!==l&&(l=s.progressMessage,r.update(s.workflowStatus??s.status,s.progressMessage)),s.status==="completed")return s.manifestData?(await j(i,s.manifestData),r.succeed(`Scene saved to ${i} (${s.creditsCharged??0} credits)`),{generationId:d,manifestPath:i,creditsCharged:s.creditsCharged}):(r.succeed("Generation complete."),{generationId:d,manifestPath:i,creditsCharged:s.creditsCharged});if(s.status==="failed")throw r.fail(s.error??"Generation failed."),new u(s.error??"Generation failed.",1);if(s.status==="refunded")throw r.fail("Generation refunded."),new u("Generation was refunded.",1)}throw r.fail("Generation timed out after 5 minutes."),new u(`Generation ${d} timed out. Check status with: vivipilot status ${d}`,1)}catch(a){throw r.fail(a instanceof Error?a.message:String(a)),a}}async function ae(t,e,n,i){if(!V(e,n))throw new u("Vivipilot generate is paid-only. Set VIVIPILOT_API_KEY or run `vivipilot login --api-key <key>`.",2);let c=v(t,["out","o"])??"scene.vivi.json",r=c.endsWith(".mp4")||c.endsWith(".webm")||c.endsWith(".gif")||c.endsWith(".mov"),a=r?`${c}.vivi.json`:c,d=v(t,["idempotency-key"])??`cli_${F()}`,b=K(t),l=await B(P(e,n),b,d,a,i);if(r){i.stderr.write(`
|
|
38
|
+
Rendering manifest directly to ${c}...
|
|
39
|
+
`);let s=c.endsWith(".mp4")?"mp4":c.endsWith(".webm")?"webm":c.endsWith(".gif")?"gif":"mov",m=p(t,["scale"]),C=$(t,["transparent"]),y=await E({manifestPath:a,outPath:c,format:s,...m?{scale:m}:{},...C?{transparent:C}:{}},e,n);try{await(await import("fs/promises")).unlink(a)}catch{}f(i,{ok:!0,manifestId:y.manifestId,out:y.outPath,size:y.size,paidOnly:!0});return}f(i,{ok:!0,generationId:l.generationId,manifestPath:l.manifestPath,creditsCharged:l.creditsCharged,paidOnly:!0})}function oe(t){t.stderr.write(`
|
|
38
40
|
\x1B[1m\x1B[36m Vivipilot Chat\x1B[0m (paid-only)
|
|
39
41
|
`),t.stderr.write(` Type a prompt to generate a motion graphics scene.
|
|
40
42
|
`),t.stderr.write(` Commands: /render <n>, /verify <n>, /list, /balance, /help, /quit
|
|
@@ -56,40 +58,40 @@ CLI/MCP generation is paid-only: no trial credits and no free generations.
|
|
|
56
58
|
- Use /render to render any scene to video after generation
|
|
57
59
|
- All generations are paid-only and charged to your API key
|
|
58
60
|
|
|
59
|
-
`)}async function de(t,e,n,i){if(!
|
|
61
|
+
`)}async function de(t,e,n,i){if(!V(e,n))throw new u("Vivipilot chat is paid-only. Set VIVIPILOT_API_KEY or run `vivipilot login --api-key <key>`.",2);let c=v(t,["out-dir"])??".",r=p(t,["width","w"]),a=p(t,["height","h"]),d=p(t,["fps"]),b=p(t,["duration","duration-seconds"]),l=P(e,n),s={scenes:[],turnNumber:0};oe(i);try{let C=await l.balance();i.stderr.write(` \x1B[33mBalance:\x1B[0m ${C.paidBalance??C.balance} credits
|
|
60
62
|
|
|
61
63
|
`)}catch{i.stderr.write(` \x1B[33mCould not fetch balance.\x1B[0m
|
|
62
64
|
|
|
63
|
-
`)}let m=
|
|
65
|
+
`)}let m=H({input:process.stdin,output:process.stderr,prompt:"\x1B[36mvivipilot>\x1B[0m "});m.prompt();for await(let C of m){let y=C.trim();if(!y){m.prompt();continue}if(y.startsWith("/")){let[g,...z]=y.split(/\s+/),k=z.join(" ");switch(g){case"/quit":case"/exit":case"/q":i.stderr.write(`
|
|
64
66
|
Goodbye!
|
|
65
67
|
`),m.close();return;case"/help":case"/h":ce(i);break;case"/list":case"/ls":{if(s.scenes.length===0)i.stderr.write(` No scenes generated yet.
|
|
66
68
|
`);else{i.stderr.write(`
|
|
67
69
|
\x1B[1mGenerated Scenes:\x1B[0m
|
|
68
|
-
`);for(let
|
|
69
|
-
`),i.stderr.write(` \x1B[2m${
|
|
70
|
+
`);for(let o=0;o<s.scenes.length;o++){let h=s.scenes[o];i.stderr.write(` ${o+1}. ${h.manifestPath} (${h.creditsCharged??"?"} credits)
|
|
71
|
+
`),i.stderr.write(` \x1B[2m${h.prompt.slice(0,80)}${h.prompt.length>80?"...":""}\x1B[0m
|
|
70
72
|
`)}i.stderr.write(`
|
|
71
|
-
`)}break}case"/balance":case"/bal":{try{let
|
|
72
|
-
`)}catch(
|
|
73
|
-
`)}break}case"/estimate":case"/est":{if(!
|
|
74
|
-
`);break}try{let
|
|
75
|
-
`)}catch(
|
|
76
|
-
`)}break}case"/render":{let
|
|
77
|
-
`);break}let
|
|
78
|
-
`);try{let
|
|
79
|
-
`)}catch(
|
|
80
|
-
`)}break}case"/verify":{let
|
|
81
|
-
`);break}let
|
|
82
|
-
`):i.stderr.write(` \x1B[31m\u2717\x1B[0m Verification failed: ${
|
|
83
|
-
`)}catch(
|
|
73
|
+
`)}break}case"/balance":case"/bal":{try{let o=await l.balance();i.stderr.write(` \x1B[33mBalance:\x1B[0m ${o.paidBalance??o.balance} credits
|
|
74
|
+
`)}catch(o){i.stderr.write(` \x1B[31mError:\x1B[0m ${o instanceof Error?o.message:String(o)}
|
|
75
|
+
`)}break}case"/estimate":case"/est":{if(!k){i.stderr.write(` Usage: /estimate <prompt>
|
|
76
|
+
`);break}try{let o=await l.estimate(W(k,{width:r,height:a,fps:d,durationSeconds:b}));i.stderr.write(` \x1B[33mEstimate:\x1B[0m ${o.estimatedCredits} credits (engine: ${o.engine})
|
|
77
|
+
`)}catch(o){i.stderr.write(` \x1B[31mError:\x1B[0m ${o instanceof Error?o.message:String(o)}
|
|
78
|
+
`)}break}case"/render":{let o=parseInt(k,10)-1;if(isNaN(o)||o<0||o>=s.scenes.length){i.stderr.write(` Usage: /render <scene number 1-${s.scenes.length}>
|
|
79
|
+
`);break}let h=s.scenes[o],w=h.manifestPath.replace(/\.vivi\.json$/,".mp4");i.stderr.write(` Rendering ${h.manifestPath} -> ${w}...
|
|
80
|
+
`);try{let I=await E({manifestPath:h.manifestPath,outPath:w},e,n);i.stderr.write(` \x1B[32m\u2713\x1B[0m Rendered to ${I.outPath} (${I.size} bytes)
|
|
81
|
+
`)}catch(I){i.stderr.write(` \x1B[31m\u2717\x1B[0m Render failed: ${I instanceof Error?I.message:String(I)}
|
|
82
|
+
`)}break}case"/verify":{let o=parseInt(k,10)-1;if(isNaN(o)||o<0||o>=s.scenes.length){i.stderr.write(` Usage: /verify <scene number 1-${s.scenes.length}>
|
|
83
|
+
`);break}let h=s.scenes[o];try{let w=await M(h.manifestPath,e,n);w.ok?i.stderr.write(` \x1B[32m\u2713\x1B[0m Manifest verified: ${w.manifest.manifestId}
|
|
84
|
+
`):i.stderr.write(` \x1B[31m\u2717\x1B[0m Verification failed: ${w.message}
|
|
85
|
+
`)}catch(w){i.stderr.write(` \x1B[31m\u2717\x1B[0m ${w instanceof Error?w.message:String(w)}
|
|
84
86
|
`)}break}default:i.stderr.write(` Unknown command: ${g}. Type /help for available commands.
|
|
85
|
-
`)}m.prompt();continue}s.turnNumber++;let
|
|
86
|
-
`);try{let g=await
|
|
87
|
+
`)}m.prompt();continue}s.turnNumber++;let x=s.turnNumber,J=`${c}/scene_${x}.vivi.json`.replace(/^\.\//,""),Y=`chat_${F()}`;i.stderr.write(`
|
|
88
|
+
`);try{let g=await B(l,W(y,{width:r,height:a,fps:d,durationSeconds:b}),Y,J,i);s.scenes.push({generationId:g.generationId,manifestPath:g.manifestPath,prompt:y,creditsCharged:g.creditsCharged}),i.stderr.write(` \x1B[2mScene #${x} saved. Use /render ${x} to render, or type another prompt.\x1B[0m
|
|
87
89
|
|
|
88
90
|
`)}catch(g){i.stderr.write(` \x1B[31mGeneration failed:\x1B[0m ${g instanceof Error?g.message:String(g)}
|
|
89
91
|
|
|
90
92
|
`)}m.prompt()}s.scenes.length>0&&i.stderr.write(`
|
|
91
93
|
Session complete. ${s.scenes.length} scene(s) generated.
|
|
92
|
-
`)}async function me(t,e,n,i){let
|
|
94
|
+
`)}async function me(t,e,n,i){let c=D(t),r=await M(c,e,n);if(!r.ok)throw new u(`Manifest verification failed: ${r.message}`,1);f(i,{ok:!0,manifestId:r.manifest.manifestId,generationId:r.manifest.generationId,canonicalPayloadHash:r.canonicalPayloadHash})}async function le(t,e,n,i){let c=D(t),r=v(t,["out","o"])??"video.mp4",a=v(t,["format"]),d=p(t,["scale"]),b=p(t,["fps"]),l=$(t,["transparent"]),s=$(t,["verify-only"]),m=await E({manifestPath:c,outPath:r,...a?{format:a}:{},...d?{scale:d}:{},...b?{fps:b}:{},...l?{transparent:l}:{},verifyOnly:s},e,n);f(i,{ok:!0,manifestId:m.manifestId,out:m.outPath,size:m.size,paidOnly:!0})}async function pe(t,e,n){let{startMcpServer:i}=await import("./mcp-DTGU64NI.js");await i({config:t,env:e,stdin:process.stdin,stdout:n.stdout,stderr:n.stderr})}async function ue(t=process.argv.slice(2),e=process.env,n={stdout:process.stdout,stderr:process.stderr}){let i=R(t);if(!i.command||i.command==="help"||$(i,["help"])){n.stdout.write(q);return}let c=S(e),r=await A(c);switch(i.command){case"login":await te(i,e,n);return;case"logout":await ie(e,n);return;case"whoami":f(n,await P(r,e).whoami());return;case"balance":f(n,await P(r,e).balance());return;case"topup":await ne(r,e,n);return;case"estimate":f(n,await P(r,e).estimate(K(i)));return;case"generate":await ae(i,r,e,n);return;case"chat":await de(i,r,e,n);return;case"status":{let a=i.positionals[0];if(!a)throw new u("Missing generation id.",2);f(n,await P(r,e).generationStatus(a));return}case"verify":await me(i,r,e,n);return;case"render":await le(i,r,e,n);return;case"mcp":await pe(r,e,n);return;default:throw new u(`Unknown command: ${i.command}
|
|
93
95
|
|
|
94
96
|
${q}`,2)}}ue().catch(t=>{if(T(t)){process.stderr.write(`${t.message}
|
|
95
97
|
`),process.exitCode=t.exitCode;return}process.stderr.write(`${t instanceof Error?t.stack??t.message:String(t)}
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,b as r,c as t,d as o}from"./chunk-ZZ72PPC3.js";import{a as C,b as A}from"./chunk-FN7FHZ3D.js";import{a as R}from"./chunk-
|
|
1
|
+
import{a as e,b as r,c as t,d as o}from"./chunk-ZZ72PPC3.js";import{a as C,b as A}from"./chunk-FN7FHZ3D.js";import{a as R}from"./chunk-ZIATVRTF.js";import{a as v}from"./chunk-FGUKEVKT.js";import{a as p,b as i,c as s,d as a,e as l,f as n,g as f,h as y,i as g,j as m}from"./chunk-L3C7EOPY.js";export{g as CliError,p as DEFAULT_API_URL,v as VivipilotApiClient,i as defaultConfigPath,l as deleteConfig,t as flagBoolean,o as flagNumber,r as flagString,m as isCliError,e as parseArgv,s as readConfig,C as readManifestFile,f as resolveApiKey,n as resolveApiUrl,y as resolvePublicKeys,R as startMcpServer,A as verifyManifestFile,a as writeConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a}from"./chunk-ZIATVRTF.js";import"./chunk-FGUKEVKT.js";import"./chunk-L3C7EOPY.js";export{a as startMcpServer};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a}from"./chunk-JJDAKVXA.js";import"./chunk-FN7FHZ3D.js";import"./chunk-L3C7EOPY.js";export{a as renderManifest};
|
package/package.json
CHANGED
package/dist/chunk-RW7OWVPD.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import{b as _,c as j,d as D,e as F}from"./chunk-FN7FHZ3D.js";import{i as w}from"./chunk-L3C7EOPY.js";import{spawn as ce}from"child_process";import{createServer as V}from"http";import{readFile as k,writeFile as I,access as K,stat as le,mkdir as he}from"fs/promises";import{constants as de,openSync as ue,writeSync as J,closeSync as me}from"fs";import{join as T}from"path";import{createHash as fe}from"crypto";import{existsSync as re}from"fs";import{access as ne,constants as oe}from"fs/promises";import{homedir as E}from"os";import{join as g}from"path";var O=class extends Error{constructor(e){super(e),this.name="BrowserNotFoundError"}};async function U(t=process.env){let e=t.VIVIPILOT_HEADLESS_BROWSER;if(e&&await S(e))return{executablePath:e,source:"env"};let n=await se();if(n)return{executablePath:n,source:"chrome-headless-shell"};let a=await ae();if(a)return{executablePath:a,source:"system-chrome"};let o=await ie();if(o)return{executablePath:o,source:"system-chromium"};throw new O("No headless browser found. Install chrome-headless-shell or Chrome/Chromium, or set VIVIPILOT_HEADLESS_BROWSER to the binary path.")}async function S(t){try{return await ne(t,oe.X_OK),!0}catch{return!1}}async function se(){let t=[process.env.PUPPETEER_EXECUTABLE_PATH,g(E(),".cache","puppeteer","chrome-headless-shell"),"/usr/bin/chrome-headless-shell","/usr/local/bin/chrome-headless-shell","/snap/bin/chrome-headless-shell",g(E(),".local","share","chrome-headless-shell","chrome-headless-shell"),"/opt/chrome-headless-shell/chrome-headless-shell","/Applications/Google Chrome Headless Shell.app/Contents/MacOS/Google Chrome Headless Shell",g(E(),"AppData","Local","chrome-headless-shell","chrome-headless-shell.exe")];for(let e of t)if(e){if(await S(e))return e;if(re(e)&&!e.endsWith("chrome-headless-shell")&&!e.endsWith(".exe")){let{readdirSync:n}=await import("fs");try{let a=n(e,{withFileTypes:!0});for(let o of a){if(!o.isDirectory())continue;let h=g(e,o.name,"chrome-headless-shell");if(await S(h))return h;let i=g(e,o.name,"chrome-headless-shell.exe");if(await S(i))return i}}catch{}}}return null}async function ae(){let t=["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/usr/bin/google-chrome-unstable","/usr/local/bin/google-chrome","/opt/google/chrome/chrome","/snap/bin/google-chrome","/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",g(E(),"AppData","Local","Google","Chrome","Application","chrome.exe"),"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"];for(let e of t)if(await S(e))return e;return null}async function ie(){let t=["/usr/bin/chromium","/usr/bin/chromium-browser","/usr/local/bin/chromium","/snap/bin/chromium","/usr/bin/brave-browser","/usr/bin/brave","/Applications/Chromium.app/Contents/MacOS/Chromium",g(E(),"AppData","Local","Chromium","Application","chromium.exe"),"C:\\Program Files\\Chromium\\Application\\chromium.exe"];for(let e of t)if(await S(e))return e;return null}function G(t,e){let n=e?.windowSize?.width??1920,a=e?.windowSize?.height??1080;return["--headless=new","--no-sandbox","--disable-gpu-sandbox","--disable-dev-shm-usage","--disable-extensions","--disable-background-networking","--disable-sync","--disable-translate","--disable-default-apps","--disable-popup-blocking","--disable-component-update","--disable-metrics","--no-first-run","--mute-audio","--enable-logging=stderr","--log-level=0","--v=1",`--window-size=${n},${a}`,"--enable-features=Vulkan","--use-gl=angle","--use-angle=swiftshader","--enable-unsafe-swiftshader","--ignore-gpu-blocklist",...e?.extraArgs??[],t]}var pe="http://localhost:4001/headless-render",we=3e5;function ge(t){return new Promise((e,n)=>{let a=null,o=null,h=V(async(i,c)=>{if(c.setHeader("Access-Control-Allow-Origin","*"),c.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),c.setHeader("Access-Control-Allow-Headers","Content-Type"),i.method==="OPTIONS"){c.writeHead(204),c.end();return}let p=new URL(i.url??"/","http://localhost");if(p.pathname==="/manifest.json"&&i.method==="GET"){try{let u=await k(t.replace(/\.mp4$|\.webm$|\.gif$|\.mov$/,".vivi.json"),"utf8");c.writeHead(200,{"content-type":"application/json"}),c.end(u)}catch{c.writeHead(404),c.end("Manifest not found")}return}if(p.pathname==="/result"&&i.method==="POST"){let u=[];i.on("data",m=>u.push(m)),i.on("end",async()=>{a=Buffer.concat(u);try{await I(t,a),c.writeHead(200,{"content-type":"application/json"}),c.end(JSON.stringify({ok:!0,size:a.length}))}catch(m){c.writeHead(500,{"content-type":"application/json"}),c.end(JSON.stringify({ok:!1,error:m instanceof Error?m.message:String(m)}))}});return}if(p.pathname==="/error"&&i.method==="POST"){let u=[];i.on("data",m=>u.push(m)),i.on("end",()=>{try{o=JSON.parse(Buffer.concat(u).toString()).message??"Unknown render error"}catch{o=Buffer.concat(u).toString()||"Unknown render error"}c.writeHead(200,{"content-type":"application/json"}),c.end(JSON.stringify({ok:!0}))});return}if(p.pathname==="/status"&&i.method==="GET"){c.writeHead(200,{"content-type":"application/json"}),c.end(JSON.stringify({done:a!==null||o!==null,error:o,hasBlob:a!==null,blobSize:a?.length??0}));return}c.writeHead(404),c.end("Not found")});h.on("error",n),h.listen(0,"127.0.0.1",()=>{let i=h.address();if(!i||typeof i=="string"){n(new Error("Failed to bind callback server"));return}e({server:h,port:i.port})})})}async function be(t,e){let n=Date.now()+e;for(;Date.now()<n;){try{let o=await(await fetch(`http://127.0.0.1:${t}/status`)).json();if(o.done)return o.error?{size:0,error:o.error}:{size:o.blobSize,error:null}}catch{}await new Promise(a=>setTimeout(a,1e3))}return{size:0,error:`Render timed out after ${e/1e3}s`}}async function Ne(t,e,n=process.env){let{manifestPath:a,outPath:o}=t,h=await _(a,e,n);if(!h.ok)throw new w(`Manifest verification failed: ${h.message}`,1);let i=await j(h.manifest,e,n);D(i);let c={...t.format?{format:t.format}:{},...t.width?{width:t.width}:{},...t.height?{height:t.height}:{}},p=F(h.manifest,c);if(!p.ok)throw new w(p.message,1);if(t.verifyOnly)return{ok:!0,outPath:o,size:0,manifestId:h.manifest.manifestId};let u;try{u=await U(n)}catch(r){throw r instanceof O?new w(r.message,1):r}let m=await k(a,"utf8"),b=JSON.parse(m),N=b.render.canvas,B="";if(b.render.assets&&Array.isArray(b.render.assets))try{B=await ye(b.render.assets)}catch(r){throw new w(`Failed to cache assets: ${r instanceof Error?r.message:String(r)}`,1)}let{server:W}=await ge(o);W.close();let P=null,H=!1,$=0,v=V((r,s)=>{if(s.setHeader("Access-Control-Allow-Origin","*"),s.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),s.setHeader("Access-Control-Allow-Headers","Content-Type"),r.method==="OPTIONS"){s.writeHead(204),s.end();return}let f=new URL(r.url??"/","http://localhost");if(B&&f.pathname.startsWith("/assets/")&&r.method==="GET"){let d=f.pathname.slice(8),l=T(B,d);k(l).then(y=>{let ee=b.render.assets?.find(te=>te.sha256===d);s.writeHead(200,{"content-type":ee?.mimeType||"application/octet-stream"}),s.end(y)}).catch(()=>{s.writeHead(404),s.end("Asset not found")});return}if(f.pathname==="/manifest.json"&&r.method==="GET"){let d={...b};d.render?.assets&&(d.render.assets=d.render.assets.map(l=>({...l,url:`http://127.0.0.1:${R}/assets/${l.sha256}`}))),s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify(d));return}if(f.pathname==="/result"&&r.method==="POST"){let d=[];r.on("data",l=>d.push(l)),r.on("end",async()=>{let l=Buffer.concat(d);try{await I(o,l),H=!0,$=l.length,s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({ok:!0,size:l.length}))}catch(y){P=y instanceof Error?y.message:String(y),s.writeHead(500,{"content-type":"application/json"}),s.end(JSON.stringify({ok:!1,error:P}))}});return}if(f.pathname==="/error"&&r.method==="POST"){let d=[];r.on("data",l=>d.push(l)),r.on("end",()=>{let l="Unknown render error";try{l=JSON.parse(Buffer.concat(d).toString()).message??l}catch{l=Buffer.concat(d).toString()||l}P=l,s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({ok:!0,error:l}))});return}if(f.pathname==="/status"&&r.method==="GET"){s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({done:H||P!==null,error:P,hasBlob:H,blobSize:$}));return}s.writeHead(404),s.end("Not found")}),R=await new Promise((r,s)=>{v.on("error",s),v.listen(0,"127.0.0.1",()=>{let f=v.address();if(!f||typeof f=="string"){s(new Error("Failed to bind manifest server"));return}r(f.port)})}),X=n.VIVIPILOT_RENDER_URL??pe,A=new URLSearchParams({manifest:`http://127.0.0.1:${R}/manifest.json`,format:t.format??"mp4",headless:"true",callback:`http://127.0.0.1:${R}/result`});t.scale&&A.set("scale",String(t.scale)),t.transparent&&A.set("transparent","true"),t.fps&&A.set("fps",String(t.fps));let Y=`${X}?${A.toString()}`,Q=G(Y,{windowSize:{width:Math.max(N.width,1280),height:Math.max(N.height,720)}}),C=ce(u.executablePath,Q,{stdio:["ignore","pipe","pipe"],env:{...n,DISPLAY:n.DISPLAY??":99"}}),Z=T(process.cwd(),"host","chrome_browser.log"),x=ue(Z,"w"),L="",z="";C.stderr?.on("data",r=>{L+=r.toString();try{J(x,r)}catch{}}),C.stdout?.on("data",r=>{z+=r.toString();try{J(x,r)}catch{}});let q=parseInt(n.VIVIPILOT_RENDER_TIMEOUT_MS??String(we),10),M=await be(R,q);C.kill("SIGTERM"),setTimeout(()=>{try{C.kill("SIGKILL")}catch{}},5e3),v.close();try{me(x)}catch{}if(M.error)throw new w(`Render failed: ${M.error}
|
|
2
|
-
Browser stderr (last 2KB):
|
|
3
|
-
${L.slice(-2e3)}
|
|
4
|
-
Browser stdout (last 1KB):
|
|
5
|
-
${z.slice(-1e3)}`,1);try{await K(o,de.R_OK);let r=await le(o);return{ok:!0,outPath:o,size:r.size,manifestId:h.manifest.manifestId}}catch{throw new w(`Render completed but output file not found at ${o}`,1)}}async function ye(t){let e=T(process.env.HOME||process.env.USERPROFILE||process.cwd(),".cache","vivipilot","assets");await he(e,{recursive:!0});for(let n of t){if(!n.sha256||!n.url)continue;let a=T(e,n.sha256);try{await K(a);continue}catch{}let o=await fetch(n.url);if(!o.ok)throw new Error(`Failed to download asset ${n.url}: HTTP ${o.status}`);let h=Buffer.from(await o.arrayBuffer()),i=fe("sha256").update(h).digest("hex");if(i!==n.sha256)throw new Error(`Asset hash mismatch for ${n.url}: expected ${n.sha256}, got ${i}`);await I(a,h)}return e}export{Ne as a};
|
package/dist/chunk-TZJPBE3B.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{f as a,g as p,i}from"./chunk-L3C7EOPY.js";var u=class{config;env;fetchImpl;constructor(e){this.config=e.config,this.env=e.env??process.env,this.fetchImpl=e.fetchImpl??fetch}apiUrl(){return a(this.config,this.env)}apiKey(){let e=p(this.config,this.env);if(!e)throw new i("Vivipilot CLI/MCP generation is paid-only. Set VIVIPILOT_API_KEY or run `vivipilot login --api-key <key>`.",2);return e}async requestJson(e,t={}){let n=new Headers(t.headers);n.set("accept","application/json"),n.set("authorization",`Bearer ${this.apiKey()}`),n.set("x-vivipilot-client","@vivipilot/cli"),t.body&&!n.has("content-type")&&n.set("content-type","application/json");let s=await this.fetchImpl(`${this.apiUrl()}${e}`,{...t,headers:n}),o=await s.text(),r=o.length>0?JSON.parse(o):{};if(!s.ok){let l=r&&typeof r=="object"&&"error"in r?String(r.error):`Vivipilot API request failed with status ${s.status}`,g=s.status===402?" Run `vivipilot topup` to buy credits.":"";throw new i(`${l}${g}`,s.status===401?2:1)}return r}async whoami(){return this.requestJson("/api/v1/me")}async balance(){return this.requestJson("/api/v1/credits/balance")}async estimate(e){return this.requestJson("/api/v1/layouts/estimate",{method:"POST",body:JSON.stringify(e)})}async generate(e,t){return this.requestJson("/api/v1/layouts/generate",{method:"POST",headers:{"idempotency-key":t},body:JSON.stringify({...e,outputFormat:"manifest"})})}async startGenerate(e,t){return this.requestJson("/api/v1/layouts/generate/start",{method:"POST",headers:{"idempotency-key":t},body:JSON.stringify({...e,outputFormat:"manifest"})})}async generationProgress(e){return this.requestJson(`/api/v1/layouts/${encodeURIComponent(e)}?progress=true`)}async generationStatus(e){return this.requestJson(`/api/v1/layouts/${encodeURIComponent(e)}`)}};export{u as a};
|
package/dist/mcp-MRADYIPQ.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a}from"./chunk-RB2L6BY5.js";import"./chunk-TZJPBE3B.js";import"./chunk-L3C7EOPY.js";export{a as startMcpServer};
|
package/dist/render-TJGWLKPC.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a}from"./chunk-RW7OWVPD.js";import"./chunk-FN7FHZ3D.js";import"./chunk-L3C7EOPY.js";export{a as renderManifest};
|