everyapp 0.3.0-rc.2 → 0.3.0-rc.4

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 (138) hide show
  1. package/README.md +1 -1
  2. package/dist/access-YWM4BSBD.js +1 -0
  3. package/dist/activity-RTCAUMQS.js +14 -0
  4. package/dist/ai-UOYQFTAX.js +5 -0
  5. package/dist/apps-PEOBCDNU.js +14 -0
  6. package/dist/chunk-26KS6TD6.js +1 -0
  7. package/dist/chunk-3EADVHFN.js +15 -0
  8. package/dist/chunk-4LKLQOM7.js +7 -0
  9. package/dist/{chunk-PNLU5U3P.js → chunk-5IC3ZN52.js} +1 -1
  10. package/dist/{chunk-H7PDL3X5.js → chunk-AMCNAKYU.js} +1 -1
  11. package/dist/{chunk-23HJ5I2A.js → chunk-ASCNE7GS.js} +1 -1
  12. package/dist/{chunk-6OGVO7QK.js → chunk-BCTEWNN4.js} +1 -1
  13. package/dist/{chunk-W3JCQHHU.js → chunk-BK4NK34C.js} +1 -1
  14. package/dist/chunk-BWNZ6F4F.js +2 -0
  15. package/dist/chunk-DCFHBCCW.js +9 -0
  16. package/dist/{chunk-2Y5Q6A5K.js → chunk-DWTJ4OLZ.js} +1 -1
  17. package/dist/chunk-EP3IQ354.js +20 -0
  18. package/dist/{chunk-OP4DYXYI.js → chunk-EP4HRYXP.js} +1 -1
  19. package/dist/{chunk-HOUHG236.js → chunk-EQLY5I46.js} +1 -1
  20. package/dist/{chunk-YHJZMJYZ.js → chunk-F5XK23HV.js} +1 -1
  21. package/dist/{chunk-5PTNYZK6.js → chunk-FB2OC32C.js} +3 -3
  22. package/dist/chunk-FQ2A7YAP.js +57 -0
  23. package/dist/{chunk-OM2HDWVP.js → chunk-GAPQSINH.js} +1 -1
  24. package/dist/chunk-GJK6GAWO.js +1 -0
  25. package/dist/chunk-H4EZTXIK.js +1 -0
  26. package/dist/chunk-HJGZGZFE.js +32 -0
  27. package/dist/{chunk-CCWBPAKH.js → chunk-I7FNTTUY.js} +3 -3
  28. package/dist/chunk-KOBKGQ27.js +91 -0
  29. package/dist/{chunk-PS7S74DG.js → chunk-KQM6ZUX5.js} +1 -1
  30. package/dist/chunk-KVCL64LI.js +1 -0
  31. package/dist/{chunk-4JRIS6S6.js → chunk-LEEUODYR.js} +1 -1
  32. package/dist/{chunk-6PGUCTJ6.js → chunk-LI3YLKLC.js} +1 -1
  33. package/dist/chunk-MEIWV7FJ.js +20 -0
  34. package/dist/{configure-F7UZCJUG.js → chunk-MLTNW3AM.js} +2 -2
  35. package/dist/chunk-NDOEON54.js +4 -0
  36. package/dist/chunk-NW7SJPSP.js +37 -0
  37. package/dist/chunk-O64FPLGX.js +1 -0
  38. package/dist/chunk-ORGV2NNT.js +8 -0
  39. package/dist/chunk-PDPUGYJW.js +47 -0
  40. package/dist/chunk-QFTBDXLF.js +146 -0
  41. package/dist/{chunk-E3EPK27P.js → chunk-QJRZFIYC.js} +2 -2
  42. package/dist/chunk-QKHLRLK3.js +15 -0
  43. package/dist/{chunk-CZXHKPRZ.js → chunk-R2DF3ENZ.js} +1 -1
  44. package/dist/{chunk-IUOBIB5M.js → chunk-R7JOHK7Y.js} +2 -2
  45. package/dist/chunk-RL72BUT4.js +1 -0
  46. package/dist/chunk-RQBSTNL7.js +1 -0
  47. package/dist/{chunk-H6QQMY7B.js → chunk-S2G4G73L.js} +1 -1
  48. package/dist/{chunk-LXYMDZER.js → chunk-SGRGD7L4.js} +1 -1
  49. package/dist/{chunk-T2GD7RHS.js → chunk-TIYSZWP5.js} +1 -1
  50. package/dist/chunk-TUXKBLLJ.js +10 -0
  51. package/dist/chunk-U4ATZJEU.js +6 -0
  52. package/dist/chunk-V7BWZOSZ.js +26 -0
  53. package/dist/{chunk-J35DDCNU.js → chunk-VVKQJSF3.js} +1 -1
  54. package/dist/chunk-WVHJIYQ3.js +14 -0
  55. package/dist/{chunk-ZEXVHB22.js → chunk-XBD7PQN5.js} +1 -1
  56. package/dist/{chunk-N5GQLGEC.js → chunk-YAZHTVUV.js} +1 -1
  57. package/dist/configure-CAWCTPYY.js +1 -0
  58. package/dist/{create-2E5HVWYK.js → create-3ZWJQXPK.js} +2 -2
  59. package/dist/{db-HEQVTDTM.js → db-B7KO37SV.js} +1 -1
  60. package/dist/delete-CB3EPOU4.js +3 -0
  61. package/dist/deploy-5O7LV5OX.js +1 -0
  62. package/dist/dev-4JYCVVOZ.js +86 -0
  63. package/dist/{email-RLZFUFUN.js → email-SWJQXEUR.js} +1 -1
  64. package/dist/{events-2V5VRBWP.js → events-GJV2KVIU.js} +13 -13
  65. package/dist/{groups-HDG5DVFY.js → groups-NNTLSXMK.js} +1 -1
  66. package/dist/index.js +104 -95
  67. package/dist/install-UAG3CNYW.js +1 -0
  68. package/dist/{login-KHJBC64Z.js → login-QEKMLMUB.js} +1 -1
  69. package/dist/mcp-AFGVOM5D.js +1 -0
  70. package/dist/provider-622VM7UZ.js +29 -0
  71. package/dist/release-CXEX6O7F.js +1 -0
  72. package/dist/{restore-FFGYV7NO.js → restore-PGIVEA5P.js} +1 -1
  73. package/dist/secret-3LUDEDUW.js +1 -0
  74. package/dist/setup-PUD375AM.js +1 -0
  75. package/dist/{signin-AP3D4XM4.js → signin-EP5JLAT5.js} +2 -2
  76. package/dist/status-LK6DNLCI.js +5 -0
  77. package/dist/teardown-6RURRSME.js +22 -0
  78. package/dist/update-YJFTOZEV.js +1 -0
  79. package/dist/{workspace-CJC5I66L.js → workspace-VW7ZZRFP.js} +3 -3
  80. package/package.json +5 -5
  81. package/skills/build-every-app/SKILL.md +22 -13
  82. package/skills/build-every-app/patterns.md +16 -3
  83. package/skills/everyapp/SKILL.md +33 -18
  84. package/skills/everyapp/mcp.md +22 -8
  85. package/skills/everyapp/security-review.md +1 -1
  86. package/skills/everyapp/setup/README.md +18 -1
  87. package/skills/everyapp/setup/access.md +11 -7
  88. package/skills/everyapp/setup/ai-gateway.md +117 -0
  89. package/skills/everyapp/setup/ai.md +139 -0
  90. package/skills/everyapp/setup/context.md +2 -2
  91. package/skills/everyapp/setup/dataforseo.md +3 -3
  92. package/skills/everyapp/setup/email.md +1 -1
  93. package/skills/everyapp/setup/gateway.md +3 -3
  94. package/skills/everyapp/setup/google.md +8 -6
  95. package/skills/everyapp/setup/microsoft-365.md +4 -2
  96. package/skills/everyapp/setup/providers.md +27 -9
  97. package/skills/everyapp/setup/remote-mcp.md +1 -1
  98. package/skills/everyapp/setup/signin-apple.md +3 -3
  99. package/skills/everyapp/setup/signin-cloudflare.md +1 -1
  100. package/skills/everyapp/setup/signin-github.md +1 -1
  101. package/skills/everyapp/setup/signin-google.md +2 -2
  102. package/skills/everyapp/setup/signin-microsoft.md +1 -1
  103. package/skills/everyapp/setup/slack.md +2 -0
  104. package/dist/access-LGEN3ABJ.js +0 -1
  105. package/dist/activity-RSD4D66F.js +0 -12
  106. package/dist/apps-4RCFXT74.js +0 -13
  107. package/dist/chunk-AGTCKFRB.js +0 -22
  108. package/dist/chunk-BHMRY53V.js +0 -2
  109. package/dist/chunk-CB4U7H2G.js +0 -4
  110. package/dist/chunk-CZTEDWZ2.js +0 -22
  111. package/dist/chunk-EGEWRFVY.js +0 -1
  112. package/dist/chunk-ELK7QZ2M.js +0 -1
  113. package/dist/chunk-FJJSSVET.js +0 -33
  114. package/dist/chunk-GKBPYVP3.js +0 -25
  115. package/dist/chunk-IFCPALVC.js +0 -46
  116. package/dist/chunk-J455FAEN.js +0 -7
  117. package/dist/chunk-JYKRUS5C.js +0 -1
  118. package/dist/chunk-LYNBZ7RV.js +0 -57
  119. package/dist/chunk-M2QQ2UD7.js +0 -132
  120. package/dist/chunk-NFU4YFXB.js +0 -1
  121. package/dist/chunk-ONLNJXVQ.js +0 -32
  122. package/dist/chunk-RMF4YGD6.js +0 -98
  123. package/dist/chunk-TLRM3P7V.js +0 -15
  124. package/dist/chunk-WOVS3GV5.js +0 -10
  125. package/dist/chunk-ZECBN24L.js +0 -15
  126. package/dist/chunk-ZFCUH5PK.js +0 -7
  127. package/dist/delete-XCSIQIRS.js +0 -3
  128. package/dist/deploy-3QOZEZHC.js +0 -1
  129. package/dist/dev-JL3ZP5BT.js +0 -86
  130. package/dist/install-Z5FYXUYM.js +0 -1
  131. package/dist/mcp-EFDUKJ5V.js +0 -1
  132. package/dist/provider-EOI5PTFH.js +0 -13
  133. package/dist/release-74J5ZIL3.js +0 -1
  134. package/dist/secret-7FKKTLHB.js +0 -1
  135. package/dist/setup-PAKRCAFO.js +0 -1
  136. package/dist/status-RMWNHOEK.js +0 -4
  137. package/dist/teardown-E4JNKAZJ.js +0 -22
  138. package/dist/update-DWVZFNPS.js +0 -1
@@ -0,0 +1 @@
1
+ import{a as l,c as i}from"./chunk-NW7SJPSP.js";import{g as n}from"./chunk-EP3IQ354.js";import"./chunk-O64FPLGX.js";import"./chunk-U4ATZJEU.js";import"./chunk-5IC3ZN52.js";import"./chunk-V7BWZOSZ.js";import"./chunk-N4E5YE4X.js";import"./chunk-BWNZ6F4F.js";import"./chunk-MEIWV7FJ.js";import"./chunk-QJRZFIYC.js";import"./chunk-TUXKBLLJ.js";import"./chunk-PDPUGYJW.js";import"./chunk-WVHJIYQ3.js";import"./chunk-GAPQSINH.js";import"./chunk-4LKLQOM7.js";import"./chunk-GJK6GAWO.js";import"./chunk-26KS6TD6.js";import"./chunk-DWTJ4OLZ.js";import"./chunk-S2G4G73L.js";import"./chunk-BCTEWNN4.js";import"./chunk-KQM6ZUX5.js";import"./chunk-F5XK23HV.js";import"./chunk-YAZHTVUV.js";import"./chunk-BK4NK34C.js";import"./chunk-QKHLRLK3.js";import"./chunk-ORGV2NNT.js";import"./chunk-KVCL64LI.js";import"./chunk-LEEUODYR.js";import"./chunk-ASCNE7GS.js";import"./chunk-3EADVHFN.js";import"./chunk-HJGZGZFE.js";import"./chunk-LI3YLKLC.js";import"./chunk-EQLY5I46.js";import{c as a}from"./chunk-DCFHBCCW.js";import"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import"./chunk-AMCNAKYU.js";import"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import{Yc as r}from"./chunk-FQ2A7YAP.js";async function b(e,p){await r(e,async o=>{let m=a(e.gateway),t=await n(p,{out:o});try{let s=await l({cwd:process.cwd(),manifest:t.manifest,release:t,gateway:m,accountId:e["account-id"],out:o,json:e.json,verbose:e.verbose,allowDeleteDurableObjectData:e["allow-delete-durable-object-data"],allowDeleteWorkflows:e["allow-delete-workflows"],forceDns:e["force-dns"],enableEmailRouting:e["enable-email-routing"],waitForLive:!e["no-wait"],offerInstall:{yes:e.yes}});o.emit({type:"done",...i(s)})}finally{await t.cleanup()}})}export{b as install};
@@ -1 +1 @@
1
- import{a as l,c as m,e as s}from"./chunk-T2GD7RHS.js";import{a as r}from"./chunk-CCWBPAKH.js";import"./chunk-JYKRUS5C.js";import{ac as i,wc as p}from"./chunk-LYNBZ7RV.js";var n="cloudflare.login";async function v(e){await p(e,async o=>{await f({out:o,interactive:r(e.json??!1)}),o.emit({type:"done"})})}async function f({out:e,interactive:o,cf:t=i()}){let a=await l(t);if(a.authenticated){e.emit({type:"step",step:n,status:"skip",message:"Already logged in to Cloudflare.",data:{email:a.email??null,token:!!process.env.CLOUDFLARE_API_TOKEN}});return}let c=await s({cf:t,interactive:o,onDeviceLogin:m(e,n)});e.emit({type:"step",step:n,status:"ok",message:"Logged in to Cloudflare.",data:{email:c.email??null}})}export{f as logInToCloudflare,v as login};
1
+ import{a as l,c as m,e as s}from"./chunk-TIYSZWP5.js";import{a as r}from"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import{Cc as i,Yc as p}from"./chunk-FQ2A7YAP.js";var n="cloudflare.login";async function v(e){await p(e,async o=>{await f({out:o,interactive:r(e.json??!1)}),o.emit({type:"done"})})}async function f({out:e,interactive:o,cf:t=i()}){let a=await l(t);if(a.authenticated){e.emit({type:"step",step:n,status:"skip",message:"Already logged in to Cloudflare.",data:{email:a.email??null,token:!!process.env.CLOUDFLARE_API_TOKEN}});return}let c=await s({cf:t,interactive:o,onDeviceLogin:m(e,n)});e.emit({type:"step",step:n,status:"ok",message:"Logged in to Cloudflare.",data:{email:c.email??null}})}export{f as logInToCloudflare,v as login};
@@ -0,0 +1 @@
1
+ import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}from"./chunk-R7JOHK7Y.js";import"./chunk-PDPUGYJW.js";import"./chunk-WVHJIYQ3.js";import"./chunk-GAPQSINH.js";import"./chunk-ORGV2NNT.js";import"./chunk-KVCL64LI.js";import"./chunk-LEEUODYR.js";import"./chunk-3EADVHFN.js";import"./chunk-EQLY5I46.js";import"./chunk-DCFHBCCW.js";import"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import"./chunk-AMCNAKYU.js";import"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import"./chunk-FQ2A7YAP.js";export{a as add,i as addServer,p as changeTool,e as list,m as listMcp,d as off,c as on,g as refresh,o as refreshApp,b as remove,l as removeApp,f as show,n as showApp,h as tool,k as turnOff,j as turnOn};
@@ -0,0 +1,29 @@
1
+ import{j as we,k as ye}from"./chunk-R7JOHK7Y.js";import{c as fe}from"./chunk-MLTNW3AM.js";import{A as he}from"./chunk-PDPUGYJW.js";import{a as U,c as J,d as ue,f as L,g as ge,h as G,i as me}from"./chunk-WVHJIYQ3.js";import"./chunk-GAPQSINH.js";import"./chunk-ORGV2NNT.js";import"./chunk-KVCL64LI.js";import{a as E}from"./chunk-LEEUODYR.js";import"./chunk-3EADVHFN.js";import"./chunk-EQLY5I46.js";import{c as x,e as D}from"./chunk-DCFHBCCW.js";import{_ as pe,ma as X}from"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import"./chunk-AMCNAKYU.js";import{a as _}from"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import{C as B,Ca as oe,D as O,Hb as k,J as se,Pa as ie,Ra as M,Rc as F,Sa as re,Ta as ae,Ua as le,Va as Y,Yc as P,bc as m,c as g,fc as Z,k as y,vb as ce,wb as q,yc as de}from"./chunk-FQ2A7YAP.js";import V from"node:fs/promises";import K from"node:path";import{stripVTControlCharacters as Ee}from"node:util";import f from"chalk";import{text as xe}from"node:stream/consumers";import{stripVTControlCharacters as be}from"node:util";import S from"chalk";function Oe(e,t){return`${e.replace(/\/$/,"")}/connect/${t}/callback`}function De(e){return e.flow?"both":"app"}function Q(e){return Object.entries(e.scopePolicy?.sets??{}).map(([t,n])=>({name:t,label:n.label,access:n.access,scopes:[...n.scopes]}))}function Fe(e,t,n,o){return t.includes(e.secrets.clientId)?"env":n.has(o)?"uploaded":"none"}function W(e,t,n){let o=Fe(t,n.gatewayNames,n.uploaded,e);return{flow:De(t),redirectUri:Oe(n.gatewayUrl,e),console:t.console,scopeSets:Q(t),client:{source:o,...o==="uploaded"?{clientId:n.uploaded.get(e).clientId}:{}},connections:n.connections.get(e)??0}}function Le(){return Object.entries(B).filter(([,e])=>Y(e)).map(([e])=>e)}function ve(e,t,n){return{id:e,name:t.label,builtIn:n,console:t.console,hosts:[...t.api],settings:[...O(t)],scopeSets:Q(t),...t.hint?{hint:t.hint}:{}}}function Pe(){return[...Le().map(e=>ve(e,B[e],!0)),...ae().map(e=>ve(e,le(e),!1))]}async function Se(e){let t=Pe(),n=F(["ID","NAME","KIND","SCOPE SETS","CONSOLE"],t.map(o=>[o.id,o.name,o.builtIn?"built in":"preset",o.scopeSets.map(c=>c.name).join(", ")||S.dim("-"),o.console]));e.out.emit({type:"done",message:`${t.length} OAuth presets: ${t.filter(o=>o.builtIn).length} built in, ${t.filter(o=>!o.builtIn).length} to register`,display:[...n,"",S.dim(`Add one: ${g} provider add --oauth <id>; a built-in one takes its client only, a preset becomes a registered provider.`)].join(`
2
+ `),data:{presets:t,next:[{command:`${g} provider add --oauth github-oauth`,note:"registers GitHub and prints the redirect URI to register at its console"}]}})}async function Ge(e,t){let n=re[e];if(n)return{kind:"preset",id:e,spec:n};let o=B[e];if(o&&Y(o))return{kind:"catalog",id:e,capability:o};if(o)throw new m("PROVIDER_PRESET_UNKNOWN",`${e} is a built-in ${o.mode==="oauth_client"?"OAuth client the Gateway completes token calls for; apps run its flow themselves":"provider, not an OAuth one"}`,{rerun:`${g} provider presets`,rerunNote:"names every preset and built-in client"});if(/[./\\]/.test(e)){let c=await te(e,{cwd:t.cwd??process.cwd(),fetcher:t.fetch}),s=ie(c);if(s.ok&&s.capability.mode!=="oauth_client")throw new m("PROVIDER_SPEC_INVALID",`${e} is an API-key spec (auth.kind ${s.capability.auth.kind})
3
+ --oauth takes a spec with auth.kind "oauth2-user". Nothing was registered.`,{fix:`register it with ${g} provider add ${e}`});return{kind:"spec",spec:c}}throw new m("PROVIDER_PRESET_UNKNOWN",`No OAuth preset ${e}
4
+ Presets: ${y(Pe().map(c=>c.id))}. A custom provider is a JSON file: ${g} provider add --oauth ./acme.json.`,{rerun:`${g} provider presets`,rerunNote:"lists them with their scope sets"})}async function ze(){if(process.stdin.isTTY)return null;let e=await xe(process.stdin);return e.trim()?e:null}async function Ve(e,t){let n=O(e),o={},c=t.flags["from-env"]?null:await(t.readStdin??ze)();if(c!==null)for(let{name:l,value:r,line:i}of fe(c)){if(!n.includes(l)){let u=/^[A-Z][A-Z0-9_]*$/.test(l)?l:`line ${i} (not shown: it may be a value)`;throw new m("SECRET_NOT_DECLARED",`${e.label}'s client isn't ${u}
5
+ The piped lines set ${y([...n])}. Nothing was set.`,{fix:`pipe ${n.join(" and ")} as NAME=value lines`})}if(!r.trim())throw new m("SECRET_EMPTY",`${l} is empty in the piped input
6
+ Nothing was set.`,{fix:"give it a value, or leave the line out"});o[l]=r}if(t.flags["from-env"]){let l=t.env??process.env;for(let r of n){let i=l[r];i&&!(r in o)&&(o[r]=i)}}let s=t.flags["client-id"]?.trim();return s&&(o[e.secrets.clientId]=s),o}async function Ce(e,t){let{out:n,flags:o}=e,c=await Ge(t,e),s=await D({gateway:x(o.gateway),accountId:o["account-id"],interactive:_(o.json??!1),login:{json:o.json,out:n,step:"login"},createCf:e.createCf}),l=s.gatewayUrl.replace(/^https:\/\//,""),r,i,u=!1,a;if(c.kind==="catalog")r=c.id,i=c.capability,a="catalog",n.emit({type:"step",step:"provider.add",status:"ok",message:`${i.label} (${r}) is built in: no row to write, its client alone is set`,data:{id:r,builtIn:!0}});else{let w=c.kind==="preset"?c.id:"the provider";n.emit({type:"step",step:"provider.add",status:"start",message:`Registering ${w}...`});let A=await J(s.db,{spec:c.spec,deployer:s.deployer,replace:o.replace??!1,install:s.install,now:e.now?.()});if(A.capability.mode!=="oauth_client")throw new Error(`${A.id} isn't an OAuth spec after all`);r=A.id,i=A.capability,u=A.replaced,a="row",n.emit({type:"step",step:"provider.add",status:"ok",message:`${u?"Replaced":"Registered"} ${i.label} (${r}) on ${l}`,data:{id:r,replaced:u}})}let p=[...O(i)],d=Oe(s.gatewayUrl,r);n.emit({type:"step",step:"provider.redirect",status:"ok",message:`Register this redirect URI at ${i.console}: ${d}`,data:{id:r,redirectUri:d,console:i.console}});let h=await Ve(i,e),v=Object.keys(h);if(v.length>0){let w=p.filter(A=>!(A in h));if(w.length>0)throw new m("PROVIDER_CLIENT_INCOMPLETE",`${i.label}'s client needs ${y(w)} too
7
+ Nothing was set; the provider ${a==="row"?"is registered":"is built in"}.`,{fix:`pipe ${w.join(" and ")} as NAME=value lines, export ${w.length===1?"it":"them"} and add --from-env, or run ${g} secret set ${p.join(" ")}`});await he({flags:{...o},out:n,names:p,readValue:async A=>h[A],...e.createCf?{createCf:e.createCf}:{}})}let[C,I,$]=await Promise.all([E(s.cf,k),L(s.db),G(s.db)]),N=W(r,i,{source:a,gatewayUrl:s.gatewayUrl,gatewayNames:C,uploaded:I,connections:$}),T=N.client,b=Q(i),j=[a==="catalog"?`${i.label} (${r}) is built in on ${l}.`:`${u?"Replaced":"Registered"} ${i.label} (${r}) on ${l}.`,`Redirect URI to register at ${i.console}: ${d}`,...b.length>0?[`Scope sets apps may list: ${b.map(w=>`${w.name} (${w.label})`).join(", ")}.`]:[],T.source==="none"?`Its client isn't set yet: ${g} secret set ${p.join(" ")} (or paste it under Admin \u2192 Providers).`:T.source==="env"?`Its client is set${v.length>0?" and live now":""}: people connect at ${s.gatewayUrl}/connect/${r}.`:`Its client was uploaded under Admin \u2192 Providers: people connect at ${s.gatewayUrl}/connect/${r}.`,`Apps list it as capabilities: { ${JSON.stringify(r)}: { access: "read", scopes: [${b.slice(0,2).map(w=>JSON.stringify(w.name)).join(", ")}], why: "..." } }.`];n.emit({type:"done",message:be(j.join(`
8
+ `)),display:[j[0],...j.slice(1).map(w=>S.dim(w))].join(`
9
+ `),data:{id:r,name:i.label,kind:"oauth",source:a,replaced:u,settings:p,...N,...T.source==="none"?{next:[{command:`${g} secret set ${p.join(" ")}`,note:`${i.label}'s client id and secret from ${i.console}; register ${d} there first`}]}:{}}})}function Ae(e){return D({gateway:x(e.flags.gateway),accountId:e.flags["account-id"],interactive:_(e.flags.json??!1),login:{json:e.flags.json,out:e.out,step:"login"},createCf:e.createCf})}function $e(e){return e===null?S.dim("-"):new Date(e).toISOString().slice(0,16).replace("T"," ")}function Re(e,t){return{id:e.id,provider:e.providerId,providerName:t,account:e.account,...e.userId?{userId:e.userId,userEmail:e.userEmail}:{},label:e.label,scopeSets:e.scopeSets,status:e.status,createdAt:new Date(e.createdAt).toISOString(),lastUsedAt:e.lastUsedAt===null?null:new Date(e.lastUsedAt).toISOString(),expiresAt:e.expiresAt===null?null:new Date(e.expiresAt).toISOString(),organizationId:e.organizationId}}async function Ie(e){let{out:t,flags:n}=e,o=await Ae(e),[c,s]=await Promise.all([U(o.db,o.install),n.all?Promise.resolve(null):pe(o.db)]),l=await ge(o.db,{...s?{organizationId:s.organization.id}:{},...n.user?{userEmail:n.user}:{},...n.provider?{providerId:n.provider}:{}}),r=a=>c.get(a)?.label??a,i=F(["PROVIDER","ACCOUNT","LABEL","SCOPE SETS","STATUS","LAST USED","CONNECTED"],l.map(a=>[`${r(a.providerId)} (${a.providerId})`,a.account==="organization"?"the organization":a.userEmail??a.userId??"?",a.label??S.dim("-"),a.scopeSets.join(", ")||S.dim("-"),a.status==="active"?S.green("active"):S.yellow(a.status),$e(a.lastUsedAt),$e(a.createdAt)])),u=n.all?"every organization":s?s.organization.name:"this Gateway";t.emit({type:"done",message:`${l.length} connection${l.length===1?"":"s"} on ${u}`,display:[...l.length>0?i:[S.dim(`No connections${n.provider?` to ${n.provider}`:""}${n.user?` for ${n.user}`:""}. People connect at ${o.gatewayUrl}/connect/<provider>.`)]].join(`
10
+ `),data:{connections:l.map(a=>Re(a,r(a.providerId))),...s?{organizationId:s.organization.id}:{}}})}async function Ne(e,t){let{out:n,flags:o}=e,c=[o.user?"--user":null,o.organization?"--organization":null,o.all?"--all":null].filter(Boolean);if(c.length!==1)throw new m("PROVIDER_CONNECTION_WHICH",c.length===0?`Say whose connection to ${t} to remove
11
+ Nothing was changed.`:`${y(c)} name different connections
12
+ Nothing was changed.`,{fix:`one of --user <email> (a person's), --organization (the organization's own) or --all (every connection to ${t})`});let s=await Ae(e),r=(await U(s.db,s.install)).get(t);if(!r||r.mode!=="oauth_client")throw new m("PROVIDER_NOT_FOUND",r?`${t} isn't an OAuth provider: nobody connects to it`:`${t} isn't a provider this Gateway offers`,{rerun:`${g} provider list`,rerunNote:"names every one"});n.emit({type:"step",step:"provider.disconnect",status:"start",message:`Removing connections to ${r.label}...`});let i=await me(s.db,{providerId:t,providerName:r.label,...o.user?{userEmail:o.user}:{},...o.organization?{organization:!0}:{}},s.deployer,e.now?.());if(i.length===0)throw new m("PROVIDER_CONNECTION_NOT_FOUND",`No connection to ${r.label} ${o.user?`for ${o.user}`:o.organization?"for the organization":"on this Gateway"}
13
+ Nothing was changed.`,{rerun:`${g} provider connections --provider ${t}`,rerunNote:"lists the ones there are"});n.emit({type:"step",step:"provider.disconnect",status:"ok",message:`Removed ${i.length} connection${i.length===1?"":"s"} to ${r.label}`,data:{id:t,removed:i.length}});let u=i.map(p=>p.account==="organization"?"the organization":p.userEmail??p.userId??"someone"),a=[`Removed ${i.length===1?`${u[0]}'s`:`${i.length}`} connection${i.length===1?"":"s"} to ${r.label} (${t}).`,'The Gateway no longer holds the tokens; apps get "not connected" until the person connects again.',`The grant stays at ${r.label} until it is revoked there: ${r.console} (Admin \u2192 Providers \u2192 Disconnect revokes it from the Gateway).`];n.emit({type:"done",message:be(a.join(`
14
+ `)),display:[a[0],...a.slice(1).map(p=>S.dim(p))].join(`
15
+ `),data:{id:t,name:r.label,removed:i.map(p=>Re(p,r.label)),revoked:!1,revokeAt:r.console}})}function ee(e){return e.mode==="oauth_client"}var z=64*1024;async function _t(e,t){await P(e,async n=>{if(e.oauth){if(t)throw new m("PROVIDER_SPEC_INVALID",`--oauth ${e.oauth} names the provider; ${t} was given too
16
+ Nothing was changed.`,{fix:`give one: ${g} provider add --oauth ${e.oauth}, or ${g} provider add ${t}`});await Ce({flags:e,out:n},e.oauth);return}if(!t)throw new m("PROVIDER_SPEC_INVALID",`Give the spec to register
17
+ A JSON file or an https URL for an API provider, or --oauth <preset> for an OAuth one.`,{rerun:`${g} provider presets`,rerunNote:"names the OAuth presets"});await Je({flags:e,out:n},t)})}async function xt(e){await P(e,t=>Se({out:t}))}async function Dt(e){await P(e,t=>Ie({flags:e,out:t}))}async function Ft(e,t){await P(e,n=>Ne({flags:e,out:n},t))}async function Lt(e){await P(e,t=>He({flags:e,out:t}))}async function Gt(e,t){await P(e,n=>qe({flags:e,out:n},t))}async function zt(e,t){await P(e,n=>Ze({flags:e,out:n},t))}async function Vt(e,t){await P(e,n=>Ue({flags:e,out:n},"on",t))}async function Bt(e,t){await P(e,n=>Ue({flags:e,out:n},"off",t))}async function Ue(e,t,n){e.out.emit({type:"step",step:"provider.alias",status:"ok",message:`everyapp provider ${t} is now everyapp mcp ${t}; this alias goes after launch week.`,data:{command:`${g} mcp ${t} ${n}`}}),await(t==="on"?we:ye)(e,n)}function H({flags:e,out:t,createCf:n}){return D({gateway:x(e.gateway),accountId:e["account-id"],interactive:_(e.json??!1),login:{json:e.json,out:t,step:"login"},createCf:n})}async function Te(e){try{return await V.access(K.join(e,"everyapp.config.ts")),!0}catch{return!1}}function R(e,t){return new m("PROVIDER_SPEC_INVALID",e,{fix:t})}async function te(e,{cwd:t,fetcher:n=fetch}){let o;if(/^[a-z][a-z0-9+.-]*:\/\//i.test(e)){let c;try{c=new URL(e)}catch{throw R(`${e} isn't a URL`,"give the spec's https URL, or a path to the file")}if(c.protocol!=="https:")throw R(`${e} isn't https`,"fetch specs over https only, or save the file and give its path");let s;try{s=await n(c,{headers:{accept:"application/json"},redirect:"follow"})}catch(l){throw R(`Couldn't fetch ${e}: ${Z(l).split(`
18
+ `)[0]}`,"check the URL, or save the file and give its path")}if(s.url&&!s.url.startsWith("https://"))throw R(`${e} redirected to a URL that isn't https`,"give the spec's final https URL");if(!s.ok)throw R(`${e} answered ${s.status}`,"check the URL, or save the file and give its path");o=await Be(s,e)}else{let c=K.resolve(t,e),s;try{if((await V.stat(c)).size>z)throw R(`${e} is over ${z/1024} KiB`,"a spec is a short JSON file");s=await V.readFile(c)}catch(l){throw l instanceof m?l:de(l)?R(`No file at ${e}`,"give the path to the spec's JSON file, or its https URL"):l}o=s.toString("utf8")}try{return JSON.parse(o)}catch(c){throw R(`${e} isn't JSON: ${Z(c).split(`
19
+ `)[0]}`,"fix the file, then add it again")}}async function Be(e,t){let n=Number(e.headers.get("content-length")),o=()=>R(`${t} is over ${z/1024} KiB`,"a spec is a short JSON file");if(Number.isFinite(n)&&n>z)throw o();if(!e.body)return"";let c=e.body.getReader(),s=[],l=0;for(;;){let{done:r,value:i}=await c.read();if(r)break;if(l+=i.byteLength,l>z)throw await c.cancel(),o();s.push(i)}return Buffer.concat(s).toString("utf8")}function ke(e){return e.mode==="oauth_client"?y([...e.api]):y([...e.hosts,...e.hostSetting?[`the host ${e.hostSetting} names`]:[]])}function Me(e){if(e.mode==="oauth_client")return"as the bearer token of the person's connection";let{auth:t}=e;switch(t.kind){case"bearer":return`as ${t.scheme??"a bearer"} token`;case"header":return`in the ${t.header} header`;case"query":return`as the ${t.param} query parameter`;case"basic":return"as HTTP Basic"}}async function Je(e,t){let{out:n,flags:o}=e,c=e.cwd??process.cwd(),s=await te(t,{cwd:c,fetcher:e.fetch}),l=await H(e),r=s&&typeof s=="object"&&typeof s.id=="string"?s.id:"the provider";n.emit({type:"step",step:"provider.add",status:"start",message:`Registering ${r}...`});let i=await J(l.db,{spec:s,deployer:l.deployer,replace:o.replace??!1,install:l.install,now:e.now?.()}),{id:u,capability:a}=i,p=[...O(a)],d=l.gatewayUrl.replace(/^https:\/\//,"");n.emit({type:"step",step:"provider.add",status:"ok",message:`${i.replaced?"Replaced":"Registered"} ${a.label} (${u}) on ${d}`,data:{id:u,replaced:i.replaced}});let h=null;await Te(c)&&(h=q(u),await je(c,u,a));let v=p.join(" "),C=[`${i.replaced?"Replaced":"Registered"} ${a.label} (${u}) on ${d}.`,`Its key goes to ${ke(a)} only, ${Me(a)}.`,...i.replaced?["The key set before was for the old spec: set it again before the Gateway sends it."]:[],...h?[`Wrote ${h} for everyapp dev (your key goes in .dev.vars).`]:[],`Apps list it as capabilities: { ${JSON.stringify(u)}: { access: "${a.access[0]}", why: "..." } }.`];n.emit({type:"done",message:Ee(C.join(`
20
+ `)),display:[C[0],...C.slice(1).map(I=>f.dim(I))].join(`
21
+ `),data:{id:u,name:a.label,...ne(a),settings:p,source:"row",replaced:i.replaced,...h?{devFile:h}:{},next:[{command:`${g} secret set ${v}`,note:`${a.label}'s ${a.mode==="oauth_client"?"client":"key"}, from ${a.mode==="oauth_client"?a.console:a.docs}; the Gateway sends it only to ${ke(a)}`}]}})}async function je(e,t,n){let o=K.join(e,ce,`${t}.json`);return await V.mkdir(K.dirname(o),{recursive:!0}),await V.writeFile(o,`${JSON.stringify(M(t,n),null,2)}
22
+ `),o}function We(e){switch(e.mode){case"api_key":return"api key";case"ai":return"ai";case"oauth_client":return"oauth client";case"service":return null}}function ne(e){switch(e.mode){case"api_key":return{hosts:[...e.hosts],...e.hostSetting?{hostSetting:e.hostSetting}:{}};case"ai":return{hosts:[new URL(e.origin).host]};case"oauth_client":return{hosts:[...new Set(e.token.flatMap(t=>t.host?[t.host]:[]))],...e.hostSetting?{hostSetting:e.hostSetting}:{}};case"service":return{hosts:[]}}}function _e(e,t,n){return e.filter(o=>o.manifest?n.mode==="ai"?oe(o.manifest).includes(t):!!(o.manifest.capabilities&&Object.hasOwn(o.manifest.capabilities,t)):!1).map(o=>o.appSlug)}function Ke(e,t,n,o){let c=new Map(e.rows().map(l=>[l.id,l])),s=[];for(let[l,r]of e.entries()){let i=We(r);if(!i)continue;let u=c.get(l);s.push({id:l,name:r.label,kind:i,...ne(r),settings:O(r).map(a=>({name:a,set:t.includes(a)})),bound:e.bound(l),source:u?"row":"catalog",...u?{addedBy:u.addedBy,addedAt:u.addedAt}:{},apps:_e(n,l,r),...ee(r)?W(l,r,{source:u?"row":"catalog",...o}):{}})}return s}async function He(e){let{out:t}=e,n=await H(e),[o,c,s,l,r]=await Promise.all([U(n.db,n.install),E(n.cf,k),X(n.db),L(n.db),G(n.db)]),i=Ke(o,c,s,{gatewayUrl:n.gatewayUrl,gatewayNames:c,uploaded:l,connections:r}),u=F(["ID","NAME","KIND","HOSTS","SETTINGS","SOURCE","APPS"],i.map(d=>[d.id,d.name,d.kind==="oauth client"&&d.flow&&d.flow!=="app"?"oauth":d.kind,[...d.hosts,...d.hostSetting?[`<${d.hostSetting}>`]:[]].join(", ")||f.dim("-"),[d.client?.source==="uploaded"?`client ${f.green("uploaded")}`:d.settings.map(h=>`${h.name} ${h.set?f.green("set"):f.yellow("not set")}`).join(", "),...d.source==="row"&&d.client?.source!=="uploaded"?[d.bound?f.green("bound"):f.yellow("not bound")]:[],...d.connections!==void 0?[`${d.connections} connected`]:[]].join(", "),d.source==="row"?`registered by ${d.addedBy??"someone"}`:f.dim("catalog"),d.apps.join(", ")||f.dim("-")])),a=i.filter(d=>d.source==="row").length,p=o.rejected.map(({id:d,code:h,message:v})=>({id:d,code:h,message:v}));t.emit({type:"done",message:`${i.length} providers: ${i.length-a} built in, ${a} registered`,display:[...u,...p.length>0?["",f.yellow(`The Gateway doesn't serve ${y(p.map(d=>d.id))}:`),...p.map(d=>f.dim(` ${d.id}: ${d.message} (${d.code})`))]:[],"",f.dim(`MCP apps: ${g} mcp list`)].join(`
23
+ `),data:{providers:i,...p.length>0?{rejected:p}:{},mcp:`${g} mcp list`}})}function Ye(e,t){let n=[...O(t)];switch(t.mode){case"api_key":return M(e,t);case"ai":return{id:e,kind:"ai",name:t.label,...t.description?{description:t.description}:{},docs:t.docs,...t.api?{api:t.api}:{},...t.hint?{hint:t.hint}:{},settings:n,hosts:[new URL(t.origin).host],routes:[...t.inferenceRoutes],...t.setup?{setup:t.setup}:{}};case"oauth_client":return t.flow&&!t.setup?M(e,t):{id:e,kind:"oauth client",name:t.label,docs:t.console,settings:n,tokenEndpoints:se(t).split(", "),authorize:{url:t.authorize.url,scopes:[...t.authorize.scopes]},redirectPaths:[...t.redirectPaths],...t.scopePolicy?{scopeSets:Object.keys(t.scopePolicy.sets)}:{},...t.hostSetting?{hostSetting:t.hostSetting}:{},...t.setup?{setup:t.setup}:{}};case"service":return{id:e,name:t.label}}}async function qe(e,t){let{out:n,flags:o}=e,c=e.cwd??process.cwd();if(o.save&&!await Te(c))throw new m("MANIFEST_NOT_FOUND",`No everyapp.config.ts in this directory
24
+ --save writes the spec where everyapp dev reads it, in an app's directory. Nothing was written.`,{fix:"run this from the app's directory"});let s=await H(e),[l,r,i,u,a]=await Promise.all([U(s.db,s.install),X(s.db),E(s.cf,k),L(s.db),G(s.db)]),p=l.get(t);if(!p||p.mode==="service"){let b=l.rejected.find(j=>j.id===t);throw new m("PROVIDER_NOT_FOUND",p?`${t} is a Gateway service, not a provider
25
+ Apps call it with its own helper (sendEmail(), listMembers()).`:b?`The Gateway doesn't serve ${t}: ${b.message}`:`${t} isn't a provider this Gateway offers`,{rerun:`${g} provider list`,rerunNote:"names every one"})}let d=l.rows().find(b=>b.id===t),h=Ye(t,p),v=null;if(o.save){if(!d||p.mode!=="api_key")throw new m("PROVIDER_BUILTIN",`${t} is built in
26
+ everyapp dev reaches it without a file. Nothing was written.`,{fix:`put its key in .dev.vars: ${O(p).join(", ")}`});await je(c,t,p),v=q(t)}let C=_e(r,t,p),I=l.bound(t),$=ee(p)?W(t,p,{source:d?"row":"catalog",gatewayUrl:s.gatewayUrl,gatewayNames:i,uploaded:u,connections:a}):null,N=$?.client,T=[d?`Registered by ${d.addedBy??"someone"}${d.addedAt?` on ${new Date(d.addedAt).toISOString().slice(0,10)}`:""}; ${$?N?.source==="uploaded"?"its client was uploaded under Admin \u2192 Providers":I?"its client is bound to these endpoints":"its client isn't set for these endpoints yet":I?"its key is bound to these hosts":"its key isn't set for these hosts yet"}.`:$?`Built in; its client is ${N?.source==="env"?"set":N?.source==="uploaded"?"uploaded under Admin \u2192 Providers":"not set"}.`:"Built in.",...$?[`Redirect URI to register at ${String($.console)}: ${String($.redirectUri)}`,`${String($.connections)} connected; people connect at ${s.gatewayUrl}/connect/${t}.`]:[],C.length>0?`Used by ${y(C)}.`:"No app lists it yet.",...v?[`Wrote ${v} for everyapp dev.`]:[]];n.emit({type:"done",message:`${p.label} (${t})`,display:[JSON.stringify(h,null,2),"",...T.map(b=>f.dim(b))].join(`
27
+ `),data:{provider:h,source:d?"row":"catalog",bound:I,...$??{},addedBy:d?.addedBy??null,addedAt:d?.addedAt??null,apps:C,...v?{saved:v}:{},...d&&!I&&N?.source!=="uploaded"?{next:[{command:`${g} secret set ${O(p).join(" ")}`,note:`${p.label}'s key; the Gateway sends it only to ${y(ne(p).hosts)}`}]}:{}}})}async function Ze(e,t){let{out:n,flags:o}=e,c=await H(e);n.emit({type:"step",step:"provider.remove",status:"start",message:`Removing ${t}...`});let s=await ue(c.db,t,c.deployer,{force:o.force??!1,now:e.now?.()}),l=await E(c.cf,k),r=s.settings.map(p=>({name:p,set:l.includes(p)})),i=r.filter(p=>p.set).map(p=>p.name),u=c.gatewayUrl.replace(/^https:\/\//,"");n.emit({type:"step",step:"provider.remove",status:"ok",message:`Removed ${s.name} (${t}) from ${u}`,data:{id:t}});let a=[`Removed ${s.name} (${t}) from ${u}.`,"The Gateway stops serving it within 30 seconds.",...s.connections>0?[`${s.connections} connection${s.connections===1?"":"s"} to it ${s.connections===1?"was":"were"} deleted (the grants stay at the provider until revoked there).`]:[],...s.client?["The client uploaded under Admin \u2192 Providers was deleted with it."]:[],...s.apps.length>0?[`${y(s.apps)} ${s.apps.length===1?"lists":"list"} it: ${s.apps.length===1?"its":"their"} calls to it fail until it is registered again.`]:[],...i.length>0?[`${y(i)} ${i.length===1?"stays":"stay"} on the Gateway, read by nothing: ${g} secret unset ${i.join(" ")} removes ${i.length===1?"it":"them"}.`]:[]];n.emit({type:"done",message:Ee(a.join(`
28
+ `)),display:[a[0],...a.slice(1).map(p=>f.dim(p))].join(`
29
+ `),data:{id:t,name:s.name,removed:!0,settings:r,apps:s.apps,connections:s.connections,client:s.client,...i.length>0?{next:[{command:`${g} secret unset ${i.join(" ")}`,note:"the key nothing reads any more"}]}:{}}})}export{z as MAX_SPEC_BYTES,_t as add,Je as addFromSpec,Dt as connections,Ft as disconnect,Lt as list,He as listProviders,Bt as off,Vt as on,xt as presets,te as readSpecSource,zt as remove,Ze as removeRegistered,Ue as runMcpAlias,Gt as show,qe as showProvider};
@@ -0,0 +1 @@
1
+ import{a as v,b as g,c as k,d as w,f as A}from"./chunk-EP3IQ354.js";import"./chunk-U4ATZJEU.js";import"./chunk-BCTEWNN4.js";import"./chunk-BK4NK34C.js";import{f as h,h as b}from"./chunk-HJGZGZFE.js";import{a as y}from"./chunk-AMCNAKYU.js";import{Bc as d,Cc as u,Yc as f,c as o,uc as c}from"./chunk-FQ2A7YAP.js";import C from"node:path";async function E({cwd:t,manifest:e,outFile:a,out:s,verbose:p=!1,createCf:R=()=>u(),installDeps:$=d}){e.install!==!1&&(s.emit({type:"step",step:"app.install",status:"start",message:"Installing dependencies..."}),await $({cwd:t,description:"Installing dependencies...",install:e.install,verbose:p}),s.emit({type:"step",step:"app.install",status:"ok"})),s.emit({type:"step",step:"app.build",status:"start",message:"Building..."}),await g({cwd:t,cf:R(),buildCommand:e.build,verbose:p});let l=h(e);await k({cwd:t,inputs:{...l,helperWorkers:(e.workers??[]).map(n=>b(l.workerName,n.name)),declared:e.resources}}),s.emit({type:"step",step:"app.build",status:"ok",message:"Built the app and checked its Build Output."}),await v({cwd:t,manifest:e,out:s});let i=C.resolve(t,a),m=C.isAbsolute(a)||a.startsWith(".")?a:`./${a}`,B=await c(t).then(n=>n.version,()=>"unknown"),r=await A({cwd:t,manifest:e,outFile:i,cfVersion:B});return s.emit({type:"step",step:"app.release",status:"ok",message:`Packed ${e.id} ${r.version} (${r.sha256.slice(0,12)}) into ${i}.`,data:{tarball:i,shown:m,appId:e.id,version:r.version,sha256:r.sha256}}),{tarball:i,shown:m,version:r.version,sha256:r.sha256}}async function H(t){await f(t,async e=>{let a=process.cwd(),s=await E({cwd:a,manifest:await y(a),outFile:t.out??w,out:e,verbose:t.verbose});e.emit({type:"done",data:{...s,next:[{command:`${o} install ${s.shown}`,note:"installs it on your Gateway"},{command:`${o} install <repository url>`,note:"installs it on any Gateway, once the tarball is attached to a GitHub release of the app's repository"}]}})})}export{H as release};
@@ -1,3 +1,3 @@
1
- import{a as A,b as P,d as v,e as C}from"./chunk-6PGUCTJ6.js";import{c as u}from"./chunk-HOUHG236.js";import{e as R,ha as h,qa as k,sa as E}from"./chunk-RMF4YGD6.js";import"./chunk-NFU4YFXB.js";import"./chunk-T2GD7RHS.js";import{a as T}from"./chunk-H7PDL3X5.js";import{a as w,i as l}from"./chunk-CCWBPAKH.js";import"./chunk-JYKRUS5C.js";import{Bb as c,f as b,jc as g,uc as I,vc as $}from"./chunk-LYNBZ7RV.js";async function K(e,o){let r=I(e.json);await $(r,async()=>{await x({cwd:process.cwd(),appId:o,gateway:e.gateway??!1,accountId:e["account-id"],yes:e.yes??!1,json:e.json??!1,out:r})})}async function x({cwd:e,appId:o,gateway:r,accountId:O,yes:j,json:m,out:n,createCf:D,interactive:y=w(m),confirm:S}){if(r&&o)throw new c("RESTORE_TARGET_TWICE",`Pass ${o} or --gateway, not both`,{data:{fix:"drop one of them"}});let a=r?null:o??(await T(e)).id,{cf:_,db:s}=await E({gateway:k(),accountId:O,interactive:y,login:{json:m,out:n,step:"db.login"},createCf:D}),d=a?P((await h(s,a)).workerName):A,i=a?`${a}'s database`:"the Gateway's database",t=await v(s,d);if(!t)throw new c("NO_RESTORE_POINT",`No restore point for ${i}
1
+ import{a as A,b as P,d as v,e as C}from"./chunk-LI3YLKLC.js";import{c as u}from"./chunk-EQLY5I46.js";import{c as k,e as E}from"./chunk-DCFHBCCW.js";import{e as R,ga as h}from"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import{a as T}from"./chunk-AMCNAKYU.js";import{a as w,i as l}from"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import{Fc as g,Wc as I,Xc as $,bc as c,f as b}from"./chunk-FQ2A7YAP.js";async function K(e,o){let r=I(e.json);await $(r,async()=>{await x({cwd:process.cwd(),appId:o,gateway:e.gateway??!1,accountId:e["account-id"],yes:e.yes??!1,json:e.json??!1,out:r})})}async function x({cwd:e,appId:o,gateway:r,accountId:O,yes:j,json:m,out:n,createCf:D,interactive:y=w(m),confirm:S}){if(r&&o)throw new c("RESTORE_TARGET_TWICE",`Pass ${o} or --gateway, not both`,{data:{fix:"drop one of them"}});let a=r?null:o??(await T(e)).id,{cf:_,db:s}=await E({gateway:k(),accountId:O,interactive:y,login:{json:m,out:n,step:"db.login"},createCf:D}),d=a?P((await h(s,a)).workerName):A,i=a?`${a}'s database`:"the Gateway's database",t=await v(s,d);if(!t)throw new c("NO_RESTORE_POINT",`No restore point for ${i}
2
2
  everyapp saves one when ${a?"a deploy":"setup or a Gateway upgrade"} migrates a database that has data.`);let p={appId:a,databaseId:t.databaseId,bookmark:t.bookmark,migrations:t.migrations,createdAt:t.createdAt,createdBy:t.createdBy};await l({yes:j,interactive:y,confirm:S,question:`Put ${i} back to before ${G(t)}?`,unattended:`Restoring ${i} needs a person at a terminal to confirm, or --yes
3
3
  It undoes ${G(t)}; anything written to it since is lost too.`,context:["Anything written to the database since then is lost too."],data:p});let f=await u({db:s,id:d,purpose:"db.restore",...a?{app:a}:{},out:n,step:"db.lease"});try{await f.check(),n.emit({type:"step",step:"db.restore",status:"start",data:p}),await R(_,t.databaseId,t.bookmark),await C(s,d),n.emit({type:"step",step:"db.restore",status:"ok",message:`Restored ${i} to before ${t.migrations.join(", ")}.`,data:p})}finally{await f.release()}n.emit({type:"done",data:p})}function G(e,o=Date.now()){let r=e.createdBy.startsWith("api-token:")?"an API token":e.createdBy;return`${b(e.migrations.length,"migration")} (${e.migrations.join(", ")}) applied ${g(o-e.createdAt)} ago by ${r}`}export{K as restore,x as restoreDatabase};
@@ -0,0 +1 @@
1
+ import{A as e,B as f,C as g,D as h,E as i,F as j,G as k,H as l,w as a,x as b,y as c,z as d}from"./chunk-PDPUGYJW.js";import"./chunk-WVHJIYQ3.js";import"./chunk-GAPQSINH.js";import"./chunk-ORGV2NNT.js";import"./chunk-KVCL64LI.js";import"./chunk-LEEUODYR.js";import"./chunk-3EADVHFN.js";import"./chunk-EQLY5I46.js";import"./chunk-DCFHBCCW.js";import"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import"./chunk-AMCNAKYU.js";import"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import"./chunk-FQ2A7YAP.js";export{j as ManifestSource,g as applyAppSettings,f as applyGatewaySettings,e as applySecretValues,l as doneData,c as list,i as listSecrets,a as set,d as setSecrets,k as storedManifestOf,b as unset,h as unsetSecrets};
@@ -0,0 +1 @@
1
+ import{a,b,c}from"./chunk-XBD7PQN5.js";import"./chunk-V7BWZOSZ.js";import"./chunk-N4E5YE4X.js";import"./chunk-MEIWV7FJ.js";import"./chunk-QJRZFIYC.js";import"./chunk-TUXKBLLJ.js";import"./chunk-PDPUGYJW.js";import"./chunk-WVHJIYQ3.js";import"./chunk-GAPQSINH.js";import"./chunk-DWTJ4OLZ.js";import"./chunk-S2G4G73L.js";import"./chunk-BCTEWNN4.js";import"./chunk-YAZHTVUV.js";import"./chunk-BK4NK34C.js";import"./chunk-QKHLRLK3.js";import"./chunk-ORGV2NNT.js";import"./chunk-KVCL64LI.js";import"./chunk-LEEUODYR.js";import"./chunk-ASCNE7GS.js";import"./chunk-3EADVHFN.js";import"./chunk-HJGZGZFE.js";import"./chunk-LI3YLKLC.js";import"./chunk-EQLY5I46.js";import"./chunk-DCFHBCCW.js";import"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import"./chunk-AMCNAKYU.js";import"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import"./chunk-FQ2A7YAP.js";export{c as reportGatewayResult,b as runGatewayCommand,a as setup};
@@ -1,8 +1,8 @@
1
- import{B as D,q as F,r as U}from"./chunk-IFCPALVC.js";import"./chunk-WOVS3GV5.js";import{a as E,n as se}from"./chunk-OM2HDWVP.js";import"./chunk-ZFCUH5PK.js";import"./chunk-4JRIS6S6.js";import"./chunk-ZECBN24L.js";import"./chunk-HOUHG236.js";import{$ as te,B as ne,qa as ie,sa as ae,y as ee}from"./chunk-RMF4YGD6.js";import"./chunk-NFU4YFXB.js";import"./chunk-T2GD7RHS.js";import"./chunk-H7PDL3X5.js";import{a as G,f as B,g as J,h as X,i as Z}from"./chunk-CCWBPAKH.js";import"./chunk-JYKRUS5C.js";import{Bb as b,N as T,O as N,P as W,Q,R as Y,U as C,c as h,k as p,oc as A,pc as x,wc as P}from"./chunk-LYNBZ7RV.js";import{stripVTControlCharacters as le}from"node:util";import c from"chalk";async function Ue(e,s){await P(e,i=>he({flags:e,out:i},s))}async function De(e){await P(e,s=>Se({flags:e,out:s}))}async function je(e,s){await P(e,i=>ye({flags:e,out:i},s))}async function Le(e,s){await P(e,i=>$e({flags:e,out:i},s))}function O({flags:e,out:s,createCf:i}){return ae({gateway:ie(e.gateway),accountId:e["account-id"],interactive:G(e.json??!1),login:{json:e.json,out:s,step:"login"},createCf:i})}function K(e){let s=e.trim().toLowerCase();if(Y(s))return W[s];throw new b("SIGNIN_UNKNOWN_PROVIDER",`${e} isn't a sign-in provider
1
+ import{B as D,q as F,r as U}from"./chunk-PDPUGYJW.js";import"./chunk-WVHJIYQ3.js";import{a as E,n as se}from"./chunk-GAPQSINH.js";import"./chunk-ORGV2NNT.js";import"./chunk-KVCL64LI.js";import"./chunk-LEEUODYR.js";import"./chunk-3EADVHFN.js";import"./chunk-EQLY5I46.js";import{c as ie,e as ae}from"./chunk-DCFHBCCW.js";import{A as ne,_ as te,w as ee}from"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import"./chunk-AMCNAKYU.js";import{a as G,f as B,g as J,h as X,i as Z}from"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import{Qc as A,Rc as x,S as T,T as N,U as W,V as Q,W as Y,Yc as P,Z as C,bc as b,c as h,k as p}from"./chunk-FQ2A7YAP.js";import{stripVTControlCharacters as le}from"node:util";import c from"chalk";async function Ue(e,s){await P(e,i=>he({flags:e,out:i},s))}async function De(e){await P(e,s=>Se({flags:e,out:s}))}async function je(e,s){await P(e,i=>ye({flags:e,out:i},s))}async function Le(e,s){await P(e,i=>$e({flags:e,out:i},s))}function O({flags:e,out:s,createCf:i}){return ae({gateway:ie(e.gateway),accountId:e["account-id"],interactive:G(e.json??!1),login:{json:e.json,out:s,step:"login"},createCf:i})}function K(e){let s=e.trim().toLowerCase();if(Y(s))return W[s];throw new b("SIGNIN_UNKNOWN_PROVIDER",`${e} isn't a sign-in provider
2
2
  The Gateway offers ${p([...T])}; oidc is any OpenID Connect issuer (Okta, Entra ID, Keycloak, Auth0\u2026).`,{rerun:`${h} signin list`,data:{providers:[...T]}})}async function de(e,s,i,t){let n=await te(e);if(!n)return;let m=await ne(e,{organizationId:n.organization.id,type:i,actor:ee(s),data:{provider:t.id,label:t.label}});m.length>0&&await e.run(m)}var u=e=>`Sign in with ${e.label}`,L=e=>`${se}/${e.setup}`,V=e=>e.status==="incomplete"?"needs setup":e.status;function ce(e){return e==="on"?c.green("on"):e==="needs setup"?c.yellow("needs setup"):c.dim("off")}function q(e,s,i){e.emit({type:"done",message:le(s.join(`
3
3
  `)),display:[s[0],...s.slice(1).map(t=>c.dim(t))].join(`
4
4
  `),data:i})}var R={clientId:"--client-id",clientSecret:"--client-secret-from-env",issuer:"--issuer",label:"--label",tenant:"--tenant"},oe={clientId:"<client id>",clientSecret:"<VAR>",issuer:"<issuer url>",label:"<button text>",tenant:"<tenant id>"};function pe(e){return{clientId:e["client-id"],issuer:e.issuer,label:e.label,tenant:e.tenant}}var j=e=>/^[\w@%+=:,./-]+$/.test(e)?e:`'${e.replace(/'/g,"'\\''")}'`;function fe(e,s,i){let t=s.replace(/\s+/g," ").slice(0,80),n=U(e.role),m=e.role==="issuer"?"SIGNIN_ISSUER_INVALID":e.role==="tenant"?"SIGNIN_TENANT_INVALID":"SETTING_VALUE_INVALID";throw new b(m,`${e.name} can't be "${t}"
5
- ${i}`,{...n?{fix:`use ${n}`}:{},data:{name:e.name}})}function re(e,s){let i=s.trim(),t=F(e.role,i);return t&&fe(e,i,t),i}async function we(e,{canKeep:s,set:i}){let t=U(e.role),n=i?"Set already: Enter keeps it.":s?"Optional: Enter leaves it unset.":null,m=[e.what,...t?[`Takes ${t}`]:[],...n?[n]:[]];return e.plain?(await B(e.name,{context:m,validate:l=>{let a=l.trim();return a?F(e.role,a):s?null:"Enter a value."}})).trim():(await J(`${e.name} (hidden)`,{context:m,optional:s})).trim()}async function he(e,s){let{out:i,flags:t}=e,n=K(s),m=new Set(n.settings.map(o=>o.role)),g=pe(t),l=Object.keys(g).filter(o=>g[o]!==void 0&&!m.has(o)).map(o=>R[o]);if(l.length>0){let o=l.map(r=>r==="--tenant"?"microsoft":"oidc");throw new b("SIGNIN_FLAG_NOT_APPLICABLE",`${p(l)} ${l.length===1?"isn't a setting":"aren't settings"} of ${u(n)}
5
+ ${i}`,{...n?{fix:`use ${n}`}:{},data:{name:e.name}})}function re(e,s){let i=s.trim(),t=F(e.role,i);return t&&fe(e,i,t),i}async function we(e,{canKeep:s,set:i}){let t=U(e.role),n=i?"Set already: Enter keeps it.":s?"Optional: Enter leaves it unset.":null,m=[e.what,...t?[`Takes ${t}`]:[],...n?[n]:[]];return e.plain?(await B(e.name,{context:m,validate:l=>{let a=l.trim();return a?F(e.role,a):s?null:"Enter a value."}})).trim():(await J(`${e.name}`,{context:m,optional:s})).trim()}async function he(e,s){let{out:i,flags:t}=e,n=K(s),m=new Set(n.settings.map(o=>o.role)),g=pe(t),l=Object.keys(g).filter(o=>g[o]!==void 0&&!m.has(o)).map(o=>R[o]);if(l.length>0){let o=l.map(r=>r==="--tenant"?"microsoft":"oidc");throw new b("SIGNIN_FLAG_NOT_APPLICABLE",`${p(l)} ${l.length===1?"isn't a setting":"aren't settings"} of ${u(n)}
6
6
  ${p(l.map((r,S)=>`${r} is ${o[S]}'s`))}; ${n.id} reads ${p(n.settings.map(r=>r.name))}.`,{fix:`drop ${p(l)}`,data:{provider:n.id,flags:l}})}let a=new Map;for(let o of n.settings){if(o.role==="clientSecret")continue;let r=g[o.role]?.trim();r&&a.set(o.name,re(o,r))}let f=n.settings.find(o=>o.role==="clientSecret"),w=t["client-secret-from-env"]?.trim();if(w){let o=process.env[w]?.trim();if(!o)throw new b("SECRET_EMPTY",`$${w} is empty or not set`,{fix:`export ${w} first, or name another variable with --client-secret-from-env`,data:{name:f.name}});a.set(f.name,o)}let v=await O(e),y=new Set(await E(v.cf)),d=N(v.gatewayUrl,n.id),z=e.interactive??G(t.json??!1),$=n.settings.filter(o=>o.required&&!a.has(o.name)&&!y.has(o.name));if($.length>0&&!z){let o=[`${h} signin add ${n.id}`,...n.settings.flatMap(r=>{if(r.role==="clientSecret")return a.has(r.name)||$.includes(r)?[`${R.clientSecret} ${w||oe.clientSecret}`]:[];let S=a.get(r.name);return S?[`${R[r.role]} ${j(S)}`]:$.includes(r)?[`${R[r.role]} ${oe[r.role]}`]:[]}),...t.gateway?[`--gateway ${j(t.gateway)}`]:[],...t["account-id"]?[`--account-id ${j(t["account-id"])}`]:[],...t.json?["--json"]:[]].join(" ");throw new b("SIGNIN_VALUE_REQUIRED",`${u(n)} needs ${p($.map(r=>r.name))}, and there's no terminal to ask at
7
7
  Give ${p($.map(r=>R[r.role]))} (the secret from an environment variable, never the command line), or run this at a terminal.`,{rerun:o,data:{provider:n.id,missing:$.map(r=>r.name),flags:$.map(r=>R[r.role]),callbackUrl:d}})}if(z){X([c.bold(u(n)),`Create the ${n.appNoun} at ${n.console}`,`Register this redirect URL with it: ${c.cyan(d)}`,c.dim(`Guide: ${L(n)}`)].join(`
8
8
  `));let o=e.readValue??we;for(let r of n.settings){if(a.has(r.name))continue;let S=y.has(r.name),_=(await o(r,{canKeep:!r.required||S,set:S})).trim();if(_)a.set(r.name,r.plain?re(r,_):_);else if(r.required&&!S)throw new b("SIGNIN_VALUE_REQUIRED",`${u(n)} needs ${r.name}
@@ -0,0 +1,5 @@
1
+ import{a as Je,f as Qe,g as Ze}from"./chunk-O64FPLGX.js";import"./chunk-U4ATZJEU.js";import{a as Xe}from"./chunk-N4E5YE4X.js";import{a as ke,d as Ee,f as Re}from"./chunk-QJRZFIYC.js";import{a as Fe}from"./chunk-WVHJIYQ3.js";import{a as ee,b as xe,d as _e,e as Ue,j as Te,l as Ge,p as We}from"./chunk-GAPQSINH.js";import{b as qe}from"./chunk-EP4HRYXP.js";import{b as Be,h as Ve,i as Ke,l as Ye}from"./chunk-S2G4G73L.js";import{p as ze}from"./chunk-BCTEWNN4.js";import{f as je}from"./chunk-KQM6ZUX5.js";import"./chunk-F5XK23HV.js";import"./chunk-YAZHTVUV.js";import{b as He}from"./chunk-BK4NK34C.js";import{K as be,O as Ne,P as Ie,b as fe,e as ge,l as he,v as we,x as Se,y as ve}from"./chunk-QKHLRLK3.js";import"./chunk-ORGV2NNT.js";import{c as Me}from"./chunk-KVCL64LI.js";import"./chunk-LEEUODYR.js";import{a as Le}from"./chunk-ASCNE7GS.js";import{c as Z}from"./chunk-3EADVHFN.js";import"./chunk-HJGZGZFE.js";import"./chunk-LI3YLKLC.js";import{a as ye,b as B}from"./chunk-EQLY5I46.js";import{d as De}from"./chunk-DCFHBCCW.js";import{ka as Pe,ma as Ce}from"./chunk-KOBKGQ27.js";import{b as X}from"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import{Hb as pe,K as de,Mc as Ae,Nc as $e,Qc as J,Rc as Q,Yc as Oe,bc as me,c as v,f as R,fc as q,g as U,ha as Y,ia as j,ja as ue,k as w,la as ce}from"./chunk-FQ2A7YAP.js";var T=e=>Number(e?.[0]?.n??0);async function et(e,t,n=Date.now()){let[r,o,i,s,d]=await e.run([{sql:"SELECT count(*) AS n FROM members WHERE organization_id = ?",params:[t]},{sql:"SELECT count(*) AS n FROM invitations WHERE organization_id = ? AND status = 'pending' AND expires_at > ?",params:[t,n]},{sql:"SELECT count(*) AS n FROM mcp_connections WHERE organization_id = ? AND revoked_at IS NULL",params:[t]},{sql:"SELECT count(*) AS n FROM user_access_tokens WHERE organization_id = ? AND revoked_at IS NULL AND expires_at > ?",params:[t,n]},{sql:"SELECT count(*) AS n FROM app_tool_catalogs WHERE organization_id = ? AND status = 'ok' AND tool_count > 0",params:[t]}]);return{members:T(r),invitations:T(o),connections:T(i),tokens:T(s),toolApps:T(d)}}import wt from"chalk";function tt(e){return e.severity==="error"||e.severity==="warning"}function b(e,...t){return{text:e.reduce((n,r,o)=>n+r+(t[o]??""),""),human:e.reduce((n,r,o)=>n+r+(o<t.length?wt.bold(t[o]):""),"")}}function te(e){return e.bound&&(e.status==="active"||e.status==="binding")}function O(e){return te(e)&&e.setup!==null&&!e.setup.ready}var D=`${v} setup`,H=`${v} deploy`,St=`${v} update`;function nt(){return{code:"GATEWAY_NOT_INSTALLED",severity:"warning",headline:"Setup stopped before it recorded the Gateway's install",detail:["The Gateway's database exists, but nothing else was recorded."],run:b`${D}`,runNote:"asks for the hostname again"}}function rt({install:e,desired:t,apps:n,unboundApps:r,owner:o,servingVersionId:i,inSync:s,waitingCertificates:d,certificates:h,access:m,accessState:c,leases:y,checkout:f,emailProblems:S,settings:I,missingOrigins:E}){let p=[],A=e.releaseVersion;if(f.behind){let u=f.checkout?.version;p.push({code:"CHECKOUT_BEHIND",severity:"warning",headline:"This checkout is behind the live Gateway",detail:[...u&&u!==A?[`This checkout has ${u}; ${A} is live.`]:[],"Deploys from here are refused until it catches up."],run:b`${"git pull"}`})}else f.upToDate===!1&&e.liveVersionId!==null&&p.push({code:"GATEWAY_OUTDATED",severity:"info",headline:"Your every-app folder has a newer Gateway than the one online",detail:["Run update now, or leave it: the next new app's deploy updates it too."],run:b`${St}`,runNote:"updates it now"});e.liveVersionId===null?p.push({code:"GATEWAY_NOT_UPLOADED",severity:"warning",headline:"The Gateway's install stopped before its first upload",detail:["Setup finishes it, then prints the link that creates the owner account."],run:b`${D}`}):(i!==e.liveVersionId?p.push({code:"GATEWAY_CHANGED",severity:"warning",headline:"The Gateway Worker was changed outside everyapp",detail:["It serves a version from the dashboard or API, not the one setup uploaded."],run:b`${D}`,runNote:`redeploys ${A}, asking first`}):s||p.push(At(r)),o||p.push({code:"NO_OWNER",severity:"warning",headline:"No owner yet, so nobody can sign in",detail:["Apps still deploy. Setup prints a one-time link that creates the owner account."],run:b`${D}`}));for(let u of t.missing)p.push({code:"APP_WORKER_MISSING",severity:"error",headline:`${u.appId}'s Worker no longer exists`,detail:[],run:b`${H} from ${u.appId}'s directory, or ${`${v} delete ${u.appId}`}`});for(let u of t.skipped)p.push({code:"APP_NOT_BOUND",severity:"error",headline:`The Gateway can't bind ${u.appId}`,detail:[`Its Worker isn't named the way the Gateway names ${u.appId}'s.`],run:b`${`${v} delete ${u.appId}`}`});let $=new Set(t.missing.map(u=>u.workerName));for(let u of n)$.has(u.workerName)||(u.status==="pending"?p.push({code:"APP_NOT_UPLOADED",severity:"warning",headline:`${u.id} was registered, but none of its deploys finished`,detail:(E?.get(u.id)??[]).map(({binding:N,variables:g})=>`${N} needs the database its Hyperdrive config connects to: ${Ze(g)} in ${Je}, on the machine that deploys it.`),run:b`${H} from the app's directory, or ${`${v} delete ${u.id}`}`}):u.status==="binding"&&u.bound&&p.push({code:"APP_BINDING",severity:"settling",headline:`${u.id} goes live on its first visit to https://${u.hostname}`,detail:[]}));I&&p.push({code:"SETTINGS_UNKNOWN",severity:"warning",headline:"Couldn't list the Workers' secrets",detail:[`Cloudflare said: ${I.error}. Whether the apps' settings are set isn't known.`]});for(let u of n)O(u)&&p.push(vt(u.id,u.setup));"error"in S&&p.push({code:"EMAIL_ROUTES_UNKNOWN",severity:"warning",headline:"Couldn't check the apps' Email Routing rules",detail:[`Cloudflare said: ${S.error}. Deleted rules won't show above.`]});for(let u of n){let N=$t(u,"error"in S?[]:S);N&&p.push(N)}h&&p.push({code:"CERTIFICATES_UNKNOWN",severity:"warning",headline:`Couldn't list ${e.zoneName}'s certificates`,detail:[`Cloudflare said: ${h.error}.`]}),d.length>0&&p.push({code:"CERT_PENDING",severity:"settling",headline:`No certificate yet for ${d.join(", ")}`,detail:["It usually comes within minutes."]}),m&&p.push({code:"ACCESS_UNKNOWN",severity:"warning",headline:"Couldn't check whether Cloudflare Access is on",detail:[`Cloudflare said: ${m.error}.`]}),c?.drift&&(Se(c.drift)||c.drift.settings)&&p.push(bt(c.drift)),c?.driftUnread&&!c.driftUnread.permission&&p.push({code:"ACCESS_DRIFT_UNKNOWN",severity:"warning",headline:"Couldn't check whether Cloudflare Access is in step with this Gateway",detail:[`Cloudflare said: ${c.driftUnread.error}.`]});for(let u of y)p.push({code:"DEPLOY_RUNNING",severity:"settling",headline:`${U(B(u))} is running`,detail:[]});return[...p.filter(u=>u.severity!=="info"),...p.filter(u=>u.severity==="info")]}function vt(e,t){return{code:"APP_NEEDS_SETUP",severity:"warning",headline:`${e} needs setup`,detail:We(t.unmet),...t.command?{run:b`${t.command.command}`}:{}}}function bt(e){return{code:"ACCESS_DRIFT",severity:"warning",headline:"Cloudflare Access is out of step with this Gateway",detail:[...e.missing.length>0?[`No Access application in front of ${w(e.missing)}.`]:[],...e.paths.length>0?[`The paths let through without a login differ on ${w(e.paths)}.`]:[],...e.signIn.length>0?[`The sign-in methods differ on ${w(e.signIn)}.`]:[],...e.settings?["The Gateway's settings name other applications than the ones found: nobody can sign in until they are written again."]:[]],run:b`${v} access on`,runNote:"brings Access in step with this Gateway"}}function At(e){if(e.length===0)return{code:"BINDINGS_DIFFER",severity:"warning",headline:"The live Gateway's apps or hostnames are out of date",detail:["It still serves something that was removed, or not yet what was added."],run:b`${D}`};let t=w(e);return{code:"BINDINGS_DIFFER",severity:"warning",headline:`The live Gateway doesn't serve ${t}`,detail:[e.length===1?`${t} is uploaded, but the live Gateway doesn't bind it.`:`${t} are uploaded, but the live Gateway doesn't bind them.`],run:b`${D}`}}function $t(e,t){let{inbound:n,routed:r,declaresInbound:o}=e.email;if(n.length>0&&!o){let y=w(n);return{code:"EMAIL_INBOUND_UNDECLARED",severity:"warning",headline:`${e.id} has ${n.length===1?"an inbound address":"inbound addresses"}, but its config doesn't turn inbound mail on`,detail:[`${e.id} was deployed without email: { inbound: true }, so nothing routes ${y}.`],fix:b`declare it in everyapp.config.ts and run ${H}, or drop ${n.length===1?"the address":"the addresses"}: ${`${v} email unset inbound`}`,where:`Run either from ${e.id}'s directory.`}}let i=n.filter(y=>!r.includes(y)),s=r.filter(y=>!n.includes(y)),d=t.filter(y=>y.workerName===e.workerName);if(i.length+s.length+d.length===0)return null;let h=[...i.length>0?[`${w(i)} ${i.length===1?"isn't":"aren't"} routed yet`]:[],...d.map(y=>`${y.address} gets no mail: ${y.problem}`),...s.length>0?[`${w(s)} ${s.length===1?"is":"are"} still routed, though no longer listed`]:[]],m=i.length+d.length,c=s.length>0?`${e.id}'s mail routes don't match its addresses`:d.length===0?`${e.id} doesn't get mail at ${w(i)} yet`:m===1?`${e.id} doesn't get mail at ${d[0].address}`:`${e.id} doesn't get mail at ${m} of its addresses`;return{code:d.length>0?"EMAIL_ROUTE_MISSING":"EMAIL_ROUTE_PENDING",severity:"warning",headline:c,detail:s.length===0&&d.length===0?[]:s.length===0&&m===1?[`${U(d[0].problem)}.`]:[`${h.join("; ")}.`],run:b`${H} from ${e.id}'s directory`}}var Nt=4,It="https://everyapp.com/docs/connect-ai",it=Object.values(de).map(e=>e.keyEnv);function ne(e,t){return e.some(n=>O(n)&&n.setup.unmet.some(r=>r.names.includes(t)))}function ot(e,t){return e.filter(n=>n.setup?.settings.some(r=>r.kind==="capability"&&r.id===t)).map(n=>n.id)}function kt(e){let{apps:t,gatewayNames:n}=e;if(!n||it.some(i=>n.includes(i)))return null;let r=[],o=[];for(let i of t){let s=(i.setup?.settings??[]).filter(d=>d.kind==="provider"&&d.set===!1).flatMap(d=>d.names.filter(h=>it.includes(h))).filter(d=>!ne(t,d));if(s.length!==0){o.push(i.id);for(let d of s)r.includes(d)||r.push(d)}}return r.length===0?null:{code:"AI_KEY",command:`${v} secret set ${r.join(" ")}`,note:`AI for ${w(o)}`,why:`${w(o)} ${o.length===1?"uses":"use"} AI once ${w(r)} ${r.length===1?"is":"are"} set; no AI key is set on the Gateway.`}}function Et(e){let{gatewayNames:t,providers:n,uploaded:r,apps:o}=e;return!t||!r?[]:n.filter(i=>!i.on&&t.includes(i.keySetting)&&ot(o,i.capability).length===0).map(i=>({code:"PROVIDER_OFFER",command:`${v} mcp on ${i.id}`,note:`offer ${i.name}'s tools to AI clients (each call spends ${i.name} credit)`,why:`${i.keySetting} is set and ${i.name} is off.`}))}function Rt(e){let{gatewayNames:t,providers:n,apps:r}=e;return t?n.filter(o=>o.on&&!t.includes(o.keySetting)&&!ne(r,o.keySetting)).map(o=>({code:"PROVIDER_KEY",command:`${v} secret set ${o.keySetting}`,note:`${o.name} is on and needs its key`,why:`${o.name} shows "needs setup" and serves no tools until ${o.keySetting} is set.`})):[]}function Pt(e){let{mode:t,uploaded:n,owner:r,access:o}=e;return t==="hosted"||!n||!r||!o||o.on?null:{code:"ACCESS_OFF",command:`${v} access on`,note:"put Cloudflare Access in front (recommended)",why:"Cloudflare Access is off; the Gateway's own sign-in is the only check in front of your apps."}}function Ct(e){let{counts:t,owner:n,apps:r,gatewayUrl:o}=e;return!t||!n||t.members!==1||t.invitations>0||!r.some(te)?null:{code:"INVITE",url:`${o}/admin/users`,note:"invite people",why:`${n.email} is the only member.`}}function Dt(e){let{counts:t,gatewayUrl:n}=e;return!t||t.connections>0||t.tokens>0||t.toolApps===0?null:{code:"CONNECT_AI",url:It,note:`connect an AI client to ${n}/mcp`,why:`No AI client is connected; ${R(t.toolApps,"app")} ${t.toolApps===1?"serves":"serve"} tools.`}}function Ot(e){return!e.uploaded||e.apps.length>0?null:{code:"CREATE_APP",command:`${v} create my-app`,note:"start your first app",why:"No apps yet."}}function xt(e){let{gatewayNames:t,counts:n,apps:r}=e;if(!t||t.includes("EMAIL_FROM")||ne(r,"EMAIL_FROM"))return null;let o=ot(r,"email").filter(s=>!r.some(d=>d.id===s&&O(d)));return!((n?.members??0)>1)&&o.length===0?null:{code:"EMAIL_UNSET",command:`${v} secret set EMAIL_FROM EMAIL_FROM_NAME`,note:"email invites and password resets; needs Cloudflare Email Sending",why:`No sender is set: invites and resets go out by link${o.length>0?`, and ${w(o)} ${o.length===1?"sends":"send"} mail once there is one`:""}.`}}function st(e){return[kt(e),...Et(e),...Rt(e),Pt(e),Ct(e),Dt(e),Ot(e),xt(e)].filter(n=>n!==null).filter(n=>!n.command||!e.problems.some(r=>r.run?.text===n.command)).slice(0,Nt)}import l from"chalk";var _t={["error"]:l.red("\u2717"),["warning"]:l.yellow("!"),["info"]:l.dim("i")};function at(e,t){return[...J([{label:"Cloudflare",value:e},{label:"Gateway",value:l.yellow("install not finished")}]),...dt([t])].join(`
2
+ `)}function lt({install:e,accountName:t,owner:n,servingRecorded:r,checkout:o,certificates:i,waitingCertificates:s,access:d,leases:h,apps:m,missingWorkers:c,skippedWorkers:y,emailProblems:f,problems:S}){let I=e.liveVersionId!==null,E=[{label:"Gateway",value:`${l.cyan(`https://${e.gatewayHost}`)}${e.deploymentMode?l.dim(` \xB7 ${e.deploymentMode.replace("_","-")}`):""}`},{label:"Cloudflare",value:t},{label:"Owner",value:n?`${n.email}${n.organizationName?l.dim(` \xB7 ${n.organizationName}`):""}`:l.yellow("none yet")},...d?[{label:"Access",value:Tt(d)}]:[],{label:"Release",value:Ut(e,r,o)},{label:"TLS",value:Gt(i,s)},...h.map((g,k)=>({label:k===0?"Deploys":"",value:U(B(g))}))],p=new Set(h.flatMap(g=>g.app?[g.app]:[])),A=new Set(s),$=g=>Lt(g,{missing:c.has(g.workerName),skipped:y.has(g.workerName),waitingCertificate:A.has(g.hostname),deploying:p.has(g.id)}),u=m.filter(g=>g.email.senders.length>0||g.email.inbound.length>0||g.email.routed.length>0),N=m.flatMap(g=>g.setup?Te(g.id,g.setup.settings):[]);return[...J(E),"",...m.length>0?Q(["APP","STATUS","URL","DEPLOYED BY","PUBLIC"],m.map(g=>[g.id,$(g),l.cyan(`https://${g.hostname}`),Wt(g.deployedBy),Ft(g)])).map(g=>g.trimEnd()):[l.dim("No apps yet.")],...u.length>0?["",...Bt(u,f)]:[],...N.length>0?["",...Ge(N)]:[],...dt(S)].join(`
3
+ `)}function Ut(e,t,n){let r=e.releaseVersion,o=i=>`${r} ${l.dim("\xB7")} ${l.yellow(i)}`;return e.liveVersionId===null?l.yellow(`${r}, never uploaded`):t?n.behind?o("this checkout is behind"):n.upToDate===!1?`${r}${l.dim(" \xB7 this checkout has newer Gateway changes")}`:n.upToDate===!0?`${r}${l.dim(" \xB7 matches this checkout")}`:r:o("changed outside everyapp")}function Tt(e){if("error"in e)return l.yellow("! couldn't check");if(!e.on)return l.dim("off");let t=l.green("on");if(e.drift===null)return e.driftUnread?`${t}${l.dim(e.driftUnread.permission?" \xB7 in step? this login can't read Access":" \xB7 in step? couldn't check")}`:t;let n=[...e.drift.missing.length>0?[`no application in front of ${w(e.drift.missing)}`]:[],...e.drift.paths.length>0?[`paths differ on ${w(e.drift.paths)}`]:[],...e.drift.signIn.length>0?[`sign-in methods differ on ${w(e.drift.signIn)}`]:[],...e.drift.settings?["the settings name other applications"]:[]];return n.length>0?`${t} ${l.yellow(`\xB7 drift: ${n.join("; ")}`)}`:`${t}${l.dim(" \xB7 in step with this Gateway \xB7 everyone signs in through Cloudflare Access first")}`}function Gt(e,t){return e===null?l.yellow("! couldn't check"):e.length===0?l.dim("no hostnames yet"):t.length>0?`waiting for ${t.join(", ")}${l.dim(" \xB7 usually a few minutes")}`:`ready on ${R(e.length,"hostname")}`}function Wt(e){return e?e.startsWith("api-token:")?"API token":e:""}var Mt=3;function Ft(e){let t=[];if(e.publicRoutes.length>0){let{shown:n,more:r}=$e(e.publicRoutes,Mt);t.push(`${n}${r>0?l.dim(` +${r} more`):""}`)}return e.mcp&&t.push(Ae(e.mcp)),t.join(l.dim(" \xB7 "))}function Lt(e,{missing:t,skipped:n,waitingCertificate:r,deploying:o}){let i=t?`${l.red("\u2717")} Worker missing`:n?`${l.red("\u2717")} can't be bound`:e.status==="pending"?`${l.yellow("!")} never deployed`:e.bound?O(e)?`${l.yellow("!")} needs setup`:e.status==="active"?`${l.green("\u2713")} live`:e.status==="binding"?`${l.dim("\u25CB")} ${r?"waiting for certificate":"live on first visit"}`:l.dim(e.status==="disabled"?"switched off":e.status):`${l.yellow("!")} not bound`;return o?`${i} \xB7 deploying`:i}function Bt(e,t){let n=e.flatMap(r=>{let{senders:o,inbound:i,routed:s,declaresInbound:d}=r.email,h=new Set(t.filter(f=>f.workerName===r.workerName).map(f=>f.address)),m=i.length>0&&!d?i.map(f=>`${l.yellow("!")} ${f} ${l.dim("(inbound off)")}`):[...i.map(f=>h.has(f)?`${f} ${l.yellow("! not delivered")}`:s.includes(f)?f:`${f} ${l.yellow("! not routed yet")}`),...s.filter(f=>!i.includes(f)).map(f=>`${f} ${l.yellow("! still routed, no longer listed")}`)],c=o.length>0?o:[l.dim("the Gateway's sender")],y=Math.max(c.length,m.length,1);return Array.from({length:y},(f,S)=>[S===0?r.id:"",c[S]??"",m[S]??(S===0?l.dim("nothing"):"")])});return Q(["EMAIL","SENDS AS","RECEIVES"],n)}function dt(e){let t=[];for(let n of e){if(n.severity==="settling")continue;let r=n.run?t.find(o=>o[0].run?.text===n.run.text):void 0;r?r.push(n):t.push([n])}return t.length===0?[]:["",...t.flatMap(Ht)]}function Ht([e,...t]){let n=e,r=[n,...t].map(i=>i.severity),o=r.includes("error")?"error":r.includes("warning")?"warning":"info";return[`${_t[o]} ${n.headline}`,...[...n.detail,...t.map(i=>`${i.headline}.`)].map(i=>l.dim(` ${i}`)),...n.fix?[` Fix: ${n.fix.human}`]:[],...n.run?[` Run: ${n.run.human}${n.runNote?` ${l.dim(n.runNote)}`:""}`]:[],...n.where?[l.dim(` ${n.where}`)]:[]]}async function tr(e){await Oe(e,async t=>{let n=await De({json:e.json,out:t,accountId:e["account-id"]}),r=Le();await Vt({gateway:n,out:t,checkout:r?()=>Be(r):null})})}function ut(e){return{code:e.code,severity:e.severity,message:[e.headline,...e.detail].join(`
4
+ `),run:e.run?.text??null,...e.runNote?{note:e.runNote}:{},...e.fix?{fix:e.fix.text}:{}}}async function Vt({gateway:e,out:t,checkout:n}){let{cf:r,account:o,db:i,install:s}=e,d=o.name??o.id;if(!s){let a=nt();t.emit({type:"done",message:`${d} has the Gateway's database, but its install didn't finish`,display:at(d,a),data:{deployed:!0,installed:!1,accountId:o.id,fixes:[ut(a)]}});return}let h=s.liveVersionId!==null,m=s.deploymentMode!=="hosted"&&h,[c,y,f,S,I,E,p,A]=await Promise.all([Ke(i,Ve(r)),Ce(i),ye(i),Xe(i),Me(r,pe),Z(i,"email-route"),h?x(()=>ee(r)):null,m?x(()=>ke(i)):null]),$=S?.owner??null,u=y.filter(a=>a.tier!==Y),N=ue.map(a=>({id:a,name:j[a].name,capability:j[a].capability,keySetting:ce(a),on:y.some(C=>C.tier===Y&&C.appSlug===a)})),g=!m||p&&"error"in p||A&&"error"in A?null:Re(p.value,A.value),k=m?p&&"error"in p?p:A&&"error"in A?A:{value:{...g,...g.on?await Kt(r,i,s,A.value):{drift:null,driftUnread:null}}}:null,G=k&&"value"in k?k.value:null,re=k&&"error"in k?k:null,ie=new Set(c.missing.map(a=>a.workerName)),[W,_,oe,ct,pt]=await Promise.all([x(()=>qe(r,s.zoneId,s.liveDomains)),x(async()=>E.length===0?[]:je(r,E)),Fe(i,s).then(a=>jt(r,i,u,ie,p,a)),S?et(i,S.organizationId).catch(()=>null):null,qt(i,u)]),V=`https://${s.gatewayHost}`,K=ze({gatewayHost:s.gatewayHost,appHosts:c.appHosts.filter(a=>He(a,s.appBaseDomain))}),mt=K.filter(a=>!s.liveDomains.includes(a)),ft=s.liveDomains.filter(a=>!K.includes(a)),se=c.appWorkers.filter(a=>!s.liveAppWorkers.includes(a)),gt=s.liveAppWorkers.filter(a=>!c.appWorkers.includes(a)),ae=!!I?.versionId&&I?.versionId===s.liveVersionId,le=ae&&se.length===0&&gt.length===0&&mt.length===0&&ft.length===0,P=u.map(a=>({id:a.appSlug,hostname:a.hostname,workerName:a.workerName,status:a.status,bound:s.liveAppWorkers.includes(a.workerName),deployedBy:a.deployedBy,email:{senders:a.email.senders,inbound:a.email.inbound,routed:E.filter(C=>C.workerName===a.workerName).map(C=>C.name),declaresInbound:a.declaresInbound},publicRoutes:a.publicRoutes,mcp:a.mcp,setup:oe.setups.get(a.appSlug)??null})),M="error"in W?null:W.value,z=(M??[]).filter(a=>!a.ready).map(a=>a.host),F=await Jt(s,n),L=rt({install:s,desired:c,apps:P,unboundApps:P.filter(a=>se.includes(a.workerName)).map(a=>a.id),owner:$,servingVersionId:I?.versionId??null,inSync:le,waitingCertificates:z,certificates:"error"in W?W:null,access:re,accessState:G,leases:f,checkout:F,emailProblems:"error"in _?_:_.value,settings:oe.unread,missingOrigins:pt}),ht=st({mode:s.deploymentMode,uploaded:h,gatewayUrl:V,owner:$,apps:P,gatewayNames:p&&"value"in p?p.value:null,access:G,providers:N,counts:ct,problems:L}),yt=lt({install:s,accountName:d,owner:$,servingRecorded:ae,checkout:F,certificates:M,waitingCertificates:z,access:re??G,leases:f,apps:P,missingWorkers:ie,skippedWorkers:new Set(c.skipped.map(a=>a.workerName)),emailProblems:"error"in _?[]:_.value,problems:L});t.emit({type:"done",message:Yt(V,P,L),display:yt,data:{deployed:!0,installed:!0,gatewayUrl:V,accountId:o.id,mode:s.deploymentMode,release:{version:s.releaseVersion,sha256:s.releaseSha256},versionId:I?.versionId??null,recordedVersionId:s.liveVersionId,owner:$,claimed:$!==null,apps:P,desiredApps:c.appWorkers,liveApps:s.liveAppWorkers,appBaseDomain:s.appBaseDomain,domains:K,liveDomains:s.liveDomains,inSync:le,upToDate:F.upToDate,checkout:F.checkout,tls:M===null?null:!z.includes(s.gatewayHost),certificates:M,access:G,leases:f,fixes:L.map(ut),next:ht.map(zt)}})}async function Kt(e,t,n,r){try{let[o,i,s,d]=await Promise.all([he(e),ge(e),fe(e),Ne(t)]),h=Ie(n),m=we(o,h),c=ve({scope:h,found:m,surfaces:d,providerIds:be(i).map(f=>f.id)}),y=r!==null&&s!==null&&m.gateway!==null&&m.apps!==null&&!Ee(r,{teamDomain:s.auth_domain.toLowerCase(),gatewayAud:m.gateway.aud,appsAud:m.apps.aud});return{drift:{...c,settings:y},driftUnread:null}}catch(o){return{drift:null,driftUnread:{permission:o instanceof me&&o.code==="ACCESS_PERMISSION_MISSING",error:q(o).split(`
5
+ `)[0]}}}}function zt(e){return{code:e.code,...e.command?{command:e.command}:{},...e.url?{url:e.url}:{},note:e.note,why:e.why}}function Yt(e,t,n){let r=t.filter(i=>i.status==="active"&&i.bound).length,o=n.filter(tt).length;return`Gateway ${e}: ${R(r,"app")} live, ${o===0?"nothing to fix":`${R(o,"problem")} to fix`}`}async function jt(e,t,n,r,o,i){let s=new Map,d=n.filter(c=>c.manifest&&Ue(c.manifest,i)&&c.tier!=="dispatch"&&c.status!=="pending"&&!r.has(c.workerName));if(d.length===0)return{setups:s,unread:null};let h=null,m=o??await x(()=>ee(e));"error"in m&&(h=m);for(let c of d){let y=await x(()=>xe(e,c.workerName));"error"in y&&(h??=y);let f="error"in y?null:y.value;f&&(c.secretNames===null||X(f)!==X(c.secretNames))&&await Pe(t,c.id,f);let{setup:S}=_e({manifest:c.manifest,appId:c.appSlug,gatewayNames:"error"in m?null:m.value,secretNames:f,lookup:i});s.set(c.appSlug,S)}return{setups:s,unread:h}}async function qt(e,t){let n=new Map,r=t.filter(i=>i.status==="pending").map(i=>({app:i,bindings:Xt(i.manifest)})).filter(({bindings:i})=>i.length>0);if(r.length===0)return n;let o=await Z(e,"hyperdrive").catch(()=>null);if(!o)return n;for(let{app:i,bindings:s}of r){let d=s.filter(h=>!o.some(m=>m.workerName===i.workerName&&m.binding===h&&m.resourceId));d.length!==0&&n.set(i.appSlug,d.map(h=>({binding:h,variables:Qe(h,s.length)})))}return n}function Xt(e){let t=e?.resources?.hyperdrive;return Array.isArray(t)?t.flatMap(n=>{let r=n?.name;return typeof r=="string"&&r?[r]:[]}):[]}async function x(e){try{return{value:await e()}}catch(t){return{error:q(t)}}}async function Jt(e,t){let n=t?await t().catch(()=>null):null;if(!n)return{upToDate:null,behind:!1,checkout:null};let r=n.treeSha256&&e.sourceTreeSha256?n.treeSha256===e.sourceTreeSha256:null;return{upToDate:r,behind:r===!1&&Ye(n,e),checkout:{version:n.version,treeSha256:n.treeSha256}}}export{Vt as reportStatus,tr as status};
@@ -0,0 +1,22 @@
1
+ import{a as Ct,d as Et}from"./chunk-5IC3ZN52.js";import{a as Ge,b as Me,c as Ue}from"./chunk-TUXKBLLJ.js";import{a as ne,c as vt,d as Ae,e as $t}from"./chunk-EP4HRYXP.js";import{A as kt,a as mt,e as ut,h as oe,k as pt,l as ye,m as ft,o as gt,r as wt,t as ht,v as yt,z as At}from"./chunk-GJK6GAWO.js";import{h as dt,i as lt}from"./chunk-26KS6TD6.js";import{a as tt,c as ot,d as nt,h as rt,i as st,j as at,k as te,l as it}from"./chunk-F5XK23HV.js";import{a as ee,b as et,i as ct}from"./chunk-YAZHTVUV.js";import"./chunk-BK4NK34C.js";import{a as Pe,h as _e,j as De,l as Oe,p as ze,v as Fe}from"./chunk-QKHLRLK3.js";import{a as Xe,d as he}from"./chunk-KVCL64LI.js";import{a as R,d as Je}from"./chunk-3EADVHFN.js";import"./chunk-HJGZGZFE.js";import"./chunk-EQLY5I46.js";import{a as Ze,k as Ve,na as Be,q as we,r as He,t as je}from"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import{a as Ke}from"./chunk-TIYSZWP5.js";import{a as qe,f as We}from"./chunk-I7FNTTUY.js";import{d as Le,j as xe}from"./chunk-RQBSTNL7.js";import{Cc as Ie,Ec as ge,Hb as fe,Qc as Qe,Yc as Ye,bc as C,d as Se,dc as Te,f as v,fc as q,hc as W,ic as be,k as I}from"./chunk-FQ2A7YAP.js";import M from"chalk";import Kt from"node:fs";import Ft from"node:os";import Rt from"node:path";var St=["23bc28f0b638fbfc1c7f838573c17ee3"];var Gt=/^[0-9a-f]{32}$/,Tt=e=>Gt.test(e),ke="EVERYAPP_TEST_ACCOUNTS";function Mt(e=process.env){let n=e.XDG_CONFIG_HOME||Rt.join(Ft.homedir(),".config");return Rt.join(n,"everyapp","test-accounts")}function Nt(e){let n=new Set,i=[];for(let r of e.split(/\r?\n/)){let c=r.replace(/#.*$/,"");for(let a of c.split(/[\s,]+/)){if(!a)continue;let m=a.toLowerCase();Tt(m)?n.add(m):i.push(a)}}return{ids:[...n],ignored:i}}function Ut(e=process.env){let n=Mt(e),i="";try{i=Kt.readFileSync(n,"utf8")}catch(a){if(a.code!=="ENOENT")throw a}let r=Nt(e[ke]??""),c=Nt(i);return{ids:[...new Set([...r.ids,...c.ids])],ignored:[...r.ignored,...c.ignored],file:n}}function Zt(e,n){return["only for a dedicated test account that holds nothing else (docs/runbooks/onboarding-loop-test-accounts.md):",`set ${ke}=${e} for this one command, or add a line ${e} to ${n}`].join(" ")}function bt({accountId:e,out:n,env:i=process.env}){let r=e.trim().toLowerCase();if(!Tt(r))throw new C("TEARDOWN_INVALID_ACCOUNT_ID",`"${e.trim()}" isn't a Cloudflare account id
2
+ An account id is 32 hex characters (Compute (Workers) \u2192 Overview, or any dashboard URL dash.cloudflare.com/<account id>/...).`,{fix:"pass the test account's id from the dashboard as --account-id",data:{accountId:e.trim()}});if(St.includes(r))throw new C("TEARDOWN_ACCOUNT_FORBIDDEN",`Cloudflare account ${r} can never be torn down
3
+ It is a live account, refused by id before any allowlist is read.`,{data:{accountId:r}});let c=Ut(i);if(c.ignored.length>0&&n.emit({type:"warning",code:"TEARDOWN_ALLOWLIST_IGNORED",message:`Ignored ${v(c.ignored.length,"entry")} of the teardown allowlist that ${c.ignored.length===1?"isn't":"aren't"} account ids: ${I(c.ignored)}
4
+ An account id is 32 hex characters (${ke}, ${c.file}).`,data:{ignored:c.ignored,file:c.file}}),!c.ids.includes(r))throw new C("TEARDOWN_NOT_ALLOWLISTED",`Cloudflare account ${r} isn't on the teardown allowlist
5
+ teardown deletes everything EveryApp made in an account, so it runs only against accounts listed as dedicated test accounts.`,{fix:Zt(r,c.file),data:{accountId:r,file:c.file}});if(!i.CLOUDFLARE_API_TOKEN)throw new C("TEARDOWN_NEEDS_TOKEN",`teardown needs CLOUDFLARE_API_TOKEN
6
+ A Cloudflare login is a person, who sees every account they belong to; teardown runs only with an API token scoped to the test account (runbook step 5).`,{fix:"set CLOUDFLARE_API_TOKEN to the test account's token for this one command, and run it again",data:{accountId:r}});return{accountId:r,allowlist:c.ids}}function It({accountId:e,allowlist:n,whoami:i}){if(!i.authenticated||i.tokenValid===!1)throw new C("CLOUDFLARE_TOKEN_REJECTED",`Cloudflare didn't accept CLOUDFLARE_API_TOKEN
7
+ It may be mistyped, expired or revoked.`,{fix:"set the test account's token (runbook step 5)"});if(i.authSource&&!/CLOUDFLARE_API_TOKEN/.test(i.authSource))throw new C("TEARDOWN_NEEDS_TOKEN",`teardown needs CLOUDFLARE_API_TOKEN, but cf authenticated with ${i.authSource}
8
+ A Cloudflare login is a person, who sees every account they belong to; teardown runs only with an API token scoped to the test account (runbook step 5).`,{fix:"set CLOUDFLARE_API_TOKEN to the test account's token for this one command, and run it again",data:{accountId:e,authSource:i.authSource}});let r=(i.accounts??[]).map(a=>({id:a.id.toLowerCase(),name:a.name??null})),c=r.filter(a=>St.includes(a.id)||!n.includes(a.id));if(c.length>0)throw new C("TEARDOWN_TOKEN_TOO_BROAD",[`CLOUDFLARE_API_TOKEN sees ${v(c.length,"account")} that ${c.length===1?"isn't a":"aren't"} test account${c.length===1?"":"s"}: ${I(c.map(a=>a.name?`${a.name} (${a.id})`:a.id))}`,"A teardown token must be scoped to the test account alone (Account Resources \u2192 Include \u2192 that account), so there is no path from here to a live account."].join(`
9
+ `),{fix:"mint a token for the test account only (runbook step 5)",data:{accountId:e,accounts:c}});if(!r.some(a=>a.id===e))throw new C("TEARDOWN_ACCOUNT_NOT_VISIBLE",`CLOUDFLARE_API_TOKEN doesn't list Cloudflare account ${e}
10
+ ${r.length===0?"It lists no accounts.":`It lists ${I(r.map(a=>a.id))}.`}`,{fix:"use that account's token, with Account Settings: Read on it (runbook step 5)",data:{accountId:e,accounts:r}})}import Pt from"chalk";var K="every-",_=e=>e.startsWith(K),re=e=>e.type==="MX"&&/\.mx\.cloudflare\.net\.?$/i.test(e.content??"")||e.type==="TXT"&&/_spf\.mx\.cloudflare\.net/i.test(e.content??""),Vt=new Set(["A","AAAA","CNAME"]),Z=e=>Vt.has(e.type.toUpperCase()),F=[["access","Access application or policy","Access applications and policy"],["email","Email Routing change","Email Routing changes"],["workflows","Workflow","Workflows"],["queueConsumers","queue consumer","queue consumers"],["workers","Worker","Workers"],["certificates","certificate pack","certificate packs"],["d1","D1 database","D1 databases"],["kv","KV namespace","KV namespaces"],["r2","R2 bucket","R2 buckets"],["hyperdrive","Hyperdrive config","Hyperdrive configs"],["queues","Queue","Queues"],["vectorize","Vectorize index","Vectorize indexes"],["aiGateway","AI Gateway","AI Gateways"],["secretsStore","Secrets Store secret","Secrets Store secrets"],["dns","DNS record","DNS records"]],V={access:"Zero Trust \u2192 Access \u2192 Applications and Policies",email:"the zone \u2192 Email \u2192 Email Routing",workflows:"Compute (Workers) \u2192 Workflows",queueConsumers:"Compute (Workers) \u2192 Queues \u2192 the queue's Settings",workers:"Compute (Workers) \u2192 Workers & Pages",certificates:"the zone \u2192 SSL/TLS \u2192 Edge Certificates",d1:"Storage & Databases \u2192 D1",kv:"Storage & Databases \u2192 KV",r2:"Storage & Databases \u2192 R2 Object Storage",hyperdrive:"Storage & Databases \u2192 Hyperdrive",queues:"Compute (Workers) \u2192 Queues",vectorize:"AI \u2192 Vectorize",aiGateway:"AI \u2192 AI Gateway",secretsStore:"Secrets Store",dns:"the zone \u2192 DNS \u2192 Records"},H=e=>e.reduce((n,i)=>n+i.rules.length+(i.catchAllWorker?1:0)+(i.disable?1:0),0),ve=e=>e.reduce((n,i)=>n+i.records.length,0);function G(e){return{access:e.access.applications.length+(e.access.policy?1:0),email:H(e.email),workflows:e.workflows.length,queueConsumers:e.queueConsumers.length,workers:e.workers.length,certificates:e.certificates.length,d1:e.d1.length,kv:e.kv.length,r2:e.r2.length,hyperdrive:e.hyperdrive.length,queues:e.queues.length,vectorize:e.vectorize.length,aiGateway:e.aiGateway.length,secretsStore:e.secretsStore.length,dns:ve(e.dns)}}function D(e){return Object.values(G(e)).reduce((n,i)=>n+i,0)}var f={worker:e=>`${R.worker} "${e}"`,workflow:e=>`Workflow "${e}"`,d1:e=>`${R.d1} "${e}"`,kv:e=>`${R.kv} "${e}"`,r2:e=>`${R.r2} "${e}"`,hyperdrive:e=>`${R.hyperdrive} "${e}"`,queue:e=>`${R.queue} "${e}"`,consumer:(e,n)=>`consumer ${e} of Queue "${n}"`,vectorize:e=>`${R.vectorize} "${e}"`,aiGateway:e=>`${R["ai-gateway"]} "${e}"`,secretsStore:e=>`${R["secrets-store-secret"]} "${e}"`,application:e=>`Access application "${e}"`,policy:e=>`Access policy "${e}"`,rule:(e,n)=>`Email Routing rule for ${e??"(no address)"} on ${n}`,catchAll:e=>`catch-all of ${e}`,routing:e=>`Email Routing on ${e}`,pack:(e,n)=>`certificate pack ${e} for ${n}`,record:(e,n)=>`DNS ${e} record at ${n}`};function Ht(e){let n=G(e),i={access:[...e.access.applications.map(r=>`application "${r.name}"`),...e.access.policy?[`policy "${e.access.policy.name}"`]:[]],email:e.email.flatMap(r=>[...r.rules.map(c=>`rule for ${c.address??"(no address)"} on ${r.zoneName}`),...r.catchAllWorker?[`${r.zoneName}'s catch-all back to dropping mail`]:[],...r.disable?[`Email Routing off for ${r.zoneName}`]:[]]),workflows:e.workflows,queueConsumers:e.queueConsumers.map(r=>`${r.script} on ${r.queueName}`),workers:e.workers.map(r=>r.domains.length>0?`${r.name} (${r.domains.join(", ")})`:r.name),certificates:e.certificates.map(r=>`${r.packId} (${r.hostname})`),d1:e.d1.map(r=>r.name),kv:e.kv.map(r=>r.title),r2:e.r2,hyperdrive:e.hyperdrive.map(r=>r.name),queues:e.queues.map(r=>r.name),vectorize:e.vectorize,aiGateway:e.aiGateway,secretsStore:e.secretsStore.map(r=>r.name),dns:e.dns.flatMap(r=>r.records.map(c=>`${c.type} at ${r.name}`))};return F.flatMap(([r,,c])=>n[r]===0?[]:[{label:`${c} (${n[r]})`,items:i[r]}])}function _t(e){let n=Ht(e).map(({label:i,items:r})=>({label:i,value:r.join(", ")}));return[...n.length>0?Qe(n):[Pt.dim("Nothing of EveryApp's to delete.")],...e.kept.length>0?["",Pt.dim(`Kept: ${e.kept.join("; ")}.`)]:[]]}function se(e){let n=G(e),i=F.flatMap(([r,c,a])=>n[r]===0?[]:[n[r]===1?`1 ${c}`:`${n[r]} ${a}`]);return`${v(D(e),"resource")}${i.length>0?`: ${i.join(", ")}`:""}`}function j(e){return{accountId:e.accountId,gateway:e.gateway,counts:G(e),total:D(e),workers:e.workers,workflows:e.workflows,d1:e.d1,kv:e.kv,r2:e.r2,hyperdrive:e.hyperdrive,queues:e.queues,queueConsumers:e.queueConsumers,vectorize:e.vectorize,aiGateway:e.aiGateway,secretsStore:e.secretsStore,access:e.access,email:e.email,certificates:e.certificates,dns:e.dns,kept:e.kept}}var jt=e=>typeof e=="object"&&e!==null&&"removedAs"in e,Bt={name:"",enabled:!1,matchers:[{type:"all"}],actions:[{type:"drop"}]},ae="teardown.delete",Qt=5e3;async function Ot({cf:e,plan:n,out:i,sleep:r=c=>new Promise(a=>setTimeout(a,c))}){let c=[],a=[],m=async(o,l,A)=>{try{let g=await A();if(g===!1)return;c.push(jt(g)?g.removedAs:l)}catch(g){if(W(g,"not_found"))return;let p=q(g).split(`
11
+ `)[0]??"";a.push({kind:o,described:l,place:V[o],reason:p}),i.emit({type:"warning",code:"TEARDOWN_DELETE_FAILED",step:ae,message:`Couldn't delete the ${l}
12
+ ${p}`,data:{kind:o,described:l,reason:p,fix:`delete it in the Cloudflare dashboard (${V[o]})`}})}},d=async(o,l,A)=>{if(l===0)return;let[,g,p]=F.find(([x])=>x===o),$=l===1?g:p,O=c.length,z=a.length;i.emit({type:"step",step:ae,status:"start",message:`Deleting ${l} ${$}...`,data:{kind:o,count:l}}),await A();let T=c.length-O,L=a.length-z;i.emit({type:"step",step:ae,status:"ok",message:L===0?`Deleted ${T} ${T===1?g:p}`:`Deleted ${T} ${T===1?g:p}; kept ${L}`,data:{kind:o,removed:c.slice(O),kept:a.slice(z).map(x=>x.described)}})};await d("access",n.access.applications.length+(n.access.policy?1:0),async()=>{for(let o of n.access.applications)await m("access",f.application(o.name),()=>ze(e,o.id));if(n.access.policy){let{id:o,name:l}=n.access.policy;await m("access",f.policy(l),()=>De(e,o))}}),await d("email",H(n.email),async()=>{for(let o of n.email){for(let l of o.rules)await m("email",f.rule(l.address,o.zoneName),()=>rt(e,o.zoneId,l.id));o.catchAllWorker&&await m("email",f.catchAll(o.zoneName),()=>at(e,o.zoneId,Bt)),o.disable&&await m("email",f.routing(o.zoneName),()=>ot(e,o.zoneId))}}),await d("workflows",n.workflows.length,async()=>{for(let o of n.workflows)await m("workflows",f.workflow(o),()=>kt(e,o))}),await d("queueConsumers",n.queueConsumers.length,async()=>{for(let o of n.queueConsumers)await m("queueConsumers",f.consumer(o.script,o.queueName),()=>ye(e,o.queueId,o.consumerId))}),await d("workers",n.workers.length,async()=>{for(let o of n.workers)await m("workers",f.worker(o.name),()=>he(e,o.name,{withReferences:!0}).catch(async l=>{if(!W(l,"queue_consumer_worker"))throw l;for(let A of await oe(e))for(let g of A.consumers??[])g.script===o.name&&g.consumer_id&&(await ye(e,A.queue_id,g.consumer_id),c.push(f.consumer(o.name,A.queue_name)));await he(e,o.name,{withReferences:!0})}))}),await d("certificates",n.certificates.length,async()=>{for(let o of n.certificates){let l=f.pack(o.packId,o.hostname);await m("certificates",l,async()=>{try{await $t(e,o.zoneId,o.packId);return}catch(A){if(W(A,"not_found"))throw A;let g=q(A).split(`
13
+ `)[0]??"",p=()=>ne(e,o.zoneId,{allStatuses:!0}),$=await p().catch(()=>null);if($&&!Ae($,o.packId)&&(await r(Qt),$=await p().catch(()=>$)),$&&Ae($,o.packId))return{removedAs:`${l} (gone with its Custom Domain)`};throw new Error(`${g}. Cloudflare removes a hostname's pack with its Custom Domain, so it is likely on its way out; if the zone still lists it, finish it in the dashboard`)}})}});let y=n.d1.find(o=>o.id===n.gateway?.databaseId)??null,N=n.d1.filter(o=>o!==y);await d("d1",N.length,async()=>{for(let o of N)await m("d1",f.d1(o.name),()=>we(e,o.id))}),await d("kv",n.kv.length,async()=>{for(let o of n.kv)await m("kv",f.kv(o.title),()=>He(e,o.id))}),await d("r2",n.r2.length,async()=>{for(let o of n.r2)await m("r2",f.r2(o),()=>Et(e,o))}),await d("hyperdrive",n.hyperdrive.length,async()=>{for(let o of n.hyperdrive)await m("hyperdrive",f.hyperdrive(o.name),()=>ut(e,o.id))}),await d("queues",n.queues.length,async()=>{for(let o of n.queues)await m("queues",f.queue(o.name),()=>pt(e,o.id))}),await d("vectorize",n.vectorize.length,async()=>{for(let o of n.vectorize)await m("vectorize",f.vectorize(o),()=>yt(e,o))}),await d("aiGateway",n.aiGateway.length,async()=>{for(let o of n.aiGateway)await m("aiGateway",f.aiGateway(o),()=>dt(e,o))}),await d("secretsStore",n.secretsStore.length,async()=>{for(let o of n.secretsStore)await m("secretsStore",f.secretsStore(o.name),()=>wt(e,o.storeId,o.id))});let S=[];for(let o of n.dns)try{for(let l of await ee(e,o.zoneId,o.name))Z(l)&&(re({type:l.type,content:l.content??null})||S.push({zoneId:o.zoneId,name:o.name,id:l.id,type:l.type}))}catch(l){i.emit({type:"warning",code:"TEARDOWN_DELETE_FAILED",step:ae,message:`Couldn't read the DNS records at ${o.name}
14
+ ${q(l).split(`
15
+ `)[0]}`,data:{kind:"dns",name:o.name,fix:`check them in the Cloudflare dashboard (${V.dns})`}})}return await d("dns",S.length,async()=>{for(let o of S)await m("dns",f.record(o.type,o.name),()=>et(e,o.zoneId,o.id))}),await d("d1",y?1:0,async()=>{if(!y)return;let o=a.filter(l=>l.kind==="dns");if(o.length>0){a.push({kind:"d1",described:f.d1(y.name),place:V.d1,reason:`kept on purpose: the next run reads the hostnames of the ${v(o.length,"kept DNS record")} above from it`});return}await m("d1",f.d1(y.name),()=>we(e,y.id))}),{removed:c,kept:a}}var $e="teardown.inventory";function Yt(e,n){return e===fe?1:n===fe?-1:e.localeCompare(n)}var Jt=(e,n)=>e===n.name||e.endsWith(`.${n.name}`);async function zt({cf:e,accountId:n,out:i}){i.emit({type:"step",step:$e,status:"start",message:`Looking for what EveryApp created in ${n}...`,data:{accountId:n}});let r=(t,s,u)=>i.emit({type:"step",step:"teardown.found",status:"ok",message:`${s} ${F.find(([w])=>w===t)[s===1?1:2]}`,data:{kind:t,count:s,items:u}}),c=[],a=async(t,s,u)=>{try{return await u()}catch(w){let h=q(w).split(`
16
+ `)[0]??"";if(be(w)){let E=W(w,"r2_not_enabled")?"R2":"Cloudflare Access";return i.emit({type:"step",step:"teardown.found",status:"skip",message:`${E} isn't enabled in this account: nothing of its to delete`,data:{what:t,service:E,reason:h}}),s}return c.push(`${t} (couldn't be listed: ${h})`),i.emit({type:"warning",code:"TEARDOWN_READ_FAILED",step:$e,message:`Couldn't list ${t}; whatever is there is kept
17
+ ${h}`,data:{what:t,reason:h,fix:"check the token's permissions (runbook step 5), then run again"}}),s}},m=await a("the Gateway's database",null,()=>je(e)),d=m?Ze(e,m):null,y=d?await a("the Gateway's install row",null,()=>Ve(d)):null,N=d?await a("the Gateway's apps",[],()=>Be(d)):[],S=d?await a("the apps' recorded resources",[],()=>Je(d)):[],o=t=>new Set(S.filter(s=>s.kind===t).map(s=>s.name)),l=(t,s)=>_(s)||o(t).has(s),A=new Set(N.flatMap(t=>t.workerName?[t.workerName]:[])),g=await a("the Workers",[],()=>Xe(e)),p=g.filter(t=>l("worker",t.name)||A.has(t.name)).sort((t,s)=>Yt(t.name,s.name)).map(t=>({name:t.name,domains:(t.references?.domains??[]).map(s=>s.hostname.toLowerCase()).sort()}));r("workers",p.length,p.map(t=>t.name));let $=(await a("the Workflows",[],()=>At(e,K))).filter(t=>_(t.script_name??"")||_(t.name)).map(t=>t.name).sort();r("workflows",$.length,$);let O=(await a("the D1 databases",[],()=>ge(e,["d1","list"],Le,{perPage:100,maxPages:10}))).filter(t=>l("d1",t.name)).map(t=>({name:t.name,id:t.uuid})).sort((t,s)=>t.id===m?1:s.id===m?-1:t.name.localeCompare(s.name));r("d1",O.length,O.map(t=>t.name));let z=(await a("the KV namespaces",[],()=>ge(e,["kv","namespaces","list"],xe,{perPage:100,maxPages:50}))).filter(t=>l("kv",t.title)).map(t=>({title:t.title,id:t.id})).sort((t,s)=>t.title.localeCompare(s.title));r("kv",z.length,z.map(t=>t.title));let T=(await a("the R2 buckets",[],()=>Ct(e,K))).filter(_).sort();r("r2",T.length,T);let L=(await a("the Hyperdrive configs",[],()=>mt(e))).filter(t=>l("hyperdrive",t.name)).map(t=>({name:t.name,id:t.id})).sort((t,s)=>t.name.localeCompare(s.name));r("hyperdrive",L.length,L.map(t=>t.name));let x=await a("the queues",[],()=>oe(e)),ie=x.filter(t=>l("queue",t.queue_name)).map(t=>({name:t.queue_name,id:t.queue_id})).sort((t,s)=>t.name.localeCompare(s.name)),xt=new Set(p.map(t=>t.name)),ce=x.flatMap(t=>(t.consumers??[]).flatMap(s=>s.consumer_id&&xt.has(s.script??"")?[{queueId:t.queue_id,queueName:t.queue_name,consumerId:s.consumer_id,script:s.script}]:[])).sort((t,s)=>t.queueName.localeCompare(s.queueName)||t.script.localeCompare(s.script));r("queueConsumers",ce.length,ce.map(t=>`${t.script} on ${t.queueName}`)),r("queues",ie.length,ie.map(t=>t.name));let de=(await a("the Vectorize indexes",[],()=>ht(e))).map(t=>t.name).filter(t=>l("vectorize",t)).sort();r("vectorize",de.length,de);let le=(await a("the AI Gateways",[],()=>lt(e,K))).map(t=>t.id).filter(t=>l("ai-gateway",t)).sort();r("aiGateway",le.length,le);let me=(await a("the Secrets Store secrets",[],async()=>{let t=[];for(let s of await ft(e))for(let u of await gt(e,s.id,K))l("secrets-store-secret",u.name)&&t.push({storeId:s.id,name:u.name,id:u.id});return t})).sort((t,s)=>t.name.localeCompare(s.name));r("secretsStore",me.length,me.map(t=>t.name));let P={applications:[],policy:null},Ce=await a("the Zero Trust team",null,()=>Pe(e));if(Ce){let t=y?{gatewayHost:y.gatewayHost.toLowerCase(),appBaseDomain:y.appBaseDomain.toLowerCase()}:null,s=await a("the Access applications",[],()=>Oe(e)),u=t?Fe(s,t):{gateway:null,apps:null},w=s.filter(k=>k.name===Me||k.name===Ue),h=new Map;for(let k of[u.gateway,u.apps,...w])k&&h.set(k.id,k);P.applications=[...h.values()].map(k=>({id:k.id,name:k.name??k.id}));let E=(await a("the Access policies",[],()=>_e(e))).find(k=>k.name===Ge);P.policy=E?{id:E.id,name:E.name}:null,c.push(`the Zero Trust team ${Ce.auth_domain} and its identity providers`)}r("access",P.applications.length+(P.policy?1:0),{applications:P.applications.map(t=>t.name),policy:P.policy?.name??null});let U=new Set;if(y){U.add(y.gatewayHost.toLowerCase());for(let t of y.liveDomains)U.add(t.toLowerCase())}for(let t of N)t.hostname&&U.add(t.hostname.toLowerCase());for(let t of p)for(let s of t.domains)U.add(s);let Ee=new Map((await a("the account's zones",[],()=>ct(e,n))).map(t=>[t.id,{id:t.id,name:t.name}])),B=new Map,Re=(t,s)=>{let u=Ee.get(t);u?B.set(u.id,u):c.push(`the zone ${s.toLowerCase()} (${t}), which isn't in account ${n}`)};y&&Re(y.zoneId,y.zoneName);for(let t of g)if(p.some(s=>s.name===t.name))for(let s of t.references?.domains??[])s.zone_id&&s.zone_name&&Re(s.zone_id,s.zone_name);let qt=t=>{let s=[...Ee.values()].filter(u=>Jt(t,u)).sort((u,w)=>w.name.length-u.name.length)[0];return s&&B.set(s.id,s),s??null},ue=new Map;for(let t of[...U].sort()){let s=qt(t);s&&ue.set(t,s)}for(let t of B.values())c.push(`the zone ${t.name}`);c.push("the Workers Paid subscription","the workers.dev subdomain");let Q=[];for(let t of[...B.values()].sort((s,u)=>s.name.localeCompare(u.name))){let s=await a(`Email Routing on ${t.name}`,null,async()=>({settings:await tt(e,t.id),rules:await nt(e,t.id),catchAll:await st(e,t.id)}));if(!s)continue;let{settings:u,rules:w,catchAll:h}=s,E=w.filter(b=>b.id!==null&&_(te(b)??"")),k=te(h),pe=k!==null&&_(k);if(E.length===0&&!pe)continue;let Wt=!h.enabled||h.actions.every(b=>b.type==="drop");Q.push({zoneId:t.id,zoneName:t.name,rules:E.map(b=>({id:b.id,address:it(b),worker:te(b)})),catchAllWorker:pe?k:null,disable:u.enabled&&E.length===w.length&&(pe||Wt)})}r("email",H(Q),Q);let Y=[],Ne=new Map;for(let[t,s]of ue){let u=Ne.get(s.id);u||(u=await a(`the certificate packs of ${s.name}`,[],()=>ne(e,s.id)),Ne.set(s.id,u));for(let w of vt(u,t))u.splice(u.indexOf(w),1),Y.push({zoneId:s.id,zoneName:s.name,hostname:t,packId:w.id})}r("certificates",Y.length,Y.map(t=>`${t.packId} (${t.hostname})`));let J=[];for(let[t,s]of[...ue].sort(([u],[w])=>u.localeCompare(w))){let u=await a(`the DNS records at ${t}`,[],()=>ee(e,s.id,t)),w=[...new Set(u.filter(h=>!Z(h)&&!re({type:h.type,content:h.content??null})).map(h=>h.type.toUpperCase()))].sort();w.length>0&&c.push(`the ${I(w)} ${w.length===1?"record":"records"} at ${t}, which EveryApp never created`),J.push({zoneId:s.id,zoneName:s.name,name:t,records:u.filter(Z).map(h=>({id:h.id,type:h.type,content:h.content??null}))})}r("dns",ve(J),J.filter(t=>t.records.length>0));let X={accountId:n,gateway:m?{databaseId:m,gatewayHost:y?.gatewayHost??null}:null,workers:p,workflows:$,d1:O,kv:z,r2:T,hyperdrive:L,queues:ie,queueConsumers:ce,vectorize:de,aiGateway:le,secretsStore:me,access:P,email:Q,certificates:Y,dns:J,kept:c};return i.emit({type:"step",step:$e,status:"ok",message:`Found ${v(D(X),"resource")} of EveryApp's in ${n}.`,data:{accountId:n,counts:G(X),total:D(X)}}),X}var Lt="teardown.check";async function vn(e){await Ye(e,n=>Xt({flags:e,out:n}))}async function Xt(e){let{flags:n,out:i}=e,r=e.env??process.env,c=e.createCf??(p=>Ie({accountId:p,env:r})),a=e.interactive??qe(n.json??!1),m=n["dry-run"]??!1;i.emit({type:"step",step:Lt,status:"start",message:"Checking the account may be torn down..."});let{accountId:d,allowlist:y}=bt({accountId:n["account-id"],out:i,env:r}),N=n.confirm?.trim().toLowerCase();if(N!==void 0&&N!==d)throw new C("TEARDOWN_CONFIRM_MISMATCH",`--confirm ${n.confirm} isn't the account id
18
+ It must repeat --account-id ${d} exactly. Nothing was read or changed.`,{fix:`pass --confirm ${d}`,data:{accountId:d,confirm:n.confirm}});It({accountId:d,allowlist:y,whoami:await Ke(c())}),i.emit({type:"step",step:Lt,status:"ok",message:`${d} is a test account, and the token sees it alone`,data:{accountId:d}});let S=c(d),o=await zt({cf:S,accountId:d,out:i}),l=D(o),A=_t(o);if(m){let p=l===0?`Nothing of EveryApp is left in ${d}.`:`Dry run: ${se(o)} would be removed from ${d}. Nothing was changed.`;i.emit({type:"done",message:p,display:[M.bold(l===0?p:`Would remove ${se(o)} from ${d}`),...l===0?A.slice(1):A,...l===0?[]:["",M.dim("Nothing was changed (--dry-run).")]].join(`
19
+ `),data:{accountId:d,dryRun:!0,removed:[],kept:[],plan:j(o)}});return}if(l===0){let p=`Nothing of EveryApp is left in ${d}.`;i.emit({type:"done",message:p,display:[M.green(p),...A.slice(1)].join(`
20
+ `),data:{accountId:d,dryRun:!1,removed:[],kept:[],plan:j(o)}});return}if(N===void 0){if(!a)throw new C("TEARDOWN_CONFIRMATION_REQUIRED",`Tearing down ${d} needs its id typed at a terminal, or --confirm ${d}
21
+ ${se(o)} would be deleted for good. Nothing was changed.`,{rerun:Se(["--confirm",d]),rerunNote:"only for a dedicated test account",authorize:!0,data:{accountId:d,plan:j(o)}});let p=(await(e.askConfirmation??eo)(d,A)).trim().toLowerCase();if(p!==d)throw new Te(p?`"${p}" doesn't match ${d}. Nothing deleted.`:"Cancelled. Nothing deleted.")}else a&&i.emit({type:"step",step:"teardown.plan",status:"ok",message:`Deleting everything EveryApp created in ${d}:`,data:{accountId:d,lines:A}});let g=await Ot({cf:S,plan:o,out:i,...e.sleep?{sleep:e.sleep}:{}});i.emit({type:"done",...to(d,o,g)})}function eo(e,n){return We(`Type ${e} to delete all of this`,{context:[`This deletes everything EveryApp created in Cloudflare account ${e}, for good:`,"",...n,""]})}function to(e,n,{removed:i,kept:r}){let c=r.length===0?`Removed ${v(i.length,"resource")} from ${e}.`:`Removed ${v(i.length,"resource")} from ${e}; kept ${r.length} (${I(r.map(m=>`${m.described}: ${m.reason}`))}).`,a=[M.green(`Removed ${v(i.length,"resource")} from ${e}`),...r.length>0?["",`Kept ${v(r.length,"resource")}:`,...r.map(m=>` ${m.described} ${M.dim(`\xB7 ${m.reason} \xB7 ${m.place}`)}`)]:[],...n.kept.length>0?["",M.dim(`Left as they were: ${n.kept.join("; ")}.`)]:[]].join(`
22
+ `);return{message:c,display:a,data:{accountId:e,dryRun:!1,removed:i,kept:r,plan:j(n)}}}export{vn as teardown,Xt as teardownAccount};
@@ -0,0 +1 @@
1
+ import{b as e}from"./chunk-XBD7PQN5.js";import{c as o}from"./chunk-V7BWZOSZ.js";import"./chunk-N4E5YE4X.js";import"./chunk-MEIWV7FJ.js";import"./chunk-QJRZFIYC.js";import"./chunk-TUXKBLLJ.js";import"./chunk-PDPUGYJW.js";import"./chunk-WVHJIYQ3.js";import"./chunk-GAPQSINH.js";import"./chunk-DWTJ4OLZ.js";import"./chunk-S2G4G73L.js";import"./chunk-BCTEWNN4.js";import"./chunk-YAZHTVUV.js";import"./chunk-BK4NK34C.js";import"./chunk-QKHLRLK3.js";import"./chunk-ORGV2NNT.js";import"./chunk-KVCL64LI.js";import"./chunk-LEEUODYR.js";import"./chunk-ASCNE7GS.js";import"./chunk-3EADVHFN.js";import"./chunk-HJGZGZFE.js";import"./chunk-LI3YLKLC.js";import"./chunk-EQLY5I46.js";import"./chunk-DCFHBCCW.js";import"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import"./chunk-AMCNAKYU.js";import"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import"./chunk-FQ2A7YAP.js";async function m(a){await e(a,(t,y)=>o(a,t,y))}export{m as update};
@@ -1,9 +1,9 @@
1
- import{a as w,b as X,e as v,f as Z,g as Q,h as $,i as b}from"./chunk-ELK7QZ2M.js";import{a as S,b as G,f as L,i as j,j as D,k as E,l as F,m as K,n as Y,o as q,p as B,q as J,r as V,s as H}from"./chunk-M2QQ2UD7.js";import{a as T}from"./chunk-4JRIS6S6.js";import{$ as W,qa as _,sa as P}from"./chunk-RMF4YGD6.js";import"./chunk-NFU4YFXB.js";import"./chunk-T2GD7RHS.js";import{a as R,i as N}from"./chunk-CCWBPAKH.js";import"./chunk-JYKRUS5C.js";import{Bb as f,Ia as x,c as h,f as y,k as z,oc as C,pc as U,qb as O,rc as M,wc as l}from"./chunk-LYNBZ7RV.js";import{stripVTControlCharacters as A}from"node:util";import d from"chalk";var u="workspace.update",ee="workspace.rename",te="workspace.role";async function Ue(e,n){await l(e,t=>ce({flags:e,out:t},n))}async function Me(e,n,t){await l(e,o=>re({flags:e,out:o},n,t))}function ie(e){let{flags:n,out:t}=e;return P({gateway:_(n.gateway),accountId:n["account-id"],interactive:R(n.json??!1),login:{json:n.json,out:t,step:"login"},createCf:e.createCf})}var ae=e=>`${/^[aeiou]/.test(e)?"an":"a"} ${e}`;async function re(e,n,t){let{out:o}=e,i=n.trim().toLowerCase(),c=await ie(e),s=await W(c.db);if(!s)throw new f("MEMBER_NOT_FOUND",`This Gateway has no workspace yet
2
- Nobody has claimed its owner link, so it has no members.`,{rerun:`${h} setup`,rerunNote:"to claim it"});let{organization:r}=s,m={id:r.id,name:r.name};o.emit({type:"step",step:te,status:"start",message:`Setting ${i}'s role...`});let a=await S(c.db,r.id,i,t);if(a.kind==="not_found")throw new f("MEMBER_NOT_FOUND",`No member ${i} in ${r.name}
1
+ import{a as w,b as X,e as v,f as Z,g as Q,h as $,i as b}from"./chunk-H4EZTXIK.js";import{f as S,g as G,k as L,n as j,o as D,p as E,q as F,r as K,s as Y,t as q,u as B,v as J,w as V,x as H}from"./chunk-QFTBDXLF.js";import"./chunk-GAPQSINH.js";import{a as T}from"./chunk-LEEUODYR.js";import{c as _,e as P}from"./chunk-DCFHBCCW.js";import{_ as W}from"./chunk-KOBKGQ27.js";import"./chunk-RL72BUT4.js";import"./chunk-TIYSZWP5.js";import{a as R,i as N}from"./chunk-I7FNTTUY.js";import"./chunk-RQBSTNL7.js";import{Hb as O,Na as x,Qc as C,Rc as U,Tc as M,Yc as l,bc as f,c as h,f as y,k as z}from"./chunk-FQ2A7YAP.js";import{stripVTControlCharacters as A}from"node:util";import d from"chalk";var u="workspace.update",ee="workspace.rename",te="workspace.role";async function Ue(e,n){await l(e,t=>ce({flags:e,out:t},n))}async function Me(e,n,t){await l(e,o=>re({flags:e,out:o},n,t))}function ie(e){let{flags:n,out:t}=e;return P({gateway:_(n.gateway),accountId:n["account-id"],interactive:R(n.json??!1),login:{json:n.json,out:t,step:"login"},createCf:e.createCf})}var ae=e=>`${/^[aeiou]/.test(e)?"an":"a"} ${e}`;async function re(e,n,t){let{out:o}=e,i=n.trim().toLowerCase(),c=await ie(e),s=await W(c.db);if(!s)throw new f("MEMBER_NOT_FOUND",`This Gateway has no workspace yet
2
+ Nobody has claimed its owner link, so it has no members.`,{rerun:`${h} setup`,rerunNote:"to claim it"});let{organization:r}=s,m={id:r.id,name:r.name};o.emit({type:"step",step:te,status:"start",message:`Setting ${i}'s role...`});let a=await S(c.db,r.id,i,t,c.deployer);if(a.kind==="not_found")throw new f("MEMBER_NOT_FOUND",`No member ${i} in ${r.name}
3
3
  Someone still invited isn't a member until they accept.`,{fix:"check the address against Manage workspace \u2192 Users, or invite them first",data:{workspace:m,email:i}});let p={id:a.member.id,email:a.member.email,name:a.member.name};if(a.kind==="last_owner")throw new f("LAST_OWNER",`${i} is the last owner of ${r.name}
4
4
  The workspace always keeps one owner, so their role stays until someone else is an owner.`,{rerun:`${h} workspace role <email> owner`,rerunNote:"for someone else, then this command again",data:{workspace:m,member:p,role:t}});let g=a.kind==="changed",k=g?`${i} is now ${ae(t)} of ${r.name}`:`${i} is already ${ae(t)} of ${r.name}`;o.emit({type:"step",step:te,status:"ok",message:k,data:{email:i,role:t,changed:g}});let I=g?"The Gateway picks up the change within 30 seconds.":"Nothing changed.";o.emit({type:"done",message:A(`${k}. ${I}`),display:[g?M(k):d.dim(`${k}.`),d.dim(I)].join(`
5
5
  `),data:{workspace:m,member:p,role:t,changed:g,...g?{previousRole:a.previousRole}:{}}})}async function ce(e,n){let{out:t}=e,o=x(n);if(!o)throw new f("WORKSPACE_NAME_INVALID",`Use 1 to ${64} characters with no line breaks
6
- A name is trimmed, and runs of spaces become one.`,{fix:"give the name as one argument, quoted when it has spaces"});let i=await ie(e);t.emit({type:"step",step:ee,status:"start",message:"Renaming the workspace..."});let{organization:c,previousName:s,changed:r}=await G(i.db,o);t.emit({type:"step",step:ee,status:"ok",message:r?`Renamed ${s} to ${o}`:`${o} already`,data:{id:c.id,name:o,previousName:s,changed:r}});let m=r?`Renamed the workspace to ${o}.`:`The workspace is named ${o} already.`,a=r?"Members see the name on their next page.":"Nothing changed.";t.emit({type:"done",message:A(`${m} ${a}`),display:[m,d.dim(a)].join(`
6
+ A name is trimmed, and runs of spaces become one.`,{fix:"give the name as one argument, quoted when it has spaces"});let i=await ie(e);t.emit({type:"step",step:ee,status:"start",message:"Renaming the workspace..."});let{organization:c,previousName:s,changed:r}=await G(i.db,o,i.deployer);t.emit({type:"step",step:ee,status:"ok",message:r?`Renamed ${s} to ${o}`:`${o} already`,data:{id:c.id,name:o,previousName:s,changed:r}});let m=r?`Renamed the workspace to ${o}.`:`The workspace is named ${o} already.`,a=r?"Members see the name on their next page.":"Nothing changed.";t.emit({type:"done",message:A(`${m} ${a}`),display:[m,d.dim(a)].join(`
7
7
  `),data:{organization:{id:c.id,name:c.name,slug:c.slug},previousName:s,changed:r,gateway:i.gatewayUrl}})}async function Se(e){await l(e,n=>de({flags:e,out:n}))}async function Ge(e){await l(e,n=>pe({flags:e,out:n}))}async function Le(e,n){await l(e,t=>le({flags:e,out:t},n,e.role))}async function je(e,n){await l(e,t=>ge({flags:e,out:t},n))}async function De(e,n){await l(e,t=>we({flags:e,out:t},n))}async function Fe(e,n){await l(e,t=>ue({flags:e,out:t},n))}async function Ke(e,n){await l(e,t=>ye({flags:e,out:t},n))}async function Ye(e,n){await l(e,t=>ve({flags:e,out:t},n))}async function qe(e,n){await l(e,t=>ke({flags:e,out:t},n))}var ne=["CLOUDFLARE_ACCESS_AUTO_JOIN_DOMAINS","CLOUDFLARE_ACCESS_OUTER_RING_AUTO_JOIN"];function me(e){return e.includes("EMAIL_FROM")&&(e.includes("RESEND_API_KEY")||e.includes("EMAIL_REST_API_TOKEN"))}async function de(e){let{out:n}=e,{gateway:t,ws:o,gatewayUrl:i}=await w(e),[c,s]=await Promise.all([j(o),T(t.cf,O)]),{organization:r,owners:m,counts:a}=c,p=o.operator,g=me(s),k=p.email===null?`${p.actor} (an API token; no account)`:p.userId?`${p.email}`:`${p.email} (no account on this Gateway)`,I=[{label:"Organization",value:`${r.name} ${d.dim(`(${r.slug}, ${r.id})`)}`},{label:"Gateway",value:d.cyan(i)},{label:"Owners",value:m.length>0?m.join(", "):d.yellow("none yet (claim the Gateway: everyapp setup)")},{label:"You",value:k},{label:"Email",value:g?"configured: the Gateway can mail invitations from Admin \u2192 Users":d.dim("not configured: this CLI prints links to hand over")},{label:"Members",value:`${a.members}${a.deactivated>0?d.dim(` (${a.deactivated} deactivated)`):""}`},{label:"Invitations",value:`${a.invitations} pending`},{label:"Groups",value:String(a.groups)},{label:"Apps",value:String(a.apps)}],oe=`${r.name}: ${y(a.members,"member")}, ${y(a.invitations,"pending invitation")}, ${y(a.groups,"group")}, ${y(a.apps,"app")}`;n.emit({type:"done",message:oe,display:C(I).join(`
8
8
  `),data:{organization:r,gateway:i,owners:m,operator:{actor:p.actor,email:p.email,userId:p.userId},email:{configured:g},counts:a}})}async function pe(e){let{out:n}=e,{ws:t}=await w(e),{members:o,invitations:i}=await D(t),c=[...o.map(a=>[a.email,a.name&&a.name!==a.email?a.name:d.dim("\u2013"),a.primaryRole??a.role,Q(a),v(a.createdAt)]),...i.map(a=>[a.email,d.dim("\u2013"),a.role,a.live?d.cyan(`invited, expires ${v(a.expiresAt)}`):d.dim("invitation expired"),v(a.createdAt)])],s=U(["EMAIL","NAME","ROLE","STATUS","SINCE"],c),r=i.filter(a=>!a.live).length,m=`${y(o.length,"member")} and ${y(i.length-r,"pending invitation")}${r>0?` (${r} expired)`:""} in ${t.organization.name}`;n.emit({type:"done",message:m,display:c.length>0?s.join(`
9
9
  `):d.dim(`Nobody in ${t.organization.name} yet.`),data:{organization:t.organization,members:o.map($),invitations:i.map(b)}})}async function le(e,n,t){let{out:o}=e,{ws:i,gatewayUrl:c}=await w(e);o.emit({type:"step",step:u,status:"start",message:`Inviting ${n}...`});let{invitation:s,token:r}=await K(i,n,t),m=E(c,r);o.emit({type:"step",step:u,status:"ok",message:`Invited ${s.email} as ${t}`,data:{invitationId:s.id}});let a=[`Invited ${s.email} to ${i.organization.name} as ${t}.`,`Send them this link (it is the invitation's only secret; it expires ${v(s.expiresAt)} UTC):`,` ${m}`,"The Gateway mailed nothing; Admin \u2192 Users mails invitations when email is configured."];o.emit({type:"done",message:A(a.join(`
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "everyapp",
3
3
  "author": "",
4
- "description": "Every App CLI - Deploy and manage apps.",
4
+ "description": "EveryApp CLI - Deploy and manage apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "version": "0.3.0-rc.2",
7
+ "version": "0.3.0-rc.4",
8
8
  "files": [
9
9
  "dist",
10
10
  "bin",
@@ -51,9 +51,9 @@
51
51
  "tsup": "^6.7.0",
52
52
  "typescript": "5.9.3",
53
53
  "vitest": "^3.2.4",
54
- "@every-app/sdk": "0.3.0-rc.2",
55
- "@every-app/toolbus": "0.1.0",
56
- "@every-app/perimeter": "0.1.0"
54
+ "@every-app/perimeter": "0.1.0",
55
+ "@every-app/sdk": "0.3.0-rc.4",
56
+ "@every-app/toolbus": "0.1.0"
57
57
  },
58
58
  "scripts": {
59
59
  "types:check": "tsc --noEmit",
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: build-every-app
3
- description: Build or change an app on Every App: create one, add a feature, a page, a table, an AI tool, a capability, a public route or a secret, the way the apps already on Every App do it. Use before writing any code for an Every App app, for "build me an app", "add X to the app" or "make the app do Y".
3
+ description: Build or change an app on EveryApp: create one, add a feature, a page, a table, an AI tool, a capability, a public route or a secret, the way the apps already on EveryApp do it. Use before writing any code for an EveryApp app, for "build me an app", "add X to the app" or "make the app do Y".
4
4
  ---
5
5
 
6
- # Build an app on Every App
6
+ # Build an app on EveryApp
7
7
 
8
- An Every App app is a private web app on the person's own Cloudflare account,
8
+ An EveryApp app is a private web app on the person's own Cloudflare account,
9
9
  behind their Gateway. The Gateway signs people in, holds every key, serves the
10
10
  app's tools to AI clients, and gives the app nothing it doesn't declare in
11
11
  `everyapp.config.ts`. You build the app; the plumbing is done.
@@ -15,7 +15,7 @@ app's tools to AI clients, and gives the app nothing it doesn't declare in
15
15
  1. `patterns.md`, beside this file (in an app,
16
16
  `.agents/skills/build-every-app/patterns.md`; in the checkout,
17
17
  `packages/cli/skills/build-every-app/patterns.md`; on the web,
18
- https://everyapp.com/docs/agent/patterns): how the apps on Every App do
18
+ https://everyapp.com/docs/agent/patterns): how the apps on EveryApp do
19
19
  each thing, and the file to copy from, in the starter or in one of four
20
20
  open apps (OpenCRM, OpenAnalytics, OpenSEO, Agentic Inbox), at
21
21
  `https://github.com/every-app/<name>`. Read the sections for the job
@@ -27,7 +27,7 @@ app's tools to AI clients, and gives the app nothing it doesn't declare in
27
27
  `--json`, setup, secrets, what only a person can do. Read it before running
28
28
  `pnpm everyapp`; never guess a flag.
29
29
  3. https://everyapp.com/llms.txt lists every agent page with a one-line
30
- summary; a page is Markdown at its URL plus `.md`. In an Every App checkout
30
+ summary; a page is Markdown at its URL plus `.md`. In an EveryApp checkout
31
31
  they are `landing-page/content/agent/*.md`. Read the page for the job:
32
32
  manifest (`everyapp.config.ts`: resources, public, providers,
33
33
  capabilities, secrets, migrations), identity (what the Worker receives,
@@ -110,11 +110,17 @@ becomes something else, not on the first feature.
110
110
  with `createGatewayFetch({ env, for: request })`; `ai.models` lists the
111
111
  models the app may ask for. The Gateway holds the key; a provider the
112
112
  manifest doesn't list is refused.
113
- 7. A capability (`members`, `email`, `dataforseo`, `context`, an OAuth client)
114
- under `capabilities` with `access` and a `why`; an OAuth client
115
- (`google-workspace`, `microsoft-365`, `slack`) also with `scopes: [...]`,
116
- the least that does the job. Called with `listMembers()`, `sendEmail()`,
117
- `integrationFetch()`; a refusal is a state the page shows, never a crash.
113
+ 7. A capability (`members`, `email`, `dataforseo`, `context`, an OAuth
114
+ provider) under `capabilities` with `access` and a `why`; an OAuth
115
+ provider (`google-workspace`, `microsoft-365`, `slack`, `github-oauth` or
116
+ any the Gateway's operator added) also with `scopes: [...]`, the least
117
+ that does the job. Called with `listMembers()`, `sendEmail()`,
118
+ `integrationFetch()`; for an OAuth provider the Gateway puts the
119
+ connected person's token on the call, so the app handles no token: on
120
+ `integrationStatus().connection === null` it shows a "Connect GitHub"
121
+ link to `connectUrl({ env, provider, returnTo })`, and on a
122
+ `integration_not_connected` refusal the same. A refusal is a state the
123
+ page shows, never a crash.
118
124
  8. Tools in `defineTools()` over the same `src/server/` queries, each with an
119
125
  honest `risk` (`read`, `write`, `destructive`), a description the model can
120
126
  act on, and `instructions` for the set. Throw `ToolError` for a message
@@ -140,9 +146,9 @@ becomes something else, not on the first feature.
140
146
  `EVERYAPP_IDENTITY_ISSUER`, or the dev host under `everyapp dev`; the four
141
147
  apps read `GATEWAY_URL` first, then the issuer); keep it. When the app
142
148
  gets a sidebar, the convention the identity page states is the first row:
143
- muted, "‹ Every App", aria-label "Back to Every App", above the app's own
149
+ muted, "‹ EveryApp", aria-label "Back to EveryApp", above the app's own
144
150
  name. OpenCRM and OpenSEO keep theirs in the sidebar footer today,
145
- labelled "Back to Every App".
151
+ labelled "Back to EveryApp".
146
152
  14. After editing `everyapp.config.ts`, restart `pnpm dev`: the local Gateway
147
153
  reads it when it starts (the tools catalog is the exception). Every
148
154
  manifest change reaches the real Gateway on the next deploy.
@@ -154,7 +160,10 @@ what the app is for; who uses it (just them, or everyone in the organization:
154
160
  then `access: "everyone"`); what it stores (the tables); what it does on its
155
161
  own (sends mail, calls a model, runs on a schedule, serves a script to a
156
162
  website); what stays private to each person. Repeat the answers back in one
157
- paragraph, then build. For a clear request, build.
163
+ paragraph, then build. For a clear request, build. The database is D1 unless
164
+ the person has a Postgres (Neon, Supabase, any URL) or asks for one: then
165
+ Hyperdrive ("Postgres through Hyperdrive" in `patterns.md`), and the person
166
+ puts its URL in `.everyapp/secrets.env` themselves.
158
167
 
159
168
  The loop: `pnpm everyapp create ../my-app` from the every-app folder (or work
160
169
  in the app you are in); the schema and the queries, the server functions, the
@@ -1,6 +1,6 @@
1
1
  # Example patterns
2
2
 
3
- How the apps on Every App do each thing, and the file to copy from. `starter/` is what `pnpm everyapp create` copies (in the Every App checkout); in an app `everyapp create` made, a `starter/` path is that path in the app. The four apps are open, at `https://github.com/every-app/<name>`, cited by their repository names: `open-crm/` (OpenCRM), `open-analytics/` (OpenAnalytics), `open-seo/` (OpenSEO) and `agentic-inbox/` (Agentic Inbox); a path is relative to that repository's root. Read a file before copying from it: each is short where it matters, and its comments say why. When a cited file is out of reach, the "What it is" and "Copy this" columns are the pattern; start from the starter's file when the row names one.
3
+ How the apps on EveryApp do each thing, and the file to copy from. `starter/` is what `pnpm everyapp create` copies (in the EveryApp checkout); in an app `everyapp create` made, a `starter/` path is that path in the app. The four apps are open, at `https://github.com/every-app/<name>`, cited by their repository names: `open-crm/` (OpenCRM), `open-analytics/` (OpenAnalytics), `open-seo/` (OpenSEO) and `agentic-inbox/` (Agentic Inbox); a path is relative to that repository's root. Read a file before copying from it: each is short where it matters, and its comments say why. When a cited file is out of reach, the "What it is" and "Copy this" columns are the pattern; start from the starter's file when the row names one.
4
4
 
5
5
  ## Identity
6
6
 
@@ -14,7 +14,7 @@ How the apps on Every App do each thing, and the file to copy from. `starter/` i
14
14
  | Identity on a public route | A public route is open to anyone, so it never assumes a member: `getEveryAppUser(request, env)` is the member when the Gateway signed one in (a member's session still travels on a public route) and `null` otherwise. The four apps' public routes read no identity at all: OpenAnalytics' collector checks the token its tracker was issued, OpenCRM's tracker takes the site from the id in the URL | `open-analytics/src/lib/edge/collector/collect.ts`, `open-analytics/src/lib/hono/routes/collect.ts`; `open-crm/src/server/features/tracking/trackingRoutes.ts` | A public route exists only when the app needs one (a webhook, a collector, an OAuth callback); it checks what it must itself and answers nothing that is anyone's without that check |
15
15
  | Organization-wide rows | Sharing by design: every table keys on the organization in the signed identity, every member sees the organization's rows, owners and admins manage. OpenCRM hangs every table off `organization_id`; OpenAnalytics' `teamId` is the organization; Agentic Inbox is one organization per deploy, every mailbox shared | `open-crm/docs/maintainers/every-app.md`, `open-crm/src/middleware/ensure-user/everyApp.ts`; `open-analytics/src/lib/edge/auth/viewer.ts`; `agentic-inbox/workers/everyapp/identity.ts` | The rule in words: authorization reads the organization and the role from the token on every request, never from a row the app wrote |
16
16
  | Roles from the Gateway | `owner`, `admin` and `member` from `user.orgRole`; anything else is a member | `open-crm/src/server/auth/org-gate.ts`; `agentic-inbox/workers/everyapp/identity.ts` | `requireOrgPermission(context, permissions)` throwing `FORBIDDEN`; `isAdmin(user)` |
17
- | The way back | A link to the Gateway. The starter's `launcher` is the identity issuer (`EVERYAPP_IDENTITY_ISSUER`, the Gateway's URL), or the dev host one label up under `everyapp dev`; the apps read `GATEWAY_URL` (set by the deploy, absent under `everyapp dev`) first, then the issuer. Where: the starter's header ("All apps"), OpenCRM's and OpenSEO's sidebar footer, Agentic Inbox's signed-in line ("Back to Every App"). The convention the identity page states for a new app: the first sidebar row, muted "‹ Every App", aria-label "Back to Every App" | `starter/src/serverFunctions/home.ts`, `starter/src/routes/index.tsx`; `open-crm/src/client/components/Sidebar.tsx`, `open-crm/src/serverFunctions/everyApp.ts`; `open-seo/src/client/components/Sidebar.tsx`; `agentic-inbox/app/routes/home.tsx`, `agentic-inbox/workers/everyapp/identity.ts` | `launcher` (the starter, dev-aware); `getEveryAppGatewayUrl()`: `GATEWAY_URL`, then `EVERYAPP_IDENTITY_ISSUER` |
17
+ | The way back | A link to the Gateway. The starter's `launcher` is the identity issuer (`EVERYAPP_IDENTITY_ISSUER`, the Gateway's URL), or the dev host one label up under `everyapp dev`; the apps read `GATEWAY_URL` (set by the deploy, absent under `everyapp dev`) first, then the issuer. Where: the starter's header ("All apps"), OpenCRM's and OpenSEO's sidebar footer, Agentic Inbox's signed-in line ("Back to EveryApp"). The convention the identity page states for a new app: the first sidebar row, muted "‹ EveryApp", aria-label "Back to EveryApp" | `starter/src/serverFunctions/home.ts`, `starter/src/routes/index.tsx`; `open-crm/src/client/components/Sidebar.tsx`, `open-crm/src/serverFunctions/everyApp.ts`; `open-seo/src/client/components/Sidebar.tsx`; `agentic-inbox/app/routes/home.tsx`, `agentic-inbox/workers/everyapp/identity.ts` | `launcher` (the starter, dev-aware); `getEveryAppGatewayUrl()`: `GATEWAY_URL`, then `EVERYAPP_IDENTITY_ISSUER` |
18
18
 
19
19
  ## Public routes with their own checks
20
20
 
@@ -42,6 +42,7 @@ How the apps on Every App do each thing, and the file to copy from. `starter/` i
42
42
  | `providers` with `why` | The AI provider the Gateway calls for the app; required unless `optional: true` | `open-crm/everyapp.config.ts` (`openrouter`), `agentic-inbox/everyapp.config.ts` (`openrouter` required, `workers-ai` optional) | One sentence for whoever sets the key |
43
43
  | `capabilities`: an API key | `dataforseo` and `context` with `access: "read"` and a `why`; `optional` where a feature can say what is missing | `open-seo/everyapp.config.ts` | `context: { access: "read", optional: true, why }` |
44
44
  | `capabilities`: an OAuth client with scopes | `google-workspace`, `microsoft-365` and `slack` with `access: "write"`, `scopes: [...]` (the least that does the job) and `optional: true`; the Gateway holds the client, the app keeps each member's tokens | `open-crm/everyapp.config.ts` | `scopes: ["gmail.read", "calendar.read"]` |
45
+ | `capabilities`: an OAuth provider the Gateway holds the tokens for | `github-oauth` (or `google-workspace`, `notion-oauth`, any the operator added) with `access: "read"`, `scopes: [...]` and `optional: true`; the person connects once in the Gateway, the app never sees a token | the OpenSEO port's `everyapp.config.ts` (`google-workspace` with `identity`, `search-console.read`, `analytics.read`) | `scopes: ["identity", "repo.read"]` |
45
46
  | `capabilities.email` and `capabilities.members` | Email as `access: "write"` (required for an inbox, optional for reports); members as `access: "read"` to know who can open the app | `agentic-inbox/everyapp.config.ts`, `open-analytics/everyapp.config.ts`, `starter/everyapp.config.ts` | The starter's `members` and `listMembers()` in `starter/src/serverFunctions/home.ts` |
46
47
  | `secrets`, required | The app's own root secret, with a `why` that says how to make one | `open-analytics/everyapp.config.ts` (`MAIN_SECRET`) | "32 or more random characters, e.g. openssl rand -base64 32" |
47
48
  | `secrets`, optional | Keys one feature needs, each with a `why`, and `docs` where a page exists (`GITHUB_TOKEN`); the feature says so where it is used | `open-crm/everyapp.config.ts` (`TOKEN_ENCRYPTION_KEY`, `GITHUB_TOKEN`, `OPENROUTER_MODEL`, `ERASURE_SECRET`) | `{ name, optional: true, why }`, plus `docs` when there is a page |
@@ -56,7 +57,17 @@ How the apps on Every App do each thing, and the file to copy from. `starter/` i
56
57
 
57
58
  | Pattern | What it is | Where | Copy this |
58
59
  | --- | --- | --- | --- |
60
+ | `resources.queues` | A Cloudflare Queue every Worker of the app sends to (`await env.JOBS.send(body)`), consumed by the app's own Worker (`consumer: true`) or a helper (`consumer: { worker }`), with Cloudflare's consumer settings and a dead-letter queue that is another of its queues. The deploy creates and records each queue, cf's upload makes the consumer, `pnpm dev` runs both locally in Miniflare. `everyApp()` returns only `{ fetch }`, so the consuming Worker exports `queue(batch, env)` beside it; without it the upload stops with `QUEUE_HANDLER_MISSING` | https://everyapp.com/docs/agent/manifest#queues (`landing-page/content/agent/manifest.md` in the checkout): the manifest example and the handler | `queues: [{ name: "JOBS", consumer: true }]`, then `export default { ...everyApp(handler, { tools }), async queue(batch, env) { for (const message of batch.messages) { …; message.ack(); } } }` |
61
+ | Postgres through Hyperdrive | Any Postgres with a URL (Neon, Supabase, PlanetScale Postgres, a server of your own): `resources.hyperdrive: [{ name: "HYPERDRIVE" }]`, `database: { provider: "postgres" }`, `migrations: { engine: "drizzle", dialect: "postgresql" }`, and the URL as `POSTGRES_DATABASE_URL` in `.everyapp/secrets.env`, which the person writes (Neon's and Supabase's direct URL, not the pooled one). The deploy makes the Hyperdrive config, caching off, and runs `drizzle-kit migrate` before the upload; the Worker opens a client per request on `env.HYPERDRIVE.connectionString`. OpenSEO's Postgres deployment runs this code; its `everyapp.config.ts` is the D1 shape its forks deploy | `open-seo/src/db/pg/client.ts`, `open-seo/src/db/provider.ts`, `open-seo/drizzle/pg.config.ts`; the manifest and the steps: https://everyapp.com/docs/agent/manifest#postgres | `withPgClient()`: `postgres(url, { max: 1, fetch_types: false })` inside each entrypoint, kept in `AsyncLocalStorage`, never in global scope |
62
+ | Turso | libSQL, not Postgres, so not Hyperdrive: the URL and token are the app's own secrets, and the client talks HTTP. No app here uses it | https://everyapp.com/docs/agent/manifest#postgres (its last paragraph) | `secrets: ["TURSO_DATABASE_URL", "TURSO_AUTH_TOKEN"]` and `createClient({ url, authToken })` from `@libsql/client/web`; `database.provider` stays `d1` or is left out |
59
63
  | Workers AI as a binding | `resources.ai` binds `env.AI` on every Worker of the app: `env.AI.run()` directly (the Agents SDK, models the OpenAI-compatible routes don't carry, an AI Gateway by id), billed to the account with no Gateway budget or cap. Under `pnpm dev` every call throws `Binding AI needs to be run remotely` until `vite.config.ts` sets `remoteBindings: true` and `cf auth login` has run. For calls the Gateway caps and counts, list the `workers-ai` provider instead | `packages/sdk/src/cloudflare/worker.ts` (`addAiBindings()`); no app uses it yet: `agentic-inbox/workers/everyapp/gateway.ts` (`gatewayAi()`) is the provider path for code written against `env.AI.run()` | `resources: { ai: ["AI"] }`, then `await env.AI.run("@cf/meta/llama-3.1-8b-instruct", { prompt })` |
64
+ | Browser Rendering | `resources.browser` binds `env.BROWSER` on every Worker of the app: a headless Chrome for screenshots, PDFs and pages that need JavaScript, driven with `@cloudflare/puppeteer` or `@cloudflare/playwright`, billed to the account (Workers Paid). Local under `pnpm dev`: the first session downloads Chrome into the global `.wrangler` cache | `packages/sdk/src/cloudflare/worker.ts` (`addBrowserBindings()`); no app uses it yet | `resources: { browser: ["BROWSER"] }`, then `const browser = await puppeteer.launch(env.BROWSER)` |
65
+ | Images | `resources.images` binds `env.IMAGES` on every Worker of the app: resize, crop, re-encode and read images from any stream (an upload, an R2 object, a `fetch()` body), transformations billed to the account. Local under `pnpm dev` through Miniflare's sharp (`503`, code `9523`, where sharp can't load) | `packages/sdk/src/cloudflare/worker.ts` (`addImagesBindings()`); no app uses it yet | `resources: { images: ["IMAGES"] }`, then `(await env.IMAGES.input(body).transform({ width: 256 }).output({ format: "image/webp" })).response()` |
66
+ | `resources.vectorize` | A Vectorize index for search by embedding: `{ name, dimensions, metric }` for your own vectors, or `{ name, preset }` for a Workers AI model's; bound as `env.<NAME>` (a `VectorizeIndex`) on every Worker of the app; the deploy creates `<worker>--<binding>` and its shape never changes after (rename the binding for a new one). Dev has no local Vectorize: calls throw until `vite.config.ts` sets `remoteBindings: true` and `cf auth login` has run | `packages/cli/src/commands/deploy/deployApp.vectorize.test.ts` (`NOTES`), [the manifest page](/docs/agent/manifest#vectorize) | `resources: { vectorize: [{ name: "VECTORS", dimensions: 768, metric: "cosine" }] }`, then `env.VECTORS.upsert([{ id, values, metadata }])` and `env.VECTORS.query(values, { topK: 5 })`; keep the rows themselves in D1 and only the vectors here |
67
+ | `resources.aiGateway` | An AI Gateway of the app's own (`every-<orgKey>-<id>-<binding>`), bound on every Worker as `env.AI_GATEWAY` = `{ id, accountId, url }`, `url` empty in development; it logs, caches and counts the model calls the app makes itself, with its own key or Workers AI. Separate from AI through the Gateway (below), whose keys and gateway are the Gateway's | [`/docs/agent/manifest`](/docs/agent/manifest) (AI Gateway); no app uses one yet | `aiGateway: [{ name: "AI_GATEWAY" }]`; `baseURL: env.AI_GATEWAY.url ? env.AI_GATEWAY.url + "/openai" : undefined`, or `env.AI.run(model, input, { gateway: { id: env.AI_GATEWAY.id } })` |
68
+ | Email sending as a binding | `resources.sendEmail` binds Cloudflare's own `send_email` binding on every Worker of the app, beside the Gateway's `email` capability (which keeps budgets, the installation's senders and a log, and stays the default). For code written against `env.EMAIL.send()`, or mail an inbound `email()` handler sends beyond `message.reply()`. The deploy makes nothing and holds the built binding to the declared addresses; Cloudflare checks the sender's Email Sending domain when the Worker sends; under `everyapp dev` Miniflare logs the message | `packages/sdk/src/cloudflare/worker.ts` (`addSendEmailBindings()`); no app uses it yet; [/docs/agent/manifest](/docs/agent/manifest#email-sending) | `sendEmail: [{ name: "EMAIL", allowedSenderAddresses: ["noreply@example.com"] }]`: `allowedSenderAddresses` is required |
69
+ | A Secrets Store secret | `resources.secretsStore` binds a secret in the account's Cloudflare Secrets Store, `<worker>--<binding>`, on every Worker of the app: managed, rotated and audited in the dashboard, read with `await env.STRIPE_KEY.get()`. The first deploy creates it from `STRIPE_KEY` in `.everyapp/secrets.env` and never rewrites it; the account needs a store made once in the dashboard (`SECRETS_STORE_MISSING`). Under `pnpm dev` the local store is empty until seeded (`/docs/agent/dev`). For a key only this app reads, an app secret (`secrets`) is simpler | `packages/sdk/src/cloudflare/worker.ts` (`addSecretsStoreBindings()`), `packages/cli/src/lib/app/resources.ts` (`provisionSecretsStore()`); no app uses it yet | `resources: { secretsStore: [{ name: "STRIPE_KEY" }] }`, then `const key = await env.STRIPE_KEY.get()` |
70
+ | Analytics Engine | `resources.analyticsEngine: [{ name: "EVENTS" }]` binds `env.EVENTS` on every Worker of the app: `writeDataPoint({ blobs, doubles, indexes })` for usage and event counts, read back through the SQL API with the dataset's name double-quoted (`FROM "every-acme-shop--events"`). The dataset is named after the Worker unless the entry names one; nothing to create or delete (data expires after 90 days); writes do nothing under `pnpm dev` | `packages/sdk/src/cloudflare/worker.ts` (`addAnalyticsEngineBindings()`); no app uses it yet | `resources: { analyticsEngine: [{ name: "EVENTS" }] }`, then `env.EVENTS.writeDataPoint({ blobs: [path], doubles: [ms], indexes: [orgId] })` |
60
71
 
61
72
  ## Sending and receiving email
62
73
 
@@ -104,6 +115,8 @@ Every app says, inside itself, what the Gateway hasn't got yet: the setting's na
104
115
  | A paused agent | A checklist of the Gateway's keys for the agent (`OPENROUTER_API_KEY`, `CONTEXT_API_KEY`): done, checking or missing, each with what being unset costs and the command an owner or admin runs; members are told whom to ask | `open-crm/src/server/features/setup/services/SetupService.ts`, `open-crm/src/client/features/agent/AgentPaused.tsx`, `open-crm/src/serverFunctions/setup.ts` | `pausedResearchMessage(canManage)`: two sentences, by role |
105
116
  | A required secret | Without the root secret every page redirects to one that names the secret and the command (`pnpm everyapp secret set MAIN_SECRET --app analytics`), or `.dev.vars` under `everyapp dev` | `open-analytics/src/middleware.ts`, `open-analytics/src/routes/$locale.runtime-config-error.tsx` | `loadRuntimeSetupMode()` asks the Worker whether it runs under `everyapp dev` |
106
117
  | A capability | `integrationStatus({ env, provider }).ready` without calling the provider, driving the "set up DataForSEO" prompt | `open-seo/src/server/lib/provider-access.ts`, `open-seo/src/serverFunctions/config.ts` | `hasProviderAccess()` |
118
+ | An OAuth account that isn't connected yet | `integrationStatus({ env, provider, for })`: `connection === null` shows a "Connect GitHub" link to `connectUrl({ env, provider, returnTo: request.url })`; `connection.reconnect` shows "Reconnect"; a call's `integration_not_connected` refusal (`gatewayRefusal()`) carries `connectPath` for the same link | the OpenSEO port's `src/server/features/google/googleAccess.ts` | `connectUrl({ env, provider: "google-workspace", returnTo })` |
119
+ | Calling an OAuth provider's API with the person's token | `integrationFetch(url, { env, provider, for })` to the provider's real URL with no `Authorization` header: the Gateway puts the token on; `account: "organization"` for work no person started | the OpenSEO port's `src/server/lib/gscClient.ts` | `integrationFetch("https://www.googleapis.com/webmasters/v3/sites", { env, provider: "google-workspace", for })` |
107
120
  | Email | A banner before the person writes: no sender allowed yet (`everyapp email set --sender`) or no `EMAIL_FROM`; owners and admins get the command with the mailbox's domain filled in, members whom to ask; Send stays enabled, the server decides | `agentic-inbox/app/components/SendingOffBanner.tsx` | `config.ready === false` from `GET /api/v1/config` |
108
121
 
109
122
  ## Cookies, headers and the bundle
@@ -135,6 +148,6 @@ Every app says, inside itself, what the Gateway hasn't got yet: the setting's na
135
148
 
136
149
  | Pattern | What it is | Where | Copy this |
137
150
  | --- | --- | --- | --- |
138
- | Release and install | `pnpm everyapp release` packs the production build, the migrations and a `release.json` into `everyapp-release.tar.gz`; from a GitHub release of the app's repository, anyone installs it with `pnpm everyapp install <repository url>`, no clone. `pnpm pack-for-app <app dir> --workflow` from an Every App checkout vendors the CLI and the SDK into the app's repository (`file:` tarballs under `vendor/`) and adds the Release workflow that publishes the tarball on every push. The vendored CLI runs `dev`, `db migrate` and `release`, not `deploy`: deploys run from an Every App checkout (`pnpm everyapp install <url>` there, or that checkout's CLI from the app's directory). OpenCRM, OpenSEO and Agentic Inbox bind an R2 bucket, so R2 is enabled once on the account before their first install (`R2_NOT_ENABLED` otherwise); OpenAnalytics needs nothing | `docs/runbooks/app-releases.md`, `scripts/pack-for-app.mjs`, `scripts/pack-for-app/release.yml` and `landing-page/content/agent/deploy.md` ("Release installs") in the checkout; `open-crm/docs/maintainers/releases.md` (tags, notes) | `pnpm pack-for-app <app dir> --workflow` once, then commit `vendor/`, `package.json`, `pnpm-lock.yaml` and the workflow; after a platform change the same command without `--workflow`; the version in `package.json` names the release |
151
+ | Release and install | `pnpm everyapp release` packs the production build, the migrations and a `release.json` into `everyapp-release.tar.gz`; from a GitHub release of the app's repository, anyone installs it with `pnpm everyapp install <repository url>`, no clone. `pnpm pack-for-app <app dir> --workflow` from an EveryApp checkout vendors the CLI and the SDK into the app's repository (`file:` tarballs under `vendor/`) and adds the Release workflow that publishes the tarball on every push. The vendored CLI runs `dev`, `db migrate` and `release`, not `deploy`: deploys run from an EveryApp checkout (`pnpm everyapp install <url>` there, or that checkout's CLI from the app's directory). OpenCRM, OpenSEO and Agentic Inbox bind an R2 bucket, so R2 is enabled once on the account before their first install (`R2_NOT_ENABLED` otherwise); OpenAnalytics needs nothing | `docs/runbooks/app-releases.md`, `scripts/pack-for-app.mjs`, `scripts/pack-for-app/release.yml` and `landing-page/content/agent/deploy.md` ("Release installs") in the checkout; `open-crm/docs/maintainers/releases.md` (tags, notes) | `pnpm pack-for-app <app dir> --workflow` once, then commit `vendor/`, `package.json`, `pnpm-lock.yaml` and the workflow; after a platform change the same command without `--workflow`; the version in `package.json` names the release |
139
152
 
140
153
  A pattern missing here: read the four manifests first, then search the app for the SDK export you need (`createGatewayFetch`, `integrationFetch`, `sendEmail`, `listMembers`, `defineTools`).