@mynthio/cli 0.0.20 → 0.0.21

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.
Files changed (3) hide show
  1. package/README.md +12 -6
  2. package/dist/bin.js +8 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -99,23 +99,29 @@ output.
99
99
  | -------------- | -------------------------------------------------------------------- |
100
100
  | `-s, --search` | Fuzzy match on model ID (which carries the org) and display name. |
101
101
  | `--org` | Only one org, fuzzy matched — `--org bfl` finds `black-forest-labs`. |
102
- | `--max-price` | Base per-image price at or below this, in USD. |
103
- | `--min-price` | Base per-image price at or above this, in USD. |
102
+ | `--type` | Only one media type: `image` or `video`. |
103
+ | `--max-price` | Price at or below this, in USD. |
104
+ | `--min-price` | Price at or above this, in USD. |
104
105
  | `--4k` | Only models that publish a 4K price. |
105
- | `--capability` | `img2img` (bills for image inputs) or `txt2img` (prompt only). |
106
+ | `--capability` | One generation mode: `txt2img`, `img2img`, `txt2vid`, or `img2vid`. |
106
107
 
107
108
  ```bash
108
109
  mynth models list -s "gemini flash" # fuzzy: tolerates typos and word order
109
110
  mynth models list --capability img2img --max-price 0.03
111
+ mynth models list --type video --json
110
112
  mynth models list --org bfl --json
111
113
  ```
112
114
 
113
115
  Search is fuzzy, not substring: `sedream` finds Seedream, and results come back ranked by relevance.
114
116
  A query that matches nothing prints `No models matched the filters.` and still exits `0`.
115
117
 
116
- `--capability` is derived from pricing, because the catalog exposes no capability field: a model that
117
- publishes `perInput` pricing bills for image inputs, so it accepts them. Models that both take a
118
- prompt and accept images are therefore reported as `img2img`.
118
+ The catalog carries image and video models. Image models are priced per image, video models per
119
+ second of output, so the `Price` column reports the cheapest rate a model charges and marks the
120
+ per-second ones with `/s`. `--max-price` and `--min-price` compare against that same figure, which
121
+ is why they are most useful alongside `--type`.
122
+
123
+ `--capability` matches the modes a model actually serves, so a model that both takes a prompt and
124
+ accepts images matches `txt2img` and `img2img` alike.
119
125
 
120
126
  ## Analyzing images
121
127
 
package/dist/bin.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import M from'chalk';import {Command,Option,Help}from'commander';import {z as z$1}from'zod';import {readFile,mkdir,writeFile,rename,rm,stat}from'fs/promises';import {hostname,homedir}from'os';import {resolve,join,dirname,extname,basename}from'path';import {spawn}from'child_process';import {existsSync}from'fs';import Bo from'ora';import wn from'fuzzysort';var ge="client_01KATK792RR5ZCHMF5YMNN1ZSE",Be=process.env.MYNTH_WORKOS_API_URL??"https://api.workos.com",He="0.0.20",ze=e=>e.replace(/\/+$/,""),We=e=>e!==void 0&&e.length>0?e:void 0,G=()=>({apiUrl:ze(process.env.MYNTH_API_URL??"https://api.mynth.io"),docsUrl:ze(process.env.MYNTH_DOCS_URL??"https://docs.mynth.io"),envApiKey:We(process.env.MYNTH_API_KEY),envDestination:We(process.env.MYNTH_DESTINATION),debug:process.env.MYNTH_DEBUG==="1"||process.env.MYNTH_DEBUG==="true"});var v={error:1,usage:2,auth:3,insufficientCredits:4,moderation:5,rateLimited:6},y=class extends Error{constructor(t,n){super(t,n),this.name=new.target.name;}},l=class extends y{},$=class extends y{},k=class extends y{status;code;constructor(t,n){super(t,n),this.status=n.status,this.code=n.code;}},_=class extends y{code;constructor(t,n,r){super(n,r),this.code=t;}},jn={UNAUTHORIZED:v.auth,INSUFFICIENT_SCOPE:v.auth,VALIDATION_ERROR:v.usage,INSUFFICIENT_BALANCE:v.insufficientCredits,SPENDING_LIMIT_EXCEEDED:v.insufficientCredits,RESTRICTED_CONTENT:v.moderation},Ye=e=>{if(e instanceof l)return v.usage;if(e instanceof $)return v.auth;if(e instanceof k){let n=e.code!==void 0?jn[e.code]:void 0;return n!==void 0?n:e.status===401||e.status===403?v.auth:e.status===429?v.rateLimited:v.error}let t=e?.code;return typeof t=="string"&&t.startsWith("commander.")?v.usage:v.error},fe=e=>{let t=e.result,n=[...(e.errors??[]).map(r=>r.code),...(t?.images??[]).map(r=>r.error?.code)].filter(r=>typeof r=="string");return n.find(r=>r==="RESTRICTED_CONTENT")??n[0]},Ge=e=>fe(e)==="RESTRICTED_CONTENT"?v.moderation:v.error;var s=(e="")=>{process.stdout.write(`${e}
2
+ import U from'chalk';import {Command,Option,Help}from'commander';import {z as z$1}from'zod';import {readFile,mkdir,writeFile,rename,rm,stat}from'fs/promises';import {hostname,homedir}from'os';import {resolve,join,dirname,extname,basename}from'path';import {spawn}from'child_process';import {existsSync}from'fs';import er from'ora';import kn from'fuzzysort';var ge="client_01KATK792RR5ZCHMF5YMNN1ZSE",Ye=process.env.MYNTH_WORKOS_API_URL??"https://api.workos.com",He="0.0.21",We=e=>e.replace(/\/+$/,""),Be=e=>e!==void 0&&e.length>0?e:void 0,G=()=>({apiUrl:We(process.env.MYNTH_API_URL??"https://api.mynth.io"),docsUrl:We(process.env.MYNTH_DOCS_URL??"https://docs.mynth.io"),envApiKey:Be(process.env.MYNTH_API_KEY),envDestination:Be(process.env.MYNTH_DESTINATION),debug:process.env.MYNTH_DEBUG==="1"||process.env.MYNTH_DEBUG==="true"});var v={error:1,usage:2,auth:3,insufficientCredits:4,moderation:5,rateLimited:6},y=class extends Error{constructor(t,n){super(t,n),this.name=new.target.name;}},l=class extends y{},$=class extends y{},k=class extends y{status;code;constructor(t,n){super(t,n),this.status=n.status,this.code=n.code;}},_=class extends y{code;constructor(t,n,i){super(n,i),this.code=t;}},Fn={UNAUTHORIZED:v.auth,INSUFFICIENT_SCOPE:v.auth,VALIDATION_ERROR:v.usage,INSUFFICIENT_BALANCE:v.insufficientCredits,SPENDING_LIMIT_EXCEEDED:v.insufficientCredits,RESTRICTED_CONTENT:v.moderation},Ge=e=>{if(e instanceof l)return v.usage;if(e instanceof $)return v.auth;if(e instanceof k){let n=e.code!==void 0?Fn[e.code]:void 0;return n!==void 0?n:e.status===401||e.status===403?v.auth:e.status===429?v.rateLimited:v.error}let t=e?.code;return typeof t=="string"&&t.startsWith("commander.")?v.usage:v.error},fe=e=>{let t=e.result,n=[...(e.errors??[]).map(i=>i.code),...(t?.images??[]).map(i=>i.error?.code)].filter(i=>typeof i=="string");return n.find(i=>i==="RESTRICTED_CONTENT")??n[0]},qe=e=>fe(e)==="RESTRICTED_CONTENT"?v.moderation:v.error;var s=(e="")=>{process.stdout.write(`${e}
3
3
  `);},T=(e="")=>{process.stderr.write(`${e}
4
- `);},u=e=>{s(JSON.stringify(e,null,2));},g={ok:M.green("\u2713"),fail:M.red("\u2717"),pending:M.yellow("\u2026")},q=e=>e==="completed"?g.ok:e==="failed"?g.fail:g.pending,O=(e,t,n="s")=>`${e} ${t}${e===1?"":n}`,qe=(e,t=2)=>{let n=" ".repeat(t);return e.split(`
5
- `).map(r=>`${n}${r}`).join(`
6
- `)};var S=z$1.lazy(()=>z$1.union([z$1.string(),z$1.number(),z$1.boolean(),z$1.null(),z$1.array(S),z$1.record(S)])),Ve=e=>z$1.object({data:e}),Nn=z$1.object({id:z$1.string(),email:z$1.string(),first_name:z$1.string().nullable().optional(),last_name:z$1.string().nullable().optional()}),Xe=z$1.object({device_code:z$1.string(),user_code:z$1.string(),verification_uri:z$1.string(),verification_uri_complete:z$1.string().optional(),expires_in:z$1.number(),interval:z$1.number().optional()}),Ze=z$1.object({access_token:z$1.string(),refresh_token:z$1.string(),user:Nn.optional()}),Qe=z$1.object({error:z$1.string().optional(),error_description:z$1.string().optional(),message:z$1.string().optional(),code:z$1.string().optional()}),et=z$1.object({kind:z$1.literal("api_key"),api_key:z$1.string(),id:z$1.string().optional()}),R=["generate","manage","keys"],we=z$1.object({raw:z$1.string(),apiKey:z$1.object({id:z$1.string(),name:z$1.string().optional(),keyPreview:z$1.string(),scopes:z$1.array(z$1.string())})}),Dn=z$1.union([z$1.string(),z$1.number()]).nullable().optional(),tt=z$1.object({id:z$1.string(),name:z$1.string().nullable(),keyPreview:z$1.string(),scopes:z$1.array(z$1.string()),spendingLimit:Dn,spendingLimitPeriod:z$1.string().nullable().optional(),createdAt:z$1.string()}),Ln=z$1.union([z$1.object({mode:z$1.literal("unlimited")}),z$1.object({mode:z$1.literal("limited"),limit:z$1.string(),period:z$1.string(),used:z$1.string(),remaining:z$1.string()})]),nt=z$1.object({userId:z$1.string(),auth:z$1.object({method:z$1.string(),apiKey:z$1.object({id:z$1.string(),name:z$1.string().nullable(),keyPreview:z$1.string(),scopes:z$1.array(z$1.string()).optional(),spending:Ln.optional()}).optional()})}),ot=z$1.object({balance:z$1.string(),reserved:z$1.string(),available:z$1.string(),currency:z$1.string()}),rt=z$1.object({id:z$1.string(),displayName:z$1.string().nullable(),pricing:z$1.object({perImage:z$1.object({base:z$1.string(),"4k":z$1.string().optional()}),perInput:z$1.string().optional()}).nullable()}),V=z$1.enum(["pending","completed","failed"]),it=z$1.object({id:z$1.string(),type:z$1.string(),status:V,userId:z$1.string().optional(),apiKeyId:z$1.string().nullable().optional(),cost:z$1.string().nullable(),request:S.optional(),result:S.nullable(),errors:z$1.array(z$1.object({code:z$1.string(),message:z$1.string().optional()})).nullish(),createdAt:z$1.string(),updatedAt:z$1.string()}),st=z$1.object({id:z$1.string(),type:z$1.string(),status:z$1.string(),cost:z$1.string().nullable(),createdAt:z$1.string(),updatedAt:z$1.string()}),at=z$1.object({id:z$1.string(),type:z$1.string(),status:V,result:S.nullable()}),dt=z$1.object({urls:z$1.array(z$1.string())}),lt=z$1.object({taskId:z$1.string(),estimatedCost:z$1.string().optional(),access:z$1.object({publicAccessToken:z$1.string()}).optional()}),ct=z$1.object({estimatedCost:z$1.string(),currency:z$1.string(),estimateKind:z$1.enum(["exact","upper_bound"])}),pt=z$1.object({url:z$1.string(),level:z$1.string()}),mt=z$1.object({url:z$1.string(),alt:z$1.string()}),ut=z$1.object({url:z$1.string(),score:z$1.number(),summary:z$1.string(),findings:z$1.array(z$1.object({finding:z$1.string(),category:z$1.string(),severity:z$1.string(),where:z$1.string(),confidence:z$1.string()})).optional(),strengths:z$1.array(z$1.object({strength:z$1.string(),confidence:z$1.string()})).optional()}),he=z$1.object({code:z$1.string(),message:z$1.string().optional()}),Fn=z$1.union([z$1.object({status:z$1.literal("success"),id:z$1.string().optional(),url:z$1.string().nullable().optional(),mynth_url:z$1.string().optional(),size:z$1.string().optional(),format:z$1.string().optional(),rating:z$1.union([z$1.object({status:z$1.literal("success"),level:z$1.string()}),z$1.object({status:z$1.literal("failed"),error:he})]).optional(),destination:z$1.union([z$1.object({status:z$1.literal("success"),name:z$1.string()}),z$1.object({status:z$1.literal("failed"),name:z$1.string(),error:he})]).optional()}),z$1.object({status:z$1.literal("failed"),error:he})]),gt=z$1.object({model:z$1.string().optional(),images:z$1.array(Fn).optional(),magic_prompt:z$1.object({positive:z$1.string(),negative:z$1.string().optional()}).optional()}),U=z$1.object({id:z$1.string(),name:z$1.string(),provider:z$1.object({id:z$1.string()}).catchall(S),config:z$1.object({path_template:z$1.string(),url_template:z$1.string().optional()}).partial(),createdAt:z$1.string(),updatedAt:z$1.string()}),ft=z$1.union([z$1.literal("all"),z$1.array(z$1.string())]),yt=z$1.object({id:z$1.string(),enabled:z$1.boolean(),url:z$1.string(),secret:z$1.string(),events:ft,apiKeyIds:z$1.array(z$1.string()).nullish(),oauthEnabled:z$1.boolean().optional(),createdAt:z$1.string().optional()}),ht=z$1.object({id:z$1.string(),enabled:z$1.boolean().optional(),url:z$1.string(),events:ft,apiKeyIds:z$1.array(z$1.string()).nullish(),oauthEnabled:z$1.boolean().optional()});var Un=e=>{if(e===void 0)return "";let t=new URLSearchParams;for(let[n,r]of Object.entries(e))r!==void 0&&t.set(n,String(r));return t.size>0?`?${t}`:""},Mn=e=>e===void 0?{headers:{}}:e instanceof FormData?{body:e,headers:{}}:{body:JSON.stringify(e),headers:{"Content-Type":"application/json"}},Jn=async e=>{try{return await e.text()}catch{return ""}},Kn=async(e,t)=>{let n=await Jn(e),r,o;try{let a=JSON.parse(n);typeof a.code=="string"&&(r=a.code),typeof a.message=="string"?o=a.message:typeof a.error=="string"&&(o=a.error);}catch{}return new k(`${t} failed (${e.status}): ${o??n??"no body"}`,{status:e.status,...r!==void 0?{code:r}:{}})},X=class{constructor(t,n){this.tokens=n;this.baseUrl=t.apiUrl;}baseUrl;async send(t,n,r={}){let o=await this.attempt(n,r);if(o.ok)return o;throw await Kn(o,t)}async fetch(t,n,r,o={}){let a=await this.send(t,n,o),d;try{d=await a.json();}catch(w){throw new k(`${t} returned invalid JSON: ${w.message}`,{status:a.status,cause:w})}let m=Ve(r).safeParse(d);if(!m.success)throw new k(`${t} returned an unexpected response shape`,{status:a.status,cause:m.error});return m.data.data}async call(t,n,r={}){await this.send(t,n,r);}async attempt(t,n){let{body:r,headers:o}=Mn(n.body),a=n.auth===false?void 0:n.token??await this.tokens.token();try{return await fetch(`${this.baseUrl}${t}${Un(n.query)}`,{method:n.method??(n.body!==void 0?"POST":"GET"),headers:{...o,...a!==void 0?{Authorization:`Bearer ${a}`}:{}},...r!==void 0?{body:r}:{}})}catch(d){throw new k(`request to ${t} failed: ${d.message}`,{status:0,cause:d})}}};var zn=2048,Wn=/^[A-Za-z0-9._~%-]+$/,Bn=e=>{let t=e.trim();if(t.length===0)throw new l("documentation path must not be empty");if(t.length>zn)throw new l("documentation path is too long");if(t.startsWith("//")||t.includes("://"))throw new l("documentation path must be a path, not a URL");if(/[?#\\]/.test(t))throw new l("documentation path must not contain a query, fragment, or backslash");let n=t.startsWith("/")?t.slice(1):t;if(n.endsWith(".md"))throw new l("documentation path must not include the .md suffix");let r=n.split("/");if(r.some(o=>!Wn.test(o)||o==="."||o===".."))throw new l("documentation path contains an invalid segment");return r.join("/")},Hn=e=>e.length>500?`${e.slice(0,500)}\u2026`:e,wt=async(e,t)=>{let n;try{n=await fetch(e);}catch(o){throw new k(`${t} failed: ${o.message}`,{status:0,cause:o})}let r=await n.text().catch(()=>"");if(!n.ok)throw new k(`${t} failed (${n.status}): ${Hn(r)||"no body"}`,{status:n.status});return r},Z=class{constructor(t){this.docsUrl=t;}async get(t){let n=Bn(t),r=n.split("/").map(encodeURIComponent).join("/");return {path:n,content:await wt(`${this.docsUrl}/${r}.md`,`docs fetch for ${n}`)}}list(){return wt(`${this.docsUrl}/llms.txt`,"docs index fetch")}};var to="credentials.json",no=384,oo=()=>{let e=process.env.XDG_CONFIG_HOME;return join(e!==void 0&&e.length>0?e:join(homedir(),".config"),"mynth")},ro=async e=>{try{return await stat(e),true}catch{return false}},Q=class{filePath;constructor(){this.filePath=join(oo(),to);}async get(){if(!await ro(this.filePath))return;let t;try{t=await readFile(this.filePath,"utf8");}catch(o){throw new y(`could not read ${this.filePath}: ${o.message}`,{cause:o})}let n;try{n=JSON.parse(t);}catch(o){throw new y(`${this.filePath} is not valid JSON`,{cause:o})}let r=et.safeParse(n);if(!r.success)throw new y(`${this.filePath} has an unexpected shape; run \`mynth auth login\``,{cause:r.error});return r.data}async set(t){try{await mkdir(dirname(this.filePath),{recursive:true,mode:448});let n=`${this.filePath}.${process.pid}.tmp`;await writeFile(n,JSON.stringify(t),{encoding:"utf8",mode:no}),await rename(n,this.filePath);}catch(n){throw new y(`could not write ${this.filePath}: ${n.message}`,{cause:n})}}async clear(){try{await rm(this.filePath,{force:true});}catch(t){throw new y(`could not delete ${this.filePath}: ${t.message}`,{cause:t})}}};var ee=class{constructor(t){this.config=t;this.store=new Q,this.envApiKeySet=t.envApiKey!==void 0;}store;envApiKeySet;cached;async token(){if(this.config.envApiKey!==void 0)return this.config.envApiKey;if(this.cached!==void 0)return this.cached;let t=await this.read();if(t===void 0)throw new $("not authenticated: run `mynth auth login` or set MYNTH_API_KEY");return this.cached=t.api_key,this.cached}async status(){if(this.envApiKeySet)return {kind:"env"};let t=await this.read().catch(()=>{});return t===void 0?{kind:"none"}:{kind:"stored",credentials:t}}async save(t){await this.store.set(t),this.cached=void 0;}async clear(){await this.store.clear(),this.cached=void 0;}async read(){try{return await this.store.get()}catch(t){throw new $(`could not read stored credentials: ${t.message}`,{cause:t})}}};var kt=()=>{let e=G(),t=new ee(e);return {config:e,session:t,api:new X(e,t),docs:new Z(e.docsUrl)}};var be=["...........",".....4.....","....343....",".....2.....",".344.2.443.","..4432344..",".....2.....",".233.2.332.","..3322233..",".....1.....","..........."],J={1:"#318267",2:"#00a27a",3:"#33bb92",4:"#6bd1ad"},io=(e,t)=>{let n=r=>r!==".";return !n(e)&&!n(t)?" ":M.level===0?n(e)&&n(t)?"\u2588":n(e)?"\u2580":"\u2584":n(t)?n(e)?e===t?M.hex(J[e])("\u2588"):M.hex(J[e]).bgHex(J[t])("\u2580"):M.hex(J[t])("\u2584"):M.hex(J[e])("\u2580")},bt=()=>{let e=[];for(let t=0;t<be.length;t+=2){let n=be[t],r=be[t+1]??"",o="";for(let a=0;a<n.length;a+=1)o+=io(n[a],r[a]??".");e.push(o.trimEnd());}return e.filter(t=>t.length>0).join(`
7
- `)};var vt=(e,t)=>e.fetch("api key create","/api-key",we,{body:{name:t.name,scopes:t.scopes},token:t.token}),At=(e,t)=>e.fetch("api key create","/api-key",we,{body:{name:t.name,scopes:t.scopes}}),$t=e=>e.fetch("api key list","/api-key",z$1.array(tt)),te=(e,t)=>e.call("api key delete",`/api-key/${t}`,{method:"DELETE"});var E=(e,t,n)=>{if(e.length===0){s(n);return}let r=e.map(d=>t.map(m=>m.value(d))),o=t.map((d,m)=>Math.max(d.header.length,...r.map(w=>w[m].length))),a=d=>d.map((m,w)=>w===d.length-1?m:m.padEnd(o[w])).join(" ");s(a(t.map(d=>d.header)));for(let d of r)s(a(d));};var f=()=>new Option("--json","Output machine-readable JSON instead of a human-readable summary"),j=()=>new Option("--yes","Confirm the destructive action (required; there is no interactive prompt)");var xt=["generate"],lo=e=>{if(e===void 0)return xt;let t=e.split(",").map(r=>r.trim()).filter(Boolean),n=t.filter(r=>!R.includes(r));if(t.length===0||n.length>0)throw new l(`invalid --scopes: ${n.join(", ")||"empty"}. Valid scopes: ${R.join(", ")}`);return t},Tt=e=>{let t=new Command("api-key").description("Manage Mynth API keys");return t.command("create").description("Create an API key. The key itself is shown once, on success.").argument("<name>","Name for the key, e.g. the app or environment it belongs to").option("--scopes <list>",`Comma-separated scopes (default: ${xt.join(",")}). Widening beyond \`generate\` requires the dashboard: the API refuses scope escalation from a CLI session.`).addOption(f()).action(async(n,r)=>{let o=await At(e.api,{name:n,scopes:lo(r.scopes)});if(r.json){u({key:o.raw,...o.apiKey});return}s(`${g.ok} Created API key "${o.apiKey.name??n}"`),s(` ID: ${o.apiKey.id}`),s(` Scopes: ${o.apiKey.scopes.join(", ")}`),s(""),s(` ${o.raw}`),s(" Save this now \u2014 it is shown only once and cannot be retrieved again.");}),t.command("list").description("List active API keys").addOption(f()).action(async n=>{let r=await $t(e.api);if(n.json){u(r);return}E(r,[{header:"ID",value:o=>o.id},{header:"Name",value:o=>o.name??"-"},{header:"Preview",value:o=>o.keyPreview},{header:"Scopes",value:o=>o.scopes.join(",")},{header:"Limit",value:o=>o.spendingLimit===null||o.spendingLimit===void 0?"-":`$${o.spendingLimit}/${o.spendingLimitPeriod??"period"}`},{header:"Created",value:o=>o.createdAt}],"No API keys found.");}),t.command("delete").description("Revoke an API key").argument("<id>","API key ID").addOption(j()).addOption(f()).action(async(n,r)=>{if(r.yes!==true)throw new l("refusing to delete without --yes");if(await te(e.api,n),r.json){u({deleted:n});return}s(`${g.ok} Revoked API key ${n}`);}),t};var ne=e=>e.fetch("me","/me",nt),Rt=e=>e.fetch("balance","/balance",ot);var co="urn:ietf:params:oauth:grant-type:device_code",Et=async e=>{try{return await e.json()}catch{return {}}},Ct=async(e,t,n)=>{try{return await fetch(`${Be}${e}`,{method:"POST",body:t,headers:{Accept:"application/json",...typeof t=="string"?{"Content-Type":"application/json"}:{}}})}catch(r){throw new y(`${n} failed: ${r.message}`,{cause:r})}},Pt=async(e,t)=>{let n=Qe.catch({}).parse(await Et(e)),r=n.error??n.code??"workos_error";return new _(r,n.error_description??n.message??`${t} failed`)},_t=async(e,t,n)=>{let r=t.safeParse(await Et(e));if(!r.success)throw new y(`${n} returned an unexpected response`,{cause:r.error});return r.data},Ot=async()=>{let e=await Ct("/user_management/authorize/device",new URLSearchParams({client_id:ge}),"device authorization");if(!e.ok)throw await Pt(e,"device authorization");return _t(e,Xe,"device authorization")},St=async e=>{let t=await Ct("/user_management/authenticate",JSON.stringify({grant_type:co,client_id:ge,device_code:e}),"device token exchange");if(!t.ok)throw await Pt(t,"device token exchange");return _t(t,Ze,"device token exchange")};var oe=e=>new Promise(t=>setTimeout(t,e)),jt=async(e,t,n)=>{let r=Array.from({length:e.length}),o=0,a=async()=>{for(;;){let d=o++;if(d>=e.length)return;r[d]=await n(e[d],d);}};return await Promise.all(Array.from({length:Math.min(t,e.length)},a)),r};var Nt=()=>{let e=process.env;return !(e.CI!==void 0||e.SSH_CONNECTION!==void 0||e.SSH_TTY!==void 0||process.stdout.isTTY!==true||process.platform==="linux"&&(e.DISPLAY===void 0&&e.WAYLAND_DISPLAY===void 0||existsSync("/.dockerenv")))},uo=e=>{switch(process.platform){case "darwin":return {command:"open",args:[e]};case "win32":return {command:"cmd",args:["/c","start","",e]};default:return {command:"xdg-open",args:[e]}}},Dt=async e=>{try{let{protocol:r}=new URL(e);if(r!=="https:"&&r!=="http:")return false}catch{return false}let{command:t,args:n}=uo(e);return new Promise(r=>{try{let o=spawn(t,[...n],{detached:true,stdio:"ignore"});o.on("error",()=>r(false)),o.unref(),setTimeout(()=>r(true),150).unref();}catch{r(false);}})};var fo="https://mynth.io/dashboard",Lt=e=>`${fo}/keys/${e}`,yo=5e3,ho=5,wo=()=>`mynth-cli (${hostname()})`,ko=async(e,t,n)=>{let r=t;for(;;){if(Date.now()>=n)throw new y("device code expired before approval");try{return await St(e)}catch(o){if(!(o instanceof _))throw o;if(o.code==="access_denied")throw new $("login was denied");if(o.code==="expired_token")throw new $("device code expired; run `mynth auth login` again");if(o.code!=="authorization_pending"&&o.code!=="slow_down")throw o;o.code==="slow_down"&&(r+=yo),await oe(r);}}},bo=e=>new Command("login").description("Authenticate with Mynth and store a long-lived API key").option("--scopes <list>",`Comma-separated scopes for the created key (default: ${R.join(",")})`).option("--no-browser","Print the login URL instead of opening a browser").addOption(f()).action(async t=>{if(e.session.envApiKeySet)throw new $("MYNTH_API_KEY is set and takes precedence over login. Unset it to log in, or keep using the env API key.");let n=t.scopes!==void 0?t.scopes.split(",").map(x=>x.trim()).filter(Boolean):[...R],r=n.filter(x=>!R.includes(x));if(n.length===0||r.length>0)throw new $(`invalid --scopes: ${r.join(", ")||"empty"}. Valid scopes: ${R.join(", ")}`);let o=await Ot(),a=o.verification_uri_complete??o.verification_uri,d=o.verification_uri_complete!==void 0,m=t.browser!==false&&Nt()?await Dt(a):false;s(""),s(` ${m?"Opened":"Open"}: ${a}`),s(d?` Check the page shows this code: ${o.user_code}`:` Enter this code: ${o.user_code}`),s(""),s("Waiting for confirmation...");let w=await ko(o.device_code,(o.interval??ho)*1e3,Date.now()+o.expires_in*1e3),b=wo(),A=await vt(e.api,{name:b,scopes:n,token:w.access_token});await e.session.save({kind:"api_key",api_key:A.raw,id:A.apiKey.id});let C=w.user?.email??w.user?.id??"unknown user";if(t.json){u({user:w.user?.id??null,apiKey:{id:A.apiKey.id,name:A.apiKey.name??b,keyPreview:A.apiKey.keyPreview,scopes:A.apiKey.scopes},storedAt:e.session.store.filePath});return}s(`${g.ok} Logged in as ${C}`),s(` Created API key "${b}" with scopes: ${A.apiKey.scopes.join(", ")}`),s(` Stored in ${e.session.store.filePath}`),s(` Set a spending limit or change scopes: ${Lt(A.apiKey.id)}`);}),vo=e=>new Command("logout").description("Revoke this machine's API key and clear local credentials").action(async()=>{let t=await e.session.status(),n=t.kind==="stored"?t.credentials.id:void 0,r=false;if(n!==void 0)try{await te(e.api,n),r=true;}catch(o){T(`Warning: could not revoke API key ${n}: ${o.message}`),T(`Revoke it manually at ${Lt(n)}`);}await e.session.clear(),s(`${g.ok} Local credentials cleared${r?" and API key revoked":""}`),n===void 0&&t.kind==="stored"&&s(" The stored key was not created by `auth login`, so it was left active."),e.session.envApiKeySet&&s("Note: MYNTH_API_KEY is still set in your environment and will still be used.");}),Ao=e=>new Command("status").description("Show how this machine is authenticated, without calling the API").addOption(f()).action(async t=>{let n=await e.session.status();if(t.json){u({source:n.kind,...n.kind==="stored"?{apiKeyId:n.credentials.id??null,path:e.session.store.filePath}:{}});return}switch(n.kind){case "env":s("Authenticated via env: MYNTH_API_KEY");return;case "none":s("Not authenticated. Run `mynth auth login`, or set an API key.");return;case "stored":{let{id:r}=n.credentials;s("Authenticated via stored API key"),r!==void 0&&s(` key: ${r}`),s(` stored: ${e.session.store.filePath}`),s(" Run `mynth whoami` for its current scopes and spending limit.");}}}),ve=e=>new Command("whoami").description("Print the active Mynth identity, verified against the API").addOption(f()).action(async t=>{let n=await e.session.status();if(n.kind==="none")throw new $("not authenticated: run `mynth auth login` or set MYNTH_API_KEY");let r=await ne(e.api);if(t.json){u({source:n.kind,...r});return}s(n.kind==="env"?"env:MYNTH_API_KEY":"api-key"),s(` user: ${r.userId}`),s(` method: ${r.auth.method}`);let o=r.auth.apiKey;o!==void 0&&(s(` key: ${o.name??"unnamed"} (${o.keyPreview})`),o.scopes!==void 0&&o.scopes.length>0&&s(` scopes: ${o.scopes.join(", ")}`),o.spending?.mode==="limited"&&s(` spend: $${o.spending.used} of $${o.spending.limit} per ${o.spending.period} ($${o.spending.remaining} left)`));}),Ft=e=>new Command("auth").description("Manage Mynth authentication").addCommand(bo(e)).addCommand(vo(e)).addCommand(Ao(e)).addCommand(ve(e));var Ut=e=>new Command("balance").description("Show account balance, and the active API key's spending limit").addOption(f()).action(async t=>{let n=await Rt(e.api),o=(await ne(e.api).catch(()=>{}))?.auth.apiKey?.spending;if(t.json){u({...n,...o!==void 0?{spending:o}:{}});return}if(s(`Balance: $${n.balance}`),s(`Reserved: $${n.reserved}`),s(`Available: $${n.available}`),o!==void 0){if(s(""),o.mode==="unlimited"){s("API key spending: unlimited");return}s(`API key limit: $${o.limit} / ${o.period}`),s(` used: $${o.used}`),s(` remaining: $${o.remaining}`);}});var Jt={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".webp":"image/webp"},Ae=Object.keys(Jt),$e=async()=>{try{let e="";process.stdin.setEncoding("utf8");for await(let t of process.stdin)e+=t;return e}catch(e){throw new y("could not read stdin",{cause:e})}},z=async e=>{if(e==="-")return $e();try{return await readFile(e,"utf8")}catch(t){throw new l(`could not read ${e}: ${t.message}`)}},Kt=async e=>{let t=await z(e);try{return JSON.parse(t)}catch(n){throw new l(`invalid JSON in ${e}: ${n.message}`)}},zt=async e=>{let t=extname(e).toLowerCase(),n=Jt[t];if(n===void 0)throw new l(`unsupported image extension "${t}" for ${e} (allowed: ${Ae.join(", ")})`);let r;try{r=await readFile(e);}catch(o){throw new l(`could not read ${e}: ${o.message}`)}return new File([new Uint8Array(r)],basename(e),{type:n})};var Wt=e=>{let t=new Command("set").description("Set local CLI configuration");t.command("api-key").description("Save an existing Mynth API key to the credentials file").argument("<value>","API key value, or `-` to read it from stdin").action(async r=>{let o=(r==="-"?await $e():r).trim();if(o.length===0)throw new l("API key is empty");await e.session.save({kind:"api_key",api_key:o}),s(`${g.ok} API key saved to ${e.session.store.filePath}`),e.session.envApiKeySet&&s("Note: MYNTH_API_KEY is also set in your environment and takes precedence.");});let n=new Command("unset").description("Unset local CLI configuration");return n.command("api-key").description("Clear stored Mynth credentials").action(async()=>{await e.session.clear(),s(`${g.ok} Stored credentials cleared`),s("This does not revoke the key; use `mynth auth logout` for a CLI-created key.");}),new Command("config").description("Manage local CLI configuration").addCommand(t).addCommand(n)};var Bt=e=>e.fetch("destination list","/destinations",z$1.array(U)),Ht=(e,t)=>e.fetch("destination fetch",`/destinations/${t}`,U),Yt=(e,t)=>e.fetch("destination create","/destinations",U,{body:t}),Gt=(e,t,n)=>e.fetch("destination update",`/destinations/${t}`,U,{method:"PUT",body:n}),qt=(e,t,n)=>e.call("destination test",`/destinations/${t}/test`,{body:{path:n}}),Vt=(e,t)=>e.call("destination delete",`/destinations/${t}`,{method:"DELETE"});var xe=/^[a-z0-9-]+$/,Te=64,Eo=["bunny","r2","s3"],Ee=["de","uk","ny","la","sg","se","br","jh","syd"],Co=["default","eu","fedramp"],Po=(e,t)=>{if(e===void 0)throw new l(`${t} is required`);return e},W=(e,t,n)=>{if(t===void 0)throw new l(`--provider ${e} requires ${n}`);return t},B=(e,t)=>t!==void 0?{[e]:t}:{},_o=(e,t)=>{switch(t){case "bunny":{let n=e.region;if(n!==void 0&&!Ee.includes(n))throw new l(`invalid bunny --region "${n}". One of: ${Ee.join(", ")}`);return {id:"bunny",storage_zone:W(t,e.storageZone,"--storage-zone"),...B("region",n)}}case "r2":return {id:"r2",account_id:W(t,e.accountId,"--account-id"),bucket:W(t,e.bucket,"--bucket"),...B("jurisdiction",e.jurisdiction)};case "s3":return {id:"s3",bucket:W(t,e.bucket,"--bucket"),region:W(t,e.region,"--region"),...B("endpoint",e.endpoint),...B("force_path_style",e.forcePathStyle)}}},Oo=async(e,t)=>{let n=(await z(e)).trim();if(n.startsWith("{"))try{return JSON.parse(n)}catch(r){throw new l(`invalid JSON secret from ${e}: ${r.message}`)}if(t!=="bunny")throw new l(`--provider ${t} needs a JSON secret: { "access_key_id": "...", "secret_access_key": "..." }`);if(n.length===0)throw new l(`secret from ${e} is empty`);return {password:n}},So=e=>({path_template:Po(e.pathTemplate,"--path-template"),...B("url_template",e.urlTemplate)}),Zt=async(e,t,n)=>{if(e.file!==void 0){if(e.provider!==void 0)throw new l("use either --file or the typed --provider flags, not both");let a=await Kt(e.file);if(a===null||typeof a!="object"||Array.isArray(a))throw new l(`${e.file} must contain a JSON object`);return {...a,...t}}let r=e.provider;if(r===void 0)throw new l("--provider or --file is required");let o=e.secret!==void 0?await Oo(e.secret,r):void 0;if(o===void 0&&n)throw new l("--secret <path|-> is required (use `-` to read the secret from stdin)");return {...t,provider:_o(e,r),config:So(e),...o!==void 0?{secret:o}:{}}},Qt=e=>e.addOption(new Option("--provider <id>","Storage provider. Required unless --file is used.").choices([...Eo])).option("--path-template <template>",'Object path template, e.g. "images/{id}"').option("--url-template <template>",'Public URL template; must contain {path}, e.g. "https://cdn.example.com/{path}"').option("--storage-zone <name>","bunny: storage zone name").option("--region <region>",`bunny: one of ${Ee.join(", ")}. s3: the bucket's region, e.g. us-east-1.`).option("--account-id <id>","r2: Cloudflare account ID").option("--bucket <name>","r2/s3: bucket name").addOption(new Option("--jurisdiction <name>","r2: data jurisdiction").choices([...Co])).option("--endpoint <url>","s3: custom endpoint for S3-compatible storage").option("--force-path-style","s3: use path-style addressing instead of virtual-hosted").option("--secret <path>","Path to a JSON secret file, or `-` to read it from stdin. bunny also accepts a bare password.").option("--file <path>","Full destination JSON body, or `-` for stdin. Alternative to the flags above.").addOption(f()),Re=e=>{s(`Destination ${e.id}`),s(` Name: ${e.name}`),s(` Provider: ${e.provider.id}`),e.config.path_template!==void 0&&s(` Path: ${e.config.path_template}`),e.config.url_template!==void 0&&s(` URL: ${e.config.url_template}`),s(` Created: ${e.createdAt}`),s(` Updated: ${e.updatedAt}`);},en=e=>{let t=new Command("destination").description("Manage storage destinations that generated images are delivered to");t.command("list").description("List storage destinations").addOption(f()).action(async o=>{let a=await Bt(e.api);if(o.json){u(a);return}E(a,[{header:"ID",value:d=>d.id},{header:"Name",value:d=>d.name},{header:"Provider",value:d=>d.provider.id},{header:"Created",value:d=>d.createdAt}],"No destinations found.");}),t.command("get").description("Fetch a destination by ID").argument("<id>","Destination ID").addOption(f()).action(async(o,a)=>{let d=await Ht(e.api,o);if(a.json){u(d);return}Re(d);});let n=t.command("create").description("Create a storage destination").argument("<name>",`Destination slug: 1-${Te} chars, ${xe.source}. Immutable.`);Qt(n).action(async(o,a)=>{if(o.length>Te||!xe.test(o))throw new l(`invalid destination name "${o}": expected 1-${Te} chars matching ${xe.source}`);let d=await Yt(e.api,await Zt(a,{name:o},true));if(a.json){u(d);return}Re(d),s(""),s(`Next: verify the credentials with \`mynth destination test ${d.id}\``);});let r=t.command("update").description("Replace a destination's provider and config. The slug is immutable.").argument("<id>","Destination ID");return Qt(r).action(async(o,a)=>{let d=await Zt(a,{},false);delete d.name;let m=await Gt(e.api,o,d);if(a.json){u(m);return}Re(m);}),t.command("test").description("Verify a destination's credentials by uploading a probe file").argument("<id>","Destination ID").option("--path <path>","Object path to write (defaults to a unique probe path)").addOption(f()).action(async(o,a)=>{let d=a.path??`mynth-cli-test/${Date.now()}.txt`;if(await qt(e.api,o,d),a.json){u({id:o,path:d,ok:true});return}s(`${g.ok} Credentials valid (wrote ${d})`);}),t.command("delete").description("Delete a destination").argument("<id>","Destination ID").addOption(j()).addOption(f()).action(async(o,a)=>{if(a.yes!==true)throw new l("refusing to delete without --yes");if(await Vt(e.api,o),a.json){u({deleted:o});return}s(`${g.ok} Deleted destination ${o}`);}),t};var tn=e=>{let t=new Command("docs").description("Read Mynth documentation (no authentication)");return t.command("get").description("Fetch a documentation page as Markdown").argument("<path>","Documentation path, without the .md suffix (e.g. guides/async-and-polling)").addOption(f()).action(async(n,r)=>{let o=await e.docs.get(n);if(r.json){u(o);return}s(o.content);}),t.command("list").description("Fetch the complete documentation index").addOption(f()).action(async n=>{let r=await e.docs.list();if(n.json){u({content:r});return}s(r);}),t};var No={4:"Production-ready",3:"Usable, with fixes",2:"Not fit for purpose",1:"Discard"},Pe=e=>e.type==="image.generate",re=e=>{let t=gt.safeParse(e??{});return t.success?{...t.data,images:t.data.images??[]}:{images:[]}},_e=e=>e.status==="success"?e.url??e.mynth_url??void 0:void 0,Ce=e=>e.message!==void 0?`${e.code}: ${e.message}`:e.code,N=e=>{if(e.length!==0){s(`${g.ok} Uploaded ${O(e.length,"image")}`);for(let t of e)s(` ${t.path} -> ${t.url}`);}},Do=e=>{let t=re(e.result),n=t.images,r=n.filter(o=>o.status==="success").length;if(s(`${g.ok} Generated ${r}/${n.length} ${n.length===1?"image":"images"} (task ${e.id})`),t.model!==void 0&&s(` Model: ${t.model}`),e.cost!==null&&s(` Cost: ${e.cost}`),t.magic_prompt!==void 0&&(s(""),s("Enhanced prompt (mynth):"),s(` ${t.magic_prompt.positive}`),t.magic_prompt.negative&&s(` negative: ${t.magic_prompt.negative}`)),n.length!==0){s("");for(let o of n){if(o.status==="failed"){s(` ${g.fail} ${Ce(o.error)}`);continue}let a=o.rating?.status==="success"?` [${o.rating.level}]`:"";s(` ${g.ok} ${_e(o)??"(no url)"}${a}`),o.destination?.status==="failed"&&s(` ${g.fail} destination ${o.destination.name}: ${Ce(o.destination.error)}`);}}},Oe=e=>{if(s(`${q(e.status)} Task ${e.id}`),s(` Type: ${e.type}`),s(` Status: ${e.status}`),e.cost!==null&&s(` Cost: ${e.cost}`),s(` Created: ${e.createdAt}`),s(` Updated: ${e.updatedAt}`),e.errors!==null&&e.errors!==void 0&&e.errors.length>0){s(""),s("Errors:");for(let t of e.errors)s(` ${g.fail} ${Ce(t)}`);}e.result!==null&&e.result!==void 0&&(s(""),s("Result:"),s(qe(JSON.stringify(e.result,null,2))));},ie=e=>{if(Pe(e)&&e.status==="completed"){Do(e);return}Oe(e);},se=e=>{let t=re(e.result);return {taskId:e.id,status:e.status,images:t.images.map(n=>n.status==="success"?{status:"success",url:n.url??null,mynth_url:n.mynth_url??null,size:n.size,format:n.format,rating:n.rating,destination:n.destination}:{status:"failed",error:n.error}),...t.magic_prompt!==void 0?{magic_prompt:t.magic_prompt}:{},...e.cost!==null?{cost:e.cost}:{},...t.model!==void 0?{model:t.model}:{}}},nn=e=>{if(s(`${g.ok} Reviewed (task ${e.taskId})`),s(` Score: ${e.score}/4 \u2014 ${No[e.score]??"unknown"}`),e.cost!==null&&s(` Cost: ${e.cost}`),s(` ${e.url}`),e.summary.length>0&&(s(""),s("Summary"),s(` ${e.summary}`)),e.findings.length>0){s(""),s(`Findings (${e.findings.length})`);for(let t of e.findings)s(` \u2022 [${t.severity}] ${t.category} \u2014 ${t.finding}`),s(` where: ${t.where} (${t.confidence} confidence)`);}if(e.strengths.length>0){s(""),s(`Strengths (${e.strengths.length})`);for(let t of e.strengths)s(` \u2022 ${t.strength} (${t.confidence} confidence)`);}};var I=(e,t=[])=>[...t,e],Se=e=>t=>{let n=Number.parseInt(t,10);if(!Number.isInteger(n)||String(n)!==t)throw new l(`invalid ${e}: "${t}" (expected an integer)`);return n},je=e=>t=>{let n=Se(e)(t);if(n<=0)throw new l(`invalid ${e}: "${t}" (expected a positive integer)`);return n},on=(e,t)=>{let n;try{n=JSON.parse(e);}catch(r){throw new l(`invalid JSON in ${t}: ${r.message}`)}if(n===null||typeof n!="object"||Array.isArray(n))throw new l(`${t} must be a JSON object`);return n},ae=e=>/^https?:\/\//i.test(e);var H=10,D=async(e,t)=>{if(t.length===0)throw new l("no files to upload");if(t.length>H)throw new l(`too many files: ${t.length} (max ${H})`);let n=new FormData;for(let o of await Promise.all(t.map(zt)))n.append("images",o);let{urls:r}=await e.fetch("upload","/image/upload",dt,{body:n});if(r.length!==t.length)throw new l(`upload returned ${r.length} URLs for ${t.length} files`);return t.map((o,a)=>({path:o,url:r[a]}))},Y=(e,t,n)=>e.fetch(`image ${t}`,`/image/${t}`,lt,{body:n}),rn=(e,t)=>e.fetch("estimate","/image/generate/estimate",ct,{body:t});var Ne=1800*1e3,Lo=12e3,Fo=2500,Uo=5e3,Mo=500,Jo=new Set([0,404,408,429]),Ko=e=>e instanceof k&&(Jo.has(e.status)||e.status>=500),zo=10,De=(e,t)=>e.fetch("task fetch",`/tasks/${t}`,it),an=(e,t={})=>e.fetch("task list","/tasks",z$1.array(st),{query:t}),Wo=(e,t)=>e.fetch("task status",`/tasks/${t}/status`,z$1.object({status:V})).then(n=>n.status),dn=(e,t)=>e.fetch("task result",`/tasks/${t}/result`,at),L=async(e,t,n=Ne)=>{let r=Date.now(),o=0;for(;;){try{let m=await Wo(e,t);if(o=0,m!=="pending")return await De(e,t)}catch(m){if(!Ko(m)||++o>zo)throw m}let a=Date.now()-r;if(a>=n)throw new k(`task ${t} did not complete within ${Math.round(n/1e3)}s; it may still finish, check \`mynth task get ${t}\``,{status:0});let d=a<Lo?Fo:Uo;await oe(d+Math.floor(Math.random()*Mo));}};var Ho=["Priming the canvas","Summoning pixels","Mixing digital pigments","Whispering to the model","Dreaming up details","Sketching silhouettes","Arranging composition","Weaving light and shadow","Polishing reflections","Sculpting atmosphere","Tracing final strokes"],Yo=2800,Go=e=>{let t=e.slice();for(let n=t.length-1;n>0;n--){let r=Math.floor(Math.random()*(n+1));[t[n],t[r]]=[t[r],t[n]];}return t},F=async(e,t={})=>{if(process.stderr.isTTY!==true)return e;let n=Go(t.messages??Ho),r=0,o=Bo({text:n[0]??"Working",stream:process.stderr}).start(),a=setInterval(()=>{r++,o.text=n[r%n.length]??"Working";},Yo);try{return await e}finally{clearInterval(a),o.stop();}};var le=2,ce=7,pe=async(e,t)=>{if(ae(t))return {url:t,uploads:[]};let n=await D(e.api,[t]);return {url:n[0].url,uploads:n}},qo=z$1.array(z$1.object({value:z$1.string(),description:z$1.string()})),Vo=e=>{let t=e.indexOf("=");if(t<=0)throw new l(`invalid --level "${e}": expected "value=description"`);let n={value:e.slice(0,t).trim(),description:e.slice(t+1).trim()};if(n.value.length===0||n.description.length===0)throw new l(`invalid --level "${e}": value and description must both be non-empty`);return n},ln=(e,t)=>{let n;try{n=JSON.parse(e);}catch(o){throw new l(`invalid JSON in ${t}: ${o.message}`)}let r=qo.safeParse(n);if(!r.success)throw new l(`invalid levels in ${t}: expected an array of { value, description }`);return r.data},me=async e=>{let t=e.level??[],n=[t.length>0?"--level":void 0,e.levelsFile!==void 0?"--levels-file":void 0,e.levelsJson!==void 0?"--levels-json":void 0].filter(a=>a!==void 0);if(n.length===0)return;if(n.length>1)throw new l(`conflicting level options: ${n.join(", ")} \u2014 use only one`);let r=t.length>0?t.map(Vo):e.levelsFile!==void 0?ln(await z(e.levelsFile),e.levelsFile):ln(e.levelsJson??"[]","--levels-json");if(r.length<le||r.length>ce)throw new l(`levels must have between ${le} and ${ce} items (got ${r.length})`);let o=new Set;for(let a of r){if(o.has(a.value))throw new l(`duplicate level value: "${a.value}"`);o.add(a.value);}return r},Xo=(e,t)=>{let n=fe(e);return new k(`${t} task ${e.id} failed${n!==void 0?` (${n})`:""}`,{status:0,...n!==void 0?{code:n}:{}})},ue=async(e,t)=>{let n=await Y(e.api,t.endpoint,t.body),r=L(e.api,n.taskId),o=t.quiet?await r:await F(r);if(o.status!=="completed")throw Xo(o,t.endpoint);let a=t.schema.safeParse(o.result);if(!a.success)throw new k(`${t.endpoint} task ${o.id} returned an unexpected result`,{status:0,cause:a.error});return {taskId:o.id,cost:o.cost,result:a.data}};var Qo=["low","high"],Fe="Image URL (http/https), or a local image file to upload first",er=e=>e.option("-l, --level <value>",`Custom rating level as "value=description" (repeatable, ${le}-${ce} items). Example: -l safe="No explicit content" -l nsfw="Contains nudity"`,I).option("--levels-file <path>",'JSON file holding an array of { "value", "description" }, or `-` for stdin. Use when descriptions contain shell metacharacters.').option("--levels-json <json>","Inline JSON array of { value, description }."),tr=e=>{let t=new Command("rate").description("Classify an image against the default sfw/nsfw levels, or custom ones").argument("<image>",Fe).addOption(f());return er(t).action(async(n,r)=>{let o=await me(r),{url:a,uploads:d}=await pe(e,n),{taskId:m,cost:w,result:b}=await ue(e,{endpoint:"rate",body:o!==void 0?{url:a,mode:"custom",levels:o}:{url:a},schema:pt,quiet:r.json===true});if(r.json){u({taskId:m,cost:w,...b});return}N(d),s(`${g.ok} Rated (task ${m})`),s(` ${b.level} ${b.url}`);}),t},nr=e=>new Command("alt").description("Generate accessibility alt text for an image").argument("<image>",Fe).addOption(f()).action(async(t,n)=>{let{url:r,uploads:o}=await pe(e,t),{taskId:a,cost:d,result:m}=await ue(e,{endpoint:"alt",body:{url:r},schema:mt,quiet:n.json===true});if(n.json){u({taskId:a,cost:d,...m});return}N(o),s(`${g.ok} Generated alt text (task ${a})`),s(` ${m.alt}`),s(` ${m.url}`);}),or=e=>new Command("review").description("Review image quality with a multi-model panel (score, findings, strengths)").argument("<image>",Fe).addOption(new Option("--effort <level>",'"high" (default) runs five strong vision models; "low" runs three smaller ones for faster, cheaper triage').choices([...Qo])).addOption(f()).action(async(t,n)=>{let{url:r,uploads:o}=await pe(e,t),{taskId:a,cost:d,result:m}=await ue(e,{endpoint:"review",body:{url:r,...n.effort!==void 0?{effort:n.effort}:{}},schema:ut,quiet:n.json===true});if(n.json){u({taskId:a,cost:d,...m});return}N(o),nn({taskId:a,cost:d,...m,findings:m.findings??[],strengths:m.strengths??[]});}),cn=e=>[tr(e),nr(e),or(e)];var dr=4,lr=(e,t,n)=>{try{let r=new URL(e).pathname.split("/").filter(Boolean).pop();if(r!==void 0&&r.length>0)return decodeURIComponent(r)}catch{}return `${t}-${n}`},pn=async e=>{let t=resolve(e.directory);try{await mkdir(t,{recursive:true});}catch(n){throw new y(`could not create ${t}: ${n.message}`,{cause:n})}return jt(e.urls,dr,async(n,r)=>{let o;try{o=await fetch(n);}catch(d){throw new y(`download failed for ${n}: ${d.message}`,{cause:d})}if(!o.ok)throw new y(`download failed for ${n} (${o.status})`);let a=join(t,lr(n,e.fallbackPrefix,r));try{await writeFile(a,new Uint8Array(await o.arrayBuffer()));}catch(d){throw new y(`could not write ${a}: ${d.message}`,{cause:d})}return a})};var Ue=20,ur=["png","jpg","webp"],Me=["auto","person","garment","pose","source","reference"],gr="https://dry-run.mynth.io/input",fr=e=>{let t=e.indexOf(":"),n,r=e;if(t>0&&!/^https?:/i.test(e)){let o=e.slice(0,t);if(!Me.includes(o))throw new l(`invalid --input role "${o}". Expected one of: ${Me.join(", ")}`);n=o,r=e.slice(t+1);}if(r.length===0)throw new l(`invalid --input "${e}": missing path or URL`);return {...n!==void 0?{role:n}:{},value:r,isLocalFile:!ae(r)}},yr=e=>{let t=e.webhookUrl??[],n=e.dashboardWebhooks===false;if(!(t.length===0&&!n))return {...n?{dashboard:false}:{},...t.length>0?{custom:t.map(r=>({url:r}))}:{}}},hr=async(e,t,n,r)=>{let o=await me(t),a=o!==void 0?{mode:"custom",levels:o}:t.contentRating===true?true:void 0,d=t.destination??e.config.envDestination,m=yr(t),w=n.map(b=>({type:"image",...b.role!==void 0?{as:b.role}:{},source:{type:"url",url:b.isLocalFile?r.get(b.value)??gr:b.value}}));return {prompt:t.prompt??"",...t.model!==void 0?{model:t.model}:{},...t.negative!==void 0?{negative_prompt:t.negative}:{},...t.magicPrompt===true?{magic_prompt:true}:{},...t.size!==void 0?{size:t.size}:{},...t.count!==void 0?{count:t.count}:{},...t.format!==void 0?{output:{format:t.format}}:{},...w.length>0?{inputs:w}:{},...d!==void 0?{destination:d}:{},...a!==void 0?{rating:a}:{},...m!==void 0?{webhook:m}:{},...t.metadata!==void 0?{metadata:on(t.metadata,"--metadata")}:{}}},wr=(e,t)=>{let n=re(e.result).images.map(_e).filter(r=>r!==void 0);return n.length===0?Promise.resolve([]):pn({urls:n,directory:t,fallbackPrefix:e.id})},mn=e=>{let t=new Command("generate").description("Generate images with Mynth").addHelpText("after",`
8
- Browse models with: mynth models list`).option("-p, --prompt <text>","Text prompt describing the image to generate").option("-n, --negative <text>","Negative prompt: elements to exclude").option("--magic-prompt","Let Mynth expand the prompt before generating").option("-m, --model <id>",'Model ID (e.g. "black-forest-labs/flux.2-pro"). Default: auto').option("-s, --size <size>",'Size preset or aspect ratio: "square", "portrait", "landscape", "1:1", "16:9", "16:9_4k", "auto", ...').option("-c, --count <number>","Number of images to generate (default: 1)",Se("--count")).addOption(new Option("-f, --format <format>","Output image format").choices([...ur])).option("-i, --input <value>",`Input image as "[role:]path-or-url" (repeatable, up to ${Ue}). Roles: ${Me.join(", ")}. Examples: -i ./img.jpg, -i source:https://example.com/a.png`,I).option("-o, --output-dir <dir>","Directory to save generated images into. Created if missing. Ignored with --async.").option("--destination <name>","Slug of a configured storage destination to deliver results to. Defaults to MYNTH_DESTINATION.").option("--metadata <json>","Inline JSON object attached to the task (max 2KB)").option("--content-rating","Classify each image with the default sfw/nsfw levels. For custom levels use --level.").option("-l, --level <value>",'Custom rating level as "value=description" (repeatable, 2-7)',I).option("--levels-file <path>","JSON file of custom rating levels, or `-` for stdin").option("--levels-json <json>","Inline JSON array of custom rating levels").option("--webhook-url <url>","Deliver this task's events to this URL (repeatable, max 5)",I).option("--no-dashboard-webhooks","Skip dashboard-configured webhooks for this task").option("--dry-run","Validate the request and print the estimated cost without generating").option("--async","Print the task ID immediately instead of waiting for the result").option("--detailed","Include the full task record in --json output").addOption(f());return t.action(async n=>{let r=n.input??[];if(r.length>Ue)throw new l(`too many --input values: ${r.length} (max ${Ue})`);let o=r.map(fr),a=[...new Set(o.filter(h=>h.isLocalFile).map(h=>h.value))],d=a.length>0&&n.dryRun!==true?await D(e.api,a):[],m=await hr(e,n,o,new Map(d.map(h=>[h.path,h.url])));if(n.dryRun===true){let h=await rn(e.api,m);if(n.json){u(h);return}let P=h.estimateKind==="upper_bound"?" (upper bound)":"";s(`${g.ok} Estimated cost: $${h.estimatedCost}${P}`);return}if(n.async===true){let h=await Y(e.api,"generate",{...m,access:{pat:{enabled:true}}}),P=h.access?.publicAccessToken;if(n.json){u({taskId:h.taskId,...h.estimatedCost!==void 0?{estimatedCost:h.estimatedCost}:{},...P!==void 0?{access:{publicAccessToken:P}}:{}});return}s(`${g.ok} Task created: ${h.taskId}`),P!==void 0&&s(` Public access token: ${P}`),s(` Await it with: mynth task wait ${h.taskId}`);return}let w=await Y(e.api,"generate",m),b=L(e.api,w.taskId),A=n.json?await b:await F(b),C=n.outputDir!==void 0?resolve(n.outputDir):void 0,x=C!==void 0?await wr(A,C):[];if(n.json){let h=n.detailed===true?A:se(A);u(C!==void 0?{...h,downloadedFiles:x}:h);return}if(N(d),ie(A),x.length>0){s(""),s(`${g.ok} Saved ${O(x.length,"image")} to ${C}`);for(let h of x)s(` ${h}`);}}),t};var un=e=>new Command("upload").description("Upload local images to Mynth's temporary input storage").argument("<files...>",`Local image files (${Ae.join(", ")})`).addOption(f()).action(async(t,n)=>{if(t.length>H)throw new l(`too many files: ${t.length} (max ${H})`);let r=await D(e.api,t);if(n.json){u({images:r});return}s(`${g.ok} Uploaded ${O(r.length,"image")}`);for(let{path:o,url:a}of r)s(` ${o}`),s(` -> ${a}`);});var gn=e=>{let t=new Command("image").description("Generate, upload, and analyze images");t.addCommand(mn(e)),t.addCommand(un(e));for(let n of cn(e))t.addCommand(n);return t};var fn=e=>e.fetch("models list","/models",z$1.array(rt),{auth:false});var kn=e=>e.id.split("/")[0],yn=e=>{let t=e.pricing?.perImage.base;if(t===void 0)return;let n=Number.parseFloat(t);return Number.isFinite(n)?n:void 0},Ir=e=>e.pricing?.perInput!==void 0,hn=e=>t=>{let n=Number.parseFloat(t);if(!Number.isFinite(n)||n<0)throw new l(`invalid ${e}: "${t}" (expected a non-negative number)`);return n},bn=.3,xr=(e,t)=>wn.go(t,e,{keys:["id","displayName"],threshold:bn}).map(n=>n.obj),Tr=(e,t)=>{let n=[...new Set(e.map(kn))];return n.includes(t)?new Set([t]):new Set(wn.go(t,n,{threshold:bn}).map(r=>r.target))},Rr=(e,t)=>{let n=e;if(t.org!==void 0){let r=Tr(n,t.org);n=n.filter(o=>r.has(kn(o)));}if(t["4k"]&&(n=n.filter(r=>r.pricing?.perImage["4k"]!==void 0)),t.capability!==void 0){let r=t.capability==="img2img";n=n.filter(o=>Ir(o)===r);}if(t.maxPrice!==void 0){let r=t.maxPrice;n=n.filter(o=>(yn(o)??1/0)<=r);}if(t.minPrice!==void 0){let r=t.minPrice;n=n.filter(o=>(yn(o)??-1/0)>=r);}return t.search!==void 0&&(n=xr(n,t.search)),n},vn=e=>{let t=new Command("models").description("Browse the public Mynth model catalog");return t.command("list").description("List available image generation models and their per-image pricing").option("-s, --search <query>","Fuzzy match against model ID (which includes the org) and name").option("--org <org>","Only models from this org, fuzzy matched (e.g. bfl, google)").option("--max-price <usd>","Only models at or below this base per-image price",hn("--max-price")).option("--min-price <usd>","Only models at or above this base per-image price",hn("--min-price")).option("--4k","Only models with 4K pricing").addOption(new Option("--capability <capability>","img2img: bills for image inputs. txt2img: prompt-only, no image inputs").choices(["img2img","txt2img"])).addOption(f()).action(async n=>{let r=Rr(await fn(e.api),n);if(n.json){u(r);return}E(r,[{header:"ID",value:o=>o.id},{header:"Name",value:o=>o.displayName??"-"},{header:"Base",value:o=>o.pricing?.perImage.base??"-"},{header:"4K",value:o=>o.pricing?.perImage["4k"]??"-"},{header:"Input fee",value:o=>o.pricing?.perInput??"-"}],"No models matched the filters.");}),t};var An=Ne/1e3,Cr=e=>e.option("--detailed","Include the full task record instead of a compact summary"),$n=e=>{let t=new Command("task").description("Inspect and await Mynth tasks");t.command("get").description("Fetch a task by ID").argument("<id>","Task ID").addOption(f()).action(async(r,o)=>{let a=await De(e.api,r);if(o.json){u(a);return}Oe(a);}),t.command("result").description("Print a task's result payload as JSON").argument("<id>","Task ID").action(async r=>{u((await dn(e.api,r)).result);});let n=t.command("wait").description("Block until a task completes or fails, then print it").argument("<id>","Task ID").option("--timeout <seconds>",`Max seconds to wait before giving up (default: ${An})`,je("--timeout")).addOption(f());return Cr(n).action(async(r,o)=>{let a=L(e.api,r,(o.timeout??An)*1e3),d=o.json?await a:await F(a);if(d.status==="failed"&&(process.exitCode=Ge(d)),o.json){let m=!o.detailed&&Pe(d);u(m?se(d):d);return}ie(d);}),t.command("list").description("List recent tasks, newest first").option("--limit <number>","Max tasks to return (1-100, default: 20)",je("--limit")).option("--after <id>","Cursor: return tasks created before this task ID").addOption(f()).action(async r=>{let o=await an(e.api,{...r.limit!==void 0?{limit:r.limit}:{},...r.after!==void 0?{after:r.after}:{}});if(r.json){u({tasks:o});return}E(o,[{header:"",value:a=>q(a.status)},{header:"ID",value:a=>a.id},{header:"Type",value:a=>a.type},{header:"Status",value:a=>a.status},{header:"Cost",value:a=>a.cost??"-"},{header:"Created",value:a=>a.createdAt}],"No tasks found.");}),t};var In=(e,t)=>e.fetch("webhook create","/webhook",yt,{body:t}),xn=(e,t,n)=>e.fetch("webhook update",`/webhook/${t}`,ht,{method:"PUT",body:n}),Tn=(e,t)=>e.call("webhook delete",`/webhook/${t}`,{method:"DELETE"});var Je=["task.completed","task.failed","task.image.generate.completed","task.image.generate.failed","task.image.rate.completed","task.image.rate.failed","task.image.alt.completed","task.image.alt.failed","task.image.review.completed","task.image.review.failed","task.video.generate.completed","task.video.generate.failed"],_r=e=>{if(e===void 0||e.length===0)throw new l("at least one --event is required");if(e.includes("all"))return "all";for(let t of e)if(!Je.includes(t))throw new l(`unknown event "${t}". Valid events: all, ${Je.join(", ")}`);return e},Or=e=>{if(e.enabled===true&&e.disabled===true)throw new l("--enabled and --disabled are mutually exclusive");return e.disabled!==true},Rn=e=>({enabled:Or(e),url:e.url,events:_r(e.event),...e.apiKeyId!==void 0?{apiKeyIds:e.apiKeyId}:{},...e.oauthEvents===true?{oauthEnabled:true}:{}}),En=e=>Array.isArray(e)?e.join(", "):String(e),Cn=e=>e.requiredOption("--url <url>","Destination URL for webhook deliveries").option("-e, --event <name>",`Event to subscribe to (repeatable). Use \`all\` for every event. One of: ${Je.join(", ")}`,I).option("--api-key-id <id>","Only deliver tasks created by this API key (repeatable). Omit to deliver for every key.",I).option("--oauth-events","Also deliver tasks created by OAuth sessions (this CLI, the playground). Off by default.").addOption(f()),Pn=e=>{let t=new Command("webhook").description("Manage registered webhooks"),n=t.command("create").description("Register a webhook. The signing secret is shown once, on success.").option("--disabled","Create the webhook disabled (default: enabled)");Cn(n).action(async o=>{let a=await In(e.api,Rn(o));if(o.json){u(a);return}s(`${g.ok} Webhook ${a.id} created`),s(` URL: ${a.url}`),s(` Enabled: ${a.enabled}`),s(` Events: ${En(a.events)}`),a.oauthEnabled!==void 0&&s(` OAuth: ${a.oauthEnabled}`),s(""),s(` Signing secret: ${a.secret}`),s(" Save this now \u2014 it is shown only once and cannot be retrieved again.");});let r=t.command("update").description("Replace a webhook's configuration. Every field is sent, so pass them all.").argument("<id>","Webhook ID").option("--enabled","Enable the webhook").option("--disabled","Disable the webhook");return Cn(r).action(async(o,a)=>{let d=await xn(e.api,o,Rn(a));if(a.json){u(d);return}s(`${g.ok} Webhook ${d.id} updated`),s(` URL: ${d.url}`),d.enabled!==void 0&&s(` Enabled: ${d.enabled}`),s(` Events: ${En(d.events)}`),d.oauthEnabled!==void 0&&s(` OAuth: ${d.oauthEnabled}`);}),t.command("delete").description("Delete a webhook").argument("<id>","Webhook ID").addOption(j()).addOption(f()).action(async(o,a)=>{if(a.yes!==true)throw new l("refusing to delete without --yes");if(await Tn(e.api,o),a.json){u({deleted:o});return}s(`${g.ok} Deleted webhook ${o}`);}),t};var Nr=100,Dr=`
4
+ `);},u=e=>{s(JSON.stringify(e,null,2));},g={ok:U.green("\u2713"),fail:U.red("\u2717"),pending:U.yellow("\u2026")},q=e=>e==="completed"?g.ok:e==="failed"?g.fail:g.pending,O=(e,t,n="s")=>`${e} ${t}${e===1?"":n}`,Ve=(e,t=2)=>{let n=" ".repeat(t);return e.split(`
5
+ `).map(i=>`${n}${i}`).join(`
6
+ `)};var S=z$1.lazy(()=>z$1.union([z$1.string(),z$1.number(),z$1.boolean(),z$1.null(),z$1.array(S),z$1.record(S)])),Xe=e=>z$1.object({data:e}),Un=z$1.object({id:z$1.string(),email:z$1.string(),first_name:z$1.string().nullable().optional(),last_name:z$1.string().nullable().optional()}),Ze=z$1.object({device_code:z$1.string(),user_code:z$1.string(),verification_uri:z$1.string(),verification_uri_complete:z$1.string().optional(),expires_in:z$1.number(),interval:z$1.number().optional()}),Qe=z$1.object({access_token:z$1.string(),refresh_token:z$1.string(),user:Un.optional()}),et=z$1.object({error:z$1.string().optional(),error_description:z$1.string().optional(),message:z$1.string().optional(),code:z$1.string().optional()}),tt=z$1.object({kind:z$1.literal("api_key"),api_key:z$1.string(),id:z$1.string().optional()}),R=["generate","manage","keys"],we=z$1.object({raw:z$1.string(),apiKey:z$1.object({id:z$1.string(),name:z$1.string().optional(),keyPreview:z$1.string(),scopes:z$1.array(z$1.string())})}),Jn=z$1.union([z$1.string(),z$1.number()]).nullable().optional(),nt=z$1.object({id:z$1.string(),name:z$1.string().nullable(),keyPreview:z$1.string(),scopes:z$1.array(z$1.string()),spendingLimit:Jn,spendingLimitPeriod:z$1.string().nullable().optional(),createdAt:z$1.string()}),Kn=z$1.union([z$1.object({mode:z$1.literal("unlimited")}),z$1.object({mode:z$1.literal("limited"),limit:z$1.string(),period:z$1.string(),used:z$1.string(),remaining:z$1.string()})]),ot=z$1.object({userId:z$1.string(),auth:z$1.object({method:z$1.string(),apiKey:z$1.object({id:z$1.string(),name:z$1.string().nullable(),keyPreview:z$1.string(),scopes:z$1.array(z$1.string()).optional(),spending:Kn.optional()}).optional()})}),rt=z$1.object({balance:z$1.string(),reserved:z$1.string(),available:z$1.string(),currency:z$1.string()}),zn=z$1.object({type:z$1.string(),kind:z$1.string().optional(),min:z$1.number().optional(),max:z$1.number()}),Wn=z$1.object({inputs:z$1.object({rules:z$1.array(zn),maxTotal:z$1.number().optional()}).optional()}),Bn=z$1.object({perImage:z$1.object({base:z$1.string(),"4k":z$1.string().optional()}),perInput:z$1.string().optional()}),Yn=z$1.object({perSecond:z$1.record(z$1.string(),z$1.string()),audio:z$1.object({perSecond:z$1.string()}).optional()}),it=z$1.object({id:z$1.string(),displayName:z$1.string().nullable(),type:z$1.string(),modes:z$1.record(z$1.string(),Wn),pricing:z$1.union([Bn,Yn]).nullable()}),V=z$1.enum(["pending","completed","failed"]),st=z$1.object({id:z$1.string(),type:z$1.string(),status:V,userId:z$1.string().optional(),apiKeyId:z$1.string().nullable().optional(),cost:z$1.string().nullable(),request:S.optional(),result:S.nullable(),errors:z$1.array(z$1.object({code:z$1.string(),message:z$1.string().optional()})).nullish(),createdAt:z$1.string(),updatedAt:z$1.string()}),at=z$1.object({id:z$1.string(),type:z$1.string(),status:z$1.string(),cost:z$1.string().nullable(),createdAt:z$1.string(),updatedAt:z$1.string()}),dt=z$1.object({id:z$1.string(),type:z$1.string(),status:V,result:S.nullable()}),lt=z$1.object({urls:z$1.array(z$1.string())}),ct=z$1.object({taskId:z$1.string(),estimatedCost:z$1.string().optional(),access:z$1.object({publicAccessToken:z$1.string()}).optional()}),pt=z$1.object({estimatedCost:z$1.string(),currency:z$1.string(),estimateKind:z$1.enum(["exact","upper_bound"])}),mt=z$1.object({url:z$1.string(),level:z$1.string()}),ut=z$1.object({url:z$1.string(),alt:z$1.string()}),gt=z$1.object({url:z$1.string(),score:z$1.number(),summary:z$1.string(),findings:z$1.array(z$1.object({finding:z$1.string(),category:z$1.string(),severity:z$1.string(),where:z$1.string(),confidence:z$1.string()})).optional(),strengths:z$1.array(z$1.object({strength:z$1.string(),confidence:z$1.string()})).optional()}),he=z$1.object({code:z$1.string(),message:z$1.string().optional()}),Hn=z$1.union([z$1.object({status:z$1.literal("success"),id:z$1.string().optional(),url:z$1.string().nullable().optional(),mynth_url:z$1.string().optional(),size:z$1.string().optional(),format:z$1.string().optional(),rating:z$1.union([z$1.object({status:z$1.literal("success"),level:z$1.string()}),z$1.object({status:z$1.literal("failed"),error:he})]).optional(),destination:z$1.union([z$1.object({status:z$1.literal("success"),name:z$1.string()}),z$1.object({status:z$1.literal("failed"),name:z$1.string(),error:he})]).optional()}),z$1.object({status:z$1.literal("failed"),error:he})]),ft=z$1.object({model:z$1.string().optional(),images:z$1.array(Hn).optional(),magic_prompt:z$1.object({positive:z$1.string(),negative:z$1.string().optional()}).optional()}),F=z$1.object({id:z$1.string(),name:z$1.string(),provider:z$1.object({id:z$1.string()}).catchall(S),config:z$1.object({path_template:z$1.string(),url_template:z$1.string().optional()}).partial(),createdAt:z$1.string(),updatedAt:z$1.string()}),yt=z$1.union([z$1.literal("all"),z$1.array(z$1.string())]),ht=z$1.object({id:z$1.string(),enabled:z$1.boolean(),url:z$1.string(),secret:z$1.string(),events:yt,apiKeyIds:z$1.array(z$1.string()).nullish(),oauthEnabled:z$1.boolean().optional(),createdAt:z$1.string().optional()}),wt=z$1.object({id:z$1.string(),enabled:z$1.boolean().optional(),url:z$1.string(),events:yt,apiKeyIds:z$1.array(z$1.string()).nullish(),oauthEnabled:z$1.boolean().optional()});var Gn=e=>{if(e===void 0)return "";let t=new URLSearchParams;for(let[n,i]of Object.entries(e))i!==void 0&&t.set(n,String(i));return t.size>0?`?${t}`:""},qn=e=>e===void 0?{headers:{}}:e instanceof FormData?{body:e,headers:{}}:{body:JSON.stringify(e),headers:{"Content-Type":"application/json"}},Vn=async e=>{try{return await e.text()}catch{return ""}},Xn=async(e,t)=>{let n=await Vn(e),i,o;try{let a=JSON.parse(n);typeof a.code=="string"&&(i=a.code),typeof a.message=="string"?o=a.message:typeof a.error=="string"&&(o=a.error);}catch{}return new k(`${t} failed (${e.status}): ${o??n??"no body"}`,{status:e.status,...i!==void 0?{code:i}:{}})},X=class{constructor(t,n){this.tokens=n;this.baseUrl=t.apiUrl;}baseUrl;async send(t,n,i={}){let o=await this.attempt(n,i);if(o.ok)return o;throw await Xn(o,t)}async fetch(t,n,i,o={}){let a=await this.send(t,n,o),d;try{d=await a.json();}catch(w){throw new k(`${t} returned invalid JSON: ${w.message}`,{status:a.status,cause:w})}let m=Xe(i).safeParse(d);if(!m.success)throw new k(`${t} returned an unexpected response shape`,{status:a.status,cause:m.error});return m.data.data}async call(t,n,i={}){await this.send(t,n,i);}async attempt(t,n){let{body:i,headers:o}=qn(n.body),a=n.auth===false?void 0:n.token??await this.tokens.token();try{return await fetch(`${this.baseUrl}${t}${Gn(n.query)}`,{method:n.method??(n.body!==void 0?"POST":"GET"),headers:{...o,...a!==void 0?{Authorization:`Bearer ${a}`}:{}},...i!==void 0?{body:i}:{}})}catch(d){throw new k(`request to ${t} failed: ${d.message}`,{status:0,cause:d})}}};var Zn=2048,Qn=/^[A-Za-z0-9._~%-]+$/,eo=e=>{let t=e.trim();if(t.length===0)throw new l("documentation path must not be empty");if(t.length>Zn)throw new l("documentation path is too long");if(t.startsWith("//")||t.includes("://"))throw new l("documentation path must be a path, not a URL");if(/[?#\\]/.test(t))throw new l("documentation path must not contain a query, fragment, or backslash");let n=t.startsWith("/")?t.slice(1):t;if(n.endsWith(".md"))throw new l("documentation path must not include the .md suffix");let i=n.split("/");if(i.some(o=>!Qn.test(o)||o==="."||o===".."))throw new l("documentation path contains an invalid segment");return i.join("/")},to=e=>e.length>500?`${e.slice(0,500)}\u2026`:e,kt=async(e,t)=>{let n;try{n=await fetch(e);}catch(o){throw new k(`${t} failed: ${o.message}`,{status:0,cause:o})}let i=await n.text().catch(()=>"");if(!n.ok)throw new k(`${t} failed (${n.status}): ${to(i)||"no body"}`,{status:n.status});return i},Z=class{constructor(t){this.docsUrl=t;}async get(t){let n=eo(t),i=n.split("/").map(encodeURIComponent).join("/");return {path:n,content:await kt(`${this.docsUrl}/${i}.md`,`docs fetch for ${n}`)}}list(){return kt(`${this.docsUrl}/llms.txt`,"docs index fetch")}};var po="credentials.json",mo=384,uo=()=>{let e=process.env.XDG_CONFIG_HOME;return join(e!==void 0&&e.length>0?e:join(homedir(),".config"),"mynth")},go=async e=>{try{return await stat(e),true}catch{return false}},Q=class{filePath;constructor(){this.filePath=join(uo(),po);}async get(){if(!await go(this.filePath))return;let t;try{t=await readFile(this.filePath,"utf8");}catch(o){throw new y(`could not read ${this.filePath}: ${o.message}`,{cause:o})}let n;try{n=JSON.parse(t);}catch(o){throw new y(`${this.filePath} is not valid JSON`,{cause:o})}let i=tt.safeParse(n);if(!i.success)throw new y(`${this.filePath} has an unexpected shape; run \`mynth auth login\``,{cause:i.error});return i.data}async set(t){try{await mkdir(dirname(this.filePath),{recursive:true,mode:448});let n=`${this.filePath}.${process.pid}.tmp`;await writeFile(n,JSON.stringify(t),{encoding:"utf8",mode:mo}),await rename(n,this.filePath);}catch(n){throw new y(`could not write ${this.filePath}: ${n.message}`,{cause:n})}}async clear(){try{await rm(this.filePath,{force:true});}catch(t){throw new y(`could not delete ${this.filePath}: ${t.message}`,{cause:t})}}};var ee=class{constructor(t){this.config=t;this.store=new Q,this.envApiKeySet=t.envApiKey!==void 0;}store;envApiKeySet;cached;async token(){if(this.config.envApiKey!==void 0)return this.config.envApiKey;if(this.cached!==void 0)return this.cached;let t=await this.read();if(t===void 0)throw new $("not authenticated: run `mynth auth login` or set MYNTH_API_KEY");return this.cached=t.api_key,this.cached}async status(){if(this.envApiKeySet)return {kind:"env"};let t=await this.read().catch(()=>{});return t===void 0?{kind:"none"}:{kind:"stored",credentials:t}}async save(t){await this.store.set(t),this.cached=void 0;}async clear(){await this.store.clear(),this.cached=void 0;}async read(){try{return await this.store.get()}catch(t){throw new $(`could not read stored credentials: ${t.message}`,{cause:t})}}};var bt=()=>{let e=G(),t=new ee(e);return {config:e,session:t,api:new X(e,t),docs:new Z(e.docsUrl)}};var be=["...........",".....4.....","....343....",".....2.....",".344.2.443.","..4432344..",".....2.....",".233.2.332.","..3322233..",".....1.....","..........."],J={1:"#318267",2:"#00a27a",3:"#33bb92",4:"#6bd1ad"},fo=(e,t)=>{let n=i=>i!==".";return !n(e)&&!n(t)?" ":U.level===0?n(e)&&n(t)?"\u2588":n(e)?"\u2580":"\u2584":n(t)?n(e)?e===t?U.hex(J[e])("\u2588"):U.hex(J[e]).bgHex(J[t])("\u2580"):U.hex(J[t])("\u2584"):U.hex(J[e])("\u2580")},vt=()=>{let e=[];for(let t=0;t<be.length;t+=2){let n=be[t],i=be[t+1]??"",o="";for(let a=0;a<n.length;a+=1)o+=fo(n[a],i[a]??".");e.push(o.trimEnd());}return e.filter(t=>t.length>0).join(`
7
+ `)};var At=(e,t)=>e.fetch("api key create","/api-key",we,{body:{name:t.name,scopes:t.scopes},token:t.token}),$t=(e,t)=>e.fetch("api key create","/api-key",we,{body:{name:t.name,scopes:t.scopes}}),xt=e=>e.fetch("api key list","/api-key",z$1.array(nt)),te=(e,t)=>e.call("api key delete",`/api-key/${t}`,{method:"DELETE"});var E=(e,t,n)=>{if(e.length===0){s(n);return}let i=e.map(d=>t.map(m=>m.value(d))),o=t.map((d,m)=>Math.max(d.header.length,...i.map(w=>w[m].length))),a=d=>d.map((m,w)=>w===d.length-1?m:m.padEnd(o[w])).join(" ");s(a(t.map(d=>d.header)));for(let d of i)s(a(d));};var f=()=>new Option("--json","Output machine-readable JSON instead of a human-readable summary"),j=()=>new Option("--yes","Confirm the destructive action (required; there is no interactive prompt)");var Tt=["generate"],wo=e=>{if(e===void 0)return Tt;let t=e.split(",").map(i=>i.trim()).filter(Boolean),n=t.filter(i=>!R.includes(i));if(t.length===0||n.length>0)throw new l(`invalid --scopes: ${n.join(", ")||"empty"}. Valid scopes: ${R.join(", ")}`);return t},Rt=e=>{let t=new Command("api-key").description("Manage Mynth API keys");return t.command("create").description("Create an API key. The key itself is shown once, on success.").argument("<name>","Name for the key, e.g. the app or environment it belongs to").option("--scopes <list>",`Comma-separated scopes (default: ${Tt.join(",")}). Widening beyond \`generate\` requires the dashboard: the API refuses scope escalation from a CLI session.`).addOption(f()).action(async(n,i)=>{let o=await $t(e.api,{name:n,scopes:wo(i.scopes)});if(i.json){u({key:o.raw,...o.apiKey});return}s(`${g.ok} Created API key "${o.apiKey.name??n}"`),s(` ID: ${o.apiKey.id}`),s(` Scopes: ${o.apiKey.scopes.join(", ")}`),s(""),s(` ${o.raw}`),s(" Save this now \u2014 it is shown only once and cannot be retrieved again.");}),t.command("list").description("List active API keys").addOption(f()).action(async n=>{let i=await xt(e.api);if(n.json){u(i);return}E(i,[{header:"ID",value:o=>o.id},{header:"Name",value:o=>o.name??"-"},{header:"Preview",value:o=>o.keyPreview},{header:"Scopes",value:o=>o.scopes.join(",")},{header:"Limit",value:o=>o.spendingLimit===null||o.spendingLimit===void 0?"-":`$${o.spendingLimit}/${o.spendingLimitPeriod??"period"}`},{header:"Created",value:o=>o.createdAt}],"No API keys found.");}),t.command("delete").description("Revoke an API key").argument("<id>","API key ID").addOption(j()).addOption(f()).action(async(n,i)=>{if(i.yes!==true)throw new l("refusing to delete without --yes");if(await te(e.api,n),i.json){u({deleted:n});return}s(`${g.ok} Revoked API key ${n}`);}),t};var ne=e=>e.fetch("me","/me",ot),Et=e=>e.fetch("balance","/balance",rt);var ko="urn:ietf:params:oauth:grant-type:device_code",Ct=async e=>{try{return await e.json()}catch{return {}}},Pt=async(e,t,n)=>{try{return await fetch(`${Ye}${e}`,{method:"POST",body:t,headers:{Accept:"application/json",...typeof t=="string"?{"Content-Type":"application/json"}:{}}})}catch(i){throw new y(`${n} failed: ${i.message}`,{cause:i})}},_t=async(e,t)=>{let n=et.catch({}).parse(await Ct(e)),i=n.error??n.code??"workos_error";return new _(i,n.error_description??n.message??`${t} failed`)},Ot=async(e,t,n)=>{let i=t.safeParse(await Ct(e));if(!i.success)throw new y(`${n} returned an unexpected response`,{cause:i.error});return i.data},St=async()=>{let e=await Pt("/user_management/authorize/device",new URLSearchParams({client_id:ge}),"device authorization");if(!e.ok)throw await _t(e,"device authorization");return Ot(e,Ze,"device authorization")},jt=async e=>{let t=await Pt("/user_management/authenticate",JSON.stringify({grant_type:ko,client_id:ge,device_code:e}),"device token exchange");if(!t.ok)throw await _t(t,"device token exchange");return Ot(t,Qe,"device token exchange")};var oe=e=>new Promise(t=>setTimeout(t,e)),Nt=async(e,t,n)=>{let i=Array.from({length:e.length}),o=0,a=async()=>{for(;;){let d=o++;if(d>=e.length)return;i[d]=await n(e[d],d);}};return await Promise.all(Array.from({length:Math.min(t,e.length)},a)),i};var Dt=()=>{let e=process.env;return !(e.CI!==void 0||e.SSH_CONNECTION!==void 0||e.SSH_TTY!==void 0||process.stdout.isTTY!==true||process.platform==="linux"&&(e.DISPLAY===void 0&&e.WAYLAND_DISPLAY===void 0||existsSync("/.dockerenv")))},Ao=e=>{switch(process.platform){case "darwin":return {command:"open",args:[e]};case "win32":return {command:"cmd",args:["/c","start","",e]};default:return {command:"xdg-open",args:[e]}}},Lt=async e=>{try{let{protocol:i}=new URL(e);if(i!=="https:"&&i!=="http:")return false}catch{return false}let{command:t,args:n}=Ao(e);return new Promise(i=>{try{let o=spawn(t,[...n],{detached:true,stdio:"ignore"});o.on("error",()=>i(false)),o.unref(),setTimeout(()=>i(true),150).unref();}catch{i(false);}})};var xo="https://mynth.io/dashboard",Mt=e=>`${xo}/keys/${e}`,Io=5e3,To=5,Ro=()=>`mynth-cli (${hostname()})`,Eo=async(e,t,n)=>{let i=t;for(;;){if(Date.now()>=n)throw new y("device code expired before approval");try{return await jt(e)}catch(o){if(!(o instanceof _))throw o;if(o.code==="access_denied")throw new $("login was denied");if(o.code==="expired_token")throw new $("device code expired; run `mynth auth login` again");if(o.code!=="authorization_pending"&&o.code!=="slow_down")throw o;o.code==="slow_down"&&(i+=Io),await oe(i);}}},Co=e=>new Command("login").description("Authenticate with Mynth and store a long-lived API key").option("--scopes <list>",`Comma-separated scopes for the created key (default: ${R.join(",")})`).option("--no-browser","Print the login URL instead of opening a browser").addOption(f()).action(async t=>{if(e.session.envApiKeySet)throw new $("MYNTH_API_KEY is set and takes precedence over login. Unset it to log in, or keep using the env API key.");let n=t.scopes!==void 0?t.scopes.split(",").map(I=>I.trim()).filter(Boolean):[...R],i=n.filter(I=>!R.includes(I));if(n.length===0||i.length>0)throw new $(`invalid --scopes: ${i.join(", ")||"empty"}. Valid scopes: ${R.join(", ")}`);let o=await St(),a=o.verification_uri_complete??o.verification_uri,d=o.verification_uri_complete!==void 0,m=t.browser!==false&&Dt()?await Lt(a):false;s(""),s(` ${m?"Opened":"Open"}: ${a}`),s(d?` Check the page shows this code: ${o.user_code}`:` Enter this code: ${o.user_code}`),s(""),s("Waiting for confirmation...");let w=await Eo(o.device_code,(o.interval??To)*1e3,Date.now()+o.expires_in*1e3),b=Ro(),A=await At(e.api,{name:b,scopes:n,token:w.access_token});await e.session.save({kind:"api_key",api_key:A.raw,id:A.apiKey.id});let C=w.user?.email??w.user?.id??"unknown user";if(t.json){u({user:w.user?.id??null,apiKey:{id:A.apiKey.id,name:A.apiKey.name??b,keyPreview:A.apiKey.keyPreview,scopes:A.apiKey.scopes},storedAt:e.session.store.filePath});return}s(`${g.ok} Logged in as ${C}`),s(` Created API key "${b}" with scopes: ${A.apiKey.scopes.join(", ")}`),s(` Stored in ${e.session.store.filePath}`),s(` Set a spending limit or change scopes: ${Mt(A.apiKey.id)}`);}),Po=e=>new Command("logout").description("Revoke this machine's API key and clear local credentials").action(async()=>{let t=await e.session.status(),n=t.kind==="stored"?t.credentials.id:void 0,i=false;if(n!==void 0)try{await te(e.api,n),i=true;}catch(o){T(`Warning: could not revoke API key ${n}: ${o.message}`),T(`Revoke it manually at ${Mt(n)}`);}await e.session.clear(),s(`${g.ok} Local credentials cleared${i?" and API key revoked":""}`),n===void 0&&t.kind==="stored"&&s(" The stored key was not created by `auth login`, so it was left active."),e.session.envApiKeySet&&s("Note: MYNTH_API_KEY is still set in your environment and will still be used.");}),_o=e=>new Command("status").description("Show how this machine is authenticated, without calling the API").addOption(f()).action(async t=>{let n=await e.session.status();if(t.json){u({source:n.kind,...n.kind==="stored"?{apiKeyId:n.credentials.id??null,path:e.session.store.filePath}:{}});return}switch(n.kind){case "env":s("Authenticated via env: MYNTH_API_KEY");return;case "none":s("Not authenticated. Run `mynth auth login`, or set an API key.");return;case "stored":{let{id:i}=n.credentials;s("Authenticated via stored API key"),i!==void 0&&s(` key: ${i}`),s(` stored: ${e.session.store.filePath}`),s(" Run `mynth whoami` for its current scopes and spending limit.");}}}),ve=e=>new Command("whoami").description("Print the active Mynth identity, verified against the API").addOption(f()).action(async t=>{let n=await e.session.status();if(n.kind==="none")throw new $("not authenticated: run `mynth auth login` or set MYNTH_API_KEY");let i=await ne(e.api);if(t.json){u({source:n.kind,...i});return}s(n.kind==="env"?"env:MYNTH_API_KEY":"api-key"),s(` user: ${i.userId}`),s(` method: ${i.auth.method}`);let o=i.auth.apiKey;o!==void 0&&(s(` key: ${o.name??"unnamed"} (${o.keyPreview})`),o.scopes!==void 0&&o.scopes.length>0&&s(` scopes: ${o.scopes.join(", ")}`),o.spending?.mode==="limited"&&s(` spend: $${o.spending.used} of $${o.spending.limit} per ${o.spending.period} ($${o.spending.remaining} left)`));}),Ft=e=>new Command("auth").description("Manage Mynth authentication").addCommand(Co(e)).addCommand(Po(e)).addCommand(_o(e)).addCommand(ve(e));var Ut=e=>new Command("balance").description("Show account balance, and the active API key's spending limit").addOption(f()).action(async t=>{let n=await Et(e.api),o=(await ne(e.api).catch(()=>{}))?.auth.apiKey?.spending;if(t.json){u({...n,...o!==void 0?{spending:o}:{}});return}if(s(`Balance: $${n.balance}`),s(`Reserved: $${n.reserved}`),s(`Available: $${n.available}`),o!==void 0){if(s(""),o.mode==="unlimited"){s("API key spending: unlimited");return}s(`API key limit: $${o.limit} / ${o.period}`),s(` used: $${o.used}`),s(` remaining: $${o.remaining}`);}});var Kt={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".webp":"image/webp"},Ae=Object.keys(Kt),$e=async()=>{try{let e="";process.stdin.setEncoding("utf8");for await(let t of process.stdin)e+=t;return e}catch(e){throw new y("could not read stdin",{cause:e})}},z=async e=>{if(e==="-")return $e();try{return await readFile(e,"utf8")}catch(t){throw new l(`could not read ${e}: ${t.message}`)}},zt=async e=>{let t=await z(e);try{return JSON.parse(t)}catch(n){throw new l(`invalid JSON in ${e}: ${n.message}`)}},Wt=async e=>{let t=extname(e).toLowerCase(),n=Kt[t];if(n===void 0)throw new l(`unsupported image extension "${t}" for ${e} (allowed: ${Ae.join(", ")})`);let i;try{i=await readFile(e);}catch(o){throw new l(`could not read ${e}: ${o.message}`)}return new File([new Uint8Array(i)],basename(e),{type:n})};var Bt=e=>{let t=new Command("set").description("Set local CLI configuration");t.command("api-key").description("Save an existing Mynth API key to the credentials file").argument("<value>","API key value, or `-` to read it from stdin").action(async i=>{let o=(i==="-"?await $e():i).trim();if(o.length===0)throw new l("API key is empty");await e.session.save({kind:"api_key",api_key:o}),s(`${g.ok} API key saved to ${e.session.store.filePath}`),e.session.envApiKeySet&&s("Note: MYNTH_API_KEY is also set in your environment and takes precedence.");});let n=new Command("unset").description("Unset local CLI configuration");return n.command("api-key").description("Clear stored Mynth credentials").action(async()=>{await e.session.clear(),s(`${g.ok} Stored credentials cleared`),s("This does not revoke the key; use `mynth auth logout` for a CLI-created key.");}),new Command("config").description("Manage local CLI configuration").addCommand(t).addCommand(n)};var Yt=e=>e.fetch("destination list","/destinations",z$1.array(F)),Ht=(e,t)=>e.fetch("destination fetch",`/destinations/${t}`,F),Gt=(e,t)=>e.fetch("destination create","/destinations",F,{body:t}),qt=(e,t,n)=>e.fetch("destination update",`/destinations/${t}`,F,{method:"PUT",body:n}),Vt=(e,t,n)=>e.call("destination test",`/destinations/${t}/test`,{body:{path:n}}),Xt=(e,t)=>e.call("destination delete",`/destinations/${t}`,{method:"DELETE"});var Ie=/^[a-z0-9-]+$/,Te=64,Lo=["bunny","r2","s3"],Ee=["de","uk","ny","la","sg","se","br","jh","syd"],Mo=["default","eu","fedramp"],Fo=(e,t)=>{if(e===void 0)throw new l(`${t} is required`);return e},W=(e,t,n)=>{if(t===void 0)throw new l(`--provider ${e} requires ${n}`);return t},B=(e,t)=>t!==void 0?{[e]:t}:{},Uo=(e,t)=>{switch(t){case "bunny":{let n=e.region;if(n!==void 0&&!Ee.includes(n))throw new l(`invalid bunny --region "${n}". One of: ${Ee.join(", ")}`);return {id:"bunny",storage_zone:W(t,e.storageZone,"--storage-zone"),...B("region",n)}}case "r2":return {id:"r2",account_id:W(t,e.accountId,"--account-id"),bucket:W(t,e.bucket,"--bucket"),...B("jurisdiction",e.jurisdiction)};case "s3":return {id:"s3",bucket:W(t,e.bucket,"--bucket"),region:W(t,e.region,"--region"),...B("endpoint",e.endpoint),...B("force_path_style",e.forcePathStyle)}}},Jo=async(e,t)=>{let n=(await z(e)).trim();if(n.startsWith("{"))try{return JSON.parse(n)}catch(i){throw new l(`invalid JSON secret from ${e}: ${i.message}`)}if(t!=="bunny")throw new l(`--provider ${t} needs a JSON secret: { "access_key_id": "...", "secret_access_key": "..." }`);if(n.length===0)throw new l(`secret from ${e} is empty`);return {password:n}},Ko=e=>({path_template:Fo(e.pathTemplate,"--path-template"),...B("url_template",e.urlTemplate)}),Qt=async(e,t,n)=>{if(e.file!==void 0){if(e.provider!==void 0)throw new l("use either --file or the typed --provider flags, not both");let a=await zt(e.file);if(a===null||typeof a!="object"||Array.isArray(a))throw new l(`${e.file} must contain a JSON object`);return {...a,...t}}let i=e.provider;if(i===void 0)throw new l("--provider or --file is required");let o=e.secret!==void 0?await Jo(e.secret,i):void 0;if(o===void 0&&n)throw new l("--secret <path|-> is required (use `-` to read the secret from stdin)");return {...t,provider:Uo(e,i),config:Ko(e),...o!==void 0?{secret:o}:{}}},en=e=>e.addOption(new Option("--provider <id>","Storage provider. Required unless --file is used.").choices([...Lo])).option("--path-template <template>",'Object path template, e.g. "images/{id}"').option("--url-template <template>",'Public URL template; must contain {path}, e.g. "https://cdn.example.com/{path}"').option("--storage-zone <name>","bunny: storage zone name").option("--region <region>",`bunny: one of ${Ee.join(", ")}. s3: the bucket's region, e.g. us-east-1.`).option("--account-id <id>","r2: Cloudflare account ID").option("--bucket <name>","r2/s3: bucket name").addOption(new Option("--jurisdiction <name>","r2: data jurisdiction").choices([...Mo])).option("--endpoint <url>","s3: custom endpoint for S3-compatible storage").option("--force-path-style","s3: use path-style addressing instead of virtual-hosted").option("--secret <path>","Path to a JSON secret file, or `-` to read it from stdin. bunny also accepts a bare password.").option("--file <path>","Full destination JSON body, or `-` for stdin. Alternative to the flags above.").addOption(f()),Re=e=>{s(`Destination ${e.id}`),s(` Name: ${e.name}`),s(` Provider: ${e.provider.id}`),e.config.path_template!==void 0&&s(` Path: ${e.config.path_template}`),e.config.url_template!==void 0&&s(` URL: ${e.config.url_template}`),s(` Created: ${e.createdAt}`),s(` Updated: ${e.updatedAt}`);},tn=e=>{let t=new Command("destination").description("Manage storage destinations that generated images are delivered to");t.command("list").description("List storage destinations").addOption(f()).action(async o=>{let a=await Yt(e.api);if(o.json){u(a);return}E(a,[{header:"ID",value:d=>d.id},{header:"Name",value:d=>d.name},{header:"Provider",value:d=>d.provider.id},{header:"Created",value:d=>d.createdAt}],"No destinations found.");}),t.command("get").description("Fetch a destination by ID").argument("<id>","Destination ID").addOption(f()).action(async(o,a)=>{let d=await Ht(e.api,o);if(a.json){u(d);return}Re(d);});let n=t.command("create").description("Create a storage destination").argument("<name>",`Destination slug: 1-${Te} chars, ${Ie.source}. Immutable.`);en(n).action(async(o,a)=>{if(o.length>Te||!Ie.test(o))throw new l(`invalid destination name "${o}": expected 1-${Te} chars matching ${Ie.source}`);let d=await Gt(e.api,await Qt(a,{name:o},true));if(a.json){u(d);return}Re(d),s(""),s(`Next: verify the credentials with \`mynth destination test ${d.id}\``);});let i=t.command("update").description("Replace a destination's provider and config. The slug is immutable.").argument("<id>","Destination ID");return en(i).action(async(o,a)=>{let d=await Qt(a,{},false);delete d.name;let m=await qt(e.api,o,d);if(a.json){u(m);return}Re(m);}),t.command("test").description("Verify a destination's credentials by uploading a probe file").argument("<id>","Destination ID").option("--path <path>","Object path to write (defaults to a unique probe path)").addOption(f()).action(async(o,a)=>{let d=a.path??`mynth-cli-test/${Date.now()}.txt`;if(await Vt(e.api,o,d),a.json){u({id:o,path:d,ok:true});return}s(`${g.ok} Credentials valid (wrote ${d})`);}),t.command("delete").description("Delete a destination").argument("<id>","Destination ID").addOption(j()).addOption(f()).action(async(o,a)=>{if(a.yes!==true)throw new l("refusing to delete without --yes");if(await Xt(e.api,o),a.json){u({deleted:o});return}s(`${g.ok} Deleted destination ${o}`);}),t};var nn=e=>{let t=new Command("docs").description("Read Mynth documentation (no authentication)");return t.command("get").description("Fetch a documentation page as Markdown").argument("<path>","Documentation path, without the .md suffix (e.g. guides/async-and-polling)").addOption(f()).action(async(n,i)=>{let o=await e.docs.get(n);if(i.json){u(o);return}s(o.content);}),t.command("list").description("Fetch the complete documentation index").addOption(f()).action(async n=>{let i=await e.docs.list();if(n.json){u({content:i});return}s(i);}),t};var Wo={4:"Production-ready",3:"Usable, with fixes",2:"Not fit for purpose",1:"Discard"},Pe=e=>e.type==="image.generate",re=e=>{let t=ft.safeParse(e??{});return t.success?{...t.data,images:t.data.images??[]}:{images:[]}},_e=e=>e.status==="success"?e.url??e.mynth_url??void 0:void 0,Ce=e=>e.message!==void 0?`${e.code}: ${e.message}`:e.code,N=e=>{if(e.length!==0){s(`${g.ok} Uploaded ${O(e.length,"image")}`);for(let t of e)s(` ${t.path} -> ${t.url}`);}},Bo=e=>{let t=re(e.result),n=t.images,i=n.filter(o=>o.status==="success").length;if(s(`${g.ok} Generated ${i}/${n.length} ${n.length===1?"image":"images"} (task ${e.id})`),t.model!==void 0&&s(` Model: ${t.model}`),e.cost!==null&&s(` Cost: ${e.cost}`),t.magic_prompt!==void 0&&(s(""),s("Enhanced prompt (mynth):"),s(` ${t.magic_prompt.positive}`),t.magic_prompt.negative&&s(` negative: ${t.magic_prompt.negative}`)),n.length!==0){s("");for(let o of n){if(o.status==="failed"){s(` ${g.fail} ${Ce(o.error)}`);continue}let a=o.rating?.status==="success"?` [${o.rating.level}]`:"";s(` ${g.ok} ${_e(o)??"(no url)"}${a}`),o.destination?.status==="failed"&&s(` ${g.fail} destination ${o.destination.name}: ${Ce(o.destination.error)}`);}}},Oe=e=>{if(s(`${q(e.status)} Task ${e.id}`),s(` Type: ${e.type}`),s(` Status: ${e.status}`),e.cost!==null&&s(` Cost: ${e.cost}`),s(` Created: ${e.createdAt}`),s(` Updated: ${e.updatedAt}`),e.errors!==null&&e.errors!==void 0&&e.errors.length>0){s(""),s("Errors:");for(let t of e.errors)s(` ${g.fail} ${Ce(t)}`);}e.result!==null&&e.result!==void 0&&(s(""),s("Result:"),s(Ve(JSON.stringify(e.result,null,2))));},ie=e=>{if(Pe(e)&&e.status==="completed"){Bo(e);return}Oe(e);},se=e=>{let t=re(e.result);return {taskId:e.id,status:e.status,images:t.images.map(n=>n.status==="success"?{status:"success",url:n.url??null,mynth_url:n.mynth_url??null,size:n.size,format:n.format,rating:n.rating,destination:n.destination}:{status:"failed",error:n.error}),...t.magic_prompt!==void 0?{magic_prompt:t.magic_prompt}:{},...e.cost!==null?{cost:e.cost}:{},...t.model!==void 0?{model:t.model}:{}}},on=e=>{if(s(`${g.ok} Reviewed (task ${e.taskId})`),s(` Score: ${e.score}/4 \u2014 ${Wo[e.score]??"unknown"}`),e.cost!==null&&s(` Cost: ${e.cost}`),s(` ${e.url}`),e.summary.length>0&&(s(""),s("Summary"),s(` ${e.summary}`)),e.findings.length>0){s(""),s(`Findings (${e.findings.length})`);for(let t of e.findings)s(` \u2022 [${t.severity}] ${t.category} \u2014 ${t.finding}`),s(` where: ${t.where} (${t.confidence} confidence)`);}if(e.strengths.length>0){s(""),s(`Strengths (${e.strengths.length})`);for(let t of e.strengths)s(` \u2022 ${t.strength} (${t.confidence} confidence)`);}};var x=(e,t=[])=>[...t,e],Se=e=>t=>{let n=Number.parseInt(t,10);if(!Number.isInteger(n)||String(n)!==t)throw new l(`invalid ${e}: "${t}" (expected an integer)`);return n},je=e=>t=>{let n=Se(e)(t);if(n<=0)throw new l(`invalid ${e}: "${t}" (expected a positive integer)`);return n},rn=(e,t)=>{let n;try{n=JSON.parse(e);}catch(i){throw new l(`invalid JSON in ${t}: ${i.message}`)}if(n===null||typeof n!="object"||Array.isArray(n))throw new l(`${t} must be a JSON object`);return n},ae=e=>/^https?:\/\//i.test(e);var Y=10,D=async(e,t)=>{if(t.length===0)throw new l("no files to upload");if(t.length>Y)throw new l(`too many files: ${t.length} (max ${Y})`);let n=new FormData;for(let o of await Promise.all(t.map(Wt)))n.append("images",o);let{urls:i}=await e.fetch("upload","/image/upload",lt,{body:n});if(i.length!==t.length)throw new l(`upload returned ${i.length} URLs for ${t.length} files`);return t.map((o,a)=>({path:o,url:i[a]}))},H=(e,t,n)=>e.fetch(`image ${t}`,`/image/${t}`,ct,{body:n}),sn=(e,t)=>e.fetch("estimate","/image/generate/estimate",pt,{body:t});var Ne=1800*1e3,Yo=12e3,Ho=2500,Go=5e3,qo=500,Vo=new Set([0,404,408,429]),Xo=e=>e instanceof k&&(Vo.has(e.status)||e.status>=500),Zo=10,De=(e,t)=>e.fetch("task fetch",`/tasks/${t}`,st),dn=(e,t={})=>e.fetch("task list","/tasks",z$1.array(at),{query:t}),Qo=(e,t)=>e.fetch("task status",`/tasks/${t}/status`,z$1.object({status:V})).then(n=>n.status),ln=(e,t)=>e.fetch("task result",`/tasks/${t}/result`,dt),L=async(e,t,n=Ne)=>{let i=Date.now(),o=0;for(;;){try{let m=await Qo(e,t);if(o=0,m!=="pending")return await De(e,t)}catch(m){if(!Xo(m)||++o>Zo)throw m}let a=Date.now()-i;if(a>=n)throw new k(`task ${t} did not complete within ${Math.round(n/1e3)}s; it may still finish, check \`mynth task get ${t}\``,{status:0});let d=a<Yo?Ho:Go;await oe(d+Math.floor(Math.random()*qo));}};var tr=["Priming the canvas","Summoning pixels","Mixing digital pigments","Whispering to the model","Dreaming up details","Sketching silhouettes","Arranging composition","Weaving light and shadow","Polishing reflections","Sculpting atmosphere","Tracing final strokes"],nr=2800,or=e=>{let t=e.slice();for(let n=t.length-1;n>0;n--){let i=Math.floor(Math.random()*(n+1));[t[n],t[i]]=[t[i],t[n]];}return t},M=async(e,t={})=>{if(process.stderr.isTTY!==true)return e;let n=or(t.messages??tr),i=0,o=er({text:n[0]??"Working",stream:process.stderr}).start(),a=setInterval(()=>{i++,o.text=n[i%n.length]??"Working";},nr);try{return await e}finally{clearInterval(a),o.stop();}};var le=2,ce=7,pe=async(e,t)=>{if(ae(t))return {url:t,uploads:[]};let n=await D(e.api,[t]);return {url:n[0].url,uploads:n}},rr=z$1.array(z$1.object({value:z$1.string(),description:z$1.string()})),ir=e=>{let t=e.indexOf("=");if(t<=0)throw new l(`invalid --level "${e}": expected "value=description"`);let n={value:e.slice(0,t).trim(),description:e.slice(t+1).trim()};if(n.value.length===0||n.description.length===0)throw new l(`invalid --level "${e}": value and description must both be non-empty`);return n},cn=(e,t)=>{let n;try{n=JSON.parse(e);}catch(o){throw new l(`invalid JSON in ${t}: ${o.message}`)}let i=rr.safeParse(n);if(!i.success)throw new l(`invalid levels in ${t}: expected an array of { value, description }`);return i.data},me=async e=>{let t=e.level??[],n=[t.length>0?"--level":void 0,e.levelsFile!==void 0?"--levels-file":void 0,e.levelsJson!==void 0?"--levels-json":void 0].filter(a=>a!==void 0);if(n.length===0)return;if(n.length>1)throw new l(`conflicting level options: ${n.join(", ")} \u2014 use only one`);let i=t.length>0?t.map(ir):e.levelsFile!==void 0?cn(await z(e.levelsFile),e.levelsFile):cn(e.levelsJson??"[]","--levels-json");if(i.length<le||i.length>ce)throw new l(`levels must have between ${le} and ${ce} items (got ${i.length})`);let o=new Set;for(let a of i){if(o.has(a.value))throw new l(`duplicate level value: "${a.value}"`);o.add(a.value);}return i},sr=(e,t)=>{let n=fe(e);return new k(`${t} task ${e.id} failed${n!==void 0?` (${n})`:""}`,{status:0,...n!==void 0?{code:n}:{}})},ue=async(e,t)=>{let n=await H(e.api,t.endpoint,t.body),i=L(e.api,n.taskId),o=t.quiet?await i:await M(i);if(o.status!=="completed")throw sr(o,t.endpoint);let a=t.schema.safeParse(o.result);if(!a.success)throw new k(`${t.endpoint} task ${o.id} returned an unexpected result`,{status:0,cause:a.error});return {taskId:o.id,cost:o.cost,result:a.data}};var dr=["low","high"],Me="Image URL (http/https), or a local image file to upload first",lr=e=>e.option("-l, --level <value>",`Custom rating level as "value=description" (repeatable, ${le}-${ce} items). Example: -l safe="No explicit content" -l nsfw="Contains nudity"`,x).option("--levels-file <path>",'JSON file holding an array of { "value", "description" }, or `-` for stdin. Use when descriptions contain shell metacharacters.').option("--levels-json <json>","Inline JSON array of { value, description }."),cr=e=>{let t=new Command("rate").description("Classify an image against the default sfw/nsfw levels, or custom ones").argument("<image>",Me).addOption(f());return lr(t).action(async(n,i)=>{let o=await me(i),{url:a,uploads:d}=await pe(e,n),{taskId:m,cost:w,result:b}=await ue(e,{endpoint:"rate",body:o!==void 0?{url:a,mode:"custom",levels:o}:{url:a},schema:mt,quiet:i.json===true});if(i.json){u({taskId:m,cost:w,...b});return}N(d),s(`${g.ok} Rated (task ${m})`),s(` ${b.level} ${b.url}`);}),t},pr=e=>new Command("alt").description("Generate accessibility alt text for an image").argument("<image>",Me).addOption(f()).action(async(t,n)=>{let{url:i,uploads:o}=await pe(e,t),{taskId:a,cost:d,result:m}=await ue(e,{endpoint:"alt",body:{url:i},schema:ut,quiet:n.json===true});if(n.json){u({taskId:a,cost:d,...m});return}N(o),s(`${g.ok} Generated alt text (task ${a})`),s(` ${m.alt}`),s(` ${m.url}`);}),mr=e=>new Command("review").description("Review image quality with a multi-model panel (score, findings, strengths)").argument("<image>",Me).addOption(new Option("--effort <level>",'"high" (default) runs five strong vision models; "low" runs three smaller ones for faster, cheaper triage').choices([...dr])).addOption(f()).action(async(t,n)=>{let{url:i,uploads:o}=await pe(e,t),{taskId:a,cost:d,result:m}=await ue(e,{endpoint:"review",body:{url:i,...n.effort!==void 0?{effort:n.effort}:{}},schema:gt,quiet:n.json===true});if(n.json){u({taskId:a,cost:d,...m});return}N(o),on({taskId:a,cost:d,...m,findings:m.findings??[],strengths:m.strengths??[]});}),pn=e=>[cr(e),pr(e),mr(e)];var hr=4,wr=(e,t,n)=>{try{let i=new URL(e).pathname.split("/").filter(Boolean).pop();if(i!==void 0&&i.length>0)return decodeURIComponent(i)}catch{}return `${t}-${n}`},mn=async e=>{let t=resolve(e.directory);try{await mkdir(t,{recursive:true});}catch(n){throw new y(`could not create ${t}: ${n.message}`,{cause:n})}return Nt(e.urls,hr,async(n,i)=>{let o;try{o=await fetch(n);}catch(d){throw new y(`download failed for ${n}: ${d.message}`,{cause:d})}if(!o.ok)throw new y(`download failed for ${n} (${o.status})`);let a=join(t,wr(n,e.fallbackPrefix,i));try{await writeFile(a,new Uint8Array(await o.arrayBuffer()));}catch(d){throw new y(`could not write ${a}: ${d.message}`,{cause:d})}return a})};var Fe=20,Ar=["png","jpg","webp"],Ue=["auto","person","garment","pose","source","reference"],$r="https://dry-run.mynth.io/input",xr=e=>{let t=e.indexOf(":"),n,i=e;if(t>0&&!/^https?:/i.test(e)){let o=e.slice(0,t);if(!Ue.includes(o))throw new l(`invalid --input role "${o}". Expected one of: ${Ue.join(", ")}`);n=o,i=e.slice(t+1);}if(i.length===0)throw new l(`invalid --input "${e}": missing path or URL`);return {...n!==void 0?{role:n}:{},value:i,isLocalFile:!ae(i)}},Ir=e=>{let t=e.webhookUrl??[],n=e.dashboardWebhooks===false;if(!(t.length===0&&!n))return {...n?{dashboard:false}:{},...t.length>0?{custom:t.map(i=>({url:i}))}:{}}},Tr=async(e,t,n,i)=>{let o=await me(t),a=o!==void 0?{mode:"custom",levels:o}:t.contentRating===true?true:void 0,d=t.destination??e.config.envDestination,m=Ir(t),w=n.map(b=>({type:"image",...b.role!==void 0?{as:b.role}:{},source:{type:"url",url:b.isLocalFile?i.get(b.value)??$r:b.value}}));return {prompt:t.prompt??"",...t.model!==void 0?{model:t.model}:{},...t.negative!==void 0?{negative_prompt:t.negative}:{},...t.magicPrompt===true?{magic_prompt:true}:{},...t.size!==void 0?{size:t.size}:{},...t.count!==void 0?{count:t.count}:{},...t.format!==void 0?{output:{format:t.format}}:{},...w.length>0?{inputs:w}:{},...d!==void 0?{destination:d}:{},...a!==void 0?{rating:a}:{},...m!==void 0?{webhook:m}:{},...t.metadata!==void 0?{metadata:rn(t.metadata,"--metadata")}:{}}},Rr=(e,t)=>{let n=re(e.result).images.map(_e).filter(i=>i!==void 0);return n.length===0?Promise.resolve([]):mn({urls:n,directory:t,fallbackPrefix:e.id})},un=e=>{let t=new Command("generate").description("Generate images with Mynth").addHelpText("after",`
8
+ Browse models with: mynth models list`).option("-p, --prompt <text>","Text prompt describing the image to generate").option("-n, --negative <text>","Negative prompt: elements to exclude").option("--magic-prompt","Let Mynth expand the prompt before generating").option("-m, --model <id>",'Model ID (e.g. "black-forest-labs/flux.2-pro"). Default: auto').option("-s, --size <size>",'Size preset or aspect ratio: "square", "portrait", "landscape", "1:1", "16:9", "16:9_4k", "auto", ...').option("-c, --count <number>","Number of images to generate (default: 1)",Se("--count")).addOption(new Option("-f, --format <format>","Output image format").choices([...Ar])).option("-i, --input <value>",`Input image as "[role:]path-or-url" (repeatable, up to ${Fe}). Roles: ${Ue.join(", ")}. Examples: -i ./img.jpg, -i source:https://example.com/a.png`,x).option("-o, --output-dir <dir>","Directory to save generated images into. Created if missing. Ignored with --async.").option("--destination <name>","Slug of a configured storage destination to deliver results to. Defaults to MYNTH_DESTINATION.").option("--metadata <json>","Inline JSON object attached to the task (max 2KB)").option("--content-rating","Classify each image with the default sfw/nsfw levels. For custom levels use --level.").option("-l, --level <value>",'Custom rating level as "value=description" (repeatable, 2-7)',x).option("--levels-file <path>","JSON file of custom rating levels, or `-` for stdin").option("--levels-json <json>","Inline JSON array of custom rating levels").option("--webhook-url <url>","Deliver this task's events to this URL (repeatable, max 5)",x).option("--no-dashboard-webhooks","Skip dashboard-configured webhooks for this task").option("--dry-run","Validate the request and print the estimated cost without generating").option("--async","Print the task ID immediately instead of waiting for the result").option("--detailed","Include the full task record in --json output").addOption(f());return t.action(async n=>{let i=n.input??[];if(i.length>Fe)throw new l(`too many --input values: ${i.length} (max ${Fe})`);let o=i.map(xr),a=[...new Set(o.filter(h=>h.isLocalFile).map(h=>h.value))],d=a.length>0&&n.dryRun!==true?await D(e.api,a):[],m=await Tr(e,n,o,new Map(d.map(h=>[h.path,h.url])));if(n.dryRun===true){let h=await sn(e.api,m);if(n.json){u(h);return}let P=h.estimateKind==="upper_bound"?" (upper bound)":"";s(`${g.ok} Estimated cost: $${h.estimatedCost}${P}`);return}if(n.async===true){let h=await H(e.api,"generate",{...m,access:{pat:{enabled:true}}}),P=h.access?.publicAccessToken;if(n.json){u({taskId:h.taskId,...h.estimatedCost!==void 0?{estimatedCost:h.estimatedCost}:{},...P!==void 0?{access:{publicAccessToken:P}}:{}});return}s(`${g.ok} Task created: ${h.taskId}`),P!==void 0&&s(` Public access token: ${P}`),s(` Await it with: mynth task wait ${h.taskId}`);return}let w=await H(e.api,"generate",m),b=L(e.api,w.taskId),A=n.json?await b:await M(b),C=n.outputDir!==void 0?resolve(n.outputDir):void 0,I=C!==void 0?await Rr(A,C):[];if(n.json){let h=n.detailed===true?A:se(A);u(C!==void 0?{...h,downloadedFiles:I}:h);return}if(N(d),ie(A),I.length>0){s(""),s(`${g.ok} Saved ${O(I.length,"image")} to ${C}`);for(let h of I)s(` ${h}`);}}),t};var gn=e=>new Command("upload").description("Upload local images to Mynth's temporary input storage").argument("<files...>",`Local image files (${Ae.join(", ")})`).addOption(f()).action(async(t,n)=>{if(t.length>Y)throw new l(`too many files: ${t.length} (max ${Y})`);let i=await D(e.api,t);if(n.json){u({images:i});return}s(`${g.ok} Uploaded ${O(i.length,"image")}`);for(let{path:o,url:a}of i)s(` ${o}`),s(` -> ${a}`);});var fn=e=>{let t=new Command("image").description("Generate, upload, and analyze images");t.addCommand(un(e)),t.addCommand(gn(e));for(let n of pn(e))t.addCommand(n);return t};var yn=e=>e.fetch("models list","/models",z$1.array(it),{auth:false});var bn={txt2img:"txt->img",img2img:"img->img",txt2vid:"txt->vid",img2vid:"img->vid"},vn=e=>e.id.split("/")[0],Je=e=>"perImage"in e,An=e=>e.type==="video"?"/s":"",Sr=e=>{let t=e.pricing;return t===null?[]:Je(t)?[t.perImage.base]:Object.values(t.perSecond)},$n=e=>{let t;for(let n of Sr(e)){let i=Number.parseFloat(n);Number.isFinite(i)&&(t===void 0||i<t.value)&&(t={raw:n,value:i});}return t},hn=e=>$n(e)?.value,jr=e=>{let t=$n(e);return t===void 0?"-":`${t.raw}${An(e)}`},xn=e=>{let t=e.pricing;if(t!==null)return Je(t)?t.perImage["4k"]:t.perSecond["4k"]},Nr=e=>{let t=xn(e);return t===void 0?"-":`${t}${An(e)}`},Dr=e=>{let t=e.pricing;if(!(t===null||!Je(t)))return t.perInput},wn=e=>t=>{let n=Number.parseFloat(t);if(!Number.isFinite(n)||n<0)throw new l(`invalid ${e}: "${t}" (expected a non-negative number)`);return n},In=.3,Lr=(e,t)=>kn.go(t,e,{keys:["id","displayName"],threshold:In}).map(n=>n.obj),Mr=(e,t)=>{let n=[...new Set(e.map(vn))];return n.includes(t)?new Set([t]):new Set(kn.go(t,n,{threshold:In}).map(i=>i.target))},Fr=(e,t)=>{let n=e;if(t.org!==void 0){let i=Mr(n,t.org);n=n.filter(o=>i.has(vn(o)));}if(t.type!==void 0){let i=t.type;n=n.filter(o=>o.type===i);}if(t["4k"]&&(n=n.filter(i=>xn(i)!==void 0)),t.capability!==void 0){let i=bn[t.capability];n=n.filter(o=>i in o.modes);}if(t.maxPrice!==void 0){let i=t.maxPrice;n=n.filter(o=>(hn(o)??1/0)<=i);}if(t.minPrice!==void 0){let i=t.minPrice;n=n.filter(o=>(hn(o)??-1/0)>=i);}return t.search!==void 0&&(n=Lr(n,t.search)),n},Tn=e=>{let t=new Command("models").description("Browse the public Mynth model catalog");return t.command("list").description("List available generation models and their pricing").option("-s, --search <query>","Fuzzy match against model ID (which includes the org) and name").option("--org <org>","Only models from this org, fuzzy matched (e.g. bfl, google)").option("--type <type>","Only models of this media type (image, video)").option("--max-price <usd>","Only models at or below this price (per image, or per second for video)",wn("--max-price")).option("--min-price <usd>","Only models at or above this price (per image, or per second for video)",wn("--min-price")).option("--4k","Only models with 4K pricing").addOption(new Option("--capability <capability>","Only models serving this generation mode").choices(Object.keys(bn))).addOption(f()).action(async n=>{let i=Fr(await yn(e.api),n);if(n.json){u(i);return}E(i,[{header:"ID",value:o=>o.id},{header:"Name",value:o=>o.displayName??"-"},{header:"Type",value:o=>o.type},{header:"Modes",value:o=>Object.keys(o.modes).join(",")||"-"},{header:"Price",value:o=>jr(o)},{header:"4K",value:o=>Nr(o)},{header:"Input fee",value:o=>Dr(o)??"-"}],"No models matched the filters.");}),t};var Rn=Ne/1e3,Jr=e=>e.option("--detailed","Include the full task record instead of a compact summary"),En=e=>{let t=new Command("task").description("Inspect and await Mynth tasks");t.command("get").description("Fetch a task by ID").argument("<id>","Task ID").addOption(f()).action(async(i,o)=>{let a=await De(e.api,i);if(o.json){u(a);return}Oe(a);}),t.command("result").description("Print a task's result payload as JSON").argument("<id>","Task ID").action(async i=>{u((await ln(e.api,i)).result);});let n=t.command("wait").description("Block until a task completes or fails, then print it").argument("<id>","Task ID").option("--timeout <seconds>",`Max seconds to wait before giving up (default: ${Rn})`,je("--timeout")).addOption(f());return Jr(n).action(async(i,o)=>{let a=L(e.api,i,(o.timeout??Rn)*1e3),d=o.json?await a:await M(a);if(d.status==="failed"&&(process.exitCode=qe(d)),o.json){let m=!o.detailed&&Pe(d);u(m?se(d):d);return}ie(d);}),t.command("list").description("List recent tasks, newest first").option("--limit <number>","Max tasks to return (1-100, default: 20)",je("--limit")).option("--after <id>","Cursor: return tasks created before this task ID").addOption(f()).action(async i=>{let o=await dn(e.api,{...i.limit!==void 0?{limit:i.limit}:{},...i.after!==void 0?{after:i.after}:{}});if(i.json){u({tasks:o});return}E(o,[{header:"",value:a=>q(a.status)},{header:"ID",value:a=>a.id},{header:"Type",value:a=>a.type},{header:"Status",value:a=>a.status},{header:"Cost",value:a=>a.cost??"-"},{header:"Created",value:a=>a.createdAt}],"No tasks found.");}),t};var Cn=(e,t)=>e.fetch("webhook create","/webhook",ht,{body:t}),Pn=(e,t,n)=>e.fetch("webhook update",`/webhook/${t}`,wt,{method:"PUT",body:n}),_n=(e,t)=>e.call("webhook delete",`/webhook/${t}`,{method:"DELETE"});var Ke=["task.completed","task.failed","task.image.generate.completed","task.image.generate.failed","task.image.rate.completed","task.image.rate.failed","task.image.alt.completed","task.image.alt.failed","task.image.review.completed","task.image.review.failed","task.video.generate.completed","task.video.generate.failed"],zr=e=>{if(e===void 0||e.length===0)throw new l("at least one --event is required");if(e.includes("all"))return "all";for(let t of e)if(!Ke.includes(t))throw new l(`unknown event "${t}". Valid events: all, ${Ke.join(", ")}`);return e},Wr=e=>{if(e.enabled===true&&e.disabled===true)throw new l("--enabled and --disabled are mutually exclusive");return e.disabled!==true},On=e=>({enabled:Wr(e),url:e.url,events:zr(e.event),...e.apiKeyId!==void 0?{apiKeyIds:e.apiKeyId}:{},...e.oauthEvents===true?{oauthEnabled:true}:{}}),Sn=e=>Array.isArray(e)?e.join(", "):String(e),jn=e=>e.requiredOption("--url <url>","Destination URL for webhook deliveries").option("-e, --event <name>",`Event to subscribe to (repeatable). Use \`all\` for every event. One of: ${Ke.join(", ")}`,x).option("--api-key-id <id>","Only deliver tasks created by this API key (repeatable). Omit to deliver for every key.",x).option("--oauth-events","Also deliver tasks created by OAuth sessions (this CLI, the playground). Off by default.").addOption(f()),Nn=e=>{let t=new Command("webhook").description("Manage registered webhooks"),n=t.command("create").description("Register a webhook. The signing secret is shown once, on success.").option("--disabled","Create the webhook disabled (default: enabled)");jn(n).action(async o=>{let a=await Cn(e.api,On(o));if(o.json){u(a);return}s(`${g.ok} Webhook ${a.id} created`),s(` URL: ${a.url}`),s(` Enabled: ${a.enabled}`),s(` Events: ${Sn(a.events)}`),a.oauthEnabled!==void 0&&s(` OAuth: ${a.oauthEnabled}`),s(""),s(` Signing secret: ${a.secret}`),s(" Save this now \u2014 it is shown only once and cannot be retrieved again.");});let i=t.command("update").description("Replace a webhook's configuration. Every field is sent, so pass them all.").argument("<id>","Webhook ID").option("--enabled","Enable the webhook").option("--disabled","Disable the webhook");return jn(i).action(async(o,a)=>{let d=await Pn(e.api,o,On(a));if(a.json){u(d);return}s(`${g.ok} Webhook ${d.id} updated`),s(` URL: ${d.url}`),d.enabled!==void 0&&s(` Enabled: ${d.enabled}`),s(` Events: ${Sn(d.events)}`),d.oauthEnabled!==void 0&&s(` OAuth: ${d.oauthEnabled}`);}),t.command("delete").description("Delete a webhook").argument("<id>","Webhook ID").addOption(j()).addOption(f()).action(async(o,a)=>{if(a.yes!==true)throw new l("refusing to delete without --yes");if(await _n(e.api,o),a.json){u({deleted:o});return}s(`${g.ok} Deleted webhook ${o}`);}),t};var Hr=100,Gr=`
9
9
  Exit codes:
10
10
  0 success
11
11
  1 error (network, server, or unexpected failure)
@@ -19,5 +19,5 @@ Environment:
19
19
  MYNTH_API_KEY API key; takes precedence over stored credentials
20
20
  MYNTH_DESTINATION default --destination for image generation
21
21
  MYNTH_DEBUG=1 print error causes and stack details to stderr
22
- XDG_CONFIG_HOME where credentials are stored (default: ~/.config)`,Ke=class extends Help{optionTerm(t){return `(${t.flags.replaceAll("<","").replaceAll(">","")})`}subcommandTerm(t){let n=t.registeredArguments.map(r=>r.required?`${r.name()}${r.variadic?"...":""}`:`[${r.name()}]`).join(" ");return n.length>0?`${t.name()} ${n}`:t.name()}},_n=e=>{e.configureHelp({helpWidth:Nr}),e.createHelp=()=>new Ke,e.exitOverride();for(let t of e.commands)_n(t);},On=()=>{let e=kt(),t=new Command("mynth").description("Official Mynth CLI").version(He).addHelpText("before",({error:n})=>(n?process.stderr.isTTY:process.stdout.isTTY)?`${bt()}
23
- `:"").addHelpText("after",Dr);for(let n of [Tt(e),Ft(e),Ut(e),Wt(e),en(e),tn(e),gn(e),vn(e),$n(e),Pn(e),ve(e)])t.addCommand(n);return _n(t),t};var Sn=e=>e instanceof Error?e.message:String(e);try{await On().parseAsync(process.argv);}catch(e){let t=e?.code;t==="commander.helpDisplayed"||t==="commander.version"||((typeof t!="string"||!t.startsWith("commander."))&&(T(Sn(e)),G().debug&&e instanceof Error&&(T("=== MYNTH_DEBUG ==="),T(e.stack??e.message),e.cause!==void 0&&T(`cause: ${Sn(e.cause)}`))),process.exitCode=Ye(e));}
22
+ XDG_CONFIG_HOME where credentials are stored (default: ~/.config)`,ze=class extends Help{optionTerm(t){return `(${t.flags.replaceAll("<","").replaceAll(">","")})`}subcommandTerm(t){let n=t.registeredArguments.map(i=>i.required?`${i.name()}${i.variadic?"...":""}`:`[${i.name()}]`).join(" ");return n.length>0?`${t.name()} ${n}`:t.name()}},Dn=e=>{e.configureHelp({helpWidth:Hr}),e.createHelp=()=>new ze,e.exitOverride();for(let t of e.commands)Dn(t);},Ln=()=>{let e=bt(),t=new Command("mynth").description("Official Mynth CLI").version(He).addHelpText("before",({error:n})=>(n?process.stderr.isTTY:process.stdout.isTTY)?`${vt()}
23
+ `:"").addHelpText("after",Gr);for(let n of [Rt(e),Ft(e),Ut(e),Bt(e),tn(e),nn(e),fn(e),Tn(e),En(e),Nn(e),ve(e)])t.addCommand(n);return Dn(t),t};var Mn=e=>e instanceof Error?e.message:String(e);try{await Ln().parseAsync(process.argv);}catch(e){let t=e?.code;t==="commander.helpDisplayed"||t==="commander.version"||((typeof t!="string"||!t.startsWith("commander."))&&(T(Mn(e)),G().debug&&e instanceof Error&&(T("=== MYNTH_DEBUG ==="),T(e.stack??e.message),e.cause!==void 0&&T(`cause: ${Mn(e.cause)}`))),process.exitCode=Ge(e));}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mynthio/cli",
3
- "version": "0.0.20",
3
+ "version": "0.0.21",
4
4
  "description": "Official Mynth CLI",
5
5
  "keywords": [
6
6
  "ai",