@mynthio/cli 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -17
- package/dist/bin.js +8 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,22 +62,22 @@ mynth image generate -p "A watercolor city skyline" --size 16:9 --count 2 -o ./o
|
|
|
62
62
|
mynth image generate -p "A neon koi pond" --magic-prompt --format png
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
| Flag | Purpose
|
|
66
|
-
| ------------------------- |
|
|
67
|
-
| `-p, --prompt` | Prompt.
|
|
68
|
-
| `-n, --negative` | Negative prompt.
|
|
69
|
-
| `--magic-prompt` | Let Mynth expand the prompt before generating. Off by default.
|
|
70
|
-
| `-m, --model` | Model ID. Defaults to `auto`. See `mynth models list`.
|
|
71
|
-
| `-s, --size` | Preset or aspect ratio: `square`, `landscape`, `16:9`, `16:9_4k`, `auto`, …
|
|
72
|
-
| `-c, --count` | Images per request.
|
|
73
|
-
| `-f, --format` | `png`, `jpg`, or `webp`.
|
|
74
|
-
| `-i, --input` | Input image as `[role:]path-or-url`, repeatable. Roles: `auto`, `
|
|
75
|
-
| `-o, --output-dir` | Download the results into this directory.
|
|
76
|
-
| `--destination` | Deliver to a configured storage destination. Defaults to `MYNTH_DESTINATION`.
|
|
77
|
-
| `--content-rating` | Classify each image `sfw`/`nsfw`. Use `--level` for custom levels.
|
|
78
|
-
| `--webhook-url` | Deliver this task's events to a URL (repeatable).
|
|
79
|
-
| `--no-dashboard-webhooks` | Skip dashboard-configured webhooks for this task.
|
|
80
|
-
| `--metadata` | Inline JSON attached to the task.
|
|
65
|
+
| Flag | Purpose |
|
|
66
|
+
| ------------------------- | -------------------------------------------------------------------------------------- |
|
|
67
|
+
| `-p, --prompt` | Prompt. |
|
|
68
|
+
| `-n, --negative` | Negative prompt. |
|
|
69
|
+
| `--magic-prompt` | Let Mynth expand the prompt before generating. Off by default. |
|
|
70
|
+
| `-m, --model` | Model ID. Defaults to `auto`. See `mynth models list`. |
|
|
71
|
+
| `-s, --size` | Preset or aspect ratio: `square`, `landscape`, `16:9`, `16:9_4k`, `auto`, … |
|
|
72
|
+
| `-c, --count` | Images per request. |
|
|
73
|
+
| `-f, --format` | `png`, `jpg`, or `webp`. |
|
|
74
|
+
| `-i, --input` | Input image as `[role:]path-or-url`, repeatable. Roles: `auto`, `source`, `reference`. |
|
|
75
|
+
| `-o, --output-dir` | Download the results into this directory. |
|
|
76
|
+
| `--destination` | Deliver to a configured storage destination. Defaults to `MYNTH_DESTINATION`. |
|
|
77
|
+
| `--content-rating` | Classify each image `sfw`/`nsfw`. Use `--level` for custom levels. |
|
|
78
|
+
| `--webhook-url` | Deliver this task's events to a URL (repeatable). |
|
|
79
|
+
| `--no-dashboard-webhooks` | Skip dashboard-configured webhooks for this task. |
|
|
80
|
+
| `--metadata` | Inline JSON attached to the task. |
|
|
81
81
|
|
|
82
82
|
Local paths passed to `-i` are uploaded first; `https://` inputs are used as-is.
|
|
83
83
|
|
|
@@ -123,6 +123,26 @@ is why they are most useful alongside `--type`.
|
|
|
123
123
|
`--capability` matches the modes a model actually serves, so a model that both takes a prompt and
|
|
124
124
|
accepts images matches `txt2img` and `img2img` alike.
|
|
125
125
|
|
|
126
|
+
## Removing backgrounds
|
|
127
|
+
|
|
128
|
+
Takes one URL or one local file. Mynth picks the model:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
mynth image remove-background https://cdn.example.com/product.jpg
|
|
132
|
+
mynth image remove-background ./shot.jpg --format webp -o ./out
|
|
133
|
+
mynth image remove-background ./shot.jpg --destination bunny-prod --async --json
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
| Flag | Purpose |
|
|
137
|
+
| ------------------------- | ----------------------------------------------------------------------------- |
|
|
138
|
+
| `-f, --format` | `png` or `webp`. Defaults to whatever the provider returns. |
|
|
139
|
+
| `-o, --output-dir` | Download the result into this directory. |
|
|
140
|
+
| `--destination` | Deliver to a configured storage destination. Defaults to `MYNTH_DESTINATION`. |
|
|
141
|
+
| `--webhook-url` | Deliver this task's events to a URL (repeatable). |
|
|
142
|
+
| `--no-dashboard-webhooks` | Skip dashboard-configured webhooks for this task. |
|
|
143
|
+
| `--metadata` | Inline JSON attached to the task. |
|
|
144
|
+
| `--async` | Print the task ID and a public access token instead of waiting. |
|
|
145
|
+
|
|
126
146
|
## Analyzing images
|
|
127
147
|
|
|
128
148
|
Every analysis command takes one URL or one local file, and waits for the result:
|
|
@@ -253,7 +273,7 @@ failure exits 1.
|
|
|
253
273
|
| Variable | Effect |
|
|
254
274
|
| --------------------- | ---------------------------------------------------------- |
|
|
255
275
|
| `MYNTH_API_KEY` | API key; takes precedence over stored credentials |
|
|
256
|
-
| `MYNTH_DESTINATION` | Default `--destination` for image
|
|
276
|
+
| `MYNTH_DESTINATION` | Default `--destination` for image commands |
|
|
257
277
|
| `MYNTH_DEBUG=1` | Print stack traces and error causes to stderr |
|
|
258
278
|
| `MYNTH_NO_KEYCHAIN=1` | Store credentials in a file instead of the system keychain |
|
|
259
279
|
| `MYNTH_API_URL` | Override the API base URL |
|
package/dist/bin.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import J from'chalk';import {Command,Option,Help}from'commander';import {z}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 dr from'ora';import Tn from'fuzzysort';var be="client_01KATK792RR5ZCHMF5YMNN1ZSE",Xe=process.env.MYNTH_WORKOS_API_URL??"https://api.workos.com",Ze="0.0.22",qe=e=>e.replace(/\/+$/,""),Ve=e=>e!==void 0&&e.length>0?e:void 0,q=()=>({apiUrl:qe(process.env.MYNTH_API_URL??"https://api.mynth.io"),docsUrl:qe(process.env.MYNTH_DOCS_URL??"https://docs.mynth.io"),envApiKey:Ve(process.env.MYNTH_API_KEY),envDestination:Ve(process.env.MYNTH_DESTINATION),debug:process.env.MYNTH_DEBUG==="1"||process.env.MYNTH_DEBUG==="true"});var b={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;}},O=class extends y{code;constructor(t,n,o){super(n,o),this.code=t;}},Hn={UNAUTHORIZED:b.auth,INSUFFICIENT_SCOPE:b.auth,VALIDATION_ERROR:b.usage,INSUFFICIENT_BALANCE:b.insufficientCredits,SPENDING_LIMIT_EXCEEDED:b.insufficientCredits,RESTRICTED_CONTENT:b.moderation},Qe=e=>{if(e instanceof l)return b.usage;if(e instanceof $)return b.auth;if(e instanceof k){let n=e.code!==void 0?Hn[e.code]:void 0;return n!==void 0?n:e.status===401||e.status===403?b.auth:e.status===429?b.rateLimited:b.error}let t=e?.code;return typeof t=="string"&&t.startsWith("commander.")?b.usage:b.error},ve=e=>{let t=e.result,n=[...(e.errors??[]).map(o=>o.code),...(t?.images??[]).map(o=>o.error?.code)].filter(o=>typeof o=="string");return n.find(o=>o==="RESTRICTED_CONTENT")??n[0]},et=e=>ve(e)==="RESTRICTED_CONTENT"?b.moderation:b.error;var a=(e="")=>{process.stdout.write(`${e}
|
|
3
3
|
`);},T=(e="")=>{process.stderr.write(`${e}
|
|
4
|
-
`);},
|
|
5
|
-
`).map(
|
|
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=`
|
|
4
|
+
`);},p=e=>{a(JSON.stringify(e,null,2));},g={ok:J.green("\u2713"),fail:J.red("\u2717"),pending:J.yellow("\u2026")},V=e=>e==="completed"?g.ok:e==="failed"?g.fail:g.pending,F=(e,t,n="s")=>`${e} ${t}${e===1?"":n}`,tt=(e,t=2)=>{let n=" ".repeat(t);return e.split(`
|
|
5
|
+
`).map(o=>`${n}${o}`).join(`
|
|
6
|
+
`)};var _=z.lazy(()=>z.union([z.string(),z.number(),z.boolean(),z.null(),z.array(_),z.record(_)])),nt=e=>z.object({data:e}),Gn=z.object({id:z.string(),email:z.string(),first_name:z.string().nullable().optional(),last_name:z.string().nullable().optional()}),ot=z.object({device_code:z.string(),user_code:z.string(),verification_uri:z.string(),verification_uri_complete:z.string().optional(),expires_in:z.number(),interval:z.number().optional()}),rt=z.object({access_token:z.string(),refresh_token:z.string(),user:Gn.optional()}),it=z.object({error:z.string().optional(),error_description:z.string().optional(),message:z.string().optional(),code:z.string().optional()}),at=z.object({kind:z.literal("api_key"),api_key:z.string(),id:z.string().optional()}),R=["generate","manage","keys"],Ie=z.object({raw:z.string(),apiKey:z.object({id:z.string(),name:z.string().optional(),keyPreview:z.string(),scopes:z.array(z.string())})}),qn=z.union([z.string(),z.number()]).nullable().optional(),st=z.object({id:z.string(),name:z.string().nullable(),keyPreview:z.string(),scopes:z.array(z.string()),spendingLimit:qn,spendingLimitPeriod:z.string().nullable().optional(),createdAt:z.string()}),Vn=z.union([z.object({mode:z.literal("unlimited")}),z.object({mode:z.literal("limited"),limit:z.string(),period:z.string(),used:z.string(),remaining:z.string()})]),dt=z.object({userId:z.string(),auth:z.object({method:z.string(),apiKey:z.object({id:z.string(),name:z.string().nullable(),keyPreview:z.string(),scopes:z.array(z.string()).optional(),spending:Vn.optional()}).optional()})}),lt=z.object({balance:z.string(),reserved:z.string(),available:z.string(),currency:z.string()}),Xn=z.object({type:z.string(),kind:z.string().optional(),min:z.number().optional(),max:z.number()}),Zn=z.object({inputs:z.object({rules:z.array(Xn),maxTotal:z.number().optional()}).optional()}),Qn=z.object({perImage:z.object({base:z.string(),"4k":z.string().optional()}),perInput:z.string().optional()}),eo=z.object({perSecond:z.record(z.string(),z.string()),audio:z.object({perSecond:z.string()}).optional()}),ct=z.object({id:z.string(),displayName:z.string().nullable(),type:z.string(),modes:z.record(z.string(),Zn),pricing:z.union([Qn,eo]).nullable()}),X=z.enum(["pending","completed","failed"]),pt=z.object({id:z.string(),type:z.string(),status:X,userId:z.string().optional(),apiKeyId:z.string().nullable().optional(),cost:z.string().nullable(),request:_.optional(),result:_.nullable(),errors:z.array(z.object({code:z.string(),message:z.string().optional()})).nullish(),createdAt:z.string(),updatedAt:z.string()}),mt=z.object({id:z.string(),type:z.string(),status:z.string(),cost:z.string().nullable(),createdAt:z.string(),updatedAt:z.string()}),ut=z.object({id:z.string(),type:z.string(),status:X,result:_.nullable()}),gt=z.object({urls:z.array(z.string())}),ft=z.object({taskId:z.string(),estimatedCost:z.string().optional(),access:z.object({publicAccessToken:z.string()}).optional()}),yt=z.object({estimatedCost:z.string(),currency:z.string(),estimateKind:z.enum(["exact","upper_bound"])}),ht=z.object({level:z.string()}),wt=z.object({alt:z.string()}),kt=z.object({score:z.number(),summary:z.string(),findings:z.array(z.object({finding:z.string(),category:z.string(),severity:z.string(),where:z.string(),confidence:z.string()})).optional(),strengths:z.array(z.object({strength:z.string(),confidence:z.string()})).optional()}),$e=z.object({code:z.string(),message:z.string().optional()}),bt=z.union([z.object({status:z.literal("success"),name:z.string()}),z.object({status:z.literal("failed"),name:z.string(),error:$e})]),to=z.union([z.object({status:z.literal("success"),id:z.string().optional(),url:z.string().nullable().optional(),mynth_url:z.string().optional(),size:z.string().optional(),format:z.string().optional(),rating:z.union([z.object({status:z.literal("success"),level:z.string()}),z.object({status:z.literal("failed"),error:$e})]).optional(),destination:bt.optional()}),z.object({status:z.literal("failed"),error:$e})]),vt=z.object({model:z.string().optional(),images:z.array(to).optional(),magic_prompt:z.object({positive:z.string(),negative:z.string().optional()}).optional()}),At=z.object({image:z.object({id:z.string(),url:z.string().nullable(),mynth_url:z.string(),size:z.string(),format:z.string(),destination:bt.optional()})}),U=z.object({id:z.string(),name:z.string(),provider:z.object({id:z.string()}).catchall(_),config:z.object({path_template:z.string(),url_template:z.string().optional()}).partial(),createdAt:z.string(),updatedAt:z.string()}),$t=z.union([z.literal("all"),z.array(z.string())]),It=z.object({id:z.string(),enabled:z.boolean(),url:z.string(),secret:z.string(),events:$t,apiKeyIds:z.array(z.string()).nullish(),oauthEnabled:z.boolean().optional(),createdAt:z.string().optional()}),xt=z.object({id:z.string(),enabled:z.boolean().optional(),url:z.string(),events:$t,apiKeyIds:z.array(z.string()).nullish(),oauthEnabled:z.boolean().optional()});var no=e=>{if(e===void 0)return "";let t=new URLSearchParams;for(let[n,o]of Object.entries(e))o!==void 0&&t.set(n,String(o));return t.size>0?`?${t}`:""},oo=e=>e===void 0?{headers:{}}:e instanceof FormData?{body:e,headers:{}}:{body:JSON.stringify(e),headers:{"Content-Type":"application/json"}},ro=async e=>{try{return await e.text()}catch{return ""}},io=async(e,t)=>{let n=await ro(e),o,r;try{let s=JSON.parse(n);typeof s.code=="string"&&(o=s.code),typeof s.message=="string"?r=s.message:typeof s.error=="string"&&(r=s.error);}catch{}return new k(`${t} failed (${e.status}): ${r??n??"no body"}`,{status:e.status,...o!==void 0?{code:o}:{}})},Z=class{constructor(t,n){this.tokens=n;this.baseUrl=t.apiUrl;}baseUrl;async send(t,n,o={}){let r=await this.attempt(n,o);if(r.ok)return r;throw await io(r,t)}async fetch(t,n,o,r={}){let s=await this.send(t,n,r),d;try{d=await s.json();}catch(h){throw new k(`${t} returned invalid JSON: ${h.message}`,{status:s.status,cause:h})}let c=nt(o).safeParse(d);if(!c.success)throw new k(`${t} returned an unexpected response shape`,{status:s.status,cause:c.error});return c.data.data}async call(t,n,o={}){await this.send(t,n,o);}async attempt(t,n){let{body:o,headers:r}=oo(n.body),s=n.auth===false?void 0:n.token??await this.tokens.token();try{return await fetch(`${this.baseUrl}${t}${no(n.query)}`,{method:n.method??(n.body!==void 0?"POST":"GET"),headers:{...r,...s!==void 0?{Authorization:`Bearer ${s}`}:{}},...o!==void 0?{body:o}:{}})}catch(d){throw new k(`request to ${t} failed: ${d.message}`,{status:0,cause:d})}}};var ao=2048,so=/^[A-Za-z0-9._~%-]+$/,lo=e=>{let t=e.trim();if(t.length===0)throw new l("documentation path must not be empty");if(t.length>ao)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 o=n.split("/");if(o.some(r=>!so.test(r)||r==="."||r===".."))throw new l("documentation path contains an invalid segment");return o.join("/")},co=e=>e.length>500?`${e.slice(0,500)}\u2026`:e,Tt=async(e,t)=>{let n;try{n=await fetch(e);}catch(r){throw new k(`${t} failed: ${r.message}`,{status:0,cause:r})}let o=await n.text().catch(()=>"");if(!n.ok)throw new k(`${t} failed (${n.status}): ${co(o)||"no body"}`,{status:n.status});return o},Q=class{constructor(t){this.docsUrl=t;}async get(t){let n=lo(t),o=n.split("/").map(encodeURIComponent).join("/");return {path:n,content:await Tt(`${this.docsUrl}/${o}.md`,`docs fetch for ${n}`)}}list(){return Tt(`${this.docsUrl}/llms.txt`,"docs index fetch")}};var ko="credentials.json",bo=384,vo=()=>{let e=process.env.XDG_CONFIG_HOME;return join(e!==void 0&&e.length>0?e:join(homedir(),".config"),"mynth")},Ao=async e=>{try{return await stat(e),true}catch{return false}},ee=class{filePath;constructor(){this.filePath=join(vo(),ko);}async get(){if(!await Ao(this.filePath))return;let t;try{t=await readFile(this.filePath,"utf8");}catch(r){throw new y(`could not read ${this.filePath}: ${r.message}`,{cause:r})}let n;try{n=JSON.parse(t);}catch(r){throw new y(`${this.filePath} is not valid JSON`,{cause:r})}let o=at.safeParse(n);if(!o.success)throw new y(`${this.filePath} has an unexpected shape; run \`mynth auth login\``,{cause:o.error});return o.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:bo}),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 te=class{constructor(t){this.config=t;this.store=new ee,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 Rt=()=>{let e=q(),t=new te(e);return {config:e,session:t,api:new Z(e,t),docs:new Q(e.docsUrl)}};var Te=["...........",".....4.....","....343....",".....2.....",".344.2.443.","..4432344..",".....2.....",".233.2.332.","..3322233..",".....1.....","..........."],K={1:"#318267",2:"#00a27a",3:"#33bb92",4:"#6bd1ad"},$o=(e,t)=>{let n=o=>o!==".";return !n(e)&&!n(t)?" ":J.level===0?n(e)&&n(t)?"\u2588":n(e)?"\u2580":"\u2584":n(t)?n(e)?e===t?J.hex(K[e])("\u2588"):J.hex(K[e]).bgHex(K[t])("\u2580"):J.hex(K[t])("\u2584"):J.hex(K[e])("\u2580")},Ct=()=>{let e=[];for(let t=0;t<Te.length;t+=2){let n=Te[t],o=Te[t+1]??"",r="";for(let s=0;s<n.length;s+=1)r+=$o(n[s],o[s]??".");e.push(r.trimEnd());}return e.filter(t=>t.length>0).join(`
|
|
7
|
+
`)};var Et=(e,t)=>e.fetch("api key create","/api-key",Ie,{body:{name:t.name,scopes:t.scopes},token:t.token}),Pt=(e,t)=>e.fetch("api key create","/api-key",Ie,{body:{name:t.name,scopes:t.scopes}}),Ot=e=>e.fetch("api key list","/api-key",z.array(st)),ne=(e,t)=>e.call("api key delete",`/api-key/${t}`,{method:"DELETE"});var C=(e,t,n)=>{if(e.length===0){a(n);return}let o=e.map(d=>t.map(c=>c.value(d))),r=t.map((d,c)=>Math.max(d.header.length,...o.map(h=>h[c].length))),s=d=>d.map((c,h)=>h===d.length-1?c:c.padEnd(r[h])).join(" ");a(s(t.map(d=>d.header)));for(let d of o)a(s(d));};var f=()=>new Option("--json","Output machine-readable JSON instead of a human-readable summary"),S=()=>new Option("--yes","Confirm the destructive action (required; there is no interactive prompt)");var St=["generate"],To=e=>{if(e===void 0)return St;let t=e.split(",").map(o=>o.trim()).filter(Boolean),n=t.filter(o=>!R.includes(o));if(t.length===0||n.length>0)throw new l(`invalid --scopes: ${n.join(", ")||"empty"}. Valid scopes: ${R.join(", ")}`);return t},jt=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: ${St.join(",")}). Widening beyond \`generate\` requires the dashboard: the API refuses scope escalation from a CLI session.`).addOption(f()).action(async(n,o)=>{let r=await Pt(e.api,{name:n,scopes:To(o.scopes)});if(o.json){p({key:r.raw,...r.apiKey});return}a(`${g.ok} Created API key "${r.apiKey.name??n}"`),a(` ID: ${r.apiKey.id}`),a(` Scopes: ${r.apiKey.scopes.join(", ")}`),a(""),a(` ${r.raw}`),a(" 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 o=await Ot(e.api);if(n.json){p(o);return}C(o,[{header:"ID",value:r=>r.id},{header:"Name",value:r=>r.name??"-"},{header:"Preview",value:r=>r.keyPreview},{header:"Scopes",value:r=>r.scopes.join(",")},{header:"Limit",value:r=>r.spendingLimit===null||r.spendingLimit===void 0?"-":`$${r.spendingLimit}/${r.spendingLimitPeriod??"period"}`},{header:"Created",value:r=>r.createdAt}],"No API keys found.");}),t.command("delete").description("Revoke an API key").argument("<id>","API key ID").addOption(S()).addOption(f()).action(async(n,o)=>{if(o.yes!==true)throw new l("refusing to delete without --yes");if(await ne(e.api,n),o.json){p({deleted:n});return}a(`${g.ok} Revoked API key ${n}`);}),t};var oe=e=>e.fetch("me","/me",dt),Dt=e=>e.fetch("balance","/balance",lt);var Ro="urn:ietf:params:oauth:grant-type:device_code",Nt=async e=>{try{return await e.json()}catch{return {}}},Lt=async(e,t,n)=>{try{return await fetch(`${Xe}${e}`,{method:"POST",body:t,headers:{Accept:"application/json",...typeof t=="string"?{"Content-Type":"application/json"}:{}}})}catch(o){throw new y(`${n} failed: ${o.message}`,{cause:o})}},Mt=async(e,t)=>{let n=it.catch({}).parse(await Nt(e)),o=n.error??n.code??"workos_error";return new O(o,n.error_description??n.message??`${t} failed`)},Ft=async(e,t,n)=>{let o=t.safeParse(await Nt(e));if(!o.success)throw new y(`${n} returned an unexpected response`,{cause:o.error});return o.data},Ut=async()=>{let e=await Lt("/user_management/authorize/device",new URLSearchParams({client_id:be}),"device authorization");if(!e.ok)throw await Mt(e,"device authorization");return Ft(e,ot,"device authorization")},Jt=async e=>{let t=await Lt("/user_management/authenticate",JSON.stringify({grant_type:Ro,client_id:be,device_code:e}),"device token exchange");if(!t.ok)throw await Mt(t,"device token exchange");return Ft(t,rt,"device token exchange")};var re=e=>new Promise(t=>setTimeout(t,e)),Kt=async(e,t,n)=>{let o=Array.from({length:e.length}),r=0,s=async()=>{for(;;){let d=r++;if(d>=e.length)return;o[d]=await n(e[d],d);}};return await Promise.all(Array.from({length:Math.min(t,e.length)},s)),o};var zt=()=>{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")))},Po=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]}}},Wt=async e=>{try{let{protocol:o}=new URL(e);if(o!=="https:"&&o!=="http:")return false}catch{return false}let{command:t,args:n}=Po(e);return new Promise(o=>{try{let r=spawn(t,[...n],{detached:true,stdio:"ignore"});r.on("error",()=>o(false)),r.unref(),setTimeout(()=>o(true),150).unref();}catch{o(false);}})};var _o="https://mynth.io/dashboard",Bt=e=>`${_o}/keys/${e}`,So=5e3,jo=5,Do=()=>`mynth-cli (${hostname()})`,No=async(e,t,n)=>{let o=t;for(;;){if(Date.now()>=n)throw new y("device code expired before approval");try{return await Jt(e)}catch(r){if(!(r instanceof O))throw r;if(r.code==="access_denied")throw new $("login was denied");if(r.code==="expired_token")throw new $("device code expired; run `mynth auth login` again");if(r.code!=="authorization_pending"&&r.code!=="slow_down")throw r;r.code==="slow_down"&&(o+=So),await re(o);}}},Lo=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(P=>P.trim()).filter(Boolean):[...R],o=n.filter(P=>!R.includes(P));if(n.length===0||o.length>0)throw new $(`invalid --scopes: ${o.join(", ")||"empty"}. Valid scopes: ${R.join(", ")}`);let r=await Ut(),s=r.verification_uri_complete??r.verification_uri,d=r.verification_uri_complete!==void 0,c=t.browser!==false&&zt()?await Wt(s):false;a(""),a(` ${c?"Opened":"Open"}: ${s}`),a(d?` Check the page shows this code: ${r.user_code}`:` Enter this code: ${r.user_code}`),a(""),a("Waiting for confirmation...");let h=await No(r.device_code,(r.interval??jo)*1e3,Date.now()+r.expires_in*1e3),v=Do(),w=await Et(e.api,{name:v,scopes:n,token:h.access_token});await e.session.save({kind:"api_key",api_key:w.raw,id:w.apiKey.id});let I=h.user?.email??h.user?.id??"unknown user";if(t.json){p({user:h.user?.id??null,apiKey:{id:w.apiKey.id,name:w.apiKey.name??v,keyPreview:w.apiKey.keyPreview,scopes:w.apiKey.scopes},storedAt:e.session.store.filePath});return}a(`${g.ok} Logged in as ${I}`),a(` Created API key "${v}" with scopes: ${w.apiKey.scopes.join(", ")}`),a(` Stored in ${e.session.store.filePath}`),a(` Set a spending limit or change scopes: ${Bt(w.apiKey.id)}`);}),Mo=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,o=false;if(n!==void 0)try{await ne(e.api,n),o=true;}catch(r){T(`Warning: could not revoke API key ${n}: ${r.message}`),T(`Revoke it manually at ${Bt(n)}`);}await e.session.clear(),a(`${g.ok} Local credentials cleared${o?" and API key revoked":""}`),n===void 0&&t.kind==="stored"&&a(" The stored key was not created by `auth login`, so it was left active."),e.session.envApiKeySet&&a("Note: MYNTH_API_KEY is still set in your environment and will still be used.");}),Fo=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){p({source:n.kind,...n.kind==="stored"?{apiKeyId:n.credentials.id??null,path:e.session.store.filePath}:{}});return}switch(n.kind){case "env":a("Authenticated via env: MYNTH_API_KEY");return;case "none":a("Not authenticated. Run `mynth auth login`, or set an API key.");return;case "stored":{let{id:o}=n.credentials;a("Authenticated via stored API key"),o!==void 0&&a(` key: ${o}`),a(` stored: ${e.session.store.filePath}`),a(" Run `mynth whoami` for its current scopes and spending limit.");}}}),Re=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 o=await oe(e.api);if(t.json){p({source:n.kind,...o});return}a(n.kind==="env"?"env:MYNTH_API_KEY":"api-key"),a(` user: ${o.userId}`),a(` method: ${o.auth.method}`);let r=o.auth.apiKey;r!==void 0&&(a(` key: ${r.name??"unnamed"} (${r.keyPreview})`),r.scopes!==void 0&&r.scopes.length>0&&a(` scopes: ${r.scopes.join(", ")}`),r.spending?.mode==="limited"&&a(` spend: $${r.spending.used} of $${r.spending.limit} per ${r.spending.period} ($${r.spending.remaining} left)`));}),Yt=e=>new Command("auth").description("Manage Mynth authentication").addCommand(Lo(e)).addCommand(Mo(e)).addCommand(Fo(e)).addCommand(Re(e));var Ht=e=>new Command("balance").description("Show account balance, and the active API key's spending limit").addOption(f()).action(async t=>{let n=await Dt(e.api),r=(await oe(e.api).catch(()=>{}))?.auth.apiKey?.spending;if(t.json){p({...n,...r!==void 0?{spending:r}:{}});return}if(a(`Balance: $${n.balance}`),a(`Reserved: $${n.reserved}`),a(`Available: $${n.available}`),r!==void 0){if(a(""),r.mode==="unlimited"){a("API key spending: unlimited");return}a(`API key limit: $${r.limit} / ${r.period}`),a(` used: $${r.used}`),a(` remaining: $${r.remaining}`);}});var qt={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".webp":"image/webp"},Ce=Object.keys(qt),Ee=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})}},W=async e=>{if(e==="-")return Ee();try{return await readFile(e,"utf8")}catch(t){throw new l(`could not read ${e}: ${t.message}`)}},Vt=async e=>{let t=await W(e);try{return JSON.parse(t)}catch(n){throw new l(`invalid JSON in ${e}: ${n.message}`)}},Xt=async e=>{let t=extname(e).toLowerCase(),n=qt[t];if(n===void 0)throw new l(`unsupported image extension "${t}" for ${e} (allowed: ${Ce.join(", ")})`);let o;try{o=await readFile(e);}catch(r){throw new l(`could not read ${e}: ${r.message}`)}return new File([new Uint8Array(o)],basename(e),{type:n})};var Zt=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 o=>{let r=(o==="-"?await Ee():o).trim();if(r.length===0)throw new l("API key is empty");await e.session.save({kind:"api_key",api_key:r}),a(`${g.ok} API key saved to ${e.session.store.filePath}`),e.session.envApiKeySet&&a("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(),a(`${g.ok} Stored credentials cleared`),a("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 Qt=e=>e.fetch("destination list","/destinations",z.array(U)),en=(e,t)=>e.fetch("destination fetch",`/destinations/${t}`,U),tn=(e,t)=>e.fetch("destination create","/destinations",U,{body:t}),nn=(e,t,n)=>e.fetch("destination update",`/destinations/${t}`,U,{method:"PUT",body:n}),on=(e,t,n)=>e.call("destination test",`/destinations/${t}/test`,{body:{path:n}}),rn=(e,t)=>e.call("destination delete",`/destinations/${t}`,{method:"DELETE"});var Oe=/^[a-z0-9-]+$/,_e=64,Bo=["bunny","r2","s3"],je=["de","uk","ny","la","sg","se","br","jh","syd"],Yo=["default","eu","fedramp"],Ho=(e,t)=>{if(e===void 0)throw new l(`${t} is required`);return e},B=(e,t,n)=>{if(t===void 0)throw new l(`--provider ${e} requires ${n}`);return t},Y=(e,t)=>t!==void 0?{[e]:t}:{},Go=(e,t)=>{switch(t){case "bunny":{let n=e.region;if(n!==void 0&&!je.includes(n))throw new l(`invalid bunny --region "${n}". One of: ${je.join(", ")}`);return {id:"bunny",storage_zone:B(t,e.storageZone,"--storage-zone"),...Y("region",n)}}case "r2":return {id:"r2",account_id:B(t,e.accountId,"--account-id"),bucket:B(t,e.bucket,"--bucket"),...Y("jurisdiction",e.jurisdiction)};case "s3":return {id:"s3",bucket:B(t,e.bucket,"--bucket"),region:B(t,e.region,"--region"),...Y("endpoint",e.endpoint),...Y("force_path_style",e.forcePathStyle)}}},qo=async(e,t)=>{let n=(await W(e)).trim();if(n.startsWith("{"))try{return JSON.parse(n)}catch(o){throw new l(`invalid JSON secret from ${e}: ${o.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}},Vo=e=>({path_template:Ho(e.pathTemplate,"--path-template"),...Y("url_template",e.urlTemplate)}),sn=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 s=await Vt(e.file);if(s===null||typeof s!="object"||Array.isArray(s))throw new l(`${e.file} must contain a JSON object`);return {...s,...t}}let o=e.provider;if(o===void 0)throw new l("--provider or --file is required");let r=e.secret!==void 0?await qo(e.secret,o):void 0;if(r===void 0&&n)throw new l("--secret <path|-> is required (use `-` to read the secret from stdin)");return {...t,provider:Go(e,o),config:Vo(e),...r!==void 0?{secret:r}:{}}},dn=e=>e.addOption(new Option("--provider <id>","Storage provider. Required unless --file is used.").choices([...Bo])).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 ${je.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([...Yo])).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()),Se=e=>{a(`Destination ${e.id}`),a(` Name: ${e.name}`),a(` Provider: ${e.provider.id}`),e.config.path_template!==void 0&&a(` Path: ${e.config.path_template}`),e.config.url_template!==void 0&&a(` URL: ${e.config.url_template}`),a(` Created: ${e.createdAt}`),a(` Updated: ${e.updatedAt}`);},ln=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 r=>{let s=await Qt(e.api);if(r.json){p(s);return}C(s,[{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(r,s)=>{let d=await en(e.api,r);if(s.json){p(d);return}Se(d);});let n=t.command("create").description("Create a storage destination").argument("<name>",`Destination slug: 1-${_e} chars, ${Oe.source}. Immutable.`);dn(n).action(async(r,s)=>{if(r.length>_e||!Oe.test(r))throw new l(`invalid destination name "${r}": expected 1-${_e} chars matching ${Oe.source}`);let d=await tn(e.api,await sn(s,{name:r},true));if(s.json){p(d);return}Se(d),a(""),a(`Next: verify the credentials with \`mynth destination test ${d.id}\``);});let o=t.command("update").description("Replace a destination's provider and config. The slug is immutable.").argument("<id>","Destination ID");return dn(o).action(async(r,s)=>{let d=await sn(s,{},false);delete d.name;let c=await nn(e.api,r,d);if(s.json){p(c);return}Se(c);}),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(r,s)=>{let d=s.path??`mynth-cli-test/${Date.now()}.txt`;if(await on(e.api,r,d),s.json){p({id:r,path:d,ok:true});return}a(`${g.ok} Credentials valid (wrote ${d})`);}),t.command("delete").description("Delete a destination").argument("<id>","Destination ID").addOption(S()).addOption(f()).action(async(r,s)=>{if(s.yes!==true)throw new l("refusing to delete without --yes");if(await rn(e.api,r),s.json){p({deleted:r});return}a(`${g.ok} Deleted destination ${r}`);}),t};var cn=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,o)=>{let r=await e.docs.get(n);if(o.json){p(r);return}a(r.content);}),t.command("list").description("Fetch the complete documentation index").addOption(f()).action(async n=>{let o=await e.docs.list();if(n.json){p({content:o});return}a(o);}),t};var Zo={4:"Production-ready",3:"Usable, with fixes",2:"Not fit for purpose",1:"Discard"},De=e=>e.type==="image.generate",ae=e=>{let t=vt.safeParse(e??{});return t.success?{...t.data,images:t.data.images??[]}:{images:[]}},Ne=e=>e.status==="success"?e.url??e.mynth_url??void 0:void 0,ie=e=>e.message!==void 0?`${e.code}: ${e.message}`:e.code,E=e=>{if(e.length!==0){a(`${g.ok} Uploaded ${F(e.length,"image")}`);for(let t of e)a(` ${t.path} -> ${t.url}`);}},se=(e,t)=>{if(e.length!==0){a(""),a(`${g.ok} Saved ${F(e.length,"image")} to ${t}`);for(let n of e)a(` ${n}`);}},pn=e=>{let{image:t}=e;a(`${g.ok} Removed background (task ${e.taskId})`),e.cost!==null&&a(` Cost: ${e.cost}`),a(` Size: ${t.size} ${t.format}`),a(""),a(` ${g.ok} ${t.url??t.mynth_url}`),t.destination?.status==="failed"&&a(` ${g.fail} destination ${t.destination.name}: ${ie(t.destination.error)}`);},Qo=e=>{let t=ae(e.result),n=t.images,o=n.filter(r=>r.status==="success").length;if(a(`${g.ok} Generated ${o}/${n.length} ${n.length===1?"image":"images"} (task ${e.id})`),t.model!==void 0&&a(` Model: ${t.model}`),e.cost!==null&&a(` Cost: ${e.cost}`),t.magic_prompt!==void 0&&(a(""),a("Enhanced prompt (mynth):"),a(` ${t.magic_prompt.positive}`),t.magic_prompt.negative&&a(` negative: ${t.magic_prompt.negative}`)),n.length!==0){a("");for(let r of n){if(r.status==="failed"){a(` ${g.fail} ${ie(r.error)}`);continue}let s=r.rating?.status==="success"?` [${r.rating.level}]`:"";a(` ${g.ok} ${Ne(r)??"(no url)"}${s}`),r.destination?.status==="failed"&&a(` ${g.fail} destination ${r.destination.name}: ${ie(r.destination.error)}`);}}},Le=e=>{if(a(`${V(e.status)} Task ${e.id}`),a(` Type: ${e.type}`),a(` Status: ${e.status}`),e.cost!==null&&a(` Cost: ${e.cost}`),a(` Created: ${e.createdAt}`),a(` Updated: ${e.updatedAt}`),e.errors!==null&&e.errors!==void 0&&e.errors.length>0){a(""),a("Errors:");for(let t of e.errors)a(` ${g.fail} ${ie(t)}`);}e.result!==null&&e.result!==void 0&&(a(""),a("Result:"),a(tt(JSON.stringify(e.result,null,2))));},de=e=>{if(De(e)&&e.status==="completed"){Qo(e);return}Le(e);},le=e=>{let t=ae(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}:{}}},mn=e=>{if(a(`${g.ok} Reviewed (task ${e.taskId})`),a(` Score: ${e.score}/4 \u2014 ${Zo[e.score]??"unknown"}`),e.cost!==null&&a(` Cost: ${e.cost}`),a(` ${e.url}`),e.summary.length>0&&(a(""),a("Summary"),a(` ${e.summary}`)),e.findings.length>0){a(""),a(`Findings (${e.findings.length})`);for(let t of e.findings)a(` \u2022 [${t.severity}] ${t.category} \u2014 ${t.finding}`),a(` where: ${t.where} (${t.confidence} confidence)`);}if(e.strengths.length>0){a(""),a(`Strengths (${e.strengths.length})`);for(let t of e.strengths)a(` \u2022 ${t.strength} (${t.confidence} confidence)`);}};var x=(e,t=[])=>[...t,e],Me=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},Fe=e=>t=>{let n=Me(e)(t);if(n<=0)throw new l(`invalid ${e}: "${t}" (expected a positive integer)`);return n},ce=(e,t)=>{let n;try{n=JSON.parse(e);}catch(o){throw new l(`invalid JSON in ${t}: ${o.message}`)}if(n===null||typeof n!="object"||Array.isArray(n))throw new l(`${t} must be a JSON object`);return n},pe=e=>/^https?:\/\//i.test(e);var H=10,j=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 r of await Promise.all(t.map(Xt)))n.append("images",r);let{urls:o}=await e.fetch("upload","/image/upload",gt,{body:n});if(o.length!==t.length)throw new l(`upload returned ${o.length} URLs for ${t.length} files`);return t.map((r,s)=>({path:r,url:o[s]}))},G=(e,t,n)=>e.fetch(`image ${t}`,`/image/${t}`,ft,{body:n}),un=(e,t)=>e.fetch("estimate","/image/generate/estimate",yt,{body:t});var Ue=1800*1e3,er=12e3,tr=2500,nr=5e3,or=500,rr=new Set([0,404,408,429]),ir=e=>e instanceof k&&(rr.has(e.status)||e.status>=500),ar=10,Je=(e,t)=>e.fetch("task fetch",`/tasks/${t}`,pt),fn=(e,t={})=>e.fetch("task list","/tasks",z.array(mt),{query:t}),sr=(e,t)=>e.fetch("task status",`/tasks/${t}/status`,z.object({status:X})).then(n=>n.status),yn=(e,t)=>e.fetch("task result",`/tasks/${t}/result`,ut),D=async(e,t,n=Ue)=>{let o=Date.now(),r=0;for(;;){try{let c=await sr(e,t);if(r=0,c!=="pending")return await Je(e,t)}catch(c){if(!ir(c)||++r>ar)throw c}let s=Date.now()-o;if(s>=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=s<er?tr:nr;await re(d+Math.floor(Math.random()*or));}};var lr=["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"],cr=2800,pr=e=>{let t=e.slice();for(let n=t.length-1;n>0;n--){let o=Math.floor(Math.random()*(n+1));[t[n],t[o]]=[t[o],t[n]];}return t},N=async(e,t={})=>{if(process.stderr.isTTY!==true)return e;let n=pr(t.messages??lr),o=0,r=dr({text:n[0]??"Working",stream:process.stderr}).start(),s=setInterval(()=>{o++,r.text=n[o%n.length]??"Working";},cr);try{return await e}finally{clearInterval(s),r.stop();}};var ue=2,ge=7,L=async(e,t)=>{if(pe(t))return {url:t,uploads:[]};let n=await j(e.api,[t]);return {url:n[0].url,uploads:n}},fe=e=>e.option("--destination <name>","Slug of a configured storage destination to deliver results to. Defaults to MYNTH_DESTINATION.").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"),ye=(e,t)=>{let n=t.destination??e.config.envDestination,o=t.webhookUrl??[],r=t.dashboardWebhooks===false,s=o.length===0&&!r?void 0:{...r?{dashboard:false}:{},...o.length>0?{custom:o.map(d=>({url:d}))}:{}};return {...n!==void 0?{destination:n}:{},...s!==void 0?{webhook:s}:{}}},he=async(e,t)=>{let n=await G(e.api,t.endpoint,{...t.body,access:{pat:{enabled:true}}}),o=n.access?.publicAccessToken;if(t.json){p({taskId:n.taskId,...n.estimatedCost!==void 0?{estimatedCost:n.estimatedCost}:{},...o!==void 0?{access:{publicAccessToken:o}}:{}});return}a(`${g.ok} Task created: ${n.taskId}`),o!==void 0&&a(` Public access token: ${o}`),a(` Await it with: mynth task wait ${n.taskId}`);},mr=z.array(z.object({value:z.string(),description:z.string()})),ur=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},hn=(e,t)=>{let n;try{n=JSON.parse(e);}catch(r){throw new l(`invalid JSON in ${t}: ${r.message}`)}let o=mr.safeParse(n);if(!o.success)throw new l(`invalid levels in ${t}: expected an array of { value, description }`);return o.data},we=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(s=>s!==void 0);if(n.length===0)return;if(n.length>1)throw new l(`conflicting level options: ${n.join(", ")} \u2014 use only one`);let o=t.length>0?t.map(ur):e.levelsFile!==void 0?hn(await W(e.levelsFile),e.levelsFile):hn(e.levelsJson??"[]","--levels-json");if(o.length<ue||o.length>ge)throw new l(`levels must have between ${ue} and ${ge} items (got ${o.length})`);let r=new Set;for(let s of o){if(r.has(s.value))throw new l(`duplicate level value: "${s.value}"`);r.add(s.value);}return o},gr=(e,t)=>{let n=ve(e);return new k(`${t} task ${e.id} failed${n!==void 0?` (${n})`:""}`,{status:0,...n!==void 0?{code:n}:{}})},M=async(e,t)=>{let n=await G(e.api,t.endpoint,t.body),o=D(e.api,n.taskId),r=t.quiet?await o:await N(o);if(r.status!=="completed")throw gr(r,t.endpoint);let s=t.schema.safeParse(r.result);if(!s.success)throw new k(`${t.endpoint} task ${r.id} returned an unexpected result`,{status:0,cause:s.error});return {taskId:r.id,cost:r.cost,result:s.data}};var yr=["low","high"],ze="Image URL (http/https), or a local image file to upload first",hr=e=>e.option("-l, --level <value>",`Custom rating level as "value=description" (repeatable, ${ue}-${ge} 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 }."),wr=e=>{let t=new Command("rate").description("Classify an image against the default sfw/nsfw levels, or custom ones").argument("<image>",ze).addOption(f());return hr(t).action(async(n,o)=>{let r=await we(o),{url:s,uploads:d}=await L(e,n),{taskId:c,cost:h,result:v}=await M(e,{endpoint:"rate",body:r!==void 0?{url:s,mode:"custom",levels:r}:{url:s},schema:ht,quiet:o.json===true});if(o.json){p({taskId:c,cost:h,...v});return}E(d),a(`${g.ok} Rated (task ${c})`),a(` ${v.level} ${s}`);}),t},kr=e=>new Command("alt").description("Generate accessibility alt text for an image").argument("<image>",ze).addOption(f()).action(async(t,n)=>{let{url:o,uploads:r}=await L(e,t),{taskId:s,cost:d,result:c}=await M(e,{endpoint:"alt",body:{url:o},schema:wt,quiet:n.json===true});if(n.json){p({taskId:s,cost:d,...c});return}E(r),a(`${g.ok} Generated alt text (task ${s})`),a(` ${c.alt}`),a(` ${o}`);}),br=e=>new Command("review").description("Review image quality with a multi-model panel (score, findings, strengths)").argument("<image>",ze).addOption(new Option("--effort <level>",'"high" (default) runs five strong vision models; "low" runs three smaller ones for faster, cheaper triage').choices([...yr])).addOption(f()).action(async(t,n)=>{let{url:o,uploads:r}=await L(e,t),{taskId:s,cost:d,result:c}=await M(e,{endpoint:"review",body:{url:o,...n.effort!==void 0?{effort:n.effort}:{}},schema:kt,quiet:n.json===true});if(n.json){p({taskId:s,cost:d,...c});return}E(r),mn({taskId:s,cost:d,url:o,...c,findings:c.findings??[],strengths:c.strengths??[]});}),wn=e=>[wr(e),kr(e),br(e)];var xr=4,Tr=(e,t,n)=>{try{let o=new URL(e).pathname.split("/").filter(Boolean).pop();if(o!==void 0&&o.length>0)return decodeURIComponent(o)}catch{}return `${t}-${n}`},ke=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 Kt(e.urls,xr,async(n,o)=>{let r;try{r=await fetch(n);}catch(d){throw new y(`download failed for ${n}: ${d.message}`,{cause:d})}if(!r.ok)throw new y(`download failed for ${n} (${r.status})`);let s=join(t,Tr(n,e.fallbackPrefix,o));try{await writeFile(s,new Uint8Array(await r.arrayBuffer()));}catch(d){throw new y(`could not write ${s}: ${d.message}`,{cause:d})}return s})};var We=20,Pr=["png","jpg","webp"],Be=["auto","source","reference"],Or="https://dry-run.mynth.io/input",_r=e=>{let t=e.indexOf(":"),n,o=e;if(t>0&&!/^https?:/i.test(e)){let r=e.slice(0,t);if(!Be.includes(r))throw new l(`invalid --input role "${r}". Expected one of: ${Be.join(", ")}`);n=r,o=e.slice(t+1);}if(o.length===0)throw new l(`invalid --input "${e}": missing path or URL`);return {...n!==void 0?{role:n}:{},value:o,isLocalFile:!pe(o)}},Sr=async(e,t,n,o)=>{let r=await we(t),s=r!==void 0?{mode:"custom",levels:r}:t.contentRating===true?true:void 0,d=n.map(c=>({type:"image",...c.role!==void 0?{as:c.role}:{},source:{type:"url",url:c.isLocalFile?o.get(c.value)??Or:c.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}}:{},...d.length>0?{inputs:d}:{},...ye(e,t),...s!==void 0?{rating:s}:{},...t.metadata!==void 0?{metadata:ce(t.metadata,"--metadata")}:{}}},jr=(e,t)=>{let n=ae(e.result).images.map(Ne).filter(o=>o!==void 0);return n.length===0?Promise.resolve([]):ke({urls:n,directory:t,fallbackPrefix:e.id})},kn=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)",Me("--count")).addOption(new Option("-f, --format <format>","Output image format").choices([...Pr])).option("-i, --input <value>",`Input image as "[role:]path-or-url" (repeatable, up to ${We}). Roles: ${Be.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("--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");return fe(t).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()),t.action(async n=>{let o=n.input??[];if(o.length>We)throw new l(`too many --input values: ${o.length} (max ${We})`);let r=o.map(_r),s=[...new Set(r.filter(A=>A.isLocalFile).map(A=>A.value))],d=s.length>0&&n.dryRun!==true?await j(e.api,s):[],c=await Sr(e,n,r,new Map(d.map(A=>[A.path,A.url])));if(n.dryRun===true){let A=await un(e.api,c);if(n.json){p(A);return}let Yn=A.estimateKind==="upper_bound"?" (upper bound)":"";a(`${g.ok} Estimated cost: $${A.estimatedCost}${Yn}`);return}if(n.async===true){await he(e,{endpoint:"generate",body:c,json:n.json===true});return}let h=await G(e.api,"generate",c),v=D(e.api,h.taskId),w=n.json?await v:await N(v),I=n.outputDir!==void 0?resolve(n.outputDir):void 0,P=I!==void 0?await jr(w,I):[];if(n.json){let A=n.detailed===true?w:le(w);p(I!==void 0?{...A,downloadedFiles:P}:A);return}E(d),de(w),I!==void 0&&se(P,I);}),t};var Mr=["png","webp"],bn=e=>{let t=new Command("remove-background").description("Remove the background from an image. Mynth picks the model.").argument("<image>","Image URL (http/https), or a local image file to upload first").addOption(new Option("-f, --format <format>","Output format. Default: whatever the provider returns").choices([...Mr])).option("-o, --output-dir <dir>","Directory to save the result into. Created if missing. Ignored with --async.").option("--metadata <json>","Inline JSON object attached to the task (max 2KB)");return fe(t).option("--async","Print the task ID immediately instead of waiting for the result").addOption(f()),t.action(async(n,o)=>{let{url:r,uploads:s}=await L(e,n),d={url:r,...o.format!==void 0?{output:{format:o.format}}:{},...ye(e,o),...o.metadata!==void 0?{metadata:ce(o.metadata,"--metadata")}:{}};if(o.async===true){await he(e,{endpoint:"remove-background",body:d,json:o.json===true});return}let{taskId:c,cost:h,result:v}=await M(e,{endpoint:"remove-background",body:d,schema:At,quiet:o.json===true}),w=o.outputDir!==void 0?resolve(o.outputDir):void 0,I=w!==void 0?await ke({urls:[v.image.url??v.image.mynth_url],directory:w,fallbackPrefix:c}):[];if(o.json){p({taskId:c,cost:h,...v,...w!==void 0?{downloadedFiles:I}:{}});return}E(s),pn({taskId:c,cost:h,...v}),w!==void 0&&se(I,w);}),t};var vn=e=>new Command("upload").description("Upload local images to Mynth's temporary input storage").argument("<files...>",`Local image files (${Ce.join(", ")})`).addOption(f()).action(async(t,n)=>{if(t.length>H)throw new l(`too many files: ${t.length} (max ${H})`);let o=await j(e.api,t);if(n.json){p({images:o});return}a(`${g.ok} Uploaded ${F(o.length,"image")}`);for(let{path:r,url:s}of o)a(` ${r}`),a(` -> ${s}`);});var An=e=>{let t=new Command("image").description("Generate, upload, analyze, and remove backgrounds from images");t.addCommand(kn(e)),t.addCommand(vn(e)),t.addCommand(bn(e));for(let n of wn(e))t.addCommand(n);return t};var $n=e=>e.fetch("models list","/models",z.array(ct),{auth:false});var Rn={txt2img:"txt->img",img2img:"img->img",txt2vid:"txt->vid",img2vid:"img->vid"},Cn=e=>e.id.split("/")[0],Ye=e=>"perImage"in e,En=e=>e.type==="video"?"/s":"",Wr=e=>{let t=e.pricing;return t===null?[]:Ye(t)?[t.perImage.base]:Object.values(t.perSecond)},Pn=e=>{let t;for(let n of Wr(e)){let o=Number.parseFloat(n);Number.isFinite(o)&&(t===void 0||o<t.value)&&(t={raw:n,value:o});}return t},In=e=>Pn(e)?.value,Br=e=>{let t=Pn(e);return t===void 0?"-":`${t.raw}${En(e)}`},On=e=>{let t=e.pricing;if(t!==null)return Ye(t)?t.perImage["4k"]:t.perSecond["4k"]},Yr=e=>{let t=On(e);return t===void 0?"-":`${t}${En(e)}`},Hr=e=>{let t=e.pricing;if(!(t===null||!Ye(t)))return t.perInput},xn=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},_n=.3,Gr=(e,t)=>Tn.go(t,e,{keys:["id","displayName"],threshold:_n}).map(n=>n.obj),qr=(e,t)=>{let n=[...new Set(e.map(Cn))];return n.includes(t)?new Set([t]):new Set(Tn.go(t,n,{threshold:_n}).map(o=>o.target))},Vr=(e,t)=>{let n=e;if(t.org!==void 0){let o=qr(n,t.org);n=n.filter(r=>o.has(Cn(r)));}if(t.type!==void 0){let o=t.type;n=n.filter(r=>r.type===o);}if(t["4k"]&&(n=n.filter(o=>On(o)!==void 0)),t.capability!==void 0){let o=Rn[t.capability];n=n.filter(r=>o in r.modes);}if(t.maxPrice!==void 0){let o=t.maxPrice;n=n.filter(r=>(In(r)??1/0)<=o);}if(t.minPrice!==void 0){let o=t.minPrice;n=n.filter(r=>(In(r)??-1/0)>=o);}return t.search!==void 0&&(n=Gr(n,t.search)),n},Sn=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)",xn("--max-price")).option("--min-price <usd>","Only models at or above this price (per image, or per second for video)",xn("--min-price")).option("--4k","Only models with 4K pricing").addOption(new Option("--capability <capability>","Only models serving this generation mode").choices(Object.keys(Rn))).addOption(f()).action(async n=>{let o=Vr(await $n(e.api),n);if(n.json){p(o);return}C(o,[{header:"ID",value:r=>r.id},{header:"Name",value:r=>r.displayName??"-"},{header:"Type",value:r=>r.type},{header:"Modes",value:r=>Object.keys(r.modes).join(",")||"-"},{header:"Price",value:r=>Br(r)},{header:"4K",value:r=>Yr(r)},{header:"Input fee",value:r=>Hr(r)??"-"}],"No models matched the filters.");}),t};var jn=Ue/1e3,Zr=e=>e.option("--detailed","Include the full task record instead of a compact summary"),Dn=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(o,r)=>{let s=await Je(e.api,o);if(r.json){p(s);return}Le(s);}),t.command("result").description("Print a task's result payload as JSON").argument("<id>","Task ID").action(async o=>{p((await yn(e.api,o)).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: ${jn})`,Fe("--timeout")).addOption(f());return Zr(n).action(async(o,r)=>{let s=D(e.api,o,(r.timeout??jn)*1e3),d=r.json?await s:await N(s);if(d.status==="failed"&&(process.exitCode=et(d)),r.json){let c=!r.detailed&&De(d);p(c?le(d):d);return}de(d);}),t.command("list").description("List recent tasks, newest first").option("--limit <number>","Max tasks to return (1-100, default: 20)",Fe("--limit")).option("--after <id>","Cursor: return tasks created before this task ID").addOption(f()).action(async o=>{let r=await fn(e.api,{...o.limit!==void 0?{limit:o.limit}:{},...o.after!==void 0?{after:o.after}:{}});if(o.json){p({tasks:r});return}C(r,[{header:"",value:s=>V(s.status)},{header:"ID",value:s=>s.id},{header:"Type",value:s=>s.type},{header:"Status",value:s=>s.status},{header:"Cost",value:s=>s.cost??"-"},{header:"Created",value:s=>s.createdAt}],"No tasks found.");}),t};var Nn=(e,t)=>e.fetch("webhook create","/webhook",It,{body:t}),Ln=(e,t,n)=>e.fetch("webhook update",`/webhook/${t}`,xt,{method:"PUT",body:n}),Mn=(e,t)=>e.call("webhook delete",`/webhook/${t}`,{method:"DELETE"});var He=["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.image.remove_background.completed","task.image.remove_background.failed","task.video.generate.completed","task.video.generate.failed"],ei=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(!He.includes(t))throw new l(`unknown event "${t}". Valid events: all, ${He.join(", ")}`);return e},ti=e=>{if(e.enabled===true&&e.disabled===true)throw new l("--enabled and --disabled are mutually exclusive");return e.disabled!==true},Fn=e=>({enabled:ti(e),url:e.url,events:ei(e.event),...e.apiKeyId!==void 0?{apiKeyIds:e.apiKeyId}:{},...e.oauthEvents===true?{oauthEnabled:true}:{}}),Un=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: ${He.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()),Kn=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 r=>{let s=await Nn(e.api,Fn(r));if(r.json){p(s);return}a(`${g.ok} Webhook ${s.id} created`),a(` URL: ${s.url}`),a(` Enabled: ${s.enabled}`),a(` Events: ${Un(s.events)}`),s.oauthEnabled!==void 0&&a(` OAuth: ${s.oauthEnabled}`),a(""),a(` Signing secret: ${s.secret}`),a(" Save this now \u2014 it is shown only once and cannot be retrieved again.");});let o=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(o).action(async(r,s)=>{let d=await Ln(e.api,r,Fn(s));if(s.json){p(d);return}a(`${g.ok} Webhook ${d.id} updated`),a(` URL: ${d.url}`),d.enabled!==void 0&&a(` Enabled: ${d.enabled}`),a(` Events: ${Un(d.events)}`),d.oauthEnabled!==void 0&&a(` OAuth: ${d.oauthEnabled}`);}),t.command("delete").description("Delete a webhook").argument("<id>","Webhook ID").addOption(S()).addOption(f()).action(async(r,s)=>{if(s.yes!==true)throw new l("refusing to delete without --yes");if(await Mn(e.api,r),s.json){p({deleted:r});return}a(`${g.ok} Deleted webhook ${r}`);}),t};var ri=100,ii=`
|
|
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)`,
|
|
23
|
-
`:"").addHelpText("after",
|
|
22
|
+
XDG_CONFIG_HOME where credentials are stored (default: ~/.config)`,Ge=class extends Help{optionTerm(t){return `(${t.flags.replaceAll("<","").replaceAll(">","")})`}subcommandTerm(t){let n=t.registeredArguments.map(o=>o.required?`${o.name()}${o.variadic?"...":""}`:`[${o.name()}]`).join(" ");return n.length>0?`${t.name()} ${n}`:t.name()}},zn=e=>{e.configureHelp({helpWidth:ri}),e.createHelp=()=>new Ge,e.exitOverride();for(let t of e.commands)zn(t);},Wn=()=>{let e=Rt(),t=new Command("mynth").description("Official Mynth CLI").version(Ze).addHelpText("before",({error:n})=>(n?process.stderr.isTTY:process.stdout.isTTY)?`${Ct()}
|
|
23
|
+
`:"").addHelpText("after",ii);for(let n of [jt(e),Yt(e),Ht(e),Zt(e),ln(e),cn(e),An(e),Sn(e),Dn(e),Kn(e),Re(e)])t.addCommand(n);return zn(t),t};var Bn=e=>e instanceof Error?e.message:String(e);try{await Wn().parseAsync(process.argv);}catch(e){let t=e?.code;t==="commander.helpDisplayed"||t==="commander.version"||((typeof t!="string"||!t.startsWith("commander."))&&(T(Bn(e)),q().debug&&e instanceof Error&&(T("=== MYNTH_DEBUG ==="),T(e.stack??e.message),e.cause!==void 0&&T(`cause: ${Bn(e.cause)}`))),process.exitCode=Qe(e));}
|