otaflow 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/otaflow.mjs +3 -3
  2. package/package.json +2 -2
package/dist/otaflow.mjs CHANGED
@@ -464,9 +464,9 @@ ${c.join(`
464
464
  "updates": {
465
465
  "codeSigningCertificate": "${e}",
466
466
  "codeSigningMetadata": { "keyid": "${t}", "alg": "rsa-v1_5-sha256" }
467
- }`,ZCt=Me.make("init",{keyId:Q5,keyDir:ee.optional(ee.String("key-dir").pipe(ee.withDescription("Where the private key is kept (default ~/.config/otaflow/keys/<app>)"))),certDir:ee.optional(ee.String("cert-dir").pipe(ee.withDescription("Where the certificate is written (default ./certs)"))),years:ee.String("years").pipe(ee.withDescription("Certificate validity in years"),ee.withDefault("10")),force:ee.Boolean("force").pipe(ee.withDescription("Replace an existing key (binaries already shipped keep trusting the old certificate)"),ee.withDefault(!1))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let{root:n,project:o}=yield*qs(),s=Number.parseInt(e.years,10);if(!Number.isInteger(s)||s<1||s>30)return yield*T.fail(new Un({message:"--years must be between 1 and 30."}));let i=Se.getOrElse(e.keyDir,()=>XU(o.appId)),a=Se.getOrElse(e.certDir,()=>t1(n,"certs")),A=t1(i,`${e.keyId}.key.pem`),c=t1(a,e.keyId==="main"?"certificate.pem":`${e.keyId}.certificate.pem`);if((yield*INe(A))&&!e.force)return yield*T.fail(new _u({message:`${A} already exists. Keep it, or pass --force to make a NEW key (installed apps trust the old certificate only).`}));yield*B5("Generating a 2048-bit RSA key and certificate (a few seconds)...");let u=yield*ENe(o.appSlug,s);yield*mNe(A,u.privateKeyPem),yield*CNe(c,u.certificatePem);let l=yield*NNe({apiUrl:t,appId:o.appId,keyId:e.keyId,privateKeyPem:u.privateKeyPem,certificatePem:u.certificatePem}).pipe(T.tapError(()=>B5(`The key was saved (${A}) but registering failed. Retry: otaflow keys register --key ${A} --cert ${c} --key-id ${e.keyId}`)));yield*sB(r,[`Key "${l.keyId}" registered (fingerprint ${l.fingerprint.slice(0,16)}\u2026, valid until ${l.notAfter.slice(0,10)}).`,`Private key: ${A}`," Back it up (a password manager, plus anyone who must publish). Otaflow cannot recover it, and losing it means no updates reach installed apps until a new build ships.",`Certificate: ${c}`,zCt(`./${c.startsWith(n)?c.slice(n.length+1):c}`,e.keyId)].join(`
467
+ }`,ZCt=Me.make("init",{keyId:Q5,keyDir:ee.optional(ee.String("key-dir").pipe(ee.withDescription("Where the private key is kept (default ~/.config/otaflow/keys/<app>)"))),certDir:ee.optional(ee.String("cert-dir").pipe(ee.withDescription("Where the certificate is written (default ./certs)"))),years:ee.String("years").pipe(ee.withDescription("Certificate validity in years"),ee.withDefault("10")),force:ee.Boolean("force").pipe(ee.withDescription("Overwrite the local key file of this id, for a key that was never registered. To replace a registered key, use a new --key-id: ids are never reused"),ee.withDefault(!1))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let{root:n,project:o}=yield*qs(),s=Number.parseInt(e.years,10);if(!Number.isInteger(s)||s<1||s>30)return yield*T.fail(new Un({message:"--years must be between 1 and 30."}));let i=Se.getOrElse(e.keyDir,()=>XU(o.appId)),a=Se.getOrElse(e.certDir,()=>t1(n,"certs")),A=t1(i,`${e.keyId}.key.pem`),c=t1(a,e.keyId==="main"?"certificate.pem":`${e.keyId}.certificate.pem`);if((yield*INe(A))&&!e.force)return yield*T.fail(new _u({message:`${A} already exists. Keep it. To make a replacement key, pass a new --key-id (an id is never reused; installed apps trust the old certificate only). --force overwrites this file and only makes sense if that key was never registered.`}));yield*B5("Generating a 2048-bit RSA key and certificate (a few seconds)...");let u=yield*ENe(o.appSlug,s);yield*mNe(A,u.privateKeyPem),yield*CNe(c,u.certificatePem);let l=yield*NNe({apiUrl:t,appId:o.appId,keyId:e.keyId,privateKeyPem:u.privateKeyPem,certificatePem:u.certificatePem}).pipe(T.tapError(()=>B5(`The key was saved (${A}) but registering failed. Retry: otaflow keys register --key ${A} --cert ${c} --key-id ${e.keyId}`)));yield*sB(r,[`Key "${l.keyId}" registered (fingerprint ${l.fingerprint.slice(0,16)}\u2026, valid until ${l.notAfter.slice(0,10)}).`,`Private key: ${A}`," Back it up (a password manager, plus anyone who must publish). Otaflow cannot recover it, and losing it means no updates reach installed apps until a new build ships.",`Certificate: ${c}`,zCt(`./${c.startsWith(n)?c.slice(n.length+1):c}`,e.keyId)].join(`
468
468
  `),{keyId:l.keyId,fingerprint:l.fingerprint,notAfter:l.notAfter,privateKeyPath:A,certificatePath:c})}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Generate a code-signing key and register its public half")),XCt=Me.make("register",{key:ee.String("key").pipe(ee.withDescription("Path to the RSA private key (PEM)")),cert:ee.String("cert").pipe(ee.withDescription("Path to its code-signing certificate (PEM)")),keyId:Q5},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let{project:n}=yield*qs(),o=yield*ob(e.key,"private key"),s=yield*ob(e.cert,"certificate"),i=yield*NNe({apiUrl:t,appId:n.appId,keyId:e.keyId,privateKeyPem:o,certificatePem:s});yield*sB(r,`Key "${i.keyId}" registered (fingerprint ${i.fingerprint.slice(0,16)}\u2026).`,i)}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Register an existing key and certificate (for example one that already shipped)")),eIt=Me.make("public",{key:ee.optional(ee.String("key").pipe(ee.withDescription("Path to the private key (default: the app's key folder)"))),keyId:Q5},e=>T.gen(function*(){let{json:t}=yield*qr,{project:r}=yield*qs(),n=Se.getOrElse(e.key,()=>t1(XU(r.appId),`${e.keyId}.key.pem`)),o=yield*BNe(yield*ob(n,"private key"));yield*sB(t,o,{keyId:e.keyId,fingerprint:o})})).pipe(Me.withDescription("Print the fingerprint of a local private key")),tIt=Me.make("list",{},()=>T.gen(function*(){let{apiUrl:e,json:t}=yield*qr;yield*T.gen(function*(){let r=yield*wr,{project:n}=yield*qs(),o=yield*Ta(e),{keys:s}=yield*r.listSigningKeys(o,n.appId);yield*sB(t,s.length?s.map(i=>`${i.keyId.padEnd(16)} ${i.fingerprint.slice(0,16)}\u2026 ${i.revokedAt?`revoked ${i.revokedAt.slice(0,10)}`:"active"}`).join(`
469
- `):"No keys registered. Run `otaflow keys init`.",{keys:s})}).pipe(T.provide(rn(e)))})).pipe(Me.withDescription("List the keys registered for this app")),rIt=Me.make("revoke",{keyId:ee.String("key-id").pipe(ee.withDescription("The key to stop accepting, from `keys list`"))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let n=yield*wr,{project:o}=yield*qs(),s=yield*Ta(t);yield*n.revokeSigningKey(s,o.appId,e.keyId),yield*sB(r,`Key "${e.keyId}" revoked: publishes signed with it are refused. Installs that trust its certificate keep running what they run.`,{keyId:e.keyId,revoked:!0})}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Stop accepting publishes signed with a key (account owner)")),DNe=Me.make("keys").pipe(Me.withDescription("Manage the app's code-signing key"),Me.withSubcommands([ZCt,XCt,eIt,tIt,rIt]));import{randomUUID as nIt}from"node:crypto";var LE=(e,t,r)=>hn.log(e?JSON.stringify(r):t),oIt=e=>hn.error(e),Lp=e=>e.slice(0,8),TNe=ee.String("platform").pipe(ee.withDescription("ios"),ee.withDefault("ios")),kNe=Me.make("promote",{updateId:ee.optional(ee.String("update").pipe(ee.withDescription("The preview update to promote (default: preview's head)"))),platform:TNe,wait:ee.Boolean("wait").pipe(ee.withDescription("When approval is needed, wait for the owner or manager to decide"),ee.withDefault(!1)),timeout:ee.String("timeout").pipe(ee.withDescription("Seconds to wait with --wait"),ee.withDefault("600"))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let n=yield*wr,{project:o}=yield*qs(),s=yield*Hu(t),i=yield*Se.match(e.updateId,{onSome:u=>T.succeed(u),onNone:()=>n.channels(s,o.appId).pipe(T.flatMap(u=>{let l=(u.channels.find(f=>f.channel==="preview")?.heads??[]).filter(f=>f.platform===e.platform);return l.length===1&&l[0]?T.succeed(l[0].updateId):T.fail(new Un({message:l.length===0?"Preview has no update to promote.":"Preview has several runtimes: pass --update <id> (see `otaflow list`)."}))}))}),a=yield*n.promote(s,o.appId,{updateId:i,platform:e.platform});if(a.status==="promoted")return yield*LE(r,`Promoted ${Lp(i)} to production.`,a);let A=a.requestId??"";if(yield*LE(r,`Requested: ${Lp(i)} waits for the owner or manager (request ${A}, valid until ${a.expiresAt?.slice(0,16).replace("T"," ")}).`,a),!e.wait)return;yield*oIt("Waiting for a decision...");let c=Date.now()+(Number.parseInt(e.timeout,10)||600)*1e3;for(;Date.now()<c;){yield*T.sleep(as.seconds(3));let l=(yield*n.promotions(s,o.appId)).promotions.find(f=>f.id===A);if(l&&l.status!=="pending")return yield*LE(r,l.status==="approved"?`Approved: ${Lp(i)} is on production.`:`Request ${l.status}${l.decisionNote?`: ${l.decisionNote}`:"."}`,l)}return yield*T.fail(new Un({message:"Still waiting; the request stays open. Check `otaflow promotions list`."}))}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Move a preview update to production (the owner directly; others ask)")),RNe=ee.String("request").pipe(ee.withDescription("The promotion request id (see `otaflow promotions list`)")),S5=(e,t)=>Me.make(e,e==="reject"?{request:RNe,note:ee.optional(ee.String("note").pipe(ee.withDescription("Why")))}:{request:RNe,note:ee.optional(ee.String("note").pipe(ee.withDescription("Unused")))},r=>T.gen(function*(){let{apiUrl:n,json:o}=yield*qr;yield*T.gen(function*(){let s=yield*wr,{project:i}=yield*qs(),a=yield*Hu(n),A=yield*s.decidePromotion(a,i.appId,r.request,e,e==="reject"?Se.getOrUndefined(r.note):void 0);yield*LE(o,`Request ${A.status}.`,A)}).pipe(T.provide(rn(n)))})).pipe(Me.withDescription(t)),sIt=Me.make("list",{status:ee.optional(ee.String("status").pipe(ee.withDescription("pending, approved, rejected, expired or cancelled")))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let n=yield*wr,{project:o}=yield*qs(),s=yield*Hu(t),{promotions:i}=yield*n.promotions(s,o.appId,Se.getOrUndefined(e.status));yield*LE(r,i.length?i.map(a=>`${a.id} ${a.status.padEnd(9)} update ${Lp(a.updateId)} by ${a.requestedBy} ${a.createdAt.slice(0,16).replace("T"," ")}${a.decisionNote?` "${a.decisionNote}"`:""}`).join(`
469
+ `):"No keys registered. Run `otaflow keys init`.",{keys:s})}).pipe(T.provide(rn(e)))})).pipe(Me.withDescription("List the keys registered for this app")),rIt=Me.make("revoke",{keyId:ee.String("key-id").pipe(ee.withDescription("The key to stop accepting, from `keys list`"))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let n=yield*wr,{project:o}=yield*qs(),s=yield*Ta(t);yield*n.revokeSigningKey(s,o.appId,e.keyId),yield*sB(r,`Key "${e.keyId}" revoked: publishes signed with it are refused. Installs that trust its certificate keep running what they run.`,{keyId:e.keyId,revoked:!0})}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Stop accepting publishes signed with a key (account owner)")),DNe=Me.make("keys").pipe(Me.withDescription("Manage the app's code-signing key"),Me.withSubcommands([ZCt,XCt,eIt,tIt,rIt]));import{randomUUID as nIt}from"node:crypto";var LE=(e,t,r)=>hn.log(e?JSON.stringify(r):t),oIt=e=>hn.error(e),Lp=e=>e.slice(0,8),TNe=ee.String("platform").pipe(ee.withDescription("ios"),ee.withDefault("ios")),kNe=Me.make("promote",{updateId:ee.optional(ee.String("update").pipe(ee.withDescription("The preview update to promote (default: preview's head)"))),platform:TNe,wait:ee.Boolean("wait").pipe(ee.withDescription("When approval is needed, wait for the owner or manager to decide"),ee.withDefault(!1)),timeout:ee.String("timeout").pipe(ee.withDescription("Seconds to wait with --wait"),ee.withDefault("600"))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let n=yield*wr,{project:o}=yield*qs(),s=yield*Hu(t),i=yield*Se.match(e.updateId,{onSome:u=>T.succeed(u),onNone:()=>n.channels(s,o.appId).pipe(T.flatMap(u=>{let l=(u.channels.find(f=>f.channel==="preview")?.heads??[]).filter(f=>f.platform===e.platform);return l.length===1&&l[0]?T.succeed(l[0].updateId):T.fail(new Un({message:l.length===0?"Preview has no update to promote.":"Preview has several runtimes: pass --update <id> (see `otaflow list`)."}))}))}),a=yield*n.promote(s,o.appId,{updateId:i,platform:e.platform});if(a.status==="promoted")return yield*LE(r,`Promoted ${Lp(i)} to production.`,a);let A=a.requestId??"";if(yield*LE(r,`Requested: ${Lp(i)} waits for the owner or manager (request ${A}, valid until ${a.expiresAt?.slice(0,16).replace("T"," ")}).`,a),!e.wait)return;yield*oIt("Waiting for a decision...");let c=Date.now()+(Number.parseInt(e.timeout,10)||600)*1e3;for(;Date.now()<c;){yield*T.sleep(as.seconds(3));let l=(yield*n.promotions(s,o.appId)).promotions.find(f=>f.id===A);if(l&&l.status!=="pending")return yield*LE(r,l.status==="approved"?`Approved: ${Lp(i)} is on production.`:`Request ${l.status}${l.decisionNote?`: ${l.decisionNote}`:"."}`,l)}return yield*T.fail(new Un({message:"Still waiting; the request stays open. Check `otaflow promotions list`."}))}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Move a preview update to production (the owner directly; others ask)")),RNe=ee.String("request").pipe(ee.withDescription("The promotion request id (see `otaflow promotions list`)")),S5=(e,t)=>Me.make(e,e==="reject"?{request:RNe,note:ee.optional(ee.String("note").pipe(ee.withDescription("Why")))}:{request:RNe},r=>T.gen(function*(){let{apiUrl:n,json:o}=yield*qr;yield*T.gen(function*(){let s=yield*wr,{project:i}=yield*qs(),a=yield*Hu(n),A=yield*s.decidePromotion(a,i.appId,r.request,e,e==="reject"?Se.getOrUndefined(r.note):void 0);yield*LE(o,`Request ${A.status}.`,A)}).pipe(T.provide(rn(n)))})).pipe(Me.withDescription(t)),sIt=Me.make("list",{status:ee.optional(ee.String("status").pipe(ee.withDescription("pending, approved, rejected, expired or cancelled")))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let n=yield*wr,{project:o}=yield*qs(),s=yield*Hu(t),{promotions:i}=yield*n.promotions(s,o.appId,Se.getOrUndefined(e.status));yield*LE(r,i.length?i.map(a=>`${a.id} ${a.status.padEnd(9)} update ${Lp(a.updateId)} by ${a.requestedBy} ${a.createdAt.slice(0,16).replace("T"," ")}${a.decisionNote?` "${a.decisionNote}"`:""}`).join(`
470
470
  `):"No promotion requests.",{promotions:i})}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("List promotion requests")),vNe=Me.make("promotions").pipe(Me.withDescription("Decide promotion requests"),Me.withSubcommands([sIt,S5("approve","Approve a request: its update goes to production (owner or manager)"),S5("reject","Reject a request, with a note"),S5("cancel","Withdraw your own request")])),FNe=Me.make("rollback",{channel:ee.String("channel").pipe(ee.withDescription("preview or production")),to:ee.optional(ee.String("to").pipe(ee.withDescription("Go back to this earlier update (id)"))),previous:ee.Boolean("previous").pipe(ee.withDescription("Undo the latest update: go back to the one before it"),ee.withDefault(!1)),embedded:ee.Boolean("embedded").pipe(ee.withDescription("Return devices to the JavaScript inside their binary"),ee.withDefault(!1)),runtime:ee.optional(ee.String("runtime").pipe(ee.withDescription("The runtime version (needed when the channel serves several)"))),platform:TNe,message:ee.optional(ee.String("message").pipe(ee.withAlias("m"),ee.withDescription("A short note"))),keyId:ee.String("key-id").pipe(ee.withDescription("Which registered key signs"),ee.withDefault("main")),key:ee.optional(ee.String("key").pipe(ee.withDescription("Path to the private key"))),prepared:ee.Boolean("prepared").pipe(ee.withDescription("Use the rollback that was signed when the update was published, so no private key is needed (the default when the key is not on this machine)"),ee.withDefault(!1))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let n=yield*wr,{project:o}=yield*qs(),s=yield*Hu(t);if([Se.isSome(e.to),e.previous,e.embedded].filter(Boolean).length!==1)return yield*T.fail(new Un({message:"Choose exactly one of --to <id>, --previous or --embedded."}));let a=(yield*n.updates(s,o.appId,{channel:e.channel,limit:100})).updates.filter(E=>E.platform===e.platform),A=[...new Set(a.map(E=>E.runtimeVersion))],c=Se.getOrElse(e.runtime,()=>A[0]??"");if(!c)return yield*T.fail(new Un({message:`${e.channel} has nothing to roll back.`}));if(Se.isNone(e.runtime)&&A.length>1)return yield*T.fail(new Un({message:`${e.channel} serves several runtimes (${A.map(E=>E.slice(0,12)).join(", ")}\u2026): pass --runtime.`}));let u=a.filter(E=>E.runtimeVersion===c),l=e.prepared?void 0:yield*e1({appId:o.appId,keyId:e.keyId,keyFlag:e.key}).pipe(T.result);if(l===void 0||l._tag==="Failure"){if(Se.isSome(e.to))return yield*T.fail(l?._tag==="Failure"?l.failure:new Un({message:"--to needs the signing key: pass --key or use --previous."}));let E=yield*n.rollbackReady(s,o.appId,{channel:e.channel,platform:e.platform,runtimeVersion:c,kind:e.embedded?"embedded":"previous"});return yield*LE(r,`Rolled ${e.channel} back to ${E.to==="embedded"?"the embedded JavaScript":Lp(E.to)} (new update ${Lp(E.updateId)}), with the rollback signed at publish: no key was needed.
471
471
  Devices pick it up on their next launch and run it on the one after.`,E)}let f=l.success,d=new Date().toISOString(),p,g;if(e.embedded)p={kind:"embedded"},g=JSON.stringify({type:"rollBackToEmbedded",parameters:{commitTime:d}});else{let E=e.previous?u[1]?.updateId:Se.getOrUndefined(e.to);if(!E)return yield*T.fail(new Un({message:"There is no earlier update to go back to; use --embedded."}));let y=yield*n.getUpdate(s,o.appId,E,e.channel,e.platform);p={kind:"update",updateId:E},g=JSON.stringify({...JSON.parse(y.body),id:nIt(),createdAt:d})}let h=yield*oB(f,e.keyId,g),m=yield*n.rollback(s,o.appId,{channel:e.channel,platform:e.platform,runtimeVersion:c,keyId:e.keyId,to:p,body:g,signature:h,...Se.isSome(e.message)?{message:e.message.value}:{}});yield*LE(r,`Rolled ${e.channel} back to ${m.to==="embedded"?"the embedded JavaScript":Lp(m.to)} (new update ${Lp(m.updateId)}).
472
472
  Devices pick it up on their next launch and run it on the one after.`,m)}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Go back: re-issue an earlier update, or return to the embedded JavaScript"));import{randomUUID as VNe}from"node:crypto";import{execFile as iIt}from"node:child_process";import{createHash as UNe}from"node:crypto";import{mkdtemp as aIt,readdir as AIt,readFile as x5,rm as cIt}from"node:fs/promises";import{tmpdir as uIt}from"node:os";import{join as r1}from"node:path";import{promisify as lIt}from"node:util";var fIt=lIt(iIt),iB=(e,t,r)=>T.tryPromise({try:async()=>{let{stdout:n}=await fIt(e,[...t],{cwd:r.cwd,env:{...process.env,...r.env},maxBuffer:67108864});return n},catch:n=>{let o=n instanceof Error?n.stderr||n.message:"";return new BA({message:`${r.what} failed.
@@ -484,4 +484,4 @@ ${c.channel}${c.paused?" PAUSED":""}`),c.heads.length===0&&A.push(" no updates
484
484
  Recent updates`);for(let c of a.updates)A.push(` ${n1(c.updateId)} ${c.channel.padEnd(10)} ${c.platform} ${c.origin.padEnd(8)} commit ${n1(c.gitSha)} ${b5(c.publishedAt)}${c.message?` "${c.message}"`:""}`);yield*o1(r,A.join(`
485
485
  `),{channels:i.channels,updates:a.updates})}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Show each channel, its head update, paused state and recent updates")),IIt=Me.make("list",{},()=>T.gen(function*(){let{apiUrl:e,json:t}=yield*qr;yield*T.gen(function*(){let r=yield*wr,n=yield*Ta(e),o=yield*r.listApps(n);yield*o1(t,o.length?o.map(s=>`${s.slug.padEnd(24)} ${s.id} ${s.role}${s.status==="active"?"":` (${s.status})`}`).join(`
486
486
  `):"No apps yet. Create one with `otaflow apps create`.",{apps:o})}).pipe(T.provide(rn(e)))})).pipe(Me.withDescription("List the apps you can see")),yIt=Me.make("create",{name:ee.String("name").pipe(ee.withDescription("Display name")),slug:ee.String("slug").pipe(ee.withDescription("Permanent address name: 3-40 characters of a-z, 0-9 and dashes. It is compiled into builds and never reused")),bundleId:ee.optional(ee.String("bundle-id").pipe(ee.withDescription("iOS bundle identifier")))},e=>T.gen(function*(){let{apiUrl:t,json:r}=yield*qr;yield*T.gen(function*(){let n=yield*wr,o=yield*Ta(t),s=yield*n.createApp(o,{name:e.name,slug:e.slug,...Se.isSome(e.bundleId)?{bundleIdIos:e.bundleId.value}:{}});yield*o1(r,`Created ${s.slug} (${s.id}).
487
- Next: \`otaflow init --app ${s.slug}\` in your project, then \`otaflow keys init\`.`,s)}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Create an app in your account")),jNe=Me.make("apps").pipe(Me.withDescription("List and create apps"),Me.withSubcommands([IIt,yIt]));var zNe=qr.pipe(Me.withDescription("Over-the-air updates for Expo apps"),Me.withSubcommands([Fxe,Uxe,Lxe,jNe,Mxe,bNe,DNe,qNe,$Ne,kNe,vNe,FNe,WNe,KNe]));var Mp=e=>hn.error(e.message).pipe(T.andThen(T.sync(()=>{process.exitCode=1}))),BIt="0.1.0";Me.run(zNe,{version:BIt}).pipe(T.catchTags({ApiFailure:Mp,NotLoggedIn:Mp,LoginFailed:Mp,CredentialStoreFailure:Mp,ProjectNotLinked:Mp,KeyFileFailure:Mp,UsageError:Mp,ToolFailure:Mp}),T.provide(vF.layer),LF.runMain);
487
+ Next: \`otaflow init --app ${s.slug}\` in your project, then \`otaflow keys init\`.`,s)}).pipe(T.provide(rn(t)))})).pipe(Me.withDescription("Create an app in your account")),jNe=Me.make("apps").pipe(Me.withDescription("List and create apps"),Me.withSubcommands([IIt,yIt]));var zNe=qr.pipe(Me.withDescription("Over-the-air updates for Expo apps"),Me.withSubcommands([Fxe,Uxe,Lxe,jNe,Mxe,bNe,DNe,qNe,$Ne,kNe,vNe,FNe,WNe,KNe]));var Mp=e=>hn.error(e.message).pipe(T.andThen(T.sync(()=>{process.exitCode=1}))),BIt="0.1.1";Me.run(zNe,{version:BIt}).pipe(T.catchTags({ApiFailure:Mp,NotLoggedIn:Mp,LoginFailed:Mp,CredentialStoreFailure:Mp,ProjectNotLinked:Mp,KeyFileFailure:Mp,UsageError:Mp,ToolFailure:Mp}),T.provide(vF.layer),LF.runMain);
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "otaflow",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Over-the-air updates for Expo apps: sign, publish, promote and roll back from your terminal.",
6
6
  "bin": {
7
- "otaflow": "./dist/otaflow.mjs"
7
+ "otaflow": "dist/otaflow.mjs"
8
8
  },
9
9
  "scripts": {
10
10
  "dev": "tsx src/main.ts",