pixelvault-cli 0.15.0 → 0.17.0

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 CHANGED
@@ -33,10 +33,17 @@ pixelvault upload screenshot.png
33
33
  # Upload multiple images
34
34
  pixelvault upload *.png --folder screenshots
35
35
 
36
- # Upload an MP4 without buffering it in memory
36
+ # Upload a local MP4 without buffering it in memory
37
37
  pixelvault upload demo.mp4
38
38
  # https://img.pixelvault.dev/proj_abc/vid_xyz.mp4
39
39
 
40
+ # Or import a public MP4 without downloading it first
41
+ pixelvault upload "https://media.example.com/demo.mp4"
42
+ # https://img.pixelvault.dev/proj_abc/vid_xyz.mp4
43
+
44
+ # Private video + expiry-matched signed poster URL
45
+ pixelvault upload demo.mp4 --private --expires 12h --json
46
+
40
47
  # List your images
41
48
  pixelvault list
42
49
 
@@ -65,8 +72,8 @@ pixelvault export -o my-images.tar
65
72
  The CLI is designed for AI coding agents. Output contract:
66
73
 
67
74
  - `upload` prints **only the URL** to stdout for images and MP4 videos (add `--optimized` for the smaller WebP/AVIF image variant; it falls back to the original URL for SVG, GIF and private images)
68
- - `upload` streams MP4 files and automatically switches to multipart upload above 100 MB. Video limits: Free 25 MB, Starter 100 MB, Pro 250 MB
69
- - Image uploads support `--idempotency-key` and `--dedupe`; those options and the other image-specific flags are rejected for MP4 files
75
+ - `upload` streams local MP4 files and automatically switches to multipart upload above 100 MB. It also accepts public MP4 URLs and has PixelVault import them directly. `--private [--expires 12h]` returns signed video and poster URLs. Video limits: Free 25 MB, Starter 100 MB, Pro 250 MB
76
+ - Image uploads support `--idempotency-key` and `--dedupe`; folder, optimization, reuse, metadata, and AI-description options are rejected for MP4 files and URLs
70
77
  - `list` outputs one URL per line
71
78
  - `meta` prints the stored metadata as JSON
72
79
  - `delete` outputs nothing on success
@@ -94,7 +101,7 @@ npx pixelvault-cli upload build-output.png
94
101
  |---------|-------------|
95
102
  | `register` | Create a new account |
96
103
  | `login` | Log in to existing account |
97
- | `upload <files...>` | Upload images or MP4 videos (prints URLs to stdout) |
104
+ | `upload <files-or-urls...>` | Upload images, MP4 files, or public MP4 URLs (prints URLs to stdout) |
98
105
  | `generate <prompt>` | Generate an image from a prompt and host it (prints the URL; spends monthly AI credits) |
99
106
  | `list` | List uploaded images (filter by `--tag`, `--search`, `--meta`) |
100
107
  | `tags` | List the AI tags on your described images with counts |
@@ -117,16 +124,21 @@ npx pixelvault-cli upload build-output.png
117
124
  pixelvault upload photo.jpg # Single file
118
125
  pixelvault upload *.png --folder icons # Bulk with folder
119
126
  pixelvault upload shot.png --json # Full JSON response
120
- pixelvault upload demo.mp4 --json # Stream an MP4; multipart above 100 MB
127
+ pixelvault upload demo.mp4 --json # Stream a local MP4; response includes poster_url
128
+ pixelvault upload "https://media.example.com/demo.mp4" --private --expires 12h --json
121
129
  pixelvault upload shot.png --meta env=prod --meta run=42 # Tag with custom metadata (key=value, repeatable)
122
130
  pixelvault upload shot.png --metadata '{"n":3}' # ...or a JSON object (typed values)
123
131
  pixelvault upload shot.png --describe # AI alt text + caption + OCR in the background
124
132
  pixelvault upload shot.png --describe --wait --json # ...wait (~10s) and include it in the JSON
125
133
  ```
126
134
 
127
- MP4 uploads support `--json`; image-only options such as `--folder`, `--private`,
128
- `--optimized`, reuse, metadata, and AI descriptions are rejected. Maximum video
129
- size is 25 MB on Free, 100 MB on Starter, and 250 MB on Pro.
135
+ MP4 file and URL uploads support `--private`, `--expires`, and `--json`; image-only
136
+ options such as `--folder`, `--optimized`, reuse, metadata, and AI descriptions
137
+ are rejected. PixelVault stores the original video without transcoding and returns
138
+ a generated JPEG `poster_url` when frame extraction succeeds. Remote imports require
139
+ a public HTTP(S) URL whose response includes a valid `Content-Length`; PixelVault
140
+ fetches the video directly. Maximum video size is 25 MB on Free, 100 MB on Starter,
141
+ and 250 MB on Pro.
130
142
 
131
143
  ### Generate Options
132
144
 
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- var Dt=Object.defineProperty;var w=(e,t)=>()=>(e&&(t=e(e=0)),t);var $=(e,t)=>{for(var r in t)Dt(e,r,{get:t[r],enumerable:!0})};var s,S=w(()=>{"use strict";s=class extends Error{exitCode;constructor(t,r=1){super(t),this.name="CliError",this.exitCode=r}}});function f(e){process.stdout.write(e+`
1
+ var Lt=Object.defineProperty;var w=(e,t)=>()=>(e&&(t=e(e=0)),t);var S=(e,t)=>{for(var r in t)Lt(e,r,{get:t[r],enumerable:!0})};var s,$=w(()=>{"use strict";s=class extends Error{exitCode;constructor(t,r=1){super(t),this.name="CliError",this.exitCode=r}}});function f(e){process.stdout.write(e+`
2
2
  `)}function p(e){process.stderr.write(e+`
3
- `)}function g(e){f(JSON.stringify(e,null,2))}function T(e){if(e<1024)return`${e} B`;let t=["KB","MB","GB","TB"],r=e/1024,i=0;for(;r>=1024&&i<t.length-1;)r/=1024,i++;return`${r.toFixed(r>=100||Number.isInteger(r)?0:1)} ${t[i]}`}var k=w(()=>{"use strict"});import{readFileSync as Ut,writeFileSync as Lt,mkdirSync as Bt}from"fs";import{join as je}from"path";import{homedir as Kt}from"os";function Pe(){return pe}function I(){try{let e=Ut(pe,"utf-8");return JSON.parse(e)}catch{return{}}}function Vt(e){Bt(Oe,{recursive:!0,mode:448}),Lt(pe,JSON.stringify(e,null,2)+`
4
- `,{mode:384})}function N(e){let t=I();Vt({...t,...e})}function de(){return process.env.PIXELVAULT_API_KEY||I().api_key}function H(){return process.env.PIXELVAULT_API_URL||I().api_url||Mt}function h(){let e=de();if(!e)throw new Error("Not authenticated. Run `pixelvault register` or `pixelvault login`, or set PIXELVAULT_API_KEY.");return e}var Oe,pe,Mt,x=w(()=>{"use strict";Oe=je(Kt(),".pixelvault"),pe=je(Oe,"config.json"),Mt="https://api.pixelvault.dev"});async function d(e){let r=`${H()}${e.path}`,i={...e.headers};i["X-PixelVault-Client"]||(i["X-PixelVault-Client"]=Jt),e.auth&&(i.Authorization=`Bearer ${e.auth}`);let n;e.formData?n=e.formData:e.rawBody!==void 0?n=e.rawBody:e.body!==void 0&&(i["Content-Type"]="application/json",n=JSON.stringify(e.body));let o;try{let a={method:e.method||"GET",headers:i,body:n};n instanceof ReadableStream&&(a.duplex="half"),o=await fetch(r,a)}catch(a){throw new s(`Network error: ${a instanceof Error?a.message:"Failed to connect"}`,1)}if(!o.ok){let a;try{a=await o.json()}catch{}let u=a?.error?.message||`HTTP ${o.status}: ${o.statusText}`;throw new q(u,o.status,a?.error?.code,o.headers?.get?.("Retry-After")??void 0)}return await o.json()}var le,Jt,q,_=w(()=>{"use strict";x();S();le="0.15.0",Jt=`pixelvault-cli/${le}`,q=class extends s{status;code;retryAfter;constructor(t,r,i,n){super(t,1),this.name="ApiError",this.status=r,this.code=i,this.retryAfter=n}}});import{createInterface as Gt}from"readline";function G(e){let t=Gt({input:process.stdin,output:process.stderr});return new Promise(r=>{t.question(e,i=>{t.close(),r(i.trim())})})}async function Q(e){return process.stdin.isTTY?new Promise(t=>{process.stderr.write(e),process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.setEncoding("utf-8");let r="",i=n=>{n===`
3
+ `)}function h(e){f(JSON.stringify(e,null,2))}function U(e){if(e<1024)return`${e} B`;let t=["KB","MB","GB","TB"],r=e/1024,i=0;for(;r>=1024&&i<t.length-1;)r/=1024,i++;return`${r.toFixed(r>=100||Number.isInteger(r)?0:1)} ${t[i]}`}var k=w(()=>{"use strict"});import{readFileSync as Bt,writeFileSync as Mt,mkdirSync as Kt}from"fs";import{join as Oe}from"path";import{homedir as Vt}from"os";function Ee(){return de}function I(){try{let e=Bt(de,"utf-8");return JSON.parse(e)}catch{return{}}}function Gt(e){Kt(Pe,{recursive:!0,mode:448}),Mt(de,JSON.stringify(e,null,2)+`
4
+ `,{mode:384})}function N(e){let t=I();Gt({...t,...e})}function le(){return process.env.PIXELVAULT_API_KEY||I().api_key}function H(){return process.env.PIXELVAULT_API_URL||I().api_url||Jt}function y(){let e=le();if(!e)throw new Error("Not authenticated. Run `pixelvault register` or `pixelvault login`, or set PIXELVAULT_API_KEY.");return e}var Pe,de,Jt,x=w(()=>{"use strict";Pe=Oe(Vt(),".pixelvault"),de=Oe(Pe,"config.json"),Jt="https://api.pixelvault.dev"});async function l(e){let r=`${H()}${e.path}`,i={...e.headers};i["X-PixelVault-Client"]||(i["X-PixelVault-Client"]=Xt),e.auth&&(i.Authorization=`Bearer ${e.auth}`);let n;e.formData?n=e.formData:e.rawBody!==void 0?n=e.rawBody:e.body!==void 0&&(i["Content-Type"]="application/json",n=JSON.stringify(e.body));let o;try{let a={method:e.method||"GET",headers:i,body:n};n instanceof ReadableStream&&(a.duplex="half"),o=await fetch(r,a)}catch(a){throw new s(`Network error: ${a instanceof Error?a.message:"Failed to connect"}`,1)}if(!o.ok){let a;try{a=await o.json()}catch{}let u=a?.error?.message||`HTTP ${o.status}: ${o.statusText}`;throw new D(u,o.status,a?.error?.code,o.headers?.get?.("Retry-After")??void 0)}return await o.json()}var ue,Xt,D,_=w(()=>{"use strict";x();$();ue="0.17.0",Xt=`pixelvault-cli/${ue}`,D=class extends s{status;code;retryAfter;constructor(t,r,i,n){super(t,1),this.name="ApiError",this.status=r,this.code=i,this.retryAfter=n}}});import{createInterface as Wt}from"readline";function G(e){let t=Wt({input:process.stdin,output:process.stderr});return new Promise(r=>{t.question(e,i=>{t.close(),r(i.trim())})})}async function Q(e){return process.stdin.isTTY?new Promise(t=>{process.stderr.write(e),process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.setEncoding("utf-8");let r="",i=n=>{n===`
5
5
  `||n==="\r"||n===""?(process.stdin.setRawMode(!1),process.stdin.pause(),process.stdin.removeListener("data",i),process.stderr.write(`
6
- `),t(r)):n===""?(process.stdin.setRawMode(!1),process.exit(130)):n==="\x7F"||n==="\b"?r=r.slice(0,-1):r+=n};process.stdin.on("data",i)}):G(e)}var ue=w(()=>{"use strict"});var Ee={};$(Ee,{default:()=>Xt});import{defineCommand as Wt}from"citty";var Xt,Ie=w(()=>{"use strict";_();x();ue();k();Xt=Wt({meta:{name:"register",description:"Create a new PixelVault account"},args:{email:{type:"string",description:"Email address"},password:{type:"string",description:"Password (min 8 characters). Omit for a passwordless account."},passwordless:{type:"boolean",description:"Create the account without a password (for agents/automation). Set a password later via the web 'Forgot password' flow."}},async run({args:e}){let t=!!process.stdin.isTTY,r=e.email||(t?await G("Email: "):"");r||(p("Email is required. Pass --email <email>."),process.exit(1));let i;e.password?i=e.password:e.passwordless?i=void 0:t?i=await Q("Password (leave blank for a passwordless account): ")||void 0:i=void 0,i!==void 0&&i.length<8&&(p("Password must be at least 8 characters."),process.exit(1)),p("Creating account...");let n=await d({method:"POST",path:"/v1/auth/register",body:{email:r,...i?{password:i}:{},attribution:{source:"cli"}}});N({api_key:n.data.default_project.api_keys.live,default_project:n.data.default_project.id,email:n.data.email}),p("Account created. API key saved to ~/.pixelvault/config.json"),p(`Email: ${n.data.email}`),p(`Project: ${n.data.default_project.id}`),(n.data.password_set===!1||!i)&&p("No password set. To enable dashboard login, use 'Forgot password' at https://pixelvault.dev/forgot-password to set one."),n.data.email_verified||p("Verify your email to lift the upload limit for the free tier.")}})});var Ce={};$(Ce,{default:()=>Ht});import{defineCommand as Yt}from"citty";var Ht,Ae=w(()=>{"use strict";_();x();ue();k();Ht=Yt({meta:{name:"login",description:"Log in to your PixelVault account"},args:{email:{type:"string",description:"Email address"},password:{type:"string",description:"Password"}},async run({args:e}){let t=e.email||await G("Email: "),r=e.password||await Q("Password: ");(!t||!r)&&(p("Email and password are required."),process.exit(1)),p("Logging in...");let i=await d({method:"POST",path:"/v1/auth/login",body:{email:t,password:r}}),n=i.data.token,o;try{o=(await d({method:"GET",path:"/v1/projects",auth:n})).data[0]?.id}catch{}if(o){let a=await d({method:"POST",path:"/v1/auth/rotate-key",auth:n,body:{project_id:o}});N({api_key:a.data.api_key,default_project:o,email:i.data.email}),p("Logged in. API key saved to ~/.pixelvault/config.json"),p(`Email: ${i.data.email}`),p(`Project: ${o}`)}else p(`Logged in as ${i.data.email}.`),p("Could not auto-configure API key. Set PIXELVAULT_API_KEY manually."),N({email:i.data.email,default_project:void 0}),I().api_key&&p("Warning: the saved api_key may belong to a previous login. Replace it with `pixelvault config set api_key <key>`.")}})});function W(e,t="--expires"){let r=/^(\d+)\s*([smhd]?)$/i.exec(e.trim());if(!r)throw new s(`Invalid ${t} "${e}". Use e.g. 30m, 12h, 7d, or a number of seconds.`);let i=(r[2]??"").toLowerCase(),n=i==="d"?86400:i==="h"?3600:i==="m"?60:1,o=Number(r[1]??"")*n;if(o<Qt||o>Zt)throw new s(`${t} must be between 60s and 30d (got ${o}s).`);return o}var Qt,Zt,ce=w(()=>{"use strict";S();Qt=60,Zt=720*60*60});function Te(e){let{idempotencyKey:t,dedupe:r,fileCount:i}=e;if(t!==void 0){if(!ei.test(t))return"--idempotency-key must be 1-255 printable ASCII characters with no spaces.";if(r)return"--dedupe cannot be combined with --idempotency-key. Use the key for retry-safety, or --dedupe to reuse an identical image.";if(i>1)return"--idempotency-key identifies a single upload, so it can't be used with multiple files. Upload them one at a time with distinct keys."}return null}var ei,Ne=w(()=>{"use strict";ei=/^[\x21-\x7e]{1,255}$/});function qe(e){return e==null||e===""?[]:(Array.isArray(e)?e:[e]).map(String)}function D(e){let t=new Map,r=qe(e.metadata);if(r.length>1)throw new s("--metadata can be given only once.");if(r.length===1){let i;try{i=JSON.parse(r[0])}catch{throw new s(`--metadata must be a JSON object, e.g. --metadata '{"env":"prod"}'.`)}if(i===null||typeof i!="object"||Array.isArray(i))throw new s(`--metadata must be a JSON object, e.g. --metadata '{"env":"prod"}'.`);for(let[n,o]of Object.entries(i))t.set(n,o)}for(let i of qe(e.meta)){let n=i.indexOf("=");if(n<=0)throw new s(`--meta expects key=value, got "${i}".`);let o=i.slice(0,n),a=i.slice(n+1);if(t.has(o)&&t.get(o)!==a)throw new s(`Metadata key "${o}" was given with conflicting values.`);t.set(o,a)}return t.size===0?null:Object.fromEntries(t)}var Z=w(()=>{"use strict";S()});async function ee(e,t,r={}){let i=r.sleep??(u=>new Promise(b=>setTimeout(b,u))),n=r.now??Date.now,o=n()+ii,a=null;for(;;){if(a=(await d({path:`/v1/images/${encodeURIComponent(e)}`,auth:t})).data.description??null,!a||a.status!=="pending"||n()>=o)return a;await i(ti)}}function F(e){return e?e.status==="done"?`Alt: ${e.alt??""}${e.tags?.length?` (tags: ${e.tags.join(", ")})`:""}`:e.status==="failed"?`Description failed (${e.error??"unknown"}).`:"Description is still pending; run `pixelvault get <id> --json` in a few seconds.":"No description was produced."}var ti,ii,me=w(()=>{"use strict";_();ti=2e3,ii=4e4});import{createReadStream as ri,statSync as ni}from"fs";import{basename as oi}from"path";import{Readable as ai}from"stream";function ze(e,t,r){let i=ri(e,{...t!==void 0?{start:t}:{},...r!==void 0?{end:r}:{}});return ai.toWeb(i)}async function De(e,t){let r=ni(e);if(!r.isFile()||r.size<=0)throw new s("Video must be a non-empty file.",1);let i=oi(e);if(r.size<=si){let{data:o}=await d({method:"POST",path:"/v1/videos",auth:t,rawBody:ze(e),headers:{"Content-Type":"video/mp4","Content-Length":String(r.size),"X-PixelVault-Filename":encodeURIComponent(i)}});return o}let n;try{let o=await d({method:"POST",path:"/v1/videos/uploads",auth:t,body:{filename:i,size:r.size}});n=o.data.upload_id;let a=o.data.part_size;if(!Number.isSafeInteger(a)||a<=0)throw new s("The API returned an invalid video part size.",1);let u=1;for(let c=0;c<r.size;c+=a,u+=1){let l=Math.min(a,r.size-c);await d({method:"PUT",path:`/v1/videos/uploads/${n}/parts/${u}`,auth:t,rawBody:ze(e,c,c+l-1),headers:{"Content-Length":String(l)}})}let{data:b}=await d({method:"POST",path:`/v1/videos/uploads/${n}/complete`,auth:t});return b}catch(o){throw n&&await d({method:"DELETE",path:`/v1/videos/uploads/${n}`,auth:t}).catch(()=>{}),o}}var si,Fe=w(()=>{"use strict";_();S();si=1e8});var Le={};$(Le,{default:()=>ui});import{defineCommand as pi}from"citty";import{readFileSync as di}from"fs";import{basename as li,extname as Ue}from"path";var ui,Be=w(()=>{"use strict";_();x();ce();Ne();Z();me();k();Fe();ui=pi({meta:{name:"upload",description:"Upload one or more images or MP4 videos"},args:{files:{type:"positional",description:"Image or MP4 file path(s) to upload",required:!0},folder:{type:"string",description:"Folder to organize images in"},private:{type:"boolean",description:"Upload privately \u2014 returns a signed URL only holders of the link can open (needs a secret key)",default:!1},expires:{type:"string",description:"Lifetime of a --private signed link: e.g. 30m, 12h, 7d (default 7d; min 60s, max 30d)"},optimized:{type:"boolean",description:"Print the optimized URL (WebP/AVIF, usually much smaller) instead of the original; falls back to the original URL when none is returned (SVG, GIF, private)",default:!1},"idempotency-key":{type:"string",description:"Make retries safe: repeating the same key with the same file returns the original image instead of storing a copy (single file only)"},dedupe:{type:"boolean",description:"Reuse an identical image already in your project instead of storing and counting another upload",default:!1},meta:{type:"string",description:"Attach custom metadata as key=value (repeatable), e.g. --meta env=prod --meta run=42"},metadata:{type:"string",description:`Attach custom metadata as a JSON object, e.g. --metadata '{"env":"prod","n":3}'`},describe:{type:"boolean",description:"Generate an AI description (alt text, caption, OCR) in the background; needs a secret key. Free 20 in total, Starter 1,000/mo, Pro 10,000/mo. Read it with `get <id> --json`, or add --wait",default:!1},wait:{type:"boolean",description:"With --describe: wait up to ~40s for the description and include it in --json output (alt text goes to stderr otherwise)",default:!1},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let t=h(),r=null;e.expires!==void 0&&(e.private||(p("--expires only applies to --private uploads (it sets the signed link's lifetime)."),process.exit(1)),r=W(String(e.expires))),e.private&&e.folder&&p("Note: --folder is ignored for --private uploads.");let i=e._.length?e._.map(String):[String(e.files)],n=i.some(y=>Ue(y).toLowerCase()===".mp4"),o=e.folder||e.private||e.expires||e.optimized||e["idempotency-key"]||e.dedupe||e.meta||e.metadata||e.describe||e.wait;n&&o&&(p("MP4 uploads support only --json. Folder, private-link, optimization, reuse, metadata, and AI-description options are image-only."),process.exit(1));let a=!1,u=e["idempotency-key"],b=Te({idempotencyKey:u,dedupe:e.dedupe,fileCount:i.length});b&&(p(b),process.exit(1));let c=D({meta:e.meta,metadata:e.metadata});e.wait&&!e.describe&&(p("--wait only applies together with --describe."),process.exit(1));let l=[];for(let y of i)try{if(Ue(y).toLowerCase()===".mp4"){let R=await De(y,t);e.json?l.push(R):f(R.url);continue}let E=di(y),K=li(y),O=new FormData;O.append("file",new File([E],K)),e.folder&&O.append("folder",e.folder),e.private&&(O.append("visibility","private"),r!==null&&O.append("sign_expires_in",String(r))),e.dedupe&&O.append("dedupe","true"),c&&O.append("metadata",JSON.stringify(c)),e.describe&&O.append("describe","true");let v=await d({method:"POST",path:"/v1/images",auth:t,formData:O,...u?{headers:{"Idempotency-Key":u}}:{}});if(v.data.replayed?p(`Replayed: ${y} was already uploaded with this key; returning the original.`):v.data.deduplicated&&p(`Deduplicated: ${y} matches an existing image; nothing new was stored.`),e.private&&v.data.visibility!=="private"){a=!0,p(`Refusing to report ${y}: server did not confirm a private upload.`);continue}e.describe&&(v.data.describe_skipped?p(`Description skipped for ${y}: ${v.data.describe_skipped}.`):e.wait&&v.data.description?.status==="pending"?(v.data.description=await ee(v.data.id,t),e.json||p(F(v.data.description??null))):v.data.description?.status==="pending"?p(`Describing ${y} in the background; read it with: pixelvault get ${v.data.id} --json`):v.data.description?.status==="done"&&!e.json&&p(F(v.data.description))),e.json?l.push(v.data):f(e.optimized?v.data.optimized_url??v.data.url:v.data.url)}catch(E){a=!0,p(`Failed to upload ${y}: ${E instanceof Error?E.message:String(E)}`)}e.json&&l.length>0&&g(l.length===1?l[0]:l),a&&process.exit(1)}})});var Ke={};$(Ke,{default:()=>mi});import{defineCommand as ci}from"citty";var mi,Me=w(()=>{"use strict";_();x();S();ce();k();mi=ci({meta:{name:"generate",description:"Generate an image from a prompt and host it (spends monthly AI credits; secret key required)"},args:{prompt:{type:"positional",description:"What to draw. Quote it, or pass several words",required:!0},model:{type:"string",alias:"m",description:"flux-1-schnell (default, 1 credit), sdxl-lightning or sdxl-base (1, paid plans), lucid-origin or phoenix-1.0 (20, paid plans)"},folder:{type:"string",description:"Folder to organize the image in"},private:{type:"boolean",description:"Keep it private: returns a signed URL only holders of the link can open",default:!1},expires:{type:"string",description:"Lifetime of a --private signed link: e.g. 30m, 12h, 7d (default 7d; min 60s, max 30d)"},"delete-after":{type:"string",description:"Delete the image automatically after this long: e.g. 1h, 7d (min 60s, max 30d)"},optimized:{type:"boolean",description:"Print the optimized URL (WebP/AVIF, usually much smaller) instead of the original; falls back to the original URL when none is returned",default:!1},json:{type:"boolean",description:"Output the full JSON response",default:!1}},async run({args:e}){let t=h(),r=(e._.length?e._.map(String):[String(e.prompt??"")]).join(" ").trim();if(!r)throw new s('Give a prompt, e.g. pixelvault generate "a red bicycle".');if(e.expires!==void 0&&!e.private)throw new s("--expires only applies to --private images (it sets the signed link's lifetime). Use --delete-after to expire the image itself.");let i=e.expires!==void 0?W(String(e.expires)):void 0,n=e["delete-after"]!==void 0?W(String(e["delete-after"]),"--delete-after"):void 0;e.private&&e.folder&&p("Note: --folder is ignored for --private images.");let o=await d({method:"POST",path:"/v1/images/generate",auth:t,body:{prompt:r,...e.model?{model:String(e.model)}:{},...e.folder?{folder:String(e.folder)}:{},...e.private?{visibility:"private"}:{},...i!==void 0?{sign_expires_in:i}:{},...n!==void 0?{expires_in:n}:{}}});if(e.private&&o.data.visibility!=="private")throw new s("Refusing to report the image: the server did not confirm it is private.");let a=o.data.generation;p(`Generated with ${a.model}: ${a.credits_charged} credit${a.credits_charged===1?"":"s"} used, ${a.credits_remaining} left this month.`),e.json?g(o.data):f(e.optimized?o.data.optimized_url??o.data.url:o.data.url)}})});var Ve={};$(Ve,{default:()=>gi});import{defineCommand as fi}from"citty";var gi,Je=w(()=>{"use strict";_();x();k();Z();gi=fi({meta:{name:"list",description:"List uploaded images"},args:{page:{type:"string",description:"Page number",default:"1"},"per-page":{type:"string",description:"Items per page (max 100)",default:"20"},search:{type:"string",description:'Search the AI descriptions (alt text, caption, OCR text) of images uploaded with --describe, best match first, e.g. --search "invoice 4821" (quotes, -word work)',alias:"q"},tag:{type:"string",description:"Only images carrying this AI tag (one tag, e.g. --tag boat); see `pixelvault tags`"},meta:{type:"string",description:"Only images whose metadata has key=value (repeatable; string match), e.g. --meta env=prod"},metadata:{type:"string",description:`Only images whose metadata contains this JSON object (typed match), e.g. --metadata '{"n":3}'`},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let t=h(),r=new URLSearchParams({page:e.page,per_page:e["per-page"]}),i=D({meta:e.meta,metadata:e.metadata});i&&r.set("metadata",JSON.stringify(i));let n=typeof e.search=="string"?e.search.trim():"";n&&r.set("q",n);let o=typeof e.tag=="string"?e.tag.trim():"";o&&r.set("tag",o);let a=await d({path:`/v1/images?${r}`,auth:t});if(e.json)g(a);else for(let u of a.data)f(u.url)}})});var Ge={};$(Ge,{default:()=>yi});import{defineCommand as hi}from"citty";var yi,We=w(()=>{"use strict";_();x();k();yi=hi({meta:{name:"tags",description:"List the AI tags on your described images with how many images carry each (filter with `list --tag`)"},args:{json:{type:"boolean",description:"Output the full JSON response",default:!1}},async run({args:e}){let t=h(),r=await d({path:"/v1/images/tags",auth:t});if(e.json)g(r);else for(let i of r.data)f(`${i.tag} ${i.count}`)}})});var Xe={};$(Xe,{default:()=>bi});import{defineCommand as wi}from"citty";var bi,Ye=w(()=>{"use strict";_();x();k();S();me();bi=wi({meta:{name:"describe",description:"Write an AI description (alt text, caption, OCR text, tags) for an image you already uploaded"},args:{id:{type:"positional",description:"Image ID (e.g. img_abc123)",required:!0},wait:{type:"boolean",description:"Wait up to ~40s for the description and print it (otherwise it runs in the background)",default:!1},json:{type:"boolean",description:"Output JSON ({ id, description }); with --wait it includes the full description",default:!1}},async run({args:e}){let t=h(),r=String(e.id),i=await d({method:"POST",path:`/v1/images/${encodeURIComponent(r)}/describe`,auth:t});if(i.data.describe_skipped)throw new s(`Description not started: ${i.data.describe_skipped}.`);let n=i.data.description??null;if(e.wait&&n?.status==="pending"&&(n=await ee(r,t)),n?.status==="failed")throw new s(F(n));if(e.json){g({id:r,description:n});return}n?.status==="done"?(f(n.alt??""),n.tags?.length&&p(`Tags: ${n.tags.join(", ")}`)):p(F(n))}})});async function it(){let[e=0,t=0]=process.versions.node.split(".").map(Number);if(e<fe||e===fe&&t<Ze)throw new s(`optimize needs Node ${fe}.${Ze} or newer (you have ${process.versions.node}).`);try{let r=await import("sharp");return r.default??r}catch(r){let i=r instanceof Error?r.message.split(`
6
+ `),t(r)):n===""?(process.stdin.setRawMode(!1),process.exit(130)):n==="\x7F"||n==="\b"?r=r.slice(0,-1):r+=n};process.stdin.on("data",i)}):G(e)}var ce=w(()=>{"use strict"});var Ie={};S(Ie,{default:()=>Ht});import{defineCommand as Yt}from"citty";var Ht,Ce=w(()=>{"use strict";_();x();ce();k();Ht=Yt({meta:{name:"register",description:"Create a new PixelVault account"},args:{email:{type:"string",description:"Email address"},password:{type:"string",description:"Password (min 8 characters). Omit for a passwordless account."},passwordless:{type:"boolean",description:"Create the account without a password (for agents/automation). Set a password later via the web 'Forgot password' flow."}},async run({args:e}){let t=!!process.stdin.isTTY,r=e.email||(t?await G("Email: "):"");r||(p("Email is required. Pass --email <email>."),process.exit(1));let i;e.password?i=e.password:e.passwordless?i=void 0:t?i=await Q("Password (leave blank for a passwordless account): ")||void 0:i=void 0,i!==void 0&&i.length<8&&(p("Password must be at least 8 characters."),process.exit(1)),p("Creating account...");let n=await l({method:"POST",path:"/v1/auth/register",body:{email:r,...i?{password:i}:{},attribution:{source:"cli"}}});N({api_key:n.data.default_project.api_keys.live,default_project:n.data.default_project.id,email:n.data.email}),p("Account created. API key saved to ~/.pixelvault/config.json"),p(`Email: ${n.data.email}`),p(`Project: ${n.data.default_project.id}`),(n.data.password_set===!1||!i)&&p("No password set. To enable dashboard login, use 'Forgot password' at https://pixelvault.dev/forgot-password to set one."),n.data.email_verified||p("Verify your email to lift the upload limit for the free tier.")}})});var Ae={};S(Ae,{default:()=>Zt});import{defineCommand as Qt}from"citty";var Zt,Te=w(()=>{"use strict";_();x();ce();k();Zt=Qt({meta:{name:"login",description:"Log in to your PixelVault account"},args:{email:{type:"string",description:"Email address"},password:{type:"string",description:"Password"}},async run({args:e}){let t=e.email||await G("Email: "),r=e.password||await Q("Password: ");(!t||!r)&&(p("Email and password are required."),process.exit(1)),p("Logging in...");let i=await l({method:"POST",path:"/v1/auth/login",body:{email:t,password:r}}),n=i.data.token,o;try{o=(await l({method:"GET",path:"/v1/projects",auth:n})).data[0]?.id}catch{}if(o){let a=await l({method:"POST",path:"/v1/auth/rotate-key",auth:n,body:{project_id:o}});N({api_key:a.data.api_key,default_project:o,email:i.data.email}),p("Logged in. API key saved to ~/.pixelvault/config.json"),p(`Email: ${i.data.email}`),p(`Project: ${o}`)}else p(`Logged in as ${i.data.email}.`),p("Could not auto-configure API key. Set PIXELVAULT_API_KEY manually."),N({email:i.data.email,default_project:void 0}),I().api_key&&p("Warning: the saved api_key may belong to a previous login. Replace it with `pixelvault config set api_key <key>`.")}})});function X(e,t="--expires"){let r=/^(\d+)\s*([smhd]?)$/i.exec(e.trim());if(!r)throw new s(`Invalid ${t} "${e}". Use e.g. 30m, 12h, 7d, or a number of seconds.`);let i=(r[2]??"").toLowerCase(),n=i==="d"?86400:i==="h"?3600:i==="m"?60:1,o=Number(r[1]??"")*n;if(o<ei||o>ti)throw new s(`${t} must be between 60s and 30d (got ${o}s).`);return o}var ei,ti,me=w(()=>{"use strict";$();ei=60,ti=720*60*60});function Ue(e){let{idempotencyKey:t,dedupe:r,fileCount:i}=e;if(t!==void 0){if(!ii.test(t))return"--idempotency-key must be 1-255 printable ASCII characters with no spaces.";if(r)return"--dedupe cannot be combined with --idempotency-key. Use the key for retry-safety, or --dedupe to reuse an identical image.";if(i>1)return"--idempotency-key identifies a single upload, so it can't be used with multiple files. Upload them one at a time with distinct keys."}return null}var ii,Ne=w(()=>{"use strict";ii=/^[\x21-\x7e]{1,255}$/});function De(e){return e==null||e===""?[]:(Array.isArray(e)?e:[e]).map(String)}function z(e){let t=new Map,r=De(e.metadata);if(r.length>1)throw new s("--metadata can be given only once.");if(r.length===1){let i;try{i=JSON.parse(r[0])}catch{throw new s(`--metadata must be a JSON object, e.g. --metadata '{"env":"prod"}'.`)}if(i===null||typeof i!="object"||Array.isArray(i))throw new s(`--metadata must be a JSON object, e.g. --metadata '{"env":"prod"}'.`);for(let[n,o]of Object.entries(i))t.set(n,o)}for(let i of De(e.meta)){let n=i.indexOf("=");if(n<=0)throw new s(`--meta expects key=value, got "${i}".`);let o=i.slice(0,n),a=i.slice(n+1);if(t.has(o)&&t.get(o)!==a)throw new s(`Metadata key "${o}" was given with conflicting values.`);t.set(o,a)}return t.size===0?null:Object.fromEntries(t)}var Z=w(()=>{"use strict";$()});async function ee(e,t,r={}){let i=r.sleep??(u=>new Promise(b=>setTimeout(b,u))),n=r.now??Date.now,o=n()+ni,a=null;for(;;){if(a=(await l({path:`/v1/images/${encodeURIComponent(e)}`,auth:t})).data.description??null,!a||a.status!=="pending"||n()>=o)return a;await i(ri)}}function L(e){return e?e.status==="done"?`Alt: ${e.alt??""}${e.tags?.length?` (tags: ${e.tags.join(", ")})`:""}`:e.status==="failed"?`Description failed (${e.error??"unknown"}).`:"Description is still pending; run `pixelvault get <id> --json` in a few seconds.":"No description was produced."}var ri,ni,fe=w(()=>{"use strict";_();ri=2e3,ni=4e4});import{createReadStream as oi,statSync as ai}from"fs";import{basename as si}from"path";import{Readable as pi}from"stream";function qe(e,t,r){let i=oi(e,{...t!==void 0?{start:t}:{},...r!==void 0?{end:r}:{}});return pi.toWeb(i)}function te(e){try{let t=new URL(e).protocol;return t==="https:"||t==="http:"}catch{return!1}}async function ze(e,t,r={}){let{data:i}=await l({method:"POST",path:"/v1/videos",auth:t,body:{url:e,visibility:r.private?"private":"public",...r.signExpiresIn?{sign_expires_in:r.signExpiresIn}:{}}});return i}async function Le(e,t,r={}){let i=ai(e);if(!i.isFile()||i.size<=0)throw new s("Video must be a non-empty file.",1);let n=si(e);if(i.size<=di){let{data:a}=await l({method:"POST",path:"/v1/videos",auth:t,rawBody:qe(e),headers:{"Content-Type":"video/mp4","Content-Length":String(i.size),"X-PixelVault-Filename":encodeURIComponent(n),"X-PixelVault-Visibility":r.private?"private":"public",...r.signExpiresIn?{"X-PixelVault-Sign-Expires-In":String(r.signExpiresIn)}:{}}});return a}let o;try{let a=await l({method:"POST",path:"/v1/videos/uploads",auth:t,body:{filename:n,size:i.size,visibility:r.private?"private":"public"}});o=a.data.upload_id;let u=a.data.part_size;if(!Number.isSafeInteger(u)||u<=0)throw new s("The API returned an invalid video part size.",1);let b=1;for(let d=0;d<i.size;d+=u,b+=1){let c=Math.min(u,i.size-d);await l({method:"PUT",path:`/v1/videos/uploads/${o}/parts/${b}`,auth:t,rawBody:qe(e,d,d+c-1),headers:{"Content-Length":String(c)}})}let{data:g}=await l({method:"POST",path:`/v1/videos/uploads/${o}/complete`,auth:t,...r.signExpiresIn?{headers:{"X-PixelVault-Sign-Expires-In":String(r.signExpiresIn)}}:{}});return g}catch(a){throw o&&await l({method:"DELETE",path:`/v1/videos/uploads/${o}`,auth:t}).catch(()=>{}),a}}var di,Fe=w(()=>{"use strict";_();$();di=1e8});var Me={};S(Me,{default:()=>mi});import{defineCommand as li}from"citty";import{readFileSync as ui}from"fs";import{basename as ci,extname as Be}from"path";var mi,Ke=w(()=>{"use strict";_();x();me();Ne();Z();fe();k();Fe();mi=li({meta:{name:"upload",description:"Upload one or more images, MP4 files, or public MP4 URLs"},args:{files:{type:"positional",description:"Image/MP4 file path(s), or public MP4 URL(s), to upload",required:!0},folder:{type:"string",description:"Folder to organize images in"},private:{type:"boolean",description:"Upload privately \u2014 returns a signed URL only holders of the link can open (needs a secret key)",default:!1},expires:{type:"string",description:"Lifetime of a --private signed link: e.g. 30m, 12h, 7d (default 7d; min 60s, max 30d)"},optimized:{type:"boolean",description:"Print the optimized URL (WebP/AVIF, usually much smaller) instead of the original; falls back to the original URL when none is returned (SVG, GIF, private)",default:!1},"idempotency-key":{type:"string",description:"Make retries safe: repeating the same key with the same file returns the original image instead of storing a copy (single file only)"},dedupe:{type:"boolean",description:"Reuse an identical image already in your project instead of storing and counting another upload",default:!1},meta:{type:"string",description:"Attach custom metadata as key=value (repeatable), e.g. --meta env=prod --meta run=42"},metadata:{type:"string",description:`Attach custom metadata as a JSON object, e.g. --metadata '{"env":"prod","n":3}'`},describe:{type:"boolean",description:"Generate an AI description (alt text, caption, OCR) in the background; needs a secret key. Free 20 in total, Starter 1,000/mo, Pro 10,000/mo. Read it with `get <id> --json`, or add --wait",default:!1},wait:{type:"boolean",description:"With --describe: wait up to ~40s for the description and include it in --json output (alt text goes to stderr otherwise)",default:!1},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let t=y(),r=null;e.expires!==void 0&&!e.private&&(p("--expires only applies to --private uploads (it sets the signed link's lifetime)."),process.exit(1)),e.private&&(r=X(String(e.expires??"7d"))),e.private&&e.folder&&p("Note: --folder is ignored for --private uploads.");let i=e._.length?e._.map(String):[String(e.files)],n=i.some(c=>te(c)||Be(c).toLowerCase()===".mp4"),o=e.folder||e.optimized||e["idempotency-key"]||e.dedupe||e.meta||e.metadata||e.describe||e.wait;n&&o&&(p("MP4 uploads do not support folder, optimization, reuse, metadata, or AI-description options. Use --private and --expires for signed video delivery."),process.exit(1));let a=!1,u=e["idempotency-key"],b=Ue({idempotencyKey:u,dedupe:e.dedupe,fileCount:i.length});b&&(p(b),process.exit(1));let g=z({meta:e.meta,metadata:e.metadata});e.wait&&!e.describe&&(p("--wait only applies together with --describe."),process.exit(1));let d=[];for(let c of i)try{if(te(c)||Be(c).toLowerCase()===".mp4"){let R={private:!!e.private,signExpiresIn:r},A=te(c)?await ze(c,t,R):await Le(c,t,R);if(e.private&&A.visibility!=="private")throw new Error("The API did not confirm private visibility; refusing to print the URL.");e.json?d.push(A):f(A.url);continue}let E=ui(c),K=ci(c),O=new FormData;O.append("file",new File([E],K)),e.folder&&O.append("folder",e.folder),e.private&&(O.append("visibility","private"),r!==null&&O.append("sign_expires_in",String(r))),e.dedupe&&O.append("dedupe","true"),g&&O.append("metadata",JSON.stringify(g)),e.describe&&O.append("describe","true");let v=await l({method:"POST",path:"/v1/images",auth:t,formData:O,...u?{headers:{"Idempotency-Key":u}}:{}});if(v.data.replayed?p(`Replayed: ${c} was already uploaded with this key; returning the original.`):v.data.deduplicated&&p(`Deduplicated: ${c} matches an existing image; nothing new was stored.`),e.private&&v.data.visibility!=="private"){a=!0,p(`Refusing to report ${c}: server did not confirm a private upload.`);continue}e.describe&&(v.data.describe_skipped?p(`Description skipped for ${c}: ${v.data.describe_skipped}.`):e.wait&&v.data.description?.status==="pending"?(v.data.description=await ee(v.data.id,t),e.json||p(L(v.data.description??null))):v.data.description?.status==="pending"?p(`Describing ${c} in the background; read it with: pixelvault get ${v.data.id} --json`):v.data.description?.status==="done"&&!e.json&&p(L(v.data.description))),e.json?d.push(v.data):f(e.optimized?v.data.optimized_url??v.data.url:v.data.url)}catch(E){a=!0,p(`Failed to upload ${c}: ${E instanceof Error?E.message:String(E)}`)}e.json&&d.length>0&&h(d.length===1?d[0]:d),a&&process.exit(1)}})});var Ve={};S(Ve,{default:()=>gi});import{defineCommand as fi}from"citty";var gi,Je=w(()=>{"use strict";_();x();$();me();k();gi=fi({meta:{name:"generate",description:"Generate an image from a prompt and host it (spends monthly AI credits; secret key required)"},args:{prompt:{type:"positional",description:"What to draw. Quote it, or pass several words",required:!0},model:{type:"string",alias:"m",description:"flux-1-schnell (default, 1 credit), sdxl-lightning or sdxl-base (1, paid plans), lucid-origin or phoenix-1.0 (20, paid plans)"},folder:{type:"string",description:"Folder to organize the image in"},private:{type:"boolean",description:"Keep it private: returns a signed URL only holders of the link can open",default:!1},expires:{type:"string",description:"Lifetime of a --private signed link: e.g. 30m, 12h, 7d (default 7d; min 60s, max 30d)"},"delete-after":{type:"string",description:"Delete the image automatically after this long: e.g. 1h, 7d (min 60s, max 30d)"},optimized:{type:"boolean",description:"Print the optimized URL (WebP/AVIF, usually much smaller) instead of the original; falls back to the original URL when none is returned",default:!1},json:{type:"boolean",description:"Output the full JSON response",default:!1}},async run({args:e}){let t=y(),r=(e._.length?e._.map(String):[String(e.prompt??"")]).join(" ").trim();if(!r)throw new s('Give a prompt, e.g. pixelvault generate "a red bicycle".');if(e.expires!==void 0&&!e.private)throw new s("--expires only applies to --private images (it sets the signed link's lifetime). Use --delete-after to expire the image itself.");let i=e.expires!==void 0?X(String(e.expires)):void 0,n=e["delete-after"]!==void 0?X(String(e["delete-after"]),"--delete-after"):void 0;e.private&&e.folder&&p("Note: --folder is ignored for --private images.");let o=await l({method:"POST",path:"/v1/images/generate",auth:t,body:{prompt:r,...e.model?{model:String(e.model)}:{},...e.folder?{folder:String(e.folder)}:{},...e.private?{visibility:"private"}:{},...i!==void 0?{sign_expires_in:i}:{},...n!==void 0?{expires_in:n}:{}}});if(e.private&&o.data.visibility!=="private")throw new s("Refusing to report the image: the server did not confirm it is private.");let a=o.data.generation;p(`Generated with ${a.model}: ${a.credits_charged} credit${a.credits_charged===1?"":"s"} used, ${a.credits_remaining} left this month.`),e.json?h(o.data):f(e.optimized?o.data.optimized_url??o.data.url:o.data.url)}})});var Ge={};S(Ge,{default:()=>yi});import{defineCommand as hi}from"citty";var yi,Xe=w(()=>{"use strict";_();x();k();Z();yi=hi({meta:{name:"list",description:"List uploaded images"},args:{page:{type:"string",description:"Page number",default:"1"},"per-page":{type:"string",description:"Items per page (max 100)",default:"20"},search:{type:"string",description:'Search the AI descriptions (alt text, caption, OCR text) of images uploaded with --describe, best match first, e.g. --search "invoice 4821" (quotes, -word work)',alias:"q"},tag:{type:"string",description:"Only images carrying this AI tag (one tag, e.g. --tag boat); see `pixelvault tags`"},meta:{type:"string",description:"Only images whose metadata has key=value (repeatable; string match), e.g. --meta env=prod"},metadata:{type:"string",description:`Only images whose metadata contains this JSON object (typed match), e.g. --metadata '{"n":3}'`},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let t=y(),r=new URLSearchParams({page:e.page,per_page:e["per-page"]}),i=z({meta:e.meta,metadata:e.metadata});i&&r.set("metadata",JSON.stringify(i));let n=typeof e.search=="string"?e.search.trim():"";n&&r.set("q",n);let o=typeof e.tag=="string"?e.tag.trim():"";o&&r.set("tag",o);let a=await l({path:`/v1/images?${r}`,auth:t});if(e.json)h(a);else for(let u of a.data)f(u.url)}})});var We={};S(We,{default:()=>bi});import{defineCommand as wi}from"citty";var bi,Ye=w(()=>{"use strict";_();x();k();bi=wi({meta:{name:"tags",description:"List the AI tags on your described images with how many images carry each (filter with `list --tag`)"},args:{json:{type:"boolean",description:"Output the full JSON response",default:!1}},async run({args:e}){let t=y(),r=await l({path:"/v1/images/tags",auth:t});if(e.json)h(r);else for(let i of r.data)f(`${i.tag} ${i.count}`)}})});var He={};S(He,{default:()=>xi});import{defineCommand as vi}from"citty";var xi,Qe=w(()=>{"use strict";_();x();k();$();fe();xi=vi({meta:{name:"describe",description:"Write an AI description (alt text, caption, OCR text, tags) for an image you already uploaded"},args:{id:{type:"positional",description:"Image ID (e.g. img_abc123)",required:!0},wait:{type:"boolean",description:"Wait up to ~40s for the description and print it (otherwise it runs in the background)",default:!1},json:{type:"boolean",description:"Output JSON ({ id, description }); with --wait it includes the full description",default:!1}},async run({args:e}){let t=y(),r=String(e.id),i=await l({method:"POST",path:`/v1/images/${encodeURIComponent(r)}/describe`,auth:t});if(i.data.describe_skipped)throw new s(`Description not started: ${i.data.describe_skipped}.`);let n=i.data.description??null;if(e.wait&&n?.status==="pending"&&(n=await ee(r,t)),n?.status==="failed")throw new s(L(n));if(e.json){h({id:r,description:n});return}n?.status==="done"?(f(n.alt??""),n.tags?.length&&p(`Tags: ${n.tags.join(", ")}`)):p(L(n))}})});async function nt(){let[e=0,t=0]=process.versions.node.split(".").map(Number);if(e<ge||e===ge&&t<tt)throw new s(`optimize needs Node ${ge}.${tt} or newer (you have ${process.versions.node}).`);try{let r=await import("sharp");return r.default??r}catch(r){let i=r instanceof Error?r.message.split(`
7
7
  `)[0]:String(r);throw new s(`optimize needs the optional 'sharp' package, which couldn't be loaded for this platform.
8
8
  Reason: ${i}
9
- Fix: reinstall pixelvault-cli without --omit=optional (npm install -g pixelvault-cli).`)}}function vi(e,t){switch(e==="keep"?t:e==="jpg"?"jpeg":e){case"jpeg":return{sharpFormat:"jpeg",ext:"jpg"};case"png":return{sharpFormat:"png",ext:"png"};case"webp":return{sharpFormat:"webp",ext:"webp"};case"avif":return{sharpFormat:"avif",ext:"avif"};default:return null}}async function rt(e,t,r,i){let n={inputBytes:t.length,outputBytes:t.length,ext:r},o=R=>({status:"skipped",reason:R,...n}),a;try{a=await e(t,He).metadata()}catch(R){let M=R instanceof Error?R.message:String(R);throw new Error(/pixel limit/i.test(M)?`image is over ${et/1e6} megapixels`:"not a readable image")}if(a.format==="svg")return o("SVG is not supported");if(a.format==="gif")return o("GIF is not supported");if((a.pages??1)>1)return o("animated images are not supported");if(a.format==="heif"&&a.compression==="hevc")return o("HEIC is not supported (convert it at https://pixelvault.dev/tools/heic-to-url/)");let u=a.format==="heif"?"avif":a.format,b=vi(i.format,u);if(!b)return o(`unsupported format: ${a.format}`);let c=e(t,He).rotate();switch((i.maxWidth||i.maxHeight)&&(c=c.resize({width:i.maxWidth,height:i.maxHeight,fit:"inside",withoutEnlargement:!0})),b.sharpFormat==="jpeg"&&a.hasAlpha&&(c=c.flatten({background:"#ffffff"})),i.keepMetadata&&(c=c.keepMetadata()),b.sharpFormat){case"jpeg":c=c.jpeg({quality:i.quality,mozjpeg:!0});break;case"png":c=c.png({compressionLevel:9,effort:10});break;case"webp":c=c.webp({quality:i.quality,effort:5});break;case"avif":c=c.avif({quality:i.quality,effort:4});break}let{data:l,info:y}=await c.toBuffer({resolveWithObject:!0});if(l.length>=t.length)return o("already optimal (result was not smaller)");let E=(a.orientation??1)>=5,K=E?a.height:a.width,O=E?a.width:a.height;return b.sharpFormat===u&&y.width===K&&y.height===O&&(t.length-l.length)/t.length<Qe/100?o(`already optimal (would save under ${Qe}%)`):{status:"optimized",inputBytes:t.length,outputBytes:l.length,ext:b.ext,width:y.width,height:y.height,buffer:l}}function he(e,t){return e<=0?0:Math.round((e-t)/e*100)}var et,ge,He,Qe,tt,fe,Ze,nt=w(()=>{"use strict";S();et=100*1e3*1e3,ge=100*1024*1024,He={limitInputPixels:et,failOn:"error"},Qe=5,tt=["jpg","jpeg","png","webp","avif"],fe=20,Ze=9});var ut={};$(ut,{assertSafePlans:()=>lt,collectItems:()=>st,default:()=>Ni,outputExt:()=>pt,parsePositiveInt:()=>te,planOutput:()=>dt,writeAtomic:()=>be});import{defineCommand as xi}from"citty";import{createHash as _i}from"crypto";import{availableParallelism as ki}from"os";import{readFileSync as $i,writeFileSync as Si,renameSync as Ri,unlinkSync as ji,mkdirSync as Oi,statSync as ye,lstatSync as we,readdirSync as Pi,existsSync as Ei}from"fs";import{basename as L,dirname as ie,extname as B,join as U,relative as Ii,resolve as z}from"path";function te(e,t){if(t==null||t==="")return;let r=Number(t);if(!Number.isInteger(r)||r<1)throw new s(`${e} must be a positive whole number.`);return r}function Ai(e){return tt.includes(B(e).slice(1).toLowerCase())}function at(e,t,r){for(let i of Pi(e)){if(i.startsWith(".")||i==="node_modules")continue;let n=U(e,i),o=we(n);if(!o.isSymbolicLink())if(o.isDirectory()){if(r&&z(n)===r)continue;at(n,t,r)}else Ai(n)&&!Ci.test(L(n,B(n)))&&t.push(n)}}function st(e,t,r){let i=r?z(r):void 0,n=[];for(let o of e){let a;try{a=ye(o)}catch{throw new s(`No such file or directory: ${o}`)}if(a.isDirectory()){if(!t)throw new s(`${o} is a directory. Pass --recursive to optimize everything inside it.`);let u=[];at(o,u,i);for(let b of u.sort())n.push({file:b,root:o})}else n.push({file:o,root:null})}return n}function pt(e,t){return t==="keep"?B(e.file).slice(1):t==="jpg"?"jpg":t}function dt(e,t,r){let i=L(e.file,B(e.file)),n=pt(e,t);if(r.inPlace)return{item:e,optimized:e.file,kept:null};if(r.outDir){let o=e.root?ie(Ii(e.root,e.file)):".",a=U(r.outDir,o);return{item:e,optimized:U(a,`${i}.${n}`),kept:U(a,L(e.file))}}return r.writeBeside?{item:e,optimized:U(ie(e.file),`${i}.optimized.${n}`),kept:null}:{item:e,optimized:null,kept:null}}function lt(e,t){let r=new Set(e.map(n=>z(n.item.file))),i=new Map;for(let n of e){let o=z(n.item.file);if(t.inPlace&&we(n.item.file).isSymbolicLink())throw new s(`${n.item.file} is a symlink; refusing to overwrite it in place. Optimize the real file instead.`);let a=[[n.optimized,!1],[n.kept,!0]];for(let[u,b]of a){if(!u)continue;let c=z(u),l=c===o;if(l&&b&&!t.inPlace&&n.optimized!==u)continue;if(l&&!t.inPlace)throw new s(`${n.item.file}: the output would overwrite the original. Use --in-place if that is what you want.`);if(!l&&r.has(c))throw new s(`${u} is also an input file; refusing to overwrite it. Use different output settings.`);let y=i.get(c);if(y&&y!==n.item.file)throw new s(`${n.item.file} and ${y} would both be written to ${u}. Optimize them separately or use --out-dir with --recursive.`);if(i.set(c,n.item.file),!l&&Ei(c)&&we(c).isSymbolicLink())throw new s(`${u} is a symlink; refusing to write through it.`)}}}function be(e,t,r){Oi(ie(e),{recursive:!0});let i=U(ie(e),`.${L(e)}.${process.pid}.tmp`);try{Si(i,t,r!==void 0?{mode:r}:void 0),Ri(i,e)}catch(n){try{ji(i)}catch{}throw n}}async function Ti(e,t,r){let i=new Array(e.length),n=0,o=Array.from({length:Math.min(t,e.length)},async()=>{for(;n<e.length;){let a=n++;i[a]=await r(e[a])}});return await Promise.all(o),i}var ot,Ci,Ni,ct=w(()=>{"use strict";_();x();S();k();nt();ot=["keep","webp","avif","jpg","png"],Ci=/\.optimized$/i;Ni=xi({meta:{name:"optimize",description:"Compress images locally (no account needed): resize, convert, strip metadata. Never writes a file bigger than the original"},args:{files:{type:"positional",description:"Image files (or directories with --recursive)",required:!0},format:{type:"string",description:"Output format: keep (default), webp, avif, jpg, png",default:"keep"},"max-width":{type:"string",description:"Downscale to at most this width in px (never upscales)"},"max-height":{type:"string",description:"Downscale to at most this height in px (never upscales)"},quality:{type:"string",description:"JPEG/WebP/AVIF quality 1-100 (default 80). PNG output is always lossless",default:"80"},"keep-metadata":{type:"boolean",description:"Keep EXIF/GPS and other metadata (stripped by default)",default:!1},"out-dir":{type:"string",description:"Write results into this directory (mirrors subfolders with --recursive)"},"in-place":{type:"boolean",description:"Overwrite each original (only with --format keep). Default writes name.optimized.ext beside it",default:!1},recursive:{type:"boolean",description:"Descend into directories",alias:"r",default:!1},"dry-run":{type:"boolean",description:"Report what would be saved without writing anything",default:!1},host:{type:"boolean",description:"Upload the optimized image and print its CDN URL (needs an API key). Local files are written only with --out-dir/--in-place",default:!1},json:{type:"boolean",description:"Output a JSON report (always an array)",default:!1}},async run({args:e}){let t=String(e.format);if(!ot.includes(t))throw new s(`--format must be one of: ${ot.join(", ")}.`);let r=te("--quality",e.quality)??80;if(r>100)throw new s("--quality must be a whole number between 1 and 100.");let i=te("--max-width",e["max-width"]),n=te("--max-height",e["max-height"]),o=e["out-dir"]?String(e["out-dir"]):void 0;if(e["in-place"]&&o)throw new s("Use either --in-place or --out-dir, not both.");if(e["in-place"]&&t!=="keep")throw new s("--in-place only works with --format keep (changing format would need a different file name).");let a=!!e["dry-run"],u=!!e.host;if(u&&a)throw new s("--host can't be combined with --dry-run (it would upload).");let b=u?h():void 0,c=e._.length?e._.map(String):[String(e.files)],l=st(c,!!e.recursive,o);if(l.length===0)throw new s("No supported images found (jpg, png, webp, avif).");let y={outDir:o,inPlace:!!e["in-place"],writeBeside:!u},E=l.map(m=>dt(m,t,y));lt(E,y);let K=await it(),O={format:t,maxWidth:i,maxHeight:n,quality:r,keepMetadata:!!e["keep-metadata"]},v=u?2:Math.max(1,Math.min(4,ki())),R=await Ti(E,v,async m=>{let{item:j}=m,qt=B(j.file).slice(1).toLowerCase(),P={input:j.file,output:null,status:"error",input_bytes:0,output_bytes:0,saved_percent:0},X;try{if(ye(j.file).size>ge)throw new Error(`file is over ${ge/1024/1024} MB`);X=$i(j.file);let C=await rt(K,X,qt,O),J=C.status==="optimized"&&C.buffer?C.buffer:null;if(P.status=C.status,P.reason=C.reason,P.input_bytes=C.inputBytes,P.output_bytes=C.outputBytes,P.saved_percent=he(C.inputBytes,C.outputBytes),!a)if(J&&m.optimized){let A=y.inPlace?ye(j.file).mode&511:void 0;be(m.optimized,J,A),P.output=m.optimized}else!J&&m.kept&&z(m.kept)!==z(j.file)&&(be(m.kept,X),P.output=m.kept);if(u&&b)try{let A=J??X,zt=J?`${L(j.file,B(j.file))}.${C.ext}`:L(j.file),ke=new FormData;ke.append("file",new File([new Uint8Array(A)],zt));let $e=await d({method:"POST",path:"/v1/images",auth:b,formData:ke,headers:{"Idempotency-Key":_i("sha256").update(A).digest("hex")}});P.url=$e.data.optimized_url??$e.data.url}catch(A){P.upload_error=A instanceof Error?A.message:String(A)}}catch(Y){P.status="error",P.reason=Y instanceof Error?Y.message:String(Y)}return P}),M=0,se=0,V=0;for(let m of R){if(m.status==="error"){V++,p(`Failed ${m.input}: ${m.reason}`);continue}if(m.upload_error&&(V++,p(`Upload failed for ${m.input}: ${m.upload_error}`)),M+=m.input_bytes,se+=m.output_bytes,m.status==="skipped")p(`Skipped ${m.input}: ${m.reason}`);else{let j=m.output?` \u2192 ${m.output}`:m.url?` \u2192 ${m.url}`:"";p(`${a?"Would save":"Optimized"} ${m.input}${j} ${T(m.input_bytes)} \u2192 ${T(m.output_bytes)} (-${m.saved_percent}%)`)}}if(R.length>1&&(p(`Total: ${T(M)} \u2192 ${T(se)} (-${he(M,se)}%)`),V&&p(`${V} of ${R.length} failed.`)),e.json)g(R);else for(let m of R){if(m.status==="error")continue;let j=m.url??m.output??(m.status==="skipped"?m.input:null);j&&f(j)}V&&(process.exitCode=1)}})});var ft={};$(ft,{applyTransform:()=>mt,default:()=>Di});import{defineCommand as qi}from"citty";import{writeFileSync as zi}from"fs";function mt(e,t){if(!t)return e;let r=t.trim().replace(/^[?&]+/,"");if(!r)return e;let i=new URLSearchParams(r).toString();return i?e.includes("?")?`${e}&${i}`:`${e}?${i}`:e}var Di,gt=w(()=>{"use strict";_();x();k();S();Di=qi({meta:{name:"get",description:"Get an image's CDN URL or metadata, or download it \u2014 optionally transformed"},args:{id:{type:"positional",description:"Image ID (e.g. img_abc123)",required:!0},output:{type:"string",description:"Download the image to this file path",alias:"o"},transform:{type:"string",description:'Transform params to apply, e.g. "w=400&fmt=webp", "segment=foreground", "tile=img_logo.png"',alias:"t"},json:{type:"boolean",description:"Output full JSON metadata",default:!1}},async run({args:e}){let t=h(),r=await d({path:`/v1/images/${encodeURIComponent(e.id)}`,auth:t}),i=mt(r.data.url,e.transform);if(e.output){let n=e.output,o;try{let a=await fetch(i);if(!a.ok)throw new s(`Failed to download image: HTTP ${a.status}`,1);o=await a.arrayBuffer()}catch(a){throw a instanceof s?a:new s(`Failed to download image: ${a instanceof Error?a.message:String(a)}`,1)}if(o.byteLength===0)throw new s("Downloaded 0 bytes \u2014 refusing to write an empty file",1);try{zi(n,Buffer.from(o))}catch(a){throw new s(`Failed to save ${n}: ${a instanceof Error?a.message:String(a)}`,1)}p(`Saved ${n}`),e.json?g(e.transform?{...r,transformed_url:i,output:n}:{...r,output:n}):f(i);return}e.json?g(e.transform?{...r,transformed_url:i}:r):f(i)}})});var ht={};$(ht,{default:()=>Ui});import{defineCommand as Fi}from"citty";var Ui,yt=w(()=>{"use strict";_();x();Z();S();k();Ui=Fi({meta:{name:"meta",description:"Replace an image's custom metadata (the new object replaces the old one; use --clear to remove it)"},args:{id:{type:"positional",description:"Image ID (e.g. img_abc123)",required:!0},meta:{type:"string",description:"Metadata as key=value (repeatable), e.g. --meta env=prod --meta run=42"},metadata:{type:"string",description:`Metadata as a JSON object, e.g. --metadata '{"env":"prod","n":3}'`},clear:{type:"boolean",description:"Remove all metadata from the image",default:!1}},async run({args:e}){let t=h(),r=D({meta:e.meta,metadata:e.metadata});if(e.clear&&r)throw new s("Use either --clear or --meta/--metadata, not both.");if(!e.clear&&!r)throw new s("Nothing to set: pass --meta key=value, --metadata '<json>', or --clear.");let i=await d({method:"PATCH",path:`/v1/images/${encodeURIComponent(String(e.id))}`,auth:t,body:{metadata:e.clear?null:r}});g(i.data.metadata)}})});var wt={};$(wt,{default:()=>Bi});import{defineCommand as Li}from"citty";var Bi,bt=w(()=>{"use strict";_();x();Bi=Li({meta:{name:"delete",description:"Delete an image by ID"},args:{id:{type:"positional",description:"Image ID (e.g. img_abc123)",required:!0}},async run({args:e}){let t=h();await d({method:"DELETE",path:`/v1/images/${e.id}`,auth:t})}})});var xt={};$(xt,{default:()=>Wi,defaultOutputPath:()=>vt});import{defineCommand as Ki}from"citty";import{writeFileSync as Mi,mkdirSync as Vi}from"fs";import{execFileSync as Ji}from"child_process";function vt(e,t){return t&&t.length>0?t:`pixelvault-export-${e}.tar`}var Gi,Wi,_t=w(()=>{"use strict";_();x();k();S();Gi=e=>new Promise(t=>setTimeout(t,e)),Wi=Ki({meta:{name:"export",description:"Export every image in your project as a tar archive (manifest.json + all images). Your data is always portable \u2014 no lock-in."},args:{output:{type:"string",description:"Write the archive to this path (default: pixelvault-export-<job>.tar)",alias:"o"},extract:{type:"boolean",description:"Extract the archive into a directory after download (requires `tar`)",default:!1},timeout:{type:"string",description:"Max seconds to wait for the export to finish (default: 600)",default:"600"},json:{type:"boolean",description:"Output the final job JSON to stdout",default:!1}},async run({args:e}){let t=h(),r=(Number(e.timeout)||600)*1e3,i;try{i=await d({method:"POST",path:"/v1/export",auth:t})}catch(l){throw l instanceof s&&/in progress/i.test(l.message)?new s("An export for this project is already in progress. Wait for it to finish, then try again.",1):l}p(`Export started \u2014 ${i.image_count} image(s), job ${i.job_id}`);let n=Date.now()+r,o,a=1e3;for(;o=await d({path:`/v1/export-jobs/${encodeURIComponent(i.job_id)}`,auth:t}),o.status!=="complete";){if(o.status==="failed")throw new s(`Export failed: ${o.error??"unknown error"}`,1);if(o.status==="expired")throw new s("Export expired before it could be downloaded. Start a new one.",1);if(Date.now()>n)throw new s(`Timed out after ${e.timeout}s (job still ${o.status}). It may still finish \u2014 re-run to poll, or raise --timeout.`,1);p(` ${o.status}\u2026 ${o.processed_count}/${o.image_count}`),await Gi(a),a=Math.min(Math.round(a*1.5),5e3)}let u=vt(i.job_id,e.output),b=`${H()}/v1/export-jobs/${encodeURIComponent(i.job_id)}/download`,c;try{let l=await fetch(b,{headers:{Authorization:`Bearer ${t}`}});if(!l.ok)throw new s(`Failed to download export: HTTP ${l.status}`,1);c=await l.arrayBuffer()}catch(l){throw l instanceof s?l:new s(`Failed to download export: ${l instanceof Error?l.message:String(l)}`,1)}if(c.byteLength===0)throw new s("Downloaded 0 bytes \u2014 refusing to write an empty archive.",1);try{Mi(u,Buffer.from(c))}catch(l){throw new s(`Failed to save ${u}: ${l instanceof Error?l.message:String(l)}`,1)}if(p(`Saved ${u} \u2014 ${o.image_count} image(s), ${c.byteLength} bytes`),e.extract){let l=u.replace(/\.tar$/,"")||`${u}-extracted`;try{Vi(l,{recursive:!0}),Ji("tar",["-xf",u,"-C",l],{stdio:"ignore"}),p(`Extracted into ${l}/`)}catch(y){throw new s(`Saved ${u}, but extraction failed (is \`tar\` installed?): ${y instanceof Error?y.message:String(y)}`,1)}}e.json?g({...o,output:u}):f(u)}})});var St={};$(St,{default:()=>Yi,renderUsage:()=>$t});import{defineCommand as Xi}from"citty";function $t(e){return[`Plan: ${e.plan}`,`Period: ${e.period} (resets ${e.resets_at})`,`Storage: ${T(e.usage.storage_bytes)} used / ${ve(e.limits.storage_bytes)} limit / ${ve(e.remaining.storage_bytes)} remaining`,`Uploads: ${e.usage.uploads} used / ${kt(e.limits.uploads_per_month)} limit / ${kt(e.remaining.uploads)} remaining`,`Bandwidth: ${ve(e.limits.bandwidth_bytes_per_month)} limit per month (usage not reported)`,...e.generation?[`AI images: ${e.generation.credits_used} credits used / ${e.generation.credits_limit} limit / ${e.generation.credits_remaining} remaining`]:[]]}var ve,kt,Yi,Rt=w(()=>{"use strict";_();x();k();ve=e=>e==null?"unlimited":T(e),kt=e=>e==null?"unlimited":String(e);Yi=Xi({meta:{name:"usage",description:"Show plan, quota usage and limits (secret key required)"},args:{json:{type:"boolean",description:"Output the usage object as JSON",default:!1}},async run({args:e}){let t=h(),r=await d({path:"/v1/usage",auth:t});if(e.json)g(r.data);else for(let i of $t(r.data))f(i)}})});var jt={};$(jt,{default:()=>er,explainKeyError:()=>ne});import{defineCommand as re}from"citty";function ne(e){if(e instanceof q){switch(e.code){case"key_limit_reached":throw new s("Key limit reached (25 per project). Revoke an unused key with `pixelvault keys revoke <id>` first.",1);case"rate_limit_exceeded":{let t=e.retryAfter?` Retry after ${e.retryAfter} seconds.`:"";throw new s(`Rate limit exceeded (20 key changes per hour per project).${t}`,1)}case"cannot_revoke_current_key":throw new s("Cannot revoke the key you are authenticating with. Create a new key, switch to it (PIXELVAULT_API_KEY), then revoke the old one with the new key.",1);case"not_found":throw new s("Key not found. Only secret keys can be revoked; check the id with `pixelvault keys list`.",1);case"validation_error":throw new s(`Invalid request: ${e.message}`,1);case"forbidden":throw new s("This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.",1)}if(e.status===403)throw new s("This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.",1)}throw e}var Hi,Qi,Zi,er,Ot=w(()=>{"use strict";_();x();S();k();Hi=re({meta:{name:"list",description:"List the project's API keys"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let t=h(),r;try{r=await d({path:"/v1/keys",auth:t})}catch(i){ne(i)}if(e.json){g(r.data);return}for(let i of r.data)f([i.id,i.type,i.environment,`${i.key_prefix}...`,i.label??"-",`created ${i.created_at}`,`last used ${i.last_used_at??"never"}`,i.current?"(current)":""].filter(Boolean).join(" "))}}),Qi=re({meta:{name:"create",description:"Create a new secret API key (the raw key is printed once)"},args:{label:{type:"string",description:"Label for the key (max 60 chars)"},env:{type:"string",description:"Environment: live or test (default: live)"},json:{type:"boolean",description:"Output the full data object as JSON",default:!1}},async run({args:e}){let t=h();if(e.env&&e.env!=="live"&&e.env!=="test")throw new s("--env must be 'live' or 'test'.",1);if(e.label&&e.label.length>60)throw new s("--label must be 60 characters or fewer.",1);let r={};e.label&&(r.label=e.label),e.env&&(r.environment=e.env);let i;try{i=await d({method:"POST",path:"/v1/keys",auth:t,body:r})}catch(n){ne(n)}p("This key is shown ONCE. Store it now (secret manager / env var); never commit it or paste it into logs."),e.json?g(i.data):f(i.data.api_key)}}),Zi=re({meta:{name:"revoke",description:"Revoke a secret API key by id (not the one in use)"},args:{id:{type:"positional",description:"Key id (see `pixelvault keys list`)",required:!0},json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let t=h(),r;try{r=await d({method:"DELETE",path:`/v1/keys/${encodeURIComponent(e.id)}`,auth:t})}catch(i){ne(i)}e.json?g(r.data):p(`Revoked key ${e.id}.`)}}),er=re({meta:{name:"keys",description:"Manage API keys (list, create, revoke)"},subCommands:{list:Hi,create:Qi,revoke:Zi}})});var Pt={};$(Pt,{default:()=>ir,explainBillingError:()=>oe});import{defineCommand as tr}from"citty";function oe(e){if(e instanceof q)switch(e.code){case"test_key_forbidden":throw new s("Billing needs a live key (pv_live_...); test keys can't start checkout or open the portal.",1);case"publishable_key_forbidden":throw new s("Billing needs a secret live key (pv_live_...); publishable keys are not allowed.",1);case"email_not_verified":throw new s("Verify your email before upgrading (check your inbox for the code).",1);case"already_subscribed":throw new s("You already have a subscription. Change or cancel it with `pixelvault billing`.",1);case"no_subscription":throw new s("This account has no billing history yet. Start one with `pixelvault upgrade <starter|pro>`.",1);case"billing_unavailable":throw new s("Paid plans are not available right now. Try again later.",1);case"rate_limit_exceeded":{let t=e.retryAfter?` Retry after ${e.retryAfter} seconds.`:"";throw new s(`Rate limit exceeded (10 billing requests per hour per account).${t}`,1)}}throw e}var ir,xe=w(()=>{"use strict";_();x();S();k();ir=tr({meta:{name:"billing",description:"Print a Stripe billing-portal URL to change plan, update payment details or cancel (live key required)"},args:{json:{type:"boolean",description:"Output { url } as JSON",default:!1}},async run({args:e}){let t=h(),r;try{r=await d({method:"POST",path:"/v1/billing/portal",body:{},auth:t})}catch(i){oe(i)}e.json?g({url:r.url}):(f(r.url),p("Open this link yourself to change plan, update payment details or cancel. It can cancel the subscription, so don't paste it anywhere shared."))}})});var Et={};$(Et,{default:()=>nr});import{defineCommand as rr}from"citty";var _e,nr,It=w(()=>{"use strict";_();x();S();k();xe();_e=["starter","pro"],nr=rr({meta:{name:"upgrade",description:"Print a Stripe Checkout URL to subscribe to a paid plan (live key required; nothing is charged until you pay)"},args:{plan:{type:"positional",description:`Plan to subscribe to: ${_e.join(" | ")}`,required:!0},json:{type:"boolean",description:"Output { url } as JSON",default:!1}},async run({args:e}){let t=String(e.plan);if(!_e.includes(t))throw new s(`Unknown plan "${t}". Choose one of: ${_e.join(", ")}.`,1);let r=h(),i;try{i=await d({method:"POST",path:"/v1/billing/checkout",body:{plan:t},auth:r})}catch(n){oe(n)}e.json?g({url:i.url}):(f(i.url),p("Open this link to complete payment. The plan changes once Stripe confirms \u2014 check with `pixelvault usage`."))}})});var Ct={};$(Ct,{default:()=>ar});import{defineCommand as or}from"citty";var ar,At=w(()=>{"use strict";x();k();ar=or({meta:{name:"whoami",description:"Show current authentication state"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let t=I(),r=de();!r&&!t.email&&(p("Not authenticated. Run `pixelvault register` or `pixelvault login`."),process.exit(1));let i={email:t.email||"(unknown)",project:t.default_project||"(unknown)",api_key:r?`${r.slice(0,12)}...${r.slice(-4)}`:"(not set)",source:process.env.PIXELVAULT_API_KEY?"env":"config",identity_source:"cached"};if(e.json)g(i);else{let n=o=>o?" (cached, not verified for this key)":"";f(`Email: ${i.email}${n(t.email)}`),f(`Project: ${i.project}${n(t.default_project)}`),f(`API Key: ${i.api_key} (${i.source})`)}}})});var Tt={};$(Tt,{default:()=>lr});import{defineCommand as ae}from"citty";var sr,pr,dr,lr,Nt=w(()=>{"use strict";x();k();sr=ae({meta:{name:"get",description:"Get a config value"},args:{key:{type:"positional",description:"Config key (api_key, default_project, email, api_url)",required:!0}},async run({args:e}){let r=I()[e.key];r===void 0&&(p(`Key "${e.key}" is not set.`),process.exit(1)),f(String(r))}}),pr=ae({meta:{name:"set",description:"Set a config value"},args:{key:{type:"positional",description:"Config key",required:!0},value:{type:"positional",description:"Config value",required:!0}},async run({args:e}){let t=["api_key","default_project","email","api_url"];t.includes(e.key)||(p(`Invalid key. Valid keys: ${t.join(", ")}`),process.exit(1)),e.key==="api_key"&&e.value!==I().api_key?N({api_key:e.value,email:void 0,default_project:void 0}):N({[e.key]:e.value}),p(`Set ${e.key} = ${e.key==="api_key"?"***":e.value}`)}}),dr=ae({meta:{name:"show",description:"Show all config values"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let t=I();if(e.json)g(t);else{p(`Config: ${Pe()}`);for(let[r,i]of Object.entries(t)){let n=r==="api_key"&&i?`${i.slice(0,12)}...`:i;f(`${r} = ${n}`)}}}}),lr=ae({meta:{name:"config",description:"Manage CLI configuration"},subCommands:{get:sr,set:pr,show:dr}})});import{defineCommand as ur}from"citty";S();k();import{runCommand as Ft,runMain as Se}from"citty";async function Re(e,t=process.argv.slice(2),r=i=>process.exit(i)){if(t.includes("--help")||t.includes("-h")||t.length===1&&t[0]==="--version"){await Se(e,{rawArgs:t});return}try{await Ft(e,{rawArgs:t})}catch(n){if(n instanceof s){p(`Error: ${n.message}`),r(n.exitCode);return}if(n instanceof Error&&n.name==="CLIError"){await Se(e,{rawArgs:t});return}p(`Error: ${n instanceof Error?n.message:String(n)}`),r(1)}}_();var cr=ur({meta:{name:"pixelvault",version:le,description:"CLI for PixelVault \u2014 agent-first image and video hosting"},subCommands:{register:()=>Promise.resolve().then(()=>(Ie(),Ee)).then(e=>e.default),login:()=>Promise.resolve().then(()=>(Ae(),Ce)).then(e=>e.default),upload:()=>Promise.resolve().then(()=>(Be(),Le)).then(e=>e.default),generate:()=>Promise.resolve().then(()=>(Me(),Ke)).then(e=>e.default),list:()=>Promise.resolve().then(()=>(Je(),Ve)).then(e=>e.default),tags:()=>Promise.resolve().then(()=>(We(),Ge)).then(e=>e.default),describe:()=>Promise.resolve().then(()=>(Ye(),Xe)).then(e=>e.default),optimize:()=>Promise.resolve().then(()=>(ct(),ut)).then(e=>e.default),get:()=>Promise.resolve().then(()=>(gt(),ft)).then(e=>e.default),meta:()=>Promise.resolve().then(()=>(yt(),ht)).then(e=>e.default),delete:()=>Promise.resolve().then(()=>(bt(),wt)).then(e=>e.default),export:()=>Promise.resolve().then(()=>(_t(),xt)).then(e=>e.default),usage:()=>Promise.resolve().then(()=>(Rt(),St)).then(e=>e.default),keys:()=>Promise.resolve().then(()=>(Ot(),jt)).then(e=>e.default),upgrade:()=>Promise.resolve().then(()=>(It(),Et)).then(e=>e.default),billing:()=>Promise.resolve().then(()=>(xe(),Pt)).then(e=>e.default),whoami:()=>Promise.resolve().then(()=>(At(),Ct)).then(e=>e.default),config:()=>Promise.resolve().then(()=>(Nt(),Tt)).then(e=>e.default)}});Re(cr);
9
+ Fix: reinstall pixelvault-cli without --omit=optional (npm install -g pixelvault-cli).`)}}function _i(e,t){switch(e==="keep"?t:e==="jpg"?"jpeg":e){case"jpeg":return{sharpFormat:"jpeg",ext:"jpg"};case"png":return{sharpFormat:"png",ext:"png"};case"webp":return{sharpFormat:"webp",ext:"webp"};case"avif":return{sharpFormat:"avif",ext:"avif"};default:return null}}async function ot(e,t,r,i){let n={inputBytes:t.length,outputBytes:t.length,ext:r},o=R=>({status:"skipped",reason:R,...n}),a;try{a=await e(t,Ze).metadata()}catch(R){let A=R instanceof Error?R.message:String(R);throw new Error(/pixel limit/i.test(A)?`image is over ${it/1e6} megapixels`:"not a readable image")}if(a.format==="svg")return o("SVG is not supported");if(a.format==="gif")return o("GIF is not supported");if((a.pages??1)>1)return o("animated images are not supported");if(a.format==="heif"&&a.compression==="hevc")return o("HEIC is not supported (convert it at https://pixelvault.dev/tools/heic-to-url/)");let u=a.format==="heif"?"avif":a.format,b=_i(i.format,u);if(!b)return o(`unsupported format: ${a.format}`);let g=e(t,Ze).rotate();switch((i.maxWidth||i.maxHeight)&&(g=g.resize({width:i.maxWidth,height:i.maxHeight,fit:"inside",withoutEnlargement:!0})),b.sharpFormat==="jpeg"&&a.hasAlpha&&(g=g.flatten({background:"#ffffff"})),i.keepMetadata&&(g=g.keepMetadata()),b.sharpFormat){case"jpeg":g=g.jpeg({quality:i.quality,mozjpeg:!0});break;case"png":g=g.png({compressionLevel:9,effort:10});break;case"webp":g=g.webp({quality:i.quality,effort:5});break;case"avif":g=g.avif({quality:i.quality,effort:4});break}let{data:d,info:c}=await g.toBuffer({resolveWithObject:!0});if(d.length>=t.length)return o("already optimal (result was not smaller)");let E=(a.orientation??1)>=5,K=E?a.height:a.width,O=E?a.width:a.height;return b.sharpFormat===u&&c.width===K&&c.height===O&&(t.length-d.length)/t.length<et/100?o(`already optimal (would save under ${et}%)`):{status:"optimized",inputBytes:t.length,outputBytes:d.length,ext:b.ext,width:c.width,height:c.height,buffer:d}}function ye(e,t){return e<=0?0:Math.round((e-t)/e*100)}var it,he,Ze,et,rt,ge,tt,at=w(()=>{"use strict";$();it=100*1e3*1e3,he=100*1024*1024,Ze={limitInputPixels:it,failOn:"error"},et=5,rt=["jpg","jpeg","png","webp","avif"],ge=20,tt=9});var mt={};S(mt,{assertSafePlans:()=>ct,collectItems:()=>dt,default:()=>Di,outputExt:()=>lt,parsePositiveInt:()=>ie,planOutput:()=>ut,writeAtomic:()=>ve});import{defineCommand as ki}from"citty";import{createHash as Si}from"crypto";import{availableParallelism as $i}from"os";import{readFileSync as Ri,writeFileSync as ji,renameSync as Oi,unlinkSync as Pi,mkdirSync as Ei,statSync as we,lstatSync as be,readdirSync as Ii,existsSync as Ci}from"fs";import{basename as B,dirname as re,extname as M,join as F,relative as Ai,resolve as q}from"path";function ie(e,t){if(t==null||t==="")return;let r=Number(t);if(!Number.isInteger(r)||r<1)throw new s(`${e} must be a positive whole number.`);return r}function Ui(e){return rt.includes(M(e).slice(1).toLowerCase())}function pt(e,t,r){for(let i of Ii(e)){if(i.startsWith(".")||i==="node_modules")continue;let n=F(e,i),o=be(n);if(!o.isSymbolicLink())if(o.isDirectory()){if(r&&q(n)===r)continue;pt(n,t,r)}else Ui(n)&&!Ti.test(B(n,M(n)))&&t.push(n)}}function dt(e,t,r){let i=r?q(r):void 0,n=[];for(let o of e){let a;try{a=we(o)}catch{throw new s(`No such file or directory: ${o}`)}if(a.isDirectory()){if(!t)throw new s(`${o} is a directory. Pass --recursive to optimize everything inside it.`);let u=[];pt(o,u,i);for(let b of u.sort())n.push({file:b,root:o})}else n.push({file:o,root:null})}return n}function lt(e,t){return t==="keep"?M(e.file).slice(1):t==="jpg"?"jpg":t}function ut(e,t,r){let i=B(e.file,M(e.file)),n=lt(e,t);if(r.inPlace)return{item:e,optimized:e.file,kept:null};if(r.outDir){let o=e.root?re(Ai(e.root,e.file)):".",a=F(r.outDir,o);return{item:e,optimized:F(a,`${i}.${n}`),kept:F(a,B(e.file))}}return r.writeBeside?{item:e,optimized:F(re(e.file),`${i}.optimized.${n}`),kept:null}:{item:e,optimized:null,kept:null}}function ct(e,t){let r=new Set(e.map(n=>q(n.item.file))),i=new Map;for(let n of e){let o=q(n.item.file);if(t.inPlace&&be(n.item.file).isSymbolicLink())throw new s(`${n.item.file} is a symlink; refusing to overwrite it in place. Optimize the real file instead.`);let a=[[n.optimized,!1],[n.kept,!0]];for(let[u,b]of a){if(!u)continue;let g=q(u),d=g===o;if(d&&b&&!t.inPlace&&n.optimized!==u)continue;if(d&&!t.inPlace)throw new s(`${n.item.file}: the output would overwrite the original. Use --in-place if that is what you want.`);if(!d&&r.has(g))throw new s(`${u} is also an input file; refusing to overwrite it. Use different output settings.`);let c=i.get(g);if(c&&c!==n.item.file)throw new s(`${n.item.file} and ${c} would both be written to ${u}. Optimize them separately or use --out-dir with --recursive.`);if(i.set(g,n.item.file),!d&&Ci(g)&&be(g).isSymbolicLink())throw new s(`${u} is a symlink; refusing to write through it.`)}}}function ve(e,t,r){Ei(re(e),{recursive:!0});let i=F(re(e),`.${B(e)}.${process.pid}.tmp`);try{ji(i,t,r!==void 0?{mode:r}:void 0),Oi(i,e)}catch(n){try{Pi(i)}catch{}throw n}}async function Ni(e,t,r){let i=new Array(e.length),n=0,o=Array.from({length:Math.min(t,e.length)},async()=>{for(;n<e.length;){let a=n++;i[a]=await r(e[a])}});return await Promise.all(o),i}var st,Ti,Di,ft=w(()=>{"use strict";_();x();$();k();at();st=["keep","webp","avif","jpg","png"],Ti=/\.optimized$/i;Di=ki({meta:{name:"optimize",description:"Compress images locally (no account needed): resize, convert, strip metadata. Never writes a file bigger than the original"},args:{files:{type:"positional",description:"Image files (or directories with --recursive)",required:!0},format:{type:"string",description:"Output format: keep (default), webp, avif, jpg, png",default:"keep"},"max-width":{type:"string",description:"Downscale to at most this width in px (never upscales)"},"max-height":{type:"string",description:"Downscale to at most this height in px (never upscales)"},quality:{type:"string",description:"JPEG/WebP/AVIF quality 1-100 (default 80). PNG output is always lossless",default:"80"},"keep-metadata":{type:"boolean",description:"Keep EXIF/GPS and other metadata (stripped by default)",default:!1},"out-dir":{type:"string",description:"Write results into this directory (mirrors subfolders with --recursive)"},"in-place":{type:"boolean",description:"Overwrite each original (only with --format keep). Default writes name.optimized.ext beside it",default:!1},recursive:{type:"boolean",description:"Descend into directories",alias:"r",default:!1},"dry-run":{type:"boolean",description:"Report what would be saved without writing anything",default:!1},host:{type:"boolean",description:"Upload the optimized image and print its CDN URL (needs an API key). Local files are written only with --out-dir/--in-place",default:!1},json:{type:"boolean",description:"Output a JSON report (always an array)",default:!1}},async run({args:e}){let t=String(e.format);if(!st.includes(t))throw new s(`--format must be one of: ${st.join(", ")}.`);let r=ie("--quality",e.quality)??80;if(r>100)throw new s("--quality must be a whole number between 1 and 100.");let i=ie("--max-width",e["max-width"]),n=ie("--max-height",e["max-height"]),o=e["out-dir"]?String(e["out-dir"]):void 0;if(e["in-place"]&&o)throw new s("Use either --in-place or --out-dir, not both.");if(e["in-place"]&&t!=="keep")throw new s("--in-place only works with --format keep (changing format would need a different file name).");let a=!!e["dry-run"],u=!!e.host;if(u&&a)throw new s("--host can't be combined with --dry-run (it would upload).");let b=u?y():void 0,g=e._.length?e._.map(String):[String(e.files)],d=dt(g,!!e.recursive,o);if(d.length===0)throw new s("No supported images found (jpg, png, webp, avif).");let c={outDir:o,inPlace:!!e["in-place"],writeBeside:!u},E=d.map(m=>ut(m,t,c));ct(E,c);let K=await nt(),O={format:t,maxWidth:i,maxHeight:n,quality:r,keepMetadata:!!e["keep-metadata"]},v=u?2:Math.max(1,Math.min(4,$i())),R=await Ni(E,v,async m=>{let{item:j}=m,qt=M(j.file).slice(1).toLowerCase(),P={input:j.file,output:null,status:"error",input_bytes:0,output_bytes:0,saved_percent:0},W;try{if(we(j.file).size>he)throw new Error(`file is over ${he/1024/1024} MB`);W=Ri(j.file);let C=await ot(K,W,qt,O),J=C.status==="optimized"&&C.buffer?C.buffer:null;if(P.status=C.status,P.reason=C.reason,P.input_bytes=C.inputBytes,P.output_bytes=C.outputBytes,P.saved_percent=ye(C.inputBytes,C.outputBytes),!a)if(J&&m.optimized){let T=c.inPlace?we(j.file).mode&511:void 0;ve(m.optimized,J,T),P.output=m.optimized}else!J&&m.kept&&q(m.kept)!==q(j.file)&&(ve(m.kept,W),P.output=m.kept);if(u&&b)try{let T=J??W,zt=J?`${B(j.file,M(j.file))}.${C.ext}`:B(j.file),Se=new FormData;Se.append("file",new File([new Uint8Array(T)],zt));let $e=await l({method:"POST",path:"/v1/images",auth:b,formData:Se,headers:{"Idempotency-Key":Si("sha256").update(T).digest("hex")}});P.url=$e.data.optimized_url??$e.data.url}catch(T){P.upload_error=T instanceof Error?T.message:String(T)}}catch(Y){P.status="error",P.reason=Y instanceof Error?Y.message:String(Y)}return P}),A=0,pe=0,V=0;for(let m of R){if(m.status==="error"){V++,p(`Failed ${m.input}: ${m.reason}`);continue}if(m.upload_error&&(V++,p(`Upload failed for ${m.input}: ${m.upload_error}`)),A+=m.input_bytes,pe+=m.output_bytes,m.status==="skipped")p(`Skipped ${m.input}: ${m.reason}`);else{let j=m.output?` \u2192 ${m.output}`:m.url?` \u2192 ${m.url}`:"";p(`${a?"Would save":"Optimized"} ${m.input}${j} ${U(m.input_bytes)} \u2192 ${U(m.output_bytes)} (-${m.saved_percent}%)`)}}if(R.length>1&&(p(`Total: ${U(A)} \u2192 ${U(pe)} (-${ye(A,pe)}%)`),V&&p(`${V} of ${R.length} failed.`)),e.json)h(R);else for(let m of R){if(m.status==="error")continue;let j=m.url??m.output??(m.status==="skipped"?m.input:null);j&&f(j)}V&&(process.exitCode=1)}})});var ht={};S(ht,{applyTransform:()=>gt,default:()=>Li});import{defineCommand as qi}from"citty";import{writeFileSync as zi}from"fs";function gt(e,t){if(!t)return e;let r=t.trim().replace(/^[?&]+/,"");if(!r)return e;let i=new URLSearchParams(r).toString();return i?e.includes("?")?`${e}&${i}`:`${e}?${i}`:e}var Li,yt=w(()=>{"use strict";_();x();k();$();Li=qi({meta:{name:"get",description:"Get an image's CDN URL or metadata, or download it \u2014 optionally transformed"},args:{id:{type:"positional",description:"Image ID (e.g. img_abc123)",required:!0},output:{type:"string",description:"Download the image to this file path",alias:"o"},transform:{type:"string",description:'Transform params to apply, e.g. "w=400&fmt=webp", "segment=foreground", "tile=img_logo.png"',alias:"t"},json:{type:"boolean",description:"Output full JSON metadata",default:!1}},async run({args:e}){let t=y(),r=await l({path:`/v1/images/${encodeURIComponent(e.id)}`,auth:t}),i=gt(r.data.url,e.transform);if(e.output){let n=e.output,o;try{let a=await fetch(i);if(!a.ok)throw new s(`Failed to download image: HTTP ${a.status}`,1);o=await a.arrayBuffer()}catch(a){throw a instanceof s?a:new s(`Failed to download image: ${a instanceof Error?a.message:String(a)}`,1)}if(o.byteLength===0)throw new s("Downloaded 0 bytes \u2014 refusing to write an empty file",1);try{zi(n,Buffer.from(o))}catch(a){throw new s(`Failed to save ${n}: ${a instanceof Error?a.message:String(a)}`,1)}p(`Saved ${n}`),e.json?h(e.transform?{...r,transformed_url:i,output:n}:{...r,output:n}):f(i);return}e.json?h(e.transform?{...r,transformed_url:i}:r):f(i)}})});var wt={};S(wt,{default:()=>Bi});import{defineCommand as Fi}from"citty";var Bi,bt=w(()=>{"use strict";_();x();Z();$();k();Bi=Fi({meta:{name:"meta",description:"Replace an image's custom metadata (the new object replaces the old one; use --clear to remove it)"},args:{id:{type:"positional",description:"Image ID (e.g. img_abc123)",required:!0},meta:{type:"string",description:"Metadata as key=value (repeatable), e.g. --meta env=prod --meta run=42"},metadata:{type:"string",description:`Metadata as a JSON object, e.g. --metadata '{"env":"prod","n":3}'`},clear:{type:"boolean",description:"Remove all metadata from the image",default:!1}},async run({args:e}){let t=y(),r=z({meta:e.meta,metadata:e.metadata});if(e.clear&&r)throw new s("Use either --clear or --meta/--metadata, not both.");if(!e.clear&&!r)throw new s("Nothing to set: pass --meta key=value, --metadata '<json>', or --clear.");let i=await l({method:"PATCH",path:`/v1/images/${encodeURIComponent(String(e.id))}`,auth:t,body:{metadata:e.clear?null:r}});h(i.data.metadata)}})});var vt={};S(vt,{default:()=>Ki});import{defineCommand as Mi}from"citty";var Ki,xt=w(()=>{"use strict";_();x();Ki=Mi({meta:{name:"delete",description:"Delete an image by ID"},args:{id:{type:"positional",description:"Image ID (e.g. img_abc123)",required:!0}},async run({args:e}){let t=y();await l({method:"DELETE",path:`/v1/images/${e.id}`,auth:t})}})});var kt={};S(kt,{default:()=>Yi,defaultOutputPath:()=>_t});import{defineCommand as Vi}from"citty";import{writeFileSync as Ji,mkdirSync as Gi}from"fs";import{execFileSync as Xi}from"child_process";function _t(e,t){return t&&t.length>0?t:`pixelvault-export-${e}.tar`}var Wi,Yi,St=w(()=>{"use strict";_();x();k();$();Wi=e=>new Promise(t=>setTimeout(t,e)),Yi=Vi({meta:{name:"export",description:"Export every image in your project as a tar archive (manifest.json + all images). Your data is always portable \u2014 no lock-in."},args:{output:{type:"string",description:"Write the archive to this path (default: pixelvault-export-<job>.tar)",alias:"o"},extract:{type:"boolean",description:"Extract the archive into a directory after download (requires `tar`)",default:!1},timeout:{type:"string",description:"Max seconds to wait for the export to finish (default: 600)",default:"600"},json:{type:"boolean",description:"Output the final job JSON to stdout",default:!1}},async run({args:e}){let t=y(),r=(Number(e.timeout)||600)*1e3,i;try{i=await l({method:"POST",path:"/v1/export",auth:t})}catch(d){throw d instanceof s&&/in progress/i.test(d.message)?new s("An export for this project is already in progress. Wait for it to finish, then try again.",1):d}p(`Export started \u2014 ${i.image_count} image(s), job ${i.job_id}`);let n=Date.now()+r,o,a=1e3;for(;o=await l({path:`/v1/export-jobs/${encodeURIComponent(i.job_id)}`,auth:t}),o.status!=="complete";){if(o.status==="failed")throw new s(`Export failed: ${o.error??"unknown error"}`,1);if(o.status==="expired")throw new s("Export expired before it could be downloaded. Start a new one.",1);if(Date.now()>n)throw new s(`Timed out after ${e.timeout}s (job still ${o.status}). It may still finish \u2014 re-run to poll, or raise --timeout.`,1);p(` ${o.status}\u2026 ${o.processed_count}/${o.image_count}`),await Wi(a),a=Math.min(Math.round(a*1.5),5e3)}let u=_t(i.job_id,e.output),b=`${H()}/v1/export-jobs/${encodeURIComponent(i.job_id)}/download`,g;try{let d=await fetch(b,{headers:{Authorization:`Bearer ${t}`}});if(!d.ok)throw new s(`Failed to download export: HTTP ${d.status}`,1);g=await d.arrayBuffer()}catch(d){throw d instanceof s?d:new s(`Failed to download export: ${d instanceof Error?d.message:String(d)}`,1)}if(g.byteLength===0)throw new s("Downloaded 0 bytes \u2014 refusing to write an empty archive.",1);try{Ji(u,Buffer.from(g))}catch(d){throw new s(`Failed to save ${u}: ${d instanceof Error?d.message:String(d)}`,1)}if(p(`Saved ${u} \u2014 ${o.image_count} image(s), ${g.byteLength} bytes`),e.extract){let d=u.replace(/\.tar$/,"")||`${u}-extracted`;try{Gi(d,{recursive:!0}),Xi("tar",["-xf",u,"-C",d],{stdio:"ignore"}),p(`Extracted into ${d}/`)}catch(c){throw new s(`Saved ${u}, but extraction failed (is \`tar\` installed?): ${c instanceof Error?c.message:String(c)}`,1)}}e.json?h({...o,output:u}):f(u)}})});var jt={};S(jt,{default:()=>Qi,renderUsage:()=>Rt});import{defineCommand as Hi}from"citty";function Rt(e){return[`Plan: ${e.plan}`,`Period: ${e.period} (resets ${e.resets_at})`,`Storage: ${U(e.usage.storage_bytes)} used / ${xe(e.limits.storage_bytes)} limit / ${xe(e.remaining.storage_bytes)} remaining`,`Uploads: ${e.usage.uploads} used / ${$t(e.limits.uploads_per_month)} limit / ${$t(e.remaining.uploads)} remaining`,`Bandwidth: ${xe(e.limits.bandwidth_bytes_per_month)} limit per month (usage not reported)`,...e.generation?[`AI images: ${e.generation.credits_used} credits used / ${e.generation.credits_limit} limit / ${e.generation.credits_remaining} remaining`]:[]]}var xe,$t,Qi,Ot=w(()=>{"use strict";_();x();k();xe=e=>e==null?"unlimited":U(e),$t=e=>e==null?"unlimited":String(e);Qi=Hi({meta:{name:"usage",description:"Show plan, quota usage and limits (secret key required)"},args:{json:{type:"boolean",description:"Output the usage object as JSON",default:!1}},async run({args:e}){let t=y(),r=await l({path:"/v1/usage",auth:t});if(e.json)h(r.data);else for(let i of Rt(r.data))f(i)}})});var Pt={};S(Pt,{default:()=>ir,explainKeyError:()=>oe});import{defineCommand as ne}from"citty";function oe(e){if(e instanceof D){switch(e.code){case"key_limit_reached":throw new s("Key limit reached (25 per project). Revoke an unused key with `pixelvault keys revoke <id>` first.",1);case"rate_limit_exceeded":{let t=e.retryAfter?` Retry after ${e.retryAfter} seconds.`:"";throw new s(`Rate limit exceeded (20 key changes per hour per project).${t}`,1)}case"cannot_revoke_current_key":throw new s("Cannot revoke the key you are authenticating with. Create a new key, switch to it (PIXELVAULT_API_KEY), then revoke the old one with the new key.",1);case"not_found":throw new s("Key not found. Only secret keys can be revoked; check the id with `pixelvault keys list`.",1);case"validation_error":throw new s(`Invalid request: ${e.message}`,1);case"forbidden":throw new s("This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.",1)}if(e.status===403)throw new s("This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.",1)}throw e}var Zi,er,tr,ir,Et=w(()=>{"use strict";_();x();$();k();Zi=ne({meta:{name:"list",description:"List the project's API keys"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let t=y(),r;try{r=await l({path:"/v1/keys",auth:t})}catch(i){oe(i)}if(e.json){h(r.data);return}for(let i of r.data)f([i.id,i.type,i.environment,`${i.key_prefix}...`,i.label??"-",`created ${i.created_at}`,`last used ${i.last_used_at??"never"}`,i.current?"(current)":""].filter(Boolean).join(" "))}}),er=ne({meta:{name:"create",description:"Create a new secret API key (the raw key is printed once)"},args:{label:{type:"string",description:"Label for the key (max 60 chars)"},env:{type:"string",description:"Environment: live or test (default: live)"},json:{type:"boolean",description:"Output the full data object as JSON",default:!1}},async run({args:e}){let t=y();if(e.env&&e.env!=="live"&&e.env!=="test")throw new s("--env must be 'live' or 'test'.",1);if(e.label&&e.label.length>60)throw new s("--label must be 60 characters or fewer.",1);let r={};e.label&&(r.label=e.label),e.env&&(r.environment=e.env);let i;try{i=await l({method:"POST",path:"/v1/keys",auth:t,body:r})}catch(n){oe(n)}p("This key is shown ONCE. Store it now (secret manager / env var); never commit it or paste it into logs."),e.json?h(i.data):f(i.data.api_key)}}),tr=ne({meta:{name:"revoke",description:"Revoke a secret API key by id (not the one in use)"},args:{id:{type:"positional",description:"Key id (see `pixelvault keys list`)",required:!0},json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let t=y(),r;try{r=await l({method:"DELETE",path:`/v1/keys/${encodeURIComponent(e.id)}`,auth:t})}catch(i){oe(i)}e.json?h(r.data):p(`Revoked key ${e.id}.`)}}),ir=ne({meta:{name:"keys",description:"Manage API keys (list, create, revoke)"},subCommands:{list:Zi,create:er,revoke:tr}})});var It={};S(It,{default:()=>nr,explainBillingError:()=>ae});import{defineCommand as rr}from"citty";function ae(e){if(e instanceof D)switch(e.code){case"test_key_forbidden":throw new s("Billing needs a live key (pv_live_...); test keys can't start checkout or open the portal.",1);case"publishable_key_forbidden":throw new s("Billing needs a secret live key (pv_live_...); publishable keys are not allowed.",1);case"email_not_verified":throw new s("Verify your email before upgrading (check your inbox for the code).",1);case"already_subscribed":throw new s("You already have a subscription. Change or cancel it with `pixelvault billing`.",1);case"no_subscription":throw new s("This account has no billing history yet. Start one with `pixelvault upgrade <starter|pro>`.",1);case"billing_unavailable":throw new s("Paid plans are not available right now. Try again later.",1);case"rate_limit_exceeded":{let t=e.retryAfter?` Retry after ${e.retryAfter} seconds.`:"";throw new s(`Rate limit exceeded (10 billing requests per hour per account).${t}`,1)}}throw e}var nr,_e=w(()=>{"use strict";_();x();$();k();nr=rr({meta:{name:"billing",description:"Print a Stripe billing-portal URL to change plan, update payment details or cancel (live key required)"},args:{json:{type:"boolean",description:"Output { url } as JSON",default:!1}},async run({args:e}){let t=y(),r;try{r=await l({method:"POST",path:"/v1/billing/portal",body:{},auth:t})}catch(i){ae(i)}e.json?h({url:r.url}):(f(r.url),p("Open this link yourself to change plan, update payment details or cancel. It can cancel the subscription, so don't paste it anywhere shared."))}})});var Ct={};S(Ct,{default:()=>ar});import{defineCommand as or}from"citty";var ke,ar,At=w(()=>{"use strict";_();x();$();k();_e();ke=["starter","pro"],ar=or({meta:{name:"upgrade",description:"Print a Stripe Checkout URL to subscribe to a paid plan (live key required; nothing is charged until you pay)"},args:{plan:{type:"positional",description:`Plan to subscribe to: ${ke.join(" | ")}`,required:!0},json:{type:"boolean",description:"Output { url } as JSON",default:!1}},async run({args:e}){let t=String(e.plan);if(!ke.includes(t))throw new s(`Unknown plan "${t}". Choose one of: ${ke.join(", ")}.`,1);let r=y(),i;try{i=await l({method:"POST",path:"/v1/billing/checkout",body:{plan:t},auth:r})}catch(n){ae(n)}e.json?h({url:i.url}):(f(i.url),p("Open this link to complete payment. The plan changes once Stripe confirms \u2014 check with `pixelvault usage`."))}})});var Tt={};S(Tt,{default:()=>pr});import{defineCommand as sr}from"citty";var pr,Ut=w(()=>{"use strict";x();k();pr=sr({meta:{name:"whoami",description:"Show current authentication state"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let t=I(),r=le();!r&&!t.email&&(p("Not authenticated. Run `pixelvault register` or `pixelvault login`."),process.exit(1));let i={email:t.email||"(unknown)",project:t.default_project||"(unknown)",api_key:r?`${r.slice(0,12)}...${r.slice(-4)}`:"(not set)",source:process.env.PIXELVAULT_API_KEY?"env":"config",identity_source:"cached"};if(e.json)h(i);else{let n=o=>o?" (cached, not verified for this key)":"";f(`Email: ${i.email}${n(t.email)}`),f(`Project: ${i.project}${n(t.default_project)}`),f(`API Key: ${i.api_key} (${i.source})`)}}})});var Nt={};S(Nt,{default:()=>cr});import{defineCommand as se}from"citty";var dr,lr,ur,cr,Dt=w(()=>{"use strict";x();k();dr=se({meta:{name:"get",description:"Get a config value"},args:{key:{type:"positional",description:"Config key (api_key, default_project, email, api_url)",required:!0}},async run({args:e}){let r=I()[e.key];r===void 0&&(p(`Key "${e.key}" is not set.`),process.exit(1)),f(String(r))}}),lr=se({meta:{name:"set",description:"Set a config value"},args:{key:{type:"positional",description:"Config key",required:!0},value:{type:"positional",description:"Config value",required:!0}},async run({args:e}){let t=["api_key","default_project","email","api_url"];t.includes(e.key)||(p(`Invalid key. Valid keys: ${t.join(", ")}`),process.exit(1)),e.key==="api_key"&&e.value!==I().api_key?N({api_key:e.value,email:void 0,default_project:void 0}):N({[e.key]:e.value}),p(`Set ${e.key} = ${e.key==="api_key"?"***":e.value}`)}}),ur=se({meta:{name:"show",description:"Show all config values"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let t=I();if(e.json)h(t);else{p(`Config: ${Ee()}`);for(let[r,i]of Object.entries(t)){let n=r==="api_key"&&i?`${i.slice(0,12)}...`:i;f(`${r} = ${n}`)}}}}),cr=se({meta:{name:"config",description:"Manage CLI configuration"},subCommands:{get:dr,set:lr,show:ur}})});import{defineCommand as mr}from"citty";$();k();import{runCommand as Ft,runMain as Re}from"citty";async function je(e,t=process.argv.slice(2),r=i=>process.exit(i)){if(t.includes("--help")||t.includes("-h")||t.length===1&&t[0]==="--version"){await Re(e,{rawArgs:t});return}try{await Ft(e,{rawArgs:t})}catch(n){if(n instanceof s){p(`Error: ${n.message}`),r(n.exitCode);return}if(n instanceof Error&&n.name==="CLIError"){await Re(e,{rawArgs:t});return}p(`Error: ${n instanceof Error?n.message:String(n)}`),r(1)}}_();var fr=mr({meta:{name:"pixelvault",version:ue,description:"CLI for PixelVault \u2014 agent-first image and video hosting"},subCommands:{register:()=>Promise.resolve().then(()=>(Ce(),Ie)).then(e=>e.default),login:()=>Promise.resolve().then(()=>(Te(),Ae)).then(e=>e.default),upload:()=>Promise.resolve().then(()=>(Ke(),Me)).then(e=>e.default),generate:()=>Promise.resolve().then(()=>(Je(),Ve)).then(e=>e.default),list:()=>Promise.resolve().then(()=>(Xe(),Ge)).then(e=>e.default),tags:()=>Promise.resolve().then(()=>(Ye(),We)).then(e=>e.default),describe:()=>Promise.resolve().then(()=>(Qe(),He)).then(e=>e.default),optimize:()=>Promise.resolve().then(()=>(ft(),mt)).then(e=>e.default),get:()=>Promise.resolve().then(()=>(yt(),ht)).then(e=>e.default),meta:()=>Promise.resolve().then(()=>(bt(),wt)).then(e=>e.default),delete:()=>Promise.resolve().then(()=>(xt(),vt)).then(e=>e.default),export:()=>Promise.resolve().then(()=>(St(),kt)).then(e=>e.default),usage:()=>Promise.resolve().then(()=>(Ot(),jt)).then(e=>e.default),keys:()=>Promise.resolve().then(()=>(Et(),Pt)).then(e=>e.default),upgrade:()=>Promise.resolve().then(()=>(At(),Ct)).then(e=>e.default),billing:()=>Promise.resolve().then(()=>(_e(),It)).then(e=>e.default),whoami:()=>Promise.resolve().then(()=>(Ut(),Tt)).then(e=>e.default),config:()=>Promise.resolve().then(()=>(Dt(),Nt)).then(e=>e.default)}});je(fr);
10
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/errors.ts","../src/lib/output.ts","../src/lib/config.ts","../src/lib/client.ts","../src/lib/prompt.ts","../src/commands/register.ts","../src/commands/login.ts","../src/lib/duration.ts","../src/lib/upload-options.ts","../src/lib/metadata.ts","../src/lib/describe.ts","../src/lib/video-upload.ts","../src/commands/upload.ts","../src/commands/generate.ts","../src/commands/list.ts","../src/commands/tags.ts","../src/commands/describe.ts","../src/lib/optimize.ts","../src/commands/optimize.ts","../src/commands/get.ts","../src/commands/meta.ts","../src/commands/delete.ts","../src/commands/export.ts","../src/commands/usage.ts","../src/commands/keys.ts","../src/commands/billing.ts","../src/commands/upgrade.ts","../src/commands/whoami.ts","../src/commands/config.ts","../src/index.ts","../src/lib/run.ts"],"sourcesContent":["export class CliError extends Error {\n exitCode: number;\n\n constructor(message: string, exitCode = 1) {\n super(message);\n this.name = \"CliError\";\n this.exitCode = exitCode;\n }\n}\n","/**\n * Write to stdout (for machine-parseable output — URLs, JSON).\n */\nexport function stdout(text: string): void {\n process.stdout.write(text + \"\\n\");\n}\n\n/**\n * Write to stderr (for human messages — prompts, progress, errors).\n */\nexport function stderr(text: string): void {\n process.stderr.write(text + \"\\n\");\n}\n\n/**\n * Output data as JSON to stdout.\n */\nexport function jsonOut(data: unknown): void {\n stdout(JSON.stringify(data, null, 2));\n}\n\n/**\n * Print an error message to stderr and exit.\n */\nexport function fatal(message: string, exitCode = 1): never {\n stderr(`Error: ${message}`);\n process.exit(exitCode);\n}\n\n/**\n * Format a byte count for humans (1024-based).\n */\nexport function formatBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n const units = [\"KB\", \"MB\", \"GB\", \"TB\"];\n let v = n / 1024;\n let i = 0;\n while (v >= 1024 && i < units.length - 1) {\n v /= 1024;\n i++;\n }\n return `${v.toFixed(v >= 100 || Number.isInteger(v) ? 0 : 1)} ${units[i]}`;\n}\n","import { readFileSync, writeFileSync, mkdirSync, chmodSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport interface Config {\n api_key?: string;\n default_project?: string;\n email?: string;\n api_url?: string;\n}\n\nconst CONFIG_DIR = join(homedir(), \".pixelvault\");\nconst CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\nconst DEFAULT_API_URL = \"https://api.pixelvault.dev\";\n\nexport function getConfigPath(): string {\n return CONFIG_FILE;\n}\n\nexport function readConfig(): Config {\n try {\n const raw = readFileSync(CONFIG_FILE, \"utf-8\");\n return JSON.parse(raw) as Config;\n } catch {\n return {};\n }\n}\n\nexport function writeConfig(config: Config): void {\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });\n writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\nexport function updateConfig(updates: Partial<Config>): void {\n const config = readConfig();\n writeConfig({ ...config, ...updates });\n}\n\nexport function getApiKey(): string | undefined {\n return process.env[\"PIXELVAULT_API_KEY\"] || readConfig().api_key;\n}\n\nexport function getApiUrl(): string {\n return (\n process.env[\"PIXELVAULT_API_URL\"] ||\n readConfig().api_url ||\n DEFAULT_API_URL\n );\n}\n\nexport function requireApiKey(): string {\n const key = getApiKey();\n if (!key) {\n throw new Error(\n \"Not authenticated. Run `pixelvault register` or `pixelvault login`, or set PIXELVAULT_API_KEY.\"\n );\n }\n return key;\n}\n","import { getApiUrl } from \"./config.js\";\nimport { CliError } from \"./errors.js\";\n\ninterface RequestOptions {\n method?: string;\n path: string;\n body?: unknown;\n headers?: Record<string, string>;\n formData?: FormData;\n rawBody?: BodyInit;\n}\n\ninterface ApiError {\n error: { code: string; message: string };\n}\n\n// Identifies the CLI to the API so signups made through it can be attributed to\n// this channel (server reads X-PixelVault-Client at register/device-login — see\n// pixelvault docs/specs/lead-attribution.md). Keep the version in sync with\n// package.json on release (tests/lib/version.test.ts fails if they drift).\nexport const VERSION = \"0.15.0\";\nconst CLIENT_ID = `pixelvault-cli/${VERSION}`;\n\n/** CliError carrying the API's machine-readable error details. */\nexport class ApiRequestError extends CliError {\n status: number;\n code?: string;\n retryAfter?: string;\n\n constructor(\n message: string,\n status: number,\n code?: string,\n retryAfter?: string\n ) {\n super(message, 1);\n this.name = \"ApiError\";\n this.status = status;\n this.code = code;\n this.retryAfter = retryAfter;\n }\n}\n\nexport async function apiRequest<T>(\n options: RequestOptions & { auth?: string }\n): Promise<T> {\n const baseUrl = getApiUrl();\n const url = `${baseUrl}${options.path}`;\n\n const headers: Record<string, string> = { ...options.headers };\n\n if (!headers[\"X-PixelVault-Client\"]) {\n headers[\"X-PixelVault-Client\"] = CLIENT_ID;\n }\n\n if (options.auth) {\n headers[\"Authorization\"] = `Bearer ${options.auth}`;\n }\n\n let fetchBody: BodyInit | undefined;\n\n if (options.formData) {\n fetchBody = options.formData;\n } else if (options.rawBody !== undefined) {\n fetchBody = options.rawBody;\n } else if (options.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n fetchBody = JSON.stringify(options.body);\n }\n\n let res: Response;\n try {\n const init: RequestInit & { duplex?: \"half\" } = {\n method: options.method || \"GET\",\n headers,\n body: fetchBody,\n };\n if (fetchBody instanceof ReadableStream) init.duplex = \"half\";\n res = await fetch(url, init);\n } catch (err) {\n throw new CliError(\n `Network error: ${err instanceof Error ? err.message : \"Failed to connect\"}`,\n 1\n );\n }\n\n if (!res.ok) {\n let errorBody: ApiError | undefined;\n try {\n errorBody = (await res.json()) as ApiError;\n } catch {\n // ignore parse failures\n }\n\n const message =\n errorBody?.error?.message || `HTTP ${res.status}: ${res.statusText}`;\n throw new ApiRequestError(\n message,\n res.status,\n errorBody?.error?.code,\n res.headers?.get?.(\"Retry-After\") ?? undefined\n );\n }\n\n return (await res.json()) as T;\n}\n","import { createInterface } from \"node:readline\";\n\n/**\n * Prompt the user for input via stderr (keeps stdout clean for agents).\n */\nexport function prompt(question: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Prompt for a password (input is not echoed).\n * Uses raw mode to suppress echo when available, falls back to regular prompt.\n */\nexport async function promptPassword(question: string): Promise<string> {\n if (!process.stdin.isTTY) {\n return prompt(question);\n }\n\n return new Promise((resolve) => {\n process.stderr.write(question);\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.setEncoding(\"utf-8\");\n\n let password = \"\";\n\n const onData = (char: string) => {\n if (char === \"\\n\" || char === \"\\r\" || char === \"\\u0004\") {\n process.stdin.setRawMode(false);\n process.stdin.pause();\n process.stdin.removeListener(\"data\", onData);\n process.stderr.write(\"\\n\");\n resolve(password);\n } else if (char === \"\\u0003\") {\n // Ctrl+C\n process.stdin.setRawMode(false);\n process.exit(130);\n } else if (\n char === \"\\u007F\" ||\n char === \"\\b\"\n ) {\n // Backspace\n password = password.slice(0, -1);\n } else {\n password += char;\n }\n };\n\n process.stdin.on(\"data\", onData);\n });\n}\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { updateConfig } from \"../lib/config.js\";\nimport { prompt, promptPassword } from \"../lib/prompt.js\";\nimport { stderr } from \"../lib/output.js\";\n\ninterface RegisterResponse {\n data: {\n account_id: string;\n email: string;\n email_verified: boolean;\n // Optional: older API versions omit it. Absent → treat as \"unknown\".\n password_set?: boolean;\n plan: string;\n default_project: {\n id: string;\n name: string;\n api_keys: { live: string };\n };\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"register\",\n description: \"Create a new PixelVault account\",\n },\n args: {\n email: {\n type: \"string\",\n description: \"Email address\",\n },\n password: {\n type: \"string\",\n description: \"Password (min 8 characters). Omit for a passwordless account.\",\n },\n passwordless: {\n type: \"boolean\",\n description:\n \"Create the account without a password (for agents/automation). Set a password later via the web 'Forgot password' flow.\",\n },\n },\n async run({ args }) {\n const interactive = Boolean(process.stdin.isTTY);\n\n // Email is always required. Only prompt when we have a TTY — in a headless\n // (agent) run, missing --email is a hard error rather than a hung prompt.\n const email = args.email || (interactive ? await prompt(\"Email: \") : \"\");\n if (!email) {\n stderr(\"Email is required. Pass --email <email>.\");\n process.exit(1);\n }\n\n // Resolve the password. Precedence: explicit --password > --passwordless >\n // interactive prompt (blank = passwordless) > headless default (passwordless,\n // so a non-TTY run never blocks on a hidden prompt).\n let password: string | undefined;\n if (args.password) {\n password = args.password;\n } else if (args.passwordless) {\n password = undefined;\n } else if (interactive) {\n const entered = await promptPassword(\n \"Password (leave blank for a passwordless account): \"\n );\n password = entered || undefined;\n } else {\n password = undefined;\n }\n\n if (password !== undefined && password.length < 8) {\n stderr(\"Password must be at least 8 characters.\");\n process.exit(1);\n }\n\n stderr(\"Creating account...\");\n\n const res = await apiRequest<RegisterResponse>({\n method: \"POST\",\n path: \"/v1/auth/register\",\n // Only send the password field when one was chosen — omitting it creates\n // a passwordless account server-side. `attribution.source: \"cli\"` buckets\n // the signup to this channel (server treats it as an untrusted hint that\n // wins over the derived source — see lead-attribution spec).\n body: {\n email,\n ...(password ? { password } : {}),\n attribution: { source: \"cli\" },\n },\n });\n\n updateConfig({\n api_key: res.data.default_project.api_keys.live,\n default_project: res.data.default_project.id,\n email: res.data.email,\n });\n\n stderr(`Account created. API key saved to ~/.pixelvault/config.json`);\n stderr(`Email: ${res.data.email}`);\n stderr(`Project: ${res.data.default_project.id}`);\n\n // password_set === false (or a passwordless request on an older API) means\n // there's no dashboard login yet — tell the user how to claim one.\n const passwordless = res.data.password_set === false || !password;\n if (passwordless) {\n stderr(\n \"No password set. To enable dashboard login, use 'Forgot password' at \" +\n \"https://pixelvault.dev/forgot-password to set one.\"\n );\n }\n\n if (!res.data.email_verified) {\n stderr(\"Verify your email to lift the upload limit for the free tier.\");\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { readConfig, updateConfig, getApiUrl } from \"../lib/config.js\";\nimport { prompt, promptPassword } from \"../lib/prompt.js\";\nimport { stderr } from \"../lib/output.js\";\n\ninterface LoginResponse {\n data: {\n token: string;\n account_id: string;\n email: string;\n email_verified: boolean;\n };\n}\n\ninterface RotateKeyResponse {\n data: {\n key_id: string;\n api_key: string;\n project_id: string;\n environment: string;\n };\n}\n\ninterface ProjectsResponse {\n data: Array<{ id: string; name: string }>;\n}\n\nexport default defineCommand({\n meta: {\n name: \"login\",\n description: \"Log in to your PixelVault account\",\n },\n args: {\n email: {\n type: \"string\",\n description: \"Email address\",\n },\n password: {\n type: \"string\",\n description: \"Password\",\n },\n },\n async run({ args }) {\n const email = args.email || (await prompt(\"Email: \"));\n const password = args.password || (await promptPassword(\"Password: \"));\n\n if (!email || !password) {\n stderr(\"Email and password are required.\");\n process.exit(1);\n }\n\n stderr(\"Logging in...\");\n\n // Step 1: Login to get JWT\n const loginRes = await apiRequest<LoginResponse>({\n method: \"POST\",\n path: \"/v1/auth/login\",\n body: { email, password },\n });\n\n const token = loginRes.data.token;\n\n // Step 2: Get projects to find default project ID\n let projectId: string | undefined;\n try {\n const projectsRes = await apiRequest<ProjectsResponse>({\n method: \"GET\",\n path: \"/v1/projects\",\n auth: token,\n });\n projectId = projectsRes.data[0]?.id;\n } catch {\n // Projects endpoint may not exist yet — continue without project ID\n }\n\n // Step 3: Rotate key to get a fresh API key\n if (projectId) {\n const rotateRes = await apiRequest<RotateKeyResponse>({\n method: \"POST\",\n path: \"/v1/auth/rotate-key\",\n auth: token,\n body: { project_id: projectId },\n });\n\n updateConfig({\n api_key: rotateRes.data.api_key,\n default_project: projectId,\n email: loginRes.data.email,\n });\n\n stderr(`Logged in. API key saved to ~/.pixelvault/config.json`);\n stderr(`Email: ${loginRes.data.email}`);\n stderr(`Project: ${projectId}`);\n } else {\n // Fallback: save JWT for manual key setup\n stderr(`Logged in as ${loginRes.data.email}.`);\n stderr(\n `Could not auto-configure API key. Set PIXELVAULT_API_KEY manually.`\n );\n // Any saved key/project may be from a previous account; drop the project\n // so whoami doesn't pair it with this email, and flag the key.\n updateConfig({ email: loginRes.data.email, default_project: undefined });\n if (readConfig().api_key) {\n stderr(\"Warning: the saved api_key may belong to a previous login. Replace it with `pixelvault config set api_key <key>`.\");\n }\n }\n },\n});\n","import { CliError } from \"./errors.js\";\n\n/** Signed-URL lifetime bounds, matching the API's `sign_expires_in` (60s..30d). */\nexport const MIN_EXPIRES_SECONDS = 60;\nexport const MAX_EXPIRES_SECONDS = 30 * 24 * 60 * 60;\n\n/**\n * Parse a signed-link lifetime like `30m`, `12h`, `7d`, `90s`, or a bare number\n * of seconds, into seconds. Rejects anything outside the API's accepted range so\n * a bad value fails locally with a clear message instead of a 400 from the server.\n */\nexport function parseExpires(input: string, flag = \"--expires\"): number {\n const match = /^(\\d+)\\s*([smhd]?)$/i.exec(input.trim());\n if (!match) {\n throw new CliError(\n `Invalid ${flag} \"${input}\". Use e.g. 30m, 12h, 7d, or a number of seconds.`\n );\n }\n\n const unit = (match[2] ?? \"\").toLowerCase();\n const multiplier = unit === \"d\" ? 86400 : unit === \"h\" ? 3600 : unit === \"m\" ? 60 : 1;\n const seconds = Number(match[1] ?? \"\") * multiplier;\n\n if (seconds < MIN_EXPIRES_SECONDS || seconds > MAX_EXPIRES_SECONDS) {\n throw new CliError(\n `${flag} must be between 60s and 30d (got ${seconds}s).`\n );\n }\n\n return seconds;\n}\n","/**\n * Validation for the retry-safety / reuse flags on `upload`, done up front so a\n * bad combination fails before any file is read or sent.\n */\n\n// Mirrors the API: 1-255 printable ASCII characters, no spaces.\nconst IDEMPOTENCY_KEY = /^[\\x21-\\x7e]{1,255}$/;\n\n/** Returns an error message for an invalid flag combination, or null when fine. */\nexport function validateReuseOptions(opts: {\n idempotencyKey?: string;\n dedupe?: boolean;\n fileCount: number;\n}): string | null {\n const { idempotencyKey, dedupe, fileCount } = opts;\n if (idempotencyKey !== undefined) {\n if (!IDEMPOTENCY_KEY.test(idempotencyKey)) {\n return \"--idempotency-key must be 1-255 printable ASCII characters with no spaces.\";\n }\n if (dedupe) {\n return \"--dedupe cannot be combined with --idempotency-key. Use the key for retry-safety, or --dedupe to reuse an identical image.\";\n }\n if (fileCount > 1) {\n return \"--idempotency-key identifies a single upload, so it can't be used with multiple files. Upload them one at a time with distinct keys.\";\n }\n }\n return null;\n}\n","import { CliError } from \"./errors.js\";\n\n/** citty yields a string for one flag and an array when the flag is repeated. */\nfunction asList(v: unknown): string[] {\n if (v === undefined || v === null || v === \"\") return [];\n return (Array.isArray(v) ? v : [v]).map(String);\n}\n\n/**\n * Build a metadata object from `--meta key=value` (repeatable, string values)\n * and/or `--metadata '<json object>'` (typed values). Returns null when neither\n * flag was given. A key present in both with different values is an error.\n */\nexport function parseMetadataFlags(opts: {\n meta?: unknown;\n metadata?: unknown;\n}): Record<string, unknown> | null {\n // A Map so a key like \"__proto__\" stays an ordinary entry.\n const out = new Map<string, unknown>();\n\n const json = asList(opts.metadata);\n if (json.length > 1) throw new CliError(\"--metadata can be given only once.\");\n if (json.length === 1) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json[0] as string);\n } catch {\n throw new CliError(\"--metadata must be a JSON object, e.g. --metadata '{\\\"env\\\":\\\"prod\\\"}'.\");\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new CliError(\"--metadata must be a JSON object, e.g. --metadata '{\\\"env\\\":\\\"prod\\\"}'.\");\n }\n for (const [k, v] of Object.entries(parsed)) out.set(k, v);\n }\n\n for (const pair of asList(opts.meta)) {\n const eq = pair.indexOf(\"=\");\n if (eq <= 0) throw new CliError(`--meta expects key=value, got \"${pair}\".`);\n const key = pair.slice(0, eq);\n const value = pair.slice(eq + 1);\n if (out.has(key) && out.get(key) !== value) {\n throw new CliError(`Metadata key \"${key}\" was given with conflicting values.`);\n }\n out.set(key, value);\n }\n\n return out.size === 0 ? null : Object.fromEntries(out);\n}\n","import { apiRequest } from \"./client.js\";\n\nexport interface ImageDescription {\n status: \"pending\" | \"done\" | \"failed\";\n alt?: string;\n caption?: string;\n ocr_text?: string;\n tags?: string[];\n error?: string;\n cached?: boolean;\n}\n\ninterface GetResponse {\n data: { description?: ImageDescription | null };\n}\n\nconst POLL_MS = 2000;\nconst MAX_WAIT_MS = 40_000;\n\n/**\n * Poll GET /v1/images/:id until its AI description is `done` or `failed` (or the wait\n * runs out, returning the last state). The server reports a description that never\n * finished as `failed`/`describe_timeout`, so this never waits forever on a lost job.\n * `sleep`/`now` are injectable for tests.\n */\nexport async function waitForDescription(\n id: string,\n auth: string,\n deps: { sleep?: (ms: number) => Promise<void>; now?: () => number } = {},\n): Promise<ImageDescription | null> {\n const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));\n const now = deps.now ?? Date.now;\n const deadline = now() + MAX_WAIT_MS;\n let last: ImageDescription | null = null;\n for (;;) {\n const res = await apiRequest<GetResponse>({ path: `/v1/images/${encodeURIComponent(id)}`, auth });\n last = res.data.description ?? null;\n if (!last || last.status !== \"pending\") return last;\n if (now() >= deadline) return last;\n await sleep(POLL_MS);\n }\n}\n\n/** One human-readable stderr line summarising a finished description. */\nexport function describeSummary(d: ImageDescription | null): string {\n if (!d) return \"No description was produced.\";\n if (d.status === \"done\") return `Alt: ${d.alt ?? \"\"}${d.tags?.length ? ` (tags: ${d.tags.join(\", \")})` : \"\"}`;\n if (d.status === \"failed\") return `Description failed (${d.error ?? \"unknown\"}).`;\n return \"Description is still pending; run `pixelvault get <id> --json` in a few seconds.\";\n}\n","import { createReadStream, statSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { apiRequest } from \"./client.js\";\nimport { CliError } from \"./errors.js\";\n\nconst DIRECT_MAX_BYTES = 100_000_000;\n\nexport interface VideoData {\n id: string;\n kind: \"video\";\n url: string;\n mime_type: \"video/mp4\";\n size: number;\n filename: string;\n created_at: string;\n}\n\ninterface UploadSession {\n upload_id: string;\n part_size: number;\n}\n\nfunction fileBody(filePath: string, start?: number, end?: number): ReadableStream<Uint8Array> {\n const stream = createReadStream(filePath, {\n ...(start !== undefined ? { start } : {}),\n ...(end !== undefined ? { end } : {}),\n });\n return Readable.toWeb(stream) as ReadableStream<Uint8Array>;\n}\n\n/** Upload one MP4 without buffering it in the CLI process. */\nexport async function uploadVideoFile(filePath: string, apiKey: string): Promise<VideoData> {\n const stat = statSync(filePath);\n if (!stat.isFile() || stat.size <= 0) {\n throw new CliError(\"Video must be a non-empty file.\", 1);\n }\n\n const filename = basename(filePath);\n if (stat.size <= DIRECT_MAX_BYTES) {\n const { data } = await apiRequest<{ data: VideoData }>({\n method: \"POST\",\n path: \"/v1/videos\",\n auth: apiKey,\n rawBody: fileBody(filePath),\n headers: {\n \"Content-Type\": \"video/mp4\",\n \"Content-Length\": String(stat.size),\n \"X-PixelVault-Filename\": encodeURIComponent(filename),\n },\n });\n return data;\n }\n\n let uploadId: string | undefined;\n try {\n const session = await apiRequest<{ data: UploadSession }>({\n method: \"POST\",\n path: \"/v1/videos/uploads\",\n auth: apiKey,\n body: { filename, size: stat.size },\n });\n uploadId = session.data.upload_id;\n const partSize = session.data.part_size;\n if (!Number.isSafeInteger(partSize) || partSize <= 0) {\n throw new CliError(\"The API returned an invalid video part size.\", 1);\n }\n\n let partNumber = 1;\n for (let start = 0; start < stat.size; start += partSize, partNumber += 1) {\n const length = Math.min(partSize, stat.size - start);\n await apiRequest({\n method: \"PUT\",\n path: `/v1/videos/uploads/${uploadId}/parts/${partNumber}`,\n auth: apiKey,\n rawBody: fileBody(filePath, start, start + length - 1),\n headers: { \"Content-Length\": String(length) },\n });\n }\n\n const { data } = await apiRequest<{ data: VideoData }>({\n method: \"POST\",\n path: `/v1/videos/uploads/${uploadId}/complete`,\n auth: apiKey,\n });\n return data;\n } catch (error) {\n if (uploadId) {\n await apiRequest({\n method: \"DELETE\",\n path: `/v1/videos/uploads/${uploadId}`,\n auth: apiKey,\n }).catch(() => undefined);\n }\n throw error;\n }\n}\n","import { defineCommand } from \"citty\";\nimport { readFileSync } from \"node:fs\";\nimport { basename, extname } from \"node:path\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { parseExpires } from \"../lib/duration.js\";\nimport { validateReuseOptions } from \"../lib/upload-options.js\";\nimport { parseMetadataFlags } from \"../lib/metadata.js\";\nimport { waitForDescription, describeSummary, type ImageDescription } from \"../lib/describe.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { uploadVideoFile } from \"../lib/video-upload.js\";\n\ninterface UploadResponse {\n data: {\n id: string;\n kind?: \"image\" | \"video\";\n url: string;\n optimized_url?: string;\n visibility?: \"public\" | \"private\";\n mime_type: string;\n size: number;\n filename: string;\n folder?: string | null;\n created_at: string;\n signed_expires?: number;\n /** The API reused an identical existing image (--dedupe). */\n deduplicated?: boolean;\n /** The API returned the original of a repeated --idempotency-key. */\n replayed?: boolean;\n metadata?: Record<string, unknown> | null;\n /** AI description (--describe): pending right after upload, `done` once ready. */\n description?: ImageDescription | null;\n /** --describe was requested but could not start (quota, type, busy, ...). */\n describe_skipped?: string;\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"upload\",\n description: \"Upload one or more images or MP4 videos\",\n },\n args: {\n files: {\n type: \"positional\",\n description: \"Image or MP4 file path(s) to upload\",\n required: true,\n },\n folder: {\n type: \"string\",\n description: \"Folder to organize images in\",\n },\n private: {\n type: \"boolean\",\n description:\n \"Upload privately — returns a signed URL only holders of the link can open (needs a secret key)\",\n default: false,\n },\n expires: {\n type: \"string\",\n description:\n \"Lifetime of a --private signed link: e.g. 30m, 12h, 7d (default 7d; min 60s, max 30d)\",\n },\n optimized: {\n type: \"boolean\",\n description:\n \"Print the optimized URL (WebP/AVIF, usually much smaller) instead of the original; falls back to the original URL when none is returned (SVG, GIF, private)\",\n default: false,\n },\n \"idempotency-key\": {\n type: \"string\",\n description:\n \"Make retries safe: repeating the same key with the same file returns the original image instead of storing a copy (single file only)\",\n },\n dedupe: {\n type: \"boolean\",\n description:\n \"Reuse an identical image already in your project instead of storing and counting another upload\",\n default: false,\n },\n meta: {\n type: \"string\",\n description:\n \"Attach custom metadata as key=value (repeatable), e.g. --meta env=prod --meta run=42\",\n },\n metadata: {\n type: \"string\",\n description: 'Attach custom metadata as a JSON object, e.g. --metadata \\'{\"env\":\"prod\",\"n\":3}\\'',\n },\n describe: {\n type: \"boolean\",\n description:\n \"Generate an AI description (alt text, caption, OCR) in the background; needs a secret key. Free 20 in total, Starter 1,000/mo, Pro 10,000/mo. Read it with `get <id> --json`, or add --wait\",\n default: false,\n },\n wait: {\n type: \"boolean\",\n description:\n \"With --describe: wait up to ~40s for the description and include it in --json output (alt text goes to stderr otherwise)\",\n default: false,\n },\n json: {\n type: \"boolean\",\n description: \"Output full JSON response\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n // Resolve the signed-link lifetime up front so a bad value fails before any\n // upload happens. `--expires` only makes sense for a private (signed) URL.\n let signExpires: number | null = null;\n if (args.expires !== undefined) {\n if (!args.private) {\n stderr(\"--expires only applies to --private uploads (it sets the signed link's lifetime).\");\n process.exit(1);\n }\n signExpires = parseExpires(String(args.expires));\n }\n\n if (args.private && args.folder) {\n stderr(\"Note: --folder is ignored for --private uploads.\");\n }\n\n // A citty positional binds only the first value; every positional (including\n // the first, and anything a shell glob expanded to) lands in `args._`.\n const files = args._.length ? args._.map(String) : [String(args.files)];\n\n const hasVideo = files.some((filePath) => extname(filePath).toLowerCase() === \".mp4\");\n const imageOnlyOption =\n args.folder ||\n args.private ||\n args.expires ||\n args.optimized ||\n args[\"idempotency-key\"] ||\n args.dedupe ||\n args.meta ||\n args.metadata ||\n args.describe ||\n args.wait;\n if (hasVideo && imageOnlyOption) {\n stderr(\n \"MP4 uploads support only --json. Folder, private-link, optimization, reuse, metadata, and AI-description options are image-only.\"\n );\n process.exit(1);\n }\n let hadError = false;\n\n const idempotencyKey = args[\"idempotency-key\"] as string | undefined;\n const reuseError = validateReuseOptions({\n idempotencyKey,\n dedupe: args.dedupe,\n fileCount: files.length,\n });\n if (reuseError) {\n stderr(reuseError);\n process.exit(1);\n }\n\n const metadata = parseMetadataFlags({ meta: args.meta, metadata: args.metadata });\n\n if (args.wait && !args.describe) {\n stderr(\"--wait only applies together with --describe.\");\n process.exit(1);\n }\n\n const results: UploadResponse[\"data\"][] = [];\n\n for (const filePath of files) {\n try {\n if (extname(filePath).toLowerCase() === \".mp4\") {\n const video = await uploadVideoFile(filePath, apiKey);\n if (args.json) {\n results.push(video);\n } else {\n stdout(video.url);\n }\n continue;\n }\n\n const buffer = readFileSync(filePath);\n const fileName = basename(filePath);\n\n const formData = new FormData();\n formData.append(\n \"file\",\n new File([buffer], fileName)\n );\n\n if (args.folder) {\n formData.append(\"folder\", args.folder);\n }\n\n if (args.private) {\n // The server routes private images to a signed-URL-only path and\n // returns the ready-to-share signed URL in `data.url`. A user-supplied\n // folder is ignored for private uploads (server-side).\n formData.append(\"visibility\", \"private\");\n if (signExpires !== null) {\n formData.append(\"sign_expires_in\", String(signExpires));\n }\n }\n\n if (args.dedupe) {\n formData.append(\"dedupe\", \"true\");\n }\n\n if (metadata) {\n formData.append(\"metadata\", JSON.stringify(metadata));\n }\n\n if (args.describe) {\n formData.append(\"describe\", \"true\");\n }\n\n const res = await apiRequest<UploadResponse>({\n method: \"POST\",\n path: \"/v1/images\",\n auth: apiKey,\n formData,\n ...(idempotencyKey ? { headers: { \"Idempotency-Key\": idempotencyKey } } : {}),\n });\n\n // stderr only: stdout stays the bare URL so scripts keep working.\n if (res.data.replayed) {\n stderr(`Replayed: ${filePath} was already uploaded with this key; returning the original.`);\n } else if (res.data.deduplicated) {\n stderr(`Deduplicated: ${filePath} matches an existing image; nothing new was stored.`);\n }\n\n // Fail closed: if we asked for private but the server didn't confirm it,\n // don't hand back a URL the user would wrongly trust as private.\n if (args.private && res.data.visibility !== \"private\") {\n hadError = true;\n stderr(`Refusing to report ${filePath}: server did not confirm a private upload.`);\n continue;\n }\n\n // The upload itself never fails because of --describe; say what happened on stderr.\n if (args.describe) {\n if (res.data.describe_skipped) {\n stderr(`Description skipped for ${filePath}: ${res.data.describe_skipped}.`);\n } else if (args.wait && res.data.description?.status === \"pending\") {\n res.data.description = await waitForDescription(res.data.id, apiKey);\n if (!args.json) stderr(describeSummary(res.data.description ?? null));\n } else if (res.data.description?.status === \"pending\") {\n stderr(`Describing ${filePath} in the background; read it with: pixelvault get ${res.data.id} --json`);\n } else if (res.data.description?.status === \"done\" && !args.json) {\n stderr(describeSummary(res.data.description)); // reused from an identical image\n }\n }\n\n if (args.json) {\n results.push(res.data);\n } else {\n stdout(args.optimized ? (res.data.optimized_url ?? res.data.url) : res.data.url);\n }\n } catch (err) {\n hadError = true;\n stderr(\n `Failed to upload ${filePath}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n if (args.json && results.length > 0) {\n jsonOut(results.length === 1 ? results[0] : results);\n }\n\n if (hadError) {\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { parseExpires } from \"../lib/duration.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\ninterface GenerateResponse {\n data: {\n id: string;\n url: string;\n optimized_url?: string;\n visibility?: \"public\" | \"private\";\n mime_type: string;\n size: number;\n width?: number;\n height?: number;\n filename: string;\n folder: string | null;\n created_at: string;\n expires_in?: number;\n signed_expires?: number;\n generation: {\n model: string;\n credits_charged: number;\n credits_remaining: number;\n };\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"generate\",\n description:\n \"Generate an image from a prompt and host it (spends monthly AI credits; secret key required)\",\n },\n args: {\n prompt: {\n type: \"positional\",\n description: \"What to draw. Quote it, or pass several words\",\n required: true,\n },\n model: {\n type: \"string\",\n alias: \"m\",\n description:\n \"flux-1-schnell (default, 1 credit), sdxl-lightning or sdxl-base (1, paid plans), lucid-origin or phoenix-1.0 (20, paid plans)\",\n },\n folder: {\n type: \"string\",\n description: \"Folder to organize the image in\",\n },\n private: {\n type: \"boolean\",\n description:\n \"Keep it private: returns a signed URL only holders of the link can open\",\n default: false,\n },\n expires: {\n type: \"string\",\n description:\n \"Lifetime of a --private signed link: e.g. 30m, 12h, 7d (default 7d; min 60s, max 30d)\",\n },\n \"delete-after\": {\n type: \"string\",\n description:\n \"Delete the image automatically after this long: e.g. 1h, 7d (min 60s, max 30d)\",\n },\n optimized: {\n type: \"boolean\",\n description:\n \"Print the optimized URL (WebP/AVIF, usually much smaller) instead of the original; falls back to the original URL when none is returned\",\n default: false,\n },\n json: {\n type: \"boolean\",\n description: \"Output the full JSON response\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n // Every positional (including the first) lands in `args._`, so an unquoted\n // multi-word prompt still works.\n const prompt = (args._.length ? args._.map(String) : [String(args.prompt ?? \"\")])\n .join(\" \")\n .trim();\n if (!prompt) {\n throw new CliError('Give a prompt, e.g. pixelvault generate \"a red bicycle\".');\n }\n\n // Resolve durations up front so a bad value fails before any credit is spent.\n if (args.expires !== undefined && !args.private) {\n throw new CliError(\n \"--expires only applies to --private images (it sets the signed link's lifetime). Use --delete-after to expire the image itself.\"\n );\n }\n const signExpires =\n args.expires !== undefined ? parseExpires(String(args.expires)) : undefined;\n const deleteAfter =\n args[\"delete-after\"] !== undefined\n ? parseExpires(String(args[\"delete-after\"]), \"--delete-after\")\n : undefined;\n\n if (args.private && args.folder) {\n stderr(\"Note: --folder is ignored for --private images.\");\n }\n\n const res = await apiRequest<GenerateResponse>({\n method: \"POST\",\n path: \"/v1/images/generate\",\n auth: apiKey,\n body: {\n prompt,\n ...(args.model ? { model: String(args.model) } : {}),\n ...(args.folder ? { folder: String(args.folder) } : {}),\n ...(args.private ? { visibility: \"private\" } : {}),\n ...(signExpires !== undefined ? { sign_expires_in: signExpires } : {}),\n ...(deleteAfter !== undefined ? { expires_in: deleteAfter } : {}),\n },\n });\n\n // Fail closed: if we asked for private but the server didn't confirm it,\n // don't hand back a URL the user would wrongly trust as private.\n if (args.private && res.data.visibility !== \"private\") {\n throw new CliError(\n \"Refusing to report the image: the server did not confirm it is private.\"\n );\n }\n\n const g = res.data.generation;\n // stderr only: stdout stays the bare URL so scripts and agents can capture it.\n stderr(\n `Generated with ${g.model}: ${g.credits_charged} credit${g.credits_charged === 1 ? \"\" : \"s\"} used, ${g.credits_remaining} left this month.`\n );\n\n if (args.json) {\n jsonOut(res.data);\n } else {\n stdout(args.optimized ? (res.data.optimized_url ?? res.data.url) : res.data.url);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, jsonOut } from \"../lib/output.js\";\nimport { parseMetadataFlags } from \"../lib/metadata.js\";\n\ninterface ListResponse {\n data: Array<{\n id: string;\n url: string;\n mime_type: string;\n size: number;\n filename: string;\n folder: string | null;\n created_at: string;\n metadata?: Record<string, unknown> | null;\n description?: { status: string; alt?: string; caption?: string; ocr_text?: string; tags?: string[] } | null;\n }>;\n meta: {\n total: number;\n page: number;\n per_page: number;\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"list\",\n description: \"List uploaded images\",\n },\n args: {\n page: {\n type: \"string\",\n description: \"Page number\",\n default: \"1\",\n },\n \"per-page\": {\n type: \"string\",\n description: \"Items per page (max 100)\",\n default: \"20\",\n },\n search: {\n type: \"string\",\n description:\n 'Search the AI descriptions (alt text, caption, OCR text) of images uploaded with --describe, best match first, e.g. --search \"invoice 4821\" (quotes, -word work)',\n alias: \"q\",\n },\n tag: {\n type: \"string\",\n description: \"Only images carrying this AI tag (one tag, e.g. --tag boat); see `pixelvault tags`\",\n },\n meta: {\n type: \"string\",\n description:\n \"Only images whose metadata has key=value (repeatable; string match), e.g. --meta env=prod\",\n },\n metadata: {\n type: \"string\",\n description:\n 'Only images whose metadata contains this JSON object (typed match), e.g. --metadata \\'{\"n\":3}\\'',\n },\n json: {\n type: \"boolean\",\n description: \"Output full JSON response\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n const params = new URLSearchParams({\n page: args.page,\n per_page: args[\"per-page\"],\n });\n const filter = parseMetadataFlags({ meta: args.meta, metadata: args.metadata });\n if (filter) params.set(\"metadata\", JSON.stringify(filter));\n const search = typeof args.search === \"string\" ? args.search.trim() : \"\";\n if (search) params.set(\"q\", search);\n const tag = typeof args.tag === \"string\" ? args.tag.trim() : \"\";\n if (tag) params.set(\"tag\", tag);\n\n const res = await apiRequest<ListResponse>({\n path: `/v1/images?${params}`,\n auth: apiKey,\n });\n\n if (args.json) {\n jsonOut(res);\n } else {\n for (const img of res.data) {\n stdout(img.url);\n }\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, jsonOut } from \"../lib/output.js\";\n\ninterface TagsResponse {\n data: Array<{ tag: string; count: number }>;\n}\n\nexport default defineCommand({\n meta: {\n name: \"tags\",\n description:\n \"List the AI tags on your described images with how many images carry each (filter with `list --tag`)\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output the full JSON response\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n const res = await apiRequest<TagsResponse>({ path: \"/v1/images/tags\", auth: apiKey });\n\n if (args.json) {\n jsonOut(res);\n } else {\n for (const t of res.data) {\n stdout(`${t.tag}\\t${t.count}`);\n }\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { waitForDescription, describeSummary, type ImageDescription } from \"../lib/describe.js\";\n\ninterface DescribeResponse {\n data: {\n id: string;\n description?: ImageDescription | null;\n /** Why no description was started: quota, email_unverified, busy, unsupported_type, too_large, unavailable. */\n describe_skipped?: string;\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"describe\",\n description:\n \"Write an AI description (alt text, caption, OCR text, tags) for an image you already uploaded\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Image ID (e.g. img_abc123)\",\n required: true,\n },\n wait: {\n type: \"boolean\",\n description: \"Wait up to ~40s for the description and print it (otherwise it runs in the background)\",\n default: false,\n },\n json: {\n type: \"boolean\",\n description: \"Output JSON ({ id, description }); with --wait it includes the full description\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n const id = String(args.id);\n\n // Idempotent: an image that is already described, or in progress, costs nothing.\n const res = await apiRequest<DescribeResponse>({\n method: \"POST\",\n path: `/v1/images/${encodeURIComponent(id)}/describe`,\n auth: apiKey,\n });\n\n if (res.data.describe_skipped) {\n throw new CliError(`Description not started: ${res.data.describe_skipped}.`);\n }\n\n let description = res.data.description ?? null;\n if (args.wait && description?.status === \"pending\") {\n description = await waitForDescription(id, apiKey);\n }\n\n if (description?.status === \"failed\") {\n throw new CliError(describeSummary(description));\n }\n\n if (args.json) {\n jsonOut({ id, description });\n return;\n }\n\n if (description?.status === \"done\") {\n stdout(description.alt ?? \"\");\n if (description.tags?.length) stderr(`Tags: ${description.tags.join(\", \")}`);\n } else {\n stderr(describeSummary(description));\n }\n },\n});\n","import { CliError } from \"./errors.js\";\n\nexport type OutFormat = \"keep\" | \"webp\" | \"avif\" | \"jpg\" | \"png\";\n\nexport interface OptimizeOptions {\n format: OutFormat;\n maxWidth?: number;\n maxHeight?: number;\n /** 1-100. Applies to JPEG/WebP/AVIF output; PNG output is always lossless. */\n quality: number;\n keepMetadata: boolean;\n}\n\nexport interface OptimizeResult {\n status: \"optimized\" | \"skipped\";\n /** Why the original was kept (only when status is \"skipped\"). */\n reason?: string;\n inputBytes: number;\n outputBytes: number;\n /** Extension (no dot) of the output; equals the input's when skipped. */\n ext: string;\n width?: number;\n height?: number;\n /** The optimized bytes (only when status is \"optimized\"). */\n buffer?: Buffer;\n}\n\n// Minimal structural type for the bits of sharp we use, so tests can inject a fake\n// and the CLI still builds when the optional dependency isn't installed.\nexport type SharpFactory = (input: Buffer, options?: Record<string, unknown>) => any;\n\n/** Decode limits: refuse decompression bombs instead of allocating gigabytes. */\nexport const MAX_INPUT_PIXELS = 100 * 1000 * 1000;\nexport const MAX_INPUT_BYTES = 100 * 1024 * 1024;\nconst SHARP_INPUT = { limitInputPixels: MAX_INPUT_PIXELS, failOn: \"error\" } as const;\n\n/** Below this saving, a same-format, same-size re-encode is left alone. */\nexport const MIN_SAVING_PERCENT = 5;\n\nexport const SUPPORTED_EXTENSIONS = [\"jpg\", \"jpeg\", \"png\", \"webp\", \"avif\"] as const;\n\n/** sharp's floor (it is an optional dependency, so npm only warns on older Node). */\nconst MIN_NODE_MAJOR = 20;\nconst MIN_NODE_MINOR = 9;\n\n/**\n * Load sharp lazily. It is an optionalDependency: when its prebuilt binary is\n * missing (unusual libc, `--omit=optional`, locked-down CI) we say how to fix it\n * instead of crashing, and every other command keeps working.\n */\nexport async function loadSharp(): Promise<SharpFactory> {\n const [major = 0, minor = 0] = process.versions.node.split(\".\").map(Number);\n if (major < MIN_NODE_MAJOR || (major === MIN_NODE_MAJOR && minor < MIN_NODE_MINOR)) {\n throw new CliError(\n `optimize needs Node ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR} or newer (you have ${process.versions.node}).`\n );\n }\n try {\n const mod: any = await import(\"sharp\");\n return (mod.default ?? mod) as SharpFactory;\n } catch (err) {\n const why = err instanceof Error ? err.message.split(\"\\n\")[0] : String(err);\n throw new CliError(\n \"optimize needs the optional 'sharp' package, which couldn't be loaded for this platform.\\n\" +\n ` Reason: ${why}\\n` +\n \" Fix: reinstall pixelvault-cli without --omit=optional (npm install -g pixelvault-cli).\"\n );\n }\n}\n\n/** Map our format names to sharp's, resolving \"keep\" from the detected input format. */\nfunction resolveOutput(format: OutFormat, detected: string): { sharpFormat: string; ext: string } | null {\n const target = format === \"keep\" ? detected : format === \"jpg\" ? \"jpeg\" : format;\n switch (target) {\n case \"jpeg\":\n return { sharpFormat: \"jpeg\", ext: \"jpg\" };\n case \"png\":\n return { sharpFormat: \"png\", ext: \"png\" };\n case \"webp\":\n return { sharpFormat: \"webp\", ext: \"webp\" };\n case \"avif\":\n return { sharpFormat: \"avif\", ext: \"avif\" };\n default:\n return null;\n }\n}\n\nexport async function optimizeBuffer(\n sharp: SharpFactory,\n input: Buffer,\n inputExt: string,\n opts: OptimizeOptions\n): Promise<OptimizeResult> {\n const base = { inputBytes: input.length, outputBytes: input.length, ext: inputExt };\n const skip = (reason: string): OptimizeResult => ({ status: \"skipped\", reason, ...base });\n\n let meta: any;\n try {\n meta = await sharp(input, SHARP_INPUT).metadata();\n } catch (err) {\n // A corrupt or oversized file is a failure the caller must see (CI should not pass on it).\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(/pixel limit/i.test(msg) ? `image is over ${MAX_INPUT_PIXELS / 1e6} megapixels` : \"not a readable image\");\n }\n\n // Animated images, vector graphics and HEIC are out of scope for v1.\n if (meta.format === \"svg\") return skip(\"SVG is not supported\");\n if (meta.format === \"gif\") return skip(\"GIF is not supported\");\n if ((meta.pages ?? 1) > 1) return skip(\"animated images are not supported\");\n if (meta.format === \"heif\" && meta.compression === \"hevc\") {\n return skip(\"HEIC is not supported (convert it at https://pixelvault.dev/tools/heic-to-url/)\");\n }\n\n const detected = meta.format === \"heif\" ? \"avif\" : meta.format;\n const out = resolveOutput(opts.format, detected);\n if (!out) return skip(`unsupported format: ${meta.format}`);\n\n // Apply EXIF orientation BEFORE metadata is dropped, otherwise phone photos come out sideways.\n let pipeline = sharp(input, SHARP_INPUT).rotate();\n\n if (opts.maxWidth || opts.maxHeight) {\n pipeline = pipeline.resize({\n width: opts.maxWidth,\n height: opts.maxHeight,\n fit: \"inside\",\n withoutEnlargement: true,\n });\n }\n\n // JPEG has no alpha: flatten onto white rather than letting transparency go black.\n if (out.sharpFormat === \"jpeg\" && meta.hasAlpha) {\n pipeline = pipeline.flatten({ background: \"#ffffff\" });\n }\n\n if (opts.keepMetadata) pipeline = pipeline.keepMetadata(); // sharp strips metadata by default\n\n switch (out.sharpFormat) {\n case \"jpeg\":\n pipeline = pipeline.jpeg({ quality: opts.quality, mozjpeg: true });\n break;\n case \"png\":\n pipeline = pipeline.png({ compressionLevel: 9, effort: 10 }); // lossless\n break;\n case \"webp\":\n pipeline = pipeline.webp({ quality: opts.quality, effort: 5 });\n break;\n case \"avif\":\n pipeline = pipeline.avif({ quality: opts.quality, effort: 4 });\n break;\n }\n\n const { data, info } = await pipeline.toBuffer({ resolveWithObject: true });\n\n // Never hand back something bigger than what we were given.\n if (data.length >= input.length) {\n return skip(\"already optimal (result was not smaller)\");\n }\n // Same format and same pixel size: a sliver of saving isn't worth rewriting the file. Keeps\n // re-runs (pre-commit hooks, CI) idempotent instead of nibbling an image every time. Compared on\n // the real detected format and oriented dimensions (not just the flags), with the unrounded ratio.\n const swap = (meta.orientation ?? 1) >= 5;\n const inW = swap ? meta.height : meta.width;\n const inH = swap ? meta.width : meta.height;\n const unchanged = out.sharpFormat === detected && info.width === inW && info.height === inH;\n if (unchanged && (input.length - data.length) / input.length < MIN_SAVING_PERCENT / 100) {\n return skip(`already optimal (would save under ${MIN_SAVING_PERCENT}%)`);\n }\n return {\n status: \"optimized\",\n inputBytes: input.length,\n outputBytes: data.length,\n ext: out.ext,\n width: info.width,\n height: info.height,\n buffer: data,\n };\n}\n\n/** Percentage saved, e.g. 84 for \"-84%\". */\nexport function savedPercent(before: number, after: number): number {\n if (before <= 0) return 0;\n return Math.round(((before - after) / before) * 100);\n}\n","import { defineCommand } from \"citty\";\nimport { createHash } from \"node:crypto\";\nimport { availableParallelism } from \"node:os\";\nimport {\n readFileSync,\n writeFileSync,\n renameSync,\n unlinkSync,\n mkdirSync,\n statSync,\n lstatSync,\n readdirSync,\n existsSync,\n} from \"node:fs\";\nimport { basename, dirname, extname, join, relative, resolve, sep } from \"node:path\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { stdout, stderr, jsonOut, formatBytes } from \"../lib/output.js\";\nimport {\n loadSharp,\n optimizeBuffer,\n savedPercent,\n SUPPORTED_EXTENSIONS,\n MAX_INPUT_BYTES,\n type OptimizeOptions,\n type OutFormat,\n} from \"../lib/optimize.js\";\n\nconst FORMATS: OutFormat[] = [\"keep\", \"webp\", \"avif\", \"jpg\", \"png\"];\nconst OWNED = /\\.optimized$/i; // stem of files this command writes beside the original\n\ninterface UploadResponse {\n data: { id: string; url: string; optimized_url?: string };\n}\n\nexport interface Item {\n file: string;\n /** Directory the file was found under (for --recursive + --out-dir layout), or null. */\n root: string | null;\n}\n\nexport interface Mode {\n outDir?: string;\n inPlace: boolean;\n /** Write `name.optimized.ext` beside the input (the default when not hosting). */\n writeBeside: boolean;\n}\n\nexport interface Plan {\n item: Item;\n /** Where the optimized bytes go, or null when this run writes nothing locally. */\n optimized: string | null;\n /** Where an untouched original is copied under --out-dir (so the output tree stays complete). */\n kept: string | null;\n}\n\ninterface Row {\n input: string;\n output: string | null;\n status: \"optimized\" | \"skipped\" | \"error\";\n reason?: string;\n input_bytes: number;\n output_bytes: number;\n saved_percent: number;\n url?: string;\n upload_error?: string;\n}\n\n/** Parse a positive-integer flag, or undefined when unset. */\nexport function parsePositiveInt(name: string, value: unknown): number | undefined {\n if (value === undefined || value === null || value === \"\") return undefined;\n const n = Number(value);\n if (!Number.isInteger(n) || n < 1) throw new CliError(`${name} must be a positive whole number.`);\n return n;\n}\n\nfunction isSupported(path: string): boolean {\n return (SUPPORTED_EXTENSIONS as readonly string[]).includes(extname(path).slice(1).toLowerCase());\n}\n\n/** Descend into `dir`, skipping symlinks (no cycles, no escaping the root), dotfiles, our own outputs and `skipDir`. */\nfunction walk(dir: string, out: string[], skipDir: string | undefined): void {\n for (const name of readdirSync(dir)) {\n if (name.startsWith(\".\") || name === \"node_modules\") continue;\n const p = join(dir, name);\n const st = lstatSync(p);\n if (st.isSymbolicLink()) continue;\n if (st.isDirectory()) {\n if (skipDir && resolve(p) === skipDir) continue;\n walk(p, out, skipDir);\n } else if (isSupported(p) && !OWNED.test(basename(p, extname(p)))) {\n out.push(p);\n }\n }\n}\n\n/** Expand the positional paths into files; directories need --recursive. */\nexport function collectItems(paths: string[], recursive: boolean, outDir?: string): Item[] {\n const skipDir = outDir ? resolve(outDir) : undefined;\n const items: Item[] = [];\n for (const p of paths) {\n let st;\n try {\n st = statSync(p);\n } catch {\n throw new CliError(`No such file or directory: ${p}`);\n }\n if (st.isDirectory()) {\n if (!recursive) throw new CliError(`${p} is a directory. Pass --recursive to optimize everything inside it.`);\n const found: string[] = [];\n walk(p, found, skipDir);\n for (const f of found.sort()) items.push({ file: f, root: p });\n } else {\n items.push({ file: p, root: null });\n }\n }\n return items;\n}\n\n/**\n * The extension an optimized file gets. `keep` preserves the input's own spelling\n * (`.jpeg` stays `.jpeg`, `.JPG` stays `.JPG`); otherwise it is the target format's.\n */\nexport function outputExt(item: Item, format: OutFormat): string {\n if (format === \"keep\") return extname(item.file).slice(1);\n return format === \"jpg\" ? \"jpg\" : format;\n}\n\nexport function planOutput(item: Item, format: OutFormat, mode: Mode): Plan {\n const stem = basename(item.file, extname(item.file));\n const ext = outputExt(item, format);\n if (mode.inPlace) return { item, optimized: item.file, kept: null }; // exact original path\n if (mode.outDir) {\n const rel = item.root ? dirname(relative(item.root, item.file)) : \".\";\n const dir = join(mode.outDir, rel);\n return { item, optimized: join(dir, `${stem}.${ext}`), kept: join(dir, basename(item.file)) };\n }\n if (mode.writeBeside) {\n return { item, optimized: join(dirname(item.file), `${stem}.optimized.${ext}`), kept: null };\n }\n return { item, optimized: null, kept: null };\n}\n\n/**\n * Refuse any plan that could lose data, before a single byte is written:\n * two inputs mapping to one output, an output that is another input (or an input\n * in disguise), a symlink as the destination, or --in-place on a symlink.\n */\nexport function assertSafePlans(plans: Plan[], mode: Mode): void {\n const inputs = new Set(plans.map((p) => resolve(p.item.file)));\n const claimed = new Map<string, string>(); // output path -> input that claimed it\n\n for (const plan of plans) {\n const src = resolve(plan.item.file);\n\n if (mode.inPlace && lstatSync(plan.item.file).isSymbolicLink()) {\n throw new CliError(`${plan.item.file} is a symlink; refusing to overwrite it in place. Optimize the real file instead.`);\n }\n\n const targets: Array<[string | null, boolean]> = [\n [plan.optimized, false],\n [plan.kept, true],\n ];\n for (const [target, isKept] of targets) {\n if (!target) continue;\n const out = resolve(target);\n const own = out === src;\n\n // Copying an untouched original onto itself is a harmless no-op; overwriting it with\n // optimized bytes is not, and is caught below.\n if (own && isKept && !mode.inPlace && plan.optimized !== target) continue;\n if (own && !mode.inPlace) {\n throw new CliError(`${plan.item.file}: the output would overwrite the original. Use --in-place if that is what you want.`);\n }\n if (!own && inputs.has(out)) {\n throw new CliError(`${target} is also an input file; refusing to overwrite it. Use different output settings.`);\n }\n const prior = claimed.get(out);\n if (prior && prior !== plan.item.file) {\n throw new CliError(`${plan.item.file} and ${prior} would both be written to ${target}. Optimize them separately or use --out-dir with --recursive.`);\n }\n claimed.set(out, plan.item.file);\n\n if (!own && existsSync(out) && lstatSync(out).isSymbolicLink()) {\n throw new CliError(`${target} is a symlink; refusing to write through it.`);\n }\n }\n }\n}\n\n/** Write via a temp file in the same directory, then rename, so a crash can't truncate the target. */\nexport function writeAtomic(target: string, data: Buffer, mode?: number): void {\n mkdirSync(dirname(target), { recursive: true });\n const tmp = join(dirname(target), `.${basename(target)}.${process.pid}.tmp`);\n try {\n writeFileSync(tmp, data, mode !== undefined ? { mode } : undefined);\n renameSync(tmp, target);\n } catch (err) {\n try {\n unlinkSync(tmp);\n } catch {\n /* nothing to clean up */\n }\n throw err;\n }\n}\n\nasync function pool<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let next = 0;\n const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {\n while (next < items.length) {\n const i = next++;\n results[i] = await fn(items[i] as T);\n }\n });\n await Promise.all(workers);\n return results;\n}\n\nexport default defineCommand({\n meta: {\n name: \"optimize\",\n description:\n \"Compress images locally (no account needed): resize, convert, strip metadata. Never writes a file bigger than the original\",\n },\n args: {\n files: {\n type: \"positional\",\n description: \"Image files (or directories with --recursive)\",\n required: true,\n },\n format: {\n type: \"string\",\n description: \"Output format: keep (default), webp, avif, jpg, png\",\n default: \"keep\",\n },\n \"max-width\": { type: \"string\", description: \"Downscale to at most this width in px (never upscales)\" },\n \"max-height\": { type: \"string\", description: \"Downscale to at most this height in px (never upscales)\" },\n quality: {\n type: \"string\",\n description: \"JPEG/WebP/AVIF quality 1-100 (default 80). PNG output is always lossless\",\n default: \"80\",\n },\n \"keep-metadata\": {\n type: \"boolean\",\n description: \"Keep EXIF/GPS and other metadata (stripped by default)\",\n default: false,\n },\n \"out-dir\": { type: \"string\", description: \"Write results into this directory (mirrors subfolders with --recursive)\" },\n \"in-place\": {\n type: \"boolean\",\n description: \"Overwrite each original (only with --format keep). Default writes name.optimized.ext beside it\",\n default: false,\n },\n recursive: { type: \"boolean\", description: \"Descend into directories\", alias: \"r\", default: false },\n \"dry-run\": { type: \"boolean\", description: \"Report what would be saved without writing anything\", default: false },\n host: {\n type: \"boolean\",\n description:\n \"Upload the optimized image and print its CDN URL (needs an API key). Local files are written only with --out-dir/--in-place\",\n default: false,\n },\n json: { type: \"boolean\", description: \"Output a JSON report (always an array)\", default: false },\n },\n async run({ args }) {\n const format = String(args.format) as OutFormat;\n if (!FORMATS.includes(format)) {\n throw new CliError(`--format must be one of: ${FORMATS.join(\", \")}.`);\n }\n const quality = parsePositiveInt(\"--quality\", args.quality) ?? 80;\n if (quality > 100) throw new CliError(\"--quality must be a whole number between 1 and 100.\");\n const maxWidth = parsePositiveInt(\"--max-width\", args[\"max-width\"]);\n const maxHeight = parsePositiveInt(\"--max-height\", args[\"max-height\"]);\n\n const outDir = args[\"out-dir\"] ? String(args[\"out-dir\"]) : undefined;\n if (args[\"in-place\"] && outDir) throw new CliError(\"Use either --in-place or --out-dir, not both.\");\n if (args[\"in-place\"] && format !== \"keep\") {\n throw new CliError(\"--in-place only works with --format keep (changing format would need a different file name).\");\n }\n\n const dryRun = Boolean(args[\"dry-run\"]);\n const host = Boolean(args.host);\n if (host && dryRun) throw new CliError(\"--host can't be combined with --dry-run (it would upload).\");\n const apiKey = host ? requireApiKey() : undefined;\n\n const paths = args._.length ? args._.map(String) : [String(args.files)];\n const items = collectItems(paths, Boolean(args.recursive), outDir);\n if (items.length === 0) throw new CliError(\"No supported images found (jpg, png, webp, avif).\");\n\n const mode: Mode = {\n outDir,\n inPlace: Boolean(args[\"in-place\"]),\n // With --host the point is the URL; only write beside the original when not hosting.\n writeBeside: !host,\n };\n const plans = items.map((item) => planOutput(item, format, mode));\n assertSafePlans(plans, mode);\n\n const sharp = await loadSharp();\n const opts: OptimizeOptions = { format, maxWidth, maxHeight, quality, keepMetadata: Boolean(args[\"keep-metadata\"]) };\n // Uploads share the API's rate limit, so go gentler when hosting.\n const concurrency = host ? 2 : Math.max(1, Math.min(4, availableParallelism()));\n\n const rows = await pool(plans, concurrency, async (plan): Promise<Row> => {\n const { item } = plan;\n const inputExt = extname(item.file).slice(1).toLowerCase();\n const row: Row = {\n input: item.file,\n output: null,\n status: \"error\",\n input_bytes: 0,\n output_bytes: 0,\n saved_percent: 0,\n };\n let input: Buffer;\n try {\n const size = statSync(item.file).size;\n if (size > MAX_INPUT_BYTES) throw new Error(`file is over ${MAX_INPUT_BYTES / 1024 / 1024} MB`);\n input = readFileSync(item.file);\n const res = await optimizeBuffer(sharp, input, inputExt, opts);\n const optimized = res.status === \"optimized\" && res.buffer ? res.buffer : null;\n row.status = res.status;\n row.reason = res.reason;\n row.input_bytes = res.inputBytes;\n row.output_bytes = res.outputBytes;\n row.saved_percent = savedPercent(res.inputBytes, res.outputBytes);\n\n if (!dryRun) {\n if (optimized && plan.optimized) {\n const keepMode = mode.inPlace ? statSync(item.file).mode & 0o777 : undefined;\n writeAtomic(plan.optimized, optimized, keepMode);\n row.output = plan.optimized;\n } else if (!optimized && plan.kept && resolve(plan.kept) !== resolve(item.file)) {\n // Keep the output tree complete: copy the untouched original across.\n writeAtomic(plan.kept, input);\n row.output = plan.kept;\n }\n }\n\n if (host && apiKey) {\n // Upload failures are reported separately so the local result above is never lost.\n try {\n // Skipped (\"already optimal\") files are uploaded as-is so every input gets a URL.\n const bytes = optimized ?? input;\n const name = optimized ? `${basename(item.file, extname(item.file))}.${res.ext}` : basename(item.file);\n const formData = new FormData();\n formData.append(\"file\", new File([new Uint8Array(bytes)], name));\n const up = await apiRequest<UploadResponse>({\n method: \"POST\",\n path: \"/v1/images\",\n auth: apiKey,\n formData,\n // Same bytes → same image, so a retried batch doesn't create duplicates.\n headers: { \"Idempotency-Key\": createHash(\"sha256\").update(bytes).digest(\"hex\") },\n });\n row.url = up.data.optimized_url ?? up.data.url;\n } catch (err) {\n row.upload_error = err instanceof Error ? err.message : String(err);\n }\n }\n } catch (err) {\n row.status = \"error\";\n row.reason = err instanceof Error ? err.message : String(err);\n }\n return row;\n });\n\n let totalBefore = 0;\n let totalAfter = 0;\n let failures = 0;\n for (const r of rows) {\n if (r.status === \"error\") {\n failures++;\n stderr(`Failed ${r.input}: ${r.reason}`);\n continue;\n }\n if (r.upload_error) {\n failures++;\n stderr(`Upload failed for ${r.input}: ${r.upload_error}`);\n }\n totalBefore += r.input_bytes;\n totalAfter += r.output_bytes;\n if (r.status === \"skipped\") {\n stderr(`Skipped ${r.input}: ${r.reason}`);\n } else {\n const where = r.output ? ` → ${r.output}` : r.url ? ` → ${r.url}` : \"\";\n stderr(\n `${dryRun ? \"Would save\" : \"Optimized\"} ${r.input}${where} ${formatBytes(r.input_bytes)} → ${formatBytes(r.output_bytes)} (-${r.saved_percent}%)`\n );\n }\n }\n if (rows.length > 1) {\n stderr(`Total: ${formatBytes(totalBefore)} → ${formatBytes(totalAfter)} (-${savedPercent(totalBefore, totalAfter)}%)`);\n if (failures) stderr(`${failures} of ${rows.length} failed.`);\n }\n\n if (args.json) {\n jsonOut(rows); // always an array, so scripts need only one code path\n } else {\n // stdout stays machine-readable: the hosted URL, else the written (or, if skipped, original) path.\n for (const r of rows) {\n if (r.status === \"error\") continue;\n const line = r.url ?? r.output ?? (r.status === \"skipped\" ? r.input : null);\n if (line) stdout(line);\n }\n }\n\n // exitCode (not process.exit) so large piped stdout isn't truncated.\n if (failures) process.exitCode = 1;\n },\n});\n","import { defineCommand } from \"citty\";\nimport { writeFileSync } from \"node:fs\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { CliError } from \"../lib/errors.js\";\n\ninterface GetResponse {\n data: {\n id: string;\n url: string;\n mime_type: string;\n size: number;\n filename: string;\n folder: string | null;\n created_at: string;\n metadata?: Record<string, unknown> | null;\n description?: Record<string, unknown> | null;\n };\n}\n\n/**\n * Append a transform query string to a CDN URL. Accepts params with or without a\n * leading `?`/`&` (e.g. `w=400&fmt=webp`, `?segment=foreground`). See the transform\n * docs: https://pixelvault.dev/docs#transforms\n *\n * The params are reserialized through URLSearchParams so reserved characters in\n * values are percent-encoded — without this a hex color like `background=#ffaa00`\n * would be parsed as a URL fragment and lost. Idempotent for already-encoded input.\n */\nexport function applyTransform(url: string, transform?: string): string {\n if (!transform) return url;\n const raw = transform.trim().replace(/^[?&]+/, \"\");\n if (!raw) return url;\n const params = new URLSearchParams(raw).toString();\n if (!params) return url;\n return url.includes(\"?\") ? `${url}&${params}` : `${url}?${params}`;\n}\n\nexport default defineCommand({\n meta: {\n name: \"get\",\n description:\n \"Get an image's CDN URL or metadata, or download it — optionally transformed\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Image ID (e.g. img_abc123)\",\n required: true,\n },\n output: {\n type: \"string\",\n description: \"Download the image to this file path\",\n alias: \"o\",\n },\n transform: {\n type: \"string\",\n description:\n 'Transform params to apply, e.g. \"w=400&fmt=webp\", \"segment=foreground\", \"tile=img_logo.png\"',\n alias: \"t\",\n },\n json: {\n type: \"boolean\",\n description: \"Output full JSON metadata\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n const res = await apiRequest<GetResponse>({\n path: `/v1/images/${encodeURIComponent(args.id)}`,\n auth: apiKey,\n });\n\n const url = applyTransform(res.data.url, args.transform as string | undefined);\n\n // Download mode: fetch the (possibly transformed) bytes to disk.\n if (args.output) {\n const output = args.output as string;\n let bytes: ArrayBuffer;\n try {\n const dl = await fetch(url);\n if (!dl.ok) {\n throw new CliError(`Failed to download image: HTTP ${dl.status}`, 1);\n }\n bytes = await dl.arrayBuffer();\n } catch (err) {\n if (err instanceof CliError) throw err;\n throw new CliError(\n `Failed to download image: ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n // Refuse to write an empty file — a 200 with an empty/truncated body would\n // otherwise report success and leave a broken image that agents chain on.\n if (bytes.byteLength === 0) {\n throw new CliError(\"Downloaded 0 bytes — refusing to write an empty file\", 1);\n }\n try {\n writeFileSync(output, Buffer.from(bytes));\n } catch (err) {\n throw new CliError(\n `Failed to save ${output}: ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n stderr(`Saved ${output}`);\n // Honor --json even in download mode so a caller that always requests JSON\n // gets a stable stdout contract regardless of -o.\n if (args.json) {\n jsonOut(\n args.transform ? { ...res, transformed_url: url, output } : { ...res, output },\n );\n } else {\n stdout(url); // URL to stdout (machine-parseable), confirmation to stderr\n }\n return;\n }\n\n if (args.json) {\n // When a transform is applied there's no separate record for it, so surface\n // the resolved URL alongside the raw metadata.\n jsonOut(args.transform ? { ...res, transformed_url: url } : res);\n } else {\n stdout(url);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { parseMetadataFlags } from \"../lib/metadata.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { jsonOut } from \"../lib/output.js\";\n\ninterface PatchResponse {\n data: { id: string; metadata: Record<string, unknown> | null };\n}\n\nexport default defineCommand({\n meta: {\n name: \"meta\",\n description:\n \"Replace an image's custom metadata (the new object replaces the old one; use --clear to remove it)\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Image ID (e.g. img_abc123)\",\n required: true,\n },\n meta: {\n type: \"string\",\n description: \"Metadata as key=value (repeatable), e.g. --meta env=prod --meta run=42\",\n },\n metadata: {\n type: \"string\",\n description: 'Metadata as a JSON object, e.g. --metadata \\'{\"env\":\"prod\",\"n\":3}\\'',\n },\n clear: {\n type: \"boolean\",\n description: \"Remove all metadata from the image\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n const metadata = parseMetadataFlags({ meta: args.meta, metadata: args.metadata });\n\n if (args.clear && metadata) {\n throw new CliError(\"Use either --clear or --meta/--metadata, not both.\");\n }\n if (!args.clear && !metadata) {\n throw new CliError(\"Nothing to set: pass --meta key=value, --metadata '<json>', or --clear.\");\n }\n\n const res = await apiRequest<PatchResponse>({\n method: \"PATCH\",\n path: `/v1/images/${encodeURIComponent(String(args.id))}`,\n auth: apiKey,\n body: { metadata: args.clear ? null : metadata },\n });\n\n jsonOut(res.data.metadata);\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stderr } from \"../lib/output.js\";\n\nexport default defineCommand({\n meta: {\n name: \"delete\",\n description: \"Delete an image by ID\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Image ID (e.g. img_abc123)\",\n required: true,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n await apiRequest<{ success: boolean }>({\n method: \"DELETE\",\n path: `/v1/images/${args.id}`,\n auth: apiKey,\n });\n\n // Silent on success (agent-friendly)\n },\n});\n","import { defineCommand } from \"citty\";\nimport { writeFileSync, mkdirSync } from \"node:fs\";\nimport { execFileSync } from \"node:child_process\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { getApiUrl, requireApiKey } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { CliError } from \"../lib/errors.js\";\n\ninterface StartResponse {\n job_id: string;\n status: string;\n image_count: number;\n status_url: string;\n}\n\ninterface StatusResponse {\n id: string;\n status: \"pending\" | \"processing\" | \"complete\" | \"failed\" | \"expired\";\n image_count: number;\n processed_count: number;\n bytes: number;\n created_at: string;\n expires_at: string;\n download_url?: string;\n error?: string;\n}\n\n/** Default archive filename when `--output` isn't given. Pure, for tests. */\nexport function defaultOutputPath(jobId: string, out?: string): string {\n return out && out.length > 0 ? out : `pixelvault-export-${jobId}.tar`;\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport default defineCommand({\n meta: {\n name: \"export\",\n description:\n \"Export every image in your project as a tar archive (manifest.json + all images). Your data is always portable — no lock-in.\",\n },\n args: {\n output: {\n type: \"string\",\n description: \"Write the archive to this path (default: pixelvault-export-<job>.tar)\",\n alias: \"o\",\n },\n extract: {\n type: \"boolean\",\n description: \"Extract the archive into a directory after download (requires `tar`)\",\n default: false,\n },\n timeout: {\n type: \"string\",\n description: \"Max seconds to wait for the export to finish (default: 600)\",\n default: \"600\",\n },\n json: {\n type: \"boolean\",\n description: \"Output the final job JSON to stdout\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n const timeoutMs = (Number(args.timeout) || 600) * 1000;\n\n // 1. Start the async export job (project-scoped by the secret key).\n let start: StartResponse;\n try {\n start = await apiRequest<StartResponse>({ method: \"POST\", path: \"/v1/export\", auth: apiKey });\n } catch (err) {\n // Surface the \"already running\" case helpfully.\n if (err instanceof CliError && /in progress/i.test(err.message)) {\n throw new CliError(\n \"An export for this project is already in progress. Wait for it to finish, then try again.\",\n 1,\n );\n }\n throw err;\n }\n stderr(`Export started — ${start.image_count} image(s), job ${start.job_id}`);\n\n // 2. Poll until the archive is ready (backoff 1s → 5s).\n const deadline = Date.now() + timeoutMs;\n let status: StatusResponse;\n let delay = 1000;\n for (;;) {\n status = await apiRequest<StatusResponse>({\n path: `/v1/export-jobs/${encodeURIComponent(start.job_id)}`,\n auth: apiKey,\n });\n if (status.status === \"complete\") break;\n if (status.status === \"failed\") {\n throw new CliError(`Export failed: ${status.error ?? \"unknown error\"}`, 1);\n }\n if (status.status === \"expired\") {\n throw new CliError(\"Export expired before it could be downloaded. Start a new one.\", 1);\n }\n if (Date.now() > deadline) {\n throw new CliError(\n `Timed out after ${args.timeout}s (job still ${status.status}). It may still finish — re-run to poll, or raise --timeout.`,\n 1,\n );\n }\n stderr(` ${status.status}… ${status.processed_count}/${status.image_count}`);\n await sleep(delay);\n delay = Math.min(Math.round(delay * 1.5), 5000);\n }\n\n // 3. Download the tar. apiRequest parses JSON, so fetch the raw bytes directly\n // (the endpoint requires the same Bearer auth).\n const out = defaultOutputPath(start.job_id, args.output as string | undefined);\n const downloadUrl = `${getApiUrl()}/v1/export-jobs/${encodeURIComponent(start.job_id)}/download`;\n let bytes: ArrayBuffer;\n try {\n const dl = await fetch(downloadUrl, { headers: { Authorization: `Bearer ${apiKey}` } });\n if (!dl.ok) throw new CliError(`Failed to download export: HTTP ${dl.status}`, 1);\n bytes = await dl.arrayBuffer();\n } catch (err) {\n if (err instanceof CliError) throw err;\n throw new CliError(\n `Failed to download export: ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n // A 200 with an empty/truncated body must not report success for a backup.\n if (bytes.byteLength === 0) {\n throw new CliError(\"Downloaded 0 bytes — refusing to write an empty archive.\", 1);\n }\n try {\n writeFileSync(out, Buffer.from(bytes));\n } catch (err) {\n throw new CliError(\n `Failed to save ${out}: ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n stderr(`Saved ${out} — ${status.image_count} image(s), ${bytes.byteLength} bytes`);\n\n // 4. Optional extraction via the system `tar`.\n if (args.extract) {\n const dir = out.replace(/\\.tar$/, \"\") || `${out}-extracted`;\n try {\n mkdirSync(dir, { recursive: true });\n execFileSync(\"tar\", [\"-xf\", out, \"-C\", dir], { stdio: \"ignore\" });\n stderr(`Extracted into ${dir}/`);\n } catch (err) {\n throw new CliError(\n `Saved ${out}, but extraction failed (is \\`tar\\` installed?): ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n }\n\n if (args.json) {\n jsonOut({ ...status, output: out });\n } else {\n stdout(out); // machine-parseable path on stdout; progress went to stderr\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, jsonOut, formatBytes } from \"../lib/output.js\";\n\nexport interface UsageData {\n plan: string;\n period: string;\n resets_at: string;\n usage: { storage_bytes: number; uploads: number };\n limits: {\n storage_bytes: number | null;\n uploads_per_month: number | null;\n bandwidth_bytes_per_month: number | null;\n };\n remaining: { storage_bytes: number | null; uploads: number | null };\n /** AI image-generation credits (`pixelvault generate`) for the month. */\n generation?: {\n credits_used: number;\n credits_limit: number;\n credits_remaining: number;\n };\n}\n\nconst bytes = (n: number | null | undefined) =>\n n == null ? \"unlimited\" : formatBytes(n);\nconst count = (n: number | null | undefined) =>\n n == null ? \"unlimited\" : String(n);\n\nexport function renderUsage(d: UsageData): string[] {\n return [\n `Plan: ${d.plan}`,\n `Period: ${d.period} (resets ${d.resets_at})`,\n `Storage: ${formatBytes(d.usage.storage_bytes)} used / ${bytes(d.limits.storage_bytes)} limit / ${bytes(d.remaining.storage_bytes)} remaining`,\n `Uploads: ${d.usage.uploads} used / ${count(d.limits.uploads_per_month)} limit / ${count(d.remaining.uploads)} remaining`,\n `Bandwidth: ${bytes(d.limits.bandwidth_bytes_per_month)} limit per month (usage not reported)`,\n ...(d.generation\n ? [\n `AI images: ${d.generation.credits_used} credits used / ${d.generation.credits_limit} limit / ${d.generation.credits_remaining} remaining`,\n ]\n : []),\n ];\n}\n\nexport default defineCommand({\n meta: {\n name: \"usage\",\n description: \"Show plan, quota usage and limits (secret key required)\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output the usage object as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n const res = await apiRequest<{ data: UsageData }>({\n path: \"/v1/usage\",\n auth: apiKey,\n });\n\n if (args.json) {\n jsonOut(res.data);\n } else {\n for (const line of renderUsage(res.data)) stdout(line);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest, ApiRequestError } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\nexport interface KeyInfo {\n id: string;\n type: \"secret\" | \"publishable\";\n environment: string;\n label: string | null;\n key_prefix: string;\n allowed_origins: string[] | null;\n created_at: string;\n last_used_at: string | null;\n current: boolean;\n}\n\n/** Turn known key-management API errors into actionable messages. */\nexport function explainKeyError(err: unknown): never {\n if (err instanceof ApiRequestError) {\n switch (err.code) {\n case \"key_limit_reached\":\n throw new CliError(\n \"Key limit reached (25 per project). Revoke an unused key with `pixelvault keys revoke <id>` first.\",\n 1\n );\n case \"rate_limit_exceeded\": {\n const wait = err.retryAfter\n ? ` Retry after ${err.retryAfter} seconds.`\n : \"\";\n throw new CliError(\n `Rate limit exceeded (20 key changes per hour per project).${wait}`,\n 1\n );\n }\n case \"cannot_revoke_current_key\":\n throw new CliError(\n \"Cannot revoke the key you are authenticating with. Create a new key, switch to it (PIXELVAULT_API_KEY), then revoke the old one with the new key.\",\n 1\n );\n case \"not_found\":\n throw new CliError(\n \"Key not found. Only secret keys can be revoked; check the id with `pixelvault keys list`.\",\n 1\n );\n case \"validation_error\":\n throw new CliError(`Invalid request: ${err.message}`, 1);\n case \"forbidden\":\n throw new CliError(\n \"This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.\",\n 1\n );\n }\n if (err.status === 403) {\n throw new CliError(\n \"This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.\",\n 1\n );\n }\n }\n throw err;\n}\n\nconst list = defineCommand({\n meta: { name: \"list\", description: \"List the project's API keys\" },\n args: {\n json: { type: \"boolean\", description: \"Output as JSON\", default: false },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n let res: { data: KeyInfo[] };\n try {\n res = await apiRequest<{ data: KeyInfo[] }>({\n path: \"/v1/keys\",\n auth: apiKey,\n });\n } catch (err) {\n explainKeyError(err);\n }\n\n if (args.json) {\n jsonOut(res.data);\n return;\n }\n for (const k of res.data) {\n stdout(\n [\n k.id,\n k.type,\n k.environment,\n `${k.key_prefix}...`,\n k.label ?? \"-\",\n `created ${k.created_at}`,\n `last used ${k.last_used_at ?? \"never\"}`,\n k.current ? \"(current)\" : \"\",\n ]\n .filter(Boolean)\n .join(\" \")\n );\n }\n },\n});\n\nconst create = defineCommand({\n meta: {\n name: \"create\",\n description: \"Create a new secret API key (the raw key is printed once)\",\n },\n args: {\n label: { type: \"string\", description: \"Label for the key (max 60 chars)\" },\n env: {\n type: \"string\",\n description: \"Environment: live or test (default: live)\",\n },\n json: {\n type: \"boolean\",\n description: \"Output the full data object as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n if (args.env && args.env !== \"live\" && args.env !== \"test\") {\n throw new CliError(\"--env must be 'live' or 'test'.\", 1);\n }\n if (args.label && args.label.length > 60) {\n throw new CliError(\"--label must be 60 characters or fewer.\", 1);\n }\n\n const body: { label?: string; environment?: string } = {};\n if (args.label) body.label = args.label;\n if (args.env) body.environment = args.env;\n\n let res: { data: { id: string; api_key: string } };\n try {\n res = await apiRequest<{ data: { id: string; api_key: string } }>({\n method: \"POST\",\n path: \"/v1/keys\",\n auth: apiKey,\n body,\n });\n } catch (err) {\n explainKeyError(err);\n }\n\n stderr(\n \"This key is shown ONCE. Store it now (secret manager / env var); never commit it or paste it into logs.\"\n );\n if (args.json) {\n jsonOut(res.data);\n } else {\n stdout(res.data.api_key);\n }\n },\n});\n\nconst revoke = defineCommand({\n meta: {\n name: \"revoke\",\n description: \"Revoke a secret API key by id (not the one in use)\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Key id (see `pixelvault keys list`)\",\n required: true,\n },\n json: { type: \"boolean\", description: \"Output as JSON\", default: false },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n let res: { data: { deleted: boolean } };\n try {\n res = await apiRequest<{ data: { deleted: boolean } }>({\n method: \"DELETE\",\n path: `/v1/keys/${encodeURIComponent(args.id)}`,\n auth: apiKey,\n });\n } catch (err) {\n explainKeyError(err);\n }\n\n if (args.json) {\n jsonOut(res.data);\n } else {\n stderr(`Revoked key ${args.id}.`);\n }\n },\n});\n\nexport default defineCommand({\n meta: {\n name: \"keys\",\n description: \"Manage API keys (list, create, revoke)\",\n },\n subCommands: { list, create, revoke },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest, ApiRequestError } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\n/** Turn known billing API errors into actionable messages. */\nexport function explainBillingError(err: unknown): never {\n if (err instanceof ApiRequestError) {\n switch (err.code) {\n case \"test_key_forbidden\":\n throw new CliError(\n \"Billing needs a live key (pv_live_...); test keys can't start checkout or open the portal.\",\n 1\n );\n case \"publishable_key_forbidden\":\n throw new CliError(\n \"Billing needs a secret live key (pv_live_...); publishable keys are not allowed.\",\n 1\n );\n case \"email_not_verified\":\n throw new CliError(\n \"Verify your email before upgrading (check your inbox for the code).\",\n 1\n );\n case \"already_subscribed\":\n throw new CliError(\n \"You already have a subscription. Change or cancel it with `pixelvault billing`.\",\n 1\n );\n case \"no_subscription\":\n throw new CliError(\n \"This account has no billing history yet. Start one with `pixelvault upgrade <starter|pro>`.\",\n 1\n );\n case \"billing_unavailable\":\n throw new CliError(\"Paid plans are not available right now. Try again later.\", 1);\n case \"rate_limit_exceeded\": {\n const wait = err.retryAfter ? ` Retry after ${err.retryAfter} seconds.` : \"\";\n throw new CliError(\n `Rate limit exceeded (10 billing requests per hour per account).${wait}`,\n 1\n );\n }\n }\n }\n throw err;\n}\n\nexport default defineCommand({\n meta: {\n name: \"billing\",\n description:\n \"Print a Stripe billing-portal URL to change plan, update payment details or cancel (live key required)\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output { url } as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n let res: { url: string };\n try {\n res = await apiRequest<{ url: string }>({\n method: \"POST\",\n path: \"/v1/billing/portal\",\n body: {},\n auth: apiKey,\n });\n } catch (err) {\n explainBillingError(err);\n }\n\n if (args.json) {\n jsonOut({ url: res.url });\n } else {\n stdout(res.url);\n stderr(\n \"Open this link yourself to change plan, update payment details or cancel. It can cancel the subscription, so don't paste it anywhere shared.\"\n );\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { explainBillingError } from \"./billing.js\";\n\nconst PLANS = [\"starter\", \"pro\"] as const;\n\nexport default defineCommand({\n meta: {\n name: \"upgrade\",\n description:\n \"Print a Stripe Checkout URL to subscribe to a paid plan (live key required; nothing is charged until you pay)\",\n },\n args: {\n plan: {\n type: \"positional\",\n description: `Plan to subscribe to: ${PLANS.join(\" | \")}`,\n required: true,\n },\n json: {\n type: \"boolean\",\n description: \"Output { url } as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const plan = String(args.plan);\n if (!(PLANS as readonly string[]).includes(plan)) {\n throw new CliError(`Unknown plan \"${plan}\". Choose one of: ${PLANS.join(\", \")}.`, 1);\n }\n const apiKey = requireApiKey();\n let res: { url: string };\n try {\n res = await apiRequest<{ url: string }>({\n method: \"POST\",\n path: \"/v1/billing/checkout\",\n body: { plan },\n auth: apiKey,\n });\n } catch (err) {\n explainBillingError(err);\n }\n\n if (args.json) {\n jsonOut({ url: res.url });\n } else {\n stdout(res.url);\n stderr(\n \"Open this link to complete payment. The plan changes once Stripe confirms — check with `pixelvault usage`.\"\n );\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { readConfig, getApiKey } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\nexport default defineCommand({\n meta: {\n name: \"whoami\",\n description: \"Show current authentication state\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const config = readConfig();\n const apiKey = getApiKey();\n\n if (!apiKey && !config.email) {\n stderr(\"Not authenticated. Run `pixelvault register` or `pixelvault login`.\");\n process.exit(1);\n }\n\n const info = {\n email: config.email || \"(unknown)\",\n project: config.default_project || \"(unknown)\",\n api_key: apiKey\n ? `${apiKey.slice(0, 12)}...${apiKey.slice(-4)}`\n : \"(not set)\",\n source: process.env[\"PIXELVAULT_API_KEY\"] ? \"env\" : \"config\",\n // email/project come from config written at login; nothing checks them\n // against the current key.\n identity_source: \"cached\",\n };\n\n if (args.json) {\n jsonOut(info);\n } else {\n // email/project are read from config, not looked up for the current key.\n const tag = (v?: string) => (v ? \" (cached, not verified for this key)\" : \"\");\n stdout(`Email: ${info.email}${tag(config.email)}`);\n stdout(`Project: ${info.project}${tag(config.default_project)}`);\n stdout(`API Key: ${info.api_key} (${info.source})`);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { readConfig, updateConfig, getConfigPath } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\nconst configGet = defineCommand({\n meta: {\n name: \"get\",\n description: \"Get a config value\",\n },\n args: {\n key: {\n type: \"positional\",\n description: \"Config key (api_key, default_project, email, api_url)\",\n required: true,\n },\n },\n async run({ args }) {\n const config = readConfig();\n const value = config[args.key as keyof typeof config];\n\n if (value === undefined) {\n stderr(`Key \"${args.key}\" is not set.`);\n process.exit(1);\n }\n\n stdout(String(value));\n },\n});\n\nconst configSet = defineCommand({\n meta: {\n name: \"set\",\n description: \"Set a config value\",\n },\n args: {\n key: {\n type: \"positional\",\n description: \"Config key\",\n required: true,\n },\n value: {\n type: \"positional\",\n description: \"Config value\",\n required: true,\n },\n },\n async run({ args }) {\n const validKeys = [\"api_key\", \"default_project\", \"email\", \"api_url\"];\n if (!validKeys.includes(args.key)) {\n stderr(`Invalid key. Valid keys: ${validKeys.join(\", \")}`);\n process.exit(1);\n }\n\n if (args.key === \"api_key\" && args.value !== readConfig().api_key) {\n // email and default_project were cached at login for the previous key and\n // may belong to a different account, so drop them rather than mislead.\n updateConfig({ api_key: args.value, email: undefined, default_project: undefined });\n } else {\n updateConfig({ [args.key]: args.value });\n }\n stderr(`Set ${args.key} = ${args.key === \"api_key\" ? \"***\" : args.value}`);\n },\n});\n\nconst configShow = defineCommand({\n meta: {\n name: \"show\",\n description: \"Show all config values\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const config = readConfig();\n\n if (args.json) {\n jsonOut(config);\n } else {\n stderr(`Config: ${getConfigPath()}`);\n for (const [key, value] of Object.entries(config)) {\n const display = key === \"api_key\" && value\n ? `${(value as string).slice(0, 12)}...`\n : value;\n stdout(`${key} = ${display}`);\n }\n }\n },\n});\n\nexport default defineCommand({\n meta: {\n name: \"config\",\n description: \"Manage CLI configuration\",\n },\n subCommands: {\n get: configGet,\n set: configSet,\n show: configShow,\n },\n});\n","import { defineCommand } from \"citty\";\nimport { runCli } from \"./lib/run.js\";\nimport { VERSION } from \"./lib/client.js\";\n\nconst main = defineCommand({\n meta: {\n name: \"pixelvault\",\n version: VERSION,\n description: \"CLI for PixelVault — agent-first image and video hosting\",\n },\n subCommands: {\n register: () => import(\"./commands/register.js\").then((m) => m.default),\n login: () => import(\"./commands/login.js\").then((m) => m.default),\n upload: () => import(\"./commands/upload.js\").then((m) => m.default),\n generate: () => import(\"./commands/generate.js\").then((m) => m.default),\n list: () => import(\"./commands/list.js\").then((m) => m.default),\n tags: () => import(\"./commands/tags.js\").then((m) => m.default),\n describe: () => import(\"./commands/describe.js\").then((m) => m.default),\n optimize: () => import(\"./commands/optimize.js\").then((m) => m.default),\n get: () => import(\"./commands/get.js\").then((m) => m.default),\n meta: () => import(\"./commands/meta.js\").then((m) => m.default),\n delete: () => import(\"./commands/delete.js\").then((m) => m.default),\n export: () => import(\"./commands/export.js\").then((m) => m.default),\n usage: () => import(\"./commands/usage.js\").then((m) => m.default),\n keys: () => import(\"./commands/keys.js\").then((m) => m.default),\n upgrade: () => import(\"./commands/upgrade.js\").then((m) => m.default),\n billing: () => import(\"./commands/billing.js\").then((m) => m.default),\n whoami: () => import(\"./commands/whoami.js\").then((m) => m.default),\n config: () => import(\"./commands/config.js\").then((m) => m.default),\n },\n});\n\nvoid runCli(main);\n","import { runCommand, runMain, type CommandDef } from \"citty\";\nimport { CliError } from \"./errors.js\";\nimport { stderr } from \"./output.js\";\n\n/**\n * Entry point wrapper around citty.\n *\n * citty's `runMain` swallows every error itself: for anything that isn't its own\n * usage error it prints the error WITH a stack trace, prints the message a second\n * time, and always exits 1 — so a `CliError` printed twice with a stack and its\n * `exitCode` was ignored (and the `.catch` after `runMain` never ran). We hand\n * `--help` / `--version` to `runMain` (it renders those well) and run everything\n * else through `runCommand` so we own error output.\n */\nexport async function runCli(\n main: CommandDef,\n rawArgs: string[] = process.argv.slice(2),\n exit: (code: number) => void = (code) => process.exit(code)\n): Promise<void> {\n const wantsMeta =\n rawArgs.includes(\"--help\") ||\n rawArgs.includes(\"-h\") ||\n (rawArgs.length === 1 && rawArgs[0] === \"--version\");\n if (wantsMeta) {\n await runMain(main, { rawArgs });\n return;\n }\n\n try {\n await runCommand(main, { rawArgs });\n } catch (err) {\n if (err instanceof CliError) {\n stderr(`Error: ${err.message}`);\n exit(err.exitCode);\n return;\n }\n // citty's own usage errors (missing argument, unknown command) surface before\n // any command body runs, so replaying through runMain is side-effect free and\n // gets citty's usage rendering.\n if (err instanceof Error && err.name === \"CLIError\") {\n await runMain(main, { rawArgs });\n return;\n }\n stderr(`Error: ${err instanceof Error ? err.message : String(err)}`);\n exit(1);\n }\n}\n"],"mappings":"+HAAA,IAAaA,EAAbC,EAAAC,EAAA,kBAAaF,EAAN,cAAuB,KAAM,CAClC,SAEA,YAAYG,EAAiBC,EAAW,EAAG,CACzC,MAAMD,CAAO,EACb,KAAK,KAAO,WACZ,KAAK,SAAWC,CAClB,CACF,ICLO,SAASC,EAAOC,EAAoB,CACzC,QAAQ,OAAO,MAAMA,EAAO;AAAA,CAAI,CAClC,CAKO,SAASC,EAAOD,EAAoB,CACzC,QAAQ,OAAO,MAAMA,EAAO;AAAA,CAAI,CAClC,CAKO,SAASE,EAAQC,EAAqB,CAC3CJ,EAAO,KAAK,UAAUI,EAAM,KAAM,CAAC,CAAC,CACtC,CAaO,SAASC,EAAYC,EAAmB,CAC7C,GAAIA,EAAI,KAAM,MAAO,GAAGA,CAAC,KACzB,IAAMC,EAAQ,CAAC,KAAM,KAAM,KAAM,IAAI,EACjCC,EAAIF,EAAI,KACR,EAAI,EACR,KAAOE,GAAK,MAAQ,EAAID,EAAM,OAAS,GACrCC,GAAK,KACL,IAEF,MAAO,GAAGA,EAAE,QAAQA,GAAK,KAAO,OAAO,UAAUA,CAAC,EAAI,EAAI,CAAC,CAAC,IAAID,EAAM,CAAC,CAAC,EAC1E,CA1CA,IAAAE,EAAAC,EAAA,oBCAA,OAAS,gBAAAC,GAAc,iBAAAC,GAAe,aAAAC,OAA4B,KAClE,OAAS,QAAAC,OAAY,OACrB,OAAS,WAAAC,OAAe,KAajB,SAASC,IAAwB,CACtC,OAAOC,EACT,CAEO,SAASC,GAAqB,CACnC,GAAI,CACF,IAAMC,EAAMR,GAAaM,GAAa,OAAO,EAC7C,OAAO,KAAK,MAAME,CAAG,CACvB,MAAQ,CACN,MAAO,CAAC,CACV,CACF,CAEO,SAASC,GAAYC,EAAsB,CAChDR,GAAUS,GAAY,CAAE,UAAW,GAAM,KAAM,GAAM,CAAC,EACtDV,GAAcK,GAAa,KAAK,UAAUI,EAAQ,KAAM,CAAC,EAAI;AAAA,EAAM,CACjE,KAAM,GACR,CAAC,CACH,CAEO,SAASE,EAAaC,EAAgC,CAC3D,IAAMH,EAASH,EAAW,EAC1BE,GAAY,CAAE,GAAGC,EAAQ,GAAGG,CAAQ,CAAC,CACvC,CAEO,SAASC,IAAgC,CAC9C,OAAO,QAAQ,IAAI,oBAAyBP,EAAW,EAAE,OAC3D,CAEO,SAASQ,GAAoB,CAClC,OACE,QAAQ,IAAI,oBACZR,EAAW,EAAE,SACbS,EAEJ,CAEO,SAASC,GAAwB,CACtC,IAAMC,EAAMJ,GAAU,EACtB,GAAI,CAACI,EACH,MAAM,IAAI,MACR,gGACF,EAEF,OAAOA,CACT,CA5DA,IAWMP,GACAL,GACAU,GAbNG,EAAAC,EAAA,kBAWMT,GAAaR,GAAKC,GAAQ,EAAG,aAAa,EAC1CE,GAAcH,GAAKQ,GAAY,aAAa,EAC5CK,GAAkB,+BC8BxB,eAAsBK,EACpBC,EACY,CAEZ,IAAMC,EAAM,GADIC,EAAU,CACJ,GAAGF,EAAQ,IAAI,GAE/BG,EAAkC,CAAE,GAAGH,EAAQ,OAAQ,EAExDG,EAAQ,qBAAqB,IAChCA,EAAQ,qBAAqB,EAAIC,IAG/BJ,EAAQ,OACVG,EAAQ,cAAmB,UAAUH,EAAQ,IAAI,IAGnD,IAAIK,EAEAL,EAAQ,SACVK,EAAYL,EAAQ,SACXA,EAAQ,UAAY,OAC7BK,EAAYL,EAAQ,QACXA,EAAQ,OAAS,SAC1BG,EAAQ,cAAc,EAAI,mBAC1BE,EAAY,KAAK,UAAUL,EAAQ,IAAI,GAGzC,IAAIM,EACJ,GAAI,CACF,IAAMC,EAA0C,CAC9C,OAAQP,EAAQ,QAAU,MAC1B,QAAAG,EACA,KAAME,CACR,EACIA,aAAqB,iBAAgBE,EAAK,OAAS,QACvDD,EAAM,MAAM,MAAML,EAAKM,CAAI,CAC7B,OAASC,EAAK,CACZ,MAAM,IAAIC,EACR,kBAAkBD,aAAe,MAAQA,EAAI,QAAU,mBAAmB,GAC1E,CACF,CACF,CAEA,GAAI,CAACF,EAAI,GAAI,CACX,IAAII,EACJ,GAAI,CACFA,EAAa,MAAMJ,EAAI,KAAK,CAC9B,MAAQ,CAER,CAEA,IAAMK,EACJD,GAAW,OAAO,SAAW,QAAQJ,EAAI,MAAM,KAAKA,EAAI,UAAU,GACpE,MAAM,IAAIM,EACRD,EACAL,EAAI,OACJI,GAAW,OAAO,KAClBJ,EAAI,SAAS,MAAM,aAAa,GAAK,MACvC,CACF,CAEA,OAAQ,MAAMA,EAAI,KAAK,CACzB,CAzGA,IAoBaO,GACPT,GAGOQ,EAxBbE,EAAAC,EAAA,kBAAAC,IACAC,IAmBaJ,GAAU,SACjBT,GAAY,kBAAkBS,EAAO,GAG9BD,EAAN,cAA8BH,CAAS,CAC5C,OACA,KACA,WAEA,YACEE,EACAO,EACAC,EACAC,EACA,CACA,MAAMT,EAAS,CAAC,EAChB,KAAK,KAAO,WACZ,KAAK,OAASO,EACd,KAAK,KAAOC,EACZ,KAAK,WAAaC,CACpB,CACF,ICzCA,OAAS,mBAAAC,OAAuB,WAKzB,SAASC,EAAOC,EAAmC,CACxD,IAAMC,EAAKH,GAAgB,CACzB,MAAO,QAAQ,MACf,OAAQ,QAAQ,MAClB,CAAC,EAED,OAAO,IAAI,QAASI,GAAY,CAC9BD,EAAG,SAASD,EAAWG,GAAW,CAChCF,EAAG,MAAM,EACTC,EAAQC,EAAO,KAAK,CAAC,CACvB,CAAC,CACH,CAAC,CACH,CAMA,eAAsBC,EAAeJ,EAAmC,CACtE,OAAK,QAAQ,MAAM,MAIZ,IAAI,QAASE,GAAY,CAC9B,QAAQ,OAAO,MAAMF,CAAQ,EAC7B,QAAQ,MAAM,WAAW,EAAI,EAC7B,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,YAAY,OAAO,EAEjC,IAAIK,EAAW,GAETC,EAAUC,GAAiB,CAC3BA,IAAS;AAAA,GAAQA,IAAS,MAAQA,IAAS,KAC7C,QAAQ,MAAM,WAAW,EAAK,EAC9B,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,eAAe,OAAQD,CAAM,EAC3C,QAAQ,OAAO,MAAM;AAAA,CAAI,EACzBJ,EAAQG,CAAQ,GACPE,IAAS,KAElB,QAAQ,MAAM,WAAW,EAAK,EAC9B,QAAQ,KAAK,GAAG,GAEhBA,IAAS,QACTA,IAAS,KAGTF,EAAWA,EAAS,MAAM,EAAG,EAAE,EAE/BA,GAAYE,CAEhB,EAEA,QAAQ,MAAM,GAAG,OAAQD,CAAM,CACjC,CAAC,EAlCQP,EAAOC,CAAQ,CAmC1B,CA5DA,IAAAQ,GAAAC,EAAA,oBCAA,IAAAC,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IAsBOD,GAtBPE,GAAAC,EAAA,kBACAC,IACAC,IACAC,KACAC,IAkBOP,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,WACN,YAAa,iCACf,EACA,KAAM,CACJ,MAAO,CACL,KAAM,SACN,YAAa,eACf,EACA,SAAU,CACR,KAAM,SACN,YAAa,+DACf,EACA,aAAc,CACZ,KAAM,UACN,YACE,yHACJ,CACF,EACA,MAAM,IAAI,CAAE,KAAAO,CAAK,EAAG,CAClB,IAAMC,EAAc,EAAQ,QAAQ,MAAM,MAIpCC,EAAQF,EAAK,QAAUC,EAAc,MAAME,EAAO,SAAS,EAAI,IAChED,IACHE,EAAO,0CAA0C,EACjD,QAAQ,KAAK,CAAC,GAMhB,IAAIC,EACAL,EAAK,SACPK,EAAWL,EAAK,SACPA,EAAK,aACdK,EAAW,OACFJ,EAITI,EAHgB,MAAMC,EACpB,qDACF,GACsB,OAEtBD,EAAW,OAGTA,IAAa,QAAaA,EAAS,OAAS,IAC9CD,EAAO,yCAAyC,EAChD,QAAQ,KAAK,CAAC,GAGhBA,EAAO,qBAAqB,EAE5B,IAAMG,EAAM,MAAMC,EAA6B,CAC7C,OAAQ,OACR,KAAM,oBAKN,KAAM,CACJ,MAAAN,EACA,GAAIG,EAAW,CAAE,SAAAA,CAAS,EAAI,CAAC,EAC/B,YAAa,CAAE,OAAQ,KAAM,CAC/B,CACF,CAAC,EAEDI,EAAa,CACX,QAASF,EAAI,KAAK,gBAAgB,SAAS,KAC3C,gBAAiBA,EAAI,KAAK,gBAAgB,GAC1C,MAAOA,EAAI,KAAK,KAClB,CAAC,EAEDH,EAAO,6DAA6D,EACpEA,EAAO,UAAUG,EAAI,KAAK,KAAK,EAAE,EACjCH,EAAO,YAAYG,EAAI,KAAK,gBAAgB,EAAE,EAAE,GAI3BA,EAAI,KAAK,eAAiB,IAAS,CAACF,IAEvDD,EACE,yHAEF,EAGGG,EAAI,KAAK,gBACZH,EAAO,+DAA+D,CAE1E,CACF,CAAC,ICnHD,IAAAM,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IA4BOD,GA5BPE,GAAAC,EAAA,kBACAC,IACAC,IACAC,KACAC,IAwBOP,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,QACN,YAAa,mCACf,EACA,KAAM,CACJ,MAAO,CACL,KAAM,SACN,YAAa,eACf,EACA,SAAU,CACR,KAAM,SACN,YAAa,UACf,CACF,EACA,MAAM,IAAI,CAAE,KAAAO,CAAK,EAAG,CAClB,IAAMC,EAAQD,EAAK,OAAU,MAAME,EAAO,SAAS,EAC7CC,EAAWH,EAAK,UAAa,MAAMI,EAAe,YAAY,GAEhE,CAACH,GAAS,CAACE,KACbE,EAAO,kCAAkC,EACzC,QAAQ,KAAK,CAAC,GAGhBA,EAAO,eAAe,EAGtB,IAAMC,EAAW,MAAMC,EAA0B,CAC/C,OAAQ,OACR,KAAM,iBACN,KAAM,CAAE,MAAAN,EAAO,SAAAE,CAAS,CAC1B,CAAC,EAEKK,EAAQF,EAAS,KAAK,MAGxBG,EACJ,GAAI,CAMFA,GALoB,MAAMF,EAA6B,CACrD,OAAQ,MACR,KAAM,eACN,KAAMC,CACR,CAAC,GACuB,KAAK,CAAC,GAAG,EACnC,MAAQ,CAER,CAGA,GAAIC,EAAW,CACb,IAAMC,EAAY,MAAMH,EAA8B,CACpD,OAAQ,OACR,KAAM,sBACN,KAAMC,EACN,KAAM,CAAE,WAAYC,CAAU,CAChC,CAAC,EAEDE,EAAa,CACX,QAASD,EAAU,KAAK,QACxB,gBAAiBD,EACjB,MAAOH,EAAS,KAAK,KACvB,CAAC,EAEDD,EAAO,uDAAuD,EAC9DA,EAAO,UAAUC,EAAS,KAAK,KAAK,EAAE,EACtCD,EAAO,YAAYI,CAAS,EAAE,CAChC,MAEEJ,EAAO,gBAAgBC,EAAS,KAAK,KAAK,GAAG,EAC7CD,EACE,oEACF,EAGAM,EAAa,CAAE,MAAOL,EAAS,KAAK,MAAO,gBAAiB,MAAU,CAAC,EACnEM,EAAW,EAAE,SACfP,EAAO,mHAAmH,CAGhI,CACF,CAAC,ICjGM,SAASQ,EAAaC,EAAeC,EAAO,YAAqB,CACtE,IAAMC,EAAQ,uBAAuB,KAAKF,EAAM,KAAK,CAAC,EACtD,GAAI,CAACE,EACH,MAAM,IAAIC,EACR,WAAWF,CAAI,KAAKD,CAAK,mDAC3B,EAGF,IAAMI,GAAQF,EAAM,CAAC,GAAK,IAAI,YAAY,EACpCG,EAAaD,IAAS,IAAM,MAAQA,IAAS,IAAM,KAAOA,IAAS,IAAM,GAAK,EAC9EE,EAAU,OAAOJ,EAAM,CAAC,GAAK,EAAE,EAAIG,EAEzC,GAAIC,EAAUC,IAAuBD,EAAUE,GAC7C,MAAM,IAAIL,EACR,GAAGF,CAAI,qCAAqCK,CAAO,KACrD,EAGF,OAAOA,CACT,CA9BA,IAGaC,GACAC,GAJbC,GAAAC,EAAA,kBAAAC,IAGaJ,GAAsB,GACtBC,GAAsB,IAAU,GAAK,KCK3C,SAASI,GAAqBC,EAInB,CAChB,GAAM,CAAE,eAAAC,EAAgB,OAAAC,EAAQ,UAAAC,CAAU,EAAIH,EAC9C,GAAIC,IAAmB,OAAW,CAChC,GAAI,CAACG,GAAgB,KAAKH,CAAc,EACtC,MAAO,6EAET,GAAIC,EACF,MAAO,6HAET,GAAIC,EAAY,EACd,MAAO,sIAEX,CACA,OAAO,IACT,CA3BA,IAMMC,GANNC,GAAAC,EAAA,kBAMMF,GAAkB,yBCHxB,SAASG,GAAOC,EAAsB,CACpC,OAAuBA,GAAM,MAAQA,IAAM,GAAW,CAAC,GAC/C,MAAM,QAAQA,CAAC,EAAIA,EAAI,CAACA,CAAC,GAAG,IAAI,MAAM,CAChD,CAOO,SAASC,EAAmBC,EAGA,CAEjC,IAAMC,EAAM,IAAI,IAEVC,EAAOL,GAAOG,EAAK,QAAQ,EACjC,GAAIE,EAAK,OAAS,EAAG,MAAM,IAAIC,EAAS,oCAAoC,EAC5E,GAAID,EAAK,SAAW,EAAG,CACrB,IAAIE,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMF,EAAK,CAAC,CAAW,CACvC,MAAQ,CACN,MAAM,IAAIC,EAAS,qEAAyE,CAC9F,CACA,GAAIC,IAAW,MAAQ,OAAOA,GAAW,UAAY,MAAM,QAAQA,CAAM,EACvE,MAAM,IAAID,EAAS,qEAAyE,EAE9F,OAAW,CAACE,EAAGP,CAAC,IAAK,OAAO,QAAQM,CAAM,EAAGH,EAAI,IAAII,EAAGP,CAAC,CAC3D,CAEA,QAAWQ,KAAQT,GAAOG,EAAK,IAAI,EAAG,CACpC,IAAMO,EAAKD,EAAK,QAAQ,GAAG,EAC3B,GAAIC,GAAM,EAAG,MAAM,IAAIJ,EAAS,kCAAkCG,CAAI,IAAI,EAC1E,IAAME,EAAMF,EAAK,MAAM,EAAGC,CAAE,EACtBE,EAAQH,EAAK,MAAMC,EAAK,CAAC,EAC/B,GAAIN,EAAI,IAAIO,CAAG,GAAKP,EAAI,IAAIO,CAAG,IAAMC,EACnC,MAAM,IAAIN,EAAS,iBAAiBK,CAAG,sCAAsC,EAE/EP,EAAI,IAAIO,EAAKC,CAAK,CACpB,CAEA,OAAOR,EAAI,OAAS,EAAI,KAAO,OAAO,YAAYA,CAAG,CACvD,CA/CA,IAAAS,EAAAC,EAAA,kBAAAC,MCyBA,eAAsBC,GACpBC,EACAC,EACAC,EAAsE,CAAC,EACrC,CAClC,IAAMC,EAAQD,EAAK,QAAWE,GAAe,IAAI,QAAeC,GAAM,WAAWA,EAAGD,CAAE,CAAC,GACjFE,EAAMJ,EAAK,KAAO,KAAK,IACvBK,EAAWD,EAAI,EAAIE,GACrBC,EAAgC,KACpC,OAAS,CAIP,GAFAA,GADY,MAAMC,EAAwB,CAAE,KAAM,cAAc,mBAAmBV,CAAE,CAAC,GAAI,KAAAC,CAAK,CAAC,GACrF,KAAK,aAAe,KAC3B,CAACQ,GAAQA,EAAK,SAAW,WACzBH,EAAI,GAAKC,EAAU,OAAOE,EAC9B,MAAMN,EAAMQ,EAAO,CACrB,CACF,CAGO,SAASC,EAAgBC,EAAoC,CAClE,OAAKA,EACDA,EAAE,SAAW,OAAe,QAAQA,EAAE,KAAO,EAAE,GAAGA,EAAE,MAAM,OAAS,WAAWA,EAAE,KAAK,KAAK,IAAI,CAAC,IAAM,EAAE,GACvGA,EAAE,SAAW,SAAiB,uBAAuBA,EAAE,OAAS,SAAS,KACtE,mFAHQ,8BAIjB,CAjDA,IAgBMF,GACAH,GAjBNM,GAAAC,EAAA,kBAAAC,IAgBML,GAAU,IACVH,GAAc,MCjBpB,OAAS,oBAAAS,GAAkB,YAAAC,OAAgB,KAC3C,OAAS,YAAAC,OAAgB,OACzB,OAAS,YAAAC,OAAgB,SAqBzB,SAASC,GAASC,EAAkBC,EAAgBC,EAA0C,CAC5F,IAAMC,EAASR,GAAiBK,EAAU,CACxC,GAAIC,IAAU,OAAY,CAAE,MAAAA,CAAM,EAAI,CAAC,EACvC,GAAIC,IAAQ,OAAY,CAAE,IAAAA,CAAI,EAAI,CAAC,CACrC,CAAC,EACD,OAAOJ,GAAS,MAAMK,CAAM,CAC9B,CAGA,eAAsBC,GAAgBJ,EAAkBK,EAAoC,CAC1F,IAAMC,EAAOV,GAASI,CAAQ,EAC9B,GAAI,CAACM,EAAK,OAAO,GAAKA,EAAK,MAAQ,EACjC,MAAM,IAAIC,EAAS,kCAAmC,CAAC,EAGzD,IAAMC,EAAWX,GAASG,CAAQ,EAClC,GAAIM,EAAK,MAAQG,GAAkB,CACjC,GAAM,CAAE,KAAAC,CAAK,EAAI,MAAMC,EAAgC,CACrD,OAAQ,OACR,KAAM,aACN,KAAMN,EACN,QAASN,GAASC,CAAQ,EAC1B,QAAS,CACP,eAAgB,YAChB,iBAAkB,OAAOM,EAAK,IAAI,EAClC,wBAAyB,mBAAmBE,CAAQ,CACtD,CACF,CAAC,EACD,OAAOE,CACT,CAEA,IAAIE,EACJ,GAAI,CACF,IAAMC,EAAU,MAAMF,EAAoC,CACxD,OAAQ,OACR,KAAM,qBACN,KAAMN,EACN,KAAM,CAAE,SAAAG,EAAU,KAAMF,EAAK,IAAK,CACpC,CAAC,EACDM,EAAWC,EAAQ,KAAK,UACxB,IAAMC,EAAWD,EAAQ,KAAK,UAC9B,GAAI,CAAC,OAAO,cAAcC,CAAQ,GAAKA,GAAY,EACjD,MAAM,IAAIP,EAAS,+CAAgD,CAAC,EAGtE,IAAIQ,EAAa,EACjB,QAASd,EAAQ,EAAGA,EAAQK,EAAK,KAAML,GAASa,EAAUC,GAAc,EAAG,CACzE,IAAMC,EAAS,KAAK,IAAIF,EAAUR,EAAK,KAAOL,CAAK,EACnD,MAAMU,EAAW,CACf,OAAQ,MACR,KAAM,sBAAsBC,CAAQ,UAAUG,CAAU,GACxD,KAAMV,EACN,QAASN,GAASC,EAAUC,EAAOA,EAAQe,EAAS,CAAC,EACrD,QAAS,CAAE,iBAAkB,OAAOA,CAAM,CAAE,CAC9C,CAAC,CACH,CAEA,GAAM,CAAE,KAAAN,CAAK,EAAI,MAAMC,EAAgC,CACrD,OAAQ,OACR,KAAM,sBAAsBC,CAAQ,YACpC,KAAMP,CACR,CAAC,EACD,OAAOK,CACT,OAASO,EAAO,CACd,MAAIL,GACF,MAAMD,EAAW,CACf,OAAQ,SACR,KAAM,sBAAsBC,CAAQ,GACpC,KAAMP,CACR,CAAC,EAAE,MAAM,IAAG,EAAY,EAEpBY,CACR,CACF,CAhGA,IAMMR,GANNS,GAAAC,EAAA,kBAGAC,IACAC,IAEMZ,GAAmB,MCNzB,IAAAa,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAC9B,OAAS,gBAAAC,OAAoB,KAC7B,OAAS,YAAAC,GAAU,WAAAC,OAAe,OAFlC,IAqCOJ,GArCPK,GAAAC,EAAA,kBAGAC,IACAC,IACAC,KACAC,KACAC,IACAC,KACAC,IACAC,KA2BOd,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,SACN,YAAa,yCACf,EACA,KAAM,CACJ,MAAO,CACL,KAAM,aACN,YAAa,sCACb,SAAU,EACZ,EACA,OAAQ,CACN,KAAM,SACN,YAAa,8BACf,EACA,QAAS,CACP,KAAM,UACN,YACE,sGACF,QAAS,EACX,EACA,QAAS,CACP,KAAM,SACN,YACE,uFACJ,EACA,UAAW,CACT,KAAM,UACN,YACE,8JACF,QAAS,EACX,EACA,kBAAmB,CACjB,KAAM,SACN,YACE,sIACJ,EACA,OAAQ,CACN,KAAM,UACN,YACE,kGACF,QAAS,EACX,EACA,KAAM,CACJ,KAAM,SACN,YACE,sFACJ,EACA,SAAU,CACR,KAAM,SACN,YAAa,iFACf,EACA,SAAU,CACR,KAAM,UACN,YACE,8LACF,QAAS,EACX,EACA,KAAM,CACJ,KAAM,UACN,YACE,2HACF,QAAS,EACX,EACA,KAAM,CACJ,KAAM,UACN,YAAa,4BACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAc,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EAIzBC,EAA6B,KAC7BH,EAAK,UAAY,SACdA,EAAK,UACRI,EAAO,mFAAmF,EAC1F,QAAQ,KAAK,CAAC,GAEhBD,EAAcE,EAAa,OAAOL,EAAK,OAAO,CAAC,GAG7CA,EAAK,SAAWA,EAAK,QACvBI,EAAO,kDAAkD,EAK3D,IAAME,EAAQN,EAAK,EAAE,OAASA,EAAK,EAAE,IAAI,MAAM,EAAI,CAAC,OAAOA,EAAK,KAAK,CAAC,EAEhEO,EAAWD,EAAM,KAAME,GAAanB,GAAQmB,CAAQ,EAAE,YAAY,IAAM,MAAM,EAC9EC,EACJT,EAAK,QACLA,EAAK,SACLA,EAAK,SACLA,EAAK,WACLA,EAAK,iBAAiB,GACtBA,EAAK,QACLA,EAAK,MACLA,EAAK,UACLA,EAAK,UACLA,EAAK,KACHO,GAAYE,IACdL,EACE,kIACF,EACA,QAAQ,KAAK,CAAC,GAEhB,IAAIM,EAAW,GAETC,EAAiBX,EAAK,iBAAiB,EACvCY,EAAaC,GAAqB,CACtC,eAAAF,EACA,OAAQX,EAAK,OACb,UAAWM,EAAM,MACnB,CAAC,EACGM,IACFR,EAAOQ,CAAU,EACjB,QAAQ,KAAK,CAAC,GAGhB,IAAME,EAAWC,EAAmB,CAAE,KAAMf,EAAK,KAAM,SAAUA,EAAK,QAAS,CAAC,EAE5EA,EAAK,MAAQ,CAACA,EAAK,WACrBI,EAAO,+CAA+C,EACtD,QAAQ,KAAK,CAAC,GAGhB,IAAMY,EAAoC,CAAC,EAE3C,QAAWR,KAAYF,EACrB,GAAI,CACF,GAAIjB,GAAQmB,CAAQ,EAAE,YAAY,IAAM,OAAQ,CAC9C,IAAMS,EAAQ,MAAMC,GAAgBV,EAAUP,CAAM,EAChDD,EAAK,KACPgB,EAAQ,KAAKC,CAAK,EAElBE,EAAOF,EAAM,GAAG,EAElB,QACF,CAEA,IAAMG,EAASjC,GAAaqB,CAAQ,EAC9Ba,EAAWjC,GAASoB,CAAQ,EAE5Bc,EAAW,IAAI,SACrBA,EAAS,OACP,OACA,IAAI,KAAK,CAACF,CAAM,EAAGC,CAAQ,CAC7B,EAEIrB,EAAK,QACPsB,EAAS,OAAO,SAAUtB,EAAK,MAAM,EAGnCA,EAAK,UAIPsB,EAAS,OAAO,aAAc,SAAS,EACnCnB,IAAgB,MAClBmB,EAAS,OAAO,kBAAmB,OAAOnB,CAAW,CAAC,GAItDH,EAAK,QACPsB,EAAS,OAAO,SAAU,MAAM,EAG9BR,GACFQ,EAAS,OAAO,WAAY,KAAK,UAAUR,CAAQ,CAAC,EAGlDd,EAAK,UACPsB,EAAS,OAAO,WAAY,MAAM,EAGpC,IAAMC,EAAM,MAAMC,EAA2B,CAC3C,OAAQ,OACR,KAAM,aACN,KAAMvB,EACN,SAAAqB,EACA,GAAIX,EAAiB,CAAE,QAAS,CAAE,kBAAmBA,CAAe,CAAE,EAAI,CAAC,CAC7E,CAAC,EAWD,GARIY,EAAI,KAAK,SACXnB,EAAO,aAAaI,CAAQ,8DAA8D,EACjFe,EAAI,KAAK,cAClBnB,EAAO,iBAAiBI,CAAQ,qDAAqD,EAKnFR,EAAK,SAAWuB,EAAI,KAAK,aAAe,UAAW,CACrDb,EAAW,GACXN,EAAO,sBAAsBI,CAAQ,4CAA4C,EACjF,QACF,CAGIR,EAAK,WACHuB,EAAI,KAAK,iBACXnB,EAAO,2BAA2BI,CAAQ,KAAKe,EAAI,KAAK,gBAAgB,GAAG,EAClEvB,EAAK,MAAQuB,EAAI,KAAK,aAAa,SAAW,WACvDA,EAAI,KAAK,YAAc,MAAME,GAAmBF,EAAI,KAAK,GAAItB,CAAM,EAC9DD,EAAK,MAAMI,EAAOsB,EAAgBH,EAAI,KAAK,aAAe,IAAI,CAAC,GAC3DA,EAAI,KAAK,aAAa,SAAW,UAC1CnB,EAAO,cAAcI,CAAQ,oDAAoDe,EAAI,KAAK,EAAE,SAAS,EAC5FA,EAAI,KAAK,aAAa,SAAW,QAAU,CAACvB,EAAK,MAC1DI,EAAOsB,EAAgBH,EAAI,KAAK,WAAW,CAAC,GAI5CvB,EAAK,KACPgB,EAAQ,KAAKO,EAAI,IAAI,EAErBJ,EAAOnB,EAAK,UAAauB,EAAI,KAAK,eAAiBA,EAAI,KAAK,IAAOA,EAAI,KAAK,GAAG,CAEnF,OAASI,EAAK,CACZjB,EAAW,GACXN,EACE,oBAAoBI,CAAQ,KAAKmB,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,EACnF,CACF,CAGE3B,EAAK,MAAQgB,EAAQ,OAAS,GAChCY,EAAQZ,EAAQ,SAAW,EAAIA,EAAQ,CAAC,EAAIA,CAAO,EAGjDN,GACF,QAAQ,KAAK,CAAC,CAElB,CACF,CAAC,IClRD,IAAAmB,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IA8BOD,GA9BPE,GAAAC,EAAA,kBACAC,IACAC,IACAC,IACAC,KACAC,IAyBOR,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,WACN,YACE,8FACJ,EACA,KAAM,CACJ,OAAQ,CACN,KAAM,aACN,YAAa,gDACb,SAAU,EACZ,EACA,MAAO,CACL,KAAM,SACN,MAAO,IACP,YACE,+HACJ,EACA,OAAQ,CACN,KAAM,SACN,YAAa,iCACf,EACA,QAAS,CACP,KAAM,UACN,YACE,0EACF,QAAS,EACX,EACA,QAAS,CACP,KAAM,SACN,YACE,uFACJ,EACA,eAAgB,CACd,KAAM,SACN,YACE,gFACJ,EACA,UAAW,CACT,KAAM,UACN,YACE,0IACF,QAAS,EACX,EACA,KAAM,CACJ,KAAM,UACN,YAAa,gCACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAQ,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EAIvBC,GAAUH,EAAK,EAAE,OAASA,EAAK,EAAE,IAAI,MAAM,EAAI,CAAC,OAAOA,EAAK,QAAU,EAAE,CAAC,GAC5E,KAAK,GAAG,EACR,KAAK,EACR,GAAI,CAACG,EACH,MAAM,IAAIC,EAAS,0DAA0D,EAI/E,GAAIJ,EAAK,UAAY,QAAa,CAACA,EAAK,QACtC,MAAM,IAAII,EACR,iIACF,EAEF,IAAMC,EACJL,EAAK,UAAY,OAAYM,EAAa,OAAON,EAAK,OAAO,CAAC,EAAI,OAC9DO,EACJP,EAAK,cAAc,IAAM,OACrBM,EAAa,OAAON,EAAK,cAAc,CAAC,EAAG,gBAAgB,EAC3D,OAEFA,EAAK,SAAWA,EAAK,QACvBQ,EAAO,iDAAiD,EAG1D,IAAMC,EAAM,MAAMC,EAA6B,CAC7C,OAAQ,OACR,KAAM,sBACN,KAAMT,EACN,KAAM,CACJ,OAAAE,EACA,GAAIH,EAAK,MAAQ,CAAE,MAAO,OAAOA,EAAK,KAAK,CAAE,EAAI,CAAC,EAClD,GAAIA,EAAK,OAAS,CAAE,OAAQ,OAAOA,EAAK,MAAM,CAAE,EAAI,CAAC,EACrD,GAAIA,EAAK,QAAU,CAAE,WAAY,SAAU,EAAI,CAAC,EAChD,GAAIK,IAAgB,OAAY,CAAE,gBAAiBA,CAAY,EAAI,CAAC,EACpE,GAAIE,IAAgB,OAAY,CAAE,WAAYA,CAAY,EAAI,CAAC,CACjE,CACF,CAAC,EAID,GAAIP,EAAK,SAAWS,EAAI,KAAK,aAAe,UAC1C,MAAM,IAAIL,EACR,yEACF,EAGF,IAAMO,EAAIF,EAAI,KAAK,WAEnBD,EACE,kBAAkBG,EAAE,KAAK,KAAKA,EAAE,eAAe,UAAUA,EAAE,kBAAoB,EAAI,GAAK,GAAG,UAAUA,EAAE,iBAAiB,mBAC1H,EAEIX,EAAK,KACPY,EAAQH,EAAI,IAAI,EAEhBI,EAAOb,EAAK,UAAaS,EAAI,KAAK,eAAiBA,EAAI,KAAK,IAAOA,EAAI,KAAK,GAAG,CAEnF,CACF,CAAC,IC/ID,IAAAK,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IAyBOD,GAzBPE,GAAAC,EAAA,kBACAC,IACAC,IACAC,IACAC,IAqBOP,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,OACN,YAAa,sBACf,EACA,KAAM,CACJ,KAAM,CACJ,KAAM,SACN,YAAa,cACb,QAAS,GACX,EACA,WAAY,CACV,KAAM,SACN,YAAa,2BACb,QAAS,IACX,EACA,OAAQ,CACN,KAAM,SACN,YACE,mKACF,MAAO,GACT,EACA,IAAK,CACH,KAAM,SACN,YAAa,oFACf,EACA,KAAM,CACJ,KAAM,SACN,YACE,2FACJ,EACA,SAAU,CACR,KAAM,SACN,YACE,+FACJ,EACA,KAAM,CACJ,KAAM,UACN,YAAa,4BACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAO,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EAEvBC,EAAS,IAAI,gBAAgB,CACjC,KAAMH,EAAK,KACX,SAAUA,EAAK,UAAU,CAC3B,CAAC,EACKI,EAASC,EAAmB,CAAE,KAAML,EAAK,KAAM,SAAUA,EAAK,QAAS,CAAC,EAC1EI,GAAQD,EAAO,IAAI,WAAY,KAAK,UAAUC,CAAM,CAAC,EACzD,IAAME,EAAS,OAAON,EAAK,QAAW,SAAWA,EAAK,OAAO,KAAK,EAAI,GAClEM,GAAQH,EAAO,IAAI,IAAKG,CAAM,EAClC,IAAMC,EAAM,OAAOP,EAAK,KAAQ,SAAWA,EAAK,IAAI,KAAK,EAAI,GACzDO,GAAKJ,EAAO,IAAI,MAAOI,CAAG,EAE9B,IAAMC,EAAM,MAAMC,EAAyB,CACzC,KAAM,cAAcN,CAAM,GAC1B,KAAMF,CACR,CAAC,EAED,GAAID,EAAK,KACPU,EAAQF,CAAG,MAEX,SAAWG,KAAOH,EAAI,KACpBI,EAAOD,EAAI,GAAG,CAGpB,CACF,CAAC,IC9FD,IAAAE,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IASOD,GATPE,GAAAC,EAAA,kBACAC,IACAC,IACAC,IAMON,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,OACN,YACE,sGACJ,EACA,KAAM,CACJ,KAAM,CACJ,KAAM,UACN,YAAa,gCACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAM,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EAEvBC,EAAM,MAAMC,EAAyB,CAAE,KAAM,kBAAmB,KAAMH,CAAO,CAAC,EAEpF,GAAID,EAAK,KACPK,EAAQF,CAAG,MAEX,SAAWG,KAAKH,EAAI,KAClBI,EAAO,GAAGD,EAAE,GAAG,IAAKA,EAAE,KAAK,EAAE,CAGnC,CACF,CAAC,ICnCD,IAAAE,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IAgBOD,GAhBPE,GAAAC,EAAA,kBACAC,IACAC,IACAC,IACAC,IACAL,KAWOF,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,WACN,YACE,+FACJ,EACA,KAAM,CACJ,GAAI,CACF,KAAM,aACN,YAAa,6BACb,SAAU,EACZ,EACA,KAAM,CACJ,KAAM,UACN,YAAa,yFACb,QAAS,EACX,EACA,KAAM,CACJ,KAAM,UACN,YAAa,kFACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAO,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EACvBC,EAAK,OAAOH,EAAK,EAAE,EAGnBI,EAAM,MAAMC,EAA6B,CAC7C,OAAQ,OACR,KAAM,cAAc,mBAAmBF,CAAE,CAAC,YAC1C,KAAMF,CACR,CAAC,EAED,GAAIG,EAAI,KAAK,iBACX,MAAM,IAAIE,EAAS,4BAA4BF,EAAI,KAAK,gBAAgB,GAAG,EAG7E,IAAIG,EAAcH,EAAI,KAAK,aAAe,KAK1C,GAJIJ,EAAK,MAAQO,GAAa,SAAW,YACvCA,EAAc,MAAMC,GAAmBL,EAAIF,CAAM,GAG/CM,GAAa,SAAW,SAC1B,MAAM,IAAID,EAASG,EAAgBF,CAAW,CAAC,EAGjD,GAAIP,EAAK,KAAM,CACbU,EAAQ,CAAE,GAAAP,EAAI,YAAAI,CAAY,CAAC,EAC3B,MACF,CAEIA,GAAa,SAAW,QAC1BI,EAAOJ,EAAY,KAAO,EAAE,EACxBA,EAAY,MAAM,QAAQK,EAAO,SAASL,EAAY,KAAK,KAAK,IAAI,CAAC,EAAE,GAE3EK,EAAOH,EAAgBF,CAAW,CAAC,CAEvC,CACF,CAAC,ICzBD,eAAsBM,IAAmC,CACvD,GAAM,CAACC,EAAQ,EAAGC,EAAQ,CAAC,EAAI,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,IAAI,MAAM,EAC1E,GAAID,EAAQE,IAAmBF,IAAUE,IAAkBD,EAAQE,GACjE,MAAM,IAAIC,EACR,uBAAuBF,EAAc,IAAIC,EAAc,uBAAuB,QAAQ,SAAS,IAAI,IACrG,EAEF,GAAI,CACF,IAAME,EAAW,KAAM,QAAO,OAAO,EACrC,OAAQA,EAAI,SAAWA,CACzB,OAASC,EAAK,CACZ,IAAMC,EAAMD,aAAe,MAAQA,EAAI,QAAQ,MAAM;AAAA,CAAI,EAAE,CAAC,EAAI,OAAOA,CAAG,EAC1E,MAAM,IAAIF,EACR;AAAA,YACeG,CAAG;AAAA,yFAEpB,CACF,CACF,CAGA,SAASC,GAAcC,EAAmBC,EAA+D,CAEvG,OADeD,IAAW,OAASC,EAAWD,IAAW,MAAQ,OAASA,EAC1D,CACd,IAAK,OACH,MAAO,CAAE,YAAa,OAAQ,IAAK,KAAM,EAC3C,IAAK,MACH,MAAO,CAAE,YAAa,MAAO,IAAK,KAAM,EAC1C,IAAK,OACH,MAAO,CAAE,YAAa,OAAQ,IAAK,MAAO,EAC5C,IAAK,OACH,MAAO,CAAE,YAAa,OAAQ,IAAK,MAAO,EAC5C,QACE,OAAO,IACX,CACF,CAEA,eAAsBE,GACpBC,EACAC,EACAC,EACAC,EACyB,CACzB,IAAMC,EAAO,CAAE,WAAYH,EAAM,OAAQ,YAAaA,EAAM,OAAQ,IAAKC,CAAS,EAC5EG,EAAQC,IAAoC,CAAE,OAAQ,UAAW,OAAAA,EAAQ,GAAGF,CAAK,GAEnFG,EACJ,GAAI,CACFA,EAAO,MAAMP,EAAMC,EAAOO,EAAW,EAAE,SAAS,CAClD,OAASd,EAAK,CAEZ,IAAMe,EAAMf,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EAC3D,MAAM,IAAI,MAAM,eAAe,KAAKe,CAAG,EAAI,iBAAiBC,GAAmB,GAAG,cAAgB,sBAAsB,CAC1H,CAGA,GAAIH,EAAK,SAAW,MAAO,OAAOF,EAAK,sBAAsB,EAC7D,GAAIE,EAAK,SAAW,MAAO,OAAOF,EAAK,sBAAsB,EAC7D,IAAKE,EAAK,OAAS,GAAK,EAAG,OAAOF,EAAK,mCAAmC,EAC1E,GAAIE,EAAK,SAAW,QAAUA,EAAK,cAAgB,OACjD,OAAOF,EAAK,iFAAiF,EAG/F,IAAMP,EAAWS,EAAK,SAAW,OAAS,OAASA,EAAK,OAClDI,EAAMf,GAAcO,EAAK,OAAQL,CAAQ,EAC/C,GAAI,CAACa,EAAK,OAAON,EAAK,uBAAuBE,EAAK,MAAM,EAAE,EAG1D,IAAIK,EAAWZ,EAAMC,EAAOO,EAAW,EAAE,OAAO,EAkBhD,QAhBIL,EAAK,UAAYA,EAAK,aACxBS,EAAWA,EAAS,OAAO,CACzB,MAAOT,EAAK,SACZ,OAAQA,EAAK,UACb,IAAK,SACL,mBAAoB,EACtB,CAAC,GAICQ,EAAI,cAAgB,QAAUJ,EAAK,WACrCK,EAAWA,EAAS,QAAQ,CAAE,WAAY,SAAU,CAAC,GAGnDT,EAAK,eAAcS,EAAWA,EAAS,aAAa,GAEhDD,EAAI,YAAa,CACvB,IAAK,OACHC,EAAWA,EAAS,KAAK,CAAE,QAAST,EAAK,QAAS,QAAS,EAAK,CAAC,EACjE,MACF,IAAK,MACHS,EAAWA,EAAS,IAAI,CAAE,iBAAkB,EAAG,OAAQ,EAAG,CAAC,EAC3D,MACF,IAAK,OACHA,EAAWA,EAAS,KAAK,CAAE,QAAST,EAAK,QAAS,OAAQ,CAAE,CAAC,EAC7D,MACF,IAAK,OACHS,EAAWA,EAAS,KAAK,CAAE,QAAST,EAAK,QAAS,OAAQ,CAAE,CAAC,EAC7D,KACJ,CAEA,GAAM,CAAE,KAAAU,EAAM,KAAAC,CAAK,EAAI,MAAMF,EAAS,SAAS,CAAE,kBAAmB,EAAK,CAAC,EAG1E,GAAIC,EAAK,QAAUZ,EAAM,OACvB,OAAOI,EAAK,0CAA0C,EAKxD,IAAMU,GAAQR,EAAK,aAAe,IAAM,EAClCS,EAAMD,EAAOR,EAAK,OAASA,EAAK,MAChCU,EAAMF,EAAOR,EAAK,MAAQA,EAAK,OAErC,OADkBI,EAAI,cAAgBb,GAAYgB,EAAK,QAAUE,GAAOF,EAAK,SAAWG,IACtEhB,EAAM,OAASY,EAAK,QAAUZ,EAAM,OAASiB,GAAqB,IAC3Eb,EAAK,qCAAqCa,EAAkB,IAAI,EAElE,CACL,OAAQ,YACR,WAAYjB,EAAM,OAClB,YAAaY,EAAK,OAClB,IAAKF,EAAI,IACT,MAAOG,EAAK,MACZ,OAAQA,EAAK,OACb,OAAQD,CACV,CACF,CAGO,SAASM,GAAaC,EAAgBC,EAAuB,CAClE,OAAID,GAAU,EAAU,EACjB,KAAK,OAAQA,EAASC,GAASD,EAAU,GAAG,CACrD,CAtLA,IAgCaV,GACAY,GACPd,GAGOU,GAEAK,GAGPjC,GACAC,GA3CNiC,GAAAC,EAAA,kBAAAC,IAgCahB,GAAmB,IAAM,IAAO,IAChCY,GAAkB,IAAM,KAAO,KACtCd,GAAc,CAAE,iBAAkBE,GAAkB,OAAQ,OAAQ,EAG7DQ,GAAqB,EAErBK,GAAuB,CAAC,MAAO,OAAQ,MAAO,OAAQ,MAAM,EAGnEjC,GAAiB,GACjBC,GAAiB,IC3CvB,IAAAoC,GAAA,GAAAC,EAAAD,GAAA,qBAAAE,GAAA,iBAAAC,GAAA,YAAAC,GAAA,cAAAC,GAAA,qBAAAC,GAAA,eAAAC,GAAA,gBAAAC,KAAA,OAAS,iBAAAC,OAAqB,QAC9B,OAAS,cAAAC,OAAkB,SAC3B,OAAS,wBAAAC,OAA4B,KACrC,OACE,gBAAAC,GACA,iBAAAC,GACA,cAAAC,GACA,cAAAC,GACA,aAAAC,GACA,YAAAC,GACA,aAAAC,GACA,eAAAC,GACA,cAAAC,OACK,KACP,OAAS,YAAAC,EAAU,WAAAC,GAAS,WAAAC,EAAS,QAAAC,EAAM,YAAAC,GAAU,WAAAC,MAAoB,OAwDlE,SAASpB,GAAiBqB,EAAcC,EAAoC,CACjF,GAA2BA,GAAU,MAAQA,IAAU,GAAI,OAC3D,IAAMC,EAAI,OAAOD,CAAK,EACtB,GAAI,CAAC,OAAO,UAAUC,CAAC,GAAKA,EAAI,EAAG,MAAM,IAAIC,EAAS,GAAGH,CAAI,mCAAmC,EAChG,OAAOE,CACT,CAEA,SAASE,GAAYC,EAAuB,CAC1C,OAAQC,GAA2C,SAASV,EAAQS,CAAI,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,CAClG,CAGA,SAASE,GAAKC,EAAaC,EAAeC,EAAmC,CAC3E,QAAWV,KAAQR,GAAYgB,CAAG,EAAG,CACnC,GAAIR,EAAK,WAAW,GAAG,GAAKA,IAAS,eAAgB,SACrD,IAAMW,EAAId,EAAKW,EAAKR,CAAI,EAClBY,EAAKrB,GAAUoB,CAAC,EACtB,GAAI,CAAAC,EAAG,eAAe,EACtB,GAAIA,EAAG,YAAY,EAAG,CACpB,GAAIF,GAAWX,EAAQY,CAAC,IAAMD,EAAS,SACvCH,GAAKI,EAAGF,EAAKC,CAAO,CACtB,MAAWN,GAAYO,CAAC,GAAK,CAACE,GAAM,KAAKnB,EAASiB,EAAGf,EAAQe,CAAC,CAAC,CAAC,GAC9DF,EAAI,KAAKE,CAAC,CAEd,CACF,CAGO,SAASnC,GAAasC,EAAiBC,EAAoBC,EAAyB,CACzF,IAAMN,EAAUM,EAASjB,EAAQiB,CAAM,EAAI,OACrCC,EAAgB,CAAC,EACvB,QAAWN,KAAKG,EAAO,CACrB,IAAIF,EACJ,GAAI,CACFA,EAAKtB,GAASqB,CAAC,CACjB,MAAQ,CACN,MAAM,IAAIR,EAAS,8BAA8BQ,CAAC,EAAE,CACtD,CACA,GAAIC,EAAG,YAAY,EAAG,CACpB,GAAI,CAACG,EAAW,MAAM,IAAIZ,EAAS,GAAGQ,CAAC,qEAAqE,EAC5G,IAAMO,EAAkB,CAAC,EACzBX,GAAKI,EAAGO,EAAOR,CAAO,EACtB,QAAWS,KAAKD,EAAM,KAAK,EAAGD,EAAM,KAAK,CAAE,KAAME,EAAG,KAAMR,CAAE,CAAC,CAC/D,MACEM,EAAM,KAAK,CAAE,KAAMN,EAAG,KAAM,IAAK,CAAC,CAEtC,CACA,OAAOM,CACT,CAMO,SAASvC,GAAU0C,EAAYC,EAA2B,CAC/D,OAAIA,IAAW,OAAezB,EAAQwB,EAAK,IAAI,EAAE,MAAM,CAAC,EACjDC,IAAW,MAAQ,MAAQA,CACpC,CAEO,SAASzC,GAAWwC,EAAYC,EAAmBC,EAAkB,CAC1E,IAAMC,EAAO7B,EAAS0B,EAAK,KAAMxB,EAAQwB,EAAK,IAAI,CAAC,EAC7CI,EAAM9C,GAAU0C,EAAMC,CAAM,EAClC,GAAIC,EAAK,QAAS,MAAO,CAAE,KAAAF,EAAM,UAAWA,EAAK,KAAM,KAAM,IAAK,EAClE,GAAIE,EAAK,OAAQ,CACf,IAAMG,EAAML,EAAK,KAAOzB,GAAQG,GAASsB,EAAK,KAAMA,EAAK,IAAI,CAAC,EAAI,IAC5DZ,EAAMX,EAAKyB,EAAK,OAAQG,CAAG,EACjC,MAAO,CAAE,KAAAL,EAAM,UAAWvB,EAAKW,EAAK,GAAGe,CAAI,IAAIC,CAAG,EAAE,EAAG,KAAM3B,EAAKW,EAAKd,EAAS0B,EAAK,IAAI,CAAC,CAAE,CAC9F,CACA,OAAIE,EAAK,YACA,CAAE,KAAAF,EAAM,UAAWvB,EAAKF,GAAQyB,EAAK,IAAI,EAAG,GAAGG,CAAI,cAAcC,CAAG,EAAE,EAAG,KAAM,IAAK,EAEtF,CAAE,KAAAJ,EAAM,UAAW,KAAM,KAAM,IAAK,CAC7C,CAOO,SAAS7C,GAAgBmD,EAAeJ,EAAkB,CAC/D,IAAMK,EAAS,IAAI,IAAID,EAAM,IAAKf,GAAMZ,EAAQY,EAAE,KAAK,IAAI,CAAC,CAAC,EACvDiB,EAAU,IAAI,IAEpB,QAAWC,KAAQH,EAAO,CACxB,IAAMI,EAAM/B,EAAQ8B,EAAK,KAAK,IAAI,EAElC,GAAIP,EAAK,SAAW/B,GAAUsC,EAAK,KAAK,IAAI,EAAE,eAAe,EAC3D,MAAM,IAAI1B,EAAS,GAAG0B,EAAK,KAAK,IAAI,mFAAmF,EAGzH,IAAME,EAA2C,CAC/C,CAACF,EAAK,UAAW,EAAK,EACtB,CAACA,EAAK,KAAM,EAAI,CAClB,EACA,OAAW,CAACG,EAAQC,CAAM,IAAKF,EAAS,CACtC,GAAI,CAACC,EAAQ,SACb,IAAMvB,EAAMV,EAAQiC,CAAM,EACpBE,EAAMzB,IAAQqB,EAIpB,GAAII,GAAOD,GAAU,CAACX,EAAK,SAAWO,EAAK,YAAcG,EAAQ,SACjE,GAAIE,GAAO,CAACZ,EAAK,QACf,MAAM,IAAInB,EAAS,GAAG0B,EAAK,KAAK,IAAI,qFAAqF,EAE3H,GAAI,CAACK,GAAOP,EAAO,IAAIlB,CAAG,EACxB,MAAM,IAAIN,EAAS,GAAG6B,CAAM,kFAAkF,EAEhH,IAAMG,EAAQP,EAAQ,IAAInB,CAAG,EAC7B,GAAI0B,GAASA,IAAUN,EAAK,KAAK,KAC/B,MAAM,IAAI1B,EAAS,GAAG0B,EAAK,KAAK,IAAI,QAAQM,CAAK,6BAA6BH,CAAM,+DAA+D,EAIrJ,GAFAJ,EAAQ,IAAInB,EAAKoB,EAAK,KAAK,IAAI,EAE3B,CAACK,GAAOzC,GAAWgB,CAAG,GAAKlB,GAAUkB,CAAG,EAAE,eAAe,EAC3D,MAAM,IAAIN,EAAS,GAAG6B,CAAM,8CAA8C,CAE9E,CACF,CACF,CAGO,SAASnD,GAAYmD,EAAgBI,EAAcd,EAAqB,CAC7EjC,GAAUM,GAAQqC,CAAM,EAAG,CAAE,UAAW,EAAK,CAAC,EAC9C,IAAMK,EAAMxC,EAAKF,GAAQqC,CAAM,EAAG,IAAItC,EAASsC,CAAM,CAAC,IAAI,QAAQ,GAAG,MAAM,EAC3E,GAAI,CACF9C,GAAcmD,EAAKD,EAAMd,IAAS,OAAY,CAAE,KAAAA,CAAK,EAAI,MAAS,EAClEnC,GAAWkD,EAAKL,CAAM,CACxB,OAASM,EAAK,CACZ,GAAI,CACFlD,GAAWiD,CAAG,CAChB,MAAQ,CAER,CACA,MAAMC,CACR,CACF,CAEA,eAAeC,GAAWtB,EAAYuB,EAAeC,EAA2C,CAC9F,IAAMC,EAAe,IAAI,MAAMzB,EAAM,MAAM,EACvC0B,EAAO,EACLC,EAAU,MAAM,KAAK,CAAE,OAAQ,KAAK,IAAIJ,EAAOvB,EAAM,MAAM,CAAE,EAAG,SAAY,CAChF,KAAO0B,EAAO1B,EAAM,QAAQ,CAC1B,IAAM4B,EAAIF,IACVD,EAAQG,CAAC,EAAI,MAAMJ,EAAGxB,EAAM4B,CAAC,CAAM,CACrC,CACF,CAAC,EACD,aAAM,QAAQ,IAAID,CAAO,EAClBF,CACT,CA3NA,IA6BMI,GACAjC,GA+LCpC,GA7NPsE,GAAAC,EAAA,kBAeAC,IACAC,IACAC,IACAC,IACAL,KAUMD,GAAuB,CAAC,OAAQ,OAAQ,OAAQ,MAAO,KAAK,EAC5DjC,GAAQ,gBA+LPpC,GAAQK,GAAc,CAC3B,KAAM,CACJ,KAAM,WACN,YACE,4HACJ,EACA,KAAM,CACJ,MAAO,CACL,KAAM,aACN,YAAa,gDACb,SAAU,EACZ,EACA,OAAQ,CACN,KAAM,SACN,YAAa,sDACb,QAAS,MACX,EACA,YAAa,CAAE,KAAM,SAAU,YAAa,wDAAyD,EACrG,aAAc,CAAE,KAAM,SAAU,YAAa,yDAA0D,EACvG,QAAS,CACP,KAAM,SACN,YAAa,2EACb,QAAS,IACX,EACA,gBAAiB,CACf,KAAM,UACN,YAAa,yDACb,QAAS,EACX,EACA,UAAW,CAAE,KAAM,SAAU,YAAa,yEAA0E,EACpH,WAAY,CACV,KAAM,UACN,YAAa,iGACb,QAAS,EACX,EACA,UAAW,CAAE,KAAM,UAAW,YAAa,2BAA4B,MAAO,IAAK,QAAS,EAAM,EAClG,UAAW,CAAE,KAAM,UAAW,YAAa,sDAAuD,QAAS,EAAM,EACjH,KAAM,CACJ,KAAM,UACN,YACE,8HACF,QAAS,EACX,EACA,KAAM,CAAE,KAAM,UAAW,YAAa,yCAA0C,QAAS,EAAM,CACjG,EACA,MAAM,IAAI,CAAE,KAAAuE,CAAK,EAAG,CAClB,IAAMhC,EAAS,OAAOgC,EAAK,MAAM,EACjC,GAAI,CAACP,GAAQ,SAASzB,CAAM,EAC1B,MAAM,IAAIlB,EAAS,4BAA4B2C,GAAQ,KAAK,IAAI,CAAC,GAAG,EAEtE,IAAMQ,EAAU3E,GAAiB,YAAa0E,EAAK,OAAO,GAAK,GAC/D,GAAIC,EAAU,IAAK,MAAM,IAAInD,EAAS,qDAAqD,EAC3F,IAAMoD,EAAW5E,GAAiB,cAAe0E,EAAK,WAAW,CAAC,EAC5DG,EAAY7E,GAAiB,eAAgB0E,EAAK,YAAY,CAAC,EAE/DrC,EAASqC,EAAK,SAAS,EAAI,OAAOA,EAAK,SAAS,CAAC,EAAI,OAC3D,GAAIA,EAAK,UAAU,GAAKrC,EAAQ,MAAM,IAAIb,EAAS,+CAA+C,EAClG,GAAIkD,EAAK,UAAU,GAAKhC,IAAW,OACjC,MAAM,IAAIlB,EAAS,8FAA8F,EAGnH,IAAMsD,EAAS,EAAQJ,EAAK,SAAS,EAC/BK,EAAO,EAAQL,EAAK,KAC1B,GAAIK,GAAQD,EAAQ,MAAM,IAAItD,EAAS,4DAA4D,EACnG,IAAMwD,EAASD,EAAOE,EAAc,EAAI,OAElC9C,EAAQuC,EAAK,EAAE,OAASA,EAAK,EAAE,IAAI,MAAM,EAAI,CAAC,OAAOA,EAAK,KAAK,CAAC,EAChEpC,EAAQzC,GAAasC,EAAO,EAAQuC,EAAK,UAAYrC,CAAM,EACjE,GAAIC,EAAM,SAAW,EAAG,MAAM,IAAId,EAAS,mDAAmD,EAE9F,IAAMmB,EAAa,CACjB,OAAAN,EACA,QAAS,EAAQqC,EAAK,UAAU,EAEhC,YAAa,CAACK,CAChB,EACMhC,EAAQT,EAAM,IAAKG,GAASxC,GAAWwC,EAAMC,EAAQC,CAAI,CAAC,EAChE/C,GAAgBmD,EAAOJ,CAAI,EAE3B,IAAMuC,EAAQ,MAAMC,GAAU,EACxBC,EAAwB,CAAE,OAAA1C,EAAQ,SAAAkC,EAAU,UAAAC,EAAW,QAAAF,EAAS,aAAc,EAAQD,EAAK,eAAe,CAAG,EAE7GW,EAAcN,EAAO,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG1E,GAAqB,CAAC,CAAC,EAExEiF,EAAO,MAAM1B,GAAKb,EAAOsC,EAAa,MAAOnC,GAAuB,CACxE,GAAM,CAAE,KAAAT,CAAK,EAAIS,EACXqC,GAAWtE,EAAQwB,EAAK,IAAI,EAAE,MAAM,CAAC,EAAE,YAAY,EACnD+C,EAAW,CACf,MAAO/C,EAAK,KACZ,OAAQ,KACR,OAAQ,QACR,YAAa,EACb,aAAc,EACd,cAAe,CACjB,EACIgD,EACJ,GAAI,CAEF,GADa9E,GAAS8B,EAAK,IAAI,EAAE,KACtBiD,GAAiB,MAAM,IAAI,MAAM,gBAAgBA,GAAkB,KAAO,IAAI,KAAK,EAC9FD,EAAQnF,GAAamC,EAAK,IAAI,EAC9B,IAAMkD,EAAM,MAAMC,GAAeV,EAAOO,EAAOF,GAAUH,CAAI,EACvDS,EAAYF,EAAI,SAAW,aAAeA,EAAI,OAASA,EAAI,OAAS,KAO1E,GANAH,EAAI,OAASG,EAAI,OACjBH,EAAI,OAASG,EAAI,OACjBH,EAAI,YAAcG,EAAI,WACtBH,EAAI,aAAeG,EAAI,YACvBH,EAAI,cAAgBM,GAAaH,EAAI,WAAYA,EAAI,WAAW,EAE5D,CAACb,EACH,GAAIe,GAAa3C,EAAK,UAAW,CAC/B,IAAM6C,EAAWpD,EAAK,QAAUhC,GAAS8B,EAAK,IAAI,EAAE,KAAO,IAAQ,OACnEvC,GAAYgD,EAAK,UAAW2C,EAAWE,CAAQ,EAC/CP,EAAI,OAAStC,EAAK,SACpB,KAAW,CAAC2C,GAAa3C,EAAK,MAAQ9B,EAAQ8B,EAAK,IAAI,IAAM9B,EAAQqB,EAAK,IAAI,IAE5EvC,GAAYgD,EAAK,KAAMuC,CAAK,EAC5BD,EAAI,OAAStC,EAAK,MAItB,GAAI6B,GAAQC,EAEV,GAAI,CAEF,IAAMgB,EAAQH,GAAaJ,EACrBpE,GAAOwE,EAAY,GAAG9E,EAAS0B,EAAK,KAAMxB,EAAQwB,EAAK,IAAI,CAAC,CAAC,IAAIkD,EAAI,GAAG,GAAK5E,EAAS0B,EAAK,IAAI,EAC/FwD,GAAW,IAAI,SACrBA,GAAS,OAAO,OAAQ,IAAI,KAAK,CAAC,IAAI,WAAWD,CAAK,CAAC,EAAG3E,EAAI,CAAC,EAC/D,IAAM6E,GAAK,MAAMC,EAA2B,CAC1C,OAAQ,OACR,KAAM,aACN,KAAMnB,EACN,SAAAiB,GAEA,QAAS,CAAE,kBAAmB7F,GAAW,QAAQ,EAAE,OAAO4F,CAAK,EAAE,OAAO,KAAK,CAAE,CACjF,CAAC,EACDR,EAAI,IAAMU,GAAG,KAAK,eAAiBA,GAAG,KAAK,GAC7C,OAASvC,EAAK,CACZ6B,EAAI,aAAe7B,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CACpE,CAEJ,OAASA,EAAK,CACZ6B,EAAI,OAAS,QACbA,EAAI,OAAS7B,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAC9D,CACA,OAAO6B,CACT,CAAC,EAEGY,EAAc,EACdC,GAAa,EACbC,EAAW,EACf,QAAWC,KAAKjB,EAAM,CACpB,GAAIiB,EAAE,SAAW,QAAS,CACxBD,IACAE,EAAO,UAAUD,EAAE,KAAK,KAAKA,EAAE,MAAM,EAAE,EACvC,QACF,CAOA,GANIA,EAAE,eACJD,IACAE,EAAO,qBAAqBD,EAAE,KAAK,KAAKA,EAAE,YAAY,EAAE,GAE1DH,GAAeG,EAAE,YACjBF,IAAcE,EAAE,aACZA,EAAE,SAAW,UACfC,EAAO,WAAWD,EAAE,KAAK,KAAKA,EAAE,MAAM,EAAE,MACnC,CACL,IAAME,EAAQF,EAAE,OAAS,WAAMA,EAAE,MAAM,GAAKA,EAAE,IAAM,WAAMA,EAAE,GAAG,GAAK,GACpEC,EACE,GAAG1B,EAAS,aAAe,WAAW,IAAIyB,EAAE,KAAK,GAAGE,CAAK,KAAKC,EAAYH,EAAE,WAAW,CAAC,WAAMG,EAAYH,EAAE,YAAY,CAAC,MAAMA,EAAE,aAAa,IAChJ,CACF,CACF,CAMA,GALIjB,EAAK,OAAS,IAChBkB,EAAO,UAAUE,EAAYN,CAAW,CAAC,WAAMM,EAAYL,EAAU,CAAC,MAAMP,GAAaM,EAAaC,EAAU,CAAC,IAAI,EACjHC,GAAUE,EAAO,GAAGF,CAAQ,OAAOhB,EAAK,MAAM,UAAU,GAG1DZ,EAAK,KACPiC,EAAQrB,CAAI,MAGZ,SAAWiB,KAAKjB,EAAM,CACpB,GAAIiB,EAAE,SAAW,QAAS,SAC1B,IAAMK,EAAOL,EAAE,KAAOA,EAAE,SAAWA,EAAE,SAAW,UAAYA,EAAE,MAAQ,MAClEK,GAAMC,EAAOD,CAAI,CACvB,CAIEN,IAAU,QAAQ,SAAW,EACnC,CACF,CAAC,IC5ZD,IAAAQ,GAAA,GAAAC,EAAAD,GAAA,oBAAAE,GAAA,YAAAC,KAAA,OAAS,iBAAAC,OAAqB,QAC9B,OAAS,iBAAAC,OAAqB,KA6BvB,SAASH,GAAeI,EAAaC,EAA4B,CACtE,GAAI,CAACA,EAAW,OAAOD,EACvB,IAAME,EAAMD,EAAU,KAAK,EAAE,QAAQ,SAAU,EAAE,EACjD,GAAI,CAACC,EAAK,OAAOF,EACjB,IAAMG,EAAS,IAAI,gBAAgBD,CAAG,EAAE,SAAS,EACjD,OAAKC,EACEH,EAAI,SAAS,GAAG,EAAI,GAAGA,CAAG,IAAIG,CAAM,GAAK,GAAGH,CAAG,IAAIG,CAAM,GAD5CH,CAEtB,CArCA,IAuCOH,GAvCPO,GAAAC,EAAA,kBAEAC,IACAC,IACAC,IACAC,IAkCOZ,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,MACN,YACE,kFACJ,EACA,KAAM,CACJ,GAAI,CACF,KAAM,aACN,YAAa,6BACb,SAAU,EACZ,EACA,OAAQ,CACN,KAAM,SACN,YAAa,uCACb,MAAO,GACT,EACA,UAAW,CACT,KAAM,SACN,YACE,8FACF,MAAO,GACT,EACA,KAAM,CACJ,KAAM,UACN,YAAa,4BACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAY,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EAEvBC,EAAM,MAAMC,EAAwB,CACxC,KAAM,cAAc,mBAAmBJ,EAAK,EAAE,CAAC,GAC/C,KAAMC,CACR,CAAC,EAEKX,EAAMJ,GAAeiB,EAAI,KAAK,IAAKH,EAAK,SAA+B,EAG7E,GAAIA,EAAK,OAAQ,CACf,IAAMK,EAASL,EAAK,OAChBM,EACJ,GAAI,CACF,IAAMC,EAAK,MAAM,MAAMjB,CAAG,EAC1B,GAAI,CAACiB,EAAG,GACN,MAAM,IAAIC,EAAS,kCAAkCD,EAAG,MAAM,GAAI,CAAC,EAErED,EAAQ,MAAMC,EAAG,YAAY,CAC/B,OAASE,EAAK,CACZ,MAAIA,aAAeD,EAAgBC,EAC7B,IAAID,EACR,6BAA6BC,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,GAC7E,CACF,CACF,CAGA,GAAIH,EAAM,aAAe,EACvB,MAAM,IAAIE,EAAS,4DAAwD,CAAC,EAE9E,GAAI,CACFnB,GAAcgB,EAAQ,OAAO,KAAKC,CAAK,CAAC,CAC1C,OAASG,EAAK,CACZ,MAAM,IAAID,EACR,kBAAkBH,CAAM,KAAKI,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,GAC7E,CACF,CACF,CACAC,EAAO,SAASL,CAAM,EAAE,EAGpBL,EAAK,KACPW,EACEX,EAAK,UAAY,CAAE,GAAGG,EAAK,gBAAiBb,EAAK,OAAAe,CAAO,EAAI,CAAE,GAAGF,EAAK,OAAAE,CAAO,CAC/E,EAEAO,EAAOtB,CAAG,EAEZ,MACF,CAEIU,EAAK,KAGPW,EAAQX,EAAK,UAAY,CAAE,GAAGG,EAAK,gBAAiBb,CAAI,EAAIa,CAAG,EAE/DS,EAAOtB,CAAG,CAEd,CACF,CAAC,ICjID,IAAAuB,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IAWOD,GAXPE,GAAAC,EAAA,kBACAC,IACAC,IACAC,IACAC,IACAC,IAMOR,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,OACN,YACE,oGACJ,EACA,KAAM,CACJ,GAAI,CACF,KAAM,aACN,YAAa,6BACb,SAAU,EACZ,EACA,KAAM,CACJ,KAAM,SACN,YAAa,wEACf,EACA,SAAU,CACR,KAAM,SACN,YAAa,mEACf,EACA,MAAO,CACL,KAAM,UACN,YAAa,qCACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAQ,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EACvBC,EAAWC,EAAmB,CAAE,KAAMJ,EAAK,KAAM,SAAUA,EAAK,QAAS,CAAC,EAEhF,GAAIA,EAAK,OAASG,EAChB,MAAM,IAAIE,EAAS,oDAAoD,EAEzE,GAAI,CAACL,EAAK,OAAS,CAACG,EAClB,MAAM,IAAIE,EAAS,yEAAyE,EAG9F,IAAMC,EAAM,MAAMC,EAA0B,CAC1C,OAAQ,QACR,KAAM,cAAc,mBAAmB,OAAOP,EAAK,EAAE,CAAC,CAAC,GACvD,KAAMC,EACN,KAAM,CAAE,SAAUD,EAAK,MAAQ,KAAOG,CAAS,CACjD,CAAC,EAEDK,EAAQF,EAAI,KAAK,QAAQ,CAC3B,CACF,CAAC,ICzDD,IAAAG,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IAKOD,GALPE,GAAAC,EAAA,kBACAC,IACAC,IAGOL,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,SACN,YAAa,uBACf,EACA,KAAM,CACJ,GAAI,CACF,KAAM,aACN,YAAa,6BACb,SAAU,EACZ,CACF,EACA,MAAM,IAAI,CAAE,KAAAK,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EAE7B,MAAMC,EAAiC,CACrC,OAAQ,SACR,KAAM,cAAcH,EAAK,EAAE,GAC3B,KAAMC,CACR,CAAC,CAGH,CACF,CAAC,IC5BD,IAAAG,GAAA,GAAAC,EAAAD,GAAA,aAAAE,GAAA,sBAAAC,KAAA,OAAS,iBAAAC,OAAqB,QAC9B,OAAS,iBAAAC,GAAe,aAAAC,OAAiB,KACzC,OAAS,gBAAAC,OAAoB,gBA0BtB,SAASJ,GAAkBK,EAAeC,EAAsB,CACrE,OAAOA,GAAOA,EAAI,OAAS,EAAIA,EAAM,qBAAqBD,CAAK,MACjE,CA9BA,IAgCME,GAECR,GAlCPS,GAAAC,EAAA,kBAGAC,IACAC,IACAC,IACAC,IA0BMN,GAASO,GAAe,IAAI,QAASC,GAAY,WAAWA,EAASD,CAAE,CAAC,EAEvEf,GAAQE,GAAc,CAC3B,KAAM,CACJ,KAAM,SACN,YACE,mIACJ,EACA,KAAM,CACJ,OAAQ,CACN,KAAM,SACN,YAAa,wEACb,MAAO,GACT,EACA,QAAS,CACP,KAAM,UACN,YAAa,uEACb,QAAS,EACX,EACA,QAAS,CACP,KAAM,SACN,YAAa,8DACb,QAAS,KACX,EACA,KAAM,CACJ,KAAM,UACN,YAAa,sCACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAe,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EACvBC,GAAa,OAAOH,EAAK,OAAO,GAAK,KAAO,IAG9CI,EACJ,GAAI,CACFA,EAAQ,MAAMC,EAA0B,CAAE,OAAQ,OAAQ,KAAM,aAAc,KAAMJ,CAAO,CAAC,CAC9F,OAASK,EAAK,CAEZ,MAAIA,aAAeC,GAAY,eAAe,KAAKD,EAAI,OAAO,EACtD,IAAIC,EACR,4FACA,CACF,EAEID,CACR,CACAE,EAAO,yBAAoBJ,EAAM,WAAW,kBAAkBA,EAAM,MAAM,EAAE,EAG5E,IAAMK,EAAW,KAAK,IAAI,EAAIN,EAC1BO,EACAC,EAAQ,IACZ,KACED,EAAS,MAAML,EAA2B,CACxC,KAAM,mBAAmB,mBAAmBD,EAAM,MAAM,CAAC,GACzD,KAAMH,CACR,CAAC,EACGS,EAAO,SAAW,YALf,CAMP,GAAIA,EAAO,SAAW,SACpB,MAAM,IAAIH,EAAS,kBAAkBG,EAAO,OAAS,eAAe,GAAI,CAAC,EAE3E,GAAIA,EAAO,SAAW,UACpB,MAAM,IAAIH,EAAS,iEAAkE,CAAC,EAExF,GAAI,KAAK,IAAI,EAAIE,EACf,MAAM,IAAIF,EACR,mBAAmBP,EAAK,OAAO,gBAAgBU,EAAO,MAAM,oEAC5D,CACF,EAEFF,EAAO,KAAKE,EAAO,MAAM,UAAKA,EAAO,eAAe,IAAIA,EAAO,WAAW,EAAE,EAC5E,MAAMnB,GAAMoB,CAAK,EACjBA,EAAQ,KAAK,IAAI,KAAK,MAAMA,EAAQ,GAAG,EAAG,GAAI,CAChD,CAIA,IAAMrB,EAAMN,GAAkBoB,EAAM,OAAQJ,EAAK,MAA4B,EACvEY,EAAc,GAAGC,EAAU,CAAC,mBAAmB,mBAAmBT,EAAM,MAAM,CAAC,YACjFU,EACJ,GAAI,CACF,IAAMC,EAAK,MAAM,MAAMH,EAAa,CAAE,QAAS,CAAE,cAAe,UAAUX,CAAM,EAAG,CAAE,CAAC,EACtF,GAAI,CAACc,EAAG,GAAI,MAAM,IAAIR,EAAS,mCAAmCQ,EAAG,MAAM,GAAI,CAAC,EAChFD,EAAQ,MAAMC,EAAG,YAAY,CAC/B,OAAST,EAAK,CACZ,MAAIA,aAAeC,EAAgBD,EAC7B,IAAIC,EACR,8BAA8BD,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,GAC9E,CACF,CACF,CAEA,GAAIQ,EAAM,aAAe,EACvB,MAAM,IAAIP,EAAS,gEAA4D,CAAC,EAElF,GAAI,CACFrB,GAAcI,EAAK,OAAO,KAAKwB,CAAK,CAAC,CACvC,OAASR,EAAK,CACZ,MAAM,IAAIC,EACR,kBAAkBjB,CAAG,KAAKgB,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,GAC1E,CACF,CACF,CAIA,GAHAE,EAAO,SAASlB,CAAG,WAAMoB,EAAO,WAAW,cAAcI,EAAM,UAAU,QAAQ,EAG7Ed,EAAK,QAAS,CAChB,IAAMgB,EAAM1B,EAAI,QAAQ,SAAU,EAAE,GAAK,GAAGA,CAAG,aAC/C,GAAI,CACFH,GAAU6B,EAAK,CAAE,UAAW,EAAK,CAAC,EAClC5B,GAAa,MAAO,CAAC,MAAOE,EAAK,KAAM0B,CAAG,EAAG,CAAE,MAAO,QAAS,CAAC,EAChER,EAAO,kBAAkBQ,CAAG,GAAG,CACjC,OAASV,EAAK,CACZ,MAAM,IAAIC,EACR,SAASjB,CAAG,oDAAoDgB,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,GAChH,CACF,CACF,CACF,CAEIN,EAAK,KACPiB,EAAQ,CAAE,GAAGP,EAAQ,OAAQpB,CAAI,CAAC,EAElC4B,EAAO5B,CAAG,CAEd,CACF,CAAC,IChKD,IAAA6B,GAAA,GAAAC,EAAAD,GAAA,aAAAE,GAAA,gBAAAC,KAAA,OAAS,iBAAAC,OAAqB,QA6BvB,SAASD,GAAYE,EAAwB,CAClD,MAAO,CACL,cAAcA,EAAE,IAAI,GACpB,cAAcA,EAAE,MAAM,YAAYA,EAAE,SAAS,IAC7C,cAAcC,EAAYD,EAAE,MAAM,aAAa,CAAC,WAAWE,GAAMF,EAAE,OAAO,aAAa,CAAC,YAAYE,GAAMF,EAAE,UAAU,aAAa,CAAC,aACpI,cAAcA,EAAE,MAAM,OAAO,WAAWG,GAAMH,EAAE,OAAO,iBAAiB,CAAC,YAAYG,GAAMH,EAAE,UAAU,OAAO,CAAC,aAC/G,cAAcE,GAAMF,EAAE,OAAO,yBAAyB,CAAC,wCACvD,GAAIA,EAAE,WACF,CACE,cAAcA,EAAE,WAAW,YAAY,mBAAmBA,EAAE,WAAW,aAAa,YAAYA,EAAE,WAAW,iBAAiB,YAChI,EACA,CAAC,CACP,CACF,CA1CA,IAwBME,GAEAC,GAkBCN,GA5CPO,GAAAC,EAAA,kBACAC,IACAC,IACAC,IAqBMN,GAASO,GACbA,GAAK,KAAO,YAAcR,EAAYQ,CAAC,EACnCN,GAASM,GACbA,GAAK,KAAO,YAAc,OAAOA,CAAC,EAiB7BZ,GAAQE,GAAc,CAC3B,KAAM,CACJ,KAAM,QACN,YAAa,yDACf,EACA,KAAM,CACJ,KAAM,CACJ,KAAM,UACN,YAAa,kCACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAW,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EACvBC,EAAM,MAAMC,EAAgC,CAChD,KAAM,YACN,KAAMH,CACR,CAAC,EAED,GAAID,EAAK,KACPK,EAAQF,EAAI,IAAI,MAEhB,SAAWG,KAAQlB,GAAYe,EAAI,IAAI,EAAGI,EAAOD,CAAI,CAEzD,CACF,CAAC,ICrED,IAAAE,GAAA,GAAAC,EAAAD,GAAA,aAAAE,GAAA,oBAAAC,KAAA,OAAS,iBAAAC,OAAqB,QAmBvB,SAASD,GAAgBE,EAAqB,CACnD,GAAIA,aAAeC,EAAiB,CAClC,OAAQD,EAAI,KAAM,CAChB,IAAK,oBACH,MAAM,IAAIE,EACR,qGACA,CACF,EACF,IAAK,sBAAuB,CAC1B,IAAMC,EAAOH,EAAI,WACb,gBAAgBA,EAAI,UAAU,YAC9B,GACJ,MAAM,IAAIE,EACR,6DAA6DC,CAAI,GACjE,CACF,CACF,CACA,IAAK,4BACH,MAAM,IAAID,EACR,oJACA,CACF,EACF,IAAK,YACH,MAAM,IAAIA,EACR,4FACA,CACF,EACF,IAAK,mBACH,MAAM,IAAIA,EAAS,oBAAoBF,EAAI,OAAO,GAAI,CAAC,EACzD,IAAK,YACH,MAAM,IAAIE,EACR,oFACA,CACF,CACJ,CACA,GAAIF,EAAI,SAAW,IACjB,MAAM,IAAIE,EACR,oFACA,CACF,CAEJ,CACA,MAAMF,CACR,CA9DA,IAgEMI,GAwCAC,GAqDAC,GAkCCT,GA/LPU,GAAAC,EAAA,kBACAC,IACAC,IACAC,IACAC,IA4DMR,GAAOL,GAAc,CACzB,KAAM,CAAE,KAAM,OAAQ,YAAa,6BAA8B,EACjE,KAAM,CACJ,KAAM,CAAE,KAAM,UAAW,YAAa,iBAAkB,QAAS,EAAM,CACzE,EACA,MAAM,IAAI,CAAE,KAAAc,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EACzBC,EACJ,GAAI,CACFA,EAAM,MAAMC,EAAgC,CAC1C,KAAM,WACN,KAAMH,CACR,CAAC,CACH,OAASd,EAAK,CACZF,GAAgBE,CAAG,CACrB,CAEA,GAAIa,EAAK,KAAM,CACbK,EAAQF,EAAI,IAAI,EAChB,MACF,CACA,QAAWG,KAAKH,EAAI,KAClBI,EACE,CACED,EAAE,GACFA,EAAE,KACFA,EAAE,YACF,GAAGA,EAAE,UAAU,MACfA,EAAE,OAAS,IACX,WAAWA,EAAE,UAAU,GACvB,aAAaA,EAAE,cAAgB,OAAO,GACtCA,EAAE,QAAU,YAAc,EAC5B,EACG,OAAO,OAAO,EACd,KAAK,IAAI,CACd,CAEJ,CACF,CAAC,EAEKd,GAASN,GAAc,CAC3B,KAAM,CACJ,KAAM,SACN,YAAa,2DACf,EACA,KAAM,CACJ,MAAO,CAAE,KAAM,SAAU,YAAa,kCAAmC,EACzE,IAAK,CACH,KAAM,SACN,YAAa,2CACf,EACA,KAAM,CACJ,KAAM,UACN,YAAa,sCACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAc,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EAC7B,GAAIF,EAAK,KAAOA,EAAK,MAAQ,QAAUA,EAAK,MAAQ,OAClD,MAAM,IAAIX,EAAS,kCAAmC,CAAC,EAEzD,GAAIW,EAAK,OAASA,EAAK,MAAM,OAAS,GACpC,MAAM,IAAIX,EAAS,0CAA2C,CAAC,EAGjE,IAAMmB,EAAiD,CAAC,EACpDR,EAAK,QAAOQ,EAAK,MAAQR,EAAK,OAC9BA,EAAK,MAAKQ,EAAK,YAAcR,EAAK,KAEtC,IAAIG,EACJ,GAAI,CACFA,EAAM,MAAMC,EAAsD,CAChE,OAAQ,OACR,KAAM,WACN,KAAMH,EACN,KAAAO,CACF,CAAC,CACH,OAASrB,EAAK,CACZF,GAAgBE,CAAG,CACrB,CAEAsB,EACE,yGACF,EACIT,EAAK,KACPK,EAAQF,EAAI,IAAI,EAEhBI,EAAOJ,EAAI,KAAK,OAAO,CAE3B,CACF,CAAC,EAEKV,GAASP,GAAc,CAC3B,KAAM,CACJ,KAAM,SACN,YAAa,oDACf,EACA,KAAM,CACJ,GAAI,CACF,KAAM,aACN,YAAa,sCACb,SAAU,EACZ,EACA,KAAM,CAAE,KAAM,UAAW,YAAa,iBAAkB,QAAS,EAAM,CACzE,EACA,MAAM,IAAI,CAAE,KAAAc,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EACzBC,EACJ,GAAI,CACFA,EAAM,MAAMC,EAA2C,CACrD,OAAQ,SACR,KAAM,YAAY,mBAAmBJ,EAAK,EAAE,CAAC,GAC7C,KAAMC,CACR,CAAC,CACH,OAASd,EAAK,CACZF,GAAgBE,CAAG,CACrB,CAEIa,EAAK,KACPK,EAAQF,EAAI,IAAI,EAEhBM,EAAO,eAAeT,EAAK,EAAE,GAAG,CAEpC,CACF,CAAC,EAEMhB,GAAQE,GAAc,CAC3B,KAAM,CACJ,KAAM,OACN,YAAa,wCACf,EACA,YAAa,CAAE,KAAAK,GAAM,OAAAC,GAAQ,OAAAC,EAAO,CACtC,CAAC,ICrMD,IAAAiB,GAAA,GAAAC,EAAAD,GAAA,aAAAE,GAAA,wBAAAC,KAAA,OAAS,iBAAAC,OAAqB,QAOvB,SAASD,GAAoBE,EAAqB,CACvD,GAAIA,aAAeC,EACjB,OAAQD,EAAI,KAAM,CAChB,IAAK,qBACH,MAAM,IAAIE,EACR,6FACA,CACF,EACF,IAAK,4BACH,MAAM,IAAIA,EACR,mFACA,CACF,EACF,IAAK,qBACH,MAAM,IAAIA,EACR,sEACA,CACF,EACF,IAAK,qBACH,MAAM,IAAIA,EACR,kFACA,CACF,EACF,IAAK,kBACH,MAAM,IAAIA,EACR,8FACA,CACF,EACF,IAAK,sBACH,MAAM,IAAIA,EAAS,2DAA4D,CAAC,EAClF,IAAK,sBAAuB,CAC1B,IAAMC,EAAOH,EAAI,WAAa,gBAAgBA,EAAI,UAAU,YAAc,GAC1E,MAAM,IAAIE,EACR,kEAAkEC,CAAI,GACtE,CACF,CACF,CACF,CAEF,MAAMH,CACR,CA/CA,IAiDOH,GAjDPO,GAAAC,EAAA,kBACAC,IACAC,IACAC,IACAC,IA6COZ,GAAQE,GAAc,CAC3B,KAAM,CACJ,KAAM,UACN,YACE,wGACJ,EACA,KAAM,CACJ,KAAM,CACJ,KAAM,UACN,YAAa,yBACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAW,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAc,EACzBC,EACJ,GAAI,CACFA,EAAM,MAAMC,EAA4B,CACtC,OAAQ,OACR,KAAM,qBACN,KAAM,CAAC,EACP,KAAMH,CACR,CAAC,CACH,OAASX,EAAK,CACZF,GAAoBE,CAAG,CACzB,CAEIU,EAAK,KACPK,EAAQ,CAAE,IAAKF,EAAI,GAAI,CAAC,GAExBG,EAAOH,EAAI,GAAG,EACdI,EACE,8IACF,EAEJ,CACF,CAAC,ICrFD,IAAAC,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IAOMC,GAECF,GATPG,GAAAC,EAAA,kBACAC,IACAC,IACAC,IACAC,IACAC,KAEMP,GAAQ,CAAC,UAAW,KAAK,EAExBF,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,UACN,YACE,+GACJ,EACA,KAAM,CACJ,KAAM,CACJ,KAAM,aACN,YAAa,yBAAyBC,GAAM,KAAK,KAAK,CAAC,GACvD,SAAU,EACZ,EACA,KAAM,CACJ,KAAM,UACN,YAAa,yBACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAQ,CAAK,EAAG,CAClB,IAAMC,EAAO,OAAOD,EAAK,IAAI,EAC7B,GAAI,CAAER,GAA4B,SAASS,CAAI,EAC7C,MAAM,IAAIC,EAAS,iBAAiBD,CAAI,qBAAqBT,GAAM,KAAK,IAAI,CAAC,IAAK,CAAC,EAErF,IAAMW,EAASC,EAAc,EACzBC,EACJ,GAAI,CACFA,EAAM,MAAMC,EAA4B,CACtC,OAAQ,OACR,KAAM,uBACN,KAAM,CAAE,KAAAL,CAAK,EACb,KAAME,CACR,CAAC,CACH,OAASI,EAAK,CACZC,GAAoBD,CAAG,CACzB,CAEIP,EAAK,KACPS,EAAQ,CAAE,IAAKJ,EAAI,GAAI,CAAC,GAExBK,EAAOL,EAAI,GAAG,EACdM,EACE,iHACF,EAEJ,CACF,CAAC,ICtDD,IAAAC,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IAIOD,GAJPE,GAAAC,EAAA,kBACAC,IACAC,IAEOL,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,SACN,YAAa,mCACf,EACA,KAAM,CACJ,KAAM,CACJ,KAAM,UACN,YAAa,iBACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAK,CAAK,EAAG,CAClB,IAAMC,EAASC,EAAW,EACpBC,EAASC,GAAU,EAErB,CAACD,GAAU,CAACF,EAAO,QACrBI,EAAO,qEAAqE,EAC5E,QAAQ,KAAK,CAAC,GAGhB,IAAMC,EAAO,CACX,MAAOL,EAAO,OAAS,YACvB,QAASA,EAAO,iBAAmB,YACnC,QAASE,EACL,GAAGA,EAAO,MAAM,EAAG,EAAE,CAAC,MAAMA,EAAO,MAAM,EAAE,CAAC,GAC5C,YACJ,OAAQ,QAAQ,IAAI,mBAAwB,MAAQ,SAGpD,gBAAiB,QACnB,EAEA,GAAIH,EAAK,KACPO,EAAQD,CAAI,MACP,CAEL,IAAME,EAAOC,GAAgBA,EAAI,uCAAyC,GAC1EC,EAAO,YAAYJ,EAAK,KAAK,GAAGE,EAAIP,EAAO,KAAK,CAAC,EAAE,EACnDS,EAAO,YAAYJ,EAAK,OAAO,GAAGE,EAAIP,EAAO,eAAe,CAAC,EAAE,EAC/DS,EAAO,YAAYJ,EAAK,OAAO,KAAKA,EAAK,MAAM,GAAG,CACpD,CACF,CACF,CAAC,IC/CD,IAAAK,GAAA,GAAAC,EAAAD,GAAA,aAAAE,KAAA,OAAS,iBAAAC,OAAqB,QAA9B,IAIMC,GAyBAC,GAmCAC,GA6BCJ,GA7FPK,GAAAC,EAAA,kBACAD,IACAE,IAEML,GAAYD,GAAc,CAC9B,KAAM,CACJ,KAAM,MACN,YAAa,oBACf,EACA,KAAM,CACJ,IAAK,CACH,KAAM,aACN,YAAa,wDACb,SAAU,EACZ,CACF,EACA,MAAM,IAAI,CAAE,KAAAO,CAAK,EAAG,CAElB,IAAMC,EADSC,EAAW,EACLF,EAAK,GAA0B,EAEhDC,IAAU,SACZE,EAAO,QAAQH,EAAK,GAAG,eAAe,EACtC,QAAQ,KAAK,CAAC,GAGhBI,EAAO,OAAOH,CAAK,CAAC,CACtB,CACF,CAAC,EAEKN,GAAYF,GAAc,CAC9B,KAAM,CACJ,KAAM,MACN,YAAa,oBACf,EACA,KAAM,CACJ,IAAK,CACH,KAAM,aACN,YAAa,aACb,SAAU,EACZ,EACA,MAAO,CACL,KAAM,aACN,YAAa,eACb,SAAU,EACZ,CACF,EACA,MAAM,IAAI,CAAE,KAAAO,CAAK,EAAG,CAClB,IAAMK,EAAY,CAAC,UAAW,kBAAmB,QAAS,SAAS,EAC9DA,EAAU,SAASL,EAAK,GAAG,IAC9BG,EAAO,4BAA4BE,EAAU,KAAK,IAAI,CAAC,EAAE,EACzD,QAAQ,KAAK,CAAC,GAGZL,EAAK,MAAQ,WAAaA,EAAK,QAAUE,EAAW,EAAE,QAGxDI,EAAa,CAAE,QAASN,EAAK,MAAO,MAAO,OAAW,gBAAiB,MAAU,CAAC,EAElFM,EAAa,CAAE,CAACN,EAAK,GAAG,EAAGA,EAAK,KAAM,CAAC,EAEzCG,EAAO,OAAOH,EAAK,GAAG,MAAMA,EAAK,MAAQ,UAAY,MAAQA,EAAK,KAAK,EAAE,CAC3E,CACF,CAAC,EAEKJ,GAAaH,GAAc,CAC/B,KAAM,CACJ,KAAM,OACN,YAAa,wBACf,EACA,KAAM,CACJ,KAAM,CACJ,KAAM,UACN,YAAa,iBACb,QAAS,EACX,CACF,EACA,MAAM,IAAI,CAAE,KAAAO,CAAK,EAAG,CAClB,IAAMO,EAASL,EAAW,EAE1B,GAAIF,EAAK,KACPQ,EAAQD,CAAM,MACT,CACLJ,EAAO,WAAWM,GAAc,CAAC,EAAE,EACnC,OAAW,CAACC,EAAKT,CAAK,IAAK,OAAO,QAAQM,CAAM,EAAG,CACjD,IAAMI,EAAUD,IAAQ,WAAaT,EACjC,GAAIA,EAAiB,MAAM,EAAG,EAAE,CAAC,MACjCA,EACJG,EAAO,GAAGM,CAAG,MAAMC,CAAO,EAAE,CAC9B,CACF,CACF,CACF,CAAC,EAEMnB,GAAQC,GAAc,CAC3B,KAAM,CACJ,KAAM,SACN,YAAa,0BACf,EACA,YAAa,CACX,IAAKC,GACL,IAAKC,GACL,KAAMC,EACR,CACF,CAAC,ICvGD,OAAS,iBAAAgB,OAAqB,QCC9BC,IACAC,IAFA,OAAS,cAAAC,GAAY,WAAAC,OAAgC,QAcrD,eAAsBC,GACpBC,EACAC,EAAoB,QAAQ,KAAK,MAAM,CAAC,EACxCC,EAAgCC,GAAS,QAAQ,KAAKA,CAAI,EAC3C,CAKf,GAHEF,EAAQ,SAAS,QAAQ,GACzBA,EAAQ,SAAS,IAAI,GACpBA,EAAQ,SAAW,GAAKA,EAAQ,CAAC,IAAM,YAC3B,CACb,MAAMH,GAAQE,EAAM,CAAE,QAAAC,CAAQ,CAAC,EAC/B,MACF,CAEA,GAAI,CACF,MAAMJ,GAAWG,EAAM,CAAE,QAAAC,CAAQ,CAAC,CACpC,OAASG,EAAK,CACZ,GAAIA,aAAeC,EAAU,CAC3BC,EAAO,UAAUF,EAAI,OAAO,EAAE,EAC9BF,EAAKE,EAAI,QAAQ,EACjB,MACF,CAIA,GAAIA,aAAe,OAASA,EAAI,OAAS,WAAY,CACnD,MAAMN,GAAQE,EAAM,CAAE,QAAAC,CAAQ,CAAC,EAC/B,MACF,CACAK,EAAO,UAAUF,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,EAAE,EACnEF,EAAK,CAAC,CACR,CACF,CD5CAK,IAEA,IAAMC,GAAOC,GAAc,CACzB,KAAM,CACJ,KAAM,aACN,QAASC,GACT,YAAa,+DACf,EACA,YAAa,CACX,SAAU,IAAM,sCAAiC,KAAMC,GAAMA,EAAE,OAAO,EACtE,MAAO,IAAM,sCAA8B,KAAMA,GAAMA,EAAE,OAAO,EAChE,OAAQ,IAAM,sCAA+B,KAAMA,GAAMA,EAAE,OAAO,EAClE,SAAU,IAAM,sCAAiC,KAAMA,GAAMA,EAAE,OAAO,EACtE,KAAM,IAAM,sCAA6B,KAAMA,GAAMA,EAAE,OAAO,EAC9D,KAAM,IAAM,sCAA6B,KAAMA,GAAMA,EAAE,OAAO,EAC9D,SAAU,IAAM,sCAAiC,KAAMA,GAAMA,EAAE,OAAO,EACtE,SAAU,IAAM,sCAAiC,KAAMA,GAAMA,EAAE,OAAO,EACtE,IAAK,IAAM,sCAA4B,KAAMA,GAAMA,EAAE,OAAO,EAC5D,KAAM,IAAM,sCAA6B,KAAMA,GAAMA,EAAE,OAAO,EAC9D,OAAQ,IAAM,sCAA+B,KAAMA,GAAMA,EAAE,OAAO,EAClE,OAAQ,IAAM,sCAA+B,KAAMA,GAAMA,EAAE,OAAO,EAClE,MAAO,IAAM,sCAA8B,KAAMA,GAAMA,EAAE,OAAO,EAChE,KAAM,IAAM,sCAA6B,KAAMA,GAAMA,EAAE,OAAO,EAC9D,QAAS,IAAM,sCAAgC,KAAMA,GAAMA,EAAE,OAAO,EACpE,QAAS,IAAM,sCAAgC,KAAMA,GAAMA,EAAE,OAAO,EACpE,OAAQ,IAAM,sCAA+B,KAAMA,GAAMA,EAAE,OAAO,EAClE,OAAQ,IAAM,sCAA+B,KAAMA,GAAMA,EAAE,OAAO,CACpE,CACF,CAAC,EAEIC,GAAOJ,EAAI","names":["CliError","init_errors","__esmMin","message","exitCode","stdout","text","stderr","jsonOut","data","formatBytes","n","units","v","init_output","__esmMin","readFileSync","writeFileSync","mkdirSync","join","homedir","getConfigPath","CONFIG_FILE","readConfig","raw","writeConfig","config","CONFIG_DIR","updateConfig","updates","getApiKey","getApiUrl","DEFAULT_API_URL","requireApiKey","key","init_config","__esmMin","apiRequest","options","url","getApiUrl","headers","CLIENT_ID","fetchBody","res","init","err","CliError","errorBody","message","ApiRequestError","VERSION","init_client","__esmMin","init_config","init_errors","status","code","retryAfter","createInterface","prompt","question","rl","resolve","answer","promptPassword","password","onData","char","init_prompt","__esmMin","register_exports","__export","register_default","defineCommand","init_register","__esmMin","init_client","init_config","init_prompt","init_output","args","interactive","email","prompt","stderr","password","promptPassword","res","apiRequest","updateConfig","login_exports","__export","login_default","defineCommand","init_login","__esmMin","init_client","init_config","init_prompt","init_output","args","email","prompt","password","promptPassword","stderr","loginRes","apiRequest","token","projectId","rotateRes","updateConfig","readConfig","parseExpires","input","flag","match","CliError","unit","multiplier","seconds","MIN_EXPIRES_SECONDS","MAX_EXPIRES_SECONDS","init_duration","__esmMin","init_errors","validateReuseOptions","opts","idempotencyKey","dedupe","fileCount","IDEMPOTENCY_KEY","init_upload_options","__esmMin","asList","v","parseMetadataFlags","opts","out","json","CliError","parsed","k","pair","eq","key","value","init_metadata","__esmMin","init_errors","waitForDescription","id","auth","deps","sleep","ms","r","now","deadline","MAX_WAIT_MS","last","apiRequest","POLL_MS","describeSummary","d","init_describe","__esmMin","init_client","createReadStream","statSync","basename","Readable","fileBody","filePath","start","end","stream","uploadVideoFile","apiKey","stat","CliError","filename","DIRECT_MAX_BYTES","data","apiRequest","uploadId","session","partSize","partNumber","length","error","init_video_upload","__esmMin","init_client","init_errors","upload_exports","__export","upload_default","defineCommand","readFileSync","basename","extname","init_upload","__esmMin","init_client","init_config","init_duration","init_upload_options","init_metadata","init_describe","init_output","init_video_upload","args","apiKey","requireApiKey","signExpires","stderr","parseExpires","files","hasVideo","filePath","imageOnlyOption","hadError","idempotencyKey","reuseError","validateReuseOptions","metadata","parseMetadataFlags","results","video","uploadVideoFile","stdout","buffer","fileName","formData","res","apiRequest","waitForDescription","describeSummary","err","jsonOut","generate_exports","__export","generate_default","defineCommand","init_generate","__esmMin","init_client","init_config","init_errors","init_duration","init_output","args","apiKey","requireApiKey","prompt","CliError","signExpires","parseExpires","deleteAfter","stderr","res","apiRequest","g","jsonOut","stdout","list_exports","__export","list_default","defineCommand","init_list","__esmMin","init_client","init_config","init_output","init_metadata","args","apiKey","requireApiKey","params","filter","parseMetadataFlags","search","tag","res","apiRequest","jsonOut","img","stdout","tags_exports","__export","tags_default","defineCommand","init_tags","__esmMin","init_client","init_config","init_output","args","apiKey","requireApiKey","res","apiRequest","jsonOut","t","stdout","describe_exports","__export","describe_default","defineCommand","init_describe","__esmMin","init_client","init_config","init_output","init_errors","args","apiKey","requireApiKey","id","res","apiRequest","CliError","description","waitForDescription","describeSummary","jsonOut","stdout","stderr","loadSharp","major","minor","MIN_NODE_MAJOR","MIN_NODE_MINOR","CliError","mod","err","why","resolveOutput","format","detected","optimizeBuffer","sharp","input","inputExt","opts","base","skip","reason","meta","SHARP_INPUT","msg","MAX_INPUT_PIXELS","out","pipeline","data","info","swap","inW","inH","MIN_SAVING_PERCENT","savedPercent","before","after","MAX_INPUT_BYTES","SUPPORTED_EXTENSIONS","init_optimize","__esmMin","init_errors","optimize_exports","__export","assertSafePlans","collectItems","optimize_default","outputExt","parsePositiveInt","planOutput","writeAtomic","defineCommand","createHash","availableParallelism","readFileSync","writeFileSync","renameSync","unlinkSync","mkdirSync","statSync","lstatSync","readdirSync","existsSync","basename","dirname","extname","join","relative","resolve","name","value","n","CliError","isSupported","path","SUPPORTED_EXTENSIONS","walk","dir","out","skipDir","p","st","OWNED","paths","recursive","outDir","items","found","f","item","format","mode","stem","ext","rel","plans","inputs","claimed","plan","src","targets","target","isKept","own","prior","data","tmp","err","pool","limit","fn","results","next","workers","i","FORMATS","init_optimize","__esmMin","init_client","init_config","init_errors","init_output","args","quality","maxWidth","maxHeight","dryRun","host","apiKey","requireApiKey","sharp","loadSharp","opts","concurrency","rows","inputExt","row","input","MAX_INPUT_BYTES","res","optimizeBuffer","optimized","savedPercent","keepMode","bytes","formData","up","apiRequest","totalBefore","totalAfter","failures","r","stderr","where","formatBytes","jsonOut","line","stdout","get_exports","__export","applyTransform","get_default","defineCommand","writeFileSync","url","transform","raw","params","init_get","__esmMin","init_client","init_config","init_output","init_errors","args","apiKey","requireApiKey","res","apiRequest","output","bytes","dl","CliError","err","stderr","jsonOut","stdout","meta_exports","__export","meta_default","defineCommand","init_meta","__esmMin","init_client","init_config","init_metadata","init_errors","init_output","args","apiKey","requireApiKey","metadata","parseMetadataFlags","CliError","res","apiRequest","jsonOut","delete_exports","__export","delete_default","defineCommand","init_delete","__esmMin","init_client","init_config","args","apiKey","requireApiKey","apiRequest","export_exports","__export","export_default","defaultOutputPath","defineCommand","writeFileSync","mkdirSync","execFileSync","jobId","out","sleep","init_export","__esmMin","init_client","init_config","init_output","init_errors","ms","resolve","args","apiKey","requireApiKey","timeoutMs","start","apiRequest","err","CliError","stderr","deadline","status","delay","downloadUrl","getApiUrl","bytes","dl","dir","jsonOut","stdout","usage_exports","__export","usage_default","renderUsage","defineCommand","d","formatBytes","bytes","count","init_usage","__esmMin","init_client","init_config","init_output","n","args","apiKey","requireApiKey","res","apiRequest","jsonOut","line","stdout","keys_exports","__export","keys_default","explainKeyError","defineCommand","err","ApiRequestError","CliError","wait","list","create","revoke","init_keys","__esmMin","init_client","init_config","init_errors","init_output","args","apiKey","requireApiKey","res","apiRequest","jsonOut","k","stdout","body","stderr","billing_exports","__export","billing_default","explainBillingError","defineCommand","err","ApiRequestError","CliError","wait","init_billing","__esmMin","init_client","init_config","init_errors","init_output","args","apiKey","requireApiKey","res","apiRequest","jsonOut","stdout","stderr","upgrade_exports","__export","upgrade_default","defineCommand","PLANS","init_upgrade","__esmMin","init_client","init_config","init_errors","init_output","init_billing","args","plan","CliError","apiKey","requireApiKey","res","apiRequest","err","explainBillingError","jsonOut","stdout","stderr","whoami_exports","__export","whoami_default","defineCommand","init_whoami","__esmMin","init_config","init_output","args","config","readConfig","apiKey","getApiKey","stderr","info","jsonOut","tag","v","stdout","config_exports","__export","config_default","defineCommand","configGet","configSet","configShow","init_config","__esmMin","init_output","args","value","readConfig","stderr","stdout","validKeys","updateConfig","config","jsonOut","getConfigPath","key","display","defineCommand","init_errors","init_output","runCommand","runMain","runCli","main","rawArgs","exit","code","err","CliError","stderr","init_client","main","defineCommand","VERSION","m","runCli"]}
1
+ {"version":3,"sources":["../src/lib/errors.ts","../src/lib/output.ts","../src/lib/config.ts","../src/lib/client.ts","../src/lib/prompt.ts","../src/commands/register.ts","../src/commands/login.ts","../src/lib/duration.ts","../src/lib/upload-options.ts","../src/lib/metadata.ts","../src/lib/describe.ts","../src/lib/video-upload.ts","../src/commands/upload.ts","../src/commands/generate.ts","../src/commands/list.ts","../src/commands/tags.ts","../src/commands/describe.ts","../src/lib/optimize.ts","../src/commands/optimize.ts","../src/commands/get.ts","../src/commands/meta.ts","../src/commands/delete.ts","../src/commands/export.ts","../src/commands/usage.ts","../src/commands/keys.ts","../src/commands/billing.ts","../src/commands/upgrade.ts","../src/commands/whoami.ts","../src/commands/config.ts","../src/index.ts","../src/lib/run.ts"],"sourcesContent":["export class CliError extends Error {\n exitCode: number;\n\n constructor(message: string, exitCode = 1) {\n super(message);\n this.name = \"CliError\";\n this.exitCode = exitCode;\n }\n}\n","/**\n * Write to stdout (for machine-parseable output — URLs, JSON).\n */\nexport function stdout(text: string): void {\n process.stdout.write(text + \"\\n\");\n}\n\n/**\n * Write to stderr (for human messages — prompts, progress, errors).\n */\nexport function stderr(text: string): void {\n process.stderr.write(text + \"\\n\");\n}\n\n/**\n * Output data as JSON to stdout.\n */\nexport function jsonOut(data: unknown): void {\n stdout(JSON.stringify(data, null, 2));\n}\n\n/**\n * Print an error message to stderr and exit.\n */\nexport function fatal(message: string, exitCode = 1): never {\n stderr(`Error: ${message}`);\n process.exit(exitCode);\n}\n\n/**\n * Format a byte count for humans (1024-based).\n */\nexport function formatBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n const units = [\"KB\", \"MB\", \"GB\", \"TB\"];\n let v = n / 1024;\n let i = 0;\n while (v >= 1024 && i < units.length - 1) {\n v /= 1024;\n i++;\n }\n return `${v.toFixed(v >= 100 || Number.isInteger(v) ? 0 : 1)} ${units[i]}`;\n}\n","import { readFileSync, writeFileSync, mkdirSync, chmodSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport interface Config {\n api_key?: string;\n default_project?: string;\n email?: string;\n api_url?: string;\n}\n\nconst CONFIG_DIR = join(homedir(), \".pixelvault\");\nconst CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\nconst DEFAULT_API_URL = \"https://api.pixelvault.dev\";\n\nexport function getConfigPath(): string {\n return CONFIG_FILE;\n}\n\nexport function readConfig(): Config {\n try {\n const raw = readFileSync(CONFIG_FILE, \"utf-8\");\n return JSON.parse(raw) as Config;\n } catch {\n return {};\n }\n}\n\nexport function writeConfig(config: Config): void {\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });\n writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\nexport function updateConfig(updates: Partial<Config>): void {\n const config = readConfig();\n writeConfig({ ...config, ...updates });\n}\n\nexport function getApiKey(): string | undefined {\n return process.env[\"PIXELVAULT_API_KEY\"] || readConfig().api_key;\n}\n\nexport function getApiUrl(): string {\n return (\n process.env[\"PIXELVAULT_API_URL\"] ||\n readConfig().api_url ||\n DEFAULT_API_URL\n );\n}\n\nexport function requireApiKey(): string {\n const key = getApiKey();\n if (!key) {\n throw new Error(\n \"Not authenticated. Run `pixelvault register` or `pixelvault login`, or set PIXELVAULT_API_KEY.\"\n );\n }\n return key;\n}\n","import { getApiUrl } from \"./config.js\";\nimport { CliError } from \"./errors.js\";\n\ninterface RequestOptions {\n method?: string;\n path: string;\n body?: unknown;\n headers?: Record<string, string>;\n formData?: FormData;\n rawBody?: BodyInit;\n}\n\ninterface ApiError {\n error: { code: string; message: string };\n}\n\n// Identifies the CLI to the API so signups made through it can be attributed to\n// this channel (server reads X-PixelVault-Client at register/device-login — see\n// pixelvault docs/specs/lead-attribution.md). Keep the version in sync with\n// package.json on release (tests/lib/version.test.ts fails if they drift).\nexport const VERSION = \"0.17.0\";\nconst CLIENT_ID = `pixelvault-cli/${VERSION}`;\n\n/** CliError carrying the API's machine-readable error details. */\nexport class ApiRequestError extends CliError {\n status: number;\n code?: string;\n retryAfter?: string;\n\n constructor(\n message: string,\n status: number,\n code?: string,\n retryAfter?: string\n ) {\n super(message, 1);\n this.name = \"ApiError\";\n this.status = status;\n this.code = code;\n this.retryAfter = retryAfter;\n }\n}\n\nexport async function apiRequest<T>(\n options: RequestOptions & { auth?: string }\n): Promise<T> {\n const baseUrl = getApiUrl();\n const url = `${baseUrl}${options.path}`;\n\n const headers: Record<string, string> = { ...options.headers };\n\n if (!headers[\"X-PixelVault-Client\"]) {\n headers[\"X-PixelVault-Client\"] = CLIENT_ID;\n }\n\n if (options.auth) {\n headers[\"Authorization\"] = `Bearer ${options.auth}`;\n }\n\n let fetchBody: BodyInit | undefined;\n\n if (options.formData) {\n fetchBody = options.formData;\n } else if (options.rawBody !== undefined) {\n fetchBody = options.rawBody;\n } else if (options.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n fetchBody = JSON.stringify(options.body);\n }\n\n let res: Response;\n try {\n const init: RequestInit & { duplex?: \"half\" } = {\n method: options.method || \"GET\",\n headers,\n body: fetchBody,\n };\n if (fetchBody instanceof ReadableStream) init.duplex = \"half\";\n res = await fetch(url, init);\n } catch (err) {\n throw new CliError(\n `Network error: ${err instanceof Error ? err.message : \"Failed to connect\"}`,\n 1\n );\n }\n\n if (!res.ok) {\n let errorBody: ApiError | undefined;\n try {\n errorBody = (await res.json()) as ApiError;\n } catch {\n // ignore parse failures\n }\n\n const message =\n errorBody?.error?.message || `HTTP ${res.status}: ${res.statusText}`;\n throw new ApiRequestError(\n message,\n res.status,\n errorBody?.error?.code,\n res.headers?.get?.(\"Retry-After\") ?? undefined\n );\n }\n\n return (await res.json()) as T;\n}\n","import { createInterface } from \"node:readline\";\n\n/**\n * Prompt the user for input via stderr (keeps stdout clean for agents).\n */\nexport function prompt(question: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Prompt for a password (input is not echoed).\n * Uses raw mode to suppress echo when available, falls back to regular prompt.\n */\nexport async function promptPassword(question: string): Promise<string> {\n if (!process.stdin.isTTY) {\n return prompt(question);\n }\n\n return new Promise((resolve) => {\n process.stderr.write(question);\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.setEncoding(\"utf-8\");\n\n let password = \"\";\n\n const onData = (char: string) => {\n if (char === \"\\n\" || char === \"\\r\" || char === \"\\u0004\") {\n process.stdin.setRawMode(false);\n process.stdin.pause();\n process.stdin.removeListener(\"data\", onData);\n process.stderr.write(\"\\n\");\n resolve(password);\n } else if (char === \"\\u0003\") {\n // Ctrl+C\n process.stdin.setRawMode(false);\n process.exit(130);\n } else if (\n char === \"\\u007F\" ||\n char === \"\\b\"\n ) {\n // Backspace\n password = password.slice(0, -1);\n } else {\n password += char;\n }\n };\n\n process.stdin.on(\"data\", onData);\n });\n}\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { updateConfig } from \"../lib/config.js\";\nimport { prompt, promptPassword } from \"../lib/prompt.js\";\nimport { stderr } from \"../lib/output.js\";\n\ninterface RegisterResponse {\n data: {\n account_id: string;\n email: string;\n email_verified: boolean;\n // Optional: older API versions omit it. Absent → treat as \"unknown\".\n password_set?: boolean;\n plan: string;\n default_project: {\n id: string;\n name: string;\n api_keys: { live: string };\n };\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"register\",\n description: \"Create a new PixelVault account\",\n },\n args: {\n email: {\n type: \"string\",\n description: \"Email address\",\n },\n password: {\n type: \"string\",\n description: \"Password (min 8 characters). Omit for a passwordless account.\",\n },\n passwordless: {\n type: \"boolean\",\n description:\n \"Create the account without a password (for agents/automation). Set a password later via the web 'Forgot password' flow.\",\n },\n },\n async run({ args }) {\n const interactive = Boolean(process.stdin.isTTY);\n\n // Email is always required. Only prompt when we have a TTY — in a headless\n // (agent) run, missing --email is a hard error rather than a hung prompt.\n const email = args.email || (interactive ? await prompt(\"Email: \") : \"\");\n if (!email) {\n stderr(\"Email is required. Pass --email <email>.\");\n process.exit(1);\n }\n\n // Resolve the password. Precedence: explicit --password > --passwordless >\n // interactive prompt (blank = passwordless) > headless default (passwordless,\n // so a non-TTY run never blocks on a hidden prompt).\n let password: string | undefined;\n if (args.password) {\n password = args.password;\n } else if (args.passwordless) {\n password = undefined;\n } else if (interactive) {\n const entered = await promptPassword(\n \"Password (leave blank for a passwordless account): \"\n );\n password = entered || undefined;\n } else {\n password = undefined;\n }\n\n if (password !== undefined && password.length < 8) {\n stderr(\"Password must be at least 8 characters.\");\n process.exit(1);\n }\n\n stderr(\"Creating account...\");\n\n const res = await apiRequest<RegisterResponse>({\n method: \"POST\",\n path: \"/v1/auth/register\",\n // Only send the password field when one was chosen — omitting it creates\n // a passwordless account server-side. `attribution.source: \"cli\"` buckets\n // the signup to this channel (server treats it as an untrusted hint that\n // wins over the derived source — see lead-attribution spec).\n body: {\n email,\n ...(password ? { password } : {}),\n attribution: { source: \"cli\" },\n },\n });\n\n updateConfig({\n api_key: res.data.default_project.api_keys.live,\n default_project: res.data.default_project.id,\n email: res.data.email,\n });\n\n stderr(`Account created. API key saved to ~/.pixelvault/config.json`);\n stderr(`Email: ${res.data.email}`);\n stderr(`Project: ${res.data.default_project.id}`);\n\n // password_set === false (or a passwordless request on an older API) means\n // there's no dashboard login yet — tell the user how to claim one.\n const passwordless = res.data.password_set === false || !password;\n if (passwordless) {\n stderr(\n \"No password set. To enable dashboard login, use 'Forgot password' at \" +\n \"https://pixelvault.dev/forgot-password to set one.\"\n );\n }\n\n if (!res.data.email_verified) {\n stderr(\"Verify your email to lift the upload limit for the free tier.\");\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { readConfig, updateConfig, getApiUrl } from \"../lib/config.js\";\nimport { prompt, promptPassword } from \"../lib/prompt.js\";\nimport { stderr } from \"../lib/output.js\";\n\ninterface LoginResponse {\n data: {\n token: string;\n account_id: string;\n email: string;\n email_verified: boolean;\n };\n}\n\ninterface RotateKeyResponse {\n data: {\n key_id: string;\n api_key: string;\n project_id: string;\n environment: string;\n };\n}\n\ninterface ProjectsResponse {\n data: Array<{ id: string; name: string }>;\n}\n\nexport default defineCommand({\n meta: {\n name: \"login\",\n description: \"Log in to your PixelVault account\",\n },\n args: {\n email: {\n type: \"string\",\n description: \"Email address\",\n },\n password: {\n type: \"string\",\n description: \"Password\",\n },\n },\n async run({ args }) {\n const email = args.email || (await prompt(\"Email: \"));\n const password = args.password || (await promptPassword(\"Password: \"));\n\n if (!email || !password) {\n stderr(\"Email and password are required.\");\n process.exit(1);\n }\n\n stderr(\"Logging in...\");\n\n // Step 1: Login to get JWT\n const loginRes = await apiRequest<LoginResponse>({\n method: \"POST\",\n path: \"/v1/auth/login\",\n body: { email, password },\n });\n\n const token = loginRes.data.token;\n\n // Step 2: Get projects to find default project ID\n let projectId: string | undefined;\n try {\n const projectsRes = await apiRequest<ProjectsResponse>({\n method: \"GET\",\n path: \"/v1/projects\",\n auth: token,\n });\n projectId = projectsRes.data[0]?.id;\n } catch {\n // Projects endpoint may not exist yet — continue without project ID\n }\n\n // Step 3: Rotate key to get a fresh API key\n if (projectId) {\n const rotateRes = await apiRequest<RotateKeyResponse>({\n method: \"POST\",\n path: \"/v1/auth/rotate-key\",\n auth: token,\n body: { project_id: projectId },\n });\n\n updateConfig({\n api_key: rotateRes.data.api_key,\n default_project: projectId,\n email: loginRes.data.email,\n });\n\n stderr(`Logged in. API key saved to ~/.pixelvault/config.json`);\n stderr(`Email: ${loginRes.data.email}`);\n stderr(`Project: ${projectId}`);\n } else {\n // Fallback: save JWT for manual key setup\n stderr(`Logged in as ${loginRes.data.email}.`);\n stderr(\n `Could not auto-configure API key. Set PIXELVAULT_API_KEY manually.`\n );\n // Any saved key/project may be from a previous account; drop the project\n // so whoami doesn't pair it with this email, and flag the key.\n updateConfig({ email: loginRes.data.email, default_project: undefined });\n if (readConfig().api_key) {\n stderr(\"Warning: the saved api_key may belong to a previous login. Replace it with `pixelvault config set api_key <key>`.\");\n }\n }\n },\n});\n","import { CliError } from \"./errors.js\";\n\n/** Signed-URL lifetime bounds, matching the API's `sign_expires_in` (60s..30d). */\nexport const MIN_EXPIRES_SECONDS = 60;\nexport const MAX_EXPIRES_SECONDS = 30 * 24 * 60 * 60;\n\n/**\n * Parse a signed-link lifetime like `30m`, `12h`, `7d`, `90s`, or a bare number\n * of seconds, into seconds. Rejects anything outside the API's accepted range so\n * a bad value fails locally with a clear message instead of a 400 from the server.\n */\nexport function parseExpires(input: string, flag = \"--expires\"): number {\n const match = /^(\\d+)\\s*([smhd]?)$/i.exec(input.trim());\n if (!match) {\n throw new CliError(\n `Invalid ${flag} \"${input}\". Use e.g. 30m, 12h, 7d, or a number of seconds.`\n );\n }\n\n const unit = (match[2] ?? \"\").toLowerCase();\n const multiplier = unit === \"d\" ? 86400 : unit === \"h\" ? 3600 : unit === \"m\" ? 60 : 1;\n const seconds = Number(match[1] ?? \"\") * multiplier;\n\n if (seconds < MIN_EXPIRES_SECONDS || seconds > MAX_EXPIRES_SECONDS) {\n throw new CliError(\n `${flag} must be between 60s and 30d (got ${seconds}s).`\n );\n }\n\n return seconds;\n}\n","/**\n * Validation for the retry-safety / reuse flags on `upload`, done up front so a\n * bad combination fails before any file is read or sent.\n */\n\n// Mirrors the API: 1-255 printable ASCII characters, no spaces.\nconst IDEMPOTENCY_KEY = /^[\\x21-\\x7e]{1,255}$/;\n\n/** Returns an error message for an invalid flag combination, or null when fine. */\nexport function validateReuseOptions(opts: {\n idempotencyKey?: string;\n dedupe?: boolean;\n fileCount: number;\n}): string | null {\n const { idempotencyKey, dedupe, fileCount } = opts;\n if (idempotencyKey !== undefined) {\n if (!IDEMPOTENCY_KEY.test(idempotencyKey)) {\n return \"--idempotency-key must be 1-255 printable ASCII characters with no spaces.\";\n }\n if (dedupe) {\n return \"--dedupe cannot be combined with --idempotency-key. Use the key for retry-safety, or --dedupe to reuse an identical image.\";\n }\n if (fileCount > 1) {\n return \"--idempotency-key identifies a single upload, so it can't be used with multiple files. Upload them one at a time with distinct keys.\";\n }\n }\n return null;\n}\n","import { CliError } from \"./errors.js\";\n\n/** citty yields a string for one flag and an array when the flag is repeated. */\nfunction asList(v: unknown): string[] {\n if (v === undefined || v === null || v === \"\") return [];\n return (Array.isArray(v) ? v : [v]).map(String);\n}\n\n/**\n * Build a metadata object from `--meta key=value` (repeatable, string values)\n * and/or `--metadata '<json object>'` (typed values). Returns null when neither\n * flag was given. A key present in both with different values is an error.\n */\nexport function parseMetadataFlags(opts: {\n meta?: unknown;\n metadata?: unknown;\n}): Record<string, unknown> | null {\n // A Map so a key like \"__proto__\" stays an ordinary entry.\n const out = new Map<string, unknown>();\n\n const json = asList(opts.metadata);\n if (json.length > 1) throw new CliError(\"--metadata can be given only once.\");\n if (json.length === 1) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json[0] as string);\n } catch {\n throw new CliError(\"--metadata must be a JSON object, e.g. --metadata '{\\\"env\\\":\\\"prod\\\"}'.\");\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new CliError(\"--metadata must be a JSON object, e.g. --metadata '{\\\"env\\\":\\\"prod\\\"}'.\");\n }\n for (const [k, v] of Object.entries(parsed)) out.set(k, v);\n }\n\n for (const pair of asList(opts.meta)) {\n const eq = pair.indexOf(\"=\");\n if (eq <= 0) throw new CliError(`--meta expects key=value, got \"${pair}\".`);\n const key = pair.slice(0, eq);\n const value = pair.slice(eq + 1);\n if (out.has(key) && out.get(key) !== value) {\n throw new CliError(`Metadata key \"${key}\" was given with conflicting values.`);\n }\n out.set(key, value);\n }\n\n return out.size === 0 ? null : Object.fromEntries(out);\n}\n","import { apiRequest } from \"./client.js\";\n\nexport interface ImageDescription {\n status: \"pending\" | \"done\" | \"failed\";\n alt?: string;\n caption?: string;\n ocr_text?: string;\n tags?: string[];\n error?: string;\n cached?: boolean;\n}\n\ninterface GetResponse {\n data: { description?: ImageDescription | null };\n}\n\nconst POLL_MS = 2000;\nconst MAX_WAIT_MS = 40_000;\n\n/**\n * Poll GET /v1/images/:id until its AI description is `done` or `failed` (or the wait\n * runs out, returning the last state). The server reports a description that never\n * finished as `failed`/`describe_timeout`, so this never waits forever on a lost job.\n * `sleep`/`now` are injectable for tests.\n */\nexport async function waitForDescription(\n id: string,\n auth: string,\n deps: { sleep?: (ms: number) => Promise<void>; now?: () => number } = {},\n): Promise<ImageDescription | null> {\n const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));\n const now = deps.now ?? Date.now;\n const deadline = now() + MAX_WAIT_MS;\n let last: ImageDescription | null = null;\n for (;;) {\n const res = await apiRequest<GetResponse>({ path: `/v1/images/${encodeURIComponent(id)}`, auth });\n last = res.data.description ?? null;\n if (!last || last.status !== \"pending\") return last;\n if (now() >= deadline) return last;\n await sleep(POLL_MS);\n }\n}\n\n/** One human-readable stderr line summarising a finished description. */\nexport function describeSummary(d: ImageDescription | null): string {\n if (!d) return \"No description was produced.\";\n if (d.status === \"done\") return `Alt: ${d.alt ?? \"\"}${d.tags?.length ? ` (tags: ${d.tags.join(\", \")})` : \"\"}`;\n if (d.status === \"failed\") return `Description failed (${d.error ?? \"unknown\"}).`;\n return \"Description is still pending; run `pixelvault get <id> --json` in a few seconds.\";\n}\n","import { createReadStream, statSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { apiRequest } from \"./client.js\";\nimport { CliError } from \"./errors.js\";\n\nconst DIRECT_MAX_BYTES = 100_000_000;\n\nexport interface VideoData {\n id: string;\n kind: \"video\";\n url: string;\n poster_url?: string;\n visibility: \"public\" | \"private\";\n mime_type: \"video/mp4\";\n size: number;\n filename: string;\n created_at: string;\n signed_expires?: number;\n}\n\ninterface UploadSession {\n upload_id: string;\n part_size: number;\n}\n\nexport interface VideoUploadOptions {\n private?: boolean;\n signExpiresIn?: number | null;\n}\n\nfunction fileBody(filePath: string, start?: number, end?: number): ReadableStream<Uint8Array> {\n const stream = createReadStream(filePath, {\n ...(start !== undefined ? { start } : {}),\n ...(end !== undefined ? { end } : {}),\n });\n return Readable.toWeb(stream) as ReadableStream<Uint8Array>;\n}\n\nexport function isRemoteVideoUrl(value: string): boolean {\n try {\n const protocol = new URL(value).protocol;\n return protocol === \"https:\" || protocol === \"http:\";\n } catch {\n return false;\n }\n}\n\n/** Ask PixelVault to import an MP4 directly from a public URL. */\nexport async function uploadVideoUrl(\n sourceUrl: string,\n apiKey: string,\n options: VideoUploadOptions = {},\n): Promise<VideoData> {\n const { data } = await apiRequest<{ data: VideoData }>({\n method: \"POST\",\n path: \"/v1/videos\",\n auth: apiKey,\n body: {\n url: sourceUrl,\n visibility: options.private ? \"private\" : \"public\",\n ...(options.signExpiresIn ? { sign_expires_in: options.signExpiresIn } : {}),\n },\n });\n return data;\n}\n\n/** Upload one MP4 without buffering it in the CLI process. */\nexport async function uploadVideoFile(\n filePath: string,\n apiKey: string,\n options: VideoUploadOptions = {},\n): Promise<VideoData> {\n const stat = statSync(filePath);\n if (!stat.isFile() || stat.size <= 0) {\n throw new CliError(\"Video must be a non-empty file.\", 1);\n }\n\n const filename = basename(filePath);\n if (stat.size <= DIRECT_MAX_BYTES) {\n const { data } = await apiRequest<{ data: VideoData }>({\n method: \"POST\",\n path: \"/v1/videos\",\n auth: apiKey,\n rawBody: fileBody(filePath),\n headers: {\n \"Content-Type\": \"video/mp4\",\n \"Content-Length\": String(stat.size),\n \"X-PixelVault-Filename\": encodeURIComponent(filename),\n \"X-PixelVault-Visibility\": options.private ? \"private\" : \"public\",\n ...(options.signExpiresIn\n ? { \"X-PixelVault-Sign-Expires-In\": String(options.signExpiresIn) }\n : {}),\n },\n });\n return data;\n }\n\n let uploadId: string | undefined;\n try {\n const session = await apiRequest<{ data: UploadSession }>({\n method: \"POST\",\n path: \"/v1/videos/uploads\",\n auth: apiKey,\n body: {\n filename,\n size: stat.size,\n visibility: options.private ? \"private\" : \"public\",\n },\n });\n uploadId = session.data.upload_id;\n const partSize = session.data.part_size;\n if (!Number.isSafeInteger(partSize) || partSize <= 0) {\n throw new CliError(\"The API returned an invalid video part size.\", 1);\n }\n\n let partNumber = 1;\n for (let start = 0; start < stat.size; start += partSize, partNumber += 1) {\n const length = Math.min(partSize, stat.size - start);\n await apiRequest({\n method: \"PUT\",\n path: `/v1/videos/uploads/${uploadId}/parts/${partNumber}`,\n auth: apiKey,\n rawBody: fileBody(filePath, start, start + length - 1),\n headers: { \"Content-Length\": String(length) },\n });\n }\n\n const { data } = await apiRequest<{ data: VideoData }>({\n method: \"POST\",\n path: `/v1/videos/uploads/${uploadId}/complete`,\n auth: apiKey,\n ...(options.signExpiresIn\n ? { headers: { \"X-PixelVault-Sign-Expires-In\": String(options.signExpiresIn) } }\n : {}),\n });\n return data;\n } catch (error) {\n if (uploadId) {\n await apiRequest({\n method: \"DELETE\",\n path: `/v1/videos/uploads/${uploadId}`,\n auth: apiKey,\n }).catch(() => undefined);\n }\n throw error;\n }\n}\n","import { defineCommand } from \"citty\";\nimport { readFileSync } from \"node:fs\";\nimport { basename, extname } from \"node:path\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { parseExpires } from \"../lib/duration.js\";\nimport { validateReuseOptions } from \"../lib/upload-options.js\";\nimport { parseMetadataFlags } from \"../lib/metadata.js\";\nimport { waitForDescription, describeSummary, type ImageDescription } from \"../lib/describe.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { isRemoteVideoUrl, uploadVideoFile, uploadVideoUrl } from \"../lib/video-upload.js\";\n\ninterface UploadResponse {\n data: {\n id: string;\n kind?: \"image\" | \"video\";\n url: string;\n optimized_url?: string;\n poster_url?: string;\n visibility?: \"public\" | \"private\";\n mime_type: string;\n size: number;\n filename: string;\n folder?: string | null;\n created_at: string;\n signed_expires?: number;\n /** The API reused an identical existing image (--dedupe). */\n deduplicated?: boolean;\n /** The API returned the original of a repeated --idempotency-key. */\n replayed?: boolean;\n metadata?: Record<string, unknown> | null;\n /** AI description (--describe): pending right after upload, `done` once ready. */\n description?: ImageDescription | null;\n /** --describe was requested but could not start (quota, type, busy, ...). */\n describe_skipped?: string;\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"upload\",\n description: \"Upload one or more images, MP4 files, or public MP4 URLs\",\n },\n args: {\n files: {\n type: \"positional\",\n description: \"Image/MP4 file path(s), or public MP4 URL(s), to upload\",\n required: true,\n },\n folder: {\n type: \"string\",\n description: \"Folder to organize images in\",\n },\n private: {\n type: \"boolean\",\n description:\n \"Upload privately — returns a signed URL only holders of the link can open (needs a secret key)\",\n default: false,\n },\n expires: {\n type: \"string\",\n description:\n \"Lifetime of a --private signed link: e.g. 30m, 12h, 7d (default 7d; min 60s, max 30d)\",\n },\n optimized: {\n type: \"boolean\",\n description:\n \"Print the optimized URL (WebP/AVIF, usually much smaller) instead of the original; falls back to the original URL when none is returned (SVG, GIF, private)\",\n default: false,\n },\n \"idempotency-key\": {\n type: \"string\",\n description:\n \"Make retries safe: repeating the same key with the same file returns the original image instead of storing a copy (single file only)\",\n },\n dedupe: {\n type: \"boolean\",\n description:\n \"Reuse an identical image already in your project instead of storing and counting another upload\",\n default: false,\n },\n meta: {\n type: \"string\",\n description:\n \"Attach custom metadata as key=value (repeatable), e.g. --meta env=prod --meta run=42\",\n },\n metadata: {\n type: \"string\",\n description: 'Attach custom metadata as a JSON object, e.g. --metadata \\'{\"env\":\"prod\",\"n\":3}\\'',\n },\n describe: {\n type: \"boolean\",\n description:\n \"Generate an AI description (alt text, caption, OCR) in the background; needs a secret key. Free 20 in total, Starter 1,000/mo, Pro 10,000/mo. Read it with `get <id> --json`, or add --wait\",\n default: false,\n },\n wait: {\n type: \"boolean\",\n description:\n \"With --describe: wait up to ~40s for the description and include it in --json output (alt text goes to stderr otherwise)\",\n default: false,\n },\n json: {\n type: \"boolean\",\n description: \"Output full JSON response\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n // Resolve the signed-link lifetime up front so a bad value fails before any\n // upload happens. Private image and video links default to seven days.\n let signExpires: number | null = null;\n if (args.expires !== undefined && !args.private) {\n stderr(\"--expires only applies to --private uploads (it sets the signed link's lifetime).\");\n process.exit(1);\n }\n if (args.private) {\n signExpires = parseExpires(String(args.expires ?? \"7d\"));\n }\n\n if (args.private && args.folder) {\n stderr(\"Note: --folder is ignored for --private uploads.\");\n }\n\n // A citty positional binds only the first value; every positional (including\n // the first, and anything a shell glob expanded to) lands in `args._`.\n const files = args._.length ? args._.map(String) : [String(args.files)];\n\n const hasVideo = files.some(\n (source) => isRemoteVideoUrl(source) || extname(source).toLowerCase() === \".mp4\"\n );\n const imageOnlyOption =\n args.folder ||\n args.optimized ||\n args[\"idempotency-key\"] ||\n args.dedupe ||\n args.meta ||\n args.metadata ||\n args.describe ||\n args.wait;\n if (hasVideo && imageOnlyOption) {\n stderr(\n \"MP4 uploads do not support folder, optimization, reuse, metadata, or AI-description options. Use --private and --expires for signed video delivery.\"\n );\n process.exit(1);\n }\n let hadError = false;\n\n const idempotencyKey = args[\"idempotency-key\"] as string | undefined;\n const reuseError = validateReuseOptions({\n idempotencyKey,\n dedupe: args.dedupe,\n fileCount: files.length,\n });\n if (reuseError) {\n stderr(reuseError);\n process.exit(1);\n }\n\n const metadata = parseMetadataFlags({ meta: args.meta, metadata: args.metadata });\n\n if (args.wait && !args.describe) {\n stderr(\"--wait only applies together with --describe.\");\n process.exit(1);\n }\n\n const results: UploadResponse[\"data\"][] = [];\n\n for (const filePath of files) {\n try {\n if (isRemoteVideoUrl(filePath) || extname(filePath).toLowerCase() === \".mp4\") {\n const options = {\n private: Boolean(args.private),\n signExpiresIn: signExpires,\n };\n const video = isRemoteVideoUrl(filePath)\n ? await uploadVideoUrl(filePath, apiKey, options)\n : await uploadVideoFile(filePath, apiKey, options);\n if (args.private && video.visibility !== \"private\") {\n throw new Error(\"The API did not confirm private visibility; refusing to print the URL.\");\n }\n if (args.json) {\n results.push(video);\n } else {\n stdout(video.url);\n }\n continue;\n }\n\n const buffer = readFileSync(filePath);\n const fileName = basename(filePath);\n\n const formData = new FormData();\n formData.append(\n \"file\",\n new File([buffer], fileName)\n );\n\n if (args.folder) {\n formData.append(\"folder\", args.folder);\n }\n\n if (args.private) {\n // The server routes private images to a signed-URL-only path and\n // returns the ready-to-share signed URL in `data.url`. A user-supplied\n // folder is ignored for private uploads (server-side).\n formData.append(\"visibility\", \"private\");\n if (signExpires !== null) {\n formData.append(\"sign_expires_in\", String(signExpires));\n }\n }\n\n if (args.dedupe) {\n formData.append(\"dedupe\", \"true\");\n }\n\n if (metadata) {\n formData.append(\"metadata\", JSON.stringify(metadata));\n }\n\n if (args.describe) {\n formData.append(\"describe\", \"true\");\n }\n\n const res = await apiRequest<UploadResponse>({\n method: \"POST\",\n path: \"/v1/images\",\n auth: apiKey,\n formData,\n ...(idempotencyKey ? { headers: { \"Idempotency-Key\": idempotencyKey } } : {}),\n });\n\n // stderr only: stdout stays the bare URL so scripts keep working.\n if (res.data.replayed) {\n stderr(`Replayed: ${filePath} was already uploaded with this key; returning the original.`);\n } else if (res.data.deduplicated) {\n stderr(`Deduplicated: ${filePath} matches an existing image; nothing new was stored.`);\n }\n\n // Fail closed: if we asked for private but the server didn't confirm it,\n // don't hand back a URL the user would wrongly trust as private.\n if (args.private && res.data.visibility !== \"private\") {\n hadError = true;\n stderr(`Refusing to report ${filePath}: server did not confirm a private upload.`);\n continue;\n }\n\n // The upload itself never fails because of --describe; say what happened on stderr.\n if (args.describe) {\n if (res.data.describe_skipped) {\n stderr(`Description skipped for ${filePath}: ${res.data.describe_skipped}.`);\n } else if (args.wait && res.data.description?.status === \"pending\") {\n res.data.description = await waitForDescription(res.data.id, apiKey);\n if (!args.json) stderr(describeSummary(res.data.description ?? null));\n } else if (res.data.description?.status === \"pending\") {\n stderr(`Describing ${filePath} in the background; read it with: pixelvault get ${res.data.id} --json`);\n } else if (res.data.description?.status === \"done\" && !args.json) {\n stderr(describeSummary(res.data.description)); // reused from an identical image\n }\n }\n\n if (args.json) {\n results.push(res.data);\n } else {\n stdout(args.optimized ? (res.data.optimized_url ?? res.data.url) : res.data.url);\n }\n } catch (err) {\n hadError = true;\n stderr(\n `Failed to upload ${filePath}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n if (args.json && results.length > 0) {\n jsonOut(results.length === 1 ? results[0] : results);\n }\n\n if (hadError) {\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { parseExpires } from \"../lib/duration.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\ninterface GenerateResponse {\n data: {\n id: string;\n url: string;\n optimized_url?: string;\n visibility?: \"public\" | \"private\";\n mime_type: string;\n size: number;\n width?: number;\n height?: number;\n filename: string;\n folder: string | null;\n created_at: string;\n expires_in?: number;\n signed_expires?: number;\n generation: {\n model: string;\n credits_charged: number;\n credits_remaining: number;\n };\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"generate\",\n description:\n \"Generate an image from a prompt and host it (spends monthly AI credits; secret key required)\",\n },\n args: {\n prompt: {\n type: \"positional\",\n description: \"What to draw. Quote it, or pass several words\",\n required: true,\n },\n model: {\n type: \"string\",\n alias: \"m\",\n description:\n \"flux-1-schnell (default, 1 credit), sdxl-lightning or sdxl-base (1, paid plans), lucid-origin or phoenix-1.0 (20, paid plans)\",\n },\n folder: {\n type: \"string\",\n description: \"Folder to organize the image in\",\n },\n private: {\n type: \"boolean\",\n description:\n \"Keep it private: returns a signed URL only holders of the link can open\",\n default: false,\n },\n expires: {\n type: \"string\",\n description:\n \"Lifetime of a --private signed link: e.g. 30m, 12h, 7d (default 7d; min 60s, max 30d)\",\n },\n \"delete-after\": {\n type: \"string\",\n description:\n \"Delete the image automatically after this long: e.g. 1h, 7d (min 60s, max 30d)\",\n },\n optimized: {\n type: \"boolean\",\n description:\n \"Print the optimized URL (WebP/AVIF, usually much smaller) instead of the original; falls back to the original URL when none is returned\",\n default: false,\n },\n json: {\n type: \"boolean\",\n description: \"Output the full JSON response\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n // Every positional (including the first) lands in `args._`, so an unquoted\n // multi-word prompt still works.\n const prompt = (args._.length ? args._.map(String) : [String(args.prompt ?? \"\")])\n .join(\" \")\n .trim();\n if (!prompt) {\n throw new CliError('Give a prompt, e.g. pixelvault generate \"a red bicycle\".');\n }\n\n // Resolve durations up front so a bad value fails before any credit is spent.\n if (args.expires !== undefined && !args.private) {\n throw new CliError(\n \"--expires only applies to --private images (it sets the signed link's lifetime). Use --delete-after to expire the image itself.\"\n );\n }\n const signExpires =\n args.expires !== undefined ? parseExpires(String(args.expires)) : undefined;\n const deleteAfter =\n args[\"delete-after\"] !== undefined\n ? parseExpires(String(args[\"delete-after\"]), \"--delete-after\")\n : undefined;\n\n if (args.private && args.folder) {\n stderr(\"Note: --folder is ignored for --private images.\");\n }\n\n const res = await apiRequest<GenerateResponse>({\n method: \"POST\",\n path: \"/v1/images/generate\",\n auth: apiKey,\n body: {\n prompt,\n ...(args.model ? { model: String(args.model) } : {}),\n ...(args.folder ? { folder: String(args.folder) } : {}),\n ...(args.private ? { visibility: \"private\" } : {}),\n ...(signExpires !== undefined ? { sign_expires_in: signExpires } : {}),\n ...(deleteAfter !== undefined ? { expires_in: deleteAfter } : {}),\n },\n });\n\n // Fail closed: if we asked for private but the server didn't confirm it,\n // don't hand back a URL the user would wrongly trust as private.\n if (args.private && res.data.visibility !== \"private\") {\n throw new CliError(\n \"Refusing to report the image: the server did not confirm it is private.\"\n );\n }\n\n const g = res.data.generation;\n // stderr only: stdout stays the bare URL so scripts and agents can capture it.\n stderr(\n `Generated with ${g.model}: ${g.credits_charged} credit${g.credits_charged === 1 ? \"\" : \"s\"} used, ${g.credits_remaining} left this month.`\n );\n\n if (args.json) {\n jsonOut(res.data);\n } else {\n stdout(args.optimized ? (res.data.optimized_url ?? res.data.url) : res.data.url);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, jsonOut } from \"../lib/output.js\";\nimport { parseMetadataFlags } from \"../lib/metadata.js\";\n\ninterface ListResponse {\n data: Array<{\n id: string;\n url: string;\n mime_type: string;\n size: number;\n filename: string;\n folder: string | null;\n created_at: string;\n metadata?: Record<string, unknown> | null;\n description?: { status: string; alt?: string; caption?: string; ocr_text?: string; tags?: string[] } | null;\n }>;\n meta: {\n total: number;\n page: number;\n per_page: number;\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"list\",\n description: \"List uploaded images\",\n },\n args: {\n page: {\n type: \"string\",\n description: \"Page number\",\n default: \"1\",\n },\n \"per-page\": {\n type: \"string\",\n description: \"Items per page (max 100)\",\n default: \"20\",\n },\n search: {\n type: \"string\",\n description:\n 'Search the AI descriptions (alt text, caption, OCR text) of images uploaded with --describe, best match first, e.g. --search \"invoice 4821\" (quotes, -word work)',\n alias: \"q\",\n },\n tag: {\n type: \"string\",\n description: \"Only images carrying this AI tag (one tag, e.g. --tag boat); see `pixelvault tags`\",\n },\n meta: {\n type: \"string\",\n description:\n \"Only images whose metadata has key=value (repeatable; string match), e.g. --meta env=prod\",\n },\n metadata: {\n type: \"string\",\n description:\n 'Only images whose metadata contains this JSON object (typed match), e.g. --metadata \\'{\"n\":3}\\'',\n },\n json: {\n type: \"boolean\",\n description: \"Output full JSON response\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n const params = new URLSearchParams({\n page: args.page,\n per_page: args[\"per-page\"],\n });\n const filter = parseMetadataFlags({ meta: args.meta, metadata: args.metadata });\n if (filter) params.set(\"metadata\", JSON.stringify(filter));\n const search = typeof args.search === \"string\" ? args.search.trim() : \"\";\n if (search) params.set(\"q\", search);\n const tag = typeof args.tag === \"string\" ? args.tag.trim() : \"\";\n if (tag) params.set(\"tag\", tag);\n\n const res = await apiRequest<ListResponse>({\n path: `/v1/images?${params}`,\n auth: apiKey,\n });\n\n if (args.json) {\n jsonOut(res);\n } else {\n for (const img of res.data) {\n stdout(img.url);\n }\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, jsonOut } from \"../lib/output.js\";\n\ninterface TagsResponse {\n data: Array<{ tag: string; count: number }>;\n}\n\nexport default defineCommand({\n meta: {\n name: \"tags\",\n description:\n \"List the AI tags on your described images with how many images carry each (filter with `list --tag`)\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output the full JSON response\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n const res = await apiRequest<TagsResponse>({ path: \"/v1/images/tags\", auth: apiKey });\n\n if (args.json) {\n jsonOut(res);\n } else {\n for (const t of res.data) {\n stdout(`${t.tag}\\t${t.count}`);\n }\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { waitForDescription, describeSummary, type ImageDescription } from \"../lib/describe.js\";\n\ninterface DescribeResponse {\n data: {\n id: string;\n description?: ImageDescription | null;\n /** Why no description was started: quota, email_unverified, busy, unsupported_type, too_large, unavailable. */\n describe_skipped?: string;\n };\n}\n\nexport default defineCommand({\n meta: {\n name: \"describe\",\n description:\n \"Write an AI description (alt text, caption, OCR text, tags) for an image you already uploaded\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Image ID (e.g. img_abc123)\",\n required: true,\n },\n wait: {\n type: \"boolean\",\n description: \"Wait up to ~40s for the description and print it (otherwise it runs in the background)\",\n default: false,\n },\n json: {\n type: \"boolean\",\n description: \"Output JSON ({ id, description }); with --wait it includes the full description\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n const id = String(args.id);\n\n // Idempotent: an image that is already described, or in progress, costs nothing.\n const res = await apiRequest<DescribeResponse>({\n method: \"POST\",\n path: `/v1/images/${encodeURIComponent(id)}/describe`,\n auth: apiKey,\n });\n\n if (res.data.describe_skipped) {\n throw new CliError(`Description not started: ${res.data.describe_skipped}.`);\n }\n\n let description = res.data.description ?? null;\n if (args.wait && description?.status === \"pending\") {\n description = await waitForDescription(id, apiKey);\n }\n\n if (description?.status === \"failed\") {\n throw new CliError(describeSummary(description));\n }\n\n if (args.json) {\n jsonOut({ id, description });\n return;\n }\n\n if (description?.status === \"done\") {\n stdout(description.alt ?? \"\");\n if (description.tags?.length) stderr(`Tags: ${description.tags.join(\", \")}`);\n } else {\n stderr(describeSummary(description));\n }\n },\n});\n","import { CliError } from \"./errors.js\";\n\nexport type OutFormat = \"keep\" | \"webp\" | \"avif\" | \"jpg\" | \"png\";\n\nexport interface OptimizeOptions {\n format: OutFormat;\n maxWidth?: number;\n maxHeight?: number;\n /** 1-100. Applies to JPEG/WebP/AVIF output; PNG output is always lossless. */\n quality: number;\n keepMetadata: boolean;\n}\n\nexport interface OptimizeResult {\n status: \"optimized\" | \"skipped\";\n /** Why the original was kept (only when status is \"skipped\"). */\n reason?: string;\n inputBytes: number;\n outputBytes: number;\n /** Extension (no dot) of the output; equals the input's when skipped. */\n ext: string;\n width?: number;\n height?: number;\n /** The optimized bytes (only when status is \"optimized\"). */\n buffer?: Buffer;\n}\n\n// Minimal structural type for the bits of sharp we use, so tests can inject a fake\n// and the CLI still builds when the optional dependency isn't installed.\nexport type SharpFactory = (input: Buffer, options?: Record<string, unknown>) => any;\n\n/** Decode limits: refuse decompression bombs instead of allocating gigabytes. */\nexport const MAX_INPUT_PIXELS = 100 * 1000 * 1000;\nexport const MAX_INPUT_BYTES = 100 * 1024 * 1024;\nconst SHARP_INPUT = { limitInputPixels: MAX_INPUT_PIXELS, failOn: \"error\" } as const;\n\n/** Below this saving, a same-format, same-size re-encode is left alone. */\nexport const MIN_SAVING_PERCENT = 5;\n\nexport const SUPPORTED_EXTENSIONS = [\"jpg\", \"jpeg\", \"png\", \"webp\", \"avif\"] as const;\n\n/** sharp's floor (it is an optional dependency, so npm only warns on older Node). */\nconst MIN_NODE_MAJOR = 20;\nconst MIN_NODE_MINOR = 9;\n\n/**\n * Load sharp lazily. It is an optionalDependency: when its prebuilt binary is\n * missing (unusual libc, `--omit=optional`, locked-down CI) we say how to fix it\n * instead of crashing, and every other command keeps working.\n */\nexport async function loadSharp(): Promise<SharpFactory> {\n const [major = 0, minor = 0] = process.versions.node.split(\".\").map(Number);\n if (major < MIN_NODE_MAJOR || (major === MIN_NODE_MAJOR && minor < MIN_NODE_MINOR)) {\n throw new CliError(\n `optimize needs Node ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR} or newer (you have ${process.versions.node}).`\n );\n }\n try {\n const mod: any = await import(\"sharp\");\n return (mod.default ?? mod) as SharpFactory;\n } catch (err) {\n const why = err instanceof Error ? err.message.split(\"\\n\")[0] : String(err);\n throw new CliError(\n \"optimize needs the optional 'sharp' package, which couldn't be loaded for this platform.\\n\" +\n ` Reason: ${why}\\n` +\n \" Fix: reinstall pixelvault-cli without --omit=optional (npm install -g pixelvault-cli).\"\n );\n }\n}\n\n/** Map our format names to sharp's, resolving \"keep\" from the detected input format. */\nfunction resolveOutput(format: OutFormat, detected: string): { sharpFormat: string; ext: string } | null {\n const target = format === \"keep\" ? detected : format === \"jpg\" ? \"jpeg\" : format;\n switch (target) {\n case \"jpeg\":\n return { sharpFormat: \"jpeg\", ext: \"jpg\" };\n case \"png\":\n return { sharpFormat: \"png\", ext: \"png\" };\n case \"webp\":\n return { sharpFormat: \"webp\", ext: \"webp\" };\n case \"avif\":\n return { sharpFormat: \"avif\", ext: \"avif\" };\n default:\n return null;\n }\n}\n\nexport async function optimizeBuffer(\n sharp: SharpFactory,\n input: Buffer,\n inputExt: string,\n opts: OptimizeOptions\n): Promise<OptimizeResult> {\n const base = { inputBytes: input.length, outputBytes: input.length, ext: inputExt };\n const skip = (reason: string): OptimizeResult => ({ status: \"skipped\", reason, ...base });\n\n let meta: any;\n try {\n meta = await sharp(input, SHARP_INPUT).metadata();\n } catch (err) {\n // A corrupt or oversized file is a failure the caller must see (CI should not pass on it).\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(/pixel limit/i.test(msg) ? `image is over ${MAX_INPUT_PIXELS / 1e6} megapixels` : \"not a readable image\");\n }\n\n // Animated images, vector graphics and HEIC are out of scope for v1.\n if (meta.format === \"svg\") return skip(\"SVG is not supported\");\n if (meta.format === \"gif\") return skip(\"GIF is not supported\");\n if ((meta.pages ?? 1) > 1) return skip(\"animated images are not supported\");\n if (meta.format === \"heif\" && meta.compression === \"hevc\") {\n return skip(\"HEIC is not supported (convert it at https://pixelvault.dev/tools/heic-to-url/)\");\n }\n\n const detected = meta.format === \"heif\" ? \"avif\" : meta.format;\n const out = resolveOutput(opts.format, detected);\n if (!out) return skip(`unsupported format: ${meta.format}`);\n\n // Apply EXIF orientation BEFORE metadata is dropped, otherwise phone photos come out sideways.\n let pipeline = sharp(input, SHARP_INPUT).rotate();\n\n if (opts.maxWidth || opts.maxHeight) {\n pipeline = pipeline.resize({\n width: opts.maxWidth,\n height: opts.maxHeight,\n fit: \"inside\",\n withoutEnlargement: true,\n });\n }\n\n // JPEG has no alpha: flatten onto white rather than letting transparency go black.\n if (out.sharpFormat === \"jpeg\" && meta.hasAlpha) {\n pipeline = pipeline.flatten({ background: \"#ffffff\" });\n }\n\n if (opts.keepMetadata) pipeline = pipeline.keepMetadata(); // sharp strips metadata by default\n\n switch (out.sharpFormat) {\n case \"jpeg\":\n pipeline = pipeline.jpeg({ quality: opts.quality, mozjpeg: true });\n break;\n case \"png\":\n pipeline = pipeline.png({ compressionLevel: 9, effort: 10 }); // lossless\n break;\n case \"webp\":\n pipeline = pipeline.webp({ quality: opts.quality, effort: 5 });\n break;\n case \"avif\":\n pipeline = pipeline.avif({ quality: opts.quality, effort: 4 });\n break;\n }\n\n const { data, info } = await pipeline.toBuffer({ resolveWithObject: true });\n\n // Never hand back something bigger than what we were given.\n if (data.length >= input.length) {\n return skip(\"already optimal (result was not smaller)\");\n }\n // Same format and same pixel size: a sliver of saving isn't worth rewriting the file. Keeps\n // re-runs (pre-commit hooks, CI) idempotent instead of nibbling an image every time. Compared on\n // the real detected format and oriented dimensions (not just the flags), with the unrounded ratio.\n const swap = (meta.orientation ?? 1) >= 5;\n const inW = swap ? meta.height : meta.width;\n const inH = swap ? meta.width : meta.height;\n const unchanged = out.sharpFormat === detected && info.width === inW && info.height === inH;\n if (unchanged && (input.length - data.length) / input.length < MIN_SAVING_PERCENT / 100) {\n return skip(`already optimal (would save under ${MIN_SAVING_PERCENT}%)`);\n }\n return {\n status: \"optimized\",\n inputBytes: input.length,\n outputBytes: data.length,\n ext: out.ext,\n width: info.width,\n height: info.height,\n buffer: data,\n };\n}\n\n/** Percentage saved, e.g. 84 for \"-84%\". */\nexport function savedPercent(before: number, after: number): number {\n if (before <= 0) return 0;\n return Math.round(((before - after) / before) * 100);\n}\n","import { defineCommand } from \"citty\";\nimport { createHash } from \"node:crypto\";\nimport { availableParallelism } from \"node:os\";\nimport {\n readFileSync,\n writeFileSync,\n renameSync,\n unlinkSync,\n mkdirSync,\n statSync,\n lstatSync,\n readdirSync,\n existsSync,\n} from \"node:fs\";\nimport { basename, dirname, extname, join, relative, resolve, sep } from \"node:path\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { stdout, stderr, jsonOut, formatBytes } from \"../lib/output.js\";\nimport {\n loadSharp,\n optimizeBuffer,\n savedPercent,\n SUPPORTED_EXTENSIONS,\n MAX_INPUT_BYTES,\n type OptimizeOptions,\n type OutFormat,\n} from \"../lib/optimize.js\";\n\nconst FORMATS: OutFormat[] = [\"keep\", \"webp\", \"avif\", \"jpg\", \"png\"];\nconst OWNED = /\\.optimized$/i; // stem of files this command writes beside the original\n\ninterface UploadResponse {\n data: { id: string; url: string; optimized_url?: string };\n}\n\nexport interface Item {\n file: string;\n /** Directory the file was found under (for --recursive + --out-dir layout), or null. */\n root: string | null;\n}\n\nexport interface Mode {\n outDir?: string;\n inPlace: boolean;\n /** Write `name.optimized.ext` beside the input (the default when not hosting). */\n writeBeside: boolean;\n}\n\nexport interface Plan {\n item: Item;\n /** Where the optimized bytes go, or null when this run writes nothing locally. */\n optimized: string | null;\n /** Where an untouched original is copied under --out-dir (so the output tree stays complete). */\n kept: string | null;\n}\n\ninterface Row {\n input: string;\n output: string | null;\n status: \"optimized\" | \"skipped\" | \"error\";\n reason?: string;\n input_bytes: number;\n output_bytes: number;\n saved_percent: number;\n url?: string;\n upload_error?: string;\n}\n\n/** Parse a positive-integer flag, or undefined when unset. */\nexport function parsePositiveInt(name: string, value: unknown): number | undefined {\n if (value === undefined || value === null || value === \"\") return undefined;\n const n = Number(value);\n if (!Number.isInteger(n) || n < 1) throw new CliError(`${name} must be a positive whole number.`);\n return n;\n}\n\nfunction isSupported(path: string): boolean {\n return (SUPPORTED_EXTENSIONS as readonly string[]).includes(extname(path).slice(1).toLowerCase());\n}\n\n/** Descend into `dir`, skipping symlinks (no cycles, no escaping the root), dotfiles, our own outputs and `skipDir`. */\nfunction walk(dir: string, out: string[], skipDir: string | undefined): void {\n for (const name of readdirSync(dir)) {\n if (name.startsWith(\".\") || name === \"node_modules\") continue;\n const p = join(dir, name);\n const st = lstatSync(p);\n if (st.isSymbolicLink()) continue;\n if (st.isDirectory()) {\n if (skipDir && resolve(p) === skipDir) continue;\n walk(p, out, skipDir);\n } else if (isSupported(p) && !OWNED.test(basename(p, extname(p)))) {\n out.push(p);\n }\n }\n}\n\n/** Expand the positional paths into files; directories need --recursive. */\nexport function collectItems(paths: string[], recursive: boolean, outDir?: string): Item[] {\n const skipDir = outDir ? resolve(outDir) : undefined;\n const items: Item[] = [];\n for (const p of paths) {\n let st;\n try {\n st = statSync(p);\n } catch {\n throw new CliError(`No such file or directory: ${p}`);\n }\n if (st.isDirectory()) {\n if (!recursive) throw new CliError(`${p} is a directory. Pass --recursive to optimize everything inside it.`);\n const found: string[] = [];\n walk(p, found, skipDir);\n for (const f of found.sort()) items.push({ file: f, root: p });\n } else {\n items.push({ file: p, root: null });\n }\n }\n return items;\n}\n\n/**\n * The extension an optimized file gets. `keep` preserves the input's own spelling\n * (`.jpeg` stays `.jpeg`, `.JPG` stays `.JPG`); otherwise it is the target format's.\n */\nexport function outputExt(item: Item, format: OutFormat): string {\n if (format === \"keep\") return extname(item.file).slice(1);\n return format === \"jpg\" ? \"jpg\" : format;\n}\n\nexport function planOutput(item: Item, format: OutFormat, mode: Mode): Plan {\n const stem = basename(item.file, extname(item.file));\n const ext = outputExt(item, format);\n if (mode.inPlace) return { item, optimized: item.file, kept: null }; // exact original path\n if (mode.outDir) {\n const rel = item.root ? dirname(relative(item.root, item.file)) : \".\";\n const dir = join(mode.outDir, rel);\n return { item, optimized: join(dir, `${stem}.${ext}`), kept: join(dir, basename(item.file)) };\n }\n if (mode.writeBeside) {\n return { item, optimized: join(dirname(item.file), `${stem}.optimized.${ext}`), kept: null };\n }\n return { item, optimized: null, kept: null };\n}\n\n/**\n * Refuse any plan that could lose data, before a single byte is written:\n * two inputs mapping to one output, an output that is another input (or an input\n * in disguise), a symlink as the destination, or --in-place on a symlink.\n */\nexport function assertSafePlans(plans: Plan[], mode: Mode): void {\n const inputs = new Set(plans.map((p) => resolve(p.item.file)));\n const claimed = new Map<string, string>(); // output path -> input that claimed it\n\n for (const plan of plans) {\n const src = resolve(plan.item.file);\n\n if (mode.inPlace && lstatSync(plan.item.file).isSymbolicLink()) {\n throw new CliError(`${plan.item.file} is a symlink; refusing to overwrite it in place. Optimize the real file instead.`);\n }\n\n const targets: Array<[string | null, boolean]> = [\n [plan.optimized, false],\n [plan.kept, true],\n ];\n for (const [target, isKept] of targets) {\n if (!target) continue;\n const out = resolve(target);\n const own = out === src;\n\n // Copying an untouched original onto itself is a harmless no-op; overwriting it with\n // optimized bytes is not, and is caught below.\n if (own && isKept && !mode.inPlace && plan.optimized !== target) continue;\n if (own && !mode.inPlace) {\n throw new CliError(`${plan.item.file}: the output would overwrite the original. Use --in-place if that is what you want.`);\n }\n if (!own && inputs.has(out)) {\n throw new CliError(`${target} is also an input file; refusing to overwrite it. Use different output settings.`);\n }\n const prior = claimed.get(out);\n if (prior && prior !== plan.item.file) {\n throw new CliError(`${plan.item.file} and ${prior} would both be written to ${target}. Optimize them separately or use --out-dir with --recursive.`);\n }\n claimed.set(out, plan.item.file);\n\n if (!own && existsSync(out) && lstatSync(out).isSymbolicLink()) {\n throw new CliError(`${target} is a symlink; refusing to write through it.`);\n }\n }\n }\n}\n\n/** Write via a temp file in the same directory, then rename, so a crash can't truncate the target. */\nexport function writeAtomic(target: string, data: Buffer, mode?: number): void {\n mkdirSync(dirname(target), { recursive: true });\n const tmp = join(dirname(target), `.${basename(target)}.${process.pid}.tmp`);\n try {\n writeFileSync(tmp, data, mode !== undefined ? { mode } : undefined);\n renameSync(tmp, target);\n } catch (err) {\n try {\n unlinkSync(tmp);\n } catch {\n /* nothing to clean up */\n }\n throw err;\n }\n}\n\nasync function pool<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let next = 0;\n const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {\n while (next < items.length) {\n const i = next++;\n results[i] = await fn(items[i] as T);\n }\n });\n await Promise.all(workers);\n return results;\n}\n\nexport default defineCommand({\n meta: {\n name: \"optimize\",\n description:\n \"Compress images locally (no account needed): resize, convert, strip metadata. Never writes a file bigger than the original\",\n },\n args: {\n files: {\n type: \"positional\",\n description: \"Image files (or directories with --recursive)\",\n required: true,\n },\n format: {\n type: \"string\",\n description: \"Output format: keep (default), webp, avif, jpg, png\",\n default: \"keep\",\n },\n \"max-width\": { type: \"string\", description: \"Downscale to at most this width in px (never upscales)\" },\n \"max-height\": { type: \"string\", description: \"Downscale to at most this height in px (never upscales)\" },\n quality: {\n type: \"string\",\n description: \"JPEG/WebP/AVIF quality 1-100 (default 80). PNG output is always lossless\",\n default: \"80\",\n },\n \"keep-metadata\": {\n type: \"boolean\",\n description: \"Keep EXIF/GPS and other metadata (stripped by default)\",\n default: false,\n },\n \"out-dir\": { type: \"string\", description: \"Write results into this directory (mirrors subfolders with --recursive)\" },\n \"in-place\": {\n type: \"boolean\",\n description: \"Overwrite each original (only with --format keep). Default writes name.optimized.ext beside it\",\n default: false,\n },\n recursive: { type: \"boolean\", description: \"Descend into directories\", alias: \"r\", default: false },\n \"dry-run\": { type: \"boolean\", description: \"Report what would be saved without writing anything\", default: false },\n host: {\n type: \"boolean\",\n description:\n \"Upload the optimized image and print its CDN URL (needs an API key). Local files are written only with --out-dir/--in-place\",\n default: false,\n },\n json: { type: \"boolean\", description: \"Output a JSON report (always an array)\", default: false },\n },\n async run({ args }) {\n const format = String(args.format) as OutFormat;\n if (!FORMATS.includes(format)) {\n throw new CliError(`--format must be one of: ${FORMATS.join(\", \")}.`);\n }\n const quality = parsePositiveInt(\"--quality\", args.quality) ?? 80;\n if (quality > 100) throw new CliError(\"--quality must be a whole number between 1 and 100.\");\n const maxWidth = parsePositiveInt(\"--max-width\", args[\"max-width\"]);\n const maxHeight = parsePositiveInt(\"--max-height\", args[\"max-height\"]);\n\n const outDir = args[\"out-dir\"] ? String(args[\"out-dir\"]) : undefined;\n if (args[\"in-place\"] && outDir) throw new CliError(\"Use either --in-place or --out-dir, not both.\");\n if (args[\"in-place\"] && format !== \"keep\") {\n throw new CliError(\"--in-place only works with --format keep (changing format would need a different file name).\");\n }\n\n const dryRun = Boolean(args[\"dry-run\"]);\n const host = Boolean(args.host);\n if (host && dryRun) throw new CliError(\"--host can't be combined with --dry-run (it would upload).\");\n const apiKey = host ? requireApiKey() : undefined;\n\n const paths = args._.length ? args._.map(String) : [String(args.files)];\n const items = collectItems(paths, Boolean(args.recursive), outDir);\n if (items.length === 0) throw new CliError(\"No supported images found (jpg, png, webp, avif).\");\n\n const mode: Mode = {\n outDir,\n inPlace: Boolean(args[\"in-place\"]),\n // With --host the point is the URL; only write beside the original when not hosting.\n writeBeside: !host,\n };\n const plans = items.map((item) => planOutput(item, format, mode));\n assertSafePlans(plans, mode);\n\n const sharp = await loadSharp();\n const opts: OptimizeOptions = { format, maxWidth, maxHeight, quality, keepMetadata: Boolean(args[\"keep-metadata\"]) };\n // Uploads share the API's rate limit, so go gentler when hosting.\n const concurrency = host ? 2 : Math.max(1, Math.min(4, availableParallelism()));\n\n const rows = await pool(plans, concurrency, async (plan): Promise<Row> => {\n const { item } = plan;\n const inputExt = extname(item.file).slice(1).toLowerCase();\n const row: Row = {\n input: item.file,\n output: null,\n status: \"error\",\n input_bytes: 0,\n output_bytes: 0,\n saved_percent: 0,\n };\n let input: Buffer;\n try {\n const size = statSync(item.file).size;\n if (size > MAX_INPUT_BYTES) throw new Error(`file is over ${MAX_INPUT_BYTES / 1024 / 1024} MB`);\n input = readFileSync(item.file);\n const res = await optimizeBuffer(sharp, input, inputExt, opts);\n const optimized = res.status === \"optimized\" && res.buffer ? res.buffer : null;\n row.status = res.status;\n row.reason = res.reason;\n row.input_bytes = res.inputBytes;\n row.output_bytes = res.outputBytes;\n row.saved_percent = savedPercent(res.inputBytes, res.outputBytes);\n\n if (!dryRun) {\n if (optimized && plan.optimized) {\n const keepMode = mode.inPlace ? statSync(item.file).mode & 0o777 : undefined;\n writeAtomic(plan.optimized, optimized, keepMode);\n row.output = plan.optimized;\n } else if (!optimized && plan.kept && resolve(plan.kept) !== resolve(item.file)) {\n // Keep the output tree complete: copy the untouched original across.\n writeAtomic(plan.kept, input);\n row.output = plan.kept;\n }\n }\n\n if (host && apiKey) {\n // Upload failures are reported separately so the local result above is never lost.\n try {\n // Skipped (\"already optimal\") files are uploaded as-is so every input gets a URL.\n const bytes = optimized ?? input;\n const name = optimized ? `${basename(item.file, extname(item.file))}.${res.ext}` : basename(item.file);\n const formData = new FormData();\n formData.append(\"file\", new File([new Uint8Array(bytes)], name));\n const up = await apiRequest<UploadResponse>({\n method: \"POST\",\n path: \"/v1/images\",\n auth: apiKey,\n formData,\n // Same bytes → same image, so a retried batch doesn't create duplicates.\n headers: { \"Idempotency-Key\": createHash(\"sha256\").update(bytes).digest(\"hex\") },\n });\n row.url = up.data.optimized_url ?? up.data.url;\n } catch (err) {\n row.upload_error = err instanceof Error ? err.message : String(err);\n }\n }\n } catch (err) {\n row.status = \"error\";\n row.reason = err instanceof Error ? err.message : String(err);\n }\n return row;\n });\n\n let totalBefore = 0;\n let totalAfter = 0;\n let failures = 0;\n for (const r of rows) {\n if (r.status === \"error\") {\n failures++;\n stderr(`Failed ${r.input}: ${r.reason}`);\n continue;\n }\n if (r.upload_error) {\n failures++;\n stderr(`Upload failed for ${r.input}: ${r.upload_error}`);\n }\n totalBefore += r.input_bytes;\n totalAfter += r.output_bytes;\n if (r.status === \"skipped\") {\n stderr(`Skipped ${r.input}: ${r.reason}`);\n } else {\n const where = r.output ? ` → ${r.output}` : r.url ? ` → ${r.url}` : \"\";\n stderr(\n `${dryRun ? \"Would save\" : \"Optimized\"} ${r.input}${where} ${formatBytes(r.input_bytes)} → ${formatBytes(r.output_bytes)} (-${r.saved_percent}%)`\n );\n }\n }\n if (rows.length > 1) {\n stderr(`Total: ${formatBytes(totalBefore)} → ${formatBytes(totalAfter)} (-${savedPercent(totalBefore, totalAfter)}%)`);\n if (failures) stderr(`${failures} of ${rows.length} failed.`);\n }\n\n if (args.json) {\n jsonOut(rows); // always an array, so scripts need only one code path\n } else {\n // stdout stays machine-readable: the hosted URL, else the written (or, if skipped, original) path.\n for (const r of rows) {\n if (r.status === \"error\") continue;\n const line = r.url ?? r.output ?? (r.status === \"skipped\" ? r.input : null);\n if (line) stdout(line);\n }\n }\n\n // exitCode (not process.exit) so large piped stdout isn't truncated.\n if (failures) process.exitCode = 1;\n },\n});\n","import { defineCommand } from \"citty\";\nimport { writeFileSync } from \"node:fs\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { CliError } from \"../lib/errors.js\";\n\ninterface GetResponse {\n data: {\n id: string;\n url: string;\n mime_type: string;\n size: number;\n filename: string;\n folder: string | null;\n created_at: string;\n metadata?: Record<string, unknown> | null;\n description?: Record<string, unknown> | null;\n };\n}\n\n/**\n * Append a transform query string to a CDN URL. Accepts params with or without a\n * leading `?`/`&` (e.g. `w=400&fmt=webp`, `?segment=foreground`). See the transform\n * docs: https://pixelvault.dev/docs#transforms\n *\n * The params are reserialized through URLSearchParams so reserved characters in\n * values are percent-encoded — without this a hex color like `background=#ffaa00`\n * would be parsed as a URL fragment and lost. Idempotent for already-encoded input.\n */\nexport function applyTransform(url: string, transform?: string): string {\n if (!transform) return url;\n const raw = transform.trim().replace(/^[?&]+/, \"\");\n if (!raw) return url;\n const params = new URLSearchParams(raw).toString();\n if (!params) return url;\n return url.includes(\"?\") ? `${url}&${params}` : `${url}?${params}`;\n}\n\nexport default defineCommand({\n meta: {\n name: \"get\",\n description:\n \"Get an image's CDN URL or metadata, or download it — optionally transformed\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Image ID (e.g. img_abc123)\",\n required: true,\n },\n output: {\n type: \"string\",\n description: \"Download the image to this file path\",\n alias: \"o\",\n },\n transform: {\n type: \"string\",\n description:\n 'Transform params to apply, e.g. \"w=400&fmt=webp\", \"segment=foreground\", \"tile=img_logo.png\"',\n alias: \"t\",\n },\n json: {\n type: \"boolean\",\n description: \"Output full JSON metadata\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n const res = await apiRequest<GetResponse>({\n path: `/v1/images/${encodeURIComponent(args.id)}`,\n auth: apiKey,\n });\n\n const url = applyTransform(res.data.url, args.transform as string | undefined);\n\n // Download mode: fetch the (possibly transformed) bytes to disk.\n if (args.output) {\n const output = args.output as string;\n let bytes: ArrayBuffer;\n try {\n const dl = await fetch(url);\n if (!dl.ok) {\n throw new CliError(`Failed to download image: HTTP ${dl.status}`, 1);\n }\n bytes = await dl.arrayBuffer();\n } catch (err) {\n if (err instanceof CliError) throw err;\n throw new CliError(\n `Failed to download image: ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n // Refuse to write an empty file — a 200 with an empty/truncated body would\n // otherwise report success and leave a broken image that agents chain on.\n if (bytes.byteLength === 0) {\n throw new CliError(\"Downloaded 0 bytes — refusing to write an empty file\", 1);\n }\n try {\n writeFileSync(output, Buffer.from(bytes));\n } catch (err) {\n throw new CliError(\n `Failed to save ${output}: ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n stderr(`Saved ${output}`);\n // Honor --json even in download mode so a caller that always requests JSON\n // gets a stable stdout contract regardless of -o.\n if (args.json) {\n jsonOut(\n args.transform ? { ...res, transformed_url: url, output } : { ...res, output },\n );\n } else {\n stdout(url); // URL to stdout (machine-parseable), confirmation to stderr\n }\n return;\n }\n\n if (args.json) {\n // When a transform is applied there's no separate record for it, so surface\n // the resolved URL alongside the raw metadata.\n jsonOut(args.transform ? { ...res, transformed_url: url } : res);\n } else {\n stdout(url);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { parseMetadataFlags } from \"../lib/metadata.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { jsonOut } from \"../lib/output.js\";\n\ninterface PatchResponse {\n data: { id: string; metadata: Record<string, unknown> | null };\n}\n\nexport default defineCommand({\n meta: {\n name: \"meta\",\n description:\n \"Replace an image's custom metadata (the new object replaces the old one; use --clear to remove it)\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Image ID (e.g. img_abc123)\",\n required: true,\n },\n meta: {\n type: \"string\",\n description: \"Metadata as key=value (repeatable), e.g. --meta env=prod --meta run=42\",\n },\n metadata: {\n type: \"string\",\n description: 'Metadata as a JSON object, e.g. --metadata \\'{\"env\":\"prod\",\"n\":3}\\'',\n },\n clear: {\n type: \"boolean\",\n description: \"Remove all metadata from the image\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n const metadata = parseMetadataFlags({ meta: args.meta, metadata: args.metadata });\n\n if (args.clear && metadata) {\n throw new CliError(\"Use either --clear or --meta/--metadata, not both.\");\n }\n if (!args.clear && !metadata) {\n throw new CliError(\"Nothing to set: pass --meta key=value, --metadata '<json>', or --clear.\");\n }\n\n const res = await apiRequest<PatchResponse>({\n method: \"PATCH\",\n path: `/v1/images/${encodeURIComponent(String(args.id))}`,\n auth: apiKey,\n body: { metadata: args.clear ? null : metadata },\n });\n\n jsonOut(res.data.metadata);\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stderr } from \"../lib/output.js\";\n\nexport default defineCommand({\n meta: {\n name: \"delete\",\n description: \"Delete an image by ID\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Image ID (e.g. img_abc123)\",\n required: true,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n\n await apiRequest<{ success: boolean }>({\n method: \"DELETE\",\n path: `/v1/images/${args.id}`,\n auth: apiKey,\n });\n\n // Silent on success (agent-friendly)\n },\n});\n","import { defineCommand } from \"citty\";\nimport { writeFileSync, mkdirSync } from \"node:fs\";\nimport { execFileSync } from \"node:child_process\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { getApiUrl, requireApiKey } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { CliError } from \"../lib/errors.js\";\n\ninterface StartResponse {\n job_id: string;\n status: string;\n image_count: number;\n status_url: string;\n}\n\ninterface StatusResponse {\n id: string;\n status: \"pending\" | \"processing\" | \"complete\" | \"failed\" | \"expired\";\n image_count: number;\n processed_count: number;\n bytes: number;\n created_at: string;\n expires_at: string;\n download_url?: string;\n error?: string;\n}\n\n/** Default archive filename when `--output` isn't given. Pure, for tests. */\nexport function defaultOutputPath(jobId: string, out?: string): string {\n return out && out.length > 0 ? out : `pixelvault-export-${jobId}.tar`;\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport default defineCommand({\n meta: {\n name: \"export\",\n description:\n \"Export every image in your project as a tar archive (manifest.json + all images). Your data is always portable — no lock-in.\",\n },\n args: {\n output: {\n type: \"string\",\n description: \"Write the archive to this path (default: pixelvault-export-<job>.tar)\",\n alias: \"o\",\n },\n extract: {\n type: \"boolean\",\n description: \"Extract the archive into a directory after download (requires `tar`)\",\n default: false,\n },\n timeout: {\n type: \"string\",\n description: \"Max seconds to wait for the export to finish (default: 600)\",\n default: \"600\",\n },\n json: {\n type: \"boolean\",\n description: \"Output the final job JSON to stdout\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n const timeoutMs = (Number(args.timeout) || 600) * 1000;\n\n // 1. Start the async export job (project-scoped by the secret key).\n let start: StartResponse;\n try {\n start = await apiRequest<StartResponse>({ method: \"POST\", path: \"/v1/export\", auth: apiKey });\n } catch (err) {\n // Surface the \"already running\" case helpfully.\n if (err instanceof CliError && /in progress/i.test(err.message)) {\n throw new CliError(\n \"An export for this project is already in progress. Wait for it to finish, then try again.\",\n 1,\n );\n }\n throw err;\n }\n stderr(`Export started — ${start.image_count} image(s), job ${start.job_id}`);\n\n // 2. Poll until the archive is ready (backoff 1s → 5s).\n const deadline = Date.now() + timeoutMs;\n let status: StatusResponse;\n let delay = 1000;\n for (;;) {\n status = await apiRequest<StatusResponse>({\n path: `/v1/export-jobs/${encodeURIComponent(start.job_id)}`,\n auth: apiKey,\n });\n if (status.status === \"complete\") break;\n if (status.status === \"failed\") {\n throw new CliError(`Export failed: ${status.error ?? \"unknown error\"}`, 1);\n }\n if (status.status === \"expired\") {\n throw new CliError(\"Export expired before it could be downloaded. Start a new one.\", 1);\n }\n if (Date.now() > deadline) {\n throw new CliError(\n `Timed out after ${args.timeout}s (job still ${status.status}). It may still finish — re-run to poll, or raise --timeout.`,\n 1,\n );\n }\n stderr(` ${status.status}… ${status.processed_count}/${status.image_count}`);\n await sleep(delay);\n delay = Math.min(Math.round(delay * 1.5), 5000);\n }\n\n // 3. Download the tar. apiRequest parses JSON, so fetch the raw bytes directly\n // (the endpoint requires the same Bearer auth).\n const out = defaultOutputPath(start.job_id, args.output as string | undefined);\n const downloadUrl = `${getApiUrl()}/v1/export-jobs/${encodeURIComponent(start.job_id)}/download`;\n let bytes: ArrayBuffer;\n try {\n const dl = await fetch(downloadUrl, { headers: { Authorization: `Bearer ${apiKey}` } });\n if (!dl.ok) throw new CliError(`Failed to download export: HTTP ${dl.status}`, 1);\n bytes = await dl.arrayBuffer();\n } catch (err) {\n if (err instanceof CliError) throw err;\n throw new CliError(\n `Failed to download export: ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n // A 200 with an empty/truncated body must not report success for a backup.\n if (bytes.byteLength === 0) {\n throw new CliError(\"Downloaded 0 bytes — refusing to write an empty archive.\", 1);\n }\n try {\n writeFileSync(out, Buffer.from(bytes));\n } catch (err) {\n throw new CliError(\n `Failed to save ${out}: ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n stderr(`Saved ${out} — ${status.image_count} image(s), ${bytes.byteLength} bytes`);\n\n // 4. Optional extraction via the system `tar`.\n if (args.extract) {\n const dir = out.replace(/\\.tar$/, \"\") || `${out}-extracted`;\n try {\n mkdirSync(dir, { recursive: true });\n execFileSync(\"tar\", [\"-xf\", out, \"-C\", dir], { stdio: \"ignore\" });\n stderr(`Extracted into ${dir}/`);\n } catch (err) {\n throw new CliError(\n `Saved ${out}, but extraction failed (is \\`tar\\` installed?): ${err instanceof Error ? err.message : String(err)}`,\n 1,\n );\n }\n }\n\n if (args.json) {\n jsonOut({ ...status, output: out });\n } else {\n stdout(out); // machine-parseable path on stdout; progress went to stderr\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { stdout, jsonOut, formatBytes } from \"../lib/output.js\";\n\nexport interface UsageData {\n plan: string;\n period: string;\n resets_at: string;\n usage: { storage_bytes: number; uploads: number };\n limits: {\n storage_bytes: number | null;\n uploads_per_month: number | null;\n bandwidth_bytes_per_month: number | null;\n };\n remaining: { storage_bytes: number | null; uploads: number | null };\n /** AI image-generation credits (`pixelvault generate`) for the month. */\n generation?: {\n credits_used: number;\n credits_limit: number;\n credits_remaining: number;\n };\n}\n\nconst bytes = (n: number | null | undefined) =>\n n == null ? \"unlimited\" : formatBytes(n);\nconst count = (n: number | null | undefined) =>\n n == null ? \"unlimited\" : String(n);\n\nexport function renderUsage(d: UsageData): string[] {\n return [\n `Plan: ${d.plan}`,\n `Period: ${d.period} (resets ${d.resets_at})`,\n `Storage: ${formatBytes(d.usage.storage_bytes)} used / ${bytes(d.limits.storage_bytes)} limit / ${bytes(d.remaining.storage_bytes)} remaining`,\n `Uploads: ${d.usage.uploads} used / ${count(d.limits.uploads_per_month)} limit / ${count(d.remaining.uploads)} remaining`,\n `Bandwidth: ${bytes(d.limits.bandwidth_bytes_per_month)} limit per month (usage not reported)`,\n ...(d.generation\n ? [\n `AI images: ${d.generation.credits_used} credits used / ${d.generation.credits_limit} limit / ${d.generation.credits_remaining} remaining`,\n ]\n : []),\n ];\n}\n\nexport default defineCommand({\n meta: {\n name: \"usage\",\n description: \"Show plan, quota usage and limits (secret key required)\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output the usage object as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n const res = await apiRequest<{ data: UsageData }>({\n path: \"/v1/usage\",\n auth: apiKey,\n });\n\n if (args.json) {\n jsonOut(res.data);\n } else {\n for (const line of renderUsage(res.data)) stdout(line);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest, ApiRequestError } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\nexport interface KeyInfo {\n id: string;\n type: \"secret\" | \"publishable\";\n environment: string;\n label: string | null;\n key_prefix: string;\n allowed_origins: string[] | null;\n created_at: string;\n last_used_at: string | null;\n current: boolean;\n}\n\n/** Turn known key-management API errors into actionable messages. */\nexport function explainKeyError(err: unknown): never {\n if (err instanceof ApiRequestError) {\n switch (err.code) {\n case \"key_limit_reached\":\n throw new CliError(\n \"Key limit reached (25 per project). Revoke an unused key with `pixelvault keys revoke <id>` first.\",\n 1\n );\n case \"rate_limit_exceeded\": {\n const wait = err.retryAfter\n ? ` Retry after ${err.retryAfter} seconds.`\n : \"\";\n throw new CliError(\n `Rate limit exceeded (20 key changes per hour per project).${wait}`,\n 1\n );\n }\n case \"cannot_revoke_current_key\":\n throw new CliError(\n \"Cannot revoke the key you are authenticating with. Create a new key, switch to it (PIXELVAULT_API_KEY), then revoke the old one with the new key.\",\n 1\n );\n case \"not_found\":\n throw new CliError(\n \"Key not found. Only secret keys can be revoked; check the id with `pixelvault keys list`.\",\n 1\n );\n case \"validation_error\":\n throw new CliError(`Invalid request: ${err.message}`, 1);\n case \"forbidden\":\n throw new CliError(\n \"This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.\",\n 1\n );\n }\n if (err.status === 403) {\n throw new CliError(\n \"This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.\",\n 1\n );\n }\n }\n throw err;\n}\n\nconst list = defineCommand({\n meta: { name: \"list\", description: \"List the project's API keys\" },\n args: {\n json: { type: \"boolean\", description: \"Output as JSON\", default: false },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n let res: { data: KeyInfo[] };\n try {\n res = await apiRequest<{ data: KeyInfo[] }>({\n path: \"/v1/keys\",\n auth: apiKey,\n });\n } catch (err) {\n explainKeyError(err);\n }\n\n if (args.json) {\n jsonOut(res.data);\n return;\n }\n for (const k of res.data) {\n stdout(\n [\n k.id,\n k.type,\n k.environment,\n `${k.key_prefix}...`,\n k.label ?? \"-\",\n `created ${k.created_at}`,\n `last used ${k.last_used_at ?? \"never\"}`,\n k.current ? \"(current)\" : \"\",\n ]\n .filter(Boolean)\n .join(\" \")\n );\n }\n },\n});\n\nconst create = defineCommand({\n meta: {\n name: \"create\",\n description: \"Create a new secret API key (the raw key is printed once)\",\n },\n args: {\n label: { type: \"string\", description: \"Label for the key (max 60 chars)\" },\n env: {\n type: \"string\",\n description: \"Environment: live or test (default: live)\",\n },\n json: {\n type: \"boolean\",\n description: \"Output the full data object as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n if (args.env && args.env !== \"live\" && args.env !== \"test\") {\n throw new CliError(\"--env must be 'live' or 'test'.\", 1);\n }\n if (args.label && args.label.length > 60) {\n throw new CliError(\"--label must be 60 characters or fewer.\", 1);\n }\n\n const body: { label?: string; environment?: string } = {};\n if (args.label) body.label = args.label;\n if (args.env) body.environment = args.env;\n\n let res: { data: { id: string; api_key: string } };\n try {\n res = await apiRequest<{ data: { id: string; api_key: string } }>({\n method: \"POST\",\n path: \"/v1/keys\",\n auth: apiKey,\n body,\n });\n } catch (err) {\n explainKeyError(err);\n }\n\n stderr(\n \"This key is shown ONCE. Store it now (secret manager / env var); never commit it or paste it into logs.\"\n );\n if (args.json) {\n jsonOut(res.data);\n } else {\n stdout(res.data.api_key);\n }\n },\n});\n\nconst revoke = defineCommand({\n meta: {\n name: \"revoke\",\n description: \"Revoke a secret API key by id (not the one in use)\",\n },\n args: {\n id: {\n type: \"positional\",\n description: \"Key id (see `pixelvault keys list`)\",\n required: true,\n },\n json: { type: \"boolean\", description: \"Output as JSON\", default: false },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n let res: { data: { deleted: boolean } };\n try {\n res = await apiRequest<{ data: { deleted: boolean } }>({\n method: \"DELETE\",\n path: `/v1/keys/${encodeURIComponent(args.id)}`,\n auth: apiKey,\n });\n } catch (err) {\n explainKeyError(err);\n }\n\n if (args.json) {\n jsonOut(res.data);\n } else {\n stderr(`Revoked key ${args.id}.`);\n }\n },\n});\n\nexport default defineCommand({\n meta: {\n name: \"keys\",\n description: \"Manage API keys (list, create, revoke)\",\n },\n subCommands: { list, create, revoke },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest, ApiRequestError } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\n/** Turn known billing API errors into actionable messages. */\nexport function explainBillingError(err: unknown): never {\n if (err instanceof ApiRequestError) {\n switch (err.code) {\n case \"test_key_forbidden\":\n throw new CliError(\n \"Billing needs a live key (pv_live_...); test keys can't start checkout or open the portal.\",\n 1\n );\n case \"publishable_key_forbidden\":\n throw new CliError(\n \"Billing needs a secret live key (pv_live_...); publishable keys are not allowed.\",\n 1\n );\n case \"email_not_verified\":\n throw new CliError(\n \"Verify your email before upgrading (check your inbox for the code).\",\n 1\n );\n case \"already_subscribed\":\n throw new CliError(\n \"You already have a subscription. Change or cancel it with `pixelvault billing`.\",\n 1\n );\n case \"no_subscription\":\n throw new CliError(\n \"This account has no billing history yet. Start one with `pixelvault upgrade <starter|pro>`.\",\n 1\n );\n case \"billing_unavailable\":\n throw new CliError(\"Paid plans are not available right now. Try again later.\", 1);\n case \"rate_limit_exceeded\": {\n const wait = err.retryAfter ? ` Retry after ${err.retryAfter} seconds.` : \"\";\n throw new CliError(\n `Rate limit exceeded (10 billing requests per hour per account).${wait}`,\n 1\n );\n }\n }\n }\n throw err;\n}\n\nexport default defineCommand({\n meta: {\n name: \"billing\",\n description:\n \"Print a Stripe billing-portal URL to change plan, update payment details or cancel (live key required)\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output { url } as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const apiKey = requireApiKey();\n let res: { url: string };\n try {\n res = await apiRequest<{ url: string }>({\n method: \"POST\",\n path: \"/v1/billing/portal\",\n body: {},\n auth: apiKey,\n });\n } catch (err) {\n explainBillingError(err);\n }\n\n if (args.json) {\n jsonOut({ url: res.url });\n } else {\n stdout(res.url);\n stderr(\n \"Open this link yourself to change plan, update payment details or cancel. It can cancel the subscription, so don't paste it anywhere shared.\"\n );\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiRequest } from \"../lib/client.js\";\nimport { requireApiKey } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\nimport { explainBillingError } from \"./billing.js\";\n\nconst PLANS = [\"starter\", \"pro\"] as const;\n\nexport default defineCommand({\n meta: {\n name: \"upgrade\",\n description:\n \"Print a Stripe Checkout URL to subscribe to a paid plan (live key required; nothing is charged until you pay)\",\n },\n args: {\n plan: {\n type: \"positional\",\n description: `Plan to subscribe to: ${PLANS.join(\" | \")}`,\n required: true,\n },\n json: {\n type: \"boolean\",\n description: \"Output { url } as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const plan = String(args.plan);\n if (!(PLANS as readonly string[]).includes(plan)) {\n throw new CliError(`Unknown plan \"${plan}\". Choose one of: ${PLANS.join(\", \")}.`, 1);\n }\n const apiKey = requireApiKey();\n let res: { url: string };\n try {\n res = await apiRequest<{ url: string }>({\n method: \"POST\",\n path: \"/v1/billing/checkout\",\n body: { plan },\n auth: apiKey,\n });\n } catch (err) {\n explainBillingError(err);\n }\n\n if (args.json) {\n jsonOut({ url: res.url });\n } else {\n stdout(res.url);\n stderr(\n \"Open this link to complete payment. The plan changes once Stripe confirms — check with `pixelvault usage`.\"\n );\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { readConfig, getApiKey } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\nexport default defineCommand({\n meta: {\n name: \"whoami\",\n description: \"Show current authentication state\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const config = readConfig();\n const apiKey = getApiKey();\n\n if (!apiKey && !config.email) {\n stderr(\"Not authenticated. Run `pixelvault register` or `pixelvault login`.\");\n process.exit(1);\n }\n\n const info = {\n email: config.email || \"(unknown)\",\n project: config.default_project || \"(unknown)\",\n api_key: apiKey\n ? `${apiKey.slice(0, 12)}...${apiKey.slice(-4)}`\n : \"(not set)\",\n source: process.env[\"PIXELVAULT_API_KEY\"] ? \"env\" : \"config\",\n // email/project come from config written at login; nothing checks them\n // against the current key.\n identity_source: \"cached\",\n };\n\n if (args.json) {\n jsonOut(info);\n } else {\n // email/project are read from config, not looked up for the current key.\n const tag = (v?: string) => (v ? \" (cached, not verified for this key)\" : \"\");\n stdout(`Email: ${info.email}${tag(config.email)}`);\n stdout(`Project: ${info.project}${tag(config.default_project)}`);\n stdout(`API Key: ${info.api_key} (${info.source})`);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { readConfig, updateConfig, getConfigPath } from \"../lib/config.js\";\nimport { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\nconst configGet = defineCommand({\n meta: {\n name: \"get\",\n description: \"Get a config value\",\n },\n args: {\n key: {\n type: \"positional\",\n description: \"Config key (api_key, default_project, email, api_url)\",\n required: true,\n },\n },\n async run({ args }) {\n const config = readConfig();\n const value = config[args.key as keyof typeof config];\n\n if (value === undefined) {\n stderr(`Key \"${args.key}\" is not set.`);\n process.exit(1);\n }\n\n stdout(String(value));\n },\n});\n\nconst configSet = defineCommand({\n meta: {\n name: \"set\",\n description: \"Set a config value\",\n },\n args: {\n key: {\n type: \"positional\",\n description: \"Config key\",\n required: true,\n },\n value: {\n type: \"positional\",\n description: \"Config value\",\n required: true,\n },\n },\n async run({ args }) {\n const validKeys = [\"api_key\", \"default_project\", \"email\", \"api_url\"];\n if (!validKeys.includes(args.key)) {\n stderr(`Invalid key. Valid keys: ${validKeys.join(\", \")}`);\n process.exit(1);\n }\n\n if (args.key === \"api_key\" && args.value !== readConfig().api_key) {\n // email and default_project were cached at login for the previous key and\n // may belong to a different account, so drop them rather than mislead.\n updateConfig({ api_key: args.value, email: undefined, default_project: undefined });\n } else {\n updateConfig({ [args.key]: args.value });\n }\n stderr(`Set ${args.key} = ${args.key === \"api_key\" ? \"***\" : args.value}`);\n },\n});\n\nconst configShow = defineCommand({\n meta: {\n name: \"show\",\n description: \"Show all config values\",\n },\n args: {\n json: {\n type: \"boolean\",\n description: \"Output as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const config = readConfig();\n\n if (args.json) {\n jsonOut(config);\n } else {\n stderr(`Config: ${getConfigPath()}`);\n for (const [key, value] of Object.entries(config)) {\n const display = key === \"api_key\" && value\n ? `${(value as string).slice(0, 12)}...`\n : value;\n stdout(`${key} = ${display}`);\n }\n }\n },\n});\n\nexport default defineCommand({\n meta: {\n name: \"config\",\n description: \"Manage CLI configuration\",\n },\n subCommands: {\n get: configGet,\n set: configSet,\n show: configShow,\n },\n});\n","import { defineCommand } from \"citty\";\nimport { runCli } from \"./lib/run.js\";\nimport { VERSION } from \"./lib/client.js\";\n\nconst main = defineCommand({\n meta: {\n name: \"pixelvault\",\n version: VERSION,\n description: \"CLI for PixelVault — agent-first image and video hosting\",\n },\n subCommands: {\n register: () => import(\"./commands/register.js\").then((m) => m.default),\n login: () => import(\"./commands/login.js\").then((m) => m.default),\n upload: () => import(\"./commands/upload.js\").then((m) => m.default),\n generate: () => import(\"./commands/generate.js\").then((m) => m.default),\n list: () => import(\"./commands/list.js\").then((m) => m.default),\n tags: () => import(\"./commands/tags.js\").then((m) => m.default),\n describe: () => import(\"./commands/describe.js\").then((m) => m.default),\n optimize: () => import(\"./commands/optimize.js\").then((m) => m.default),\n get: () => import(\"./commands/get.js\").then((m) => m.default),\n meta: () => import(\"./commands/meta.js\").then((m) => m.default),\n delete: () => import(\"./commands/delete.js\").then((m) => m.default),\n export: () => import(\"./commands/export.js\").then((m) => m.default),\n usage: () => import(\"./commands/usage.js\").then((m) => m.default),\n keys: () => import(\"./commands/keys.js\").then((m) => m.default),\n upgrade: () => import(\"./commands/upgrade.js\").then((m) => m.default),\n billing: () => import(\"./commands/billing.js\").then((m) => m.default),\n whoami: () => import(\"./commands/whoami.js\").then((m) => m.default),\n config: () => import(\"./commands/config.js\").then((m) => m.default),\n },\n});\n\nvoid runCli(main);\n","import { runCommand, runMain, type CommandDef } from \"citty\";\nimport { CliError } from \"./errors.js\";\nimport { stderr } from \"./output.js\";\n\n/**\n * Entry point wrapper around citty.\n *\n * citty's `runMain` swallows every error itself: for anything that isn't its own\n * usage error it prints the error WITH a stack trace, prints the message a second\n * time, and always exits 1 — so a `CliError` printed twice with a stack and its\n * `exitCode` was ignored (and the `.catch` after `runMain` never ran). We hand\n * `--help` / `--version` to `runMain` (it renders those well) and run everything\n * else through `runCommand` so we own error output.\n */\nexport async function runCli(\n main: CommandDef,\n rawArgs: string[] = process.argv.slice(2),\n exit: (code: number) => void = (code) => process.exit(code)\n): Promise<void> {\n const wantsMeta =\n rawArgs.includes(\"--help\") ||\n rawArgs.includes(\"-h\") ||\n (rawArgs.length === 1 && rawArgs[0] === \"--version\");\n if (wantsMeta) {\n await runMain(main, { rawArgs });\n return;\n }\n\n try {\n await runCommand(main, { rawArgs });\n } catch (err) {\n if (err instanceof CliError) {\n stderr(`Error: ${err.message}`);\n exit(err.exitCode);\n return;\n }\n // citty's own usage errors (missing argument, unknown command) surface before\n // any command body runs, so replaying through runMain is side-effect free and\n // gets citty's usage rendering.\n if (err instanceof Error && err.name === \"CLIError\") {\n await runMain(main, { rawArgs });\n return;\n }\n stderr(`Error: ${err instanceof Error ? err.message : String(err)}`);\n exit(1);\n }\n}\n"],"mappings":"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","names":["CliError","init_errors","__esmMin","message","exitCode","stdout","text","stderr","jsonOut","data","formatBytes","n","units","v","init_output","__esmMin","readFileSync","writeFileSync","mkdirSync","join","homedir","getConfigPath","CONFIG_FILE","readConfig","raw","writeConfig","config","CONFIG_DIR","updateConfig","updates","getApiKey","getApiUrl","DEFAULT_API_URL","requireApiKey","key","init_config","__esmMin","apiRequest","options","url","getApiUrl","headers","CLIENT_ID","fetchBody","res","init","err","CliError","errorBody","message","ApiRequestError","VERSION","init_client","__esmMin","init_config","init_errors","status","code","retryAfter","createInterface","prompt","question","rl","resolve","answer","promptPassword","password","onData","char","init_prompt","__esmMin","register_exports","__export","register_default","defineCommand","init_register","__esmMin","init_client","init_config","init_prompt","init_output","args","interactive","email","prompt","stderr","password","promptPassword","res","apiRequest","updateConfig","login_exports","__export","login_default","defineCommand","init_login","__esmMin","init_client","init_config","init_prompt","init_output","args","email","prompt","password","promptPassword","stderr","loginRes","apiRequest","token","projectId","rotateRes","updateConfig","readConfig","parseExpires","input","flag","match","CliError","unit","multiplier","seconds","MIN_EXPIRES_SECONDS","MAX_EXPIRES_SECONDS","init_duration","__esmMin","init_errors","validateReuseOptions","opts","idempotencyKey","dedupe","fileCount","IDEMPOTENCY_KEY","init_upload_options","__esmMin","asList","v","parseMetadataFlags","opts","out","json","CliError","parsed","k","pair","eq","key","value","init_metadata","__esmMin","init_errors","waitForDescription","id","auth","deps","sleep","ms","r","now","deadline","MAX_WAIT_MS","last","apiRequest","POLL_MS","describeSummary","d","init_describe","__esmMin","init_client","createReadStream","statSync","basename","Readable","fileBody","filePath","start","end","stream","isRemoteVideoUrl","value","protocol","uploadVideoUrl","sourceUrl","apiKey","options","data","apiRequest","uploadVideoFile","stat","CliError","filename","DIRECT_MAX_BYTES","uploadId","session","partSize","partNumber","length","error","init_video_upload","__esmMin","init_client","init_errors","upload_exports","__export","upload_default","defineCommand","readFileSync","basename","extname","init_upload","__esmMin","init_client","init_config","init_duration","init_upload_options","init_metadata","init_describe","init_output","init_video_upload","args","apiKey","requireApiKey","signExpires","stderr","parseExpires","files","hasVideo","source","isRemoteVideoUrl","imageOnlyOption","hadError","idempotencyKey","reuseError","validateReuseOptions","metadata","parseMetadataFlags","results","filePath","options","video","uploadVideoUrl","uploadVideoFile","stdout","buffer","fileName","formData","res","apiRequest","waitForDescription","describeSummary","err","jsonOut","generate_exports","__export","generate_default","defineCommand","init_generate","__esmMin","init_client","init_config","init_errors","init_duration","init_output","args","apiKey","requireApiKey","prompt","CliError","signExpires","parseExpires","deleteAfter","stderr","res","apiRequest","g","jsonOut","stdout","list_exports","__export","list_default","defineCommand","init_list","__esmMin","init_client","init_config","init_output","init_metadata","args","apiKey","requireApiKey","params","filter","parseMetadataFlags","search","tag","res","apiRequest","jsonOut","img","stdout","tags_exports","__export","tags_default","defineCommand","init_tags","__esmMin","init_client","init_config","init_output","args","apiKey","requireApiKey","res","apiRequest","jsonOut","t","stdout","describe_exports","__export","describe_default","defineCommand","init_describe","__esmMin","init_client","init_config","init_output","init_errors","args","apiKey","requireApiKey","id","res","apiRequest","CliError","description","waitForDescription","describeSummary","jsonOut","stdout","stderr","loadSharp","major","minor","MIN_NODE_MAJOR","MIN_NODE_MINOR","CliError","mod","err","why","resolveOutput","format","detected","optimizeBuffer","sharp","input","inputExt","opts","base","skip","reason","meta","SHARP_INPUT","msg","MAX_INPUT_PIXELS","out","pipeline","data","info","swap","inW","inH","MIN_SAVING_PERCENT","savedPercent","before","after","MAX_INPUT_BYTES","SUPPORTED_EXTENSIONS","init_optimize","__esmMin","init_errors","optimize_exports","__export","assertSafePlans","collectItems","optimize_default","outputExt","parsePositiveInt","planOutput","writeAtomic","defineCommand","createHash","availableParallelism","readFileSync","writeFileSync","renameSync","unlinkSync","mkdirSync","statSync","lstatSync","readdirSync","existsSync","basename","dirname","extname","join","relative","resolve","name","value","n","CliError","isSupported","path","SUPPORTED_EXTENSIONS","walk","dir","out","skipDir","p","st","OWNED","paths","recursive","outDir","items","found","f","item","format","mode","stem","ext","rel","plans","inputs","claimed","plan","src","targets","target","isKept","own","prior","data","tmp","err","pool","limit","fn","results","next","workers","i","FORMATS","init_optimize","__esmMin","init_client","init_config","init_errors","init_output","args","quality","maxWidth","maxHeight","dryRun","host","apiKey","requireApiKey","sharp","loadSharp","opts","concurrency","rows","inputExt","row","input","MAX_INPUT_BYTES","res","optimizeBuffer","optimized","savedPercent","keepMode","bytes","formData","up","apiRequest","totalBefore","totalAfter","failures","r","stderr","where","formatBytes","jsonOut","line","stdout","get_exports","__export","applyTransform","get_default","defineCommand","writeFileSync","url","transform","raw","params","init_get","__esmMin","init_client","init_config","init_output","init_errors","args","apiKey","requireApiKey","res","apiRequest","output","bytes","dl","CliError","err","stderr","jsonOut","stdout","meta_exports","__export","meta_default","defineCommand","init_meta","__esmMin","init_client","init_config","init_metadata","init_errors","init_output","args","apiKey","requireApiKey","metadata","parseMetadataFlags","CliError","res","apiRequest","jsonOut","delete_exports","__export","delete_default","defineCommand","init_delete","__esmMin","init_client","init_config","args","apiKey","requireApiKey","apiRequest","export_exports","__export","export_default","defaultOutputPath","defineCommand","writeFileSync","mkdirSync","execFileSync","jobId","out","sleep","init_export","__esmMin","init_client","init_config","init_output","init_errors","ms","resolve","args","apiKey","requireApiKey","timeoutMs","start","apiRequest","err","CliError","stderr","deadline","status","delay","downloadUrl","getApiUrl","bytes","dl","dir","jsonOut","stdout","usage_exports","__export","usage_default","renderUsage","defineCommand","d","formatBytes","bytes","count","init_usage","__esmMin","init_client","init_config","init_output","n","args","apiKey","requireApiKey","res","apiRequest","jsonOut","line","stdout","keys_exports","__export","keys_default","explainKeyError","defineCommand","err","ApiRequestError","CliError","wait","list","create","revoke","init_keys","__esmMin","init_client","init_config","init_errors","init_output","args","apiKey","requireApiKey","res","apiRequest","jsonOut","k","stdout","body","stderr","billing_exports","__export","billing_default","explainBillingError","defineCommand","err","ApiRequestError","CliError","wait","init_billing","__esmMin","init_client","init_config","init_errors","init_output","args","apiKey","requireApiKey","res","apiRequest","jsonOut","stdout","stderr","upgrade_exports","__export","upgrade_default","defineCommand","PLANS","init_upgrade","__esmMin","init_client","init_config","init_errors","init_output","init_billing","args","plan","CliError","apiKey","requireApiKey","res","apiRequest","err","explainBillingError","jsonOut","stdout","stderr","whoami_exports","__export","whoami_default","defineCommand","init_whoami","__esmMin","init_config","init_output","args","config","readConfig","apiKey","getApiKey","stderr","info","jsonOut","tag","v","stdout","config_exports","__export","config_default","defineCommand","configGet","configSet","configShow","init_config","__esmMin","init_output","args","value","readConfig","stderr","stdout","validKeys","updateConfig","config","jsonOut","getConfigPath","key","display","defineCommand","init_errors","init_output","runCommand","runMain","runCli","main","rawArgs","exit","code","err","CliError","stderr","init_client","main","defineCommand","VERSION","m","runCli"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelvault-cli",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "CLI for PixelVault — agent-first image and video hosting",
5
5
  "type": "module",
6
6
  "bin": {