everyapp 0.3.0-rc.4 → 0.3.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/dist/access-DNIB63DI.js +1 -0
  2. package/dist/{activity-RTCAUMQS.js → activity-X4RPCV3W.js} +1 -1
  3. package/dist/ai-GHPXRRZK.js +6 -0
  4. package/dist/{apps-PEOBCDNU.js → apps-2OAZFYU3.js} +1 -1
  5. package/dist/{chunk-H4EZTXIK.js → chunk-23CPMF6I.js} +1 -1
  6. package/dist/chunk-2PP3ZU44.js +20 -0
  7. package/dist/chunk-4JP237FP.js +18 -0
  8. package/dist/{chunk-QFTBDXLF.js → chunk-52ZPBQ2M.js} +52 -52
  9. package/dist/chunk-5AUOAM57.js +1 -0
  10. package/dist/chunk-5DTY6SR4.js +40 -0
  11. package/dist/chunk-62XLUW2Z.js +8 -0
  12. package/dist/chunk-6BVZ7OOC.js +2 -0
  13. package/dist/{chunk-3EADVHFN.js → chunk-753QJBIK.js} +1 -1
  14. package/dist/chunk-7IO5QSK4.js +8 -0
  15. package/dist/chunk-AGGUD3LY.js +93 -0
  16. package/dist/{chunk-QKHLRLK3.js → chunk-AHAKMPNT.js} +1 -1
  17. package/dist/{chunk-EQLY5I46.js → chunk-B2UHEXGS.js} +1 -1
  18. package/dist/chunk-BL7INQ2V.js +27 -0
  19. package/dist/chunk-CU4WS7DF.js +1 -0
  20. package/dist/chunk-CV45436M.js +1 -0
  21. package/dist/chunk-D5HKHQTL.js +1 -0
  22. package/dist/{chunk-VVKQJSF3.js → chunk-DNI45OIP.js} +1 -1
  23. package/dist/chunk-DRDHAGMO.js +2 -0
  24. package/dist/{chunk-O64FPLGX.js → chunk-EOALGESI.js} +1 -1
  25. package/dist/chunk-EZLXWXCD.js +1 -0
  26. package/dist/chunk-F2SWGK4U.js +1 -0
  27. package/dist/{chunk-TIYSZWP5.js → chunk-G6E6ASCF.js} +1 -1
  28. package/dist/{chunk-NDOEON54.js → chunk-GCRYALFY.js} +1 -1
  29. package/dist/{chunk-RL72BUT4.js → chunk-GGZLG3MC.js} +1 -1
  30. package/dist/chunk-GJRF7PIC.js +5 -0
  31. package/dist/chunk-GP5EMUH3.js +1 -0
  32. package/dist/chunk-GSJHO4P7.js +1 -0
  33. package/dist/{chunk-BK4NK34C.js → chunk-GUS7QNHK.js} +1 -1
  34. package/dist/chunk-H4FU65LN.js +2 -0
  35. package/dist/chunk-HOR4CK57.js +8 -0
  36. package/dist/{chunk-TUXKBLLJ.js → chunk-J2YDBF6F.js} +1 -1
  37. package/dist/{chunk-KQM6ZUX5.js → chunk-JH5EEU6E.js} +1 -1
  38. package/dist/{chunk-SGRGD7L4.js → chunk-JQSU5VM5.js} +1 -1
  39. package/dist/{chunk-AMCNAKYU.js → chunk-KQAA5HYI.js} +1 -1
  40. package/dist/{chunk-ORGV2NNT.js → chunk-LGVZOR22.js} +1 -1
  41. package/dist/chunk-LWMA2GWN.js +1 -0
  42. package/dist/{chunk-I7FNTTUY.js → chunk-MDNG2D3M.js} +1 -1
  43. package/dist/{chunk-MEIWV7FJ.js → chunk-N56HPZCZ.js} +1 -1
  44. package/dist/chunk-OH5GXYX2.js +29 -0
  45. package/dist/{chunk-R7JOHK7Y.js → chunk-OPSXAHNF.js} +1 -1
  46. package/dist/chunk-ORHNWNQ4.js +2 -0
  47. package/dist/chunk-P3YEHCHV.js +14 -0
  48. package/dist/chunk-PBHIXZKL.js +2 -0
  49. package/dist/chunk-PRGCYHQF.js +45 -0
  50. package/dist/{chunk-FB2OC32C.js → chunk-R5MABGNW.js} +3 -3
  51. package/dist/chunk-SAOL43T3.js +47 -0
  52. package/dist/{chunk-LI3YLKLC.js → chunk-SAVGRJLI.js} +1 -1
  53. package/dist/chunk-SKEL7TJM.js +1 -0
  54. package/dist/chunk-TC2TNQFK.js +22 -0
  55. package/dist/chunk-VDYNSRWF.js +46 -0
  56. package/dist/chunk-X22RKNHX.js +4 -0
  57. package/dist/{chunk-YAZHTVUV.js → chunk-X5BYKHDN.js} +1 -1
  58. package/dist/chunk-Y32TS22S.js +10 -0
  59. package/dist/chunk-YAP4HLRA.js +9 -0
  60. package/dist/configure-ZRZQK6YU.js +1 -0
  61. package/dist/create-KGP7AQTX.js +15 -0
  62. package/dist/{db-B7KO37SV.js → db-PUVRWGR7.js} +1 -1
  63. package/dist/delete-XM6I4PG5.js +3 -0
  64. package/dist/deploy-NMR2BDBR.js +1 -0
  65. package/dist/dev-4U62GQHK.js +86 -0
  66. package/dist/{email-SWJQXEUR.js → email-OPJYM2LS.js} +1 -1
  67. package/dist/{events-GJV2KVIU.js → events-EKT2E65G.js} +1 -1
  68. package/dist/{groups-NNTLSXMK.js → groups-ZUPQUQTD.js} +1 -1
  69. package/dist/index.js +98 -92
  70. package/dist/install-7TEG6AFG.js +1 -0
  71. package/dist/login-6E2PBHMI.js +1 -0
  72. package/dist/mcp-NEHEN57H.js +1 -0
  73. package/dist/provider-7RNFWF7W.js +1 -0
  74. package/dist/release-CYJINL2Q.js +2 -0
  75. package/dist/restore-37UK7CR7.js +3 -0
  76. package/dist/scopeSets-ML62IVVO.js +5 -0
  77. package/dist/secret-RDMTPPDM.js +1 -0
  78. package/dist/setup-SRC6JZEW.js +1 -0
  79. package/dist/{signin-EP5JLAT5.js → signin-HHNCDYGV.js} +1 -1
  80. package/dist/skills-OWAJGHBJ.js +2 -0
  81. package/dist/status-JJLUUQR7.js +5 -0
  82. package/dist/teardown-M6KPZCTY.js +22 -0
  83. package/dist/token-QRSF2UFM.js +1 -0
  84. package/dist/update-UQHWUVIE.js +1 -0
  85. package/dist/{workspace-VW7ZZRFP.js → workspace-65U26ZPG.js} +1 -1
  86. package/package.json +8 -6
  87. package/skills/build-every-app/SKILL.md +32 -25
  88. package/skills/build-every-app/patterns.md +45 -40
  89. package/skills/everyapp/SKILL.md +18 -10
  90. package/skills/everyapp/mcp.md +8 -3
  91. package/skills/everyapp/setup/README.md +13 -11
  92. package/skills/everyapp/setup/access.md +1 -1
  93. package/skills/everyapp/setup/ai-gateway.md +1 -1
  94. package/skills/everyapp/setup/ai.md +8 -5
  95. package/skills/everyapp/setup/email.md +5 -5
  96. package/skills/everyapp/setup/gateway.md +7 -8
  97. package/skills/everyapp/setup/google.md +16 -11
  98. package/skills/everyapp/setup/openai.md +1 -1
  99. package/skills/everyapp/setup/openrouter.md +16 -16
  100. package/skills/everyapp/setup/providers.md +11 -206
  101. package/skills/everyapp/setup/workers-ai.md +11 -11
  102. package/starter/.github/workflows/release.yml +30 -0
  103. package/starter/.prettierignore +8 -0
  104. package/starter/AGENTS.md +35 -0
  105. package/starter/CLAUDE.md +1 -0
  106. package/starter/README.md +35 -0
  107. package/starter/cloudflare.config.ts +11 -0
  108. package/starter/drizzle/0000_todos.sql +7 -0
  109. package/starter/drizzle/meta/0000_snapshot.json +65 -0
  110. package/starter/drizzle/meta/_journal.json +13 -0
  111. package/starter/drizzle.config.ts +7 -0
  112. package/starter/everyapp.config.ts +19 -0
  113. package/starter/gitignore +20 -0
  114. package/starter/knip.jsonc +20 -0
  115. package/starter/package.json +48 -0
  116. package/starter/public/apple-touch-icon.png +0 -0
  117. package/starter/public/favicon.ico +0 -0
  118. package/starter/src/db/index.ts +5 -0
  119. package/starter/src/db/schema.ts +13 -0
  120. package/starter/src/entry.worker.ts +9 -0
  121. package/starter/src/env.d.ts +5 -0
  122. package/starter/src/routeTree.gen.ts +86 -0
  123. package/starter/src/router.tsx +6 -0
  124. package/starter/src/routes/__root.tsx +34 -0
  125. package/starter/src/routes/health.ts +18 -0
  126. package/starter/src/routes/index.tsx +264 -0
  127. package/starter/src/server/events.test.ts +50 -0
  128. package/starter/src/server/events.ts +30 -0
  129. package/starter/src/server/todos.test.ts +49 -0
  130. package/starter/src/server/todos.ts +74 -0
  131. package/starter/src/serverFunctions/home.ts +61 -0
  132. package/starter/src/serverFunctions/todos.ts +31 -0
  133. package/starter/src/styles/app.css +5 -0
  134. package/starter/src/tools.test.ts +61 -0
  135. package/starter/src/tools.ts +59 -0
  136. package/starter/tsconfig.json +18 -0
  137. package/starter/vite.config.ts +29 -0
  138. package/starter/vitest.config.ts +8 -0
  139. package/dist/access-YWM4BSBD.js +0 -1
  140. package/dist/ai-UOYQFTAX.js +0 -5
  141. package/dist/chunk-26KS6TD6.js +0 -1
  142. package/dist/chunk-4LKLQOM7.js +0 -7
  143. package/dist/chunk-5IC3ZN52.js +0 -1
  144. package/dist/chunk-ASCNE7GS.js +0 -2
  145. package/dist/chunk-BCTEWNN4.js +0 -6
  146. package/dist/chunk-DCFHBCCW.js +0 -9
  147. package/dist/chunk-DWTJ4OLZ.js +0 -6
  148. package/dist/chunk-EP3IQ354.js +0 -20
  149. package/dist/chunk-EP4HRYXP.js +0 -2
  150. package/dist/chunk-F5XK23HV.js +0 -1
  151. package/dist/chunk-FQ2A7YAP.js +0 -57
  152. package/dist/chunk-GAPQSINH.js +0 -1
  153. package/dist/chunk-GJK6GAWO.js +0 -1
  154. package/dist/chunk-HJGZGZFE.js +0 -32
  155. package/dist/chunk-KOBKGQ27.js +0 -91
  156. package/dist/chunk-KVCL64LI.js +0 -1
  157. package/dist/chunk-LEEUODYR.js +0 -1
  158. package/dist/chunk-MLTNW3AM.js +0 -8
  159. package/dist/chunk-NW7SJPSP.js +0 -37
  160. package/dist/chunk-PDPUGYJW.js +0 -47
  161. package/dist/chunk-R2DF3ENZ.js +0 -2
  162. package/dist/chunk-S2G4G73L.js +0 -18
  163. package/dist/chunk-V7BWZOSZ.js +0 -26
  164. package/dist/chunk-WVHJIYQ3.js +0 -14
  165. package/dist/chunk-XBD7PQN5.js +0 -2
  166. package/dist/configure-CAWCTPYY.js +0 -1
  167. package/dist/create-3ZWJQXPK.js +0 -22
  168. package/dist/delete-CB3EPOU4.js +0 -3
  169. package/dist/deploy-5O7LV5OX.js +0 -1
  170. package/dist/dev-4JYCVVOZ.js +0 -86
  171. package/dist/install-UAG3CNYW.js +0 -1
  172. package/dist/login-QEKMLMUB.js +0 -1
  173. package/dist/mcp-AFGVOM5D.js +0 -1
  174. package/dist/provider-622VM7UZ.js +0 -29
  175. package/dist/release-CXEX6O7F.js +0 -1
  176. package/dist/restore-PGIVEA5P.js +0 -3
  177. package/dist/secret-3LUDEDUW.js +0 -1
  178. package/dist/setup-PUD375AM.js +0 -1
  179. package/dist/status-LK6DNLCI.js +0 -5
  180. package/dist/teardown-6RURRSME.js +0 -22
  181. package/dist/update-YJFTOZEV.js +0 -1
@@ -1,92 +1,92 @@
1
- import{a as Ue}from"./chunk-GAPQSINH.js";import{A as z,B as ue,V as $e,Y as u,_ as Z,w as W,y as ze}from"./chunk-KOBKGQ27.js";import{e as O}from"./chunk-RL72BUT4.js";import{N as De,O as ce,P as pe,Q as ye,R as ve,Ya as Ie,bc as m,c as _,cb as ke,k as K,lb as we,nb as Me,ob as Le,qb as We}from"./chunk-FQ2A7YAP.js";var Pe=.01,xe=1e6,Fe=new WeakMap;function $(e){let a=Fe.get(e);return a||(a=e.run([{sql:"PRAGMA table_info(org_app_settings)"},{sql:"PRAGMA table_info(org_settings)"}]).then(([n=[],t=[]])=>n.some(i=>i.name==="monthly_cost_cap_micros")&&t.some(i=>i.name==="default_monthly_cost_cap_micros")),Fe.set(e,a)),a}async function me(e){if(!await $(e))throw new m("GATEWAY_PREDATES_COST_CAPS",`The Gateway predates dollar caps
2
- Its database has no monthly cap in dollars until the Gateway is updated to a release that has it.`,{rerun:`${_} update`,rerunNote:"then run this again"})}function ge(e){if(e==null)return e;let a=Math.round(e*100);if(!Number.isFinite(e)||Math.abs(e*100-a)>1e-6||e<Pe||e>xe)throw new m("INVALID_CAP",`A monthly dollar cap is from $${Pe} to $${xe.toLocaleString("en-US")} in whole cents, or off`);return a*1e4}var Y=e=>e===null?null:Math.round(e/1e4)/100;function Q(e){return["default_monthly_call_cap IS NULL",...e.cost?["default_monthly_cost_cap_micros IS NULL"]:[],...e.ai?["ai_default IS NULL"]:[]].join(" AND ")}var qe=new WeakMap;function ee(e){let a=qe.get(e);return a||(a=e.run([{sql:"PRAGMA table_info(org_settings)"}]).then(([n=[]])=>n.some(t=>t.name==="ai_default")),qe.set(e,a)),a}async function Oa(e){if(!await ee(e))throw new m("GATEWAY_PREDATES_AI_DEFAULT",`The Gateway predates the default model provider
3
- Its database has no ai_default column until the Gateway is updated to a release that has it.`,{rerun:`${_} update`,rerunNote:"then run this again"})}async function Ge(e,a){if(!await ee(e))return null;let[n=[]]=await e.run([{sql:"SELECT ai_default FROM org_settings WHERE organization_id = ?",params:[a]}]),t=n[0]?.ai_default;return ce(typeof t=="string"?t:null)}var He=e=>e===null?null:{provider:e.provider,model:e.model??null};async function en(e,a){if(a){let l=ye(a);if(l)throw new m("AI_DEFAULT_INVALID",`${a.provider} can't be the default model provider: ${l}`,{fix:`pick one of the providers ${_} ai list shows, as <provider> or <provider>/<model>`})}await Oa(e.db);let n=e.organization.id,t=await Ge(e.db,n),i=a===null?null:pe(a),r=(t===null?null:pe(t))!==i,d=r?await z(e.db,{organizationId:n,type:"ai.default_changed",actor:W(e.operator),data:{aiDefault:He(a),previous:He(t)},now:e.now}):[];return await e.db.run([{sql:`INSERT INTO org_settings (organization_id, ai_default, updated_at, updated_by)
4
- VALUES (?1, ?2, ?3, ?4)
5
- ON CONFLICT (organization_id) DO UPDATE SET ai_default = ?2, updated_at = ?3, updated_by = ?4`,params:[n,i,e.now,e.operator.userId]},{sql:`DELETE FROM org_settings WHERE organization_id = ? AND ${Q({cost:await $(e.db),ai:!0})}`,params:[n]},...d]),{choice:a,was:t,changed:r}}async function an(e,a){let[n,t]=await Promise.all([Ue(e),Ge(a.db,a.organization.id)]),i=d=>n.includes(d);return{resolution:ve({choice:t,isSet:i}),names:n,aiGateway:{on:De.every(i)}}}function nn(e){let a=e.trim();if(a==="off"||a==="none")return null;let n=ce(a);if(!n)throw new m("AI_DEFAULT_INVALID",`${e} isn't a provider: write the provider's id (openrouter), or the id and a model (openrouter/anthropic/claude-haiku-5.5), or off`,{fix:`${_} ai list names the providers the default can be`});return n}var Be=`SELECT users.id AS id, users.email AS email, users.name AS name, members.role AS role
1
+ import{a as Be}from"./chunk-CV45436M.js";import{$ as Z,B as M,C as ue,W as Ge,Z as u,x as w,z as He}from"./chunk-AGGUD3LY.js";import{e as O}from"./chunk-GGZLG3MC.js";import{Q as ve,R as Ie,S as ke,T as ce,U as we,V as Me,W as Le,X as We,Y as pe,Z as ze,a as _,fb as $e,i as K,lb as Ue,lc as m,ub as Pe,wb as xe,xb as Fe,zb as qe}from"./chunk-PRGCYHQF.js";var Ye=.01,Je=1e6,Ve=new WeakMap;function $(e){let a=Ve.get(e);return a||(a=e.run([{sql:"PRAGMA table_info(org_app_settings)"},{sql:"PRAGMA table_info(org_settings)"}]).then(([n=[],t=[]])=>n.some(i=>i.name==="monthly_cost_cap_micros")&&t.some(i=>i.name==="default_monthly_cost_cap_micros")),Ve.set(e,a)),a}async function me(e){if(!await $(e))throw new m("GATEWAY_PREDATES_COST_CAPS",`The Gateway predates dollar caps
2
+ Its database has no monthly cap in dollars until the Gateway is updated to a release that has it.`,{rerun:`${_} update`,rerunNote:"then run this again"})}function ge(e){if(e==null)return e;let a=Math.round(e*100);if(!Number.isFinite(e)||Math.abs(e*100-a)>1e-6||e<Ye||e>Je)throw new m("INVALID_CAP",`A monthly dollar cap is from $${Ye} to $${Je.toLocaleString("en-US")} in whole cents, or off`);return a*1e4}var Y=e=>e===null?null:Math.round(e/1e4)/100;function Q(e){return["default_monthly_call_cap IS NULL",...e.cost?["default_monthly_cost_cap_micros IS NULL"]:[],...e.ai?["ai_default IS NULL"]:[],...e.models?["ai_models IS NULL"]:[]].join(" AND ")}var Xe=new WeakMap;function Qe(e){let a=Xe.get(e);return a||(a=e.run([{sql:"PRAGMA table_info(org_settings)"}]).then(([n=[]])=>n.map(t=>String(t.name))),Xe.set(e,a)),a}async function _e(e){return(await Qe(e)).includes("ai_default")}async function ee(e){return(await Qe(e)).includes("ai_models")}async function ea(e){if(!await ee(e))throw new m("GATEWAY_PREDATES_AI_DEFAULT",`The Gateway predates the default model settings
3
+ Its database has no ai_models column until the Gateway is updated to a release that has it.`,{rerun:`${_} update`,rerunNote:"then run this again"})}async function Ee(e,a){let[n,t]=await Promise.all([_e(e),ee(e)]);if(!n)return{provider:null,models:{}};let[i=[]]=await e.run([{sql:`SELECT ai_default${t?", ai_models":""} FROM org_settings WHERE organization_id = ?`,params:[a]}]),r=d=>typeof d=="string"?d:null;return{provider:Le(r(i[0]?.ai_default)),models:Ie(r(i[0]?.ai_models))}}function je(e){if(e.provider===null)return null;let a=e.models[e.provider]?.chat;return{provider:e.provider,...a?{model:a}:{}}}async function aa(e,a){let n=e.organization.id;return[{sql:`INSERT INTO org_settings (organization_id, ai_default, ai_models, updated_at, updated_by)
4
+ VALUES (?1, ?2, ?3, ?4, ?5)
5
+ ON CONFLICT (organization_id) DO UPDATE SET ai_default = ?2, ai_models = ?3, updated_at = ?4, updated_by = ?5`,params:[n,a.provider,ke(a.models),e.now,e.operator.userId]},{sql:`DELETE FROM org_settings WHERE organization_id = ? AND ${Q({cost:await $(e.db),ai:!0,models:!0})}`,params:[n]}]}var Ke=e=>e===null?null:{provider:e.provider,model:e.model??null},Ze=e=>e===null?null:Me(e);async function pn(e,a,n){if(a){let c=We(a,n)??(a.model?pe(a.provider,{chat:a.model}):null);if(c)throw new m("AI_DEFAULT_INVALID",`${a.provider} can't be the default model provider: ${c}`,{fix:`pick one of the providers ${_} ai list shows, as <provider> or <provider>/<model>`})}await ea(e.db);let t=e.organization.id,i=await Ee(e.db,t),r=a===null?{provider:null,models:i.models}:{provider:a.provider,models:a.model?ce(i.models,a.provider,{chat:a.model}):i.models},d=je(i),l=je(r),s=Ze(d)!==Ze(l),o=s?await M(e.db,{organizationId:t,type:"ai.default_changed",actor:w(e.operator),data:{aiDefault:Ke(l),previous:Ke(d)},now:e.now}):[];return await e.db.run([...await aa(e,r),...o]),{choice:l,was:d,changed:s}}async function un(e,a,n){let t=pe(a,n);if(t)throw new m("AI_DEFAULT_INVALID",`Can't set ${a}'s models: ${t}`,{fix:`pick one of the providers ${_} ai list shows`});await ea(e.db);let i=e.organization.id,r=await Ee(e.db,i),d={provider:r.provider,models:ce(r.models,a,n)},l=r.models[a]??{},s=d.models[a]??{},o={chat:s.chat??null,embeddings:s.embeddings??null},c={chat:l.chat??null,embeddings:l.embeddings??null},S=o.chat!==c.chat||o.embeddings!==c.embeddings,b=S?await M(e.db,{organizationId:i,type:"ai.model_changed",actor:w(e.operator),data:{provider:a,models:o,previous:c},now:e.now}):[];return await e.db.run([...await aa(e,d),...b]),{provider:a,models:o,previous:c,changed:S}}async function mn(e,a,n){let[t,i]=await Promise.all([Be(e),Ee(a.db,a.organization.id)]),r=l=>t.includes(l);return{resolution:ze({...i,isSet:r,...n?{lookup:n}:{}}),names:t,aiGateway:{on:ve.every(r)}}}function gn(e){let a=e.trim();if(a==="off"||a==="none")return null;let n=we(a);if(!n)throw new m("AI_DEFAULT_INVALID",`${e} isn't a provider: write the provider's id (openrouter), or the id and a model (openrouter/anthropic/claude-haiku-5.5), or off`,{fix:`${_} ai list names the providers the default can be`});return n}var na=`SELECT users.id AS id, users.email AS email, users.name AS name, members.role AS role
6
6
  FROM members JOIN users ON users.id = members.user_id
7
7
  WHERE members.organization_id = ?1 AND lower(users.email) = ?2
8
8
  LIMIT 1`,J=(e,a,n)=>`EXISTS (
9
9
  SELECT 1 FROM members owners JOIN users owner ON owner.id = owners.user_id
10
10
  WHERE owners.organization_id = ${e} AND owners.role = 'owner' AND owners.user_id <> ${a}
11
11
  AND (owner.banned IS NULL OR owner.banned = 0 OR (owner.ban_expires IS NOT NULL AND owner.ban_expires <= ${n}))
12
- )`;async function on(e,a,n,t,i){let r=n.trim().toLowerCase(),d=Date.now(),[l=[],,s=[]]=await e.run([{sql:Be,params:[a,r]},{sql:`UPDATE members SET role = ?3
12
+ )`;async function fn(e,a,n,t,i){let r=n.trim().toLowerCase(),d=Date.now(),[l=[],,s=[]]=await e.run([{sql:na,params:[a,r]},{sql:`UPDATE members SET role = ?3
13
13
  WHERE organization_id = ?1
14
14
  AND user_id = (SELECT id FROM users WHERE lower(email) = ?2 LIMIT 1)
15
15
  AND role <> ?3
16
16
  AND NOT (
17
17
  role = 'owner' AND ?3 <> 'owner'
18
18
  AND NOT ${J("?1","members.user_id","?4")}
19
- )`,params:[a,r,t,d]},{sql:Be,params:[a,r]}]),o=Ye(l[0]);if(!o)return{kind:"not_found"};if(o.role===t)return{kind:"unchanged",member:o};let c=Ye(s[0])??o;return c.role===t?(i&&await e.run(await z(e,{organizationId:a,type:"member.role_changed",actor:W(i),data:{user:{id:c.id,email:c.email,name:c.name||null},role:t,previousRole:o.role},now:d})),{kind:"changed",member:c,previousRole:o.role}):{kind:"last_owner",member:o}}function Ye(e){return e?{id:String(e.id),email:String(e.email),name:String(e.name??""),role:String(e.role)}:null}import{createHash as Da,randomBytes as Ne,randomUUID as H}from"node:crypto";var D=e=>({id:e.id,email:e.email,name:e.name||null}),X=e=>({id:e.appSlug,rowId:e.id,name:e.name}),U=50;function A(e,a){return z(e.db,{organizationId:e.organization.id,actor:W(e.operator),now:e.now,...a})}async function ya(e){let a=await Z(e);if(!a||!a.hasOwner)throw new m("WORKSPACE_NOT_CLAIMED",`The Gateway has no owner yet
20
- The setup claim makes the workspace, named after its owner; rename it after.`,{fix:"open the Gateway in a browser and claim it, then run this again"});return a.organization}function bn(e,a,n,t=Date.now()){return $e(async()=>{let i=await ya(e),r=i.name!==a;return r&&await e.run([{sql:"UPDATE organizations SET name = ? WHERE id = ?",params:[a,i.id]},...await z(e,{organizationId:i.id,type:"organization.renamed",actor:W(n),data:{name:a,previousName:i.name},now:t})]),{organization:{...i,name:a},previousName:i.name,changed:r}})}var va=72*60*60*1e3,Ia=60*60*1e3,na=1e9,Je=60,Ve=255,Se=90,ka=19,wa=e=>Da("sha256").update(e).digest("hex"),re=e=>`((',' || replace(${e}, ' ', '') || ',') LIKE '%,owner,%')`,ta=e=>`(NOT ${re(e)} OR ${J("?2","?1","?3")})`,ae=`EXISTS (SELECT 1 FROM members m WHERE m.user_id = ?1 AND m.organization_id = ?2 AND ${ta("m.role")})`,ne=`EXISTS (SELECT 1 FROM members m WHERE m.user_id = ?1 AND m.organization_id = ?2)
19
+ )`,params:[a,r,t,d]},{sql:na,params:[a,r]}]),o=ta(l[0]);if(!o)return{kind:"not_found"};if(o.role===t)return{kind:"unchanged",member:o};let c=ta(s[0])??o;return c.role===t?(i&&await e.run(await M(e,{organizationId:a,type:"member.role_changed",actor:w(i),data:{user:{id:c.id,email:c.email,name:c.name||null},role:t,previousRole:o.role},now:d})),{kind:"changed",member:c,previousRole:o.role}):{kind:"last_owner",member:o}}function ta(e){return e?{id:String(e.id),email:String(e.email),name:String(e.name??""),role:String(e.role)}:null}import{createHash as $a,randomBytes as he,randomUUID as H}from"node:crypto";var D=e=>({id:e.id,email:e.email,name:e.name||null}),X=e=>({id:e.appSlug,rowId:e.id,name:e.name}),U=50;function A(e,a){return M(e.db,{organizationId:e.organization.id,actor:w(e.operator),now:e.now,...a})}async function Ua(e){let a=await Z(e);if(!a||!a.hasOwner)throw new m("WORKSPACE_NOT_CLAIMED",`The Gateway has no owner yet
20
+ The setup claim makes the workspace, named after its owner; rename it after.`,{fix:"open the Gateway in a browser and claim it, then run this again"});return a.organization}function In(e,a,n,t=Date.now()){return Ge(async()=>{let i=await Ua(e),r=i.name!==a;return r&&await e.run([{sql:"UPDATE organizations SET name = ? WHERE id = ?",params:[a,i.id]},...await M(e,{organizationId:i.id,type:"organization.renamed",actor:w(n),data:{name:a,previousName:i.name},now:t})]),{organization:{...i,name:a},previousName:i.name,changed:r}})}var Pa=72*60*60*1e3,xa=60*60*1e3,ma=1e9,ia=60,ra=255,Re=90,Fa=19,qa=e=>$a("sha256").update(e).digest("hex"),re=e=>`((',' || replace(${e}, ' ', '') || ',') LIKE '%,owner,%')`,ga=e=>`(NOT ${re(e)} OR ${J("?2","?1","?3")})`,ae=`EXISTS (SELECT 1 FROM members m WHERE m.user_id = ?1 AND m.organization_id = ?2 AND ${ga("m.role")})`,ne=`EXISTS (SELECT 1 FROM members m WHERE m.user_id = ?1 AND m.organization_id = ?2)
21
21
  AND NOT EXISTS (SELECT 1 FROM members mine WHERE mine.user_id = ?1 AND ${re("mine.role")}
22
- AND NOT ${J("mine.organization_id","?1","?3")})`,Ma=e=>`(banned = 1 AND (ban_expires IS NULL OR ban_expires > ${e}))`;function oe(e,a){let n=[];for(let t=0;t<e.length;t+=a)n.push(e.slice(t,t+a));return n}function La(e){let a=(e??"").split(",").map(n=>n.trim());return a.includes("owner")?"owner":a.includes("admin")?"admin":a.includes("member")?"member":null}var g=e=>Number(e??0);function se(e,a){let n=Number(e.banned??0)===1,t=e.ban_expires==null?null:Number(e.ban_expires);return{id:String(e.id),name:String(e.name??""),email:String(e.email),role:String(e.role??"member"),primaryRole:La(u(e.role)),deactivated:n&&(t===null||t>a),createdAt:g(e.created_at)}}function he(e,a){let n=String(e.status),t=g(e.expires_at);return{id:String(e.id),email:String(e.email),role:String(e.role??"member"),status:n,expiresAt:t,createdAt:g(e.created_at),live:n==="pending"&&t>a}}function ia(e){return{id:String(e.id),name:String(e.name),description:u(e.description),createdAt:g(e.created_at),memberCount:g(e.member_count),appCount:g(e.app_count)}}function Re(e){return{id:String(e.id),appSlug:String(e.app_slug),name:String(e.name),description:String(e.description??""),status:String(e.status),scope:String(e.scope),tier:String(e.tier),hostname:u(e.hostname),everyone:Number(e.is_default??0)===1,manifest:u(e.manifest),secretNames:u(e.secret_names),accessCount:g(e.access_count)}}var de="u.id, u.name, u.email, m.role, u.banned, u.ban_expires, u.created_at",Te=`a.id, a.app_slug, a.name, a.description, a.status, a.scope, a.tier, a.hostname, a.is_default, a.manifest, a.secret_names,
22
+ AND NOT ${J("mine.organization_id","?1","?3")})`,Ha=e=>`(banned = 1 AND (ban_expires IS NULL OR ban_expires > ${e}))`;function oe(e,a){let n=[];for(let t=0;t<e.length;t+=a)n.push(e.slice(t,t+a));return n}function Ga(e){let a=(e??"").split(",").map(n=>n.trim());return a.includes("owner")?"owner":a.includes("admin")?"admin":a.includes("member")?"member":null}var g=e=>Number(e??0);function se(e,a){let n=Number(e.banned??0)===1,t=e.ban_expires==null?null:Number(e.ban_expires);return{id:String(e.id),name:String(e.name??""),email:String(e.email),role:String(e.role??"member"),primaryRole:Ga(u(e.role)),deactivated:n&&(t===null||t>a),createdAt:g(e.created_at)}}function Te(e,a){let n=String(e.status),t=g(e.expires_at);return{id:String(e.id),email:String(e.email),role:String(e.role??"member"),status:n,expiresAt:t,createdAt:g(e.created_at),live:n==="pending"&&t>a}}function _a(e){return{id:String(e.id),name:String(e.name),description:u(e.description),createdAt:g(e.created_at),memberCount:g(e.member_count),appCount:g(e.app_count)}}function Ce(e){return{id:String(e.id),appSlug:String(e.app_slug),name:String(e.name),description:String(e.description??""),status:String(e.status),scope:String(e.scope),tier:String(e.tier),hostname:u(e.hostname),everyone:Number(e.is_default??0)===1,manifest:u(e.manifest),secretNames:u(e.secret_names),accessCount:g(e.access_count)}}var de="u.id, u.name, u.email, m.role, u.banned, u.ban_expires, u.created_at",Oe=`a.id, a.app_slug, a.name, a.description, a.status, a.scope, a.tier, a.hostname, a.is_default, a.manifest, a.secret_names,
23
23
  (SELECT COUNT(DISTINCT g.user_id) FROM (
24
24
  SELECT ua.user_id FROM user_app_access ua WHERE ua.app_row_id = a.id AND ua.organization_id = a.organization_id
25
25
  UNION ALL
26
26
  SELECT gm.user_id FROM group_app_access ga JOIN group_members gm ON gm.group_id = ga.group_id
27
27
  WHERE ga.app_row_id = a.id AND ga.organization_id = a.organization_id AND gm.organization_id = a.organization_id
28
- ) g JOIN members mm ON mm.user_id = g.user_id AND mm.organization_id = a.organization_id) AS access_count`,te=e=>({id:String(e.id),name:String(e.name),slug:String(e.slug)});async function ra(e,a){let n=a.trim(),[t=[],i=[]]=await e.run([{sql:`SELECT id, name, slug FROM organizations
28
+ ) g JOIN members mm ON mm.user_id = g.user_id AND mm.organization_id = a.organization_id) AS access_count`,te=e=>({id:String(e.id),name:String(e.name),slug:String(e.slug)});async function Ea(e,a){let n=a.trim(),[t=[],i=[]]=await e.run([{sql:`SELECT id, name, slug FROM organizations
29
29
  WHERE id = ?1 OR slug = ?1 OR lower(name) = lower(?1)
30
30
  ORDER BY created_at, id`,params:[n]},{sql:"SELECT id, name, slug FROM organizations ORDER BY created_at, id"}]),r=t.map(te),d=r.find(s=>s.id===n)??r.find(s=>s.slug===n);if(d)return d;if(r.length===1)return r[0];if(r.length>1)throw new m("ORG_AMBIGUOUS",`${r.length} organizations are named ${n}: ${K(r.map(s=>`${s.slug} (${s.id})`))}
31
31
  A name isn't unique, and any owner or admin can rename their organization; a slug or id is one organization's.`,{fix:"name it by its slug or id with --org",data:{organizations:r}});let l=i.map(te);throw new m("ORGANIZATION_NOT_FOUND",`No organization named ${n} on this Gateway${l.length>0?`
32
- The Gateway has: ${K(l.map(s=>`${s.slug} (${s.name})`))}.`:""}`,{fix:"name one by its slug, id or name with --org",data:{organizations:l}})}async function Wa(e,a){if(a.email===null)return{actor:a.actor,email:null,userId:null};let[n=[]]=await e.run([{sql:"SELECT id FROM users WHERE lower(email) = lower(?) AND email_verified = 1",params:[a.email]}]);return{actor:a.actor,email:a.email,userId:u(n[0]?.id)}}var oa=()=>new m("ORGANIZATION_NOT_FOUND",`The Gateway has no organization yet
33
- Its owner creates the first one by claiming it.`,{rerun:`${_} setup`,rerunNote:"prints the owner link"});async function Nn(e,a){if(a!==void 0)return ra(e,a);let n=await Z(e);if(!n)throw oa();return n.organization}async function Sn(e,a,n={}){let[t,i]=await Promise.all([Z(e),Wa(e,a)]);if(n.org===void 0&&!t)throw oa();let r=n.org===void 0?t.organization:await ra(e,n.org);return{db:e,organization:r,installOrganizationId:t?.organization.id??null,operator:i,now:n.now??Date.now()}}async function G(e,a){let n=a.trim(),[t=[]]=await e.db.run([{sql:`SELECT ${de} FROM members m JOIN users u ON u.id = m.user_id
32
+ The Gateway has: ${K(l.map(s=>`${s.slug} (${s.name})`))}.`:""}`,{fix:"name one by its slug, id or name with --org",data:{organizations:l}})}async function Ba(e,a){if(a.email===null)return{actor:a.actor,email:null,userId:null};let[n=[]]=await e.run([{sql:"SELECT id FROM users WHERE lower(email) = lower(?) AND email_verified = 1",params:[a.email]}]);return{actor:a.actor,email:a.email,userId:u(n[0]?.id)}}var fa=()=>new m("ORGANIZATION_NOT_FOUND",`The Gateway has no organization yet
33
+ Its owner creates the first one by claiming it.`,{rerun:`${_} setup`,rerunNote:"prints the owner link"});async function kn(e,a){if(a!==void 0)return Ea(e,a);let n=await Z(e);if(!n)throw fa();return n.organization}async function wn(e,a,n={}){let[t,i]=await Promise.all([Z(e),Ba(e,a)]);if(n.org===void 0&&!t)throw fa();let r=n.org===void 0?t.organization:await Ea(e,n.org);return{db:e,organization:r,installOrganizationId:t?.organization.id??null,operator:i,now:n.now??Date.now()}}async function G(e,a){let n=a.trim(),[t=[]]=await e.db.run([{sql:`SELECT ${de} FROM members m JOIN users u ON u.id = m.user_id
34
34
  WHERE m.organization_id = ?1 AND (lower(u.email) = lower(?2) OR u.id = ?2)
35
- ORDER BY (u.id = ?2) DESC LIMIT 1`,params:[e.organization.id,n]}]),i=t[0];if(!i)throw new m("USER_NOT_FOUND",`${n} isn't a member of ${e.organization.name}`,{rerun:`${_} workspace users`,rerunNote:"lists the members and invitations"});return se(i,e.now)}async function ie(e,a,{callable:n=!1}={}){let t=a.trim(),[i=[]]=await e.db.run([{sql:`SELECT ${Te} FROM apps a
35
+ ORDER BY (u.id = ?2) DESC LIMIT 1`,params:[e.organization.id,n]}]),i=t[0];if(!i)throw new m("USER_NOT_FOUND",`${n} isn't a member of ${e.organization.name}`,{rerun:`${_} workspace users`,rerunNote:"lists the members and invitations"});return se(i,e.now)}async function ie(e,a,{callable:n=!1}={}){let t=a.trim(),[i=[]]=await e.db.run([{sql:`SELECT ${Oe} FROM apps a
36
36
  WHERE (a.organization_id = ?1${n?" OR a.scope = 'shared'":""}) AND a.status != '${O}'
37
37
  AND (a.app_slug = ?2 OR a.id = ?2)
38
- ORDER BY (a.organization_id = ?1) DESC, (a.id = ?2) DESC LIMIT 1`,params:[e.organization.id,t]}]),r=i[0];if(!r)throw new m("APP_NOT_FOUND",`No app named ${t} in ${e.organization.name}`,{rerun:`${_} workspace apps`,rerunNote:"lists them"});return Re(r)}async function sa(e,a){let n=a.trim(),[t=[]]=await e.db.run([{sql:`${ua} AND (g.id = ?2 OR lower(g.name) = lower(?2)) ORDER BY (g.id = ?2) DESC LIMIT 1`,params:[e.organization.id,n]}]),i=t[0];if(!i)throw new m("GROUP_NOT_FOUND",`No group named ${n} in ${e.organization.name}`,{rerun:`${_} workspace groups`,rerunNote:"lists them"});return ia(i)}async function da(e,a){let n=a.trim(),[t=[]]=await e.db.run([{sql:`SELECT id, email, role, status, expires_at, created_at FROM invitations
38
+ ORDER BY (a.organization_id = ?1) DESC, (a.id = ?2) DESC LIMIT 1`,params:[e.organization.id,t]}]),r=i[0];if(!r)throw new m("APP_NOT_FOUND",`No app named ${t} in ${e.organization.name}`,{rerun:`${_} workspace apps`,rerunNote:"lists them"});return Ce(r)}async function Aa(e,a){let n=a.trim(),[t=[]]=await e.db.run([{sql:`${Ra} AND (g.id = ?2 OR lower(g.name) = lower(?2)) ORDER BY (g.id = ?2) DESC LIMIT 1`,params:[e.organization.id,n]}]),i=t[0];if(!i)throw new m("GROUP_NOT_FOUND",`No group named ${n} in ${e.organization.name}`,{rerun:`${_} workspace groups`,rerunNote:"lists them"});return _a(i)}async function ba(e,a){let n=a.trim(),[t=[]]=await e.db.run([{sql:`SELECT id, email, role, status, expires_at, created_at FROM invitations
39
39
  WHERE organization_id = ?1 AND status = 'pending' AND (id = ?2 OR lower(email) = lower(?2))
40
40
  ORDER BY (id = ?2) DESC, created_at DESC LIMIT 1`,params:[e.organization.id,n]}]),i=t[0];if(!i)throw new m("INVITATION_NOT_PENDING",`No pending invitation for ${n} in ${e.organization.name}
41
- It was accepted, canceled or never made.`,{rerun:`${_} workspace users`,rerunNote:"lists the pending invitations"});return he(i,e.now)}async function hn(e){let a=e.organization.id,[n=[],t=[]]=await e.db.run([{sql:`SELECT u.email FROM members m JOIN users u ON u.id = m.user_id
41
+ It was accepted, canceled or never made.`,{rerun:`${_} workspace users`,rerunNote:"lists the pending invitations"});return Te(i,e.now)}async function Mn(e){let a=e.organization.id,[n=[],t=[]]=await e.db.run([{sql:`SELECT u.email FROM members m JOIN users u ON u.id = m.user_id
42
42
  WHERE m.organization_id = ?1 AND ${re("m.role")} ORDER BY m.created_at, u.email`,params:[a]},{sql:`SELECT
43
43
  (SELECT COUNT(*) FROM members WHERE organization_id = ?1) AS members,
44
- (SELECT COUNT(*) FROM members m JOIN users u ON u.id = m.user_id WHERE m.organization_id = ?1 AND ${Ma("?2")}) AS deactivated,
44
+ (SELECT COUNT(*) FROM members m JOIN users u ON u.id = m.user_id WHERE m.organization_id = ?1 AND ${Ha("?2")}) AS deactivated,
45
45
  (SELECT COUNT(*) FROM invitations WHERE organization_id = ?1 AND status = 'pending' AND expires_at > ?2) AS invitations,
46
46
  (SELECT COUNT(*) FROM groups WHERE organization_id = ?1) AS groups,
47
- (SELECT COUNT(*) FROM apps WHERE organization_id = ?1 AND status != '${O}') AS apps`,params:[a,e.now]}]),i=t[0]??{};return{organization:e.organization,owners:n.map(r=>String(r.email)),counts:{members:g(i.members),deactivated:g(i.deactivated),invitations:g(i.invitations),groups:g(i.groups),apps:g(i.apps)}}}async function Rn(e){let[a=[],n=[]]=await e.db.run([{sql:`SELECT ${de} FROM members m JOIN users u ON u.id = m.user_id
47
+ (SELECT COUNT(*) FROM apps WHERE organization_id = ?1 AND status != '${O}') AS apps`,params:[a,e.now]}]),i=t[0]??{};return{organization:e.organization,owners:n.map(r=>String(r.email)),counts:{members:g(i.members),deactivated:g(i.deactivated),invitations:g(i.invitations),groups:g(i.groups),apps:g(i.apps)}}}async function Ln(e){let[a=[],n=[]]=await e.db.run([{sql:`SELECT ${de} FROM members m JOIN users u ON u.id = m.user_id
48
48
  WHERE m.organization_id = ?1 ORDER BY u.email`,params:[e.organization.id]},{sql:`SELECT id, email, role, status, expires_at, created_at FROM invitations
49
- WHERE organization_id = ?1 AND status = 'pending' ORDER BY created_at DESC`,params:[e.organization.id]}]),t={owner:0,admin:1,member:2};return{members:a.map(i=>se(i,e.now)).sort((i,r)=>(t[i.primaryRole??"member"]??3)-(t[r.primaryRole??"member"]??3)||i.email.localeCompare(r.email)),invitations:n.map(i=>he(i,e.now))}}function la(){let e=`einv_${Ne(32).toString("base64url")}`;return{token:e,hash:wa(e)}}var Tn=(e,a)=>`${e}/accept-invitation?token=${encodeURIComponent(a)}`,Cn=(e,a)=>`${e}/reset-password?token=${encodeURIComponent(a)}`;async function On(e,a,n){let t=a.trim().toLowerCase(),i=await za(e,"invite");await Xe(e,t);let r=H(),{token:d,hash:l}=la(),s=e.now+va,[,,o=[]]=await e.db.run([{sql:`INSERT INTO invitations (id, organization_id, email, role, status, expires_at, created_at, inviter_id)
49
+ WHERE organization_id = ?1 AND status = 'pending' ORDER BY created_at DESC`,params:[e.organization.id]}]),t={owner:0,admin:1,member:2};return{members:a.map(i=>se(i,e.now)).sort((i,r)=>(t[i.primaryRole??"member"]??3)-(t[r.primaryRole??"member"]??3)||i.email.localeCompare(r.email)),invitations:n.map(i=>Te(i,e.now))}}function Na(){let e=`einv_${he(32).toString("base64url")}`;return{token:e,hash:qa(e)}}var Wn=(e,a)=>`${e}/accept-invitation?token=${encodeURIComponent(a)}`,zn=(e,a)=>`${e}/reset-password?token=${encodeURIComponent(a)}`;async function $n(e,a,n){let t=a.trim().toLowerCase(),i=await Ya(e,"invite");await oa(e,t);let r=H(),{token:d,hash:l}=Na(),s=e.now+Pa,[,,o=[]]=await e.db.run([{sql:`INSERT INTO invitations (id, organization_id, email, role, status, expires_at, created_at, inviter_id)
50
50
  SELECT ?1, ?2, ?3, ?4, 'pending', ?5, ?6, ?7
51
51
  WHERE NOT EXISTS (SELECT 1 FROM members m JOIN users u ON u.id = m.user_id WHERE m.organization_id = ?2 AND lower(u.email) = ?3)
52
52
  AND NOT EXISTS (SELECT 1 FROM invitations i WHERE i.organization_id = ?2 AND lower(i.email) = ?3 AND i.status = 'pending' AND i.expires_at > ?6)`,params:[r,e.organization.id,t,n,s,e.now,i]},{sql:`INSERT INTO invitation_link_tokens (id, invitation_id, token_hash, created_at)
53
- SELECT ?1, ?2, ?3, ?4 WHERE EXISTS (SELECT 1 FROM invitations WHERE id = ?2)`,params:[H(),r,l,e.now]},{sql:"SELECT id, email, role, status, expires_at, created_at FROM invitations WHERE id = ?",params:[r]},...await A(e,{type:"member.invited",data:{invitation:{id:r,email:t,role:n,expiresAt:new Date(s).toISOString()},emailSent:!1},onlyIf:{sql:"EXISTS (SELECT 1 FROM invitations WHERE id = ?)",params:[r]}})]),c=o[0];if(!c)throw await Xe(e,t),new m("ALREADY_INVITED",`${t} was invited meanwhile`,{retryable:!0});return{invitation:he(c,e.now),token:d}}async function za(e,a){let{operator:n}=e;if(n.userId!==null){let[i=[]]=await e.db.run([{sql:"SELECT 1 AS member FROM members WHERE user_id = ? AND organization_id = ?",params:[n.userId,e.organization.id]}]);if(i.length>0)return n.userId}let t=n.email===null?"An API token has no account on the Gateway":n.userId===null?`${n.email} has no account on this Gateway (a verified one)`:`${n.email} isn't a member of ${e.organization.name}`;throw new m("WORKSPACE_NOT_A_MEMBER",`Only a member can ${a}: an invitation records who sent it
54
- ${t}.`,{fix:n.email===null?`log in with your Cloudflare account (${_} login), claim the Gateway (${_} setup) or get invited, or invite from Admin \u2192 Users`:`claim the Gateway (${_} setup) or get invited to ${e.organization.name}, or invite from Admin \u2192 Users`})}async function Xe(e,a){let[n=[],t=[]]=await e.db.run([{sql:"SELECT u.email FROM members m JOIN users u ON u.id = m.user_id WHERE m.organization_id = ?1 AND lower(u.email) = ?2",params:[e.organization.id,a]},{sql:"SELECT id FROM invitations WHERE organization_id = ?1 AND lower(email) = ?2 AND status = 'pending' AND expires_at > ?3",params:[e.organization.id,a,e.now]}]);if(n.length>0)throw new m("ALREADY_MEMBER",`${a} is a member of ${e.organization.name} already`,{rerun:`${_} workspace users`,rerunNote:"lists the members"});if(t.length>0)throw new m("ALREADY_INVITED",`${a} has a pending invitation to ${e.organization.name} already`,{rerun:`${_} workspace invite-link ${a}`,rerunNote:"prints a new link for it"})}async function Dn(e,a){let n=await da(e,a);if(!n.live)throw new m("INVITATION_NOT_PENDING",`The invitation for ${n.email} expired on ${new Date(n.expiresAt).toISOString()}`,{rerun:`${_} workspace invite ${n.email}`,rerunNote:"makes a new one"});let{token:t,hash:i}=la(),[r=[]]=await e.db.run([{sql:`INSERT INTO invitation_link_tokens (id, invitation_id, token_hash, created_at)
53
+ SELECT ?1, ?2, ?3, ?4 WHERE EXISTS (SELECT 1 FROM invitations WHERE id = ?2)`,params:[H(),r,l,e.now]},{sql:"SELECT id, email, role, status, expires_at, created_at FROM invitations WHERE id = ?",params:[r]},...await A(e,{type:"member.invited",data:{invitation:{id:r,email:t,role:n,expiresAt:new Date(s).toISOString()},emailSent:!1},onlyIf:{sql:"EXISTS (SELECT 1 FROM invitations WHERE id = ?)",params:[r]}})]),c=o[0];if(!c)throw await oa(e,t),new m("ALREADY_INVITED",`${t} was invited meanwhile`,{retryable:!0});return{invitation:Te(c,e.now),token:d}}async function Ya(e,a){let{operator:n}=e;if(n.userId!==null){let[i=[]]=await e.db.run([{sql:"SELECT 1 AS member FROM members WHERE user_id = ? AND organization_id = ?",params:[n.userId,e.organization.id]}]);if(i.length>0)return n.userId}let t=n.email===null?"An API token has no account on the Gateway":n.userId===null?`${n.email} has no account on this Gateway (a verified one)`:`${n.email} isn't a member of ${e.organization.name}`;throw new m("WORKSPACE_NOT_A_MEMBER",`Only a member can ${a}: an invitation records who sent it
54
+ ${t}.`,{fix:n.email===null?`log in with your Cloudflare account (${_} login), claim the Gateway (${_} setup) or get invited, or invite from Admin \u2192 Users`:`claim the Gateway (${_} setup) or get invited to ${e.organization.name}, or invite from Admin \u2192 Users`})}async function oa(e,a){let[n=[],t=[]]=await e.db.run([{sql:"SELECT u.email FROM members m JOIN users u ON u.id = m.user_id WHERE m.organization_id = ?1 AND lower(u.email) = ?2",params:[e.organization.id,a]},{sql:"SELECT id FROM invitations WHERE organization_id = ?1 AND lower(email) = ?2 AND status = 'pending' AND expires_at > ?3",params:[e.organization.id,a,e.now]}]);if(n.length>0)throw new m("ALREADY_MEMBER",`${a} is a member of ${e.organization.name} already`,{rerun:`${_} workspace users`,rerunNote:"lists the members"});if(t.length>0)throw new m("ALREADY_INVITED",`${a} has a pending invitation to ${e.organization.name} already`,{rerun:`${_} workspace invite-link ${a}`,rerunNote:"prints a new link for it"})}async function Un(e,a){let n=await ba(e,a);if(!n.live)throw new m("INVITATION_NOT_PENDING",`The invitation for ${n.email} expired on ${new Date(n.expiresAt).toISOString()}`,{rerun:`${_} workspace invite ${n.email}`,rerunNote:"makes a new one"});let{token:t,hash:i}=Na(),[r=[]]=await e.db.run([{sql:`INSERT INTO invitation_link_tokens (id, invitation_id, token_hash, created_at)
55
55
  SELECT ?1, ?2, ?3, ?4 WHERE EXISTS (SELECT 1 FROM invitations WHERE id = ?2 AND status = 'pending' AND expires_at > ?4)
56
- RETURNING id`,params:[H(),n.id,i,e.now]}]);if(r.length===0)throw new m("INVITATION_NOT_PENDING",`The invitation for ${n.email} was used or canceled meanwhile`);return{invitation:n,token:t}}async function yn(e,a){let n=await da(e,a),[t=[]]=await e.db.run([{sql:"UPDATE invitations SET status = 'canceled' WHERE id = ? AND organization_id = ? AND status = 'pending' RETURNING id",params:[n.id,e.organization.id]},...await A(e,{type:"member.invitation_cancelled",data:{invitation:{id:n.id,email:n.email,role:n.role}},onlyIf:{sql:"changes() > 0",params:[]}})]);if(t.length===0)throw new m("INVITATION_NOT_PENDING",`The invitation for ${n.email} was used or canceled meanwhile`);return{...n,status:"canceled",live:!1}}function je(e,a,n,t){let r=t.id===e.organization.id?`${a.email} is the last owner of ${t.name} who can act, so they can't be ${n}`:`${a.email} is the last owner of ${t.name} who can act, and a deactivation is account-wide: it would leave ${t.name} without one`,d=t.id===e.installOrganizationId?{rerun:`${_} workspace role <email> owner`,rerunNote:"for someone else, then this command again"}:{fix:`an owner of ${t.name} makes someone else an owner under Manage workspace \u2192 Users, then run this again`};return new m("LAST_OWNER",`${r}
57
- Make someone else an owner of ${t.name} first, then try again.`,{...d,data:{organization:t,member:{id:a.id,email:a.email,name:a.name}}})}async function $a(e,a,n){let[t=[]]=await e.db.run([{sql:`SELECT o.id, o.name, o.slug FROM members mine JOIN organizations o ON o.id = mine.organization_id
56
+ RETURNING id`,params:[H(),n.id,i,e.now]}]);if(r.length===0)throw new m("INVITATION_NOT_PENDING",`The invitation for ${n.email} was used or canceled meanwhile`);return{invitation:n,token:t}}async function Pn(e,a){let n=await ba(e,a),[t=[]]=await e.db.run([{sql:"UPDATE invitations SET status = 'canceled' WHERE id = ? AND organization_id = ? AND status = 'pending' RETURNING id",params:[n.id,e.organization.id]},...await A(e,{type:"member.invitation_cancelled",data:{invitation:{id:n.id,email:n.email,role:n.role}},onlyIf:{sql:"changes() > 0",params:[]}})]);if(t.length===0)throw new m("INVITATION_NOT_PENDING",`The invitation for ${n.email} was used or canceled meanwhile`);return{...n,status:"canceled",live:!1}}function sa(e,a,n,t){let r=t.id===e.organization.id?`${a.email} is the last owner of ${t.name} who can act, so they can't be ${n}`:`${a.email} is the last owner of ${t.name} who can act, and a deactivation is account-wide: it would leave ${t.name} without one`,d=t.id===e.installOrganizationId?{rerun:`${_} workspace role <email> owner`,rerunNote:"for someone else, then this command again"}:{fix:`an owner of ${t.name} makes someone else an owner under Manage workspace \u2192 Users, then run this again`};return new m("LAST_OWNER",`${r}
57
+ Make someone else an owner of ${t.name} first, then try again.`,{...d,data:{organization:t,member:{id:a.id,email:a.email,name:a.name}}})}async function Ja(e,a,n){let[t=[]]=await e.db.run([{sql:`SELECT o.id, o.name, o.slug FROM members mine JOIN organizations o ON o.id = mine.organization_id
58
58
  WHERE mine.user_id = ?1 AND ${n?"1":"mine.organization_id = ?3"} AND ${re("mine.role")}
59
59
  AND NOT ${J("mine.organization_id","?1","?2")}
60
- ORDER BY (mine.organization_id = ?3) DESC, o.created_at, o.id`,params:[a,e.now,e.organization.id]}]);return t.map(te)}async function V(e,a,n,t,{afterWrite:i=!1}={}){let r=i?await G(e,a.id):a,d=await $a(e,r.id,t);if(d[0])throw je(e,r,n,d[0]);if(i)throw je(e,r,n,e.organization)}function ca(e,a,n){let t=[a.id,e.organization.id,e.now];return[{sql:`UPDATE users SET banned = 1, ban_reason = ?4, ban_expires = NULL, updated_at = ?3
60
+ ORDER BY (mine.organization_id = ?3) DESC, o.created_at, o.id`,params:[a,e.now,e.organization.id]}]);return t.map(te)}async function V(e,a,n,t,{afterWrite:i=!1}={}){let r=i?await G(e,a.id):a,d=await Ja(e,r.id,t);if(d[0])throw sa(e,r,n,d[0]);if(i)throw sa(e,r,n,e.organization)}function Sa(e,a,n){let t=[a.id,e.organization.id,e.now];return[{sql:`UPDATE users SET banned = 1, ban_reason = ?4, ban_expires = NULL, updated_at = ?3
61
61
  WHERE id = ?1 AND ${ne} RETURNING id`,params:[...t,n]},{sql:`DELETE FROM sessions WHERE user_id = ?1 AND ${ne}`,params:t},{sql:`UPDATE user_access_tokens SET revoked_at = ?3, updated_at = ?3
62
62
  WHERE user_id = ?1 AND revoked_at IS NULL AND ${ne}`,params:t},{sql:`UPDATE mcp_connections SET revoked_at = ?3, revoked_reason = 'user_deactivated'
63
- WHERE user_id = ?1 AND revoked_at IS NULL AND ${ne}`,params:t}]}async function vn(e,a){let n=await G(e,a);await V(e,n,"deactivated",!0);let[t=[]]=await e.db.run([...ca(e,n,`Deactivated by ${e.operator.actor} (${_} workspace deactivate)`),...await pa(e,n)]);return t.length===0&&await V(e,n,"deactivated",!0,{afterWrite:!0}),{member:{...n,deactivated:!0},already:n.deactivated}}async function pa(e,a,n){return a.deactivated?[]:A(e,{type:"member.deactivated",data:{user:D(a),...n?{reason:n}:{}},onlyIf:{sql:"EXISTS (SELECT 1 FROM users WHERE id = ? AND banned = 1)",params:[a.id]}})}async function In(e,a){let n=await G(e,a);return await e.db.run([{sql:`UPDATE users SET banned = 0, ban_reason = NULL, ban_expires = NULL, updated_at = ?3
64
- WHERE id = ?1 AND EXISTS (SELECT 1 FROM members m WHERE m.user_id = ?1 AND m.organization_id = ?2)`,params:[n.id,e.organization.id,e.now]},...n.deactivated?await A(e,{type:"member.reactivated",data:{user:D(n)},onlyIf:{sql:"changes() > 0",params:[]}}):[]]),{member:{...n,deactivated:!1},already:!n.deactivated}}async function kn(e,a,n){let t=await G(e,a);await V(e,t,"removed",!1);let[i=[]]=await e.db.run([{sql:"SELECT COUNT(*) AS n FROM members WHERE user_id = ? AND organization_id != ?",params:[t.id,e.organization.id]}]),r=g(i[0]?.n);if(r===0&&await n.accessMayLetBackIn(t)){let[o=[]]=await e.db.run([...ca(e,t,`Removed by ${e.operator.actor} (${_} workspace remove); deactivated instead, as Cloudflare Access would let them back in`),...await pa(e,t,"deleted_with_access_auto_join")]);return o.length===0&&await V(e,t,"removed",!0,{afterWrite:!0}),{member:{...t,deactivated:!0},deactivatedInstead:!0,accountDeleted:!1,otherOrganizations:r}}let d=[t.id,e.organization.id,e.now],[,,,,l=[],s=[]]=await e.db.run([{sql:`DELETE FROM user_app_access WHERE user_id = ?1 AND organization_id = ?2 AND ${ae}`,params:d},{sql:`DELETE FROM group_members WHERE user_id = ?1 AND organization_id = ?2 AND ${ae}`,params:d},{sql:`UPDATE user_access_tokens SET revoked_at = ?3, updated_at = ?3
63
+ WHERE user_id = ?1 AND revoked_at IS NULL AND ${ne}`,params:t}]}async function xn(e,a){let n=await G(e,a);await V(e,n,"deactivated",!0);let[t=[]]=await e.db.run([...Sa(e,n,`Deactivated by ${e.operator.actor} (${_} workspace deactivate)`),...await ha(e,n)]);return t.length===0&&await V(e,n,"deactivated",!0,{afterWrite:!0}),{member:{...n,deactivated:!0},already:n.deactivated}}async function ha(e,a,n){return a.deactivated?[]:A(e,{type:"member.deactivated",data:{user:D(a),...n?{reason:n}:{}},onlyIf:{sql:"EXISTS (SELECT 1 FROM users WHERE id = ? AND banned = 1)",params:[a.id]}})}async function Fn(e,a){let n=await G(e,a);return await e.db.run([{sql:`UPDATE users SET banned = 0, ban_reason = NULL, ban_expires = NULL, updated_at = ?3
64
+ WHERE id = ?1 AND EXISTS (SELECT 1 FROM members m WHERE m.user_id = ?1 AND m.organization_id = ?2)`,params:[n.id,e.organization.id,e.now]},...n.deactivated?await A(e,{type:"member.reactivated",data:{user:D(n)},onlyIf:{sql:"changes() > 0",params:[]}}):[]]),{member:{...n,deactivated:!1},already:!n.deactivated}}async function qn(e,a,n){let t=await G(e,a);await V(e,t,"removed",!1);let[i=[]]=await e.db.run([{sql:"SELECT COUNT(*) AS n FROM members WHERE user_id = ? AND organization_id != ?",params:[t.id,e.organization.id]}]),r=g(i[0]?.n);if(r===0&&await n.accessMayLetBackIn(t)){let[o=[]]=await e.db.run([...Sa(e,t,`Removed by ${e.operator.actor} (${_} workspace remove); deactivated instead, as Cloudflare Access would let them back in`),...await ha(e,t,"deleted_with_access_auto_join")]);return o.length===0&&await V(e,t,"removed",!0,{afterWrite:!0}),{member:{...t,deactivated:!0},deactivatedInstead:!0,accountDeleted:!1,otherOrganizations:r}}let d=[t.id,e.organization.id,e.now],[,,,,l=[],s=[]]=await e.db.run([{sql:`DELETE FROM user_app_access WHERE user_id = ?1 AND organization_id = ?2 AND ${ae}`,params:d},{sql:`DELETE FROM group_members WHERE user_id = ?1 AND organization_id = ?2 AND ${ae}`,params:d},{sql:`UPDATE user_access_tokens SET revoked_at = ?3, updated_at = ?3
65
65
  WHERE user_id = ?1 AND organization_id = ?2 AND revoked_at IS NULL AND ${ae}`,params:d},{sql:`UPDATE mcp_connections SET revoked_at = ?3, revoked_reason = 'member_removed'
66
- WHERE user_id = ?1 AND organization_id = ?2 AND revoked_at IS NULL AND ${ae}`,params:d},{sql:`DELETE FROM members WHERE user_id = ?1 AND organization_id = ?2 AND ${ta("role")} RETURNING id`,params:d},n.deleteAccounts?{sql:"DELETE FROM users WHERE id = ?1 AND NOT EXISTS (SELECT 1 FROM members WHERE user_id = ?1) RETURNING id",params:[t.id]}:{sql:"SELECT NULL AS id WHERE 0"},...await A(e,{type:"member.removed",data:{user:D(t),accountDeleted:n.deleteAccounts&&r===0},onlyIf:{sql:"NOT EXISTS (SELECT 1 FROM members WHERE user_id = ? AND organization_id = ?)",params:[t.id,e.organization.id]}})]);return l.length===0&&await V(e,t,"removed",!1,{afterWrite:!0}),{member:t,deactivatedInstead:!1,accountDeleted:s.length>0,otherOrganizations:r}}async function wn(e,a){let n=await G(e,a);if(n.deactivated)throw new m("PASSWORD_RESET_REQUIRES_ACTIVE_USER",`${n.email} is deactivated, and a password-reset link is only for an active account`,{rerun:`${_} workspace reactivate ${n.email}`,rerunNote:"first, if they should be back"});let[t=[]]=await e.db.run([{sql:`SELECT o.id, o.name, o.slug FROM members m JOIN organizations o ON o.id = m.organization_id
66
+ WHERE user_id = ?1 AND organization_id = ?2 AND revoked_at IS NULL AND ${ae}`,params:d},{sql:`DELETE FROM members WHERE user_id = ?1 AND organization_id = ?2 AND ${ga("role")} RETURNING id`,params:d},n.deleteAccounts?{sql:"DELETE FROM users WHERE id = ?1 AND NOT EXISTS (SELECT 1 FROM members WHERE user_id = ?1) RETURNING id",params:[t.id]}:{sql:"SELECT NULL AS id WHERE 0"},...await A(e,{type:"member.removed",data:{user:D(t),accountDeleted:n.deleteAccounts&&r===0},onlyIf:{sql:"NOT EXISTS (SELECT 1 FROM members WHERE user_id = ? AND organization_id = ?)",params:[t.id,e.organization.id]}})]);return l.length===0&&await V(e,t,"removed",!1,{afterWrite:!0}),{member:t,deactivatedInstead:!1,accountDeleted:s.length>0,otherOrganizations:r}}async function Hn(e,a){let n=await G(e,a);if(n.deactivated)throw new m("PASSWORD_RESET_REQUIRES_ACTIVE_USER",`${n.email} is deactivated, and a password-reset link is only for an active account`,{rerun:`${_} workspace reactivate ${n.email}`,rerunNote:"first, if they should be back"});let[t=[]]=await e.db.run([{sql:`SELECT o.id, o.name, o.slug FROM members m JOIN organizations o ON o.id = m.organization_id
67
67
  WHERE m.user_id = ?1 AND m.organization_id != ?2 ORDER BY o.created_at, o.id`,params:[n.id,e.organization.id]}]);if(t.length>0){let d=t.map(te);throw new m("USER_IN_OTHER_ORGANIZATION",`${n.email} also belongs to ${K(d.map(l=>l.name))}, and a password is account-wide
68
- The Gateway's own rule: a password-reset link is never made for someone in another organization, since it would reach that one too.`,{fix:"they reset it themselves with Forgot password (the Gateway's email must be configured), or they are removed from the other organization first",data:{member:{id:n.id,email:n.email,name:n.name},organizations:d}})}let i=Ne(24).toString("base64url"),r=e.now+Ia;return await e.db.run([{sql:`INSERT INTO verifications (id, identifier, value, expires_at, created_at, updated_at)
69
- VALUES (?1, ?2, ?3, ?4, ?5, ?5)`,params:[H(),`reset-password:${i}`,n.id,r,e.now]}]),{member:n,token:i,expiresAt:r}}var ua=`SELECT g.id, g.name, g.description, g.created_at,
68
+ The Gateway's own rule: a password-reset link is never made for someone in another organization, since it would reach that one too.`,{fix:"they reset it themselves with Forgot password (the Gateway's email must be configured), or they are removed from the other organization first",data:{member:{id:n.id,email:n.email,name:n.name},organizations:d}})}let i=he(24).toString("base64url"),r=e.now+xa;return await e.db.run([{sql:`INSERT INTO verifications (id, identifier, value, expires_at, created_at, updated_at)
69
+ VALUES (?1, ?2, ?3, ?4, ?5, ?5)`,params:[H(),`reset-password:${i}`,n.id,r,e.now]}]),{member:n,token:i,expiresAt:r}}var Ra=`SELECT g.id, g.name, g.description, g.created_at,
70
70
  (SELECT COUNT(*) FROM group_members gm WHERE gm.group_id = g.id) AS member_count,
71
71
  (SELECT COUNT(*) FROM group_app_access ga JOIN apps a ON a.id = ga.app_row_id AND a.status != '${O}' WHERE ga.group_id = g.id) AS app_count
72
- FROM groups g WHERE g.organization_id = ?1`;async function Mn(e){let[a=[]]=await e.db.run([{sql:`${ua} ORDER BY g.name`,params:[e.organization.id]}]);return a.map(ia)}function ma(e){let a=e.trim();if(a.length<1||a.length>Je)throw new m("GROUP_NAME_INVALID",`A group name is 1 to ${Je} characters${a?` (${a.length} given)`:""}`);return a}function ga(e){return new m("GROUP_NAME_TAKEN",`A group named "${e}" exists already in this organization`,{rerun:`${_} workspace groups`,rerunNote:"lists them"})}var Ua=()=>`grp_${Ne(8).toString("hex")}`;async function Ln(e,a){let n=ma(a.name),t=a.description?.trim()||null,i=Ua(),[r=[]]=await e.db.run([{sql:`INSERT INTO groups (id, organization_id, name, description, created_at, created_by)
72
+ FROM groups g WHERE g.organization_id = ?1`;async function Gn(e){let[a=[]]=await e.db.run([{sql:`${Ra} ORDER BY g.name`,params:[e.organization.id]}]);return a.map(_a)}function Ta(e){let a=e.trim();if(a.length<1||a.length>ia)throw new m("GROUP_NAME_INVALID",`A group name is 1 to ${ia} characters${a?` (${a.length} given)`:""}`);return a}function Ca(e){return new m("GROUP_NAME_TAKEN",`A group named "${e}" exists already in this organization`,{rerun:`${_} workspace groups`,rerunNote:"lists them"})}var Va=()=>`grp_${he(8).toString("hex")}`;async function Bn(e,a){let n=Ta(a.name),t=a.description?.trim()||null,i=Va(),[r=[]]=await e.db.run([{sql:`INSERT INTO groups (id, organization_id, name, description, created_at, created_by)
73
73
  SELECT ?1, ?2, ?3, ?4, ?5, ?6
74
74
  WHERE NOT EXISTS (SELECT 1 FROM groups WHERE organization_id = ?2 AND lower(name) = lower(?3))
75
- RETURNING id`,params:[i,e.organization.id,n,t,e.now,e.operator.userId]},...await A(e,{type:"group.created",data:{group:{id:i,name:n},...t?{description:t}:{}},onlyIf:{sql:"EXISTS (SELECT 1 FROM groups WHERE id = ?)",params:[i]}})]);if(r.length===0)throw ga(n);return{id:i,name:n,description:t,createdAt:e.now,memberCount:0,appCount:0}}async function Wn(e,a,n){let t=await sa(e,a),i=n.name===void 0?t.name:ma(n.name),r=n.description===void 0?t.description:n.description.trim()||null,[d=[]]=await e.db.run([{sql:`UPDATE groups SET name = ?3, description = ?4
75
+ RETURNING id`,params:[i,e.organization.id,n,t,e.now,e.operator.userId]},...await A(e,{type:"group.created",data:{group:{id:i,name:n},...t?{description:t}:{}},onlyIf:{sql:"EXISTS (SELECT 1 FROM groups WHERE id = ?)",params:[i]}})]);if(r.length===0)throw Ca(n);return{id:i,name:n,description:t,createdAt:e.now,memberCount:0,appCount:0}}async function Yn(e,a,n){let t=await Aa(e,a),i=n.name===void 0?t.name:Ta(n.name),r=n.description===void 0?t.description:n.description.trim()||null,[d=[]]=await e.db.run([{sql:`UPDATE groups SET name = ?3, description = ?4
76
76
  WHERE id = ?1 AND organization_id = ?2
77
77
  AND NOT EXISTS (SELECT 1 FROM groups WHERE organization_id = ?2 AND lower(name) = lower(?3) AND id != ?1)
78
- RETURNING id`,params:[t.id,e.organization.id,i,r]},...t.name!==i||t.description!==r?await A(e,{type:"group.updated",data:{group:{id:t.id,name:i},...t.name!==i?{previousName:t.name}:{},...t.description!==r&&r?{description:r}:{}},onlyIf:{sql:"changes() > 0",params:[]}}):[]]);if(d.length===0)throw ga(i);return{...t,name:i,description:r}}async function zn(e,a){let n=await sa(e,a),t=[n.id,e.organization.id];return await e.db.run([{sql:"DELETE FROM group_app_access WHERE group_id = ?1 AND organization_id = ?2",params:t},{sql:"DELETE FROM group_members WHERE group_id = ?1 AND organization_id = ?2",params:t},{sql:"DELETE FROM groups WHERE id = ?1 AND organization_id = ?2",params:t},...await A(e,{type:"group.removed",data:{group:{id:n.id,name:n.name}},onlyIf:{sql:"NOT EXISTS (SELECT 1 FROM groups WHERE id = ?)",params:[n.id]}})]),n}async function Ke(e,a){let[n=[]]=await e.db.run([{sql:`SELECT ${de}, (gm.user_id IS NOT NULL) AS in_group
78
+ RETURNING id`,params:[t.id,e.organization.id,i,r]},...t.name!==i||t.description!==r?await A(e,{type:"group.updated",data:{group:{id:t.id,name:i},...t.name!==i?{previousName:t.name}:{},...t.description!==r&&r?{description:r}:{}},onlyIf:{sql:"changes() > 0",params:[]}}):[]]);if(d.length===0)throw Ca(i);return{...t,name:i,description:r}}async function Jn(e,a){let n=await Aa(e,a),t=[n.id,e.organization.id];return await e.db.run([{sql:"DELETE FROM group_app_access WHERE group_id = ?1 AND organization_id = ?2",params:t},{sql:"DELETE FROM group_members WHERE group_id = ?1 AND organization_id = ?2",params:t},{sql:"DELETE FROM groups WHERE id = ?1 AND organization_id = ?2",params:t},...await A(e,{type:"group.removed",data:{group:{id:n.id,name:n.name}},onlyIf:{sql:"NOT EXISTS (SELECT 1 FROM groups WHERE id = ?)",params:[n.id]}})]),n}async function da(e,a){let[n=[]]=await e.db.run([{sql:`SELECT ${de}, (gm.user_id IS NOT NULL) AS in_group
79
79
  FROM members m JOIN users u ON u.id = m.user_id
80
80
  LEFT JOIN group_members gm ON gm.group_id = ?2 AND gm.user_id = u.id
81
- WHERE m.organization_id = ?1 ORDER BY u.email`,params:[e.organization.id,a.id]}]);return n.map(t=>({...se(t,e.now),inGroup:Number(t.in_group)===1}))}function Ce(e,a){let n=new Set(e);if(a.set){let r=new Set(a.set);return{add:[...r].filter(d=>!n.has(d)),remove:[...n].filter(d=>!r.has(d))}}let t=[...new Set(a.add??[])].filter(r=>!n.has(r)),i=[...new Set(a.remove??[])].filter(r=>n.has(r));return{add:t,remove:i}}async function q(e,a){let n=[];for(let t of a){let i=await G(e,t);n.some(r=>r.id===i.id)||n.push(i)}return n}async function _e(e,a){let n=[];for(let t of a){let i=await ie(e,t);n.some(r=>r.id===i.id)||n.push(i)}return n}async function $n(e,a,n){let[t,i,r]=await Promise.all([n.set?q(e,n.set):void 0,q(e,n.add??[]),q(e,n.remove??[])]),d=await Ke(e,a),l=d.filter(c=>c.inGroup).map(c=>c.id),s=Ce(l,{set:t?.map(c=>c.id),add:i.map(c=>c.id),remove:r.map(c=>c.id)}),o=new Map(d.map(c=>[c.id,c]));return await e.db.run([...oe(s.remove,Se).map(c=>({sql:`DELETE FROM group_members WHERE group_id = ? AND organization_id = ? AND user_id IN (${c.map(()=>"?").join(", ")})`,params:[a.id,e.organization.id,...c]})),...s.add.map(c=>({sql:`INSERT INTO group_members (group_id, user_id, organization_id, added_at, added_by)
81
+ WHERE m.organization_id = ?1 ORDER BY u.email`,params:[e.organization.id,a.id]}]);return n.map(t=>({...se(t,e.now),inGroup:Number(t.in_group)===1}))}function De(e,a){let n=new Set(e);if(a.set){let r=new Set(a.set);return{add:[...r].filter(d=>!n.has(d)),remove:[...n].filter(d=>!r.has(d))}}let t=[...new Set(a.add??[])].filter(r=>!n.has(r)),i=[...new Set(a.remove??[])].filter(r=>n.has(r));return{add:t,remove:i}}async function q(e,a){let n=[];for(let t of a){let i=await G(e,t);n.some(r=>r.id===i.id)||n.push(i)}return n}async function fe(e,a){let n=[];for(let t of a){let i=await ie(e,t);n.some(r=>r.id===i.id)||n.push(i)}return n}async function Vn(e,a,n){let[t,i,r]=await Promise.all([n.set?q(e,n.set):void 0,q(e,n.add??[]),q(e,n.remove??[])]),d=await da(e,a),l=d.filter(c=>c.inGroup).map(c=>c.id),s=De(l,{set:t?.map(c=>c.id),add:i.map(c=>c.id),remove:r.map(c=>c.id)}),o=new Map(d.map(c=>[c.id,c]));return await e.db.run([...oe(s.remove,Re).map(c=>({sql:`DELETE FROM group_members WHERE group_id = ? AND organization_id = ? AND user_id IN (${c.map(()=>"?").join(", ")})`,params:[a.id,e.organization.id,...c]})),...s.add.map(c=>({sql:`INSERT INTO group_members (group_id, user_id, organization_id, added_at, added_by)
82
82
  SELECT ?1, ?2, ?3, ?4, ?5 WHERE EXISTS (SELECT 1 FROM members WHERE user_id = ?2 AND organization_id = ?3)
83
- ON CONFLICT DO NOTHING`,params:[a.id,c,e.organization.id,e.now,e.operator.userId]})),...s.add.length>0||s.remove.length>0?await A(e,{type:"group.members_changed",data:{group:{id:a.id,name:a.name},added:s.add.slice(0,U).map(c=>D(o.get(c))),removed:s.remove.slice(0,U).map(c=>D(o.get(c))),addedCount:s.add.length,removedCount:s.remove.length}}):[]]),{added:s.add.map(c=>o.get(c)),removed:s.remove.map(c=>o.get(c)),members:await Ke(e,a)}}async function Ze(e,a){let[n=[]]=await e.db.run([{sql:`SELECT ${Te}, (ga.group_id IS NOT NULL) AS granted FROM apps a
83
+ ON CONFLICT DO NOTHING`,params:[a.id,c,e.organization.id,e.now,e.operator.userId]})),...s.add.length>0||s.remove.length>0?await A(e,{type:"group.members_changed",data:{group:{id:a.id,name:a.name},added:s.add.slice(0,U).map(c=>D(o.get(c))),removed:s.remove.slice(0,U).map(c=>D(o.get(c))),addedCount:s.add.length,removedCount:s.remove.length}}):[]]),{added:s.add.map(c=>o.get(c)),removed:s.remove.map(c=>o.get(c)),members:await da(e,a)}}async function la(e,a){let[n=[]]=await e.db.run([{sql:`SELECT ${Oe}, (ga.group_id IS NOT NULL) AS granted FROM apps a
84
84
  LEFT JOIN group_app_access ga ON ga.group_id = ?2 AND ga.app_row_id = a.id
85
- WHERE a.organization_id = ?1 AND a.scope != 'shared' AND a.status != '${O}' ORDER BY a.name`,params:[e.organization.id,a.id]}]);return n.map(t=>({...Re(t),granted:Number(t.granted)===1}))}function le(e){return new m("SHARED_APP_ACCESS",`${e.appSlug} is a shared app: every member of every organization can open it, so its access can't be changed`)}async function Un(e,a,n){let[t,i,r]=await Promise.all([n.set?_e(e,n.set):void 0,_e(e,n.add??[]),_e(e,n.remove??[])]);for(let o of[...t??[],...i])if(o.scope==="shared")throw le(o);let d=await Ze(e,a),l=Ce(d.filter(o=>o.granted).map(o=>o.id),{set:t?.map(o=>o.id),add:i.map(o=>o.id),remove:r.map(o=>o.id)}),s=new Map(d.map(o=>[o.id,o]));return await e.db.run([...oe(l.remove,Se).map(o=>({sql:`DELETE FROM group_app_access WHERE group_id = ? AND organization_id = ? AND app_row_id IN (${o.map(()=>"?").join(", ")})`,params:[a.id,e.organization.id,...o]})),...l.add.map(o=>({sql:`INSERT INTO group_app_access (group_id, app_row_id, organization_id, granted_at, granted_by)
85
+ WHERE a.organization_id = ?1 AND a.scope != 'shared' AND a.status != '${O}' ORDER BY a.name`,params:[e.organization.id,a.id]}]);return n.map(t=>({...Ce(t),granted:Number(t.granted)===1}))}function le(e){return new m("SHARED_APP_ACCESS",`${e.appSlug} is a shared app: every member of every organization can open it, so its access can't be changed`)}async function Xn(e,a,n){let[t,i,r]=await Promise.all([n.set?fe(e,n.set):void 0,fe(e,n.add??[]),fe(e,n.remove??[])]);for(let o of[...t??[],...i])if(o.scope==="shared")throw le(o);let d=await la(e,a),l=De(d.filter(o=>o.granted).map(o=>o.id),{set:t?.map(o=>o.id),add:i.map(o=>o.id),remove:r.map(o=>o.id)}),s=new Map(d.map(o=>[o.id,o]));return await e.db.run([...oe(l.remove,Re).map(o=>({sql:`DELETE FROM group_app_access WHERE group_id = ? AND organization_id = ? AND app_row_id IN (${o.map(()=>"?").join(", ")})`,params:[a.id,e.organization.id,...o]})),...l.add.map(o=>({sql:`INSERT INTO group_app_access (group_id, app_row_id, organization_id, granted_at, granted_by)
86
86
  SELECT ?1, ?2, ?3, ?4, ?5
87
87
  WHERE EXISTS (SELECT 1 FROM apps WHERE id = ?2 AND organization_id = ?3 AND scope != 'shared' AND status != '${O}')
88
- ON CONFLICT DO NOTHING`,params:[a.id,o,e.organization.id,e.now,e.operator.userId]})),...l.add.length>0||l.remove.length>0?await A(e,{type:"group.apps_changed",data:{group:{id:a.id,name:a.name},granted:l.add.slice(0,U).map(o=>X(s.get(o))),revoked:l.remove.slice(0,U).map(o=>X(s.get(o))),grantedCount:l.add.length,revokedCount:l.remove.length}}):[]]),{added:l.add.map(o=>s.get(o)),removed:l.remove.map(o=>s.get(o)),apps:await Ze(e,a)}}async function Pn(e){let[a=[]]=await e.db.run([{sql:`SELECT ${Te} FROM apps a WHERE a.organization_id = ?1 AND a.status != '${O}' ORDER BY a.name`,params:[e.organization.id]}]);return a.map(Re)}function Pa(e){let a=e.trim();if(a.length<1||a.length>Ve)throw new m("APP_NAME_INVALID",`An app's name is 1 to ${Ve} characters${a?` (${a.length} given)`:", not empty"}`,{fix:"give --name the name the app library should show, or leave --name off"});return a}async function xn(e,a,n){let t=await ie(e,a);if(n.name===void 0&&n.description===void 0&&n.everyone===void 0)throw new m("NOTHING_TO_SET","Nothing to set: give --name, --description or --everyone on|off");if(t.scope==="shared"&&n.everyone)throw le(t);let i=n.name===void 0?t.name:Pa(n.name),r=n.description===void 0?t.description:n.description.trim(),d=n.everyone??t.everyone,l=[],s={id:t.appSlug,rowId:t.id,name:i},o=[];if(d&&!t.everyone){let[S=[]]=await e.db.run([{sql:"SELECT u.id, u.email, u.name FROM members m JOIN users u ON u.id = m.user_id WHERE m.organization_id = ? ORDER BY u.email",params:[e.organization.id]}]);for(let f of oe(S,ka))l.push({sql:`INSERT INTO user_app_access (id, user_id, app_row_id, organization_id, granted_by) VALUES ${f.map(()=>"(?, ?, ?, ?, ?)").join(", ")} ON CONFLICT DO NOTHING RETURNING id`,params:f.flatMap(y=>[H(),String(y.id),t.id,e.organization.id,e.operator.userId])});let b=S.map(f=>D({id:String(f.id),email:String(f.email),name:u(f.name)}));o.push(...await A(e,{type:"app.access_changed",data:{app:s,granted:b.slice(0,U),revoked:[],grantedCount:b.length,revokedCount:0,everyone:!0},appRowId:t.id}))}else n.everyone===!1&&t.everyone&&o.push(...await A(e,{type:"app.access_changed",data:{app:s,granted:[],revoked:[],grantedCount:0,revokedCount:0,everyone:!1},appRowId:t.id}));(i!==t.name||r!==t.description)&&o.push(...await A(e,{type:"app.renamed",data:{app:s,...i!==t.name?{previousName:t.name}:{},...r!==t.description?{description:r}:{}},appRowId:t.id}));let h=(await e.db.run([{sql:`UPDATE apps SET name = ?3, description = ?4, is_default = ?5, updated_at = ?6
89
- WHERE id = ?1 AND organization_id = ?2 AND status != '${O}'`,params:[t.id,e.organization.id,i,r,d?1:0,e.now]},...l,...o])).slice(1,1+l.length);return{app:await ie(e,t.id),granted:h.reduce((S,b)=>S+b.length,0)}}async function Qe(e,a){if(a.scope==="shared")throw le(a);let[n=[]]=await e.db.run([{sql:`SELECT ${de},
88
+ ON CONFLICT DO NOTHING`,params:[a.id,o,e.organization.id,e.now,e.operator.userId]})),...l.add.length>0||l.remove.length>0?await A(e,{type:"group.apps_changed",data:{group:{id:a.id,name:a.name},granted:l.add.slice(0,U).map(o=>X(s.get(o))),revoked:l.remove.slice(0,U).map(o=>X(s.get(o))),grantedCount:l.add.length,revokedCount:l.remove.length}}):[]]),{added:l.add.map(o=>s.get(o)),removed:l.remove.map(o=>s.get(o)),apps:await la(e,a)}}async function jn(e){let[a=[]]=await e.db.run([{sql:`SELECT ${Oe} FROM apps a WHERE a.organization_id = ?1 AND a.status != '${O}' ORDER BY a.name`,params:[e.organization.id]}]);return a.map(Ce)}function Xa(e){let a=e.trim();if(a.length<1||a.length>ra)throw new m("APP_NAME_INVALID",`An app's name is 1 to ${ra} characters${a?` (${a.length} given)`:", not empty"}`,{fix:"give --name the name the app library should show, or leave --name off"});return a}async function Kn(e,a,n){let t=await ie(e,a);if(n.name===void 0&&n.description===void 0&&n.everyone===void 0)throw new m("NOTHING_TO_SET","Nothing to set: give --name, --description or --everyone on|off");if(t.scope==="shared"&&n.everyone)throw le(t);let i=n.name===void 0?t.name:Xa(n.name),r=n.description===void 0?t.description:n.description.trim(),d=n.everyone??t.everyone,l=[],s={id:t.appSlug,rowId:t.id,name:i},o=[];if(d&&!t.everyone){let[b=[]]=await e.db.run([{sql:"SELECT u.id, u.email, u.name FROM members m JOIN users u ON u.id = m.user_id WHERE m.organization_id = ? ORDER BY u.email",params:[e.organization.id]}]);for(let f of oe(b,Fa))l.push({sql:`INSERT INTO user_app_access (id, user_id, app_row_id, organization_id, granted_by) VALUES ${f.map(()=>"(?, ?, ?, ?, ?)").join(", ")} ON CONFLICT DO NOTHING RETURNING id`,params:f.flatMap(y=>[H(),String(y.id),t.id,e.organization.id,e.operator.userId])});let N=b.map(f=>D({id:String(f.id),email:String(f.email),name:u(f.name)}));o.push(...await A(e,{type:"app.access_changed",data:{app:s,granted:N.slice(0,U),revoked:[],grantedCount:N.length,revokedCount:0,everyone:!0},appRowId:t.id}))}else n.everyone===!1&&t.everyone&&o.push(...await A(e,{type:"app.access_changed",data:{app:s,granted:[],revoked:[],grantedCount:0,revokedCount:0,everyone:!1},appRowId:t.id}));(i!==t.name||r!==t.description)&&o.push(...await A(e,{type:"app.renamed",data:{app:s,...i!==t.name?{previousName:t.name}:{},...r!==t.description?{description:r}:{}},appRowId:t.id}));let S=(await e.db.run([{sql:`UPDATE apps SET name = ?3, description = ?4, is_default = ?5, updated_at = ?6
89
+ WHERE id = ?1 AND organization_id = ?2 AND status != '${O}'`,params:[t.id,e.organization.id,i,r,d?1:0,e.now]},...l,...o])).slice(1,1+l.length);return{app:await ie(e,t.id),granted:S.reduce((b,N)=>b+N.length,0)}}async function ca(e,a){if(a.scope==="shared")throw le(a);let[n=[]]=await e.db.run([{sql:`SELECT ${de},
90
90
  (ua.id IS NOT NULL) AS direct,
91
91
  (SELECT group_concat(g.name, char(31)) FROM group_app_access ga
92
92
  JOIN group_members gm ON gm.group_id = ga.group_id AND gm.user_id = u.id
@@ -94,35 +94,35 @@ WHERE id = ?1 AND organization_id = ?2 AND status != '${O}'`,params:[t.id,e.orga
94
94
  WHERE ga.app_row_id = ?2 AND ga.organization_id = ?1) AS via_groups
95
95
  FROM members m JOIN users u ON u.id = m.user_id
96
96
  LEFT JOIN user_app_access ua ON ua.app_row_id = ?2 AND ua.organization_id = ?1 AND ua.user_id = u.id
97
- WHERE m.organization_id = ?1 ORDER BY u.email`,params:[e.organization.id,a.id]}]);return n.map(t=>({...se(t,e.now),direct:Number(t.direct)===1,viaGroups:u(t.via_groups)?.split("").sort()??[]}))}async function Fn(e,a,n){if(a.scope==="shared")throw le(a);let[t,i,r]=await Promise.all([n.set?q(e,n.set):void 0,q(e,n.add??[]),q(e,n.remove??[])]),d=await Qe(e,a),l=Ce(d.filter(o=>o.direct).map(o=>o.id),{set:t?.map(o=>o.id),add:i.map(o=>o.id),remove:r.map(o=>o.id)}),s=new Map(d.map(o=>[o.id,o]));return await e.db.run([...oe(l.remove,Se).map(o=>({sql:`DELETE FROM user_app_access WHERE app_row_id = ? AND organization_id = ? AND user_id IN (${o.map(()=>"?").join(", ")})`,params:[a.id,e.organization.id,...o]})),...l.add.map(o=>({sql:`INSERT INTO user_app_access (id, user_id, app_row_id, organization_id, granted_at, granted_by)
97
+ WHERE m.organization_id = ?1 ORDER BY u.email`,params:[e.organization.id,a.id]}]);return n.map(t=>({...se(t,e.now),direct:Number(t.direct)===1,viaGroups:u(t.via_groups)?.split("").sort()??[]}))}async function Zn(e,a,n){if(a.scope==="shared")throw le(a);let[t,i,r]=await Promise.all([n.set?q(e,n.set):void 0,q(e,n.add??[]),q(e,n.remove??[])]),d=await ca(e,a),l=De(d.filter(o=>o.direct).map(o=>o.id),{set:t?.map(o=>o.id),add:i.map(o=>o.id),remove:r.map(o=>o.id)}),s=new Map(d.map(o=>[o.id,o]));return await e.db.run([...oe(l.remove,Re).map(o=>({sql:`DELETE FROM user_app_access WHERE app_row_id = ? AND organization_id = ? AND user_id IN (${o.map(()=>"?").join(", ")})`,params:[a.id,e.organization.id,...o]})),...l.add.map(o=>({sql:`INSERT INTO user_app_access (id, user_id, app_row_id, organization_id, granted_at, granted_by)
98
98
  SELECT ?1, ?2, ?3, ?4, ?5, ?6 WHERE EXISTS (SELECT 1 FROM members WHERE user_id = ?2 AND organization_id = ?4)
99
- ON CONFLICT DO NOTHING`,params:[H(),o,a.id,e.organization.id,e.now,e.operator.userId]})),...l.add.length>0||l.remove.length>0?await A(e,{type:"app.access_changed",data:{app:X(a),granted:l.add.slice(0,U).map(o=>D(s.get(o))),revoked:l.remove.slice(0,U).map(o=>D(s.get(o))),grantedCount:l.add.length,revokedCount:l.remove.length},appRowId:a.id}):[]]),{granted:l.add.map(o=>s.get(o)),revoked:l.remove.map(o=>s.get(o)),users:await Qe(e,a)}}function F(e){return e.appCap!==null?{cap:e.appCap,source:"app"}:e.unlimited?{cap:null,source:null}:e.defaultCap!==null?{cap:e.defaultCap,source:"default"}:{cap:null,source:null}}var ea=(e,a)=>e===null?a!==null:a===null||e>a,aa=new WeakMap;function _a(e){let a=aa.get(e);return a||(a=e.run([{sql:"PRAGMA table_info(org_settings)"},{sql:"PRAGMA table_info(org_app_settings)"}]).then(([n=[],t=[]])=>n.length>0&&t.some(i=>i.name==="unlimited")),aa.set(e,a)),a}async function Oe(e){if(!await _a(e))throw new m("GATEWAY_PREDATES_DEFAULT_CAPS",`The Gateway predates default caps
100
- Its database has no workspace default cap and no no-cap override until the Gateway is updated to a release that has them.`,{rerun:`${_} update`,rerunNote:"then run this again"})}var Ea=e=>e===null||Number.isInteger(e)&&e>=1&&e<=na,fa=()=>new m("INVALID_CAP",`A monthly cap is a whole number from 1 to ${na.toLocaleString("en-US")}, or off`),k=e=>e==null?null:Number(e);function Aa(e,a){return!ea(e.calls,a.calls)&&!ea(e.cost,a.cost)?null:{cap:e.calls,previousCap:a.calls,...e.cost!==a.cost&&{costCap:Y(e.cost),previousCostCap:Y(a.cost)}}}function ba(e,a){return e.calls===a.calls&&e.cost===a.cost?null:{cap:e.calls,previousCap:a.calls,...e.cost!==a.cost&&{costCap:Y(e.cost),previousCostCap:Y(a.cost)}}}async function qn(e,a,n,t={}){let i=t.unlimited??!1,r=await ie(e,a,{callable:!0});if(n!==void 0&&!Ea(n))throw fa();let d=ge(t.costUsd);if(i&&(n!=null||d!=null))throw new m("INVALID_CAP",`An app has a cap or no cap, not both
99
+ ON CONFLICT DO NOTHING`,params:[H(),o,a.id,e.organization.id,e.now,e.operator.userId]})),...l.add.length>0||l.remove.length>0?await A(e,{type:"app.access_changed",data:{app:X(a),granted:l.add.slice(0,U).map(o=>D(s.get(o))),revoked:l.remove.slice(0,U).map(o=>D(s.get(o))),grantedCount:l.add.length,revokedCount:l.remove.length},appRowId:a.id}):[]]),{granted:l.add.map(o=>s.get(o)),revoked:l.remove.map(o=>s.get(o)),users:await ca(e,a)}}function F(e){return e.appCap!==null?{cap:e.appCap,source:"app"}:e.unlimited?{cap:null,source:null}:e.defaultCap!==null?{cap:e.defaultCap,source:"default"}:{cap:null,source:null}}var pa=(e,a)=>e===null?a!==null:a===null||e>a,ua=new WeakMap;function Oa(e){let a=ua.get(e);return a||(a=e.run([{sql:"PRAGMA table_info(org_settings)"},{sql:"PRAGMA table_info(org_app_settings)"}]).then(([n=[],t=[]])=>n.length>0&&t.some(i=>i.name==="unlimited")),ua.set(e,a)),a}async function ye(e){if(!await Oa(e))throw new m("GATEWAY_PREDATES_DEFAULT_CAPS",`The Gateway predates default caps
100
+ Its database has no workspace default cap and no no-cap override until the Gateway is updated to a release that has them.`,{rerun:`${_} update`,rerunNote:"then run this again"})}var Da=e=>e===null||Number.isInteger(e)&&e>=1&&e<=ma,ya=()=>new m("INVALID_CAP",`A monthly cap is a whole number from 1 to ${ma.toLocaleString("en-US")}, or off`),k=e=>e==null?null:Number(e);function va(e,a){return!pa(e.calls,a.calls)&&!pa(e.cost,a.cost)?null:{cap:e.calls,previousCap:a.calls,...e.cost!==a.cost&&{costCap:Y(e.cost),previousCostCap:Y(a.cost)}}}function Ia(e,a){return e.calls===a.calls&&e.cost===a.cost?null:{cap:e.calls,previousCap:a.calls,...e.cost!==a.cost&&{costCap:Y(e.cost),previousCostCap:Y(a.cost)}}}async function Qn(e,a,n,t={}){let i=t.unlimited??!1,r=await ie(e,a,{callable:!0});if(n!==void 0&&!Da(n))throw ya();let d=ge(t.costUsd);if(i&&(n!=null||d!=null))throw new m("INVALID_CAP",`An app has a cap or no cap, not both
101
101
  Give a number, --usd, off, or --unlimited.`);if(!i&&n===void 0&&d===void 0)throw new m("INVALID_CAP",`Missing the cap
102
- Give a number of calls, --usd, off, or --unlimited.`);let l=await _a(e.db);i&&await Oe(e.db),d!==void 0&&await me(e.db);let s=l&&await $(e.db),o=e.organization.id,[c=[],h=[]]=await e.db.run([{sql:`SELECT monthly_call_cap${l?", unlimited":""}${s?", monthly_cost_cap_micros":""} FROM org_app_settings WHERE organization_id = ? AND app_row_id = ?`,params:[o,r.id]},...l?[{sql:`SELECT default_monthly_call_cap AS cap${s?", default_monthly_cost_cap_micros AS cost_cap":""} FROM org_settings WHERE organization_id = ?`,params:[o]}]:[]]),S=k(c[0]?.monthly_call_cap),b=k(c[0]?.monthly_cost_cap_micros),f=Number(c[0]?.unlimited??0)===1,y=k(h[0]?.cap),w=k(h[0]?.cost_cap),T=i?null:n===void 0?S:n,v=i?null:d===void 0?b:d,R=F({appCap:S,unlimited:f,defaultCap:y}),B=F({appCap:b,unlimited:f,defaultCap:w}),I=F({appCap:T,unlimited:i,defaultCap:y}),P=F({appCap:v,unlimited:i,defaultCap:w}),p=n==null&&d==null?null:await ue(e.db,{organizationId:o,deployer:e.operator,now:e.now}),E={calls:I.cap,cost:P.cap},C={calls:R.cap,cost:B.cap},M=Aa(E,C),L=M?null:ba(E,C)??(I.source!==R.source||P.source!==B.source?{cap:I.cap,previousCap:R.cap}:null),N=M?await A(e,{type:"cap.raised",data:{app:X(r),...M,source:"app"},appRowId:r.id}):L?await A(e,{type:"cap.changed",data:{app:X(r),...L,source:"app"},appRowId:r.id}):[],j=T===null&&v===null&&!i?{sql:"DELETE FROM org_app_settings WHERE organization_id = ? AND app_row_id = ?",params:[o,r.id]}:s?{sql:`INSERT INTO org_app_settings (organization_id, app_row_id, monthly_call_cap, monthly_cost_cap_micros, unlimited, updated_at, updated_by)
102
+ Give a number of calls, --usd, off, or --unlimited.`);let l=await Oa(e.db);i&&await ye(e.db),d!==void 0&&await me(e.db);let s=l&&await $(e.db),o=e.organization.id,[c=[],S=[]]=await e.db.run([{sql:`SELECT monthly_call_cap${l?", unlimited":""}${s?", monthly_cost_cap_micros":""} FROM org_app_settings WHERE organization_id = ? AND app_row_id = ?`,params:[o,r.id]},...l?[{sql:`SELECT default_monthly_call_cap AS cap${s?", default_monthly_cost_cap_micros AS cost_cap":""} FROM org_settings WHERE organization_id = ?`,params:[o]}]:[]]),b=k(c[0]?.monthly_call_cap),N=k(c[0]?.monthly_cost_cap_micros),f=Number(c[0]?.unlimited??0)===1,y=k(S[0]?.cap),L=k(S[0]?.cost_cap),T=i?null:n===void 0?b:n,v=i?null:d===void 0?N:d,R=F({appCap:b,unlimited:f,defaultCap:y}),B=F({appCap:N,unlimited:f,defaultCap:L}),I=F({appCap:T,unlimited:i,defaultCap:y}),P=F({appCap:v,unlimited:i,defaultCap:L}),p=n==null&&d==null?null:await ue(e.db,{organizationId:o,deployer:e.operator,now:e.now}),E={calls:I.cap,cost:P.cap},C={calls:R.cap,cost:B.cap},W=va(E,C),z=W?null:Ia(E,C)??(I.source!==R.source||P.source!==B.source?{cap:I.cap,previousCap:R.cap}:null),h=W?await A(e,{type:"cap.raised",data:{app:X(r),...W,source:"app"},appRowId:r.id}):z?await A(e,{type:"cap.changed",data:{app:X(r),...z,source:"app"},appRowId:r.id}):[],j=T===null&&v===null&&!i?{sql:"DELETE FROM org_app_settings WHERE organization_id = ? AND app_row_id = ?",params:[o,r.id]}:s?{sql:`INSERT INTO org_app_settings (organization_id, app_row_id, monthly_call_cap, monthly_cost_cap_micros, unlimited, updated_at, updated_by)
103
103
  VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
104
104
  ON CONFLICT (organization_id, app_row_id) DO UPDATE SET monthly_call_cap = ?3, monthly_cost_cap_micros = ?4, unlimited = ?5, updated_at = ?6, updated_by = ?7`,params:[o,r.id,T,v,i?1:0,e.now,e.operator.userId]}:l?{sql:`INSERT INTO org_app_settings (organization_id, app_row_id, monthly_call_cap, unlimited, updated_at, updated_by)
105
105
  VALUES (?1, ?2, ?3, ?4, ?5, ?6)
106
106
  ON CONFLICT (organization_id, app_row_id) DO UPDATE SET monthly_call_cap = ?3, unlimited = ?4, updated_at = ?5, updated_by = ?6`,params:[o,r.id,T,i?1:0,e.now,e.operator.userId]}:{sql:`INSERT INTO org_app_settings (organization_id, app_row_id, monthly_call_cap, updated_at, updated_by)
107
107
  VALUES (?1, ?2, ?3, ?4, ?5)
108
- ON CONFLICT (organization_id, app_row_id) DO UPDATE SET monthly_call_cap = ?3, updated_at = ?4, updated_by = ?5`,params:[o,r.id,T,e.now,e.operator.userId]},x=await e.db.run([j,...p?[p]:[],...N]),Ca=p?u(x[1]?.[0]?.id):null;return{app:r,cap:T,costCapMicros:v,unlimited:i,was:S,wasCostCapMicros:b,wasUnlimited:f,effective:I,before:R,effectiveCost:P,beforeCost:B,capAlertsId:Ca}}async function Hn(e,a,n){if(a!==void 0&&!Ea(a))throw fa();let t=ge(n);if(a===void 0&&t===void 0)throw new m("INVALID_CAP",`Missing the default
109
- Give a number of calls, --usd, or off.`);await Oe(e.db),t!==void 0&&await me(e.db);let i=await $(e.db),r=e.organization.id,[d=[]]=await e.db.run([{sql:`SELECT default_monthly_call_cap AS cap${i?", default_monthly_cost_cap_micros AS cost_cap":""} FROM org_settings WHERE organization_id = ?`,params:[r]}]),l=k(d[0]?.cap),s=k(d[0]?.cost_cap),o=a===void 0?l:a,c=t===void 0?s:t,h=a==null&&t==null?null:await ue(e.db,{organizationId:r,deployer:e.operator,now:e.now}),S={calls:o,cost:c},b={calls:l,cost:s},f=Aa(S,b),y=f?null:ba(S,b),w=f?await A(e,{type:"cap.raised",data:{app:null,...f,source:"default"}}):y?await A(e,{type:"cap.changed",data:{app:null,...y,source:"default"}}):[],T=[i?{sql:`INSERT INTO org_settings (organization_id, default_monthly_call_cap, default_monthly_cost_cap_micros, updated_at, updated_by)
108
+ ON CONFLICT (organization_id, app_row_id) DO UPDATE SET monthly_call_cap = ?3, updated_at = ?4, updated_by = ?5`,params:[o,r.id,T,e.now,e.operator.userId]},x=await e.db.run([j,...p?[p]:[],...h]),za=p?u(x[1]?.[0]?.id):null;return{app:r,cap:T,costCapMicros:v,unlimited:i,was:b,wasCostCapMicros:N,wasUnlimited:f,effective:I,before:R,effectiveCost:P,beforeCost:B,capAlertsId:za}}async function et(e,a,n){if(a!==void 0&&!Da(a))throw ya();let t=ge(n);if(a===void 0&&t===void 0)throw new m("INVALID_CAP",`Missing the default
109
+ Give a number of calls, --usd, or off.`);await ye(e.db),t!==void 0&&await me(e.db);let i=await $(e.db),r=e.organization.id,[d=[]]=await e.db.run([{sql:`SELECT default_monthly_call_cap AS cap${i?", default_monthly_cost_cap_micros AS cost_cap":""} FROM org_settings WHERE organization_id = ?`,params:[r]}]),l=k(d[0]?.cap),s=k(d[0]?.cost_cap),o=a===void 0?l:a,c=t===void 0?s:t,S=a==null&&t==null?null:await ue(e.db,{organizationId:r,deployer:e.operator,now:e.now}),b={calls:o,cost:c},N={calls:l,cost:s},f=va(b,N),y=f?null:Ia(b,N),L=f?await A(e,{type:"cap.raised",data:{app:null,...f,source:"default"}}):y?await A(e,{type:"cap.changed",data:{app:null,...y,source:"default"}}):[],T=[i?{sql:`INSERT INTO org_settings (organization_id, default_monthly_call_cap, default_monthly_cost_cap_micros, updated_at, updated_by)
110
110
  VALUES (?1, ?2, ?3, ?4, ?5)
111
111
  ON CONFLICT (organization_id) DO UPDATE SET default_monthly_call_cap = ?2, default_monthly_cost_cap_micros = ?3, updated_at = ?4, updated_by = ?5`,params:[r,o,c,e.now,e.operator.userId]}:{sql:`INSERT INTO org_settings (organization_id, default_monthly_call_cap, updated_at, updated_by)
112
112
  VALUES (?1, ?2, ?3, ?4)
113
- ON CONFLICT (organization_id) DO UPDATE SET default_monthly_call_cap = ?2, updated_at = ?3, updated_by = ?4`,params:[r,o,e.now,e.operator.userId]},...a===null||t===null?[{sql:`DELETE FROM org_settings WHERE organization_id = ? AND ${Q({cost:i,ai:await ee(e.db)})}`,params:[r]}]:[]],v=await e.db.run([...T,...h?[h]:[],...w]),R=h?u(v[T.length]?.[0]?.id):null;return{cap:o,was:l,costCapMicros:c,wasCostCapMicros:s,capAlertsId:R}}var xa=/^\d{4}-(0[1-9]|1[0-2])$/;function Na(e){return`${e.getUTCFullYear()}-${String(e.getUTCMonth()+1).padStart(2,"0")}`}function Fa(e,a){let[n,t]=e.split("-").map(Number);return Na(new Date(Date.UTC(n,t-1+a,1)))}var Ee=`COALESCE(SUM(d.calls), 0) AS calls, COALESCE(SUM(d.failures), 0) AS failures,
113
+ ON CONFLICT (organization_id) DO UPDATE SET default_monthly_call_cap = ?2, updated_at = ?3, updated_by = ?4`,params:[r,o,e.now,e.operator.userId]},...a===null||t===null?[{sql:`DELETE FROM org_settings WHERE organization_id = ? AND ${Q({cost:i,ai:await _e(e.db),models:await ee(e.db)})}`,params:[r]}]:[]],v=await e.db.run([...T,...S?[S]:[],...L]),R=S?u(v[T.length]?.[0]?.id):null;return{cap:o,was:l,costCapMicros:c,wasCostCapMicros:s,capAlertsId:R}}var ja=/^\d{4}-(0[1-9]|1[0-2])$/;function ka(e){return`${e.getUTCFullYear()}-${String(e.getUTCMonth()+1).padStart(2,"0")}`}function Ka(e,a){let[n,t]=e.split("-").map(Number);return ka(new Date(Date.UTC(n,t-1+a,1)))}var Ae=`COALESCE(SUM(d.calls), 0) AS calls, COALESCE(SUM(d.failures), 0) AS failures,
114
114
  COALESCE(SUM(d.input_tokens), 0) AS input_tokens, COALESCE(SUM(d.output_tokens), 0) AS output_tokens,
115
- COALESCE(SUM(d.cost_micros), 0) AS cost_micros`,fe=e=>({calls:g(e.calls),failures:g(e.failures),inputTokens:g(e.input_tokens),outputTokens:g(e.output_tokens),costMicros:g(e.cost_micros)}),Ae={calls:0,failures:0,inputTokens:0,outputTokens:0,costMicros:0},be={monthlyCallCap:null,capUnlimited:!1,effectiveMonthlyCallCap:null,capSource:null,monthlyCostCapMicros:null,effectiveMonthlyCostCapMicros:null,costCapSource:null,paused:!1,pausedBy:null};async function Gn(e,a){if(!xa.test(a))throw new m("INVALID_MONTH",`--month ${a} isn't a month: use YYYY-MM`);await Oe(e.db);let n=await $(e.db),t=e.organization.id,i=`${a}-01`,r=`${Fa(a,1)}-01`,d=Date.parse(`${i}T00:00:00.000Z`),l=Date.parse(`${r}T00:00:00.000Z`),s="d.organization_id = ?1 AND d.day >= ?2 AND d.day < ?3",o="c.organization_id = ?1 AND c.decision = 'allowed' AND c.created_at >= ?2 AND c.created_at < ?3",[c=[],h=[],S=[],b=[],f=[],y=[],w=[]]=await e.db.run([{sql:`SELECT d.app_row_id, a.name, a.app_slug, ${Ee} FROM usage_daily d LEFT JOIN apps a ON a.id = d.app_row_id
116
- WHERE ${s} GROUP BY d.app_row_id, a.name, a.app_slug`,params:[t,i,r]},{sql:`SELECT d.user_id, u.name, u.email, ${Ee} FROM usage_daily d
115
+ COALESCE(SUM(d.cost_micros), 0) AS cost_micros`,be=e=>({calls:g(e.calls),failures:g(e.failures),inputTokens:g(e.input_tokens),outputTokens:g(e.output_tokens),costMicros:g(e.cost_micros)}),Ne={calls:0,failures:0,inputTokens:0,outputTokens:0,costMicros:0},Se={monthlyCallCap:null,capUnlimited:!1,effectiveMonthlyCallCap:null,capSource:null,monthlyCostCapMicros:null,effectiveMonthlyCostCapMicros:null,costCapSource:null,paused:!1,pausedBy:null};async function at(e,a){if(!ja.test(a))throw new m("INVALID_MONTH",`--month ${a} isn't a month: use YYYY-MM`);await ye(e.db);let n=await $(e.db),t=e.organization.id,i=`${a}-01`,r=`${Ka(a,1)}-01`,d=Date.parse(`${i}T00:00:00.000Z`),l=Date.parse(`${r}T00:00:00.000Z`),s="d.organization_id = ?1 AND d.day >= ?2 AND d.day < ?3",o="c.organization_id = ?1 AND c.decision = 'allowed' AND c.created_at >= ?2 AND c.created_at < ?3",[c=[],S=[],b=[],N=[],f=[],y=[],L=[]]=await e.db.run([{sql:`SELECT d.app_row_id, a.name, a.app_slug, ${Ae} FROM usage_daily d LEFT JOIN apps a ON a.id = d.app_row_id
116
+ WHERE ${s} GROUP BY d.app_row_id, a.name, a.app_slug`,params:[t,i,r]},{sql:`SELECT d.user_id, u.name, u.email, ${Ae} FROM usage_daily d
117
117
  LEFT JOIN members m ON m.user_id = d.user_id AND m.organization_id = d.organization_id
118
118
  LEFT JOIN users u ON u.id = m.user_id
119
- WHERE ${s} GROUP BY d.user_id, u.name, u.email`,params:[t,i,r]},{sql:`SELECT d.kind, d.target, ${Ee} FROM usage_daily d WHERE ${s} GROUP BY d.kind, d.target`,params:[t,i,r]},{sql:`SELECT c.app_row_id, a.name, a.app_slug, COUNT(*) AS tool_calls FROM mcp_tool_calls c
119
+ WHERE ${s} GROUP BY d.user_id, u.name, u.email`,params:[t,i,r]},{sql:`SELECT d.kind, d.target, ${Ae} FROM usage_daily d WHERE ${s} GROUP BY d.kind, d.target`,params:[t,i,r]},{sql:`SELECT c.app_row_id, a.name, a.app_slug, COUNT(*) AS tool_calls FROM mcp_tool_calls c
120
120
  LEFT JOIN apps a ON a.id = c.app_row_id WHERE ${o} GROUP BY c.app_row_id, a.name, a.app_slug`,params:[t,d,l]},{sql:`SELECT c.user_id, u.name, u.email, COUNT(*) AS tool_calls FROM mcp_tool_calls c
121
121
  LEFT JOIN members m ON m.user_id = c.user_id AND m.organization_id = c.organization_id
122
122
  LEFT JOIN users u ON u.id = m.user_id
123
123
  WHERE ${o} GROUP BY c.user_id, u.name, u.email`,params:[t,d,l]},{sql:`SELECT a.id AS app_row_id, a.name, a.app_slug, s.monthly_call_cap, s.unlimited${n?", s.monthly_cost_cap_micros":""} FROM apps a
124
124
  LEFT JOIN org_app_settings s ON s.app_row_id = a.id AND s.organization_id = ?1
125
- WHERE (a.organization_id = ?1 OR a.scope = 'shared') AND a.status != '${O}'`,params:[t]},{sql:`SELECT default_monthly_call_cap AS cap${n?", default_monthly_cost_cap_micros AS cost_cap":""} FROM org_settings WHERE organization_id = ?`,params:[t]}]),T=k(w[0]?.cap),v=k(w[0]?.cost_cap),R=new Map;for(let p of c)R.set(String(p.app_row_id),{appRowId:String(p.app_row_id),appSlug:u(p.app_slug),name:u(p.name),...fe(p),toolCalls:0,...be,callable:!1});for(let p of b){let E=u(p.app_row_id);if(!E)continue;let C=R.get(E);C?C.toolCalls=g(p.tool_calls):R.set(E,{appRowId:E,appSlug:u(p.app_slug),name:u(p.name),...Ae,toolCalls:g(p.tool_calls),...be,callable:!1})}let B=a===Na(new Date(e.now));for(let p of y){let E=String(p.app_row_id),C=p.monthly_call_cap==null?null:Number(p.monthly_call_cap),M=Number(p.unlimited??0)===1,L=F({appCap:C,unlimited:M,defaultCap:T}),N=R.get(E)??{appRowId:E,appSlug:u(p.app_slug),name:u(p.name),...Ae,toolCalls:0,...be,callable:!0};N.callable=!0,N.monthlyCallCap=C,N.capUnlimited=M,N.effectiveMonthlyCallCap=L.cap,N.capSource=L.source;let j=k(p.monthly_cost_cap_micros),x=F({appCap:j,unlimited:M,defaultCap:v});N.monthlyCostCapMicros=j,N.effectiveMonthlyCostCapMicros=x.cap,N.costCapSource=x.source,N.pausedBy=B?L.cap!==null&&N.calls>=L.cap?"calls":x.cap!==null&&N.costMicros>=x.cap?"cost":null:null,N.paused=N.pausedBy!==null,R.set(E,N)}let I=new Map;for(let p of h)I.set(String(p.user_id),{userId:String(p.user_id),name:u(p.name),email:u(p.email),...fe(p),toolCalls:0});for(let p of f){let E=String(p.user_id),C=I.get(E);C?C.toolCalls=g(p.tool_calls):I.set(E,{userId:E,name:u(p.name),email:u(p.email),...Ae,toolCalls:g(p.tool_calls)})}let P=(p,E)=>E.calls-p.calls||E.toolCalls-p.toolCalls;return{month:a,defaultMonthlyCallCap:T,defaultMonthlyCostCapMicros:v,byApp:[...R.values()].sort((p,E)=>P(p,E)||(p.name??p.appRowId).localeCompare(E.name??E.appRowId)),byMember:[...I.values()].sort(P),byTarget:S.map(p=>({kind:String(p.kind),target:String(p.target),...fe(p)})).sort((p,E)=>E.calls-p.calls)}}var qa=200;async function Bn(e,a){let n=Math.min(Math.max(1,Math.trunc(a.limit??50)||1),qa),t=["c.organization_id = ?"],i=[e.organization.id];a.before&&(t.push("(c.created_at < ? OR (c.created_at = ? AND c.id < ?))"),i.push(a.before.createdAt,a.before.createdAt,a.before.id));for(let[s,o]of[["c.app_row_id",a.appRowId],["c.user_id",a.userId],["c.decision",a.decision],["c.risk",a.risk]])o!==void 0&&(t.push(`${s} = ?`),i.push(o));a.since!==void 0&&(t.push("c.created_at >= ?"),i.push(a.since)),i.push(n+1);let[r=[]]=await e.db.run([{sql:`SELECT c.id, c.created_at, c.user_id, u.name AS user_name, u.email AS user_email,
125
+ WHERE (a.organization_id = ?1 OR a.scope = 'shared') AND a.status != '${O}'`,params:[t]},{sql:`SELECT default_monthly_call_cap AS cap${n?", default_monthly_cost_cap_micros AS cost_cap":""} FROM org_settings WHERE organization_id = ?`,params:[t]}]),T=k(L[0]?.cap),v=k(L[0]?.cost_cap),R=new Map;for(let p of c)R.set(String(p.app_row_id),{appRowId:String(p.app_row_id),appSlug:u(p.app_slug),name:u(p.name),...be(p),toolCalls:0,...Se,callable:!1});for(let p of N){let E=u(p.app_row_id);if(!E)continue;let C=R.get(E);C?C.toolCalls=g(p.tool_calls):R.set(E,{appRowId:E,appSlug:u(p.app_slug),name:u(p.name),...Ne,toolCalls:g(p.tool_calls),...Se,callable:!1})}let B=a===ka(new Date(e.now));for(let p of y){let E=String(p.app_row_id),C=p.monthly_call_cap==null?null:Number(p.monthly_call_cap),W=Number(p.unlimited??0)===1,z=F({appCap:C,unlimited:W,defaultCap:T}),h=R.get(E)??{appRowId:E,appSlug:u(p.app_slug),name:u(p.name),...Ne,toolCalls:0,...Se,callable:!0};h.callable=!0,h.monthlyCallCap=C,h.capUnlimited=W,h.effectiveMonthlyCallCap=z.cap,h.capSource=z.source;let j=k(p.monthly_cost_cap_micros),x=F({appCap:j,unlimited:W,defaultCap:v});h.monthlyCostCapMicros=j,h.effectiveMonthlyCostCapMicros=x.cap,h.costCapSource=x.source,h.pausedBy=B?z.cap!==null&&h.calls>=z.cap?"calls":x.cap!==null&&h.costMicros>=x.cap?"cost":null:null,h.paused=h.pausedBy!==null,R.set(E,h)}let I=new Map;for(let p of S)I.set(String(p.user_id),{userId:String(p.user_id),name:u(p.name),email:u(p.email),...be(p),toolCalls:0});for(let p of f){let E=String(p.user_id),C=I.get(E);C?C.toolCalls=g(p.tool_calls):I.set(E,{userId:E,name:u(p.name),email:u(p.email),...Ne,toolCalls:g(p.tool_calls)})}let P=(p,E)=>E.calls-p.calls||E.toolCalls-p.toolCalls;return{month:a,defaultMonthlyCallCap:T,defaultMonthlyCostCapMicros:v,byApp:[...R.values()].sort((p,E)=>P(p,E)||(p.name??p.appRowId).localeCompare(E.name??E.appRowId)),byMember:[...I.values()].sort(P),byTarget:b.map(p=>({kind:String(p.kind),target:String(p.target),...be(p)})).sort((p,E)=>E.calls-p.calls)}}var Za=200;async function nt(e,a){let n=Math.min(Math.max(1,Math.trunc(a.limit??50)||1),Za),t=["c.organization_id = ?"],i=[e.organization.id];a.before&&(t.push("(c.created_at < ? OR (c.created_at = ? AND c.id < ?))"),i.push(a.before.createdAt,a.before.createdAt,a.before.id));for(let[s,o]of[["c.app_row_id",a.appRowId],["c.user_id",a.userId],["c.decision",a.decision],["c.risk",a.risk]])o!==void 0&&(t.push(`${s} = ?`),i.push(o));a.since!==void 0&&(t.push("c.created_at >= ?"),i.push(a.since)),i.push(n+1);let[r=[]]=await e.db.run([{sql:`SELECT c.id, c.created_at, c.user_id, u.name AS user_name, u.email AS user_email,
126
126
  c.credential_kind, c.client_name, c.client_info, c.app_row_id, c.app_slug, a.name AS app_name, a.status AS app_status,
127
127
  c.tool_name, c.via, c.risk, c.decision, c.reason, c.outcome, c.duration_ms, c.result_bytes
128
128
  FROM mcp_tool_calls c
@@ -130,17 +130,17 @@ LEFT JOIN apps a ON a.id = c.app_row_id
130
130
  LEFT JOIN members m ON m.user_id = c.user_id AND m.organization_id = c.organization_id
131
131
  LEFT JOIN users u ON u.id = m.user_id
132
132
  WHERE ${t.join(" AND ")}
133
- ORDER BY c.created_at DESC, c.id DESC LIMIT ?`,params:i}]),d=r.slice(0,n),l=d.at(-1);return{calls:d.map(s=>{let o=u(s.app_row_id),c=u(s.app_status);return{id:String(s.id),createdAt:g(s.created_at),user:{id:String(s.user_id),name:u(s.user_name),email:u(s.user_email)},credential:{kind:String(s.credential_kind),clientName:u(s.client_name),clientInfo:u(s.client_info)},app:{rowId:o,slug:u(s.app_slug),name:u(s.app_name),removed:o!==null&&(c===null||c===O)},tool:{name:String(s.tool_name),risk:u(s.risk),via:u(s.via)},decision:s.decision==="allowed"?"allowed":"blocked",reason:u(s.reason),outcome:String(s.outcome),durationMs:s.duration_ms==null?null:Number(s.duration_ms),resultBytes:s.result_bytes==null?null:Number(s.result_bytes)}}),nextBefore:r.length>n&&l?{createdAt:g(l.created_at),id:String(l.id)}:null}}var Ha=/^(\*|[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*(\.\*)?)$/,Ga=80;async function Yn(e,a){await ze(e.db);let n=Math.min(Math.max(1,Math.trunc(a.limit??50)||1),ke),t=["e.organization_id = ?"],i=[e.organization.id];if(a.before&&(t.push("(e.time < ? OR (e.time = ? AND e.id < ?))"),i.push(a.before.time,a.before.time,a.before.id)),a.actorId!==void 0&&(t.push("e.actor_id = ?"),i.push(a.actorId)),a.type===void 0)t.push("e.type <> 'ping' AND e.type NOT LIKE 'tool.%'");else{let s=a.type.trim();if(s.length===0||s.length>Ga||!Ha.test(s))throw new m("INVALID_EVENT_TYPE",`--type is an event type (member.joined), a prefix (member.*, app.crm.*) or *${s?` (not ${s})`:""}`);let o=We(s);o?.op==="eq"?(t.push("e.type = ?"),i.push(o.value)):o&&(t.push("substr(e.type, 1, ?) = ?"),i.push(o.value.length,o.value))}a.since!==void 0&&(t.push("e.time >= ?"),i.push(a.since)),i.push(n+1);let[r=[]]=await e.db.run([{sql:`SELECT e.id, e.time, e.type, e.actor, e.actor_kind, e.actor_id, e.data, u.email AS user_email, u.name AS user_name
133
+ ORDER BY c.created_at DESC, c.id DESC LIMIT ?`,params:i}]),d=r.slice(0,n),l=d.at(-1);return{calls:d.map(s=>{let o=u(s.app_row_id),c=u(s.app_status);return{id:String(s.id),createdAt:g(s.created_at),user:{id:String(s.user_id),name:u(s.user_name),email:u(s.user_email)},credential:{kind:String(s.credential_kind),clientName:u(s.client_name),clientInfo:u(s.client_info)},app:{rowId:o,slug:u(s.app_slug),name:u(s.app_name),removed:o!==null&&(c===null||c===O)},tool:{name:String(s.tool_name),risk:u(s.risk),via:u(s.via)},decision:s.decision==="allowed"?"allowed":"blocked",reason:u(s.reason),outcome:String(s.outcome),durationMs:s.duration_ms==null?null:Number(s.duration_ms),resultBytes:s.result_bytes==null?null:Number(s.result_bytes)}}),nextBefore:r.length>n&&l?{createdAt:g(l.created_at),id:String(l.id)}:null}}var Qa=/^(\*|[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*(\.\*)?)$/,en=80;async function tt(e,a){await He(e.db);let n=Math.min(Math.max(1,Math.trunc(a.limit??50)||1),Ue),t=["e.organization_id = ?"],i=[e.organization.id];if(a.before&&(t.push("(e.time < ? OR (e.time = ? AND e.id < ?))"),i.push(a.before.time,a.before.time,a.before.id)),a.actorId!==void 0&&(t.push("e.actor_id = ?"),i.push(a.actorId)),a.type===void 0)t.push("e.type <> 'ping' AND e.type NOT LIKE 'tool.%'");else{let s=a.type.trim();if(s.length===0||s.length>en||!Qa.test(s))throw new m("INVALID_EVENT_TYPE",`--type is an event type (member.joined), a prefix (member.*, app.crm.*) or *${s?` (not ${s})`:""}`);let o=qe(s);o?.op==="eq"?(t.push("e.type = ?"),i.push(o.value)):o&&(t.push("substr(e.type, 1, ?) = ?"),i.push(o.value.length,o.value))}a.since!==void 0&&(t.push("e.time >= ?"),i.push(a.since)),i.push(n+1);let[r=[]]=await e.db.run([{sql:`SELECT e.id, e.time, e.type, e.actor, e.actor_kind, e.actor_id, e.data, u.email AS user_email, u.name AS user_name
134
134
  FROM events e
135
135
  LEFT JOIN users u ON e.actor_kind = 'user' AND u.id = e.actor_id
136
136
  WHERE ${t.join(" AND ")}
137
- ORDER BY e.time DESC, e.id DESC LIMIT ?`,params:i}]),d=r.slice(0,n),l=d.at(-1);return{events:d.map(s=>{let o=we(s.actor),c=u(s.actor_kind)??o.kind,h=u(s.actor_id)??o.id,S=c==="user"?{kind:c,id:h,email:u(s.user_email)??o.email,name:u(s.user_name)??o.name}:{kind:c,id:h,email:o.email,name:o.name},b={};try{b=JSON.parse(String(s.data))}catch{b={}}let f=String(s.type);return{id:String(s.id),time:g(s.time),type:f,label:Me(f),actor:S,subject:Le(f,b),appEvent:Ie(f)}}),nextBefore:r.length>n&&l?{time:g(l.time),id:String(l.id)}:null}}var Sa=`SELECT t.id, t.user_id, u.email, t.name, t.token_prefix, t.app_row_id, a.app_slug, a.name AS app_name,
137
+ ORDER BY e.time DESC, e.id DESC LIMIT ?`,params:i}]),d=r.slice(0,n),l=d.at(-1);return{events:d.map(s=>{let o=Pe(s.actor),c=u(s.actor_kind)??o.kind,S=u(s.actor_id)??o.id,b=c==="user"?{kind:c,id:S,email:u(s.user_email)??o.email,name:u(s.user_name)??o.name}:{kind:c,id:S,email:o.email,name:o.name},N={};try{N=JSON.parse(String(s.data))}catch{N={}}let f=String(s.type);return{id:String(s.id),time:g(s.time),type:f,label:xe(f),actor:b,subject:Fe(f,N),appEvent:$e(f)}}),nextBefore:r.length>n&&l?{time:g(l.time),id:String(l.id)}:null}}var wa=`SELECT t.id, t.user_id, u.email, t.name, t.token_prefix, t.app_row_id, a.app_slug, a.name AS app_name,
138
138
  t.scopes, t.created_at, t.expires_at, t.last_used_at, t.revoked_at
139
139
  FROM user_access_tokens t JOIN users u ON u.id = t.user_id
140
140
  LEFT JOIN apps a ON a.id = t.app_row_id AND a.organization_id = t.organization_id
141
- WHERE t.organization_id = ?1`;function ha(e,a){let n=e.revoked_at==null?null:Number(e.revoked_at),t=g(e.expires_at),i=[];try{let r=JSON.parse(String(e.scopes??"[]"));i=Array.isArray(r)?r.map(String):[]}catch{i=[]}return{id:String(e.id),user:{id:String(e.user_id),email:String(e.email)},name:String(e.name),prefix:String(e.token_prefix),app:{rowId:u(e.app_row_id),slug:u(e.app_slug),name:u(e.app_name)},scopes:i,createdAt:g(e.created_at),expiresAt:t,lastUsedAt:e.last_used_at==null?null:Number(e.last_used_at),revokedAt:n,active:n===null&&t>a}}async function Jn(e,a){let[n=[]]=await e.db.run([{sql:`${Sa}${a?" AND t.user_id = ?2":""} ORDER BY t.created_at DESC`,params:a?[e.organization.id,a]:[e.organization.id]}]);return n.map(t=>ha(t,e.now))}async function Vn(e,a){let[n=[]]=await e.db.run([{sql:`${Sa} AND t.id = ?2`,params:[e.organization.id,a.trim()]}]),t=n[0];if(!t)throw new m("TOKEN_NOT_FOUND",`No personal access token ${a.trim()} in ${e.organization.name}`,{rerun:`${_} workspace tokens`,rerunNote:"lists them by id"});let i=ha(t,e.now);return i.revokedAt!==null?{token:i,already:!0}:(await e.db.run([{sql:"UPDATE user_access_tokens SET revoked_at = ?3, updated_at = ?3 WHERE id = ?1 AND organization_id = ?2 AND revoked_at IS NULL",params:[i.id,e.organization.id,e.now]},...await A(e,{type:"token.revoked",data:{id:i.id,name:i.name,user:D({...i.user,name:null})},onlyIf:{sql:"changes() > 0",params:[]}})]),{token:{...i,revokedAt:e.now,active:!1},already:!1})}var Ra=`SELECT c.id, c.user_id, u.email, c.client_name, c.client_domain, c.redirect_host, c.app_scope, c.access,
141
+ WHERE t.organization_id = ?1`;function Ma(e,a){let n=e.revoked_at==null?null:Number(e.revoked_at),t=g(e.expires_at),i=[];try{let r=JSON.parse(String(e.scopes??"[]"));i=Array.isArray(r)?r.map(String):[]}catch{i=[]}return{id:String(e.id),user:{id:String(e.user_id),email:String(e.email)},name:String(e.name),prefix:String(e.token_prefix),app:{rowId:u(e.app_row_id),slug:u(e.app_slug),name:u(e.app_name)},scopes:i,createdAt:g(e.created_at),expiresAt:t,lastUsedAt:e.last_used_at==null?null:Number(e.last_used_at),revokedAt:n,active:n===null&&t>a}}async function it(e,a){let[n=[]]=await e.db.run([{sql:`${wa}${a?" AND t.user_id = ?2":""} ORDER BY t.created_at DESC`,params:a?[e.organization.id,a]:[e.organization.id]}]);return n.map(t=>Ma(t,e.now))}async function rt(e,a){let[n=[]]=await e.db.run([{sql:`${wa} AND t.id = ?2`,params:[e.organization.id,a.trim()]}]),t=n[0];if(!t)throw new m("TOKEN_NOT_FOUND",`No personal access token ${a.trim()} in ${e.organization.name}`,{rerun:`${_} workspace tokens`,rerunNote:"lists them by id"});let i=Ma(t,e.now);return i.revokedAt!==null?{token:i,already:!0}:(await e.db.run([{sql:"UPDATE user_access_tokens SET revoked_at = ?3, updated_at = ?3 WHERE id = ?1 AND organization_id = ?2 AND revoked_at IS NULL",params:[i.id,e.organization.id,e.now]},...await A(e,{type:"token.revoked",data:{id:i.id,name:i.name,user:D({...i.user,name:null})},onlyIf:{sql:"changes() > 0",params:[]}})]),{token:{...i,revokedAt:e.now,active:!1},already:!1})}var La=`SELECT c.id, c.user_id, u.email, c.client_name, c.client_domain, c.redirect_host, c.app_scope, c.access,
142
142
  c.created_at, c.last_used_at, c.revoked_at, c.revoked_reason,
143
143
  (SELECT group_concat(a.app_slug, char(31)) FROM mcp_connection_apps ca JOIN apps a ON a.id = ca.app_row_id WHERE ca.connection_id = c.id) AS app_slugs
144
144
  FROM mcp_connections c JOIN users u ON u.id = c.user_id
145
- WHERE c.organization_id = ?1`;function Ta(e){return{id:String(e.id),user:{id:String(e.user_id),email:String(e.email)},clientName:String(e.client_name),clientDomain:u(e.client_domain),redirectHost:String(e.redirect_host),apps:e.app_scope==="all"?"all":u(e.app_slugs)?.split("").sort()??[],access:e.access==="write"?"write":"read",createdAt:g(e.created_at),lastUsedAt:e.last_used_at==null?null:Number(e.last_used_at),revokedAt:e.revoked_at==null?null:Number(e.revoked_at),revokedReason:u(e.revoked_reason)}}async function Xn(e,a){let[n=[]]=await e.db.run([{sql:`${Ra} AND c.revoked_at IS NULL${a?" AND c.user_id = ?2":""} ORDER BY c.created_at DESC`,params:a?[e.organization.id,a]:[e.organization.id]}]);return n.map(Ta)}async function jn(e,a){let[n=[]]=await e.db.run([{sql:`${Ra} AND c.id = ?2`,params:[e.organization.id,a.trim()]}]),t=n[0];if(!t)throw new m("CONNECTION_NOT_FOUND",`No AI connection ${a.trim()} in ${e.organization.name}`,{rerun:`${_} workspace connections`,rerunNote:"lists them by id"});let i=Ta(t);return i.revokedAt!==null?{connection:i,already:!0}:(await e.db.run([{sql:`UPDATE mcp_connections SET revoked_at = ?3, revoked_by = ?4, revoked_reason = 'user'
146
- WHERE id = ?1 AND organization_id = ?2 AND revoked_at IS NULL`,params:[i.id,e.organization.id,e.now,e.operator.userId]},...await A(e,{type:"connection.revoked",data:{connection:{id:i.id,clientName:i.clientName},user:D({...i.user,name:null}),reason:"operator"},onlyIf:{sql:"changes() > 0",params:[]}})]),{connection:{...i,revokedAt:e.now,revokedReason:"user"},already:!1})}export{Pe as a,xe as b,en as c,an as d,nn as e,on as f,bn as g,na as h,Nn as i,Sn as j,G as k,ie as l,sa as m,hn as n,Rn as o,Tn as p,Cn as q,On as r,Dn as s,yn as t,vn as u,In as v,kn as w,wn as x,Mn as y,Ln as z,Wn as A,zn as B,Ke as C,$n as D,Ze as E,Un as F,Pn as G,Pa as H,xn as I,Qe as J,Fn as K,qn as L,Hn as M,xa as N,Na as O,Gn as P,qa as Q,Bn as R,Ha as S,Ga as T,Yn as U,Jn as V,Vn as W,Xn as X,jn as Y};
145
+ WHERE c.organization_id = ?1`;function Wa(e){return{id:String(e.id),user:{id:String(e.user_id),email:String(e.email)},clientName:String(e.client_name),clientDomain:u(e.client_domain),redirectHost:String(e.redirect_host),apps:e.app_scope==="all"?"all":u(e.app_slugs)?.split("").sort()??[],access:e.access==="write"?"write":"read",createdAt:g(e.created_at),lastUsedAt:e.last_used_at==null?null:Number(e.last_used_at),revokedAt:e.revoked_at==null?null:Number(e.revoked_at),revokedReason:u(e.revoked_reason)}}async function ot(e,a){let[n=[]]=await e.db.run([{sql:`${La} AND c.revoked_at IS NULL${a?" AND c.user_id = ?2":""} ORDER BY c.created_at DESC`,params:a?[e.organization.id,a]:[e.organization.id]}]);return n.map(Wa)}async function st(e,a){let[n=[]]=await e.db.run([{sql:`${La} AND c.id = ?2`,params:[e.organization.id,a.trim()]}]),t=n[0];if(!t)throw new m("CONNECTION_NOT_FOUND",`No AI connection ${a.trim()} in ${e.organization.name}`,{rerun:`${_} workspace connections`,rerunNote:"lists them by id"});let i=Wa(t);return i.revokedAt!==null?{connection:i,already:!0}:(await e.db.run([{sql:`UPDATE mcp_connections SET revoked_at = ?3, revoked_by = ?4, revoked_reason = 'user'
146
+ WHERE id = ?1 AND organization_id = ?2 AND revoked_at IS NULL`,params:[i.id,e.organization.id,e.now,e.operator.userId]},...await A(e,{type:"connection.revoked",data:{connection:{id:i.id,clientName:i.clientName},user:D({...i.user,name:null}),reason:"operator"},onlyIf:{sql:"changes() > 0",params:[]}})]),{connection:{...i,revokedAt:e.now,revokedReason:"user"},already:!1})}export{Ye as a,Je as b,pn as c,un as d,mn as e,gn as f,fn as g,In as h,ma as i,kn as j,wn as k,G as l,ie as m,Aa as n,Mn as o,Ln as p,Wn as q,zn as r,$n as s,Un as t,Pn as u,xn as v,Fn as w,qn as x,Hn as y,Gn as z,Bn as A,Yn as B,Jn as C,da as D,Vn as E,la as F,Xn as G,jn as H,Xa as I,Kn as J,ca as K,Zn as L,Qn as M,et as N,ja as O,ka as P,at as Q,Za as R,nt as S,Qa as T,en as U,tt as V,it as W,rt as X,ot as Y,st as Z};
@@ -0,0 +1 @@
1
+ var o=["owner","admin","member"];var n=/^[a-z]([a-z0-9-]*[a-z0-9])?$/,s={s:1,m:60,h:3600};function c(e){let r=/^(\d+)([smh]?)$/.exec(e.trim());if(!r)throw new Error("expected seconds, or a number with s, m or h (90s, 15m, 1h)");let t=Number(r[1])*s[r[2]||"s"];if(t<1||t>86400)throw new Error("expected between 1s and 24h");return t}function i(e){let r=e.trim().toLowerCase();if(!/^[^\s@]+@[^\s@]+$/.test(r))throw new Error("expected an email address");return r}function a(e){if(!n.test(e))throw new Error("expected an app id (kebab-case, like everyapp.config.ts's id)");return e}export{o as a,c as b,i as c,a as d};
@@ -0,0 +1,40 @@
1
+ import{j as De,k as Fe}from"./chunk-OPSXAHNF.js";import{c as je}from"./chunk-7IO5QSK4.js";import{B as xe,u as Te,w as _e}from"./chunk-SAOL43T3.js";import{a as _,c as X,d as Ne,h as B,i as Ge,j as z,k as Ue}from"./chunk-TC2TNQFK.js";import{a as U}from"./chunk-LWMA2GWN.js";import{d as F,f as L}from"./chunk-Y32TS22S.js";import{$ as Ee,na as ue}from"./chunk-AGGUD3LY.js";import{a as T}from"./chunk-MDNG2D3M.js";import{$a as x,A as I,B as A,G as Ae,I as J,Jc as Re,K as D,Ka as Oe,L as Ce,M as ce,Rb as G,Wc as M,a as h,ab as Ie,bb as Pe,bd as S,cb as de,i as w,ka as Se,lc as g,pc as pe}from"./chunk-PRGCYHQF.js";import Y from"node:fs/promises";import ae from"node:path";import{stripVTControlCharacters as Ze}from"node:util";import b from"chalk";var Le={"google-workspace":"google","microsoft-365":"microsoft","turso-db":"turso"};var Z="[OpenSEO](/docs/apps/openseo)",V="[OpenCRM](/docs/apps/crm)",he="[OpenMail](/docs/apps/openmail)",rt="[OpenAnalytics](/docs/apps/analytics)",it="[RepoLens](/docs/apps/repolens)",Gt={openrouter:{blurb:"One key for hundreds of models from every major lab. It's the key to set first: OpenSEO, OpenCRM and OpenMail all run on it.",usedBy:[`${Z} for its SEO agent`,`${V} for its research agent and web research`,`${he} for its email agent`],key:["Sign in or sign up at [openrouter.ai](https://openrouter.ai/sign-in), with Google, GitHub or an email address.","Add credits at [Settings \u2192 Credits](https://openrouter.ai/settings/credits): $10 is a good start, $5 the minimum. Without credits every call fails.","Open [Settings \u2192 Keys](https://openrouter.ai/settings/keys) and select **Create Key**. Name it after your Gateway, and give it a credit limit you're happy to lose, reset monthly.","Copy the key. OpenRouter shows it once, so keep the tab open until it's set."]},openai:{key:["Sign in or sign up at [platform.openai.com](https://platform.openai.com).","Add a card and prepaid credits under [Settings \u2192 Billing](https://platform.openai.com/settings/organization/billing/overview) ($5 minimum). Turn auto-reload off unless you want it.","Open [API keys](https://platform.openai.com/api-keys) and select **Create new secret key**. Owned by **You**, any project, permissions **All**. Your first key asks for a phone number.","Copy the key. It's shown once."]},anthropic:{key:["Sign in to the [Claude Console](https://platform.claude.com).","Add a card and buy credits under **Settings \u2192 Billing**.","Open [Settings \u2192 API keys](https://platform.claude.com/settings/keys) and create a key in the workspace you want the Gateway's calls billed to.","Copy the key. It's shown once."]},gemini:{name:"Google Gemini",key:["Sign in to [Google AI Studio](https://aistudio.google.com) with a Google account.","Open [Get API key](https://aistudio.google.com/apikey) and select **Create API key**, in a new or an existing Google Cloud project.","Restrict the key to the Gemini API (the key's settings in the Cloud console, **API restrictions**).","Copy the key. Free tier keys have low limits; turn on billing in the project for real use."]},mistral:{key:["Sign in to the [Mistral console](https://console.mistral.ai).","Pick a plan under **Billing**: the free plan needs no card but has low limits.","Open **API Keys**, select **Create new key**, and give it an expiry date you'll remember.","Copy the key. It's shown once."]},groq:{key:["Sign in to the [Groq console](https://console.groq.com).","Open [API Keys](https://console.groq.com/keys) and select **Create API Key**.","Copy the key. It's shown once."]},cohere:{key:["Sign in to the [Cohere dashboard](https://dashboard.cohere.com).","Open [API keys](https://dashboard.cohere.com/api-keys). A trial key is free but limited to 1,000 calls a month; create a production key for real use (it needs a card).","Copy the key."]},"workers-ai":{blurb:"Cloudflare's own models, run in the same Cloudflare account as your Gateway. There's no separate sign-up and nothing to prepay: usage is billed to that account.",usedBy:[`${he} can use it as its model provider`],key:["In the Cloudflare dashboard, open the [Workers AI page](https://dash.cloudflare.com/?to=/:account/ai/workers-ai) of your Gateway's account.","Select **Use REST API**, then **Create a Workers AI API Token**. The form comes prefilled with Workers AI permissions on this account; check the account and select **Create API Token**.","Copy the token. It's shown once.","`CLOUDFLARE_ACCOUNT_ID` is your Gateway's own account, which `everyapp setup` already set. You only set the token."]},"ai-gateway":{name:"Cloudflare AI Gateway",blurb:"Not a model provider of its own: once it's on, model calls to providers it supports pass through Cloudflare AI Gateway, in your account, for its logs, caching and analytics. Your keys stay on the Gateway.",key:["Run `pnpm everyapp ai gateway on`. It finds or creates an AI Gateway named `everyapp` in your account and turns it on. Nothing to copy.","Optional: to require a token, open the AI Gateway in the dashboard, **Settings \u2192 Authenticated Gateway**, turn it on and create a token. Then set it with `pnpm everyapp secret set AI_GATEWAY_API_KEY`."]},dataforseo:{blurb:"Search data for SEO: keyword volumes, live Google results, domain rankings and backlinks. You pay per call, from a prepaid balance.",usedBy:[`${Z} for all of its research (required)`],key:["Sign up at [app.dataforseo.com](https://app.dataforseo.com/register) and confirm your email. New accounts get $1 of credit.","Open [API Access](https://app.dataforseo.com/api-access). It shows your **API login** and **API password** (after 24 hours, select **Send by e-mail** to get the password again). It's not your dashboard password.","Leave the **IP Access** list empty: the Gateway calls from Cloudflare's addresses, which change.","Optional, before real use: **Add Funds** ($50 minimum) and a daily limit under [API Settings](https://app.dataforseo.com/api-settings).","The value to set is the login and password joined by a colon: `login:password`."]},context:{name:"Context.dev",blurb:"Renders and reads web pages, JavaScript included: brand data, LinkedIn profiles and clean page text. Free up to 1,000 rendered pages a month.",usedBy:[`${V} for company research (required)`,`${Z} for rendered site audits (optional)`],key:["Sign up or sign in at [context.dev](https://www.context.dev/signup), with an email, Google or GitHub.","Open [API keys](https://context.dev/dashboard/api-keys) and create a key. **Full access** is fine.","Copy the key. It may be shown once."]},stripe:{key:["Sign in to the [Stripe dashboard](https://dashboard.stripe.com). To try things first, switch to a sandbox (test mode).","Open [Developers \u2192 API keys](https://dashboard.stripe.com/apikeys) and select **Create restricted key**.","Give it a name, and set **Read** only on what the app needs (for example Customers, Subscriptions and Charges). Leave everything else at **None**.","Select **Create key** and copy it (`rk_live_\u2026`, or `rk_test_\u2026` in a sandbox)."]},github:{blurb:"Repositories, issues, pull requests and releases. Use a token when an app works on your own repositories; let people connect their own accounts when it works on theirs.",usedBy:[`${it} reads traffic, stars and downloads with a token`],key:["Open [Settings \u2192 Developer settings \u2192 Fine-grained tokens \u2192 Generate new token](https://github.com/settings/personal-access-tokens/new).","Name it after your Gateway and pick an expiry. Under **Resource owner**, pick the account or organization whose repositories the app reads.","Under **Repository access**, choose only the repositories the app needs.","Under **Permissions**, give the least the app needs (for example **Contents: Read** and **Metadata: Read**).","Select **Generate token** and copy it. It's shown once."],connect:["Open [Settings \u2192 Developer settings \u2192 OAuth Apps](https://github.com/settings/developers) and select **New OAuth App**. Make an OAuth App, not a GitHub App.","Fill in a name and your Gateway's address as the homepage. For **Authorization callback URL**, paste the redirect URI above.","Select **Register application**, then **Generate a new client secret**.","Copy the **Client ID** and the client secret. The secret is shown once."]},linear:{key:["In Linear, open [Settings \u2192 Account \u2192 Security & access](https://linear.app/settings/account/security).","Under **Personal API keys**, select **New API key**. Limit it to the permissions and teams the app needs.","Copy the key. It's shown once."],connect:["Open [Settings \u2192 API \u2192 OAuth applications](https://linear.app/settings/api/applications) in your Linear workspace and create a new application.","Add the redirect URI above as a callback URL.","Save, then copy the **Client ID** and **Client secret**."]},posthog:{key:["Sign in to PostHog in the region your project lives in (US or EU).","Open [Settings \u2192 Personal API keys](https://us.posthog.com/settings/user-api-keys) and select **Create personal API key**.","Pick the organization or project it can reach, and only the scopes the app needs (for example **Insight: Read**).","Copy the key (`phx_\u2026`). It's shown once. It's not the project token that starts with `phc_`."]},postmark:{key:["Sign in to [Postmark](https://account.postmarkapp.com) and open the server the app sends from.","Open the server's **API Tokens** tab.","Copy a **Server API token**. Never the Account API token."]},sendgrid:{key:["Sign in to [SendGrid](https://app.sendgrid.com) and open [Settings \u2192 API Keys](https://app.sendgrid.com/settings/api_keys).","Select **Create API Key**, choose **Restricted Access**, and give it **Mail Send** only.","Copy the key. It's shown once."]},cloudflare:{blurb:"Cloudflare's API for an app that manages zones, DNS, Workers or storage. Your Gateway already runs on Cloudflare; this is a separate token an app uses on purpose.",key:["Open [My Profile \u2192 API Tokens](https://dash.cloudflare.com/profile/api-tokens) and select **Create Token**.","Start from a template, or a custom token, with only the permissions and zones the app needs. Never use the Global API Key.","A token on your Gateway's own account can change the Gateway. Keep it narrow, or use another account.","Select **Continue to summary**, **Create Token**, and copy it. It's shown once."]},google:{name:"Google",blurb:"One OAuth client in your own Google Cloud project lets each person connect their own Google account: Gmail, Calendar, Search Console and Analytics, read only, whichever the apps you run ask for. The same page covers Sign in with Google, which is a separate client.",usedBy:[`${Z} for Search Console and Google Analytics (the \`identity\`, \`search-console.read\` and \`analytics.read\` sets)`,`${V} for Gmail and Google Calendar (the \`gmail.read\` and \`calendar.read\` sets)`],connect:["Signed in to Google, [create a Cloud project](https://console.cloud.google.com/projectcreate) (for example `EveryApp`). With Google Workspace, put it in your organization.","Enable the APIs behind the sets your apps use (the table below): for OpenSEO, the [Search Console API](https://console.cloud.google.com/apis/library/searchconsole.googleapis.com), the [Analytics Admin API](https://console.cloud.google.com/apis/library/analyticsadmin.googleapis.com) and the [Analytics Data API](https://console.cloud.google.com/apis/library/analyticsdata.googleapis.com); for OpenCRM, the Gmail API and the Google Calendar API.","Set up the [consent screen](https://console.cloud.google.com/auth/overview): an app name people will see, a support email, and the audience: **Internal** (your Workspace only, no review) or **External**.","On [Data Access](https://console.cloud.google.com/auth/scopes), add the scopes of those sets and no others: `webmasters.readonly` and `analytics.readonly` for OpenSEO, `gmail.readonly` and `calendar.readonly` for OpenCRM.","[Create a client](https://console.cloud.google.com/auth/clients): type **Web application**, and under **Authorized redirect URIs** add the redirect URI above. Copy the **Client ID** and **Client secret**, or select **Download JSON**.","**External** only: while the app is in Testing, add each person under **Audience \u2192 Test users**. Their access lasts 7 days at a time until you publish the app and Google verifies it."],extra:[{title:"Search Console",body:["For an app that lists the `search-console.read` set, OpenSEO among them:","","1. Enable the [Google Search Console API](https://console.cloud.google.com/apis/library/searchconsole.googleapis.com) in the client's project.","2. Add `https://www.googleapis.com/auth/webmasters.readonly` on the consent screen's [Data Access](https://console.cloud.google.com/auth/scopes) page.","3. Each person connects their own Google account, one that can see the site in Search Console."].join(`
2
+ `)},{title:"Google Analytics",body:["For an app that lists the `analytics.read` set, OpenSEO among them:","","1. Enable the [Google Analytics Admin API](https://console.cloud.google.com/apis/library/analyticsadmin.googleapis.com) and the [Google Analytics Data API](https://console.cloud.google.com/apis/library/analyticsdata.googleapis.com) in the client's project.","2. Add `https://www.googleapis.com/auth/analytics.readonly` on the consent screen's [Data Access](https://console.cloud.google.com/auth/scopes) page.","3. Each person connects a Google account with access to the GA4 property."].join(`
3
+ `)}]},microsoft:{name:"Microsoft",blurb:"One app registration in Microsoft Entra lets each person connect their own Outlook mailbox and calendar, read only, from a work, school or personal account. The same page covers Sign in with Microsoft, which is a separate registration.",usedBy:[`${V} for Outlook mail (the \`mail.read\` set)`],connect:["Sign in to [Microsoft Entra \u2192 App registrations](https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) with an account that administers your tenant, and select **New registration**.","Name it (people see the name when they consent). For **Supported account types**, choose any Entra ID tenant plus personal Microsoft accounts, never single tenant.","Under **Redirect URI**, platform **Web**, paste the redirect URI above, and select **Register**. Copy the **Application (client) ID**.","Under **API permissions \u2192 Add a permission \u2192 Microsoft Graph \u2192 Delegated**, add `User.Read`, `Mail.Read` and `offline_access` (and `Calendars.Read` if an app asks for `calendar.read`).","Under **Certificates & secrets \u2192 New client secret**, add one (24 months at most) and copy its **Value** right away. Note the expiry date."]},slack:{blurb:"A Slack app in your workspace whose client the Gateway holds, so apps can post to approved channels and match people by email.",usedBy:[`${V} for team agents that post to Slack (optional)`],connect:["Open [api.slack.com/apps](https://api.slack.com/apps) and select **Create New App**. **From a manifest** is quickest: your coding agent can write one from the app's scopes.","Pick the workspace. Under **OAuth & Permissions**, add the redirect URI above as a redirect URL, and the bot scopes the apps you run need.","Under **Basic Information \u2192 App Credentials**, copy the **Client ID** and **Client Secret**."]},email:{name:"Email sending",blurb:"The Gateway sends its own mail (invitations, password resets) and every app's, through one provider for the whole Gateway: Cloudflare Email Sending (the default) or Resend. It's optional: without it, an invitation shows a link to share instead.",usedBy:[`${he} to send replies (required)`,`${rt} for reports and alerts (optional)`],extra:[{title:"Cloudflare Email Sending",body:["The default. Your domain must be on Cloudflare DNS in the Gateway's account, on Workers Paid. There's no key: the Gateway sends through its own `EMAIL` binding.","","1. In the dashboard, open [Email Service \u2192 Email Sending](https://dash.cloudflare.com/?to=/:account/email-service/sending) and select **Onboard Domain**.","2. Pick your domain (or a subdomain like `mail.example.com`), review the DNS records it adds, and confirm.","3. Wait until the domain is listed under Email Sending, usually 5 to 15 minutes.","4. Set the sender, below."].join(`
4
+ `)},{title:"Resend",body:["When you already use [Resend](https://resend.com) or want its dashboard.","","1. Open [Domains](https://resend.com/domains), select **Add Domain**, and add the domain you'll send from. **Sign in to Cloudflare** adds the DNS records for you.","2. Wait for the domain to show **Verified**.","3. Open [API Keys](https://resend.com/api-keys), select **Create API Key** with **Sending access**, and copy it (`re_\u2026`). It's shown once.","4. Set it, then the sender, below:","","```bash","pnpm everyapp secret set RESEND_API_KEY","```"].join(`
5
+ `)},{title:"Set the sender",body:["Either provider needs the address mail comes from and the name people see. Neither is a secret.","","```bash","pnpm everyapp secret set EMAIL_FROM","pnpm everyapp secret set EMAIL_FROM_NAME","```","","For example `noreply@example.com` and your company's name. Or run `pnpm everyapp configure openmail`, which asks for both. When both providers are set up, `EMAIL_PROVIDER` (`cloudflare` or `resend`) picks one.","","An app that sends as addresses of its own, like OpenMail, also needs them allowed: `pnpm everyapp email set --sender '*@example.com'`."].join(`
6
+ `)},{title:"Check it worked",body:["`pnpm everyapp email show` prints the provider and the sender. Then invite someone from **Manage workspace \u2192 People**: the invitation should arrive by email.","",'Apps ask for it in `everyapp.config.ts` with `capabilities: { email: { access: "write", why: "\u2026" } }` and send with `sendEmail()`. More in [Email](/docs/agent/email).',"","**Postmark and SendGrid** are separate: an app that calls their APIs itself uses [Postmark](/docs/providers/postmark) or [SendGrid](/docs/providers/sendgrid)."].join(`
7
+ `)}]}};var lt="https://everyapp.com";function ct(e){return Le[e]??e.replace(/-oauth$/,"")}function Q(e){let t=Object.hasOwn(I,e)||Object.hasOwn(x,e);return`${lt}/docs/providers/${t?ct(e):"custom"}`}import{stripVTControlCharacters as ze}from"node:util";import O from"chalk";import dt from"node:fs";import{text as pt}from"node:stream/consumers";function W(e){return e.replace(/\s*\([^()]*\)$/,"")||e}async function ut(){if(process.stdin.isTTY)return null;let e;try{e=dt.fstatSync(0)}catch{return null}if(!e.isFIFO()&&!e.isFile())return null;let t=await pt(process.stdin);return t.trim()?t:null}async function te(e,t,n,s,i){let o={},r=n["from-env"]?null:await(s.readStdin??ut)();if(r!==null)for(let{name:l,value:u,line:d}of je(r)){if(!e.includes(l)){let c=/^[A-Z][A-Z0-9_]*$/.test(l)?l:`line ${d} (not shown: it may be a value)`;throw new g("SECRET_NOT_DECLARED",`${t} isn't ${c}
8
+ The piped lines set ${w([...e])}. Nothing was set.`,{fix:`pipe ${e.join(" and ")} as NAME=value lines`})}if(!u.trim())throw new g("SECRET_EMPTY",`${l} is empty in the piped input
9
+ Nothing was set.`,{fix:"give it a value, or leave the line out"});o[l]=u}if(n["from-env"]){let l=s.env??process.env;for(let u of e){let d=l[u];d?.trim()&&!(u in o)&&(o[u]=d)}}let a=n["client-id"]?.trim();a&&i&&(o[i]=a);for(let l of Object.keys(o))Se(l)&&(o[l]=Te(l,o[l]));return o}var ee=class extends Error{};async function ne(e){let{given:t,needed:n,settings:s}=e,i=(r,a)=>xe({flags:{...e.flags},out:e.out,names:r,readValue:a,...e.createCf?{createCf:e.createCf}:{}});if(Object.keys(t).length>0){let r=n.filter(a=>!(a in t));if(r.length>0)throw new g("PROVIDER_CLIENT_INCOMPLETE",`${e.whose} needs ${w(r)} too
10
+ Nothing was set; ${e.standing}.`,{fix:`pipe ${r.join(" and ")} as NAME=value lines, export ${r.length===1?"it":"them"} and add --from-env, or run ${h} secret set ${n.join(" ")}`});return await i(s.filter(a=>a in t),async a=>t[a]),"set"}if(n.length===0||!e.interactive)return"skipped";let o=e.ask??((r,a)=>_e(r,{question:a,optional:!0}));try{return await i([...n],async r=>{let a=(await o(r,e.questionOf?.(r)??r)).trim();if(!a)throw new ee;return a}),"set"}catch(r){if(r instanceof ee)return"skipped";throw r}}function Ve(e,t){return`${e.replace(/\/$/,"")}/connect/${t}/callback`}function ht(e){return e.flow?"both":"app"}function me(e){return Object.entries(e.scopePolicy?.sets??{}).map(([t,n])=>({name:t,label:n.label,access:n.access,scopes:[...n.scopes]}))}function We(e,t,n,s){return t.includes(e.secrets.clientId)?"env":n.has(s)?"uploaded":"none"}function oe(e,t,n){let s=We(t,n.gatewayNames,n.uploaded,e);return{flow:ht(t),redirectUri:Ve(n.gatewayUrl,e),console:t.console,scopeSets:me(t),client:{source:s,...s==="uploaded"?{clientId:n.uploaded.get(e).clientId}:{}},connections:n.connections.get(e)??0}}function mt(){return Object.entries(I).filter(([,e])=>de(e)).map(([e])=>e)}function Me(e,t,n){return{id:e,name:t.label,builtIn:n,console:t.console,hosts:[...t.api],settings:[...A(t)],scopeSets:me(t),...t.hint?{hint:t.hint}:{}}}function Ke(){return[...mt().map(e=>Me(e,I[e],!0)),...Ie().map(e=>Me(e,Pe(e),!1))]}async function Ye(e){let t=Ke(),n=M(["ID","NAME","KIND","SCOPE SETS","CONSOLE"],t.map(s=>[s.id,s.name,s.builtIn?"built in":"preset",s.scopeSets.map(i=>i.name).join(", ")||O.dim("-"),s.console]));e.out.emit({type:"done",message:`${t.length} OAuth presets: ${t.filter(s=>s.builtIn).length} built in, ${t.filter(s=>!s.builtIn).length} to register`,display:[...n,"",O.dim(`Add one: ${h} provider add --oauth <id>; a built-in one takes its client only, a preset becomes a registered provider.`)].join(`
11
+ `),data:{presets:t,next:[{command:`${h} provider add --oauth github-oauth`,note:"registers GitHub and prints the redirect URI to register at its console"}]}})}async function gt(e,t){let n=x[e];if(n)return{kind:"preset",id:e,spec:n};let s=I[e];if(s&&de(s))return{kind:"catalog",id:e,capability:s};if(s)throw new g("PROVIDER_PRESET_UNKNOWN",`${e} is a built-in ${s.mode==="oauth_client"?"OAuth client the Gateway completes token calls for; apps run its flow themselves":"provider, not an OAuth one"}`,{rerun:`${h} provider presets`,rerunNote:"names every preset and built-in client"});if(/[./\\]/.test(e)){let i=await ye(e,{cwd:t.cwd??process.cwd(),fetcher:t.fetch}),o=J(i);if(o.ok&&o.capability.mode!=="oauth_client")throw new g("PROVIDER_SPEC_INVALID",`${e} is ${o.capability.mode==="ai"?"an ai spec":`an API-key spec (auth.kind ${o.capability.auth.kind})`}
12
+ --oauth takes a spec with auth.kind "oauth2-user". Nothing was registered.`,{fix:`register it with ${h} provider add ${e}`});return{kind:"spec",spec:i}}throw new g("PROVIDER_PRESET_UNKNOWN",`No OAuth preset ${e}
13
+ Presets: ${w(Ke().map(i=>i.id))}. A custom provider is a JSON file: ${h} provider add --oauth ./acme.json.`,{rerun:`${h} provider presets`,rerunNote:"lists them with their scope sets"})}async function se(e,t){let{out:n,flags:s}=e,i=await gt(t,e),o=await L({gateway:F(s.gateway),accountId:s["account-id"],interactive:T(s.json??!1),login:{json:s.json,out:n,step:"login"},createCf:e.createCf}),r=o.gatewayUrl.replace(/^https:\/\//,""),a,l,u=!1,d;if(i.kind==="catalog"){a=i.id;let y=(await _(o.db,o.install)).get(a);l=y?.mode==="oauth_client"?y:i.capability,d="catalog",n.emit({type:"step",step:"provider.add",status:"ok",message:`${l.label} (${a}) is built in: no row to write, its client alone is set`,data:{id:a,builtIn:!0}})}else{let y=i.kind==="preset"?i.id:"the provider";n.emit({type:"step",step:"provider.add",status:"start",message:`Registering ${y}...`});let j=await X(o.db,{spec:i.spec,deployer:o.deployer,replace:s.replace??!1,install:o.install,now:e.now?.()});if(j.capability.mode!=="oauth_client")throw new Error(`${j.id} isn't an OAuth spec after all`);a=j.id,l=j.capability,u=j.replaced,d="row",n.emit({type:"step",step:"provider.add",status:"ok",message:`${u?"Replaced":"Registered"} ${l.label} (${a}) on ${r}`,data:{id:a,replaced:u}})}let c=[...A(l)],p=Ve(o.gatewayUrl,a);n.emit({type:"step",step:"provider.redirect",status:"ok",message:`Register this redirect URI at ${l.console}: ${p}`,data:{id:a,redirectUri:p,console:l.console}});let[m,k,R]=await Promise.all([U(o.cf,G),B(o.db),z(o.db)]),$=We(l,m,k,a)!=="none"?[]:d==="row"?c:c.filter(y=>!m.includes(y)),f=W(l.label),N=await te(c,`${f}'s client`,s,e,l.secrets.clientId),H=e.interactive??T(s.json??!1),v=Q(a),C=d==="catalog"?`${f} (${a}) is on ${r}.`:`${u?"Replaced":"Registered"} ${f} (${a}) on ${r}.`,fe=`Register this redirect URI at ${l.console}:
14
+ ${p}`,we=$.length===0?[C]:Object.keys(N).length>0?[C,fe,`Docs: ${v}`]:[C,`1. ${fe}`,H?"2. Paste the OAuth client it gives you.":"2. Set the client it gives you with the command below.",`Docs: ${v}`];n.emit({type:"step",step:"provider.setup",status:"ok",message:we.join(`
15
+ `),data:{id:a,docs:v}});let ve=await ne({flags:s,out:n,...e.createCf?{createCf:e.createCf}:{},whose:`${f}'s client`,standing:`the provider ${d==="row"?"is registered":"is built in"}`,settings:c,needed:$,given:N,interactive:H,...e.ask?{ask:e.ask}:{},questionOf:y=>y===l.secrets.clientId?"Client id":y===l.secrets.clientSecret?"Client secret":y})==="set",be=oe(a,l,{source:d,gatewayUrl:o.gatewayUrl,gatewayNames:ve?[...m,...c]:m,uploaded:k,connections:R}),ie=be.client.source,ke=`${o.gatewayUrl}/connect/${a}`,$e=ve?[`Apps can now list ${a}. Live now, no deploy needed.`]:ie==="env"?[`Its client is set: people connect at ${ke}. Apps can list ${a}.`]:ie==="uploaded"?[`Its client was uploaded under Admin \u2192 Providers: people connect at ${ke}. Apps can list ${a}.`]:[],le=me(l),st=[...le.length>0?[`Scope sets: ${le.map(y=>`${y.name}: ${y.label}`).join("; ")}.`]:[],`In an app's capabilities: { ${JSON.stringify(a)}: { access: "read", scopes: [${le.slice(0,2).map(y=>JSON.stringify(y.name)).join(", ")}], why: "..." } }`],at=[...$e,...s.verbose?st.map(y=>O.dim(y)):[]];n.emit({type:"done",message:ze([...we,...$e].join(`
16
+ `)),display:at.join(`
17
+ `),data:{id:a,name:l.label,kind:"oauth",source:d,replaced:u,settings:c,docs:v,...be,...ie==="none"?{next:[{command:`${h} secret set ${($.length>0?$:c).join(" ")}`}]}:{}}})}function qe(e){return L({gateway:F(e.flags.gateway),accountId:e.flags["account-id"],interactive:T(e.flags.json??!1),login:{json:e.flags.json,out:e.out,step:"login"},createCf:e.createCf})}function Be(e){return e===null?O.dim("-"):new Date(e).toISOString().slice(0,16).replace("T"," ")}function He(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 Je(e){let{out:t,flags:n}=e,s=await qe(e),[i,o]=await Promise.all([_(s.db,s.install),n.all?Promise.resolve(null):Ee(s.db)]),r=await Ge(s.db,{...o?{organizationId:o.organization.id}:{},...n.user?{userEmail:n.user}:{},...n.provider?{providerId:n.provider}:{}}),a=d=>i.get(d)?.label??d,l=M(["PROVIDER","ACCOUNT","LABEL","SCOPE SETS","STATUS","LAST USED","CONNECTED"],r.map(d=>[`${a(d.providerId)} (${d.providerId})`,d.account==="organization"?"the organization":d.userEmail??d.userId??"?",d.label??O.dim("-"),d.scopeSets.join(", ")||O.dim("-"),d.status==="active"?O.green("active"):O.yellow(d.status),Be(d.lastUsedAt),Be(d.createdAt)])),u=n.all?"every organization":o?o.organization.name:"this Gateway";t.emit({type:"done",message:`${r.length} connection${r.length===1?"":"s"} on ${u}`,display:[...r.length>0?l:[O.dim(`No connections${n.provider?` to ${n.provider}`:""}${n.user?` for ${n.user}`:""}. People connect at ${s.gatewayUrl}/connect/<provider>.`)]].join(`
18
+ `),data:{connections:r.map(d=>He(d,a(d.providerId))),...o?{organizationId:o.organization.id}:{}}})}async function Xe(e,t){let{out:n,flags:s}=e,i=[s.user?"--user":null,s.organization?"--organization":null,s.all?"--all":null].filter(Boolean);if(i.length!==1)throw new g("PROVIDER_CONNECTION_WHICH",i.length===0?`Say whose connection to ${t} to remove
19
+ Nothing was changed.`:`${w(i)} name different connections
20
+ Nothing was changed.`,{fix:`one of --user <email> (a person's), --organization (the organization's own) or --all (every connection to ${t})`});let o=await qe(e),a=(await _(o.db,o.install)).get(t);if(!a||a.mode!=="oauth_client")throw new g("PROVIDER_NOT_FOUND",a?`${t} isn't an OAuth provider: nobody connects to it`:`${t} isn't a provider this Gateway offers`,{rerun:`${h} provider list`,rerunNote:"names every one"});n.emit({type:"step",step:"provider.disconnect",status:"start",message:`Removing connections to ${a.label}...`});let l=await Ue(o.db,{providerId:t,providerName:a.label,...s.user?{userEmail:s.user}:{},...s.organization?{organization:!0}:{}},o.deployer,e.now?.());if(l.length===0)throw new g("PROVIDER_CONNECTION_NOT_FOUND",`No connection to ${a.label} ${s.user?`for ${s.user}`:s.organization?"for the organization":"on this Gateway"}
21
+ Nothing was changed.`,{rerun:`${h} provider connections --provider ${t}`,rerunNote:"lists the ones there are"});n.emit({type:"step",step:"provider.disconnect",status:"ok",message:`Removed ${l.length} connection${l.length===1?"":"s"} to ${a.label}`,data:{id:t,removed:l.length}});let u=l.map(c=>c.account==="organization"?"the organization":c.userEmail??c.userId??"someone"),d=[`Removed ${l.length===1?`${u[0]}'s`:`${l.length}`} connection${l.length===1?"":"s"} to ${a.label} (${t}).`,'The Gateway no longer holds the tokens; apps get "not connected" until the person connects again.',`The grant stays at ${a.label} until it is revoked there: ${a.console} (Admin \u2192 Providers \u2192 Disconnect revokes it from the Gateway).`];n.emit({type:"done",message:ze(d.join(`
22
+ `)),display:[d[0],...d.slice(1).map(c=>O.dim(c))].join(`
23
+ `),data:{id:t,name:a.label,removed:l.map(c=>He(c,a.label)),revoked:!1,revokeAt:a.console}})}function ge(e){return e.mode==="oauth_client"}var K=64*1024;async function Gn(e,t){await S(e,n=>yt({flags:e,out:n},t))}async function yt(e,t){let{flags:n}=e;if(n.oauth){if(t)throw new g("PROVIDER_SPEC_INVALID",`--oauth ${n.oauth} names the provider; ${t} was given too
24
+ Nothing was changed.`,{fix:`give one: ${h} provider add --oauth ${n.oauth}, or ${h} provider add ${t}`});await se(e,n.oauth);return}if(!t)throw new g("PROVIDER_SPEC_INVALID",`Give the provider to add
25
+ A built-in provider's id (provider list), a JSON spec file or https URL, or --oauth <preset>.`,{rerun:`${h} provider list`,rerunNote:"names the built-in providers"});let s=ft(t);if(s?.mode==="oauth_client"||Object.hasOwn(x,t)){await se(e,t);return}if(s){await $t(e,t,s);return}if(!/[./\\:]/.test(t))throw new g("PROVIDER_NOT_FOUND",`${t} isn't a built-in provider
26
+ A provider the catalog doesn't have is added from its spec: a JSON file or an https URL.`,{rerun:`${h} provider list`,rerunNote:"names the built-in providers"});await At(e,t)}function ft(e){return Object.hasOwn(I,e)?I[e]:null}async function Un(e){await S(e,t=>Ye({out:t}))}async function Tn(e){await S(e,t=>Je({flags:e,out:t}))}async function _n(e,t){await S(e,n=>Xe({flags:e,out:n},t))}async function xn(e){await S(e,t=>It({flags:e,out:t}))}async function jn(e,t){await S(e,n=>Rt({flags:e,out:n},t))}async function Dn(e,t){await S(e,n=>Et({flags:e,out:n},t))}async function Fn(e,t){await S(e,n=>Qe({flags:e,out:n},"on",t))}async function Ln(e,t){await S(e,n=>Qe({flags:e,out:n},"off",t))}async function Qe(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:`${h} mcp ${t} ${n}`}}),await(t==="on"?De:Fe)(e,n)}function q({flags:e,out:t,createCf:n}){return L({gateway:F(e.gateway),accountId:e["account-id"],interactive:T(e.json??!1),login:{json:e.json,out:t,step:"login"},createCf:n})}async function et(e){try{return await Y.access(ae.join(e,"everyapp.config.ts")),!0}catch{return!1}}function P(e,t){return new g("PROVIDER_SPEC_INVALID",e,{fix:t})}async function ye(e,{cwd:t,fetcher:n=fetch}){let s;if(/^[a-z][a-z0-9+.-]*:\/\//i.test(e)){let i;try{i=new URL(e)}catch{throw P(`${e} isn't a URL`,"give the spec's https URL, or a path to the file")}if(i.protocol!=="https:")throw P(`${e} isn't https`,"fetch specs over https only, or save the file and give its path");let o;try{o=await n(i,{headers:{accept:"application/json"},redirect:"follow"})}catch(r){throw P(`Couldn't fetch ${e}: ${pe(r).split(`
27
+ `)[0]}`,"check the URL, or save the file and give its path")}if(o.url&&!o.url.startsWith("https://"))throw P(`${e} redirected to a URL that isn't https`,"give the spec's final https URL");if(!o.ok)throw P(`${e} answered ${o.status}`,"check the URL, or save the file and give its path");s=await wt(o,e)}else{let i=ae.resolve(t,e),o;try{if((await Y.stat(i)).size>K)throw P(`${e} is over ${K/1024} KiB`,"a spec is a short JSON file");o=await Y.readFile(i)}catch(r){throw r instanceof g?r:Re(r)?P(`No file at ${e}`,"give the path to the spec's JSON file, or its https URL"):r}s=o.toString("utf8")}try{return JSON.parse(s)}catch(i){throw P(`${e} isn't JSON: ${pe(i).split(`
28
+ `)[0]}`,"fix the file, then add it again")}}async function wt(e,t){let n=Number(e.headers.get("content-length")),s=()=>P(`${t} is over ${K/1024} KiB`,"a spec is a short JSON file");if(Number.isFinite(n)&&n>K)throw s();if(!e.body)return"";let i=e.body.getReader(),o=[],r=0;for(;;){let{done:a,value:l}=await i.read();if(a)break;if(r+=l.byteLength,r>K)throw await i.cancel(),s();o.push(l)}return Buffer.concat(o).toString("utf8")}function vt(e){return e.mode==="oauth_client"?w([...e.api]):e.mode==="ai"?`${e.origin}${e.basePath??""}`:w([...e.hosts,...e.hostSetting?[`the host ${e.hostSetting} names`]:[]])}function bt(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"}}function kt(e,t){return[`1. Create a key at ${e}`,t?"2. Paste it below.":"2. Set it with the command below."]}async function tt(e,t){let{out:n,flags:s}=e,{id:i,capability:o,needed:r}=t,a=[...A(o)],l=W(o.label),u=await te(a,`${l}'s key`,s,e),d=e.interactive??T(s.json??!1),c=o.mode==="api_key"||o.mode==="ai"?o.docs:null,p=[...t.setup,...r.length>0&&Object.keys(u).length===0&&c?kt(c,d):[],...t.docs?[`Docs: ${t.docs}`]:[]];n.emit({type:"step",step:"provider.setup",status:"ok",message:p.join(`
29
+ `),data:{id:i,docs:t.docs}});let m=await ne({flags:s,out:n,...e.createCf?{createCf:e.createCf}:{},whose:`${l}'s key`,standing:t.standing,settings:a,needed:r,given:u,interactive:d,...e.ask?{ask:e.ask}:{}}),k=m==="set"||r.length===0,R=o.mode==="ai"?`list ${i} under providers`:`list ${i}`,E=m==="set"?[`Apps can now ${R}. Live now, no deploy needed.`]:r.length===0?[`Its key is set. Apps can ${R}; to replace the key: ${h} secret set ${a.join(" ")}`]:[],$=[o.mode==="ai"?`In an app's manifest: providers: [${JSON.stringify(i)}]`:`In an app's capabilities: { ${JSON.stringify(i)}: { access: "${o.mode==="api_key"?o.access[0]:"read"}", why: "..." } }`];n.emit({type:"done",message:Ze([...p,...E].join(`
30
+ `)),display:[...E,...s.verbose?$.map(f=>b.dim(f)):[]].join(`
31
+ `),data:{...t.data,settings:a,...t.docs?{docs:t.docs}:{},keySet:k,...k?{}:{next:[{command:`${h} secret set ${r.join(" ")}`}]}}})}async function $t(e,t,n){if(n.mode!=="api_key"&&n.mode!=="ai")throw new g("PROVIDER_BUILTIN",`${t} is a Gateway service, not a provider with a key
32
+ It is set up with its own command.`,{fix:t==="email"?`${h} email set`:`${h} provider show ${t}`});let s=await q(e),i=s.gatewayUrl.replace(/^https:\/\//,""),o=await U(s.cf,G),r=[...A(n)];e.out.emit({type:"step",step:"provider.add",status:"ok",message:`${n.label} (${t}) is built in: no row to write, its key alone is set`,data:{id:t,builtIn:!0}}),await tt(e,{id:t,capability:n,setup:[`${W(n.label)} (${t}) is on ${i}.`],needed:r.filter(a=>!o.includes(a)),docs:Q(t),standing:"the provider is built in",data:{id:t,name:n.label,kind:n.mode,...re(n),source:"catalog"}})}async function At(e,t){let{out:n,flags:s}=e,i=e.cwd??process.cwd(),o=await ye(t,{cwd:i,fetcher:e.fetch}),r=J(o);if(r.ok&&r.capability.mode==="oauth_client"){await se({...e,flags:{...s,oauth:t}},t);return}let a=await q(e),l=o&&typeof o=="object"&&typeof o.id=="string"?o.id:"the provider";n.emit({type:"step",step:"provider.add",status:"start",message:`Registering ${l}...`});let u=await X(a.db,{spec:o,deployer:a.deployer,replace:s.replace??!1,install:a.install,now:e.now?.()}),{id:d,capability:c}=u,p=a.gatewayUrl.replace(/^https:\/\//,"");n.emit({type:"step",step:"provider.add",status:"ok",message:`${u.replaced?"Replaced":"Registered"} ${c.label} (${d}) on ${p}`,data:{id:d,replaced:u.replaced}});let m=null;await et(i)&&(m=ce(d),await nt(i,d,c)),await tt(e,{id:d,capability:c,setup:[`${u.replaced?"Replaced":"Registered"} ${c.label} (${d}) on ${p}.`,`Its key goes to ${vt(c)} only, ${bt(c)}.`,...u.replaced?["The key set before was for the old spec: set it again before the Gateway sends it."]:[],...m?[`Wrote ${m} for everyapp dev (your key goes in .dev.vars).`]:[],...c.mode==="ai"?[`Apps list it under providers: [{ id: ${JSON.stringify(d)}, why: "..." }] and call /api/ai/${d}/<route> with an OpenAI client; ${h} ai default ${d} makes it the default model provider.`]:[]],needed:[...A(c)],docs:null,standing:"the provider is registered",data:{id:d,name:c.label,...re(c),source:"row",replaced:u.replaced,...m?{devFile:m}:{}}})}async function nt(e,t,n){let s=ae.join(e,Ce,`${t}.json`);return await Y.mkdir(ae.dirname(s),{recursive:!0}),await Y.writeFile(s,`${JSON.stringify(D(t,n),null,2)}
33
+ `),s}function Ct(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 re(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.map(t=>t.host))]};case"service":return{hosts:[]}}}function ot(e,t,n){return e.filter(s=>s.manifest?n.mode==="ai"?Oe(s.manifest).includes(t):!!(s.manifest.capabilities&&Object.hasOwn(s.manifest.capabilities,t)):!1).map(s=>s.appSlug)}function St(e,t,n){let s=new URL(n).host;return e.flatMap(i=>{let o=i.manifest?.capabilities?.[t];return!o||!Array.isArray(o.redirectPaths)?[]:o.redirectPaths.filter(r=>typeof r=="string").map(r=>`https://${i.appSlug}.${s}${r}`)})}function Ot(e,t,n,s){let i=new Map(e.rows().map(r=>[r.id,r])),o=[];for(let[r,a]of e.entries()){let l=Ct(a);if(!l)continue;let u=i.get(r);o.push({id:r,name:a.label,kind:l,...re(a),settings:A(a).map(d=>({name:d,set:t.includes(d)})),bound:e.bound(r),source:u?"row":"catalog",...u?{addedBy:u.addedBy,addedAt:u.addedAt}:{},apps:ot(n,r,a),...ge(a)?oe(r,a,{source:u?"row":"catalog",...s}):{}})}return o}async function It(e){let{out:t}=e,n=await q(e),[s,i,o,r,a]=await Promise.all([_(n.db,n.install),U(n.cf,G),ue(n.db),B(n.db),z(n.db)]),l=Ot(s,i,o,{gatewayUrl:n.gatewayUrl,gatewayNames:i,uploaded:r,connections:a}),u=M(["ID","NAME","KIND","HOSTS","SETTINGS","SOURCE","APPS"],l.map(p=>[p.id,p.name,p.kind==="oauth client"&&p.flow&&p.flow!=="app"?"oauth":p.kind,[...p.hosts,...p.hostSetting?[`<${p.hostSetting}>`]:[]].join(", ")||b.dim("-"),[p.client?.source==="uploaded"?`client ${b.green("uploaded")}`:p.settings.map(m=>`${m.name} ${m.set?b.green("set"):b.yellow("not set")}`).join(", "),...p.source==="row"&&p.client?.source!=="uploaded"?[p.bound?b.green("bound"):b.yellow("not bound")]:[],...p.connections!==void 0?[`${p.connections} connected`]:[]].join(", "),p.source==="row"?`registered by ${p.addedBy??"someone"}`:b.dim("catalog"),p.apps.join(", ")||b.dim("-")])),d=l.filter(p=>p.source==="row").length,c=s.rejected.map(({id:p,code:m,message:k})=>({id:p,code:m,message:k}));t.emit({type:"done",message:`${l.length} providers: ${l.length-d} built in, ${d} registered`,display:[...u,...c.length>0?["",b.yellow(`The Gateway doesn't serve ${w(c.map(p=>p.id))}:`),...c.map(p=>b.dim(` ${p.id}: ${p.message} (${p.code})`))]:[],"",b.dim(`MCP apps: ${h} mcp list`)].join(`
34
+ `),data:{providers:l,...c.length>0?{rejected:c}:{},mcp:`${h} mcp list`}})}function Pt(e,t,n){let s=[...A(t)];switch(t.mode){case"api_key":return D(e,t);case"ai":return t.models&&!t.setup?D(e,t):{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:s,hosts:[new URL(t.origin).host],routes:[...t.inferenceRoutes],...t.setup?{setup:t.setup}:{}};case"oauth_client":return t.flow&&!t.setup?D(e,t):{id:e,kind:"oauth client",name:t.label,docs:t.console,settings:s,tokenEndpoints:Ae(t).split(", "),authorize:{url:t.authorize.url,scopes:[...t.authorize.scopes]},...t.scopePolicy?{scopeSets:Object.entries(t.scopePolicy.sets).map(([i,o])=>({name:i,label:o.label,access:o.access,scopes:[...o.scopes],hosts:[...new Set(o.routes.map(r=>r.host))],source:n?.scopeSetSource(e,i)??"catalog"}))}:{},...t.setup?{setup:t.setup}:{}};case"service":return{id:e,name:t.label}}}async function Rt(e,t){let{out:n,flags:s}=e,i=e.cwd??process.cwd();if(s.save&&!await et(i))throw new g("MANIFEST_NOT_FOUND",`No everyapp.config.ts in this directory
35
+ --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 o=await q(e),[r,a,l,u,d]=await Promise.all([_(o.db,o.install),ue(o.db),U(o.cf,G),B(o.db),z(o.db)]),c=r.get(t);if(!c||c.mode==="service"){let v=r.rejected.find(C=>C.id===t);throw new g("PROVIDER_NOT_FOUND",c?`${t} is a Gateway service, not a provider
36
+ Apps call it with its own helper (sendEmail(), listMembers()).`:v?`The Gateway doesn't serve ${t}: ${v.message}`:`${t} isn't a provider this Gateway offers`,{rerun:`${h} provider list`,rerunNote:"names every one"})}let p=r.rows().find(v=>v.id===t),m=Pt(t,c,r),k=null;if(s.save){if(!p||c.mode==="oauth_client")throw new g("PROVIDER_BUILTIN",`${t} is built in
37
+ everyapp dev reaches it without a file. Nothing was written.`,{fix:`put its key in .dev.vars: ${A(c).join(", ")}`});await nt(i,t,c),k=ce(t)}let R=ot(a,t,c),E=c.mode==="oauth_client"?St(a,t,o.gatewayUrl):[],$=r.bound(t),f=ge(c)?oe(t,c,{source:p?"row":"catalog",gatewayUrl:o.gatewayUrl,gatewayNames:l,uploaded:u,connections:d}):null,N=f?.client,H=[p?`Registered by ${p.addedBy??"someone"}${p.addedAt?` on ${new Date(p.addedAt).toISOString().slice(0,10)}`:""}; ${f?N?.source==="uploaded"?"its client was uploaded under Admin \u2192 Providers":$?"its client is bound to these endpoints":"its client isn't set for these endpoints yet":$?"its key is bound to these hosts":"its key isn't set for these hosts yet"}.`:f?`Built in; its client is ${N?.source==="env"?"set":N?.source==="uploaded"?"uploaded under Admin \u2192 Providers":"not set"}.`:"Built in.",...f?[`Redirect URI to register at ${String(f.console)}: ${String(f.redirectUri)}`,`${String(f.connections)} connected; people connect at ${o.gatewayUrl}/connect/${t}.`]:[],...E.length>0?[`Apps that run the flow themselves list these callbacks; register them too: ${E.join(", ")}`]:[],R.length>0?`Used by ${w(R)}.`:"No app lists it yet.",...(()=>{let v=r.scopeSets().filter(C=>C.providerId===t);return v.length>0?[`Scope sets added here: ${w(v.map(C=>`${C.name} (by ${C.addedBy??"someone"})`))}; ${h} provider scope-set remove ${t} <name> removes one.`]:[]})(),...k?[`Wrote ${k} for everyapp dev.`]:[]];n.emit({type:"done",message:`${c.label} (${t})`,display:[JSON.stringify(m,null,2),"",...H.map(v=>b.dim(v))].join(`
38
+ `),data:{provider:m,source:p?"row":"catalog",bound:$,...f??{},...c.mode==="oauth_client"?{appRedirectUris:E}:{},addedBy:p?.addedBy??null,addedAt:p?.addedAt??null,apps:R,...k?{saved:k}:{},...p&&!$&&N?.source!=="uploaded"?{next:[{command:`${h} secret set ${A(c).join(" ")}`,note:`${c.label}'s key; the Gateway sends it only to ${w(re(c).hosts)}`}]}:{}}})}async function Et(e,t){let{out:n,flags:s}=e,i=await q(e);n.emit({type:"step",step:"provider.remove",status:"start",message:`Removing ${t}...`});let o=await Ne(i.db,t,i.deployer,{force:s.force??!1,now:e.now?.()}),r=await U(i.cf,G),a=o.settings.map(c=>({name:c,set:r.includes(c)})),l=a.filter(c=>c.set).map(c=>c.name),u=i.gatewayUrl.replace(/^https:\/\//,"");n.emit({type:"step",step:"provider.remove",status:"ok",message:`Removed ${o.name} (${t}) from ${u}`,data:{id:t}});let d=[`Removed ${o.name} (${t}) from ${u}.`,"The Gateway stops serving it within 30 seconds.",...o.connections>0?[`${o.connections} connection${o.connections===1?"":"s"} to it ${o.connections===1?"was":"were"} deleted (the grants stay at the provider until revoked there).`]:[],...o.client?["The client uploaded under Admin \u2192 Providers was deleted with it."]:[],...o.apps.length>0?[`${w(o.apps)} ${o.apps.length===1?"lists":"list"} it: ${o.apps.length===1?"its":"their"} calls to it fail until it is registered again.`]:[],...l.length>0?[`${w(l)} ${l.length===1?"stays":"stay"} on the Gateway, read by nothing: ${h} secret unset ${l.join(" ")} removes ${l.length===1?"it":"them"}.`]:[]];n.emit({type:"done",message:Ze(d.join(`
39
+ `)),display:[d[0],...d.slice(1).map(c=>b.dim(c))].join(`
40
+ `),data:{id:t,name:o.name,removed:!0,settings:a,apps:o.apps,connections:o.connections,client:o.client,...l.length>0?{next:[{command:`${h} secret unset ${l.join(" ")}`,note:"the key nothing reads any more"}]}:{}}})}export{K as a,Gn as b,yt as c,Un as d,Tn as e,_n as f,xn as g,jn as h,Dn as i,Fn as j,Ln as k,Qe as l,ye as m,$t as n,At as o,It as p,Rt as q,Et as r};