@shotstack/cli 0.3.0 → 0.3.1
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/SKILL.md +16 -6
- package/dist/shotstack.js +1 -1
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -10,6 +10,8 @@ license: Apache-2.0
|
|
|
10
10
|
|
|
11
11
|
# Shotstack CLI
|
|
12
12
|
|
|
13
|
+
This skill loads in **terminal-based AI agents** (Claude Code, Cursor, Codex CLI, Gemini CLI, etc.). All operations here are shell commands. There is no embedded UI, no iframe, no inline canvas, no MCP tool surface — only `shotstack` invocations from a terminal. To hand off to a human, run `shotstack studio <file>`; this opens the user's default browser to a `shotstack.studio` URL. Tell the user to click Render *in the browser tab*, not in any UI inside the terminal.
|
|
14
|
+
|
|
13
15
|
Two commands for the Shotstack video rendering API. `render` submits an Edit JSON and returns a render ID; `status` polls a render until done.
|
|
14
16
|
|
|
15
17
|
## Authentication
|
|
@@ -70,15 +72,23 @@ Use `render` only when you're confident the JSON is final, or there's no human t
|
|
|
70
72
|
|
|
71
73
|
3. **Fetch the current schema and docs before generating Edit JSON.** The Shotstack API evolves; LLM training data is often stale. Pull <https://shotstack.io/docs/api/api.edit.json> and <https://shotstack.io/docs/guide/llms-full.txt> for the current schema and guides before composing an Edit from scratch.
|
|
72
74
|
|
|
73
|
-
4. **Hand off to a human via `studio
|
|
74
|
-
|
|
75
|
-
## Authoring Edit JSON
|
|
75
|
+
4. **Hand off to a human via `shotstack studio <file>` when uncertain.** Don't burn render credits iterating. Generate JSON → `shotstack studio file.json` (opens browser) → human reviews/tweaks → render only when right.
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
## REQUIRED: Read `shared/agent-core.md` before composing any Edit JSON
|
|
78
78
|
|
|
79
|
-
**
|
|
79
|
+
The Shotstack schema does **not** match CSS or web conventions. Composing Edit JSON from training-data instinct will produce silently invalid renders. **Read [`shared/agent-core.md`](shared/agent-core.md) first**, every time. The most-failed conversions:
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
| You'd write (wrong) | API requires (right) |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `alignment` | `align` |
|
|
84
|
+
| `align.vertical: "center"` | `align.vertical: "middle"` |
|
|
85
|
+
| `font.name` | `font.family` |
|
|
86
|
+
| `duration` | `length` |
|
|
87
|
+
| `transitions: [...]` (array) | `transition: { in, out }` (object) |
|
|
88
|
+
| `fit: "cover"` (CSS instinct) | `fit: "crop"` |
|
|
89
|
+
| `tracks[0]` is the bottom (z-index instinct) | `tracks[0]` is the **TOP** layer |
|
|
90
|
+
|
|
91
|
+
The full ruleset (asset types, fonts, smart-string clip values, top-5 mistakes) lives in `shared/agent-core.md`. The same file is also returned by the Shotstack MCP server's `get_shotstack_guide` tool, so the conventions are identical across surfaces.
|
|
82
92
|
|
|
83
93
|
## Exit codes
|
|
84
94
|
|
package/dist/shotstack.js
CHANGED
|
@@ -20,7 +20,7 @@ Expecting one of '${E.join("', '")}'`);if(this._lifeCycleHooks[f])this._lifeCycl
|
|
|
20
20
|
`),this.outputHelp({error:!0});let E=r||{},t=E.exitCode||1,S=E.code||"commander.error";this._exit(t,S,f)}_parseOptionsEnv(){this.options.forEach((f)=>{if(f.envVar&&f.envVar in L.env){let r=f.attributeName();if(this.getOptionValue(r)===void 0||["default","config","env"].includes(this.getOptionValueSource(r)))if(f.required||f.optional)this.emit(`optionEnv:${f.name()}`,L.env[f.envVar]);else this.emit(`optionEnv:${f.name()}`)}})}_parseOptionsImplied(){let f=new Ir(this.options),r=(E)=>{return this.getOptionValue(E)!==void 0&&!["default","implied"].includes(this.getOptionValueSource(E))};this.options.filter((E)=>E.implied!==void 0&&r(E.attributeName())&&f.valueFromOption(this.getOptionValue(E.attributeName()),E)).forEach((E)=>{Object.keys(E.implied).filter((t)=>!r(t)).forEach((t)=>{this.setOptionValueWithSource(t,E.implied[t],"implied")})})}missingArgument(f){let r=`error: missing required argument '${f}'`;this.error(r,{code:"commander.missingArgument"})}optionMissingArgument(f){let r=`error: option '${f.flags}' argument missing`;this.error(r,{code:"commander.optionMissingArgument"})}missingMandatoryOptionValue(f){let r=`error: required option '${f.flags}' not specified`;this.error(r,{code:"commander.missingMandatoryOptionValue"})}_conflictingOption(f,r){let E=(R)=>{let w=R.attributeName(),T=this.getOptionValue(w),$=this.options.find((y)=>y.negate&&w===y.attributeName()),_=this.options.find((y)=>!y.negate&&w===y.attributeName());if($&&($.presetArg===void 0&&T===!1||$.presetArg!==void 0&&T===$.presetArg))return $;return _||R},t=(R)=>{let w=E(R),T=w.attributeName();if(this.getOptionValueSource(T)==="env")return`environment variable '${w.envVar}'`;return`option '${w.flags}'`},S=`error: ${t(f)} cannot be used with ${t(r)}`;this.error(S,{code:"commander.conflictingOption"})}unknownOption(f){if(this._allowUnknownOption)return;let r="";if(f.startsWith("--")&&this._showSuggestionAfterError){let t=[],S=this;do{let R=S.createHelp().visibleOptions(S).filter((w)=>w.long).map((w)=>w.long);t=t.concat(R),S=S.parent}while(S&&!S._enablePositionalOptions);r=tf(f,t)}let E=`error: unknown option '${f}'${r}`;this.error(E,{code:"commander.unknownOption"})}_excessArguments(f){if(this._allowExcessArguments)return;let r=this.registeredArguments.length,E=r===1?"":"s",S=`error: too many arguments${this.parent?` for '${this.name()}'`:""}. Expected ${r} argument${E} but got ${f.length}.`;this.error(S,{code:"commander.excessArguments"})}unknownCommand(){let f=this.args[0],r="";if(this._showSuggestionAfterError){let t=[];this.createHelp().visibleCommands(this).forEach((S)=>{if(t.push(S.name()),S.alias())t.push(S.alias())}),r=tf(f,t)}let E=`error: unknown command '${f}'${r}`;this.error(E,{code:"commander.unknownCommand"})}version(f,r,E){if(f===void 0)return this._version;this._version=f,r=r||"-V, --version",E=E||"output the version number";let t=this.createOption(r,E);return this._versionOptionName=t.attributeName(),this._registerOption(t),this.on("option:"+t.name(),()=>{this._outputConfiguration.writeOut(`${f}
|
|
21
21
|
`),this._exit(0,"commander.version",f)}),this}description(f,r){if(f===void 0&&r===void 0)return this._description;if(this._description=f,r)this._argsDescription=r;return this}summary(f){if(f===void 0)return this._summary;return this._summary=f,this}alias(f){if(f===void 0)return this._aliases[0];let r=this;if(this.commands.length!==0&&this.commands[this.commands.length-1]._executableHandler)r=this.commands[this.commands.length-1];if(f===r._name)throw Error("Command alias can't be the same as its name");let E=this.parent?._findCommand(f);if(E){let t=[E.name()].concat(E.aliases()).join("|");throw Error(`cannot add alias '${f}' to command '${this.name()}' as already have command '${t}'`)}return r._aliases.push(f),this}aliases(f){if(f===void 0)return this._aliases;return f.forEach((r)=>this.alias(r)),this}usage(f){if(f===void 0){if(this._usage)return this._usage;let r=this.registeredArguments.map((E)=>{return yr(E)});return[].concat(this.options.length||this._helpOption!==null?"[options]":[],this.commands.length?"[command]":[],this.registeredArguments.length?r:[]).join(" ")}return this._usage=f,this}name(f){if(f===void 0)return this._name;return this._name=f,this}nameFromFilename(f){return this._name=q.basename(f,q.extname(f)),this}executableDir(f){if(f===void 0)return this._executableDir;return this._executableDir=f,this}helpInformation(f){let r=this.createHelp();if(r.helpWidth===void 0)r.helpWidth=f&&f.error?this._outputConfiguration.getErrHelpWidth():this._outputConfiguration.getOutHelpWidth();return r.formatHelp(this,r)}_getHelpContext(f){f=f||{};let r={error:!!f.error},E;if(r.error)E=(t)=>this._outputConfiguration.writeErr(t);else E=(t)=>this._outputConfiguration.writeOut(t);return r.write=f.write||E,r.command=this,r}outputHelp(f){let r;if(typeof f==="function")r=f,f=void 0;let E=this._getHelpContext(f);this._getCommandAndAncestors().reverse().forEach((S)=>S.emit("beforeAllHelp",E)),this.emit("beforeHelp",E);let t=this.helpInformation(E);if(r){if(t=r(t),typeof t!=="string"&&!Buffer.isBuffer(t))throw Error("outputHelp callback must return a string or a Buffer")}if(E.write(t),this._getHelpOption()?.long)this.emit(this._getHelpOption().long);this.emit("afterHelp",E),this._getCommandAndAncestors().forEach((S)=>S.emit("afterAllHelp",E))}helpOption(f,r){if(typeof f==="boolean"){if(f)this._helpOption=this._helpOption??void 0;else this._helpOption=null;return this}return f=f??"-h, --help",r=r??"display help for command",this._helpOption=this.createOption(f,r),this}_getHelpOption(){if(this._helpOption===void 0)this.helpOption(void 0,void 0);return this._helpOption}addHelpOption(f){return this._helpOption=f,this}help(f){this.outputHelp(f);let r=L.exitCode||0;if(r===0&&f&&typeof f!=="function"&&f.error)r=1;this._exit(r,"commander.help","(outputHelp)")}addHelpText(f,r){let E=["beforeAll","before","after","afterAll"];if(!E.includes(f))throw Error(`Unexpected value for position to addHelpText.
|
|
22
22
|
Expecting one of '${E.join("', '")}'`);let t=`${f}Help`;return this.on(t,(S)=>{let R;if(typeof r==="function")R=r({error:S.error,command:S.command});else R=r;if(R)S.write(`${R}
|
|
23
|
-
`)}),this}_outputHelpIfRequested(f){let r=this._getHelpOption();if(r&&f.find((t)=>r.is(t)))this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)")}}function Sf(f){return f.map((r)=>{if(!r.startsWith("--inspect"))return r;let E,t="127.0.0.1",S="9229",R;if((R=r.match(/^(--inspect(-brk)?)$/))!==null)E=R[1];else if((R=r.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null)if(E=R[1],/^\d+$/.test(R[3]))S=R[3];else t=R[3];else if((R=r.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null)E=R[1],t=R[3],S=R[4];if(E&&S!=="0")return`${E}=${t}:${parseInt(S)+1}`;return r})}Ur.Command=v});var _f=M((ur)=>{var{Argument:wf}=H(),{Command:c}=Rf(),{CommanderError:Pr,InvalidArgumentError:Tf}=Z(),{Help:qr}=b(),{Option:$f}=x();ur.program=new c;ur.createCommand=(f)=>new c(f);ur.createOption=(f,r)=>new $f(f,r);ur.createArgument=(f,r)=>new wf(f,r);ur.Command=c;ur.Option=$f;ur.Argument=wf;ur.Help=qr;ur.CommanderError=Pr;ur.InvalidArgumentError=Tf;ur.InvalidOptionArgumentError=Tf});var yf=xf(_f(),1),{program:bE,createCommand:xE,createArgument:hE,createOption:CE,CommanderError:OE,InvalidArgumentError:vE,InvalidOptionArgumentError:cE,Command:J,Argument:nE,Option:iE,Help:gE}=yf.default;import{readFile as ar}from"node:fs/promises";import{resolve as fE}from"node:path";var Lf={name:"@shotstack/cli",version:"0.3.
|
|
23
|
+
`)}),this}_outputHelpIfRequested(f){let r=this._getHelpOption();if(r&&f.find((t)=>r.is(t)))this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)")}}function Sf(f){return f.map((r)=>{if(!r.startsWith("--inspect"))return r;let E,t="127.0.0.1",S="9229",R;if((R=r.match(/^(--inspect(-brk)?)$/))!==null)E=R[1];else if((R=r.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null)if(E=R[1],/^\d+$/.test(R[3]))S=R[3];else t=R[3];else if((R=r.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null)E=R[1],t=R[3],S=R[4];if(E&&S!=="0")return`${E}=${t}:${parseInt(S)+1}`;return r})}Ur.Command=v});var _f=M((ur)=>{var{Argument:wf}=H(),{Command:c}=Rf(),{CommanderError:Pr,InvalidArgumentError:Tf}=Z(),{Help:qr}=b(),{Option:$f}=x();ur.program=new c;ur.createCommand=(f)=>new c(f);ur.createOption=(f,r)=>new $f(f,r);ur.createArgument=(f,r)=>new wf(f,r);ur.Command=c;ur.Option=$f;ur.Argument=wf;ur.Help=qr;ur.CommanderError=Pr;ur.InvalidArgumentError=Tf;ur.InvalidOptionArgumentError=Tf});var yf=xf(_f(),1),{program:bE,createCommand:xE,createArgument:hE,createOption:CE,CommanderError:OE,InvalidArgumentError:vE,InvalidOptionArgumentError:cE,Command:J,Argument:nE,Option:iE,Help:gE}=yf.default;import{readFile as ar}from"node:fs/promises";import{resolve as fE}from"node:path";var Lf={name:"@shotstack/cli",version:"0.3.1",description:"Command-line interface for the Shotstack video rendering API.",license:"Apache-2.0",homepage:"https://github.com/shotstack/shotstack-cli",repository:{type:"git",url:"git+https://github.com/shotstack/shotstack-cli.git"},bugs:{url:"https://github.com/shotstack/shotstack-cli/issues"},type:"module",bin:{shotstack:"dist/shotstack.js"},files:["dist/shotstack.js","SKILL.md","references"],scripts:{start:"bun run src/index.ts",build:"bun build src/index.ts --target=node --minify --outfile=dist/shotstack.js --banner='#!/usr/bin/env node' && chmod +x dist/shotstack.js",prepack:"bun run build",typecheck:"tsc --noEmit",test:"bun test"},dependencies:{commander:"^12.1.0"},devDependencies:{"@types/bun":"latest",typescript:"^5.6.0"}};var Q=Lf.version,If=`shotstack-cli/${Q} (${process.platform}-${process.arch})`;class j extends Error{status;body;constructor(f,r,E){super(E);this.status=f;this.body=r;this.name="ApiError"}get isTransient(){return this.status>=500||this.status===429}}function K(f){let r=f.env.baseUrl.replace(/\/$/,"");async function E(t,S,R){let w=await fetch(`${r}${S}`,{method:t,headers:{"x-api-key":f.apiKey,"x-shotstack-origin":"cli","x-shotstack-environment":f.env.name,"user-agent":If,...R!==void 0?{"content-type":"application/json"}:{}},body:R!==void 0?JSON.stringify(R):void 0}),T=await w.text(),$=T?Wr(T):void 0;if(!w.ok)throw new j(w.status,$??T,`${t} ${S} → ${w.status} ${w.statusText}`);return $}return{post:(t,S)=>E("POST",t,S),get:(t)=>E("GET",t)}}function Wr(f){try{return JSON.parse(f)}catch{return f}}class Y extends Error{constructor(){super("SHOTSTACK_API_KEY is not set. Get a key at https://shotstack.io.");this.name="MissingApiKeyError"}}function W(){let f=process.env.SHOTSTACK_API_KEY?.trim();if(!f)throw new Y;return f}var Uf={dev:"https://api.shotstack.io/edit/dev",stage:"https://api.shotstack.io/edit/stage",v1:"https://api.shotstack.io/edit/v1"},G=Object.keys(Uf);class X extends Error{constructor(f){super(`Invalid environment "${f}". Use one of: ${G.join(", ")}.`);this.name="InvalidEnvError"}}function s(f){let r=process.env.SHOTSTACK_API_BASE_URL?.trim();if(r)return{name:"custom",baseUrl:r};let E=f??process.env.SHOTSTACK_ENV?.trim()??"v1";if(!sr(E))throw new X(E);return{name:E,baseUrl:Uf[E]}}function sr(f){return G.includes(f)}function B(f){if(f==="json")return"json";return"text"}function z(f,r,E){if(f==="json")console.log(JSON.stringify(r));else console.log(E)}import{mkdir as kr,appendFile as Dr,stat as uf,readFile as Fr,writeFile as Mf}from"node:fs/promises";import{join as n,dirname as Ar}from"node:path";import{homedir as Jf}from"node:os";var Vr=1e6,br=500000,Pf=2000;function k(){if(process.platform==="win32"){let f=process.env.APPDATA??n(Jf(),"AppData","Roaming");return n(f,"shotstack","log.jsonl")}return n(Jf(),".shotstack","log.jsonl")}function xr(f,r=new Date){let E={ts:r.toISOString(),cmd:f.cmd,args:f.args.map(ir),exit:f.exit,durationMs:f.durationMs};if(f.renderId)E.renderId=f.renderId;if(f.response!==void 0)E.response=or(i(f.response));if(f.error)E.error=hr(f.error);return E}function hr(f){return{name:f.name,message:typeof f.message==="string"?g(f.message):String(f.message)}}function Cr(f){if(f instanceof Error)return{name:f.name,message:f.message};if(f===void 0||f===null)return;return{name:"Unknown",message:String(f)}}async function qf(f,r=k()){let E=xr(f),t=JSON.stringify(E)+`
|
|
24
24
|
`;if(await kr(Ar(r),{recursive:!0,mode:448}),!await Or(r))await Mf(r,"",{mode:384});await Dr(r,t),await vr(r)}async function Or(f){try{return await uf(f),!0}catch{return!1}}async function vr(f){let r=await uf(f).catch(()=>null);if(!r||r.size<=Vr)return;let E=await Fr(f),t=E.subarray(Math.max(0,E.length-br)),S=t.indexOf(10),R=S>=0?t.subarray(S+1):t;await Mf(f,R,{mode:384})}var cr=/^https?:\/\//i,nr=/(https?:\/\/[^\s'"<>]+?)\?[^\s'"<>]*?(?:Signature|X-Amz-[A-Za-z0-9-]*)=[^\s'"<>]*/gi;function g(f){return f.replace(nr,"$1?[redacted-signed]")}function ir(f){if(typeof f!=="string")return f;if(f.startsWith("-"))return f;if(cr.test(f))return g(f);if(/[\/\\]/.test(f))return gr(f);return f}function gr(f){let r=f.split(/[\/\\]/);return r[r.length-1]||f}function i(f){if(typeof f==="string")return g(f);if(Array.isArray(f))return f.map(i);if(f&&typeof f==="object"){let r={};for(let[E,t]of Object.entries(f))r[E]=i(t);return r}return f}function or(f){let r=JSON.stringify(f);if(r===void 0||r.length<=Pf)return f;return r.slice(0,Pf)+"...[truncated]"}async function D(f,r,E){let t=Date.now();try{let S=await E(),R=S.exitCode??0;if(await qf({cmd:f,args:r,exit:R,durationMs:Date.now()-t,renderId:S.renderId,response:S.response}).catch(()=>{}),R!==0)process.exit(R)}catch(S){let R=lr(S),w=S instanceof j?S.body:void 0;throw await qf({cmd:f,args:r,exit:R,durationMs:Date.now()-t,response:w,error:Cr(S)}).catch(()=>{}),S}}function lr(f){if(f instanceof j)return f.isTransient?2:1;if(f instanceof Y||f instanceof X)return 1;if(f instanceof Error&&f.code==="ENOENT")return 1;return 2}function F(f){let r=process.argv.slice(2),E=r.indexOf(f);return E>=0?r.slice(E+1):r}var mr=new Set(["done","failed"]),dr=3000,jf=new J("status").description("Poll the status of a Shotstack render").argument("<id>","Render ID returned by `shotstack render`").option("--watch","Poll continuously until the render reaches a terminal state").option("--env <name>",`Environment: ${G.join(" | ")}`).option("--output <format>","Output format: text | json","text").action(async(f,r)=>{await D("status",F("status"),async()=>{let E=B(r.output),t=W(),S=s(r.env),R=K({apiKey:t,env:S}),w=await o(R,f,E,r.watch===!0),T=w.status==="failed"?1:0;return{renderId:w.id,response:w,exitCode:T}})});async function o(f,r,E,t){while(!0){let R=(await f.get(`/render/${encodeURIComponent(r)}`)).response;if(z(E,R,er(R)),!t||mr.has(R.status))return R;await pr(dr)}}function er(f){let r=[f.status];if(f.url)r.push(f.url);if(f.error)r.push(`error: ${f.error}`);return r.join(" ")}function pr(f){return new Promise((r)=>setTimeout(r,f))}var Yf=new J("render").description("Submit a Shotstack Edit JSON to the render API").argument("<file>","Path to a Shotstack Edit JSON file").option("--env <name>",`Environment: ${G.join(" | ")}`).option("--watch","After submitting, poll until the render reaches a terminal state").option("--output <format>","Output format: text | json","text").action(async(f,r)=>{await D("render",F("render"),async()=>{let E=B(r.output),t=W(),S=s(r.env),R=fE(process.cwd(),f),w=await ar(R,"utf8"),T=JSON.parse(w),$=K({apiKey:t,env:S}),_=await $.post("/render",T),y=_.response.id;if(!r.watch)return z(E,{id:y},y),{renderId:y,response:_.response};let U=await o($,y,E,!0),P=U.status==="failed"?1:0;return{renderId:y,response:U,exitCode:P}})});import{readFile as rE,writeFile as EE,mkdtemp as tE}from"node:fs/promises";import{tmpdir as SE}from"node:os";import{join as Gf}from"node:path";import{spawn as RE}from"node:child_process";var Xf="https://github.com/shotstack/shotstack-cli/issues/new?template=bug.yml",wE=6000,Zf=5,Qf=new J("feedback").description("Open a pre-filled GitHub issue with a flight-recorder dossier from your last 5 invocations").action(async()=>{let f=await TE(Zf),r=$E(f),E=yE(f),t=`&title=${encodeURIComponent(E)}`,S=`${Xf}${t}&body=${encodeURIComponent(r)}`;if(S.length<=wE){Nf(S),console.log("Opened browser with pre-filled issue. Review before submitting.");return}let R=await tE(Gf(SE(),"shotstack-feedback-")),w=Gf(R,"dossier.md");await EE(w,r),Nf(`${Xf}${t}`),console.log(`Dossier too large to inline. Saved to: ${w}`),console.log("Drag the file into the issue body, or paste its contents.")});async function TE(f){let r=k(),E;try{E=await rE(r,"utf8")}catch{return[]}let S=E.split(`
|
|
25
25
|
`).filter((w)=>w.trim().length>0).slice(-f).reverse(),R=[];for(let w of S)try{R.push(JSON.parse(w))}catch{}return R}function $E(f){let r=[];if(r.push("## Environment"),r.push(`- Shotstack CLI: ${Q}`),r.push(`- OS: ${process.platform}-${process.arch}`),r.push(""),r.push(`## Last ${Zf} invocations (most recent first)`),r.push(""),f.length===0)r.push("_No recorded invocations._");else for(let E of f){let t=(E.args??[]).join(" "),S=`### ${E.ts} — shotstack ${E.cmd}${t?" "+t:""}`;if(r.push(S),r.push(`- Exit: ${E.exit} Duration: ${E.durationMs}ms`),E.renderId)r.push(`- Render ID: ${E.renderId}`);if(E.response!==void 0){let R=typeof E.response==="string"?E.response:JSON.stringify(E.response);r.push(`- Response: ${R}`)}if(E.error)r.push(`- Error: ${E.error.name}: ${E.error.message}`);r.push("")}return r.push(`[full log at ${k()}]`),r.push(""),r.push("## What I expected vs what happened"),r.push("<!-- Describe the issue, request, or question here. -->"),r.join(`
|
|
26
26
|
`)}var Bf=80,_E="Feedback from shotstack CLI";function yE(f){let r=f.find((S)=>S.error);if(r&&r.error)return l(`${r.cmd}: ${r.error.name} — ${r.error.message}`);let E=f.find((S)=>zf(S.response)!==null);if(E){let S=zf(E.response);return l(`Render failed: ${S}`)}let t=f.find((S)=>S.exit!==0);if(t)return l(`${t.cmd} exited ${t.exit}`);return _E}function zf(f){if(!f||typeof f!=="object"||Array.isArray(f))return null;let r=f;if(r.state!=="failed")return null;return typeof r.error==="string"&&r.error.length>0?r.error:"unknown error"}function l(f){return f.length<=Bf?f:f.slice(0,Bf-1)+"…"}function Nf(f){let r=process.platform==="win32",E=process.platform==="darwin"?"open":r?"cmd":"xdg-open",S=RE(E,r?["/c","start","",f]:[f],{stdio:"ignore",detached:!0});S.on("error",()=>{console.error(`Could not open browser. Visit: ${f}`)}),S.unref()}import{readFile as LE}from"node:fs/promises";import{resolve as IE}from"node:path";import{spawn as Hf,execSync as UE}from"node:child_process";var Kf="https://shotstack.studio",JE=`${Kf}/api/share`,PE=3000,qE=6000,Wf=new J("studio").description("Open the Edit JSON in the shotstack.studio web editor. By default, posts the JSON to the share API and returns a short URL.").argument("<file>","Path to a Shotstack Edit JSON file").option("--copy","Copy the URL to the clipboard").option("--no-open","Do not try to open the URL in a browser").option("--no-shorten","Skip the share API; emit a base64url URL inline").option("--output <format>","Output format: text | json","text").action(async(f,r)=>{let E=B(r.output),t=IE(process.cwd(),f),S=await LE(t,"utf8"),R=JSON.parse(S);YE(R);let{url:w,shortened:T}=await uE(R,{shorten:r.shorten});if(!T&&w.length>qE)console.error(`warning: encoded URL is ${w.length} characters; large templates may exceed browser URL limits.`);if(r.copy)await GE(w);let $=r.open&&NE()&&BE(w);if(E==="json"||!$)z(E,{url:w,shortened:T},w)});async function uE(f,r={}){if(r.shorten!==!1){let t=await jE(f);if(t)return{url:t,shortened:!0};console.error("warning: could not reach the shotstack.studio share API; falling back to inline URL.")}return{url:ME(f),shortened:!1}}function ME(f){let r=Buffer.from(JSON.stringify(f),"utf8").toString("base64url");return`${Kf}/#json=${r}`}async function jE(f){try{let r=await fetch(JE,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(f),signal:AbortSignal.timeout(PE)});if(!r.ok)return null;return(await r.json()).url??null}catch{return null}}function YE(f){if(!f||typeof f!=="object")throw Error("Template must be a JSON object.");if(!("timeline"in f))throw Error("Template missing required field: timeline.");if(!("output"in f))throw Error("Template missing required field: output.")}async function GE(f){let{cmd:r,args:E}=XE();await new Promise((t,S)=>{let R=Hf(r,E,{stdio:["pipe","ignore","ignore"]});R.on("error",S),R.on("exit",(w)=>w===0?t():S(Error(`${r} exited ${w}`))),R.stdin.end(f)})}function XE(){if(process.platform==="darwin")return{cmd:"pbcopy",args:[]};if(process.platform==="win32")return{cmd:"clip",args:[]};return{cmd:"xclip",args:["-selection","clipboard"]}}function BE(f){let{cmd:r,args:E}=zE(f);try{let t=Hf(r,E,{detached:!0,stdio:"ignore"});return t.on("error",()=>{}),t.unref(),!0}catch{return!1}}function zE(f){if(process.platform==="win32")return{cmd:"cmd",args:["/c","start","",f]};if(process.platform==="darwin")return{cmd:"open",args:[f]};return{cmd:"xdg-open",args:[f]}}function NE(){if(process.platform==="darwin"||process.platform==="win32")return!0;if(!process.env.DISPLAY&&!process.env.WAYLAND_DISPLAY)return!1;try{return UE("command -v xdg-open",{stdio:"ignore"}),!0}catch{return!1}}var ZE=new J().name("shotstack").description("Command-line interface for the Shotstack video rendering API").version(Q).addCommand(Yf).addCommand(jf).addCommand(Wf).addCommand(Qf);try{await ZE.parseAsync(process.argv)}catch(f){process.exit(QE(f))}function QE(f){if(f instanceof Y||f instanceof X)return console.error(f.message),1;if(f instanceof j){if(console.error(f.message),f.body)console.error(typeof f.body==="string"?f.body:JSON.stringify(f.body));return f.isTransient?2:1}if(f instanceof Error){if("code"in f&&f.code==="ENOENT")return console.error(f.message),1;return console.error(f.message),2}return console.error(String(f)),2}
|
package/package.json
CHANGED