pixelvault-cli 0.9.0 → 0.11.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 +35 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ The CLI is designed for AI coding agents. Output contract:
|
|
|
63
63
|
- `upload` prints **only the URL** to stdout (add `--optimized` for the smaller WebP/AVIF variant; it falls back to the original URL for SVG, GIF and private uploads)
|
|
64
64
|
- `upload --idempotency-key <key>` makes retries safe: repeating the same key with the same file returns the original image instead of storing a copy (a note goes to stderr; single file only). `upload --dedupe` reuses an identical image already in your project without counting another upload. The two can't be combined
|
|
65
65
|
- `list` outputs one URL per line
|
|
66
|
+
- `meta` prints the stored metadata as JSON
|
|
66
67
|
- `delete` outputs nothing on success
|
|
67
68
|
- All human messages go to **stderr**
|
|
68
69
|
- `--json` flag for structured output
|
|
@@ -89,11 +90,13 @@ npx pixelvault-cli upload build-output.png
|
|
|
89
90
|
| `register` | Create a new account |
|
|
90
91
|
| `login` | Log in to existing account |
|
|
91
92
|
| `upload <files...>` | Upload images (prints URLs to stdout) |
|
|
93
|
+
| `generate <prompt>` | Generate an image from a prompt and host it (prints the URL; spends monthly AI credits) |
|
|
92
94
|
| `list` | List uploaded images |
|
|
93
95
|
| `get <id>` | Get an image's URL/metadata, or download it (optionally transformed) |
|
|
96
|
+
| `meta <id>` | Replace an image's custom metadata (`--meta k=v`, `--metadata '<json>'`, or `--clear`) |
|
|
94
97
|
| `delete <id>` | Delete an image |
|
|
95
98
|
| `export` | Export all project images as a tar archive |
|
|
96
|
-
| `usage` | Show plan, period,
|
|
99
|
+
| `usage` | Show plan, period, storage/upload/bandwidth usage and limits, and AI generation credits |
|
|
97
100
|
| `keys list\|create\|revoke` | List, create, and revoke the project's secret API keys |
|
|
98
101
|
| `upgrade <starter\|pro>` | Print a Stripe Checkout URL to subscribe (live key; you pay in the browser) |
|
|
99
102
|
| `billing` | Print a Stripe billing-portal URL to change plan, update payment details or cancel (live key) |
|
|
@@ -106,8 +109,25 @@ npx pixelvault-cli upload build-output.png
|
|
|
106
109
|
pixelvault upload photo.jpg # Single file
|
|
107
110
|
pixelvault upload *.png --folder icons # Bulk with folder
|
|
108
111
|
pixelvault upload shot.png --json # Full JSON response
|
|
112
|
+
pixelvault upload shot.png --meta env=prod --meta run=42 # Tag with custom metadata (key=value, repeatable)
|
|
113
|
+
pixelvault upload shot.png --metadata '{"n":3}' # ...or a JSON object (typed values)
|
|
109
114
|
```
|
|
110
115
|
|
|
116
|
+
### Generate Options
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pixelvault generate "flat vector hero, a robot at a laptop, no text" # prints the hosted URL
|
|
120
|
+
pixelvault generate a red bicycle --optimized # unquoted words work; print the WebP/AVIF URL
|
|
121
|
+
pixelvault generate "an og card" --model lucid-origin # Leonardo model (20 credits, paid plans)
|
|
122
|
+
pixelvault generate "a lake" --delete-after 1h # auto-delete the image
|
|
123
|
+
pixelvault generate "a lake" --private --expires 12h # signed link
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Spends credits from your monthly grant: free 10 (`flux-1-schnell` only), Starter 300, Pro 1,500. `flux-1-schnell`
|
|
127
|
+
and the SDXL models cost 1 credit, the Leonardo models (`lucid-origin`, `phoenix-1.0`) cost 20. A failed
|
|
128
|
+
generation is refunded. stdout is just the URL (or `--json` for the full response); the credits used and left go to
|
|
129
|
+
stderr. Needs a secret key and a verified email. `pixelvault usage` shows your credits.
|
|
130
|
+
|
|
111
131
|
### Get Options
|
|
112
132
|
|
|
113
133
|
```bash
|
|
@@ -146,8 +166,22 @@ progress to **stderr**.
|
|
|
146
166
|
pixelvault list # One URL per line
|
|
147
167
|
pixelvault list --json # Full JSON with metadata
|
|
148
168
|
pixelvault list --page 2 --per-page 50 # Pagination
|
|
169
|
+
pixelvault list --meta env=prod # Only images tagged env=prod (repeatable; all must match)
|
|
170
|
+
pixelvault list --metadata '{"n":3}' # Typed match (--meta values are strings)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Metadata
|
|
174
|
+
|
|
175
|
+
Attach custom key/values to images at upload, find them again with `list --meta`, and change them with `meta`:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pixelvault meta img_xyz --meta env=staging --meta run=43 # replaces the whole object
|
|
179
|
+
pixelvault meta img_xyz --clear # remove it
|
|
149
180
|
```
|
|
150
181
|
|
|
182
|
+
`meta` **replaces** the stored metadata (it does not merge), so include every key you want to keep. Metadata is ignored when `--dedupe` or `--idempotency-key` returns an existing image; use `meta` to change it. Limits: a JSON object, up to 50 top-level keys and 4 KB; NUL characters aren't allowed. Don't store secrets in it.
|
|
183
|
+
|
|
184
|
+
|
|
151
185
|
### Usage and Keys
|
|
152
186
|
|
|
153
187
|
Both need a **secret** key (`pv_live_...`); publishable keys get a 403.
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}function
|
|
3
|
-
`)}function
|
|
4
|
-
`,{mode:384})}function
|
|
5
|
-
`||
|
|
6
|
-
`),r(i)):o===""?(process.stdin.setRawMode(!1),process.exit(130)):o==="\x7F"||o==="\b"?i=i.slice(0,-1):i+=o};process.stdin.on("data",t)}):S(e)}var N=m(()=>{"use strict"});var X={};w(X,{default:()=>Ue});import{defineCommand as qe}from"citty";var Ue,Y=m(()=>{"use strict";b();y();N();h();Ue=qe({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 r=!!process.stdin.isTTY,i=e.email||(r?await S("Email: "):"");i||(n("Email is required. Pass --email <email>."),process.exit(1));let t;e.password?t=e.password:e.passwordless?t=void 0:r?t=await P("Password (leave blank for a passwordless account): ")||void 0:t=void 0,t!==void 0&&t.length<8&&(n("Password must be at least 8 characters."),process.exit(1)),n("Creating account...");let o=await p({method:"POST",path:"/v1/auth/register",body:{email:i,...t?{password:t}:{},attribution:{source:"cli"}}});j({api_key:o.data.default_project.api_keys.live,default_project:o.data.default_project.id,email:o.data.email}),n("Account created. API key saved to ~/.pixelvault/config.json"),n(`Email: ${o.data.email}`),n(`Project: ${o.data.default_project.id}`),(o.data.password_set===!1||!t)&&n("No password set. To enable dashboard login, use 'Forgot password' at https://pixelvault.dev/forgot-password to set one."),o.data.email_verified||n("Verify your email to lift the upload limit for the free tier.")}})});var W={};w(W,{default:()=>De});import{defineCommand as Ne}from"citty";var De,H=m(()=>{"use strict";b();y();N();h();De=Ne({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 r=e.email||await S("Email: "),i=e.password||await P("Password: ");(!r||!i)&&(n("Email and password are required."),process.exit(1)),n("Logging in...");let t=await p({method:"POST",path:"/v1/auth/login",body:{email:r,password:i}}),o=t.data.token,s;try{s=(await p({method:"GET",path:"/v1/projects",auth:o})).data[0]?.id}catch{}if(s){let d=await p({method:"POST",path:"/v1/auth/rotate-key",auth:o,body:{project_id:s}});j({api_key:d.data.api_key,default_project:s,email:t.data.email}),n("Logged in. API key saved to ~/.pixelvault/config.json"),n(`Email: ${t.data.email}`),n(`Project: ${s}`)}else n(`Logged in as ${t.data.email}.`),n("Could not auto-configure API key. Set PIXELVAULT_API_KEY manually."),j({email:t.data.email,default_project:void 0}),v().api_key&&n("Warning: the saved api_key may belong to a previous login. Replace it with `pixelvault config set api_key <key>`.")}})});function Q(e){let r=/^(\d+)\s*([smhd]?)$/i.exec(e.trim());if(!r)throw new a(`Invalid --expires "${e}". Use e.g. 30m, 12h, 7d, or a number of seconds.`);let i=(r[2]??"").toLowerCase(),t=i==="d"?86400:i==="h"?3600:i==="m"?60:1,o=Number(r[1]??"")*t;if(o<Fe||o>Be)throw new a(`--expires must be between 60s and 30d (got ${o}s).`);return o}var Fe,Be,Z=m(()=>{"use strict";k();Fe=60,Be=720*60*60});function ee(e){let{idempotencyKey:r,dedupe:i,fileCount:t}=e;if(r!==void 0){if(!Ve.test(r))return"--idempotency-key must be 1-255 printable ASCII characters with no spaces.";if(i)return"--dedupe cannot be combined with --idempotency-key. Use the key for retry-safety, or --dedupe to reuse an identical image.";if(t>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 Ve,te=m(()=>{"use strict";Ve=/^[\x21-\x7e]{1,255}$/});var re={};w(re,{default:()=>ze});import{defineCommand as Me}from"citty";import{readFileSync as Je}from"fs";import{basename as Ge}from"path";var ze,ie=m(()=>{"use strict";b();y();Z();te();h();ze=Me({meta:{name:"upload",description:"Upload one or more images"},args:{files:{type:"positional",description:"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},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let r=f(),i=null;e.expires!==void 0&&(e.private||(n("--expires only applies to --private uploads (it sets the signed link's lifetime)."),process.exit(1)),i=Q(String(e.expires))),e.private&&e.folder&&n("Note: --folder is ignored for --private uploads.");let t=e._.length?e._.map(String):[String(e.files)],o=!1,s=e["idempotency-key"],d=ee({idempotencyKey:s,dedupe:e.dedupe,fileCount:t.length});d&&(n(d),process.exit(1));let g=[];for(let $ of t)try{let _=Je($),u=Ge($),x=new FormData;x.append("file",new File([_],u)),e.folder&&x.append("folder",e.folder),e.private&&(x.append("visibility","private"),i!==null&&x.append("sign_expires_in",String(i))),e.dedupe&&x.append("dedupe","true");let C=await p({method:"POST",path:"/v1/images",auth:r,formData:x,...s?{headers:{"Idempotency-Key":s}}:{}});if(C.data.replayed?n(`Replayed: ${$} was already uploaded with this key; returning the original.`):C.data.deduplicated&&n(`Deduplicated: ${$} matches an existing image; nothing new was stored.`),e.private&&C.data.visibility!=="private"){o=!0,n(`Refusing to report ${$}: server did not confirm a private upload.`);continue}e.json?g.push(C.data):l(e.optimized?C.data.optimized_url??C.data.url:C.data.url)}catch(_){o=!0,n(`Failed to upload ${$}: ${_ instanceof Error?_.message:String(_)}`)}e.json&&g.length>0&&c(g.length===1?g[0]:g),o&&process.exit(1)}})});var oe={};w(oe,{default:()=>Ye});import{defineCommand as Xe}from"citty";var Ye,ne=m(()=>{"use strict";b();y();h();Ye=Xe({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"},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let r=f(),i=new URLSearchParams({page:e.page,per_page:e["per-page"]}),t=await p({path:`/v1/images?${i}`,auth:r});if(e.json)c(t);else for(let o of t.data)l(o.url)}})});var se={};w(se,{applyTransform:()=>ae,default:()=>Qe});import{defineCommand as We}from"citty";import{writeFileSync as He}from"fs";function ae(e,r){if(!r)return e;let i=r.trim().replace(/^[?&]+/,"");if(!i)return e;let t=new URLSearchParams(i).toString();return t?e.includes("?")?`${e}&${t}`:`${e}?${t}`:e}var Qe,de=m(()=>{"use strict";b();y();h();k();Qe=We({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 r=f(),i=await p({path:`/v1/images/${encodeURIComponent(e.id)}`,auth:r}),t=ae(i.data.url,e.transform);if(e.output){let o=e.output,s;try{let d=await fetch(t);if(!d.ok)throw new a(`Failed to download image: HTTP ${d.status}`,1);s=await d.arrayBuffer()}catch(d){throw d instanceof a?d:new a(`Failed to download image: ${d instanceof Error?d.message:String(d)}`,1)}if(s.byteLength===0)throw new a("Downloaded 0 bytes \u2014 refusing to write an empty file",1);try{He(o,Buffer.from(s))}catch(d){throw new a(`Failed to save ${o}: ${d instanceof Error?d.message:String(d)}`,1)}n(`Saved ${o}`),e.json?c(e.transform?{...i,transformed_url:t,output:o}:{...i,output:o}):l(t);return}e.json?c(e.transform?{...i,transformed_url:t}:i):l(t)}})});var pe={};w(pe,{default:()=>et});import{defineCommand as Ze}from"citty";var et,le=m(()=>{"use strict";b();y();et=Ze({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 r=f();await p({method:"DELETE",path:`/v1/images/${e.id}`,auth:r})}})});var ce={};w(ce,{default:()=>at,defaultOutputPath:()=>ue});import{defineCommand as tt}from"citty";import{writeFileSync as rt,mkdirSync as it}from"fs";import{execFileSync as ot}from"child_process";function ue(e,r){return r&&r.length>0?r:`pixelvault-export-${e}.tar`}var nt,at,me=m(()=>{"use strict";b();y();h();k();nt=e=>new Promise(r=>setTimeout(r,e)),at=tt({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 r=f(),i=(Number(e.timeout)||600)*1e3,t;try{t=await p({method:"POST",path:"/v1/export",auth:r})}catch(u){throw u instanceof a&&/in progress/i.test(u.message)?new a("An export for this project is already in progress. Wait for it to finish, then try again.",1):u}n(`Export started \u2014 ${t.image_count} image(s), job ${t.job_id}`);let o=Date.now()+i,s,d=1e3;for(;s=await p({path:`/v1/export-jobs/${encodeURIComponent(t.job_id)}`,auth:r}),s.status!=="complete";){if(s.status==="failed")throw new a(`Export failed: ${s.error??"unknown error"}`,1);if(s.status==="expired")throw new a("Export expired before it could be downloaded. Start a new one.",1);if(Date.now()>o)throw new a(`Timed out after ${e.timeout}s (job still ${s.status}). It may still finish \u2014 re-run to poll, or raise --timeout.`,1);n(` ${s.status}\u2026 ${s.processed_count}/${s.image_count}`),await nt(d),d=Math.min(Math.round(d*1.5),5e3)}let g=ue(t.job_id,e.output),$=`${R()}/v1/export-jobs/${encodeURIComponent(t.job_id)}/download`,_;try{let u=await fetch($,{headers:{Authorization:`Bearer ${r}`}});if(!u.ok)throw new a(`Failed to download export: HTTP ${u.status}`,1);_=await u.arrayBuffer()}catch(u){throw u instanceof a?u:new a(`Failed to download export: ${u instanceof Error?u.message:String(u)}`,1)}if(_.byteLength===0)throw new a("Downloaded 0 bytes \u2014 refusing to write an empty archive.",1);try{rt(g,Buffer.from(_))}catch(u){throw new a(`Failed to save ${g}: ${u instanceof Error?u.message:String(u)}`,1)}if(n(`Saved ${g} \u2014 ${s.image_count} image(s), ${_.byteLength} bytes`),e.extract){let u=g.replace(/\.tar$/,"")||`${g}-extracted`;try{it(u,{recursive:!0}),ot("tar",["-xf",g,"-C",u],{stdio:"ignore"}),n(`Extracted into ${u}/`)}catch(x){throw new a(`Saved ${g}, but extraction failed (is \`tar\` installed?): ${x instanceof Error?x.message:String(x)}`,1)}}e.json?c({...s,output:g}):l(g)}})});var ye={};w(ye,{default:()=>dt,renderUsage:()=>ge});import{defineCommand as st}from"citty";function ge(e){return[`Plan: ${e.plan}`,`Period: ${e.period} (resets ${e.resets_at})`,`Storage: ${T(e.usage.storage_bytes)} used / ${D(e.limits.storage_bytes)} limit / ${D(e.remaining.storage_bytes)} remaining`,`Uploads: ${e.usage.uploads} used / ${fe(e.limits.uploads_per_month)} limit / ${fe(e.remaining.uploads)} remaining`,`Bandwidth: ${D(e.limits.bandwidth_bytes_per_month)} limit per month (usage not reported)`]}var D,fe,dt,he=m(()=>{"use strict";b();y();h();D=e=>e==null?"unlimited":T(e),fe=e=>e==null?"unlimited":String(e);dt=st({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 r=f(),i=await p({path:"/v1/usage",auth:r});if(e.json)c(i.data);else for(let t of ge(i.data))l(t)}})});var we={};w(we,{default:()=>ct,explainKeyError:()=>I});import{defineCommand as O}from"citty";function I(e){if(e instanceof E){switch(e.code){case"key_limit_reached":throw new a("Key limit reached (25 per project). Revoke an unused key with `pixelvault keys revoke <id>` first.",1);case"rate_limit_exceeded":{let r=e.retryAfter?` Retry after ${e.retryAfter} seconds.`:"";throw new a(`Rate limit exceeded (20 key changes per hour per project).${r}`,1)}case"cannot_revoke_current_key":throw new a("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 a("Key not found. Only secret keys can be revoked; check the id with `pixelvault keys list`.",1);case"validation_error":throw new a(`Invalid request: ${e.message}`,1);case"forbidden":throw new a("This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.",1)}if(e.status===403)throw new a("This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.",1)}throw e}var pt,lt,ut,ct,be=m(()=>{"use strict";b();y();k();h();pt=O({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 r=f(),i;try{i=await p({path:"/v1/keys",auth:r})}catch(t){I(t)}if(e.json){c(i.data);return}for(let t of i.data)l([t.id,t.type,t.environment,`${t.key_prefix}...`,t.label??"-",`created ${t.created_at}`,`last used ${t.last_used_at??"never"}`,t.current?"(current)":""].filter(Boolean).join(" "))}}),lt=O({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 r=f();if(e.env&&e.env!=="live"&&e.env!=="test")throw new a("--env must be 'live' or 'test'.",1);if(e.label&&e.label.length>60)throw new a("--label must be 60 characters or fewer.",1);let i={};e.label&&(i.label=e.label),e.env&&(i.environment=e.env);let t;try{t=await p({method:"POST",path:"/v1/keys",auth:r,body:i})}catch(o){I(o)}n("This key is shown ONCE. Store it now (secret manager / env var); never commit it or paste it into logs."),e.json?c(t.data):l(t.data.api_key)}}),ut=O({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 r=f(),i;try{i=await p({method:"DELETE",path:`/v1/keys/${encodeURIComponent(e.id)}`,auth:r})}catch(t){I(t)}e.json?c(i.data):n(`Revoked key ${e.id}.`)}}),ct=O({meta:{name:"keys",description:"Manage API keys (list, create, revoke)"},subCommands:{list:pt,create:lt,revoke:ut}})});var ve={};w(ve,{default:()=>ft,explainBillingError:()=>A});import{defineCommand as mt}from"citty";function A(e){if(e instanceof E)switch(e.code){case"test_key_forbidden":throw new a("Billing needs a live key (pv_live_...); test keys can't start checkout or open the portal.",1);case"publishable_key_forbidden":throw new a("Billing needs a secret live key (pv_live_...); publishable keys are not allowed.",1);case"email_not_verified":throw new a("Verify your email before upgrading (check your inbox for the code).",1);case"already_subscribed":throw new a("You already have a subscription. Change or cancel it with `pixelvault billing`.",1);case"no_subscription":throw new a("This account has no billing history yet. Start one with `pixelvault upgrade <starter|pro>`.",1);case"billing_unavailable":throw new a("Paid plans are not available right now. Try again later.",1);case"rate_limit_exceeded":{let r=e.retryAfter?` Retry after ${e.retryAfter} seconds.`:"";throw new a(`Rate limit exceeded (10 billing requests per hour per account).${r}`,1)}}throw e}var ft,F=m(()=>{"use strict";b();y();k();h();ft=mt({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 r=f(),i;try{i=await p({method:"POST",path:"/v1/billing/portal",body:{},auth:r})}catch(t){A(t)}e.json?c({url:i.url}):(l(i.url),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."))}})});var _e={};w(_e,{default:()=>yt});import{defineCommand as gt}from"citty";var B,yt,xe=m(()=>{"use strict";b();y();k();h();F();B=["starter","pro"],yt=gt({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: ${B.join(" | ")}`,required:!0},json:{type:"boolean",description:"Output { url } as JSON",default:!1}},async run({args:e}){let r=String(e.plan);if(!B.includes(r))throw new a(`Unknown plan "${r}". Choose one of: ${B.join(", ")}.`,1);let i=f(),t;try{t=await p({method:"POST",path:"/v1/billing/checkout",body:{plan:r},auth:i})}catch(o){A(o)}e.json?c({url:t.url}):(l(t.url),n("Open this link to complete payment. The plan changes once Stripe confirms \u2014 check with `pixelvault usage`."))}})});var ke={};w(ke,{default:()=>wt});import{defineCommand as ht}from"citty";var wt,$e=m(()=>{"use strict";y();h();wt=ht({meta:{name:"whoami",description:"Show current authentication state"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let r=v(),i=q();!i&&!r.email&&(n("Not authenticated. Run `pixelvault register` or `pixelvault login`."),process.exit(1));let t={email:r.email||"(unknown)",project:r.default_project||"(unknown)",api_key:i?`${i.slice(0,12)}...${i.slice(-4)}`:"(not set)",source:process.env.PIXELVAULT_API_KEY?"env":"config",identity_source:"cached"};if(e.json)c(t);else{let o=s=>s?" (cached, not verified for this key)":"";l(`Email: ${t.email}${o(r.email)}`),l(`Project: ${t.project}${o(r.default_project)}`),l(`API Key: ${t.api_key} (${t.source})`)}}})});var je={};w(je,{default:()=>xt});import{defineCommand as L}from"citty";var bt,vt,_t,xt,Ce=m(()=>{"use strict";y();h();bt=L({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 i=v()[e.key];i===void 0&&(n(`Key "${e.key}" is not set.`),process.exit(1)),l(String(i))}}),vt=L({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 r=["api_key","default_project","email","api_url"];r.includes(e.key)||(n(`Invalid key. Valid keys: ${r.join(", ")}`),process.exit(1)),e.key==="api_key"&&e.value!==v().api_key?j({api_key:e.value,email:void 0,default_project:void 0}):j({[e.key]:e.value}),n(`Set ${e.key} = ${e.key==="api_key"?"***":e.value}`)}}),_t=L({meta:{name:"show",description:"Show all config values"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let r=v();if(e.json)c(r);else{n(`Config: ${z()}`);for(let[i,t]of Object.entries(r)){let o=i==="api_key"&&t?`${t.slice(0,12)}...`:t;l(`${i} = ${o}`)}}}}),xt=L({meta:{name:"config",description:"Manage CLI configuration"},subCommands:{get:bt,set:vt,show:_t}})});import{defineCommand as kt}from"citty";k();h();import{runCommand as Se,runMain as V}from"citty";async function M(e,r=process.argv.slice(2),i=t=>process.exit(t)){if(r.includes("--help")||r.includes("-h")||r.length===1&&r[0]==="--version"){await V(e,{rawArgs:r});return}try{await Se(e,{rawArgs:r})}catch(o){if(o instanceof a){n(`Error: ${o.message}`),i(o.exitCode);return}if(o instanceof Error&&o.name==="CLIError"){await V(e,{rawArgs:r});return}n(`Error: ${o instanceof Error?o.message:String(o)}`),i(1)}}b();var $t=kt({meta:{name:"pixelvault",version:U,description:"CLI for PixelVault \u2014 agent-first image hosting"},subCommands:{register:()=>Promise.resolve().then(()=>(Y(),X)).then(e=>e.default),login:()=>Promise.resolve().then(()=>(H(),W)).then(e=>e.default),upload:()=>Promise.resolve().then(()=>(ie(),re)).then(e=>e.default),list:()=>Promise.resolve().then(()=>(ne(),oe)).then(e=>e.default),get:()=>Promise.resolve().then(()=>(de(),se)).then(e=>e.default),delete:()=>Promise.resolve().then(()=>(le(),pe)).then(e=>e.default),export:()=>Promise.resolve().then(()=>(me(),ce)).then(e=>e.default),usage:()=>Promise.resolve().then(()=>(he(),ye)).then(e=>e.default),keys:()=>Promise.resolve().then(()=>(be(),we)).then(e=>e.default),upgrade:()=>Promise.resolve().then(()=>(xe(),_e)).then(e=>e.default),billing:()=>Promise.resolve().then(()=>(F(),ve)).then(e=>e.default),whoami:()=>Promise.resolve().then(()=>($e(),ke)).then(e=>e.default),config:()=>Promise.resolve().then(()=>(Ce(),je)).then(e=>e.default)}});M($t);
|
|
1
|
+
var Ke=Object.defineProperty;var f=(e,i)=>()=>(e&&(i=e(e=0)),i);var h=(e,i)=>{for(var r in i)Ke(e,r,{get:i[r],enumerable:!0})};var o,v=f(()=>{"use strict";o=class extends Error{exitCode;constructor(i,r=1){super(i),this.name="CliError",this.exitCode=r}}});function l(e){process.stdout.write(e+`
|
|
2
|
+
`)}function s(e){process.stderr.write(e+`
|
|
3
|
+
`)}function m(e){l(JSON.stringify(e,null,2))}function U(e){if(e<1024)return`${e} B`;let i=["KB","MB","GB","TB"],r=e/1024,t=0;for(;r>=1024&&t<i.length-1;)r/=1024,t++;return`${r.toFixed(r>=100||Number.isInteger(r)?0:1)} ${i[t]}`}var y=f(()=>{"use strict"});import{readFileSync as Te,writeFileSync as Ne,mkdirSync as Ue}from"fs";import{join as W}from"path";import{homedir as Fe}from"os";function Q(){return F}function x(){try{let e=Te(F,"utf-8");return JSON.parse(e)}catch{return{}}}function Be(e){Ue(H,{recursive:!0,mode:448}),Ne(F,JSON.stringify(e,null,2)+`
|
|
4
|
+
`,{mode:384})}function k(e){let i=x();Be({...i,...e})}function D(){return process.env.PIXELVAULT_API_KEY||x().api_key}function A(){return process.env.PIXELVAULT_API_URL||x().api_url||De}function c(){let e=D();if(!e)throw new Error("Not authenticated. Run `pixelvault register` or `pixelvault login`, or set PIXELVAULT_API_KEY.");return e}var H,F,De,g=f(()=>{"use strict";H=W(Fe(),".pixelvault"),F=W(H,"config.json"),De="https://api.pixelvault.dev"});async function p(e){let r=`${A()}${e.path}`,t={...e.headers};t["X-PixelVault-Client"]||(t["X-PixelVault-Client"]=Je),e.auth&&(t.Authorization=`Bearer ${e.auth}`);let n;e.formData?n=e.formData:e.body&&(t["Content-Type"]="application/json",n=JSON.stringify(e.body));let a;try{a=await fetch(r,{method:e.method||"GET",headers:t,body:n})}catch(d){throw new o(`Network error: ${d instanceof Error?d.message:"Failed to connect"}`,1)}if(!a.ok){let d;try{d=await a.json()}catch{}let b=d?.error?.message||`HTTP ${a.status}: ${a.statusText}`;throw new R(b,a.status,d?.error?.code,a.headers?.get?.("Retry-After")??void 0)}return await a.json()}var B,Je,R,w=f(()=>{"use strict";g();v();B="0.11.0",Je=`pixelvault-cli/${B}`,R=class extends o{status;code;retryAfter;constructor(i,r,t,n){super(i,1),this.name="ApiError",this.status=r,this.code=t,this.retryAfter=n}}});import{createInterface as Me}from"readline";function P(e){let i=Me({input:process.stdin,output:process.stderr});return new Promise(r=>{i.question(e,t=>{i.close(),r(t.trim())})})}async function I(e){return process.stdin.isTTY?new Promise(i=>{process.stderr.write(e),process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.setEncoding("utf-8");let r="",t=n=>{n===`
|
|
5
|
+
`||n==="\r"||n===""?(process.stdin.setRawMode(!1),process.stdin.pause(),process.stdin.removeListener("data",t),process.stderr.write(`
|
|
6
|
+
`),i(r)):n===""?(process.stdin.setRawMode(!1),process.exit(130)):n==="\x7F"||n==="\b"?r=r.slice(0,-1):r+=n};process.stdin.on("data",t)}):P(e)}var J=f(()=>{"use strict"});var Z={};h(Z,{default:()=>Ve});import{defineCommand as ze}from"citty";var Ve,ee=f(()=>{"use strict";w();g();J();y();Ve=ze({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 i=!!process.stdin.isTTY,r=e.email||(i?await P("Email: "):"");r||(s("Email is required. Pass --email <email>."),process.exit(1));let t;e.password?t=e.password:e.passwordless?t=void 0:i?t=await I("Password (leave blank for a passwordless account): ")||void 0:t=void 0,t!==void 0&&t.length<8&&(s("Password must be at least 8 characters."),process.exit(1)),s("Creating account...");let n=await p({method:"POST",path:"/v1/auth/register",body:{email:r,...t?{password:t}:{},attribution:{source:"cli"}}});k({api_key:n.data.default_project.api_keys.live,default_project:n.data.default_project.id,email:n.data.email}),s("Account created. API key saved to ~/.pixelvault/config.json"),s(`Email: ${n.data.email}`),s(`Project: ${n.data.default_project.id}`),(n.data.password_set===!1||!t)&&s("No password set. To enable dashboard login, use 'Forgot password' at https://pixelvault.dev/forgot-password to set one."),n.data.email_verified||s("Verify your email to lift the upload limit for the free tier.")}})});var te={};h(te,{default:()=>Xe});import{defineCommand as Ge}from"citty";var Xe,ie=f(()=>{"use strict";w();g();J();y();Xe=Ge({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 i=e.email||await P("Email: "),r=e.password||await I("Password: ");(!i||!r)&&(s("Email and password are required."),process.exit(1)),s("Logging in...");let t=await p({method:"POST",path:"/v1/auth/login",body:{email:i,password:r}}),n=t.data.token,a;try{a=(await p({method:"GET",path:"/v1/projects",auth:n})).data[0]?.id}catch{}if(a){let d=await p({method:"POST",path:"/v1/auth/rotate-key",auth:n,body:{project_id:a}});k({api_key:d.data.api_key,default_project:a,email:t.data.email}),s("Logged in. API key saved to ~/.pixelvault/config.json"),s(`Email: ${t.data.email}`),s(`Project: ${a}`)}else s(`Logged in as ${t.data.email}.`),s("Could not auto-configure API key. Set PIXELVAULT_API_KEY manually."),k({email:t.data.email,default_project:void 0}),x().api_key&&s("Warning: the saved api_key may belong to a previous login. Replace it with `pixelvault config set api_key <key>`.")}})});function O(e,i="--expires"){let r=/^(\d+)\s*([smhd]?)$/i.exec(e.trim());if(!r)throw new o(`Invalid ${i} "${e}". Use e.g. 30m, 12h, 7d, or a number of seconds.`);let t=(r[2]??"").toLowerCase(),n=t==="d"?86400:t==="h"?3600:t==="m"?60:1,a=Number(r[1]??"")*n;if(a<Ye||a>We)throw new o(`${i} must be between 60s and 30d (got ${a}s).`);return a}var Ye,We,M=f(()=>{"use strict";v();Ye=60,We=720*60*60});function re(e){let{idempotencyKey:i,dedupe:r,fileCount:t}=e;if(i!==void 0){if(!He.test(i))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(t>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 He,ne=f(()=>{"use strict";He=/^[\x21-\x7e]{1,255}$/});function oe(e){return e==null||e===""?[]:(Array.isArray(e)?e:[e]).map(String)}function C(e){let i=new Map,r=oe(e.metadata);if(r.length>1)throw new o("--metadata can be given only once.");if(r.length===1){let t;try{t=JSON.parse(r[0])}catch{throw new o(`--metadata must be a JSON object, e.g. --metadata '{"env":"prod"}'.`)}if(t===null||typeof t!="object"||Array.isArray(t))throw new o(`--metadata must be a JSON object, e.g. --metadata '{"env":"prod"}'.`);for(let[n,a]of Object.entries(t))i.set(n,a)}for(let t of oe(e.meta)){let n=t.indexOf("=");if(n<=0)throw new o(`--meta expects key=value, got "${t}".`);let a=t.slice(0,n),d=t.slice(n+1);if(i.has(a)&&i.get(a)!==d)throw new o(`Metadata key "${a}" was given with conflicting values.`);i.set(a,d)}return i.size===0?null:Object.fromEntries(i)}var L=f(()=>{"use strict";v()});var ae={};h(ae,{default:()=>tt});import{defineCommand as Qe}from"citty";import{readFileSync as Ze}from"fs";import{basename as et}from"path";var tt,se=f(()=>{"use strict";w();g();M();ne();L();y();tt=Qe({meta:{name:"upload",description:"Upload one or more images"},args:{files:{type:"positional",description:"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}'`},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let i=c(),r=null;e.expires!==void 0&&(e.private||(s("--expires only applies to --private uploads (it sets the signed link's lifetime)."),process.exit(1)),r=O(String(e.expires))),e.private&&e.folder&&s("Note: --folder is ignored for --private uploads.");let t=e._.length?e._.map(String):[String(e.files)],n=!1,a=e["idempotency-key"],d=re({idempotencyKey:a,dedupe:e.dedupe,fileCount:t.length});d&&(s(d),process.exit(1));let b=C({meta:e.meta,metadata:e.metadata}),$=[];for(let _ of t)try{let u=Ze(_),E=et(_),j=new FormData;j.append("file",new File([u],E)),e.folder&&j.append("folder",e.folder),e.private&&(j.append("visibility","private"),r!==null&&j.append("sign_expires_in",String(r))),e.dedupe&&j.append("dedupe","true"),b&&j.append("metadata",JSON.stringify(b));let S=await p({method:"POST",path:"/v1/images",auth:i,formData:j,...a?{headers:{"Idempotency-Key":a}}:{}});if(S.data.replayed?s(`Replayed: ${_} was already uploaded with this key; returning the original.`):S.data.deduplicated&&s(`Deduplicated: ${_} matches an existing image; nothing new was stored.`),e.private&&S.data.visibility!=="private"){n=!0,s(`Refusing to report ${_}: server did not confirm a private upload.`);continue}e.json?$.push(S.data):l(e.optimized?S.data.optimized_url??S.data.url:S.data.url)}catch(u){n=!0,s(`Failed to upload ${_}: ${u instanceof Error?u.message:String(u)}`)}e.json&&$.length>0&&m($.length===1?$[0]:$),n&&process.exit(1)}})});var de={};h(de,{default:()=>rt});import{defineCommand as it}from"citty";var rt,pe=f(()=>{"use strict";w();g();v();M();y();rt=it({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 i=c(),r=(e._.length?e._.map(String):[String(e.prompt??"")]).join(" ").trim();if(!r)throw new o('Give a prompt, e.g. pixelvault generate "a red bicycle".');if(e.expires!==void 0&&!e.private)throw new o("--expires only applies to --private images (it sets the signed link's lifetime). Use --delete-after to expire the image itself.");let t=e.expires!==void 0?O(String(e.expires)):void 0,n=e["delete-after"]!==void 0?O(String(e["delete-after"]),"--delete-after"):void 0;e.private&&e.folder&&s("Note: --folder is ignored for --private images.");let a=await p({method:"POST",path:"/v1/images/generate",auth:i,body:{prompt:r,...e.model?{model:String(e.model)}:{},...e.folder?{folder:String(e.folder)}:{},...e.private?{visibility:"private"}:{},...t!==void 0?{sign_expires_in:t}:{},...n!==void 0?{expires_in:n}:{}}});if(e.private&&a.data.visibility!=="private")throw new o("Refusing to report the image: the server did not confirm it is private.");let d=a.data.generation;s(`Generated with ${d.model}: ${d.credits_charged} credit${d.credits_charged===1?"":"s"} used, ${d.credits_remaining} left this month.`),e.json?m(a.data):l(e.optimized?a.data.optimized_url??a.data.url:a.data.url)}})});var le={};h(le,{default:()=>ot});import{defineCommand as nt}from"citty";var ot,ue=f(()=>{"use strict";w();g();y();L();ot=nt({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"},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 i=c(),r=new URLSearchParams({page:e.page,per_page:e["per-page"]}),t=C({meta:e.meta,metadata:e.metadata});t&&r.set("metadata",JSON.stringify(t));let n=await p({path:`/v1/images?${r}`,auth:i});if(e.json)m(n);else for(let a of n.data)l(a.url)}})});var ce={};h(ce,{applyTransform:()=>me,default:()=>dt});import{defineCommand as at}from"citty";import{writeFileSync as st}from"fs";function me(e,i){if(!i)return e;let r=i.trim().replace(/^[?&]+/,"");if(!r)return e;let t=new URLSearchParams(r).toString();return t?e.includes("?")?`${e}&${t}`:`${e}?${t}`:e}var dt,fe=f(()=>{"use strict";w();g();y();v();dt=at({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 i=c(),r=await p({path:`/v1/images/${encodeURIComponent(e.id)}`,auth:i}),t=me(r.data.url,e.transform);if(e.output){let n=e.output,a;try{let d=await fetch(t);if(!d.ok)throw new o(`Failed to download image: HTTP ${d.status}`,1);a=await d.arrayBuffer()}catch(d){throw d instanceof o?d:new o(`Failed to download image: ${d instanceof Error?d.message:String(d)}`,1)}if(a.byteLength===0)throw new o("Downloaded 0 bytes \u2014 refusing to write an empty file",1);try{st(n,Buffer.from(a))}catch(d){throw new o(`Failed to save ${n}: ${d instanceof Error?d.message:String(d)}`,1)}s(`Saved ${n}`),e.json?m(e.transform?{...r,transformed_url:t,output:n}:{...r,output:n}):l(t);return}e.json?m(e.transform?{...r,transformed_url:t}:r):l(t)}})});var ge={};h(ge,{default:()=>lt});import{defineCommand as pt}from"citty";var lt,ye=f(()=>{"use strict";w();g();L();v();y();lt=pt({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 i=c(),r=C({meta:e.meta,metadata:e.metadata});if(e.clear&&r)throw new o("Use either --clear or --meta/--metadata, not both.");if(!e.clear&&!r)throw new o("Nothing to set: pass --meta key=value, --metadata '<json>', or --clear.");let t=await p({method:"PATCH",path:`/v1/images/${encodeURIComponent(String(e.id))}`,auth:i,body:{metadata:e.clear?null:r}});m(t.data.metadata)}})});var he={};h(he,{default:()=>mt});import{defineCommand as ut}from"citty";var mt,we=f(()=>{"use strict";w();g();mt=ut({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 i=c();await p({method:"DELETE",path:`/v1/images/${e.id}`,auth:i})}})});var ve={};h(ve,{default:()=>wt,defaultOutputPath:()=>be});import{defineCommand as ct}from"citty";import{writeFileSync as ft,mkdirSync as gt}from"fs";import{execFileSync as yt}from"child_process";function be(e,i){return i&&i.length>0?i:`pixelvault-export-${e}.tar`}var ht,wt,_e=f(()=>{"use strict";w();g();y();v();ht=e=>new Promise(i=>setTimeout(i,e)),wt=ct({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 i=c(),r=(Number(e.timeout)||600)*1e3,t;try{t=await p({method:"POST",path:"/v1/export",auth:i})}catch(u){throw u instanceof o&&/in progress/i.test(u.message)?new o("An export for this project is already in progress. Wait for it to finish, then try again.",1):u}s(`Export started \u2014 ${t.image_count} image(s), job ${t.job_id}`);let n=Date.now()+r,a,d=1e3;for(;a=await p({path:`/v1/export-jobs/${encodeURIComponent(t.job_id)}`,auth:i}),a.status!=="complete";){if(a.status==="failed")throw new o(`Export failed: ${a.error??"unknown error"}`,1);if(a.status==="expired")throw new o("Export expired before it could be downloaded. Start a new one.",1);if(Date.now()>n)throw new o(`Timed out after ${e.timeout}s (job still ${a.status}). It may still finish \u2014 re-run to poll, or raise --timeout.`,1);s(` ${a.status}\u2026 ${a.processed_count}/${a.image_count}`),await ht(d),d=Math.min(Math.round(d*1.5),5e3)}let b=be(t.job_id,e.output),$=`${A()}/v1/export-jobs/${encodeURIComponent(t.job_id)}/download`,_;try{let u=await fetch($,{headers:{Authorization:`Bearer ${i}`}});if(!u.ok)throw new o(`Failed to download export: HTTP ${u.status}`,1);_=await u.arrayBuffer()}catch(u){throw u instanceof o?u:new o(`Failed to download export: ${u instanceof Error?u.message:String(u)}`,1)}if(_.byteLength===0)throw new o("Downloaded 0 bytes \u2014 refusing to write an empty archive.",1);try{ft(b,Buffer.from(_))}catch(u){throw new o(`Failed to save ${b}: ${u instanceof Error?u.message:String(u)}`,1)}if(s(`Saved ${b} \u2014 ${a.image_count} image(s), ${_.byteLength} bytes`),e.extract){let u=b.replace(/\.tar$/,"")||`${b}-extracted`;try{gt(u,{recursive:!0}),yt("tar",["-xf",b,"-C",u],{stdio:"ignore"}),s(`Extracted into ${u}/`)}catch(E){throw new o(`Saved ${b}, but extraction failed (is \`tar\` installed?): ${E instanceof Error?E.message:String(E)}`,1)}}e.json?m({...a,output:b}):l(b)}})});var $e={};h($e,{default:()=>vt,renderUsage:()=>ke});import{defineCommand as bt}from"citty";function ke(e){return[`Plan: ${e.plan}`,`Period: ${e.period} (resets ${e.resets_at})`,`Storage: ${U(e.usage.storage_bytes)} used / ${z(e.limits.storage_bytes)} limit / ${z(e.remaining.storage_bytes)} remaining`,`Uploads: ${e.usage.uploads} used / ${xe(e.limits.uploads_per_month)} limit / ${xe(e.remaining.uploads)} remaining`,`Bandwidth: ${z(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 z,xe,vt,je=f(()=>{"use strict";w();g();y();z=e=>e==null?"unlimited":U(e),xe=e=>e==null?"unlimited":String(e);vt=bt({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 i=c(),r=await p({path:"/v1/usage",auth:i});if(e.json)m(r.data);else for(let t of ke(r.data))l(t)}})});var Se={};h(Se,{default:()=>$t,explainKeyError:()=>q});import{defineCommand as K}from"citty";function q(e){if(e instanceof R){switch(e.code){case"key_limit_reached":throw new o("Key limit reached (25 per project). Revoke an unused key with `pixelvault keys revoke <id>` first.",1);case"rate_limit_exceeded":{let i=e.retryAfter?` Retry after ${e.retryAfter} seconds.`:"";throw new o(`Rate limit exceeded (20 key changes per hour per project).${i}`,1)}case"cannot_revoke_current_key":throw new o("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 o("Key not found. Only secret keys can be revoked; check the id with `pixelvault keys list`.",1);case"validation_error":throw new o(`Invalid request: ${e.message}`,1);case"forbidden":throw new o("This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.",1)}if(e.status===403)throw new o("This endpoint needs a secret key (pv_live_...); publishable keys are not allowed.",1)}throw e}var _t,xt,kt,$t,Re=f(()=>{"use strict";w();g();v();y();_t=K({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 i=c(),r;try{r=await p({path:"/v1/keys",auth:i})}catch(t){q(t)}if(e.json){m(r.data);return}for(let t of r.data)l([t.id,t.type,t.environment,`${t.key_prefix}...`,t.label??"-",`created ${t.created_at}`,`last used ${t.last_used_at??"never"}`,t.current?"(current)":""].filter(Boolean).join(" "))}}),xt=K({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 i=c();if(e.env&&e.env!=="live"&&e.env!=="test")throw new o("--env must be 'live' or 'test'.",1);if(e.label&&e.label.length>60)throw new o("--label must be 60 characters or fewer.",1);let r={};e.label&&(r.label=e.label),e.env&&(r.environment=e.env);let t;try{t=await p({method:"POST",path:"/v1/keys",auth:i,body:r})}catch(n){q(n)}s("This key is shown ONCE. Store it now (secret manager / env var); never commit it or paste it into logs."),e.json?m(t.data):l(t.data.api_key)}}),kt=K({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 i=c(),r;try{r=await p({method:"DELETE",path:`/v1/keys/${encodeURIComponent(e.id)}`,auth:i})}catch(t){q(t)}e.json?m(r.data):s(`Revoked key ${e.id}.`)}}),$t=K({meta:{name:"keys",description:"Manage API keys (list, create, revoke)"},subCommands:{list:_t,create:xt,revoke:kt}})});var Ce={};h(Ce,{default:()=>St,explainBillingError:()=>T});import{defineCommand as jt}from"citty";function T(e){if(e instanceof R)switch(e.code){case"test_key_forbidden":throw new o("Billing needs a live key (pv_live_...); test keys can't start checkout or open the portal.",1);case"publishable_key_forbidden":throw new o("Billing needs a secret live key (pv_live_...); publishable keys are not allowed.",1);case"email_not_verified":throw new o("Verify your email before upgrading (check your inbox for the code).",1);case"already_subscribed":throw new o("You already have a subscription. Change or cancel it with `pixelvault billing`.",1);case"no_subscription":throw new o("This account has no billing history yet. Start one with `pixelvault upgrade <starter|pro>`.",1);case"billing_unavailable":throw new o("Paid plans are not available right now. Try again later.",1);case"rate_limit_exceeded":{let i=e.retryAfter?` Retry after ${e.retryAfter} seconds.`:"";throw new o(`Rate limit exceeded (10 billing requests per hour per account).${i}`,1)}}throw e}var St,V=f(()=>{"use strict";w();g();v();y();St=jt({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 i=c(),r;try{r=await p({method:"POST",path:"/v1/billing/portal",body:{},auth:i})}catch(t){T(t)}e.json?m({url:r.url}):(l(r.url),s("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 Ee={};h(Ee,{default:()=>Ct});import{defineCommand as Rt}from"citty";var G,Ct,Pe=f(()=>{"use strict";w();g();v();y();V();G=["starter","pro"],Ct=Rt({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: ${G.join(" | ")}`,required:!0},json:{type:"boolean",description:"Output { url } as JSON",default:!1}},async run({args:e}){let i=String(e.plan);if(!G.includes(i))throw new o(`Unknown plan "${i}". Choose one of: ${G.join(", ")}.`,1);let r=c(),t;try{t=await p({method:"POST",path:"/v1/billing/checkout",body:{plan:i},auth:r})}catch(n){T(n)}e.json?m({url:t.url}):(l(t.url),s("Open this link to complete payment. The plan changes once Stripe confirms \u2014 check with `pixelvault usage`."))}})});var Oe={};h(Oe,{default:()=>Pt});import{defineCommand as Et}from"citty";var Pt,Ae=f(()=>{"use strict";g();y();Pt=Et({meta:{name:"whoami",description:"Show current authentication state"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let i=x(),r=D();!r&&!i.email&&(s("Not authenticated. Run `pixelvault register` or `pixelvault login`."),process.exit(1));let t={email:i.email||"(unknown)",project:i.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)m(t);else{let n=a=>a?" (cached, not verified for this key)":"";l(`Email: ${t.email}${n(i.email)}`),l(`Project: ${t.project}${n(i.default_project)}`),l(`API Key: ${t.api_key} (${t.source})`)}}})});var Ie={};h(Ie,{default:()=>Lt});import{defineCommand as N}from"citty";var Ot,At,It,Lt,Le=f(()=>{"use strict";g();y();Ot=N({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=x()[e.key];r===void 0&&(s(`Key "${e.key}" is not set.`),process.exit(1)),l(String(r))}}),At=N({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 i=["api_key","default_project","email","api_url"];i.includes(e.key)||(s(`Invalid key. Valid keys: ${i.join(", ")}`),process.exit(1)),e.key==="api_key"&&e.value!==x().api_key?k({api_key:e.value,email:void 0,default_project:void 0}):k({[e.key]:e.value}),s(`Set ${e.key} = ${e.key==="api_key"?"***":e.value}`)}}),It=N({meta:{name:"show",description:"Show all config values"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let i=x();if(e.json)m(i);else{s(`Config: ${Q()}`);for(let[r,t]of Object.entries(i)){let n=r==="api_key"&&t?`${t.slice(0,12)}...`:t;l(`${r} = ${n}`)}}}}),Lt=N({meta:{name:"config",description:"Manage CLI configuration"},subCommands:{get:Ot,set:At,show:It}})});import{defineCommand as Kt}from"citty";v();y();import{runCommand as qe,runMain as X}from"citty";async function Y(e,i=process.argv.slice(2),r=t=>process.exit(t)){if(i.includes("--help")||i.includes("-h")||i.length===1&&i[0]==="--version"){await X(e,{rawArgs:i});return}try{await qe(e,{rawArgs:i})}catch(n){if(n instanceof o){s(`Error: ${n.message}`),r(n.exitCode);return}if(n instanceof Error&&n.name==="CLIError"){await X(e,{rawArgs:i});return}s(`Error: ${n instanceof Error?n.message:String(n)}`),r(1)}}w();var qt=Kt({meta:{name:"pixelvault",version:B,description:"CLI for PixelVault \u2014 agent-first image hosting"},subCommands:{register:()=>Promise.resolve().then(()=>(ee(),Z)).then(e=>e.default),login:()=>Promise.resolve().then(()=>(ie(),te)).then(e=>e.default),upload:()=>Promise.resolve().then(()=>(se(),ae)).then(e=>e.default),generate:()=>Promise.resolve().then(()=>(pe(),de)).then(e=>e.default),list:()=>Promise.resolve().then(()=>(ue(),le)).then(e=>e.default),get:()=>Promise.resolve().then(()=>(fe(),ce)).then(e=>e.default),meta:()=>Promise.resolve().then(()=>(ye(),ge)).then(e=>e.default),delete:()=>Promise.resolve().then(()=>(we(),he)).then(e=>e.default),export:()=>Promise.resolve().then(()=>(_e(),ve)).then(e=>e.default),usage:()=>Promise.resolve().then(()=>(je(),$e)).then(e=>e.default),keys:()=>Promise.resolve().then(()=>(Re(),Se)).then(e=>e.default),upgrade:()=>Promise.resolve().then(()=>(Pe(),Ee)).then(e=>e.default),billing:()=>Promise.resolve().then(()=>(V(),Ce)).then(e=>e.default),whoami:()=>Promise.resolve().then(()=>(Ae(),Oe)).then(e=>e.default),config:()=>Promise.resolve().then(()=>(Le(),Ie)).then(e=>e.default)}});Y(qt);
|
|
7
7
|
//# 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/commands/upload.ts","../src/commands/list.ts","../src/commands/get.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}\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.9.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.body) {\n headers[\"Content-Type\"] = \"application/json\";\n fetchBody = JSON.stringify(options.body);\n }\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: options.method || \"GET\",\n headers,\n body: fetchBody,\n });\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): number {\n const match = /^(\\d+)\\s*([smhd]?)$/i.exec(input.trim());\n if (!match) {\n throw new CliError(\n `Invalid --expires \"${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 `--expires 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 { defineCommand } from \"citty\";\nimport { readFileSync } from \"node:fs\";\nimport { basename } 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 { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\ninterface UploadResponse {\n data: {\n id: string;\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 };\n}\n\nexport default defineCommand({\n meta: {\n name: \"upload\",\n description: \"Upload one or more images\",\n },\n args: {\n files: {\n type: \"positional\",\n description: \"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 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 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 results: UploadResponse[\"data\"][] = [];\n\n for (const filePath of files) {\n try {\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 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 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 { stdout, jsonOut } from \"../lib/output.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 }>;\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 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\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 { 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 };\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 { 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}\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 ];\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 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 list: () => import(\"./commands/list.js\").then((m) => m.default),\n get: () => import(\"./commands/get.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","i","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","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","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","upload_exports","__export","upload_default","defineCommand","readFileSync","basename","init_upload","__esmMin","init_client","init_config","init_duration","init_upload_options","init_output","args","apiKey","requireApiKey","signExpires","stderr","parseExpires","files","hadError","idempotencyKey","reuseError","validateReuseOptions","results","filePath","buffer","fileName","formData","res","apiRequest","stdout","err","jsonOut","list_exports","__export","list_default","defineCommand","init_list","__esmMin","init_client","init_config","init_output","args","apiKey","requireApiKey","params","res","apiRequest","jsonOut","img","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","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/commands/upload.ts","../src/commands/generate.ts","../src/commands/list.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}\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.11.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.body) {\n headers[\"Content-Type\"] = \"application/json\";\n fetchBody = JSON.stringify(options.body);\n }\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: options.method || \"GET\",\n headers,\n body: fetchBody,\n });\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 { defineCommand } from \"citty\";\nimport { readFileSync } from \"node:fs\";\nimport { basename } 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 { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\ninterface UploadResponse {\n data: {\n id: string;\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 };\n}\n\nexport default defineCommand({\n meta: {\n name: \"upload\",\n description: \"Upload one or more images\",\n },\n args: {\n files: {\n type: \"positional\",\n description: \"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 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 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 const results: UploadResponse[\"data\"][] = [];\n\n for (const filePath of files) {\n try {\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 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 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 }>;\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 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\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 { 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 };\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 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 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","i","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","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","upload_exports","__export","upload_default","defineCommand","readFileSync","basename","init_upload","__esmMin","init_client","init_config","init_duration","init_upload_options","init_metadata","init_output","args","apiKey","requireApiKey","signExpires","stderr","parseExpires","files","hadError","idempotencyKey","reuseError","validateReuseOptions","metadata","parseMetadataFlags","results","filePath","buffer","fileName","formData","res","apiRequest","stdout","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","res","apiRequest","jsonOut","img","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"]}
|