pixelvault-cli 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,9 @@ pixelvault get img_xyz -o thumb.webp -t "w=400&fmt=webp" # download a transfor
43
43
 
44
44
  # Delete an image
45
45
  pixelvault delete img_xyz
46
+
47
+ # Export everything — all your images as a tar archive (no lock-in)
48
+ pixelvault export -o my-images.tar
46
49
  ```
47
50
 
48
51
  ## Agent Integration
@@ -80,6 +83,7 @@ npx pixelvault-cli upload build-output.png
80
83
  | `list` | List uploaded images |
81
84
  | `get <id>` | Get an image's URL/metadata, or download it (optionally transformed) |
82
85
  | `delete <id>` | Delete an image |
86
+ | `export` | Export all project images as a tar archive |
83
87
  | `whoami` | Show current auth state |
84
88
  | `config get\|set\|show` | Manage CLI configuration |
85
89
 
@@ -107,6 +111,22 @@ background removal (`segment=foreground`), effects (`blur`/`saturation`/`rotate`
107
111
  and watermark (`tile=img_logo.png` — another of your own images). Reserved
108
112
  characters in values are percent-encoded for you (e.g. `background=#ffaa00`).
109
113
 
114
+ ### Export Options
115
+
116
+ ```bash
117
+ pixelvault export # download all your images to pixelvault-export-<job>.tar
118
+ pixelvault export -o backup.tar # choose the output path
119
+ pixelvault export --extract # untar into a directory after download (needs `tar`)
120
+ pixelvault export --timeout 900 # wait up to 900s for a large export (default 600)
121
+ pixelvault export --json # emit the final job JSON (archive path still to stdout)
122
+ ```
123
+
124
+ `export` downloads **every image in your project** plus a `manifest.json` (ids,
125
+ filenames, storage keys, URLs, metadata) as a single tar archive — the free,
126
+ no-lock-in "take your data anywhere" command. It's async: the CLI starts the job,
127
+ polls until it's ready, then downloads. The archive path goes to **stdout**,
128
+ progress to **stderr**.
129
+
110
130
  ### List Options
111
131
 
112
132
  ```bash
@@ -131,6 +151,14 @@ pixelvault config get api_key # Get a value
131
151
 
132
152
  - Node.js 20+
133
153
 
154
+ ## Learn more
155
+
156
+ - [PixelVault](https://pixelvault.dev) — agent-first image hosting
157
+ - [API docs](https://pixelvault.dev/docs/) — full REST reference + OpenAPI 3.1
158
+ - [Guide: Image hosting for AI agents](https://pixelvault.dev/blog/image-hosting-for-ai-agents/)
159
+ - [The simplest image hosting API](https://pixelvault.dev/image-hosting-api/)
160
+ - [Agent skill](https://github.com/pixelvault-dev/skill) — for Claude Code, Codex, and Cursor
161
+
134
162
  ## License
135
163
 
136
164
  MIT
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- var ee=Object.defineProperty;var l=(e,i)=>()=>(e&&(i=e(e=0)),i);var g=(e,i)=>{for(var r in i)ee(e,r,{get:i[r],enumerable:!0})};var p,b=l(()=>{"use strict";p=class extends Error{exitCode;constructor(i,r=1){super(i),this.name="CliError",this.exitCode=r}}});function d(e){process.stdout.write(e+`
2
- `)}function s(e){process.stderr.write(e+`
3
- `)}function f(e){d(JSON.stringify(e,null,2))}var u=l(()=>{"use strict"});import{readFileSync as te,writeFileSync as re,mkdirSync as ie}from"fs";import{join as S}from"path";import{homedir as oe}from"os";function I(){return R}function y(){try{let e=te(R,"utf-8");return JSON.parse(e)}catch{return{}}}function ne(e){ie(A,{recursive:!0,mode:448}),re(R,JSON.stringify(e,null,2)+`
4
- `,{mode:384})}function v(e){let i=y();ne({...i,...e})}function j(){return process.env.PIXELVAULT_API_KEY||y().api_key}function L(){return process.env.PIXELVAULT_API_URL||y().api_url||se}function w(){let e=j();if(!e)throw new Error("Not authenticated. Run `pixelvault register` or `pixelvault login`, or set PIXELVAULT_API_KEY.");return e}var A,R,se,c=l(()=>{"use strict";A=S(oe(),".pixelvault"),R=S(A,"config.json"),se="https://api.pixelvault.dev"});async function m(e){let r=`${L()}${e.path}`,t={...e.headers};e.auth&&(t.Authorization=`Bearer ${e.auth}`);let o;e.formData?o=e.formData:e.body&&(t["Content-Type"]="application/json",o=JSON.stringify(e.body));let a;try{a=await fetch(r,{method:e.method||"GET",headers:t,body:o})}catch(n){throw new p(`Network error: ${n instanceof Error?n.message:"Failed to connect"}`,1)}if(!a.ok){let n;try{n=await a.json()}catch{}let h=n?.error?.message||`HTTP ${a.status}: ${a.statusText}`;throw new p(h,1)}return await a.json()}var x=l(()=>{"use strict";c();b()});import{createInterface as ae}from"readline";function E(e){let i=ae({input:process.stdin,output:process.stderr});return new Promise(r=>{i.question(e,t=>{i.close(),r(t.trim())})})}async function P(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=o=>{o===`
5
- `||o==="\r"||o===""?(process.stdin.setRawMode(!1),process.stdin.pause(),process.stdin.removeListener("data",t),process.stderr.write(`
6
- `),i(r)):o===""?(process.stdin.setRawMode(!1),process.exit(130)):o==="\x7F"||o==="\b"?r=r.slice(0,-1):r+=o};process.stdin.on("data",t)}):E(e)}var $=l(()=>{"use strict"});var O={};g(O,{default:()=>de});import{defineCommand as pe}from"citty";var de,T=l(()=>{"use strict";x();c();$();u();de=pe({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 E("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 P("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 o=await m({method:"POST",path:"/v1/auth/register",body:t?{email:r,password:t}:{email:r}});v({api_key:o.data.default_project.api_keys.live,default_project:o.data.default_project.id,email:o.data.email}),s("Account created. API key saved to ~/.pixelvault/config.json"),s(`Email: ${o.data.email}`),s(`Project: ${o.data.default_project.id}`),(o.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."),o.data.email_verified||s("Verify your email to lift the upload limit for the free tier.")}})});var U={};g(U,{default:()=>me});import{defineCommand as le}from"citty";var me,q=l(()=>{"use strict";x();c();$();u();me=le({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 E("Email: "),r=e.password||await P("Password: ");(!i||!r)&&(s("Email and password are required."),process.exit(1)),s("Logging in...");let t=await m({method:"POST",path:"/v1/auth/login",body:{email:i,password:r}}),o=t.data.token,a;try{a=(await m({method:"GET",path:"/v1/projects",auth:o})).data[0]?.id}catch{}if(a){let n=await m({method:"POST",path:"/v1/auth/rotate-key",auth:o,body:{project_id:a}});v({api_key:n.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."),v({email:t.data.email})}})});function N(e){let i=/^(\d+)\s*([smhd]?)$/i.exec(e.trim());if(!i)throw new p(`Invalid --expires "${e}". Use e.g. 30m, 12h, 7d, or a number of seconds.`);let r=(i[2]??"").toLowerCase(),t=r==="d"?86400:r==="h"?3600:r==="m"?60:1,o=Number(i[1]??"")*t;if(o<fe||o>ce)throw new p(`--expires must be between 60s and 30d (got ${o}s).`);return o}var fe,ce,F=l(()=>{"use strict";b();fe=60,ce=720*60*60});var K={};g(K,{default:()=>we});import{defineCommand as ue}from"citty";import{readFileSync as ge}from"fs";import{basename as ye}from"path";var we,D=l(()=>{"use strict";x();c();F();u();we=ue({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)"},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let i=w(),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=N(String(e.expires))),e.private&&e.folder&&s("Note: --folder is ignored for --private uploads.");let t=Array.isArray(e.files)?e.files:[e.files],o=!1,a=[];for(let n of t)try{let h=ge(n),Z=ye(n),_=new FormData;_.append("file",new File([h],Z)),e.folder&&_.append("folder",e.folder),e.private&&(_.append("visibility","private"),r!==null&&_.append("sign_expires_in",String(r)));let C=await m({method:"POST",path:"/v1/images",auth:i,formData:_});if(e.private&&C.data.visibility!=="private"){o=!0,s(`Refusing to report ${n}: server did not confirm a private upload.`);continue}e.json?a.push(C.data):d(C.data.url)}catch(h){o=!0,s(`Failed to upload ${n}: ${h instanceof Error?h.message:String(h)}`)}e.json&&a.length>0&&f(a.length===1?a[0]:a),o&&process.exit(1)}})});var V={};g(V,{default:()=>ve});import{defineCommand as he}from"citty";var ve,G=l(()=>{"use strict";x();c();u();ve=he({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 i=w(),r=new URLSearchParams({page:e.page,per_page:e["per-page"]}),t=await m({path:`/v1/images?${r}`,auth:i});if(e.json)f(t);else for(let o of t.data)d(o.url)}})});var B={};g(B,{applyTransform:()=>J,default:()=>be});import{defineCommand as xe}from"citty";import{writeFileSync as _e}from"fs";function J(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 be,X=l(()=>{"use strict";x();c();u();b();be=xe({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=w(),r=await m({path:`/v1/images/${encodeURIComponent(e.id)}`,auth:i}),t=J(r.data.url,e.transform);if(e.output){let o=e.output,a;try{let n=await fetch(t);if(!n.ok)throw new p(`Failed to download image: HTTP ${n.status}`,1);a=await n.arrayBuffer()}catch(n){throw n instanceof p?n:new p(`Failed to download image: ${n instanceof Error?n.message:String(n)}`,1)}if(a.byteLength===0)throw new p("Downloaded 0 bytes \u2014 refusing to write an empty file",1);try{_e(o,Buffer.from(a))}catch(n){throw new p(`Failed to save ${o}: ${n instanceof Error?n.message:String(n)}`,1)}s(`Saved ${o}`),e.json?f(e.transform?{...r,transformed_url:t,output:o}:{...r,output:o}):d(t);return}e.json?f(e.transform?{...r,transformed_url:t}:r):d(t)}})});var M={};g(M,{default:()=>Pe});import{defineCommand as Ee}from"citty";var Pe,Y=l(()=>{"use strict";x();c();Pe=Ee({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=w();await m({method:"DELETE",path:`/v1/images/${e.id}`,auth:i})}})});var z={};g(z,{default:()=>Ce});import{defineCommand as ke}from"citty";var Ce,H=l(()=>{"use strict";c();u();Ce=ke({meta:{name:"whoami",description:"Show current authentication state"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let i=y(),r=j();!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"};e.json?f(t):(d(`Email: ${t.email}`),d(`Project: ${t.project}`),d(`API Key: ${t.api_key} (${t.source})`))}})});var Q={};g(Q,{default:()=>Se});import{defineCommand as k}from"citty";var Re,je,$e,Se,W=l(()=>{"use strict";c();u();Re=k({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=y()[e.key];r===void 0&&(s(`Key "${e.key}" is not set.`),process.exit(1)),d(String(r))}}),je=k({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)),v({[e.key]:e.value}),s(`Set ${e.key} = ${e.key==="api_key"?"***":e.value}`)}}),$e=k({meta:{name:"show",description:"Show all config values"},args:{json:{type:"boolean",description:"Output as JSON",default:!1}},async run({args:e}){let i=y();if(e.json)f(i);else{s(`Config: ${I()}`);for(let[r,t]of Object.entries(i)){let o=r==="api_key"&&t?`${t.slice(0,12)}...`:t;d(`${r} = ${o}`)}}}}),Se=k({meta:{name:"config",description:"Manage CLI configuration"},subCommands:{get:Re,set:je,show:$e}})});b();u();import{defineCommand as Ae,runMain as Ie}from"citty";var Le=Ae({meta:{name:"pixelvault",version:"0.2.0",description:"CLI for PixelVault \u2014 agent-first image hosting"},subCommands:{register:()=>Promise.resolve().then(()=>(T(),O)).then(e=>e.default),login:()=>Promise.resolve().then(()=>(q(),U)).then(e=>e.default),upload:()=>Promise.resolve().then(()=>(D(),K)).then(e=>e.default),list:()=>Promise.resolve().then(()=>(G(),V)).then(e=>e.default),get:()=>Promise.resolve().then(()=>(X(),B)).then(e=>e.default),delete:()=>Promise.resolve().then(()=>(Y(),M)).then(e=>e.default),whoami:()=>Promise.resolve().then(()=>(H(),z)).then(e=>e.default),config:()=>Promise.resolve().then(()=>(W(),Q)).then(e=>e.default)}});Ie(Le).catch(e=>{e instanceof p&&(s(`Error: ${e.message}`),process.exit(e.exitCode)),s(`Error: ${e instanceof Error?e.message:String(e)}`),process.exit(1)});
1
+ var ie=Object.defineProperty;var m=(e,r)=>()=>(e&&(r=e(e=0)),r);var x=(e,r)=>{for(var o in r)ie(e,o,{get:r[o],enumerable:!0})};var p,$=m(()=>{"use strict";p=class extends Error{exitCode;constructor(r,o=1){super(r),this.name="CliError",this.exitCode=o}}});function u(e){process.stdout.write(e+`
2
+ `)}function n(e){process.stderr.write(e+`
3
+ `)}function c(e){u(JSON.stringify(e,null,2))}var w=m(()=>{"use strict"});import{readFileSync as ne,writeFileSync as se,mkdirSync as ae}from"fs";import{join as L}from"path";import{homedir as pe}from"os";function T(){return k}function v(){try{let e=ne(k,"utf-8");return JSON.parse(e)}catch{return{}}}function le(e){ae(O,{recursive:!0,mode:448}),se(k,JSON.stringify(e,null,2)+`
4
+ `,{mode:384})}function b(e){let r=v();le({...r,...e})}function A(){return process.env.PIXELVAULT_API_KEY||v().api_key}function E(){return process.env.PIXELVAULT_API_URL||v().api_url||de}function h(){let e=A();if(!e)throw new Error("Not authenticated. Run `pixelvault register` or `pixelvault login`, or set PIXELVAULT_API_KEY.");return e}var O,k,de,g=m(()=>{"use strict";O=L(pe(),".pixelvault"),k=L(O,"config.json"),de="https://api.pixelvault.dev"});async function f(e){let o=`${E()}${e.path}`,t={...e.headers};t["X-PixelVault-Client"]||(t["X-PixelVault-Client"]=ue),e.auth&&(t.Authorization=`Bearer ${e.auth}`);let i;e.formData?i=e.formData:e.body&&(t["Content-Type"]="application/json",i=JSON.stringify(e.body));let s;try{s=await fetch(o,{method:e.method||"GET",headers:t,body:i})}catch(a){throw new p(`Network error: ${a instanceof Error?a.message:"Failed to connect"}`,1)}if(!s.ok){let a;try{a=await s.json()}catch{}let l=a?.error?.message||`HTTP ${s.status}: ${s.statusText}`;throw new p(l,1)}return await s.json()}var ue,_=m(()=>{"use strict";g();$();ue="pixelvault-cli/0.5.1"});import{createInterface as fe}from"readline";function j(e){let r=fe({input:process.stdin,output:process.stderr});return new Promise(o=>{r.question(e,t=>{r.close(),o(t.trim())})})}async function S(e){return process.stdin.isTTY?new Promise(r=>{process.stderr.write(e),process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.setEncoding("utf-8");let o="",t=i=>{i===`
5
+ `||i==="\r"||i===""?(process.stdin.setRawMode(!1),process.stdin.pause(),process.stdin.removeListener("data",t),process.stderr.write(`
6
+ `),r(o)):i===""?(process.stdin.setRawMode(!1),process.exit(130)):i==="\x7F"||i==="\b"?o=o.slice(0,-1):o+=i};process.stdin.on("data",t)}):j(e)}var I=m(()=>{"use strict"});var U={};x(U,{default:()=>ce});import{defineCommand as me}from"citty";var ce,F=m(()=>{"use strict";_();g();I();w();ce=me({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,o=e.email||(r?await j("Email: "):"");o||(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 S("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 i=await f({method:"POST",path:"/v1/auth/register",body:{email:o,...t?{password:t}:{},attribution:{source:"cli"}}});b({api_key:i.data.default_project.api_keys.live,default_project:i.data.default_project.id,email:i.data.email}),n("Account created. API key saved to ~/.pixelvault/config.json"),n(`Email: ${i.data.email}`),n(`Project: ${i.data.default_project.id}`),(i.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."),i.data.email_verified||n("Verify your email to lift the upload limit for the free tier.")}})});var q={};x(q,{default:()=>ye});import{defineCommand as ge}from"citty";var ye,N=m(()=>{"use strict";_();g();I();w();ye=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 r=e.email||await j("Email: "),o=e.password||await S("Password: ");(!r||!o)&&(n("Email and password are required."),process.exit(1)),n("Logging in...");let t=await f({method:"POST",path:"/v1/auth/login",body:{email:r,password:o}}),i=t.data.token,s;try{s=(await f({method:"GET",path:"/v1/projects",auth:i})).data[0]?.id}catch{}if(s){let a=await f({method:"POST",path:"/v1/auth/rotate-key",auth:i,body:{project_id:s}});b({api_key:a.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."),b({email:t.data.email})}})});function D(e){let r=/^(\d+)\s*([smhd]?)$/i.exec(e.trim());if(!r)throw new p(`Invalid --expires "${e}". Use e.g. 30m, 12h, 7d, or a number of seconds.`);let o=(r[2]??"").toLowerCase(),t=o==="d"?86400:o==="h"?3600:o==="m"?60:1,i=Number(r[1]??"")*t;if(i<we||i>he)throw new p(`--expires must be between 60s and 30d (got ${i}s).`);return i}var we,he,K=m(()=>{"use strict";$();we=60,he=720*60*60});var B={};x(B,{default:()=>be});import{defineCommand as xe}from"citty";import{readFileSync as ve}from"fs";import{basename as _e}from"path";var be,V=m(()=>{"use strict";_();g();K();w();be=xe({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)"},json:{type:"boolean",description:"Output full JSON response",default:!1}},async run({args:e}){let r=h(),o=null;e.expires!==void 0&&(e.private||(n("--expires only applies to --private uploads (it sets the signed link's lifetime)."),process.exit(1)),o=D(String(e.expires))),e.private&&e.folder&&n("Note: --folder is ignored for --private uploads.");let t=Array.isArray(e.files)?e.files:[e.files],i=!1,s=[];for(let a of t)try{let l=ve(a),C=_e(a),y=new FormData;y.append("file",new File([l],C)),e.folder&&y.append("folder",e.folder),e.private&&(y.append("visibility","private"),o!==null&&y.append("sign_expires_in",String(o)));let d=await f({method:"POST",path:"/v1/images",auth:r,formData:y});if(e.private&&d.data.visibility!=="private"){i=!0,n(`Refusing to report ${a}: server did not confirm a private upload.`);continue}e.json?s.push(d.data):u(d.data.url)}catch(l){i=!0,n(`Failed to upload ${a}: ${l instanceof Error?l.message:String(l)}`)}e.json&&s.length>0&&c(s.length===1?s[0]:s),i&&process.exit(1)}})});var M={};x(M,{default:()=>je});import{defineCommand as $e}from"citty";var je,J=m(()=>{"use strict";_();g();w();je=$e({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=h(),o=new URLSearchParams({page:e.page,per_page:e["per-page"]}),t=await f({path:`/v1/images?${o}`,auth:r});if(e.json)c(t);else for(let i of t.data)u(i.url)}})});var G={};x(G,{applyTransform:()=>X,default:()=>Re});import{defineCommand as Ee}from"citty";import{writeFileSync as Se}from"fs";function X(e,r){if(!r)return e;let o=r.trim().replace(/^[?&]+/,"");if(!o)return e;let t=new URLSearchParams(o).toString();return t?e.includes("?")?`${e}&${t}`:`${e}?${t}`:e}var Re,Y=m(()=>{"use strict";_();g();w();$();Re=Ee({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=h(),o=await f({path:`/v1/images/${encodeURIComponent(e.id)}`,auth:r}),t=X(o.data.url,e.transform);if(e.output){let i=e.output,s;try{let a=await fetch(t);if(!a.ok)throw new p(`Failed to download image: HTTP ${a.status}`,1);s=await a.arrayBuffer()}catch(a){throw a instanceof p?a:new p(`Failed to download image: ${a instanceof Error?a.message:String(a)}`,1)}if(s.byteLength===0)throw new p("Downloaded 0 bytes \u2014 refusing to write an empty file",1);try{Se(i,Buffer.from(s))}catch(a){throw new p(`Failed to save ${i}: ${a instanceof Error?a.message:String(a)}`,1)}n(`Saved ${i}`),e.json?c(e.transform?{...o,transformed_url:t,output:i}:{...o,output:i}):u(t);return}e.json?c(e.transform?{...o,transformed_url:t}:o):u(t)}})});var z={};x(z,{default:()=>Pe});import{defineCommand as Ce}from"citty";var Pe,H=m(()=>{"use strict";_();g();Pe=Ce({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=h();await f({method:"DELETE",path:`/v1/images/${e.id}`,auth:r})}})});var Q={};x(Q,{default:()=>Te,defaultOutputPath:()=>W});import{defineCommand as ke}from"citty";import{writeFileSync as Ae,mkdirSync as Ie}from"fs";import{execFileSync as Le}from"child_process";function W(e,r){return r&&r.length>0?r:`pixelvault-export-${e}.tar`}var Oe,Te,Z=m(()=>{"use strict";_();g();w();$();Oe=e=>new Promise(r=>setTimeout(r,e)),Te=ke({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=h(),o=(Number(e.timeout)||600)*1e3,t;try{t=await f({method:"POST",path:"/v1/export",auth:r})}catch(d){throw d instanceof p&&/in progress/i.test(d.message)?new p("An export for this project is already in progress. Wait for it to finish, then try again.",1):d}n(`Export started \u2014 ${t.image_count} image(s), job ${t.job_id}`);let i=Date.now()+o,s,a=1e3;for(;s=await f({path:`/v1/export-jobs/${encodeURIComponent(t.job_id)}`,auth:r}),s.status!=="complete";){if(s.status==="failed")throw new p(`Export failed: ${s.error??"unknown error"}`,1);if(s.status==="expired")throw new p("Export expired before it could be downloaded. Start a new one.",1);if(Date.now()>i)throw new p(`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 Oe(a),a=Math.min(Math.round(a*1.5),5e3)}let l=W(t.job_id,e.output),C=`${E()}/v1/export-jobs/${encodeURIComponent(t.job_id)}/download`,y;try{let d=await fetch(C,{headers:{Authorization:`Bearer ${r}`}});if(!d.ok)throw new p(`Failed to download export: HTTP ${d.status}`,1);y=await d.arrayBuffer()}catch(d){throw d instanceof p?d:new p(`Failed to download export: ${d instanceof Error?d.message:String(d)}`,1)}if(y.byteLength===0)throw new p("Downloaded 0 bytes \u2014 refusing to write an empty archive.",1);try{Ae(l,Buffer.from(y))}catch(d){throw new p(`Failed to save ${l}: ${d instanceof Error?d.message:String(d)}`,1)}if(n(`Saved ${l} \u2014 ${s.image_count} image(s), ${y.byteLength} bytes`),e.extract){let d=l.replace(/\.tar$/,"")||`${l}-extracted`;try{Ie(d,{recursive:!0}),Le("tar",["-xf",l,"-C",d],{stdio:"ignore"}),n(`Extracted into ${d}/`)}catch(P){throw new p(`Saved ${l}, but extraction failed (is \`tar\` installed?): ${P instanceof Error?P.message:String(P)}`,1)}}e.json?c({...s,output:l}):u(l)}})});var ee={};x(ee,{default:()=>Fe});import{defineCommand as Ue}from"citty";var Fe,te=m(()=>{"use strict";g();w();Fe=Ue({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(),o=A();!o&&!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:o?`${o.slice(0,12)}...${o.slice(-4)}`:"(not set)",source:process.env.PIXELVAULT_API_KEY?"env":"config"};e.json?c(t):(u(`Email: ${t.email}`),u(`Project: ${t.project}`),u(`API Key: ${t.api_key} (${t.source})`))}})});var re={};x(re,{default:()=>Ke});import{defineCommand as R}from"citty";var qe,Ne,De,Ke,oe=m(()=>{"use strict";g();w();qe=R({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 o=v()[e.key];o===void 0&&(n(`Key "${e.key}" is not set.`),process.exit(1)),u(String(o))}}),Ne=R({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)),b({[e.key]:e.value}),n(`Set ${e.key} = ${e.key==="api_key"?"***":e.value}`)}}),De=R({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: ${T()}`);for(let[o,t]of Object.entries(r)){let i=o==="api_key"&&t?`${t.slice(0,12)}...`:t;u(`${o} = ${i}`)}}}}),Ke=R({meta:{name:"config",description:"Manage CLI configuration"},subCommands:{get:qe,set:Ne,show:De}})});$();w();import{defineCommand as Be,runMain as Ve}from"citty";var Me=Be({meta:{name:"pixelvault",version:"0.2.0",description:"CLI for PixelVault \u2014 agent-first image hosting"},subCommands:{register:()=>Promise.resolve().then(()=>(F(),U)).then(e=>e.default),login:()=>Promise.resolve().then(()=>(N(),q)).then(e=>e.default),upload:()=>Promise.resolve().then(()=>(V(),B)).then(e=>e.default),list:()=>Promise.resolve().then(()=>(J(),M)).then(e=>e.default),get:()=>Promise.resolve().then(()=>(Y(),G)).then(e=>e.default),delete:()=>Promise.resolve().then(()=>(H(),z)).then(e=>e.default),export:()=>Promise.resolve().then(()=>(Z(),Q)).then(e=>e.default),whoami:()=>Promise.resolve().then(()=>(te(),ee)).then(e=>e.default),config:()=>Promise.resolve().then(()=>(oe(),re)).then(e=>e.default)}});Ve(Me).catch(e=>{e instanceof p&&(n(`Error: ${e.message}`),process.exit(e.exitCode)),n(`Error: ${e instanceof Error?e.message:String(e)}`),process.exit(1)});
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/commands/upload.ts","../src/commands/list.ts","../src/commands/get.ts","../src/commands/delete.ts","../src/commands/whoami.ts","../src/commands/config.ts","../src/index.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","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\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 (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 CliError(message, 1);\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.\n body: password ? { email, password } : { email },\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 { 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 updateConfig({ email: loginRes.data.email });\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","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 { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\ninterface UploadResponse {\n data: {\n id: string;\n 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 };\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 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 // citty passes positional as a single string; we handle globs via shell expansion\n const files = Array.isArray(args.files) ? args.files : [args.files];\n let hadError = false;\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 const res = await apiRequest<UploadResponse>({\n method: \"POST\",\n path: \"/v1/images\",\n auth: apiKey,\n formData,\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(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 { 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 };\n\n if (args.json) {\n jsonOut(info);\n } else {\n stdout(`Email: ${info.email}`);\n stdout(`Project: ${info.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 updateConfig({ [args.key]: args.value });\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, runMain } from \"citty\";\nimport { CliError } from \"./lib/errors.js\";\nimport { stderr } from \"./lib/output.js\";\n\nconst main = defineCommand({\n meta: {\n name: \"pixelvault\",\n version: \"0.2.0\",\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 whoami: () => import(\"./commands/whoami.js\").then((m) => m.default),\n config: () => import(\"./commands/config.js\").then((m) => m.default),\n },\n});\n\nrunMain(main).catch((err: unknown) => {\n if (err instanceof CliError) {\n stderr(`Error: ${err.message}`);\n process.exit(err.exitCode);\n }\n stderr(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n});\n"],"mappings":"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","names":["CliError","init_errors","__esmMin","message","exitCode","stdout","text","stderr","jsonOut","data","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","fetchBody","res","err","CliError","errorBody","message","init_client","__esmMin","init_config","init_errors","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","parseExpires","input","match","CliError","unit","multiplier","seconds","MIN_EXPIRES_SECONDS","MAX_EXPIRES_SECONDS","init_duration","__esmMin","init_errors","upload_exports","__export","upload_default","defineCommand","readFileSync","basename","init_upload","__esmMin","init_client","init_config","init_duration","init_output","args","apiKey","requireApiKey","signExpires","stderr","parseExpires","files","hadError","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","whoami_exports","__export","whoami_default","defineCommand","init_whoami","__esmMin","init_config","init_output","args","config","readConfig","apiKey","getApiKey","stderr","info","jsonOut","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","init_errors","init_output","defineCommand","runMain","main","m","err","CliError","stderr"]}
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/commands/upload.ts","../src/commands/list.ts","../src/commands/get.ts","../src/commands/delete.ts","../src/commands/export.ts","../src/commands/whoami.ts","../src/commands/config.ts","../src/index.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","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.\nconst CLIENT_ID = \"pixelvault-cli/0.5.1\";\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 CliError(message, 1);\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 { 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 updateConfig({ email: loginRes.data.email });\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","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 { stdout, stderr, jsonOut } from \"../lib/output.js\";\n\ninterface UploadResponse {\n data: {\n id: string;\n 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 };\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 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 // citty passes positional as a single string; we handle globs via shell expansion\n const files = Array.isArray(args.files) ? args.files : [args.files];\n let hadError = false;\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 const res = await apiRequest<UploadResponse>({\n method: \"POST\",\n path: \"/v1/images\",\n auth: apiKey,\n formData,\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(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 { 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 };\n\n if (args.json) {\n jsonOut(info);\n } else {\n stdout(`Email: ${info.email}`);\n stdout(`Project: ${info.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 updateConfig({ [args.key]: args.value });\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, runMain } from \"citty\";\nimport { CliError } from \"./lib/errors.js\";\nimport { stderr } from \"./lib/output.js\";\n\nconst main = defineCommand({\n meta: {\n name: \"pixelvault\",\n version: \"0.2.0\",\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 whoami: () => import(\"./commands/whoami.js\").then((m) => m.default),\n config: () => import(\"./commands/config.js\").then((m) => m.default),\n },\n});\n\nrunMain(main).catch((err: unknown) => {\n if (err instanceof CliError) {\n stderr(`Error: ${err.message}`);\n process.exit(err.exitCode);\n }\n stderr(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n});\n"],"mappings":"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","names":["CliError","init_errors","__esmMin","message","exitCode","stdout","text","stderr","jsonOut","data","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","init_client","__esmMin","init_config","init_errors","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","parseExpires","input","match","CliError","unit","multiplier","seconds","MIN_EXPIRES_SECONDS","MAX_EXPIRES_SECONDS","init_duration","__esmMin","init_errors","upload_exports","__export","upload_default","defineCommand","readFileSync","basename","init_upload","__esmMin","init_client","init_config","init_duration","init_output","args","apiKey","requireApiKey","signExpires","stderr","parseExpires","files","hadError","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","whoami_exports","__export","whoami_default","defineCommand","init_whoami","__esmMin","init_config","init_output","args","config","readConfig","apiKey","getApiKey","stderr","info","jsonOut","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","init_errors","init_output","defineCommand","runMain","main","m","err","CliError","stderr"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelvault-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "CLI for PixelVault — agent-first image hosting",
5
5
  "type": "module",
6
6
  "bin": {