@mynthio/cli 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,7 +21,8 @@ npx @mynthio/cli --help
21
21
  ## Usage
22
22
 
23
23
  ```bash
24
- mynth image generate # not implemented yet
24
+ mynth image generate --prompt "A cinematic product photo of a glass keyboard"
25
+ mynth image generate -p "A watercolor city skyline" --size 16:9 --count 2
25
26
  ```
26
27
 
27
28
  Run `mynth --help` for the full command list.
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import*as nn from'@effect/platform-node/NodeContext';import*as on from'@effect/platform-node/NodeRuntime';import*as g from'effect/Effect';import*as re from'effect/Layer';import*as Ge from'effect/Logger';import*as sn from'effect/LogLevel';import*as J from'@effect/cli/Command';import*as O from'effect/Console';import*as x from'effect/Match';import*as V from'effect/Data';import*as A from'effect/Option';import*as Dt from'effect/Redacted';import*as ee from'effect/Config';import*as st from'@effect/platform/FileSystem';import*as it from'@effect/platform/Path';import*as ne from'cross-keychain';import*as c from'effect/Schema';import*as Oe from'@effect/platform/HttpBody';import*as Ie from'@effect/platform/HttpClient';import*as M from'@effect/platform/HttpClientRequest';import*as Ce from'@effect/platform/HttpClientResponse';import*as j from'@effect/cli/Args';import*as i from'@effect/cli/Options';import*as Pt from'effect/Duration';import*as pe from'effect/Ref';import*as Wt from'effect/Fiber';import*as en from'@effect/platform/FetchHttpClient';var F=class extends V.TaggedError("MynthCliError"){},ve=class extends V.TaggedError("NotAuthenticatedError"){},ue=class extends V.TaggedError("CredentialsStoreError"){},B=class extends V.TaggedError("WorkOSError"){},ke=class extends V.TaggedError("AuthorizationPendingError"){},be=class extends V.TaggedError("AuthorizationExpiredError"){},Te=class extends V.TaggedError("AuthorizationDeniedError"){},d=class extends V.TaggedError("MynthApiError"){};var me=g.all({mynthApiUrl:ee.string("MYNTH_API_URL").pipe(ee.withDefault("https://api.mynth.io")),apiKeyEnvOverride:ee.redacted("MYNTH_API_KEY").pipe(ee.option)});var Et=c.Struct({id:c.String,email:c.String,first_name:c.NullOr(c.String).pipe(c.optional),last_name:c.NullOr(c.String).pipe(c.optional)}),St=c.Struct({device_code:c.String,user_code:c.String,verification_uri:c.String,verification_uri_complete:c.String.pipe(c.optional),expires_in:c.Number,interval:c.Number.pipe(c.optional)}),vt=c.Struct({access_token:c.String,refresh_token:c.String,user:Et.pipe(c.optional),organization_id:c.String.pipe(c.optional)}),kt=c.Struct({error:c.String.pipe(c.optional),error_description:c.String.pipe(c.optional),message:c.String.pipe(c.optional),code:c.String.pipe(c.optional)}),an=c.Struct({kind:c.Literal("oauth"),access_token:c.String,refresh_token:c.String,expires_at:c.Number,user:Et.pipe(c.optional)}),ln=c.Struct({kind:c.Literal("api_key"),api_key:c.String}),Ot=c.Union(an,ln);var Xe="mynth-cli",Qe="default",cn="credentials.json",ge=e=>g.mapError(t=>new ue({message:e,cause:t})),De=(e,t)=>g.tryPromise({try:e,catch:n=>n}).pipe(g.map(A.some),g.catchAll(n=>{let o=n?.name;return o==="NoKeyringError"||o==="InitError"?g.succeed(A.none()):new ue({message:t,cause:n})})),Ct=e=>g.try({try:()=>JSON.parse(e),catch:t=>new ue({message:"credentials JSON parse failed",cause:t})}).pipe(g.flatMap(c.decodeUnknown(Ot)),ge("credentials shape invalid")),wt=e=>JSON.stringify(e),pn=e=>{let t=process.env.XDG_CONFIG_HOME,n=t&&t.length>0?t:e.join(process.env.HOME??".",".config");return e.join(n,"mynth")},te=class extends g.Service()("CredentialsStore",{effect:g.gen(function*(){let t=yield*st.FileSystem,n=yield*it.Path,o=pn(n),s=n.join(o,cn),p=g.gen(function*(){if(!(yield*t.exists(s).pipe(ge("stat credentials file failed"))))return A.none();let m=yield*t.readFileString(s).pipe(ge("read credentials file failed"));return A.some(yield*Ct(m))}),u=g.fn("CredentialsStore.writeFile")(function*(r){yield*t.makeDirectory(o,{recursive:true}).pipe(ge("create config dir failed")),yield*t.writeFileString(s,wt(r)).pipe(ge("write credentials file failed")),yield*t.chmod(s,384).pipe(g.ignore);}),v=g.gen(function*(){(yield*t.exists(s).pipe(g.orElseSucceed(()=>false)))&&(yield*t.remove(s).pipe(ge("delete credentials file failed")));}),f=g.gen(function*(){let r=yield*De(()=>ne.getPassword(Xe,Qe),"keychain get failed");if(A.isNone(r))return yield*p;let m=r.value;return m===null?A.none():A.some(yield*Ct(m))}),y=g.fn("CredentialsStore.set")(function*(r){let m=yield*De(()=>ne.setPassword(Xe,Qe,wt(r)),"keychain set failed");if(A.isNone(m)){yield*u(r);return}yield*v.pipe(g.ignore);}),_=g.gen(function*(){yield*De(()=>ne.deletePassword(Xe,Qe),"keychain delete failed").pipe(g.ignore),yield*v.pipe(g.ignore);}),l=De(()=>ne.getKeyring(),"keychain probe failed").pipe(g.map(r=>A.isSome(r)&&r.value!==null));return {get:f,set:y,clear:_,usingKeychain:l,filePath:s}})}){};var Me="client_01KATK792RR5ZCHMF5YMNN1ZSE",Rt="https://api.workos.com";var dn="urn:ietf:params:oauth:grant-type:device_code",fn="refresh_token",un=e=>g.try({try:()=>{let t=e.split(".");if(t.length<2)throw new Error("malformed jwt");let n=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));if(typeof n.exp!="number")throw new Error("jwt missing exp claim");return n.exp*1e3},catch:t=>new B({message:"could not decode access token",cause:t})}),mn=e=>Oe.urlParams(Object.entries(e)),bt=e=>Ce.schemaBodyJson(kt)(e).pipe(g.orElseSucceed(()=>({}))),$t=(e,t)=>bt(e).pipe(g.flatMap(n=>{let o=n.error??n.code;return new B({message:n.error_description??n.message??t,status:e.status,...o!==void 0?{code:o}:{}})})),Tt=(e,t)=>g.gen(function*(){let n=yield*Ce.schemaBodyJson(vt)(e).pipe(g.mapError(s=>new B({message:`invalid ${t} response`,status:e.status,cause:s}))),o=yield*un(n.access_token);return {token:n,expiresAt:o}}),gn=g.fn("WorkOS.parseAuthenticate")(function*(e){if(e.status===200)return yield*Tt(e,"token");let t=yield*bt(e),n=t.error??t.code;return yield*x.value(n).pipe(x.when("authorization_pending",()=>new ke({slowDown:false})),x.when("slow_down",()=>new ke({slowDown:true})),x.when("expired_token",()=>new be),x.when("access_denied",()=>new Te),x.orElse(()=>new B({message:t.error_description??t.message??"WorkOS error",status:e.status,...n!==void 0?{code:n}:{}})))}),oe=class extends g.Service()("WorkOS",{effect:g.gen(function*(){let n=(yield*Ie.HttpClient).pipe(Ie.mapRequest(M.prependUrl(Rt))),o=(f,y)=>M.post(f).pipe(M.setBody(y),M.setHeader("Accept","application/json")),s=(f,y)=>n.execute(o("/user_management/authenticate",f)).pipe(g.mapError(_=>new B({message:`${y} request failed`,cause:_}))),p=g.fn("WorkOS.deviceAuthorize")(function*(){let f=yield*n.execute(o("/user_management/authorize/device",mn({client_id:Me}))).pipe(g.mapError(y=>new B({message:"device authorize request failed",cause:y})));return f.status!==200?yield*$t(f,"device authorize failed"):yield*Ce.schemaBodyJson(St)(f).pipe(g.mapError(y=>new B({message:"invalid device authorize response",cause:y})))}),u=g.fn("WorkOS.exchangeDeviceCode")(function*(f){let y=yield*s(Oe.unsafeJson({grant_type:dn,client_id:Me,device_code:f}),"authenticate");return yield*gn(y)}),v=g.fn("WorkOS.refresh")(function*(f){let y=yield*s(Oe.unsafeJson({grant_type:fn,client_id:Me,refresh_token:f}),"refresh");return y.status===200?yield*Tt(y,"refresh"):yield*$t(y,"refresh failed")});return {requestDeviceAuthorization:p,exchangeDeviceCode:u,refresh:v}})}){};var yn=6e4,Ze=e=>g.mapError(t=>new ve({reason:`${e}: ${t.message}`})),Le=e=>e?{user:e}:{},L=class extends g.Service()("Auth",{effect:g.gen(function*(){let t=yield*me,n=yield*te,o=yield*oe,s=A.map(t.apiKeyEnvOverride,Dt.value),p=A.isSome(s),u=r=>g.gen(function*(){if(r.expires_at-Date.now()>yn)return r;let m=yield*o.refresh(r.refresh_token).pipe(Ze("token refresh failed")),a={kind:"oauth",access_token:m.token.access_token,refresh_token:m.token.refresh_token,expires_at:m.expiresAt,...Le(m.token.user??r.user)};return yield*n.set(a).pipe(Ze("could not persist refreshed token")),a}),v=g.gen(function*(){if(A.isSome(s))return {kind:"api_key",apiKey:s.value,source:"env"};let r=yield*n.get.pipe(Ze("could not read credentials"));if(A.isNone(r))return yield*new ve({reason:"no credentials configured"});let m=r.value;if(m.kind==="api_key")return {kind:"api_key",apiKey:m.api_key,source:"stored"};let a=yield*u(m);return {kind:"oauth",accessToken:a.access_token,...Le(a.user)}}),f=g.gen(function*(){if(A.isSome(s))return {kind:"env",source:"MYNTH_API_KEY"};let r=yield*n.get.pipe(g.orElseSucceed(()=>A.none()));if(A.isNone(r))return {kind:"none"};let m=r.value;return m.kind==="api_key"?{kind:"api_key"}:{kind:"oauth",expiresAt:m.expires_at,...Le(m.user)}}),y=r=>n.set({kind:"api_key",api_key:r}),_=r=>n.set({kind:"oauth",access_token:r.accessToken,refresh_token:r.refreshToken,expires_at:r.expiresAt,...Le(r.user)}),l=n.clear;return {resolve:v,status:f,setApiKey:y,saveOAuth:_,logout:l,envApiKeySet:p}})}){};var hn=e=>new Date(e).toISOString(),Mt=e=>g.mapError(t=>new F({message:`${e}: ${t.message}`,cause:t})),En=g.fn("auth.pollForToken")(function*(e,t,n,o){let s=n;for(;;){if(Date.now()>=o)return yield*new F({message:"device code expired before approval"});let p=yield*g.either(e.exchangeDeviceCode(t));if(p._tag==="Right")return p.right;let u=p.left,v=x.value(u).pipe(x.tag("AuthorizationPendingError",f=>({kind:"retry",slowDown:f.slowDown})),x.tag("AuthorizationDeniedError",()=>({kind:"fail",message:"login denied by user"})),x.tag("AuthorizationExpiredError",()=>({kind:"fail",message:"device code expired"})),x.tag("WorkOSError",f=>({kind:"fail",message:f.message,cause:f})),x.exhaustive);if(v.kind==="fail")return yield*new F({message:v.message,..."cause"in v?{cause:v.cause}:{}});v.slowDown&&(s+=5e3),yield*g.sleep(`${s} millis`);}}),Sn=J.make("login",{},()=>g.gen(function*(){let e=yield*L,t=yield*oe;if(e.envApiKeySet)return yield*O.log(`MYNTH_API_KEY is set in your environment; that takes precedence over login.
3
- Unset it to use OAuth, or just continue using the env API key.`),yield*new F({message:"env api key takes precedence"});let n=yield*t.requestDeviceAuthorization().pipe(Mt("device authorize"));yield*O.log(""),yield*O.log(` First copy your one-time code: ${n.user_code}`),yield*O.log(` Then open: ${n.verification_uri_complete??n.verification_uri}`),yield*O.log(""),yield*O.log("Waiting for confirmation...");let o=yield*En(t,n.device_code,(n.interval??5)*1e3,Date.now()+n.expires_in*1e3);yield*e.saveOAuth({accessToken:o.token.access_token,refreshToken:o.token.refresh_token,expiresAt:o.expiresAt,...o.token.user?{user:o.token.user}:{}}).pipe(Mt("could not save credentials"));let s=o.token.user?.email??o.token.user?.id??"unknown user";yield*O.log(`\u2713 Logged in as ${s}`);})),vn=J.make("logout",{},()=>g.gen(function*(){let e=yield*L;yield*e.logout,yield*O.log("\u2713 Local credentials cleared"),e.envApiKeySet&&(yield*O.log("Note: MYNTH_API_KEY is still set in your environment and will be used."));})),kn=J.make("status",{},()=>g.gen(function*(){let e=yield*L,t=yield*te,n=yield*e.status,s=(yield*t.usingKeychain)?"system keychain":`file (${t.filePath})`,p=x.value(n).pipe(x.when({kind:"env"},()=>["Authenticated via env: MYNTH_API_KEY"]),x.when({kind:"none"},()=>["Not authenticated. Run `mynth auth login` or set an API key."]),x.when({kind:"api_key"},()=>[`Authenticated via stored API key (${s})`]),x.when({kind:"oauth"},u=>[`Authenticated via OAuth as ${u.user?.email??u.user?.id??"unknown user"} (${s})`,` access token expires: ${hn(u.expiresAt)}`]),x.exhaustive);yield*g.forEach(p,O.log);})),It=J.make("whoami",{},()=>g.gen(function*(){let t=yield*(yield*L).status;return yield*x.value(t).pipe(x.when({kind:"none"},()=>O.log("not authenticated").pipe(g.zipRight(new F({message:"not authenticated"})))),x.when({kind:"env"},()=>O.log("env:MYNTH_API_KEY")),x.when({kind:"api_key"},()=>O.log("api-key")),x.when({kind:"oauth"},n=>O.log(n.user?.email??n.user?.id??"oauth")),x.exhaustive)})),et=J.make("auth").pipe(J.withSubcommands([Sn,vn,kn,It])),tt=It;var On=g.tryPromise({try:async()=>{let e="";process.stdin.setEncoding("utf8");for await(let t of process.stdin)e+=t;return e.trim()},catch:e=>new F({message:"could not read stdin",cause:e})}),Cn=j.text({name:"value"}).pipe(j.withDescription("API key value, or `-` to read from stdin")),wn=J.make("api-key",{value:Cn},({value:e})=>g.gen(function*(){let t=yield*L,n=e==="-"?yield*On:e;if(n.length===0)return yield*new F({message:"API key is empty"});yield*t.setApiKey(n).pipe(g.mapError(o=>new F({message:`could not save API key: ${o.message}`,cause:o}))),yield*O.log("\u2713 API key saved"),t.envApiKeySet&&(yield*O.log("Note: MYNTH_API_KEY is also set in your environment and will take precedence."));})),An=J.make("set").pipe(J.withSubcommands([wn])),_n=J.make("api-key",{},()=>g.gen(function*(){yield*(yield*L).logout,yield*O.log("\u2713 Stored credentials cleared");})),xn=J.make("unset").pipe(J.withSubcommands([_n])),nt=J.make("config").pipe(J.withSubcommands([An,xn]));var Rn=e=>e.kind==="api_key"?e.apiKey:e.accessToken,Y=class extends g.Service()("MynthApi",{effect:g.gen(function*(){let t=yield*me,n=yield*L,o=yield*Ie.HttpClient,s=yield*pe.make(A.none()),p=f=>g.gen(function*(){f&&(yield*pe.set(s,A.none()));let y=yield*pe.get(s);if(A.isSome(y))return y.value;let _=yield*n.resolve;return yield*pe.set(s,A.some(_)),_}),u=g.fn("MynthApi.attempt")(function*(f,y){let _=yield*p(y),l=f.pipe(M.prependUrl(t.mynthApiUrl),M.setHeader("Authorization",`Bearer ${Rn(_)}`));yield*g.logDebug("MynthApi request",{method:l.method,url:l.url,authKind:_.kind,forceRefresh:y});let r=yield*o.execute(l).pipe(g.tapErrorCause(m=>g.logDebug("MynthApi transport error",m)),g.mapError(m=>new d({message:`request failed: ${m.message}`,status:0,cause:m})));return yield*g.logDebug("MynthApi response",{status:r.status}),r});return {execute:f=>g.gen(function*(){let y=yield*u(f,false);return y.status!==401?y:yield*u(f,true)}),baseUrl:t.mynthApiUrl}})}){};var Ue=10,je=10,we=2,Ae=7,Lt=300*1e3,$n=12e3,bn=2500,Tn=5e3,Dn=(e,t,n)=>{try{let s=new URL(e).pathname.split("/").filter(Boolean).pop();if(s&&s.length>0)return decodeURIComponent(s)}catch{}return `${t}-${n}`},Mn={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".webp":"image/webp"},In=g.fn("ImageService.readImageFile")(function*(e){let t=yield*st.FileSystem,n=yield*it.Path,o=n.extname(e).toLowerCase(),s=Mn[o];if(!s)return yield*new d({message:`unsupported image extension "${o}" for ${e} (allowed: .jpg, .jpeg, .png, .webp)`,status:0});let p=yield*t.readFile(e).pipe(g.mapError(f=>new d({message:`could not read ${e}: ${f.message}`,status:0}))),u=n.basename(e),v=new ArrayBuffer(p.byteLength);return new Uint8Array(v).set(p),new File([v],u,{type:s})}),Q=class extends g.Service()("ImageService",{effect:g.gen(function*(){let t=yield*Y,n=yield*Ie.HttpClient,o=yield*me,s=g.fn("ImageService.upload")(function*(l){if(l.length===0)return yield*new d({message:"no files to upload",status:0});if(l.length>Ue)return yield*new d({message:`too many files: ${l.length} (max ${Ue})`,status:0});yield*g.logDebug("ImageService.upload starting",{count:l.length,filePaths:l});let r=yield*g.forEach(l,In);yield*g.logDebug("ImageService.upload read files",r.map(S=>({name:S.name,size:S.size,type:S.type})));let m=new FormData;for(let S of r)m.append("images",S);let a=yield*t.execute(M.post("/image/upload").pipe(M.bodyFormData(m)));if(yield*g.logDebug("ImageService.upload response",{status:a.status}),a.status<200||a.status>=300){let S=yield*a.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`upload failed (${a.status}): ${S||"no body"}`,status:a.status})}let h=yield*Ce.schemaBodyJson(Hn)(a).pipe(g.mapError(S=>new d({message:`invalid upload response: ${S.message}`,status:a.status})));return l.map((S,H)=>({path:S,url:h.urls[H]}))}),p=g.fn("ImageService.rate")(function*(l){if(l.urls.length===0)return yield*new d({message:"no image URLs to rate",status:0});if(l.urls.length>je)return yield*new d({message:`too many images: ${l.urls.length} (max ${je})`,status:0});if(l.levels!==void 0&&(l.levels.length<we||l.levels.length>Ae))return yield*new d({message:`levels must have between ${we} and ${Ae} items (got ${l.levels.length})`,status:0});yield*g.logDebug("ImageService.rate starting",{count:l.urls.length,hasLevels:l.levels!==void 0});let r={urls:l.urls};l.levels!==void 0&&(r.levels=l.levels);let m=yield*Oe.json(r).pipe(g.mapError(S=>new d({message:`could not encode rate body: ${S.reason}`,status:0,cause:S}))),a=yield*t.execute(M.post("/image/rate").pipe(M.setBody(m)));if(yield*g.logDebug("ImageService.rate response",{status:a.status}),a.status<200||a.status>=300){let S=yield*a.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`rate failed (${a.status}): ${S||"no body"}`,status:a.status})}return yield*Ce.schemaBodyJson(Nn)(a).pipe(g.mapError(S=>new d({message:`invalid rate response: ${S.message}`,status:a.status})))}),u=g.fn("ImageService.generate")(function*(l){let r=l.requestPat?{...l.request,access:{...l.request.access,pat:{enabled:true}}}:l.request,m=yield*Oe.json(r).pipe(g.mapError(S=>new d({message:`could not encode generate body: ${S.reason}`,status:0,cause:S}))),a=yield*t.execute(M.post("/image/generate").pipe(M.setBody(m)));if(a.status<200||a.status>=300){let S=yield*a.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`generate failed (${a.status}): ${S||"no body"}`,status:a.status})}let h=yield*Ce.schemaBodyJson(Pn)(a).pipe(g.mapError(S=>new d({message:`invalid generate response: ${S.message}`,status:a.status})));return {taskId:h.taskId,pat:A.fromNullable(h.access?.publicAccessToken)}}),v=g.fn("ImageService.getTaskStatus")(function*(l,r){let m=`/tasks/${l}/status`,a=yield*A.match(r,{onSome:S=>n.execute(M.get(`${o.mynthApiUrl}${m}`).pipe(M.setHeader("Authorization",`Bearer ${S}`))).pipe(g.mapError(H=>new d({message:`task status request failed: ${H.message}`,status:0,cause:H}))),onNone:()=>t.execute(M.get(m))});if(a.status<200||a.status>=300){let S=yield*a.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`task status failed (${a.status}): ${S||"no body"}`,status:a.status})}return (yield*Ce.schemaBodyJson(Fn)(a).pipe(g.mapError(S=>new d({message:`invalid task status response: ${S.message}`,status:a.status})))).status}),f=g.fn("ImageService.getTaskDetails")(function*(l){let r=yield*t.execute(M.get(`/tasks/${l}`));if(r.status<200||r.status>=300){let a=yield*r.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`task details failed (${r.status}): ${a||"no body"}`,status:r.status})}return yield*r.json.pipe(g.mapError(a=>new d({message:`invalid task details response: ${a.message}`,status:r.status})))}),y=g.fn("ImageService.waitForTask")(function*(l,r){let m=Date.now();for(;;){let a=Date.now()-m;if(a>=Lt)return yield*new d({message:`task ${l} polling timed out after ${Lt}ms`,status:0});let h=yield*v(l,r);if(h==="completed")return yield*f(l);if(h==="failed")return yield*new d({message:`task ${l} failed during generation`,status:0});let S=a<$n?bn:Tn;yield*g.sleep(Pt.millis(S+Math.floor(Math.random()*500)));}}),_=g.fn("ImageService.downloadImages")(function*(l){let r=yield*st.FileSystem,m=yield*it.Path,a=m.resolve(l.destinationDir);return yield*r.makeDirectory(a,{recursive:true}).pipe(g.mapError(h=>new d({message:`could not create output directory ${a}: ${h.message}`,status:0}))),yield*g.forEach(l.urls,(h,S)=>g.gen(function*(){let H=yield*n.execute(M.get(h)).pipe(g.mapError(z=>new d({message:`download failed for ${h}: ${z.message}`,status:0,cause:z})));if(H.status<200||H.status>=300)return yield*new d({message:`download failed for ${h} (${H.status})`,status:H.status});let P=yield*H.arrayBuffer.pipe(g.mapError(z=>new d({message:`could not read body for ${h}: ${z.message}`,status:H.status,cause:z}))),Re=Dn(h,l.taskId,S),fe=m.join(a,Re);return yield*r.writeFile(fe,new Uint8Array(P)).pipe(g.mapError(z=>new d({message:`could not write ${fe}: ${z.message}`,status:0}))),fe}),{concurrency:4})});return {upload:s,rate:p,generate:u,waitForTask:y,getTaskDetails:f,downloadImages:_}}),dependencies:[Y.Default]}){},Hn=c.Struct({urls:c.Array(c.String)}),Ln=c.Union(c.Struct({url:c.String,rating:c.String}),c.Struct({error_code:c.String})),Nn=c.Struct({taskId:c.String,results:c.Array(Ln)}),Pn=c.Struct({taskId:c.String,access:c.optional(c.Struct({publicAccessToken:c.optional(c.String)}))}),Fn=c.Struct({status:c.Literal("pending","completed","failed")});var Un=Pt.millis(60),jn=2800,Ft=20,_e=[22,28,34,70,76,112,120,156,194,230],Ut=["\u2726","\u2727","\u2736","\xB7","\u2022"],qn="\x1B[38;5;230m",Wn="\x1B[38;5;156m",Kn="\x1B[38;5;108m",We="\x1B[0m",zn="\x1B[1m",jt="\r\x1B[K",Bn="\x1B[?25l",Jn="\x1B[?25h",Yn=["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","Teaching photons to behave","Taming entropy","Folding noise into form","Breathing life into latents","Tracing final strokes"],Kt=()=>typeof process<"u"&&process.stderr&&process.stderr.isTTY===true,rt=e=>g.sync(()=>{Kt()&&process.stderr.write(e);}),Gn=(e,t)=>{let n=Math.sin(e*.55+t*.18),o=Math.sin(e*.23-t*.11);return (n+o*.6)/1.6},Vn=e=>{let t=e%(Ft+6),n="";for(let o=0;o<Ft;o++){let p=(Gn(o,e)+1)/2,u=Math.min(_e.length-1,Math.floor(p*_e.length)),v=Math.abs(o-t);v<=1?u=_e.length-1:v===2&&(u=Math.max(u,_e.length-2));let f=_e[u];if(v===0&&e%3===0){let y=Ut[e%Ut.length];n+=`\x1B[38;5;${f}m${y}`;}else n+=`\x1B[38;5;${f}m\u2588`;}return n+We},Xn=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},Qn=(e,t)=>t[e%t.length]??"Working",Zn=(e,t,n)=>{let o=Vn(e),s=`${zn}${Wn}${t}\u2026${We}`,p=`${Kn}${n.toFixed(1)}s${We}`;return `${`${qn}\u258D${We}`} ${o} ${s} ${p}`},zt=(e,t={})=>{if(!Kt())return e;let n=Xn(t.messages??Yn),o=g.gen(function*(){let s=Date.now(),p=0,u=0,v=Date.now();for(yield*rt(Bn);;){let f=Date.now();f-v>jn&&(u++,v=f);let y=(f-s)/1e3,_=Zn(p,Qn(u,n),y);yield*rt(`${jt}${_}`),p++,yield*g.sleep(Un);}});return g.gen(function*(){let s=yield*g.fork(o);return yield*e.pipe(g.ensuring(g.gen(function*(){yield*Wt.interrupt(s),yield*rt(`${jt}${Jn}`);})))})};var lt=i.boolean("json").pipe(i.withDescription("Output machine-readable JSON instead of a human-readable summary")),eo=c.Array(c.Struct({value:c.String,description:c.String})),to=e=>g.sync(()=>e.indexOf("=")).pipe(g.flatMap(t=>t<=0?g.fail(new d({message:`invalid --level "${e}": expected "value=description"`,status:0})):g.succeed({value:e.slice(0,t).trim(),description:e.slice(t+1).trim()})),g.flatMap(t=>t.value.length===0||t.description.length===0?g.fail(new d({message:`invalid --level "${e}": value and description must be non-empty`,status:0})):g.succeed(t))),Bt=(e,t)=>g.try({try:()=>JSON.parse(e),catch:n=>new d({message:`invalid JSON in ${t}: ${n.message}`,status:0,cause:n})}).pipe(g.flatMap(n=>c.decodeUnknown(eo)(n).pipe(g.mapError(o=>new d({message:`invalid levels in ${t}: expected array of { value, description }`,status:0,cause:o}))))),Gt=g.fn("image.resolveLevels")(function*(e){let t=[e.levelPairs.length>0?"--level":null,A.isSome(e.levelsFile)?"--levels-file":null,A.isSome(e.levelsJson)?"--levels-json":null].filter(s=>s!==null);if(t.length===0)return;if(t.length>1)return yield*new d({message:`conflicting level options: ${t.join(", ")} \u2014 use only one`,status:0});let n=yield*e.levelPairs.length>0?g.forEach(e.levelPairs,to):A.match(e.levelsFile,{onSome:s=>g.gen(function*(){let u=yield*(yield*st.FileSystem).readFileString(s).pipe(g.mapError(v=>new d({message:`could not read ${s}: ${v.message}`,status:0})));return yield*Bt(u,s)}),onNone:()=>Bt(A.getOrElse(e.levelsJson,()=>"[]"),"--levels-json")});if(n.length<we||n.length>Ae)return yield*new d({message:`levels must have between ${we} and ${Ae} items (got ${n.length})`,status:0});let o=new Set;for(let s of n){if(o.has(s.value))return yield*new d({message:`duplicate level value: "${s.value}"`,status:0});o.add(s.value);}return n}),Ke=e=>/^https?:\/\//i.test(e),no=j.file({exists:"yes"}).pipe(j.withDescription("Path to a local image file (.jpg, .jpeg, .png, .webp)"),j.between(1,Ue)),oo=J.make("upload",{files:no,json:lt},({files:e,json:t})=>g.gen(function*(){let o=yield*(yield*Q).upload(e);if(t){yield*O.log(JSON.stringify({images:o},null,2));return}yield*O.log(`\u2713 Uploaded ${o.length} image${o.length===1?"":"s"}`);for(let{path:s,url:p}of o)yield*O.log(` ${s}`),yield*O.log(` \u2192 ${p}`);})),so=j.text({name:"image"}).pipe(j.withDescription("Image URL (http://, https://) or path to a local image file to upload first"),j.between(1,je)),Vt=i.text("level").pipe(i.withAlias("l"),i.withDescription('Custom rating level as "value=description" (repeatable, 2\u20137 items). Example: -l safe="No explicit content" -l nsfw="Contains nudity"'),i.repeated),Xt=i.file("levels-file",{exists:"yes"}).pipe(i.withDescription('Path to a JSON file containing an array of { "value": string, "description": string } (2\u20137 items). Alternative to --level when descriptions contain special characters.'),i.optional),Qt=i.text("levels-json").pipe(i.withDescription('Inline JSON array of { "value": string, "description": string } (2\u20137 items). Alternative to --level / --levels-file.'),i.optional),io=J.make("rate",{inputs:so,level:Vt,levelsFile:Xt,levelsJson:Qt,json:lt},({inputs:e,level:t,levelsFile:n,levelsJson:o,json:s})=>g.gen(function*(){let p=yield*Q,u=yield*Gt({levelPairs:t,levelsFile:n,levelsJson:o}),v=e.filter(Ke),f=e.filter(h=>!Ke(h)),y=f.length>0?yield*p.upload(f):[],_=new Map(y.map(h=>[h.path,h.url])),l=e.map(h=>Ke(h)?h:_.get(h)??h),r=yield*p.rate({urls:l,...u?{levels:u}:{}});if(s){yield*O.log(JSON.stringify(r,null,2));return}let m=r.results.filter(h=>"url"in h),a=r.results.filter(h=>"error_code"in h);y.length>0&&(yield*O.log(`\u2713 Uploaded ${y.length} image${y.length===1?"":"s"}`)),yield*O.log(`\u2713 Rated ${m.length}/${r.results.length} (task ${r.taskId})`);for(let h of m)yield*O.log(` ${h.rating} ${h.url}`);for(let h of a)yield*O.log(` ERROR ${h.error_code}`);if(v.length===0&&y.length>0){yield*O.log(""),yield*O.log("Uploaded source files:");for(let h of y)yield*O.log(` ${h.path} \u2192 ${h.url}`);}})),at=8,ro=["context","init","reference"],ao=e=>g.sync(()=>{let t=e.indexOf(":"),n=/^https?:/i.test(e);if(t>0&&!n){let o=e.slice(0,t);if(ro.includes(o))return {role:o,rest:e.slice(t+1)}}return {role:"reference",rest:e}}).pipe(g.flatMap(({role:t,rest:n})=>n.length===0?g.fail(new d({message:`invalid --input "${e}": missing path or URL`,status:0})):g.succeed({role:t,value:n,isFile:!Ke(n)}))),lo=j.text({name:"prompt"}).pipe(j.withDescription("Text prompt describing the image to generate")),co=i.text("negative").pipe(i.withAlias("n"),i.withDescription("Negative prompt (elements to exclude)"),i.optional),po=i.choice("enhance",["prefer_magic","prefer_native","none"]).pipe(i.withDescription('Prompt enhancement mode: "prefer_magic" (Mynth), "prefer_native" (provider), or "none"'),i.optional),fo=i.text("model").pipe(i.withAlias("m"),i.withDescription('Model ID (e.g. "black-forest-labs/flux.1-dev"). Default: "auto"'),i.optional),uo=i.text("size").pipe(i.withAlias("s"),i.withDescription('Size preset or aspect ratio: "square", "portrait", "landscape", "1:1", "16:9", "16:9_4k", "auto", etc.'),i.optional),mo=i.integer("count").pipe(i.withAlias("c"),i.withDescription("Number of images to generate (default: 1)"),i.optional),go=i.choice("format",["png","jpg","webp"]).pipe(i.withAlias("f"),i.withDescription("Output image format (default: webp)"),i.optional),yo=i.integer("quality").pipe(i.withAlias("q"),i.withDescription("Output quality 0\u2013100 (default: 80)"),i.optional),ho=i.text("input").pipe(i.withAlias("i"),i.withDescription(`Input image as "[role:]path-or-url" (repeatable, up to ${at}). Role is one of: context, init, reference (default: reference). Examples: -i ./img.jpg, -i reference:https://example.com/a.png, -i init:./seed.png`),i.repeated),Eo=i.directory("output-dir",{exists:"either"}).pipe(i.withAlias("o"),i.withDescription("Directory to save generated images to. Created if it doesn't exist. Ignored in --async mode since the task hasn't completed yet."),i.optional),So=i.text("destination").pipe(i.withDescription("Name (slug) of a user-configured destination to deliver the result to. Falls back to MYNTH_DESTINATION env var if not set."),i.optional),vo=i.text("metadata").pipe(i.withDescription("Inline JSON object of custom metadata to attach to the task (max 2KB)"),i.optional),ko=i.boolean("content-rating").pipe(i.withDescription("Enable content rating classification using default sfw/nsfw levels. For custom levels use --level / --levels-file / --levels-json.")),Oo=i.boolean("async").pipe(i.withDescription("Return the task ID immediately instead of polling until completion")),Co=i.boolean("detailed").pipe(i.withDescription("Include full task data (all fields) in the output")),wo=e=>g.try({try:()=>JSON.parse(e),catch:t=>new d({message:`invalid --metadata JSON: ${t.message}`,status:0,cause:t})}).pipe(g.flatMap(t=>t!==null&&typeof t=="object"&&!Array.isArray(t)?g.succeed(t):g.fail(new d({message:"--metadata must be a JSON object",status:0})))),Ao=J.make("generate",{prompt:lo,negative:co,enhance:po,model:fo,size:uo,count:mo,format:go,quality:yo,input:ho,outputDir:Eo,destination:So,metadata:vo,contentRating:ko,level:Vt,levelsFile:Xt,levelsJson:Qt,async:Oo,detailed:Co,json:lt},e=>g.gen(function*(){let t=yield*Q;if(e.input.length>at)return yield*new d({message:`too many --input values: ${e.input.length} (max ${at})`,status:0});let n=yield*g.forEach(e.input,ao),o=yield*A.match(e.metadata,{onSome:w=>wo(w).pipe(g.map(A.some)),onNone:()=>g.succeed(A.none())}),s=yield*Gt({levelPairs:e.level,levelsFile:e.levelsFile,levelsJson:e.levelsJson});let p=n.filter(w=>w.isFile).map(w=>w.value),u=Array.from(new Set(p)),v=u.length>0?yield*t.upload(u):[],f=new Map(v.map(w=>[w.path,w.url])),y=n.map(w=>({type:"image",role:w.role,source:{type:"url",url:w.isFile?f.get(w.value)??w.value:w.value}})),_=A.getOrUndefined(e.enhance),l=A.getOrUndefined(e.negative),m=_!==void 0||l!==void 0?{positive:e.prompt,...l!==void 0?{negative:l}:{},enhance:_===void 0||_==="none"?false:_}:e.prompt,a={},h=A.getOrUndefined(e.format),S=A.getOrUndefined(e.quality);h!==void 0&&(a.format=h),S!==void 0&&(a.quality=S);let H=s!==void 0?{enabled:true,levels:s}:e.contentRating?{enabled:true}:void 0,P={prompt:m},Re=A.getOrUndefined(e.model),fe=A.getOrUndefined(e.size),z=A.getOrUndefined(e.count),ut=A.getOrUndefined(e.destination);if(Re!==void 0&&(P.model=Re),fe!==void 0&&(P.size=fe),z!==void 0&&(P.count=z),Object.keys(a).length>0&&(P.output=a),y.length>0&&(P.inputs=y),ut!==void 0&&(P.destination=ut),H!==void 0&&(P.content_rating=H),A.isSome(o)&&(P.metadata=o.value),e.async){let w=yield*t.generate({request:P,requestPat:true}),Ve={taskId:w.taskId,access:A.match(w.pat,{onSome:rn=>({publicAccessToken:rn}),onNone:()=>{}})};if(e.json){yield*O.log(JSON.stringify(Ve,null,2));return}yield*O.log(`\u2713 Task created: ${w.taskId}`),A.isSome(w.pat)&&(yield*O.log(` PAT: ${w.pat.value}`));return}let mt=yield*t.generate({request:P,requestPat:true}),gt=t.waitForTask(mt.taskId,mt.pat),$e=yield*e.json?gt:zt(gt),yt=A.getOrUndefined(e.outputDir),Ee=yt!==void 0?(yield*it.Path).resolve(yt):void 0,Se=Ee!==void 0?yield*_o($e,Ee):[];if(e.json){let w=e.detailed?$e:xo($e),Ve=Ee!==void 0?{...w,downloadedFiles:Se}:w;yield*O.log(JSON.stringify(Ve,null,2));return}if(yield*Ro($e,v.length),Ee!==void 0&&Se.length>0){yield*O.log(""),yield*O.log(`\u2713 Saved ${Se.length} image${Se.length===1?"":"s"} to ${Ee}`);for(let w of Se)yield*O.log(` ${w}`);}})),_o=g.fn("image.downloadSucceededImages")(function*(e,t){let n=yield*Q,s=((e.result??{}).images??[]).map(p=>p).filter(p=>p.status==="succeeded").map(p=>p.url??p.mynth_url).filter(p=>typeof p=="string"&&p.length>0);return s.length===0?[]:yield*n.downloadImages({urls:s,destinationDir:t,taskId:e.id})}),xo=e=>{let t=e.result??{},n=(t.images??[]).map(o=>{let s=o;return s.status==="succeeded"?{status:"succeeded",url:s.url??null,mynth_url:s.mynth_url??null,size:s.size,content_rating:s.content_rating}:{status:"failed",error:s.error,mynth_url:s.mynth_url??null}});return {taskId:e.id,status:e.status,images:n,...t.prompt_enhance?{prompt_enhance:t.prompt_enhance}:{},...t.cost?.total!==void 0?{cost:t.cost.total}:{},...t.model!==void 0?{model:t.model}:{}}},Ro=g.fn("image.renderTaskHuman")(function*(e,t){let n=e.result??{},o=n.images??[];t>0&&(yield*O.log(`\u2713 Uploaded ${t} input image${t===1?"":"s"}`));let s=o.filter(p=>p.status==="succeeded");if(yield*O.log(`\u2713 Generated ${s.length}/${o.length} image${o.length===1?"":"s"} (task ${e.id})`),n.model!==void 0&&(yield*O.log(` Model: ${n.model}`)),n.size_auto?.value!==void 0&&(yield*O.log(` Size: ${n.size_auto.value} (auto, ${n.size_auto.source})`)),n.cost?.total!==void 0&&(yield*O.log(` Cost: ${n.cost.total}`)),n.destination?.name!==void 0&&(yield*O.log(` Destination: ${n.destination.name}`)),n.prompt_enhance?.positive!==void 0&&(yield*O.log(""),yield*O.log(`Enhanced prompt (${n.prompt_enhance.source??"unknown"}):`),yield*O.log(` ${n.prompt_enhance.positive}`),n.prompt_enhance.negative!==void 0&&n.prompt_enhance.negative.length>0&&(yield*O.log(` negative: ${n.prompt_enhance.negative}`))),o.length>0){yield*O.log("");for(let p of o){let u=p;if(u.status==="succeeded"){let v=u.content_rating,f=v?.level!==void 0?` [${v.level}]`:"",y=u.url??u.mynth_url;yield*O.log(` \u2713 ${y}${f}`);}else yield*O.log(` \u2717 ${u.error??"unknown error"}`);}}}),ct=J.make("image").pipe(J.withSubcommands([Ao,oo,io]));var ye=class extends g.Service()("TaskService",{effect:g.gen(function*(){let t=yield*Y;return {getTask:g.fn("TaskService.getTask")(function*(o){let s=yield*t.execute(M.get(`/tasks/${o}`));if(s.status<200||s.status>=300){let u=yield*s.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`task fetch failed (${s.status}): ${u||"no body"}`,status:s.status})}return yield*s.json.pipe(g.mapError(u=>new d({message:`invalid task response: ${u.message}`,status:s.status})))})}}),dependencies:[Y.Default]}){};var $o=i.boolean("json").pipe(i.withDescription("Output machine-readable JSON instead of a human-readable summary")),bo=j.text({name:"id"}).pipe(j.withDescription("Task ID")),To=e=>{switch(e){case "completed":return "\u2713";case "failed":return "\u2717";default:return "\u2026"}},Do=g.fn("task.renderHuman")(function*(e){yield*O.log(`${To(e.status)} Task ${e.id}`),yield*O.log(` Type: ${e.type}`),yield*O.log(` Status: ${e.status}`),e.cost!==null&&(yield*O.log(` Cost: ${e.cost}`)),yield*O.log(` Created: ${e.createdAt}`),yield*O.log(` Updated: ${e.updatedAt}`),e.result!==null&&e.result!==void 0&&(yield*O.log(""),yield*O.log("Result:"),yield*O.log(Mo(JSON.stringify(e.result,null,2),2)));}),Mo=(e,t)=>{let n=" ".repeat(t);return e.split(`
2
+ import*as en from'@effect/platform-node/NodeContext';import*as tn from'@effect/platform-node/NodeRuntime';import*as g from'effect/Effect';import*as ie from'effect/Layer';import*as Ge from'effect/Logger';import*as nn from'effect/LogLevel';import*as z from'@effect/cli/Command';import*as O from'effect/Console';import*as R from'effect/Match';import*as G from'effect/Data';import*as A from'effect/Option';import*as bt from'effect/Redacted';import*as Z from'effect/Config';import*as st from'@effect/platform/FileSystem';import*as it from'@effect/platform/Path';import*as te from'cross-keychain';import*as p from'effect/Schema';import*as ke from'@effect/platform/HttpBody';import*as Ie from'@effect/platform/HttpClient';import*as M from'@effect/platform/HttpClientRequest';import*as Oe from'@effect/platform/HttpClientResponse';import*as J from'@effect/cli/Args';import*as i from'@effect/cli/Options';import*as Lt from'effect/Duration';import*as ce from'effect/Ref';import*as jt from'effect/Fiber';import*as Qt from'@effect/platform/FetchHttpClient';var P=class extends G.TaggedError("MynthCliError"){},Se=class extends G.TaggedError("NotAuthenticatedError"){},fe=class extends G.TaggedError("CredentialsStoreError"){},K=class extends G.TaggedError("WorkOSError"){},ve=class extends G.TaggedError("AuthorizationPendingError"){},be=class extends G.TaggedError("AuthorizationExpiredError"){},Te=class extends G.TaggedError("AuthorizationDeniedError"){},d=class extends G.TaggedError("MynthApiError"){};var ue=g.all({mynthApiUrl:Z.string("MYNTH_API_URL").pipe(Z.withDefault("https://api.mynth.io")),apiKeyEnvOverride:Z.redacted("MYNTH_API_KEY").pipe(Z.option)});var yt=p.Struct({id:p.String,email:p.String,first_name:p.NullOr(p.String).pipe(p.optional),last_name:p.NullOr(p.String).pipe(p.optional)}),ht=p.Struct({device_code:p.String,user_code:p.String,verification_uri:p.String,verification_uri_complete:p.String.pipe(p.optional),expires_in:p.Number,interval:p.Number.pipe(p.optional)}),Et=p.Struct({access_token:p.String,refresh_token:p.String,user:yt.pipe(p.optional),organization_id:p.String.pipe(p.optional)}),St=p.Struct({error:p.String.pipe(p.optional),error_description:p.String.pipe(p.optional),message:p.String.pipe(p.optional),code:p.String.pipe(p.optional)}),sn=p.Struct({kind:p.Literal("oauth"),access_token:p.String,refresh_token:p.String,expires_at:p.Number,user:yt.pipe(p.optional)}),rn=p.Struct({kind:p.Literal("api_key"),api_key:p.String}),vt=p.Union(sn,rn);var Xe="mynth-cli",Qe="default",an="credentials.json",me=e=>g.mapError(t=>new fe({message:e,cause:t})),De=(e,t)=>g.tryPromise({try:e,catch:n=>n}).pipe(g.map(A.some),g.catchAll(n=>{let o=n?.name;return o==="NoKeyringError"||o==="InitError"?g.succeed(A.none()):new fe({message:t,cause:n})})),kt=e=>g.try({try:()=>JSON.parse(e),catch:t=>new fe({message:"credentials JSON parse failed",cause:t})}).pipe(g.flatMap(p.decodeUnknown(vt)),me("credentials shape invalid")),Ot=e=>JSON.stringify(e),ln=e=>{let t=process.env.XDG_CONFIG_HOME,n=t&&t.length>0?t:e.join(process.env.HOME??".",".config");return e.join(n,"mynth")},ee=class extends g.Service()("CredentialsStore",{effect:g.gen(function*(){let t=yield*st.FileSystem,n=yield*it.Path,o=ln(n),s=n.join(o,an),c=g.gen(function*(){if(!(yield*t.exists(s).pipe(me("stat credentials file failed"))))return A.none();let m=yield*t.readFileString(s).pipe(me("read credentials file failed"));return A.some(yield*kt(m))}),y=g.fn("CredentialsStore.writeFile")(function*(r){yield*t.makeDirectory(o,{recursive:true}).pipe(me("create config dir failed")),yield*t.writeFileString(s,Ot(r)).pipe(me("write credentials file failed")),yield*t.chmod(s,384).pipe(g.ignore);}),v=g.gen(function*(){(yield*t.exists(s).pipe(g.orElseSucceed(()=>false)))&&(yield*t.remove(s).pipe(me("delete credentials file failed")));}),u=g.gen(function*(){let r=yield*De(()=>te.getPassword(Xe,Qe),"keychain get failed");if(A.isNone(r))return yield*c;let m=r.value;return m===null?A.none():A.some(yield*kt(m))}),h=g.fn("CredentialsStore.set")(function*(r){let m=yield*De(()=>te.setPassword(Xe,Qe,Ot(r)),"keychain set failed");if(A.isNone(m)){yield*y(r);return}yield*v.pipe(g.ignore);}),_=g.gen(function*(){yield*De(()=>te.deletePassword(Xe,Qe),"keychain delete failed").pipe(g.ignore),yield*v.pipe(g.ignore);}),l=De(()=>te.getKeyring(),"keychain probe failed").pipe(g.map(r=>A.isSome(r)&&r.value!==null));return {get:u,set:h,clear:_,usingKeychain:l,filePath:s}})}){};var Me="client_01KATK792RR5ZCHMF5YMNN1ZSE",Rt="https://api.workos.com";var cn="urn:ietf:params:oauth:grant-type:device_code",pn="refresh_token",dn=e=>g.try({try:()=>{let t=e.split(".");if(t.length<2)throw new Error("malformed jwt");let n=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));if(typeof n.exp!="number")throw new Error("jwt missing exp claim");return n.exp*1e3},catch:t=>new K({message:"could not decode access token",cause:t})}),fn=e=>ke.urlParams(Object.entries(e)),_t=e=>Oe.schemaBodyJson(St)(e).pipe(g.orElseSucceed(()=>({}))),xt=(e,t)=>_t(e).pipe(g.flatMap(n=>{let o=n.error??n.code;return new K({message:n.error_description??n.message??t,status:e.status,...o!==void 0?{code:o}:{}})})),$t=(e,t)=>g.gen(function*(){let n=yield*Oe.schemaBodyJson(Et)(e).pipe(g.mapError(s=>new K({message:`invalid ${t} response`,status:e.status,cause:s}))),o=yield*dn(n.access_token);return {token:n,expiresAt:o}}),un=g.fn("WorkOS.parseAuthenticate")(function*(e){if(e.status===200)return yield*$t(e,"token");let t=yield*_t(e),n=t.error??t.code;return yield*R.value(n).pipe(R.when("authorization_pending",()=>new ve({slowDown:false})),R.when("slow_down",()=>new ve({slowDown:true})),R.when("expired_token",()=>new be),R.when("access_denied",()=>new Te),R.orElse(()=>new K({message:t.error_description??t.message??"WorkOS error",status:e.status,...n!==void 0?{code:n}:{}})))}),ne=class extends g.Service()("WorkOS",{effect:g.gen(function*(){let n=(yield*Ie.HttpClient).pipe(Ie.mapRequest(M.prependUrl(Rt))),o=(u,h)=>M.post(u).pipe(M.setBody(h),M.setHeader("Accept","application/json")),s=(u,h)=>n.execute(o("/user_management/authenticate",u)).pipe(g.mapError(_=>new K({message:`${h} request failed`,cause:_}))),c=g.fn("WorkOS.deviceAuthorize")(function*(){let u=yield*n.execute(o("/user_management/authorize/device",fn({client_id:Me}))).pipe(g.mapError(h=>new K({message:"device authorize request failed",cause:h})));return u.status!==200?yield*xt(u,"device authorize failed"):yield*Oe.schemaBodyJson(ht)(u).pipe(g.mapError(h=>new K({message:"invalid device authorize response",cause:h})))}),y=g.fn("WorkOS.exchangeDeviceCode")(function*(u){let h=yield*s(ke.unsafeJson({grant_type:cn,client_id:Me,device_code:u}),"authenticate");return yield*un(h)}),v=g.fn("WorkOS.refresh")(function*(u){let h=yield*s(ke.unsafeJson({grant_type:pn,client_id:Me,refresh_token:u}),"refresh");return h.status===200?yield*$t(h,"refresh"):yield*xt(h,"refresh failed")});return {requestDeviceAuthorization:c,exchangeDeviceCode:y,refresh:v}})}){};var mn=6e4,Ze=e=>g.mapError(t=>new Se({reason:`${e}: ${t.message}`})),Le=e=>e?{user:e}:{},L=class extends g.Service()("Auth",{effect:g.gen(function*(){let t=yield*ue,n=yield*ee,o=yield*ne,s=A.map(t.apiKeyEnvOverride,bt.value),c=A.isSome(s),y=r=>g.gen(function*(){if(r.expires_at-Date.now()>mn)return r;let m=yield*o.refresh(r.refresh_token).pipe(Ze("token refresh failed")),a={kind:"oauth",access_token:m.token.access_token,refresh_token:m.token.refresh_token,expires_at:m.expiresAt,...Le(m.token.user??r.user)};return yield*n.set(a).pipe(Ze("could not persist refreshed token")),a}),v=g.gen(function*(){if(A.isSome(s))return {kind:"api_key",apiKey:s.value,source:"env"};let r=yield*n.get.pipe(Ze("could not read credentials"));if(A.isNone(r))return yield*new Se({reason:"no credentials configured"});let m=r.value;if(m.kind==="api_key")return {kind:"api_key",apiKey:m.api_key,source:"stored"};let a=yield*y(m);return {kind:"oauth",accessToken:a.access_token,...Le(a.user)}}),u=g.gen(function*(){if(A.isSome(s))return {kind:"env",source:"MYNTH_API_KEY"};let r=yield*n.get.pipe(g.orElseSucceed(()=>A.none()));if(A.isNone(r))return {kind:"none"};let m=r.value;return m.kind==="api_key"?{kind:"api_key"}:{kind:"oauth",expiresAt:m.expires_at,...Le(m.user)}}),h=r=>n.set({kind:"api_key",api_key:r}),_=r=>n.set({kind:"oauth",access_token:r.accessToken,refresh_token:r.refreshToken,expires_at:r.expiresAt,...Le(r.user)}),l=n.clear;return {resolve:v,status:u,setApiKey:h,saveOAuth:_,logout:l,envApiKeySet:c}})}){};var gn=e=>new Date(e).toISOString(),Tt=e=>g.mapError(t=>new P({message:`${e}: ${t.message}`,cause:t})),yn=g.fn("auth.pollForToken")(function*(e,t,n,o){let s=n;for(;;){if(Date.now()>=o)return yield*new P({message:"device code expired before approval"});let c=yield*g.either(e.exchangeDeviceCode(t));if(c._tag==="Right")return c.right;let y=c.left,v=R.value(y).pipe(R.tag("AuthorizationPendingError",u=>({kind:"retry",slowDown:u.slowDown})),R.tag("AuthorizationDeniedError",()=>({kind:"fail",message:"login denied by user"})),R.tag("AuthorizationExpiredError",()=>({kind:"fail",message:"device code expired"})),R.tag("WorkOSError",u=>({kind:"fail",message:u.message,cause:u})),R.exhaustive);if(v.kind==="fail")return yield*new P({message:v.message,..."cause"in v?{cause:v.cause}:{}});v.slowDown&&(s+=5e3),yield*g.sleep(`${s} millis`);}}),hn=z.make("login",{},()=>g.gen(function*(){let e=yield*L,t=yield*ne;if(e.envApiKeySet)return yield*O.log(`MYNTH_API_KEY is set in your environment; that takes precedence over login.
3
+ Unset it to use OAuth, or just continue using the env API key.`),yield*new P({message:"env api key takes precedence"});let n=yield*t.requestDeviceAuthorization().pipe(Tt("device authorize"));yield*O.log(""),yield*O.log(` First copy your one-time code: ${n.user_code}`),yield*O.log(` Then open: ${n.verification_uri_complete??n.verification_uri}`),yield*O.log(""),yield*O.log("Waiting for confirmation...");let o=yield*yn(t,n.device_code,(n.interval??5)*1e3,Date.now()+n.expires_in*1e3);yield*e.saveOAuth({accessToken:o.token.access_token,refreshToken:o.token.refresh_token,expiresAt:o.expiresAt,...o.token.user?{user:o.token.user}:{}}).pipe(Tt("could not save credentials"));let s=o.token.user?.email??o.token.user?.id??"unknown user";yield*O.log(`\u2713 Logged in as ${s}`);})),En=z.make("logout",{},()=>g.gen(function*(){let e=yield*L;yield*e.logout,yield*O.log("\u2713 Local credentials cleared"),e.envApiKeySet&&(yield*O.log("Note: MYNTH_API_KEY is still set in your environment and will be used."));})),Sn=z.make("status",{},()=>g.gen(function*(){let e=yield*L,t=yield*ee,n=yield*e.status,s=(yield*t.usingKeychain)?"system keychain":`file (${t.filePath})`,c=R.value(n).pipe(R.when({kind:"env"},()=>["Authenticated via env: MYNTH_API_KEY"]),R.when({kind:"none"},()=>["Not authenticated. Run `mynth auth login` or set an API key."]),R.when({kind:"api_key"},()=>[`Authenticated via stored API key (${s})`]),R.when({kind:"oauth"},y=>[`Authenticated via OAuth as ${y.user?.email??y.user?.id??"unknown user"} (${s})`,` access token expires: ${gn(y.expiresAt)}`]),R.exhaustive);yield*g.forEach(c,O.log);})),Dt=z.make("whoami",{},()=>g.gen(function*(){let t=yield*(yield*L).status;return yield*R.value(t).pipe(R.when({kind:"none"},()=>O.log("not authenticated").pipe(g.zipRight(new P({message:"not authenticated"})))),R.when({kind:"env"},()=>O.log("env:MYNTH_API_KEY")),R.when({kind:"api_key"},()=>O.log("api-key")),R.when({kind:"oauth"},n=>O.log(n.user?.email??n.user?.id??"oauth")),R.exhaustive)})),et=z.make("auth").pipe(z.withSubcommands([hn,En,Sn,Dt])),tt=Dt;var vn=g.tryPromise({try:async()=>{let e="";process.stdin.setEncoding("utf8");for await(let t of process.stdin)e+=t;return e.trim()},catch:e=>new P({message:"could not read stdin",cause:e})}),kn=J.text({name:"value"}).pipe(J.withDescription("API key value, or `-` to read from stdin")),On=z.make("api-key",{value:kn},({value:e})=>g.gen(function*(){let t=yield*L,n=e==="-"?yield*vn:e;if(n.length===0)return yield*new P({message:"API key is empty"});yield*t.setApiKey(n).pipe(g.mapError(o=>new P({message:`could not save API key: ${o.message}`,cause:o}))),yield*O.log("\u2713 API key saved"),t.envApiKeySet&&(yield*O.log("Note: MYNTH_API_KEY is also set in your environment and will take precedence."));})),Cn=z.make("set").pipe(z.withSubcommands([On])),wn=z.make("api-key",{},()=>g.gen(function*(){yield*(yield*L).logout,yield*O.log("\u2713 Stored credentials cleared");})),An=z.make("unset").pipe(z.withSubcommands([wn])),nt=z.make("config").pipe(z.withSubcommands([Cn,An]));var Rn=e=>e.kind==="api_key"?e.apiKey:e.accessToken,B=class extends g.Service()("MynthApi",{effect:g.gen(function*(){let t=yield*ue,n=yield*L,o=yield*Ie.HttpClient,s=yield*ce.make(A.none()),c=u=>g.gen(function*(){u&&(yield*ce.set(s,A.none()));let h=yield*ce.get(s);if(A.isSome(h))return h.value;let _=yield*n.resolve;return yield*ce.set(s,A.some(_)),_}),y=g.fn("MynthApi.attempt")(function*(u,h){let _=yield*c(h),l=u.pipe(M.prependUrl(t.mynthApiUrl),M.setHeader("Authorization",`Bearer ${Rn(_)}`));yield*g.logDebug("MynthApi request",{method:l.method,url:l.url,authKind:_.kind,forceRefresh:h});let r=yield*o.execute(l).pipe(g.tapErrorCause(m=>g.logDebug("MynthApi transport error",m)),g.mapError(m=>new d({message:`request failed: ${m.message}`,status:0,cause:m})));return yield*g.logDebug("MynthApi response",{status:r.status}),r});return {execute:u=>g.gen(function*(){let h=yield*y(u,false);return h.status!==401?h:yield*y(u,true)}),baseUrl:t.mynthApiUrl}})}){};var Ue=10,je=10,Ce=2,we=7,It=300*1e3,xn=12e3,_n=2500,$n=5e3,bn=(e,t,n)=>{try{let s=new URL(e).pathname.split("/").filter(Boolean).pop();if(s&&s.length>0)return decodeURIComponent(s)}catch{}return `${t}-${n}`},Tn={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".webp":"image/webp"},Dn=g.fn("ImageService.readImageFile")(function*(e){let t=yield*st.FileSystem,n=yield*it.Path,o=n.extname(e).toLowerCase(),s=Tn[o];if(!s)return yield*new d({message:`unsupported image extension "${o}" for ${e} (allowed: .jpg, .jpeg, .png, .webp)`,status:0});let c=yield*t.readFile(e).pipe(g.mapError(u=>new d({message:`could not read ${e}: ${u.message}`,status:0}))),y=n.basename(e),v=new ArrayBuffer(c.byteLength);return new Uint8Array(v).set(c),new File([v],y,{type:s})}),X=class extends g.Service()("ImageService",{effect:g.gen(function*(){let t=yield*B,n=yield*Ie.HttpClient,o=yield*ue,s=g.fn("ImageService.upload")(function*(l){if(l.length===0)return yield*new d({message:"no files to upload",status:0});if(l.length>Ue)return yield*new d({message:`too many files: ${l.length} (max ${Ue})`,status:0});yield*g.logDebug("ImageService.upload starting",{count:l.length,filePaths:l});let r=yield*g.forEach(l,Dn);yield*g.logDebug("ImageService.upload read files",r.map(f=>({name:f.name,size:f.size,type:f.type})));let m=new FormData;for(let f of r)m.append("images",f);let a=yield*t.execute(M.post("/image/upload").pipe(M.bodyFormData(m)));if(yield*g.logDebug("ImageService.upload response",{status:a.status}),a.status<200||a.status>=300){let f=yield*a.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`upload failed (${a.status}): ${f||"no body"}`,status:a.status})}let E=yield*Oe.schemaBodyJson(Mn)(a).pipe(g.mapError(f=>new d({message:`invalid upload response: ${f.message}`,status:a.status})));return l.map((f,H)=>({path:f,url:E.urls[H]}))}),c=g.fn("ImageService.rate")(function*(l){if(l.urls.length===0)return yield*new d({message:"no image URLs to rate",status:0});if(l.urls.length>je)return yield*new d({message:`too many images: ${l.urls.length} (max ${je})`,status:0});if(l.levels!==void 0&&(l.levels.length<Ce||l.levels.length>we))return yield*new d({message:`levels must have between ${Ce} and ${we} items (got ${l.levels.length})`,status:0});yield*g.logDebug("ImageService.rate starting",{count:l.urls.length,hasLevels:l.levels!==void 0});let r=l.levels!==void 0?{urls:l.urls,mode:"custom",levels:l.levels}:{urls:l.urls,mode:"nsfw_sfw"},m=yield*ke.json(r).pipe(g.mapError(f=>new d({message:`could not encode rate body: ${f.reason}`,status:0,cause:f}))),a=yield*t.execute(M.post("/image/rate").pipe(M.setBody(m)));if(yield*g.logDebug("ImageService.rate response",{status:a.status}),a.status<200||a.status>=300){let f=yield*a.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`rate failed (${a.status}): ${f||"no body"}`,status:a.status})}return yield*Oe.schemaBodyJson(Hn)(a).pipe(g.mapError(f=>new d({message:`invalid rate response: ${f.message}`,status:a.status})))}),y=g.fn("ImageService.generate")(function*(l){let r=l.requestPat?{...l.request,access:{...l.request.access,pat:{enabled:true}}}:l.request,m=yield*ke.json(r).pipe(g.mapError(f=>new d({message:`could not encode generate body: ${f.reason}`,status:0,cause:f}))),a=yield*t.execute(M.post("/image/generate").pipe(M.setBody(m)));if(a.status<200||a.status>=300){let f=yield*a.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`generate failed (${a.status}): ${f||"no body"}`,status:a.status})}let E=yield*Oe.schemaBodyJson(Ln)(a).pipe(g.mapError(f=>new d({message:`invalid generate response: ${f.message}`,status:a.status})));return {taskId:E.taskId,pat:A.fromNullable(E.access?.publicAccessToken)}}),v=g.fn("ImageService.getTaskStatus")(function*(l,r){let m=`/tasks/${l}/status`,a=yield*A.match(r,{onSome:f=>n.execute(M.get(`${o.mynthApiUrl}${m}`).pipe(M.setHeader("Authorization",`Bearer ${f}`))).pipe(g.mapError(H=>new d({message:`task status request failed: ${H.message}`,status:0,cause:H}))),onNone:()=>t.execute(M.get(m))});if(a.status<200||a.status>=300){let f=yield*a.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`task status failed (${a.status}): ${f||"no body"}`,status:a.status})}return (yield*Oe.schemaBodyJson(Nn)(a).pipe(g.mapError(f=>new d({message:`invalid task status response: ${f.message}`,status:a.status})))).status}),u=g.fn("ImageService.getTaskDetails")(function*(l){let r=yield*t.execute(M.get(`/tasks/${l}`));if(r.status<200||r.status>=300){let a=yield*r.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`task details failed (${r.status}): ${a||"no body"}`,status:r.status})}return yield*r.json.pipe(g.mapError(a=>new d({message:`invalid task details response: ${a.message}`,status:r.status})))}),h=g.fn("ImageService.waitForTask")(function*(l,r){let m=Date.now();for(;;){let a=Date.now()-m;if(a>=It)return yield*new d({message:`task ${l} polling timed out after ${It}ms`,status:0});let E=yield*v(l,r);if(E==="completed")return yield*u(l);if(E==="failed")return yield*new d({message:`task ${l} failed during generation`,status:0});let f=a<xn?_n:$n;yield*g.sleep(Lt.millis(f+Math.floor(Math.random()*500)));}}),_=g.fn("ImageService.downloadImages")(function*(l){let r=yield*st.FileSystem,m=yield*it.Path,a=m.resolve(l.destinationDir);return yield*r.makeDirectory(a,{recursive:true}).pipe(g.mapError(E=>new d({message:`could not create output directory ${a}: ${E.message}`,status:0}))),yield*g.forEach(l.urls,(E,f)=>g.gen(function*(){let H=yield*n.execute(M.get(E)).pipe(g.mapError(W=>new d({message:`download failed for ${E}: ${W.message}`,status:0,cause:W})));if(H.status<200||H.status>=300)return yield*new d({message:`download failed for ${E} (${H.status})`,status:H.status});let xe=yield*H.arrayBuffer.pipe(g.mapError(W=>new d({message:`could not read body for ${E}: ${W.message}`,status:H.status,cause:W}))),_e=bn(E,l.taskId,f),de=m.join(a,_e);return yield*r.writeFile(de,new Uint8Array(xe)).pipe(g.mapError(W=>new d({message:`could not write ${de}: ${W.message}`,status:0}))),de}),{concurrency:4})});return {upload:s,rate:c,generate:y,waitForTask:h,getTaskDetails:u,downloadImages:_}}),dependencies:[B.Default]}){},Mn=p.Struct({urls:p.Array(p.String)}),In=p.Union(p.Struct({status:p.Literal("success"),url:p.String,level:p.String}),p.Struct({status:p.Literal("failed"),url:p.String,error:p.Struct({code:p.String})})),Hn=p.Struct({task:p.Struct({id:p.String,cost:p.String}),results:p.Array(In)}),Ln=p.Struct({taskId:p.String,access:p.optional(p.Struct({publicAccessToken:p.optional(p.String)}))}),Nn=p.Struct({status:p.Literal("pending","completed","failed")});var Pn=Lt.millis(60),Fn=2800,Nt=20,Ae=[22,28,34,70,76,112,120,156,194,230],Pt=["\u2726","\u2727","\u2736","\xB7","\u2022"],Un="\x1B[38;5;230m",jn="\x1B[38;5;156m",qn="\x1B[38;5;108m",We="\x1B[0m",Wn="\x1B[1m",Ft="\r\x1B[K",Kn="\x1B[?25l",zn="\x1B[?25h",Bn=["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","Teaching photons to behave","Taming entropy","Folding noise into form","Breathing life into latents","Tracing final strokes"],qt=()=>typeof process<"u"&&process.stderr&&process.stderr.isTTY===true,rt=e=>g.sync(()=>{qt()&&process.stderr.write(e);}),Jn=(e,t)=>{let n=Math.sin(e*.55+t*.18),o=Math.sin(e*.23-t*.11);return (n+o*.6)/1.6},Yn=e=>{let t=e%(Nt+6),n="";for(let o=0;o<Nt;o++){let c=(Jn(o,e)+1)/2,y=Math.min(Ae.length-1,Math.floor(c*Ae.length)),v=Math.abs(o-t);v<=1?y=Ae.length-1:v===2&&(y=Math.max(y,Ae.length-2));let u=Ae[y];if(v===0&&e%3===0){let h=Pt[e%Pt.length];n+=`\x1B[38;5;${u}m${h}`;}else n+=`\x1B[38;5;${u}m\u2588`;}return n+We},Gn=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},Vn=(e,t)=>t[e%t.length]??"Working",Xn=(e,t,n)=>{let o=Yn(e),s=`${Wn}${jn}${t}\u2026${We}`,c=`${qn}${n.toFixed(1)}s${We}`;return `${`${Un}\u258D${We}`} ${o} ${s} ${c}`},Wt=(e,t={})=>{if(!qt())return e;let n=Gn(t.messages??Bn),o=g.gen(function*(){let s=Date.now(),c=0,y=0,v=Date.now();for(yield*rt(Kn);;){let u=Date.now();u-v>Fn&&(y++,v=u);let h=(u-s)/1e3,_=Xn(c,Vn(y,n),h);yield*rt(`${Ft}${_}`),c++,yield*g.sleep(Pn);}});return g.gen(function*(){let s=yield*g.fork(o);return yield*e.pipe(g.ensuring(g.gen(function*(){yield*jt.interrupt(s),yield*rt(`${Ft}${zn}`);})))})};var lt=i.boolean("json").pipe(i.withDescription("Output machine-readable JSON instead of a human-readable summary")),Qn=p.Array(p.Struct({value:p.String,description:p.String})),Zn=e=>g.sync(()=>e.indexOf("=")).pipe(g.flatMap(t=>t<=0?g.fail(new d({message:`invalid --level "${e}": expected "value=description"`,status:0})):g.succeed({value:e.slice(0,t).trim(),description:e.slice(t+1).trim()})),g.flatMap(t=>t.value.length===0||t.description.length===0?g.fail(new d({message:`invalid --level "${e}": value and description must be non-empty`,status:0})):g.succeed(t))),Kt=(e,t)=>g.try({try:()=>JSON.parse(e),catch:n=>new d({message:`invalid JSON in ${t}: ${n.message}`,status:0,cause:n})}).pipe(g.flatMap(n=>p.decodeUnknown(Qn)(n).pipe(g.mapError(o=>new d({message:`invalid levels in ${t}: expected array of { value, description }`,status:0,cause:o}))))),Jt=g.fn("image.resolveLevels")(function*(e){let t=[e.levelPairs.length>0?"--level":null,A.isSome(e.levelsFile)?"--levels-file":null,A.isSome(e.levelsJson)?"--levels-json":null].filter(s=>s!==null);if(t.length===0)return;if(t.length>1)return yield*new d({message:`conflicting level options: ${t.join(", ")} \u2014 use only one`,status:0});let n=yield*e.levelPairs.length>0?g.forEach(e.levelPairs,Zn):A.match(e.levelsFile,{onSome:s=>g.gen(function*(){let y=yield*(yield*st.FileSystem).readFileString(s).pipe(g.mapError(v=>new d({message:`could not read ${s}: ${v.message}`,status:0})));return yield*Kt(y,s)}),onNone:()=>Kt(A.getOrElse(e.levelsJson,()=>"[]"),"--levels-json")});if(n.length<Ce||n.length>we)return yield*new d({message:`levels must have between ${Ce} and ${we} items (got ${n.length})`,status:0});let o=new Set;for(let s of n){if(o.has(s.value))return yield*new d({message:`duplicate level value: "${s.value}"`,status:0});o.add(s.value);}return n}),Ke=e=>/^https?:\/\//i.test(e),eo=J.file({exists:"yes"}).pipe(J.withDescription("Path to a local image file (.jpg, .jpeg, .png, .webp)"),J.between(1,Ue)),to=z.make("upload",{files:eo,json:lt},({files:e,json:t})=>g.gen(function*(){let o=yield*(yield*X).upload(e);if(t){yield*O.log(JSON.stringify({images:o},null,2));return}yield*O.log(`\u2713 Uploaded ${o.length} image${o.length===1?"":"s"}`);for(let{path:s,url:c}of o)yield*O.log(` ${s}`),yield*O.log(` \u2192 ${c}`);})),no=J.text({name:"image"}).pipe(J.withDescription("Image URL (http://, https://) or path to a local image file to upload first"),J.between(1,je)),Yt=i.text("level").pipe(i.withAlias("l"),i.withDescription('Custom rating level as "value=description" (repeatable, 2\u20137 items). Example: -l safe="No explicit content" -l nsfw="Contains nudity"'),i.repeated),Gt=i.file("levels-file",{exists:"yes"}).pipe(i.withDescription('Path to a JSON file containing an array of { "value": string, "description": string } (2\u20137 items). Alternative to --level when descriptions contain special characters.'),i.optional),Vt=i.text("levels-json").pipe(i.withDescription('Inline JSON array of { "value": string, "description": string } (2\u20137 items). Alternative to --level / --levels-file.'),i.optional),oo=z.make("rate",{inputs:no,level:Yt,levelsFile:Gt,levelsJson:Vt,json:lt},({inputs:e,level:t,levelsFile:n,levelsJson:o,json:s})=>g.gen(function*(){let c=yield*X,y=yield*Jt({levelPairs:t,levelsFile:n,levelsJson:o}),v=e.filter(Ke),u=e.filter(E=>!Ke(E)),h=u.length>0?yield*c.upload(u):[],_=new Map(h.map(E=>[E.path,E.url])),l=e.map(E=>Ke(E)?E:_.get(E)??E),r=yield*c.rate({urls:l,...y?{levels:y}:{}});if(s){yield*O.log(JSON.stringify(r,null,2));return}let m=r.results.filter(E=>E.status==="success"),a=r.results.filter(E=>E.status==="failed");h.length>0&&(yield*O.log(`\u2713 Uploaded ${h.length} image${h.length===1?"":"s"}`)),yield*O.log(`\u2713 Rated ${m.length}/${r.results.length} (task ${r.task.id})`);for(let E of m)yield*O.log(` ${E.level} ${E.url}`);for(let E of a)yield*O.log(` ERROR ${E.error.code} ${E.url}`);if(v.length===0&&h.length>0){yield*O.log(""),yield*O.log("Uploaded source files:");for(let E of h)yield*O.log(` ${E.path} \u2192 ${E.url}`);}})),at=8,so=["auto","init","reference"],io=e=>g.sync(()=>{let t=e.indexOf(":"),n=/^https?:/i.test(e);if(t>0&&!n){let o=e.slice(0,t);if(so.includes(o))return {role:o,rest:e.slice(t+1)}}return {role:"reference",rest:e}}).pipe(g.flatMap(({role:t,rest:n})=>n.length===0?g.fail(new d({message:`invalid --input "${e}": missing path or URL`,status:0})):g.succeed({role:t,value:n,isFile:!Ke(n)}))),ro=i.text("prompt").pipe(i.withAlias("p"),i.withDescription("Text prompt describing the image to generate")),ao=i.text("negative").pipe(i.withAlias("n"),i.withDescription("Negative prompt (elements to exclude)"),i.optional),lo=i.choice("enhance",["prefer_magic","prefer_native","none"]).pipe(i.withDescription('Prompt enhancement mode: "prefer_magic" (Mynth) or "none". "prefer_native" is no longer supported by the API.'),i.optional),co=i.text("model").pipe(i.withAlias("m"),i.withDescription('Model ID (e.g. "black-forest-labs/flux.1-dev"). Default: "auto"'),i.optional),po=i.text("size").pipe(i.withAlias("s"),i.withDescription('Size preset or aspect ratio: "square", "portrait", "landscape", "1:1", "16:9", "16:9_4k", "auto", etc.'),i.optional),fo=i.integer("count").pipe(i.withAlias("c"),i.withDescription("Number of images to generate (default: 1)"),i.optional),uo=i.choice("format",["png","jpg","webp"]).pipe(i.withAlias("f"),i.withDescription("Output image format (default: webp)"),i.optional),mo=i.integer("quality").pipe(i.withAlias("q"),i.withDescription("Output quality 0\u2013100 (default: 80)"),i.optional),go=i.text("input").pipe(i.withAlias("i"),i.withDescription(`Input image as "[role:]path-or-url" (repeatable, up to ${at}). Role is one of: auto, init, reference (default: reference). Examples: -i ./img.jpg, -i reference:https://example.com/a.png, -i init:./seed.png`),i.repeated),yo=i.directory("output-dir",{exists:"either"}).pipe(i.withAlias("o"),i.withDescription("Directory to save generated images to. Created if it doesn't exist. Ignored in --async mode since the task hasn't completed yet."),i.optional),ho=i.text("destination").pipe(i.withDescription("Name (slug) of a user-configured destination to deliver the result to. Falls back to MYNTH_DESTINATION env var if not set."),i.optional),Eo=i.text("metadata").pipe(i.withDescription("Inline JSON object of custom metadata to attach to the task (max 2KB)"),i.optional),So=i.boolean("content-rating").pipe(i.withDescription("Enable content rating classification using default sfw/nsfw levels. For custom levels use --level / --levels-file / --levels-json.")),vo=i.boolean("async").pipe(i.withDescription("Return the task ID immediately instead of polling until completion")),ko=i.boolean("detailed").pipe(i.withDescription("Include full task data (all fields) in the output")),Oo=e=>g.try({try:()=>JSON.parse(e),catch:t=>new d({message:`invalid --metadata JSON: ${t.message}`,status:0,cause:t})}).pipe(g.flatMap(t=>t!==null&&typeof t=="object"&&!Array.isArray(t)?g.succeed(t):g.fail(new d({message:"--metadata must be a JSON object",status:0})))),Co=z.make("generate",{prompt:ro,negative:ao,enhance:lo,model:co,size:po,count:fo,format:uo,quality:mo,input:go,outputDir:yo,destination:ho,metadata:Eo,contentRating:So,level:Yt,levelsFile:Gt,levelsJson:Vt,async:vo,detailed:ko,json:lt},e=>g.gen(function*(){let t=yield*X,n=A.getOrUndefined(e.enhance);if(n==="prefer_native")return yield*new d({message:'--enhance prefer_native is no longer supported by the API; use "prefer_magic" or "none"',status:0});if(e.input.length>at)return yield*new d({message:`too many --input values: ${e.input.length} (max ${at})`,status:0});let o=yield*g.forEach(e.input,io),s=yield*A.match(e.metadata,{onSome:w=>Oo(w).pipe(g.map(A.some)),onNone:()=>g.succeed(A.none())}),c=yield*Jt({levelPairs:e.level,levelsFile:e.levelsFile,levelsJson:e.levelsJson});let y=o.filter(w=>w.isFile).map(w=>w.value),v=Array.from(new Set(y)),u=v.length>0?yield*t.upload(v):[],h=new Map(u.map(w=>[w.path,w.url])),_=o.map(w=>({type:"image",role:w.role,source:{type:"url",url:w.isFile?h.get(w.value)??w.value:w.value}})),l=A.getOrUndefined(e.negative),r={},m=A.getOrUndefined(e.format),a=A.getOrUndefined(e.quality);m!==void 0&&(r.format=m),a!==void 0&&(r.quality=a);let E=c!==void 0?{mode:"custom",levels:c}:e.contentRating?true:void 0,f={prompt:e.prompt},H=A.getOrUndefined(e.model),xe=A.getOrUndefined(e.size),_e=A.getOrUndefined(e.count),de=A.getOrUndefined(e.destination);if(H!==void 0&&(f.model=H),l!==void 0&&(f.negative_prompt=l),n==="prefer_magic"&&(f.magic_prompt=true),xe!==void 0&&(f.size=xe),_e!==void 0&&(f.count=_e),Object.keys(r).length>0&&(f.output=r),_.length>0&&(f.inputs=_),de!==void 0&&(f.destination=de),E!==void 0&&(f.rating=E),A.isSome(s)&&(f.metadata=s.value),e.async){let w=yield*t.generate({request:f,requestPat:true}),Ve={taskId:w.taskId,access:A.match(w.pat,{onSome:on=>({publicAccessToken:on}),onNone:()=>{}})};if(e.json){yield*O.log(JSON.stringify(Ve,null,2));return}yield*O.log(`\u2713 Task created: ${w.taskId}`),A.isSome(w.pat)&&(yield*O.log(` PAT: ${w.pat.value}`));return}let W=yield*t.generate({request:f,requestPat:true}),ut=t.waitForTask(W.taskId,W.pat),$e=yield*e.json?ut:Wt(ut),mt=A.getOrUndefined(e.outputDir),he=mt!==void 0?(yield*it.Path).resolve(mt):void 0,Ee=he!==void 0?yield*wo($e,he):[];if(e.json){let w=e.detailed?$e:Ao($e),Ve=he!==void 0?{...w,downloadedFiles:Ee}:w;yield*O.log(JSON.stringify(Ve,null,2));return}if(yield*Ro($e,u.length),he!==void 0&&Ee.length>0){yield*O.log(""),yield*O.log(`\u2713 Saved ${Ee.length} image${Ee.length===1?"":"s"} to ${he}`);for(let w of Ee)yield*O.log(` ${w}`);}})),wo=g.fn("image.downloadSucceededImages")(function*(e,t){let n=yield*X,s=((e.result??{}).images??[]).map(c=>c).filter(c=>c.status==="success").map(c=>c.url??c.mynth_url).filter(c=>typeof c=="string"&&c.length>0);return s.length===0?[]:yield*n.downloadImages({urls:s,destinationDir:t,taskId:e.id})}),Ao=e=>{let t=e.result??{},n=(t.images??[]).map(o=>{let s=o;return s.status==="success"?{status:"success",url:s.url??null,mynth_url:s.mynth_url??null,size:s.size,rating:s.rating}:{status:"failed",error:s.error,mynth_url:s.mynth_url??null}});return {taskId:e.id,status:e.status,images:n,...t.magic_prompt?{magic_prompt:t.magic_prompt}:{},...t.cost?.total!==void 0?{cost:t.cost.total}:{},...t.model!==void 0?{model:t.model}:{}}},Ro=g.fn("image.renderTaskHuman")(function*(e,t){let n=e.result??{},o=n.images??[];t>0&&(yield*O.log(`\u2713 Uploaded ${t} input image${t===1?"":"s"}`));let s=o.filter(c=>c.status==="success");if(yield*O.log(`\u2713 Generated ${s.length}/${o.length} image${o.length===1?"":"s"} (task ${e.id})`),n.model!==void 0&&(yield*O.log(` Model: ${n.model}`)),n.size_auto?.value!==void 0&&(yield*O.log(` Size: ${n.size_auto.value} (auto, ${n.size_auto.source})`)),n.cost?.total!==void 0&&(yield*O.log(` Cost: ${n.cost.total}`)),n.destination?.name!==void 0&&(yield*O.log(` Destination: ${n.destination.name}`)),n.magic_prompt?.positive!==void 0&&(yield*O.log(""),yield*O.log("Enhanced prompt (mynth):"),yield*O.log(` ${n.magic_prompt.positive}`),n.magic_prompt.negative!==void 0&&n.magic_prompt.negative.length>0&&(yield*O.log(` negative: ${n.magic_prompt.negative}`))),o.length>0){yield*O.log("");for(let c of o){let y=c;if(y.status==="success"){let v=y.rating,u=v?.level!==void 0?` [${v.level}]`:"",h=y.url??y.mynth_url;yield*O.log(` \u2713 ${h}${u}`);}else yield*O.log(` \u2717 ${y.error??"unknown error"}`);}}}),ct=z.make("image").pipe(z.withSubcommands([Co,to,oo]));var ge=class extends g.Service()("TaskService",{effect:g.gen(function*(){let t=yield*B;return {getTask:g.fn("TaskService.getTask")(function*(o){let s=yield*t.execute(M.get(`/tasks/${o}`));if(s.status<200||s.status>=300){let y=yield*s.text.pipe(g.orElseSucceed(()=>""));return yield*new d({message:`task fetch failed (${s.status}): ${y||"no body"}`,status:s.status})}return yield*s.json.pipe(g.mapError(y=>new d({message:`invalid task response: ${y.message}`,status:s.status})))})}}),dependencies:[B.Default]}){};var xo=i.boolean("json").pipe(i.withDescription("Output machine-readable JSON instead of a human-readable summary")),_o=J.text({name:"id"}).pipe(J.withDescription("Task ID")),$o=e=>{switch(e){case "completed":return "\u2713";case "failed":return "\u2717";default:return "\u2026"}},bo=g.fn("task.renderHuman")(function*(e){yield*O.log(`${$o(e.status)} Task ${e.id}`),yield*O.log(` Type: ${e.type}`),yield*O.log(` Status: ${e.status}`),e.cost!==null&&(yield*O.log(` Cost: ${e.cost}`)),yield*O.log(` Created: ${e.createdAt}`),yield*O.log(` Updated: ${e.updatedAt}`),e.result!==null&&e.result!==void 0&&(yield*O.log(""),yield*O.log("Result:"),yield*O.log(To(JSON.stringify(e.result,null,2),2)));}),To=(e,t)=>{let n=" ".repeat(t);return e.split(`
4
4
  `).map(o=>`${n}${o}`).join(`
5
- `)},Io=J.make("get",{id:bo,json:$o},({id:e,json:t})=>g.gen(function*(){let o=yield*(yield*ye).getTask(e);if(t){yield*O.log(JSON.stringify(o,null,2));return}yield*Do(o);})),pt=J.make("task").pipe(J.withSubcommands([Io]));var Ho=J.make("mynth").pipe(J.withSubcommands([et,nt,ct,pt,tt])),dt=J.run(Ho,{name:"Mynth CLI",version:"0.0.4"});var Lo=en.layer,No=re.mergeAll(te.Default,Lo),Po=oe.Default.pipe(re.provideMerge(No)),Fo=L.Default.pipe(re.provideMerge(Po)),Uo=Y.Default.pipe(re.provideMerge(Fo)),jo=Q.DefaultWithoutDependencies.pipe(re.provideMerge(Uo)),tn=ye.DefaultWithoutDependencies.pipe(re.provideMerge(jo));var ft=process.env.MYNTH_DEBUG==="1"||process.env.MYNTH_DEBUG==="true",qo=ft?Ge.pretty.pipe(re.provide(Ge.minimumLogLevel(sn.Debug))):re.empty,Wo=ft?dt(process.argv).pipe(g.tapErrorCause(e=>g.sync(()=>{console.error("=== MYNTH_DEBUG cause ==="),console.error(JSON.stringify(e,null,2));}))):dt(process.argv);Wo.pipe(g.provide(tn),g.provide(nn.layer),g.provide(qo),on.runMain({disableErrorReporting:!ft}));
5
+ `)},Do=z.make("get",{id:_o,json:xo},({id:e,json:t})=>g.gen(function*(){let o=yield*(yield*ge).getTask(e);if(t){yield*O.log(JSON.stringify(o,null,2));return}yield*bo(o);})),pt=z.make("task").pipe(z.withSubcommands([Do]));var Mo=z.make("mynth").pipe(z.withSubcommands([et,nt,ct,pt,tt])),dt=z.run(Mo,{name:"Mynth CLI",version:"0.0.6"});var Io=Qt.layer,Ho=ie.mergeAll(ee.Default,Io),Lo=ne.Default.pipe(ie.provideMerge(Ho)),No=L.Default.pipe(ie.provideMerge(Lo)),Po=B.Default.pipe(ie.provideMerge(No)),Fo=X.DefaultWithoutDependencies.pipe(ie.provideMerge(Po)),Zt=ge.DefaultWithoutDependencies.pipe(ie.provideMerge(Fo));var ft=process.env.MYNTH_DEBUG==="1"||process.env.MYNTH_DEBUG==="true",Uo=ft?Ge.pretty.pipe(ie.provide(Ge.minimumLogLevel(nn.Debug))):ie.empty,jo=ft?dt(process.argv).pipe(g.tapErrorCause(e=>g.sync(()=>{console.error("=== MYNTH_DEBUG cause ==="),console.error(JSON.stringify(e,null,2));}))):dt(process.argv);jo.pipe(g.provide(Zt),g.provide(en.layer),g.provide(Uo),tn.runMain({disableErrorReporting:!ft}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mynthio/cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Official Mynth CLI",
5
5
  "keywords": [
6
6
  "ai",