everyapp 0.3.0-rc.2 → 0.3.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/access-YWM4BSBD.js +1 -0
- package/dist/activity-RTCAUMQS.js +14 -0
- package/dist/ai-UOYQFTAX.js +5 -0
- package/dist/apps-PEOBCDNU.js +14 -0
- package/dist/chunk-26KS6TD6.js +1 -0
- package/dist/chunk-3EADVHFN.js +15 -0
- package/dist/chunk-4LKLQOM7.js +7 -0
- package/dist/{chunk-PNLU5U3P.js → chunk-5IC3ZN52.js} +1 -1
- package/dist/{chunk-H7PDL3X5.js → chunk-AMCNAKYU.js} +1 -1
- package/dist/{chunk-23HJ5I2A.js → chunk-ASCNE7GS.js} +1 -1
- package/dist/{chunk-6OGVO7QK.js → chunk-BCTEWNN4.js} +1 -1
- package/dist/{chunk-W3JCQHHU.js → chunk-BK4NK34C.js} +1 -1
- package/dist/chunk-BWNZ6F4F.js +2 -0
- package/dist/chunk-DCFHBCCW.js +9 -0
- package/dist/{chunk-2Y5Q6A5K.js → chunk-DWTJ4OLZ.js} +1 -1
- package/dist/chunk-EP3IQ354.js +20 -0
- package/dist/{chunk-OP4DYXYI.js → chunk-EP4HRYXP.js} +1 -1
- package/dist/{chunk-HOUHG236.js → chunk-EQLY5I46.js} +1 -1
- package/dist/{chunk-YHJZMJYZ.js → chunk-F5XK23HV.js} +1 -1
- package/dist/{chunk-5PTNYZK6.js → chunk-FB2OC32C.js} +3 -3
- package/dist/chunk-FQ2A7YAP.js +57 -0
- package/dist/{chunk-OM2HDWVP.js → chunk-GAPQSINH.js} +1 -1
- package/dist/chunk-GJK6GAWO.js +1 -0
- package/dist/chunk-H4EZTXIK.js +1 -0
- package/dist/chunk-HJGZGZFE.js +32 -0
- package/dist/{chunk-CCWBPAKH.js → chunk-I7FNTTUY.js} +3 -3
- package/dist/chunk-KOBKGQ27.js +91 -0
- package/dist/{chunk-PS7S74DG.js → chunk-KQM6ZUX5.js} +1 -1
- package/dist/chunk-KVCL64LI.js +1 -0
- package/dist/{chunk-4JRIS6S6.js → chunk-LEEUODYR.js} +1 -1
- package/dist/{chunk-6PGUCTJ6.js → chunk-LI3YLKLC.js} +1 -1
- package/dist/chunk-MEIWV7FJ.js +20 -0
- package/dist/{configure-F7UZCJUG.js → chunk-MLTNW3AM.js} +2 -2
- package/dist/chunk-NDOEON54.js +4 -0
- package/dist/chunk-NW7SJPSP.js +37 -0
- package/dist/chunk-O64FPLGX.js +1 -0
- package/dist/chunk-ORGV2NNT.js +8 -0
- package/dist/chunk-PDPUGYJW.js +47 -0
- package/dist/chunk-QFTBDXLF.js +146 -0
- package/dist/{chunk-E3EPK27P.js → chunk-QJRZFIYC.js} +2 -2
- package/dist/chunk-QKHLRLK3.js +15 -0
- package/dist/{chunk-CZXHKPRZ.js → chunk-R2DF3ENZ.js} +1 -1
- package/dist/{chunk-IUOBIB5M.js → chunk-R7JOHK7Y.js} +2 -2
- package/dist/chunk-RL72BUT4.js +1 -0
- package/dist/chunk-RQBSTNL7.js +1 -0
- package/dist/{chunk-H6QQMY7B.js → chunk-S2G4G73L.js} +1 -1
- package/dist/{chunk-LXYMDZER.js → chunk-SGRGD7L4.js} +1 -1
- package/dist/{chunk-T2GD7RHS.js → chunk-TIYSZWP5.js} +1 -1
- package/dist/chunk-TUXKBLLJ.js +10 -0
- package/dist/chunk-U4ATZJEU.js +6 -0
- package/dist/chunk-V7BWZOSZ.js +26 -0
- package/dist/{chunk-J35DDCNU.js → chunk-VVKQJSF3.js} +1 -1
- package/dist/chunk-WVHJIYQ3.js +14 -0
- package/dist/{chunk-ZEXVHB22.js → chunk-XBD7PQN5.js} +1 -1
- package/dist/{chunk-N5GQLGEC.js → chunk-YAZHTVUV.js} +1 -1
- package/dist/configure-CAWCTPYY.js +1 -0
- package/dist/{create-2E5HVWYK.js → create-3ZWJQXPK.js} +2 -2
- package/dist/{db-HEQVTDTM.js → db-B7KO37SV.js} +1 -1
- package/dist/delete-CB3EPOU4.js +3 -0
- package/dist/deploy-5O7LV5OX.js +1 -0
- package/dist/dev-4JYCVVOZ.js +86 -0
- package/dist/{email-RLZFUFUN.js → email-SWJQXEUR.js} +1 -1
- package/dist/{events-2V5VRBWP.js → events-GJV2KVIU.js} +13 -13
- package/dist/{groups-HDG5DVFY.js → groups-NNTLSXMK.js} +1 -1
- package/dist/index.js +104 -95
- package/dist/install-UAG3CNYW.js +1 -0
- package/dist/{login-KHJBC64Z.js → login-QEKMLMUB.js} +1 -1
- package/dist/mcp-AFGVOM5D.js +1 -0
- package/dist/provider-622VM7UZ.js +29 -0
- package/dist/release-CXEX6O7F.js +1 -0
- package/dist/{restore-FFGYV7NO.js → restore-PGIVEA5P.js} +1 -1
- package/dist/secret-3LUDEDUW.js +1 -0
- package/dist/setup-PUD375AM.js +1 -0
- package/dist/{signin-AP3D4XM4.js → signin-EP5JLAT5.js} +2 -2
- package/dist/status-LK6DNLCI.js +5 -0
- package/dist/teardown-6RURRSME.js +22 -0
- package/dist/update-YJFTOZEV.js +1 -0
- package/dist/{workspace-CJC5I66L.js → workspace-VW7ZZRFP.js} +3 -3
- package/package.json +5 -5
- package/skills/build-every-app/SKILL.md +22 -13
- package/skills/build-every-app/patterns.md +16 -3
- package/skills/everyapp/SKILL.md +33 -18
- package/skills/everyapp/mcp.md +22 -8
- package/skills/everyapp/security-review.md +1 -1
- package/skills/everyapp/setup/README.md +18 -1
- package/skills/everyapp/setup/access.md +11 -7
- package/skills/everyapp/setup/ai-gateway.md +117 -0
- package/skills/everyapp/setup/ai.md +139 -0
- package/skills/everyapp/setup/context.md +2 -2
- package/skills/everyapp/setup/dataforseo.md +3 -3
- package/skills/everyapp/setup/email.md +1 -1
- package/skills/everyapp/setup/gateway.md +3 -3
- package/skills/everyapp/setup/google.md +8 -6
- package/skills/everyapp/setup/microsoft-365.md +4 -2
- package/skills/everyapp/setup/providers.md +27 -9
- package/skills/everyapp/setup/remote-mcp.md +1 -1
- package/skills/everyapp/setup/signin-apple.md +3 -3
- package/skills/everyapp/setup/signin-cloudflare.md +1 -1
- package/skills/everyapp/setup/signin-github.md +1 -1
- package/skills/everyapp/setup/signin-google.md +2 -2
- package/skills/everyapp/setup/signin-microsoft.md +1 -1
- package/skills/everyapp/setup/slack.md +2 -0
- package/dist/access-LGEN3ABJ.js +0 -1
- package/dist/activity-RSD4D66F.js +0 -12
- package/dist/apps-4RCFXT74.js +0 -13
- package/dist/chunk-AGTCKFRB.js +0 -22
- package/dist/chunk-BHMRY53V.js +0 -2
- package/dist/chunk-CB4U7H2G.js +0 -4
- package/dist/chunk-CZTEDWZ2.js +0 -22
- package/dist/chunk-EGEWRFVY.js +0 -1
- package/dist/chunk-ELK7QZ2M.js +0 -1
- package/dist/chunk-FJJSSVET.js +0 -33
- package/dist/chunk-GKBPYVP3.js +0 -25
- package/dist/chunk-IFCPALVC.js +0 -46
- package/dist/chunk-J455FAEN.js +0 -7
- package/dist/chunk-JYKRUS5C.js +0 -1
- package/dist/chunk-LYNBZ7RV.js +0 -57
- package/dist/chunk-M2QQ2UD7.js +0 -132
- package/dist/chunk-NFU4YFXB.js +0 -1
- package/dist/chunk-ONLNJXVQ.js +0 -32
- package/dist/chunk-RMF4YGD6.js +0 -98
- package/dist/chunk-TLRM3P7V.js +0 -15
- package/dist/chunk-WOVS3GV5.js +0 -10
- package/dist/chunk-ZECBN24L.js +0 -15
- package/dist/chunk-ZFCUH5PK.js +0 -7
- package/dist/delete-XCSIQIRS.js +0 -3
- package/dist/deploy-3QOZEZHC.js +0 -1
- package/dist/dev-JL3ZP5BT.js +0 -86
- package/dist/install-Z5FYXUYM.js +0 -1
- package/dist/mcp-EFDUKJ5V.js +0 -1
- package/dist/provider-EOI5PTFH.js +0 -13
- package/dist/release-74J5ZIL3.js +0 -1
- package/dist/secret-7FKKTLHB.js +0 -1
- package/dist/setup-PAKRCAFO.js +0 -1
- package/dist/status-RMWNHOEK.js +0 -4
- package/dist/teardown-E4JNKAZJ.js +0 -22
- package/dist/update-DWVZFNPS.js +0 -1
package/skills/everyapp/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: everyapp
|
|
3
|
-
description: Create, run, deploy and manage private web apps on Cloudflare with everyapp (
|
|
3
|
+
description: Create, run, deploy and manage private web apps on Cloudflare with everyapp (EveryApp). Use before running any everyapp command, to deploy an app, set a secret, query its database, check the Gateway, or answer any "how do I" question about EveryApp.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
everyapp: self-hosted platform for private web apps on Cloudflare. While
|
|
7
7
|
publishing is paused the command is `pnpm everyapp` (never `npx everyapp`),
|
|
8
|
-
run from the
|
|
8
|
+
run from the EveryApp checkout or from an app's directory (its package.json
|
|
9
9
|
links the CLI).
|
|
10
10
|
|
|
11
11
|
Docs: two worlds. The user docs (https://everyapp.com/docs) are short and
|
|
@@ -13,7 +13,9 @@ for the person. The agent docs are yours: https://everyapp.com/llms.txt
|
|
|
13
13
|
lists every page with a one-line summary; a page is Markdown at its URL
|
|
14
14
|
plus .md (https://everyapp.com/docs/agent/manifest.md); in a checkout they
|
|
15
15
|
are landing-page/content/agent/*.md. Pages: setup (driving setup for a
|
|
16
|
-
person, every handoff),
|
|
16
|
+
person, every handoff), apps (the open apps: each one's id, repository,
|
|
17
|
+
install command, every setting and whether it needs R2), cli (every
|
|
18
|
+
command and flag), json (the --json
|
|
17
19
|
protocol), errors (every code and its fix), troubleshooting (by symptom),
|
|
18
20
|
status (the report, every line, and its fixes in order), manifest
|
|
19
21
|
(everyapp.config.ts), identity (headers, token, env, SDK), secrets
|
|
@@ -21,7 +23,11 @@ status (the report, every line, and its fixes in order), manifest
|
|
|
21
23
|
email, events (webhooks, email alerts, the signature, every event type,
|
|
22
24
|
emit()), mcp (tools, connector, OAuth), deploy (the pipeline, what the policy
|
|
23
25
|
check refuses), providers (API providers: the catalog, registering one,
|
|
24
|
-
a spec's fields), access (Cloudflare Access),
|
|
26
|
+
a spec's fields), access (Cloudflare Access), signin (sign-in providers:
|
|
27
|
+
Continue with Google, Microsoft, GitHub, Apple, Cloudflare or SSO; every
|
|
28
|
+
setting, the signin commands), ai (the Gateway's default model
|
|
29
|
+
provider: the order, `ai default|show|list`, AI Gateway in front, what an
|
|
30
|
+
app writes), workspace (the workspace from
|
|
25
31
|
the terminal and from an AI client: every command and every tool), gateway
|
|
26
32
|
(everything a Gateway offers: apps vs MCP apps vs the compact MCP surface,
|
|
27
33
|
people, groups and caps, the CLI by job, what to suggest next, other MCP
|
|
@@ -29,8 +35,10 @@ servers), build (the build-every-app skill as a page), patterns (its
|
|
|
29
35
|
example-patterns index), repo/* (security model, capabilities, runbooks).
|
|
30
36
|
Step-by-step setup of each
|
|
31
37
|
integration (the Gateway, Access, email, DataForSEO, Context, OpenRouter,
|
|
32
|
-
OpenAI, Workers AI,
|
|
33
|
-
|
|
38
|
+
OpenAI, Workers AI, the default model provider (setup/ai.md) and Cloudflare
|
|
39
|
+
AI Gateway (setup/ai-gateway.md), Google, Microsoft 365, Slack; every
|
|
40
|
+
catalog provider by id in setup/providers.md): setup/README.md beside this
|
|
41
|
+
file.
|
|
34
42
|
Answering "how do I": fetch the page for the subject and answer from it
|
|
35
43
|
with the exact command or manifest field, naming the page. A failed
|
|
36
44
|
command: look its code up on errors (anchor: the code, lowercased) before
|
|
@@ -55,8 +63,8 @@ scope sets (google-workspace, microsoft-365, slack) is listed with
|
|
|
55
63
|
scopes: [...] and held to them; after a schema change run pnpm run
|
|
56
64
|
db:generate; a value only dev needs goes in .dev.vars, never in git (dev
|
|
57
65
|
page). The way back: the first row of the app's sidebar (or of its page
|
|
58
|
-
header when it has no sidebar) is a muted "‹
|
|
59
|
-
GATEWAY_URL (aria-label "Back to
|
|
66
|
+
header when it has no sidebar) is a muted "‹ EveryApp" link to
|
|
67
|
+
GATEWAY_URL (aria-label "Back to EveryApp"), above the app's own name,
|
|
60
68
|
so every app on the Gateway reads the same; put nothing else about the
|
|
61
69
|
Gateway in the footer.
|
|
62
70
|
|
|
@@ -107,11 +115,15 @@ Data: pnpm everyapp db migrate (local) pnpm everyapp db query "<sql>" [--lo
|
|
|
107
115
|
(a production write stops with CONFIRMATION_REQUIRED: add --yes only
|
|
108
116
|
if the user said so). pnpm everyapp db restore --json undoes the last
|
|
109
117
|
deploy's migrations (and every write since): only when the user asks.
|
|
110
|
-
Postgres:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
the
|
|
118
|
+
Postgres: any Postgres with a URL (Neon, Supabase, …) goes through Hyperdrive:
|
|
119
|
+
resources.hyperdrive, database.provider "postgres", migrations.dialect
|
|
120
|
+
"postgresql" (manifest page, #postgres). The deploy reads
|
|
121
|
+
POSTGRES_DATABASE_URL (and <BINDING>_ORIGIN_URL) from
|
|
122
|
+
.everyapp/secrets.env, which the user writes (configure doesn't);
|
|
123
|
+
never print, commit or ask for its values in chat. Without it deploy
|
|
124
|
+
stops HYPERDRIVE_ORIGIN_MISSING (data.variables, data.file) and status
|
|
125
|
+
names it under the app. deploy runs drizzle-kit migrate before the
|
|
126
|
+
upload. Turso is libSQL: app secrets, not Hyperdrive.
|
|
115
127
|
Secrets: pnpm everyapp configure <app> --json sets up an app: every setting its
|
|
116
128
|
manifest declares, each where it is held (an AI key or a capability's
|
|
117
129
|
client on the Gateway, once for every app; the app's own secrets on
|
|
@@ -248,7 +260,7 @@ Tools: an app's AI tools are defineTools() in src/tools.ts; the Gateway
|
|
|
248
260
|
http://<app-id>.localhost:8787/mcp) and snapshots them at deploy.
|
|
249
261
|
How to write, connect and debug them: mcp.md beside this file.
|
|
250
262
|
Wipe: pnpm everyapp teardown --account-id <id> --dry-run --json deletes
|
|
251
|
-
EVERYTHING
|
|
263
|
+
EVERYTHING EveryApp made in one Cloudflare account (the Gateway too):
|
|
252
264
|
only on a dedicated test account the user names, never one they use.
|
|
253
265
|
Always --dry-run --json first and show the user data.plan. It needs
|
|
254
266
|
EVERYAPP_TEST_ACCOUNTS=<id> and CLOUDFLARE_API_TOKEN scoped to that
|
|
@@ -270,16 +282,19 @@ Security: the Gateway signs people in and passes identity to the app in a
|
|
|
270
282
|
against the list there. No CI job and no API key: the review runs
|
|
271
283
|
where you run. Fix what it finds, or say in the pull request why a
|
|
272
284
|
finding is a false positive. What the Gateway promises and what an
|
|
273
|
-
app must do itself: docs/threat-model.md in the
|
|
285
|
+
app must do itself: docs/threat-model.md in the EveryApp checkout
|
|
274
286
|
(https://everyapp.com/docs/agent/repo/threat-model).
|
|
275
287
|
|
|
276
288
|
Only a person can: approve the Cloudflare login, open the owner link, pick
|
|
277
289
|
a workers.dev subdomain, pay for Workers Paid, buy a domain. Interactive
|
|
278
290
|
setup optionally offers browser deployment: a Yes/No prompt (default No),
|
|
279
291
|
then a hidden Cloudflare API token prompt. Never ask for its value in chat.
|
|
280
|
-
It then
|
|
281
|
-
when the account has none); under
|
|
282
|
-
|
|
292
|
+
It then puts Cloudflare Access in front by default (asked at a terminal,
|
|
293
|
+
Yes by default; the team name asked for when the account has none); under
|
|
294
|
+
--json or --yes it goes on without a question when the account can (a Zero
|
|
295
|
+
Trust team exists, the Cloudflare login is an owner or admin), else the
|
|
296
|
+
warning ACCESS_SETUP_SKIPPED says why and names access on; --no-access
|
|
297
|
+
leaves it off. --yes answers No to browser deployment.
|
|
283
298
|
Handing off: stop, tell the person in one or two lines what to do (the
|
|
284
299
|
action's url and code, what to click, what to come back with), and resume
|
|
285
300
|
with data.rerun when they say it is done. Two more only a person can do:
|
package/skills/everyapp/mcp.md
CHANGED
|
@@ -159,8 +159,10 @@ Workspace tools (Gateway connector only; every admin action of the UI, listed
|
|
|
159
159
|
`everyapp_workspace_apps`, `everyapp_workspace_app_set`,
|
|
160
160
|
`everyapp_workspace_app_access`, `everyapp_workspace_app_access_set`,
|
|
161
161
|
`everyapp_workspace_app_cap`, `everyapp_workspace_cap_default`,
|
|
162
|
-
`
|
|
163
|
-
`
|
|
162
|
+
`everyapp_ai_show` (any member), `everyapp_ai_default`,
|
|
163
|
+
`everyapp_workspace_usage`, `everyapp_workspace_activity`,
|
|
164
|
+
`everyapp_workspace_audit` (the audit log: who did what, 90 days),
|
|
165
|
+
`everyapp_workspace_rename`, and the
|
|
164
166
|
events tools `everyapp_workspace_events`, `everyapp_workspace_events_add`
|
|
165
167
|
(a generated signing secret comes back once, in that answer: it opens
|
|
166
168
|
nothing on the Gateway, so the person stores it in the receiver now),
|
|
@@ -190,8 +192,17 @@ Provider tools (Gateway connector only; one audit row per call like the
|
|
|
190
192
|
never a replace of a row that is there) and
|
|
191
193
|
`everyapp_provider_remove` (`{ id }`: refuses a built-in, a row
|
|
192
194
|
whose key is set, and a row an app lists). A hosted Gateway hides
|
|
193
|
-
both writers.
|
|
194
|
-
|
|
195
|
+
both writers. `everyapp_provider_add` also takes `{ preset }`, an
|
|
196
|
+
OAuth preset (github-oauth, notion-oauth, …; the list tool's
|
|
197
|
+
`presets`) or a built-in client (google-workspace), and answers
|
|
198
|
+
the redirect URI to register at the provider's console. Every
|
|
199
|
+
member gets `everyapp_provider_connections` (their own connected
|
|
200
|
+
accounts; `all: true` the organization's, owners and admins) and
|
|
201
|
+
`everyapp_provider_disconnect` (their own; owners and admins
|
|
202
|
+
anyone's or the organization's). Connecting is a browser flow at
|
|
203
|
+
`<gateway>/connect/<provider>`, never a tool. No key, client or
|
|
204
|
+
token ever passes through a tool: `everyapp secret set NAME` from
|
|
205
|
+
the terminal, or Admin → Providers. Full reference:
|
|
195
206
|
/docs/agent/mcp#provider-tools.
|
|
196
207
|
Sign-in provider tools (Gateway connector only, owners and admins, reads):
|
|
197
208
|
`everyapp_signin_list` (Manage workspace → Sign-in: each sign-in
|
|
@@ -264,8 +275,11 @@ snapshot yet shows no hint (its tools load on first use).
|
|
|
264
275
|
Scripts and anything that takes a header: a personal access token. In the
|
|
265
276
|
Gateway open **Admin** (the sidebar footer's **Manage workspace**; owners and
|
|
266
277
|
admins only), tab **Tokens**, heading **Personal access tokens**, button
|
|
267
|
-
**New token**: a name,
|
|
268
|
-
|
|
278
|
+
**New token**: a name, an access level (**Read only**, the default:
|
|
279
|
+
`mcp:read`; **Read & write**: `mcp:write`; **Full access**: `*`, with a
|
|
280
|
+
warning), **All my apps** or one app (the one app is the default when there
|
|
281
|
+
is one), an expiry (default 30 days). The token is `epat_…`, shown once.
|
|
282
|
+
Send it as
|
|
269
283
|
`Authorization: Bearer epat_…` to `https://<gateway>/mcp` (a token made for one
|
|
270
284
|
app sees that app alone):
|
|
271
285
|
|
|
@@ -284,7 +298,7 @@ deactivating a person ends all of theirs; tokens are revoked on Admin, Tokens.
|
|
|
284
298
|
|
|
285
299
|
Other servers worth having beside the Gateway's (Cloudflare's API server for
|
|
286
300
|
the owner, read before write; nothing else): [setup/gateway.md](setup/gateway.md),
|
|
287
|
-
"Optional: MCP servers worth having beside
|
|
301
|
+
"Optional: MCP servers worth having beside EveryApp". Offer one in a line;
|
|
288
302
|
never add it unasked.
|
|
289
303
|
|
|
290
304
|
## Troubleshooting
|
|
@@ -310,7 +324,7 @@ Admin (a connection made with **Only these apps** needs a reconnect to add one).
|
|
|
310
324
|
expired or revoked token, a bearer that isn't `epat_` on an app host, the token
|
|
311
325
|
of a revoked connection); its body depends on the surface. On
|
|
312
326
|
`https://<gateway>/mcp` with OAuth on: an empty body and
|
|
313
|
-
`WWW-Authenticate: Bearer realm="OAuth", resource_metadata="…/.well-known/oauth-protected-resource/mcp", scope="mcp:read"`
|
|
327
|
+
`WWW-Authenticate: Bearer realm="OAuth", resource_metadata="…/.well-known/oauth-protected-resource/mcp", scope="mcp:read mcp:write"`
|
|
314
328
|
(`error="invalid_token"` added when a token was sent); a revoked connection's
|
|
315
329
|
token gets `{"error":"invalid_token","error_description":"This connection was revoked or lost its access. Connect again."}`.
|
|
316
330
|
On `https://<app-host>/mcp` (where the manifest sets `mcp`), and on the Gateway with OAuth off:
|
|
@@ -8,7 +8,7 @@ by the agent that made the change, with no CI job and no API key.
|
|
|
8
8
|
## The app boundaries
|
|
9
9
|
|
|
10
10
|
What the review looks for (what the Gateway promises and what the app must
|
|
11
|
-
do itself is the threat model, `docs/threat-model.md` in the
|
|
11
|
+
do itself is the threat model, `docs/threat-model.md` in the EveryApp
|
|
12
12
|
checkout, served at https://everyapp.com/docs/agent/repo/threat-model):
|
|
13
13
|
|
|
14
14
|
- A route, server function or tool that reads who the user is from anything
|
|
@@ -21,6 +21,8 @@ click when they have to do something themselves.
|
|
|
21
21
|
| OpenRouter | [openrouter.md](openrouter.md) | `OPENROUTER_API_KEY` | OpenSEO, Open CRM, agentic-inbox |
|
|
22
22
|
| OpenAI | [openai.md](openai.md) | `OPENAI_API_KEY`, `AI_PROXY_EXTRA_ROUTES` | apps listing the `openai` provider |
|
|
23
23
|
| Workers AI | [workers-ai.md](workers-ai.md) | `WORKERS_AI_API_TOKEN` (`CLOUDFLARE_ACCOUNT_ID` is set by `everyapp setup`) | agentic-inbox (optional) |
|
|
24
|
+
| The default model provider (any one key) | [ai.md](ai.md) | one provider's key (OpenRouter first); the choice with `everyapp ai default` | every app whose manifest says `ai: { chat: true }` (agentic-inbox, OpenCRM) |
|
|
25
|
+
| Cloudflare AI Gateway in front of the models | [ai-gateway.md](ai-gateway.md) | `AI_GATEWAY_ID` (set by `everyapp ai gateway on`; optional `AI_GATEWAY_API_KEY`) | every AI provider call, once it is on |
|
|
24
26
|
| Google (Gmail, Calendar, Search Console and Analytics) | [google.md](google.md) | `GOOGLE_WORKSPACE_CLIENT_ID`, `GOOGLE_WORKSPACE_CLIENT_SECRET` | Open CRM |
|
|
25
27
|
| Microsoft 365 (Outlook mail and calendar) | [microsoft-365.md](microsoft-365.md) | `MICROSOFT_365_CLIENT_ID`, `MICROSOFT_365_CLIENT_SECRET` | Open CRM |
|
|
26
28
|
| Slack (OAuth client) | [slack.md](slack.md) | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET` | Open CRM |
|
|
@@ -49,8 +51,23 @@ whole spec, hint and smoke request included, and its setting is set like
|
|
|
49
51
|
any Gateway setting (`pnpm everyapp secret set <NAME>`, which first names
|
|
50
52
|
the provider and the hosts its key goes to).
|
|
51
53
|
|
|
54
|
+
An OAuth provider (GitHub, Notion, Linear, GitLab, Atlassian, HubSpot,
|
|
55
|
+
Zoom, Dropbox, Spotify, X, or any custom one; the agent docs'
|
|
56
|
+
`providers` page, "OAuth providers") has no file either: `pnpm everyapp
|
|
57
|
+
provider presets` lists what can be added, `pnpm everyapp provider add
|
|
58
|
+
--oauth <preset>` adds it and prints the redirect URI the person registers
|
|
59
|
+
at the provider's console (`https://<gateway>/connect/<id>/callback`) and
|
|
60
|
+
the two settings to set, `pnpm everyapp secret set <ID>_CLIENT_ID
|
|
61
|
+
<ID>_CLIENT_SECRET` (an owner or admin may paste them in Manage workspace →
|
|
62
|
+
Providers instead). Only a person can make the OAuth app at the provider
|
|
63
|
+
and approve a consent; after the client is set, each member connects their
|
|
64
|
+
own account under Connected accounts in the account menu. Google,
|
|
65
|
+
Microsoft 365 and Slack below are the same: their files set the client,
|
|
66
|
+
and the same client serves both the app-driven flow those files describe
|
|
67
|
+
and the Gateway-owned one.
|
|
68
|
+
|
|
52
69
|
Which integrations a person needs: the apps they deploy decide. The four
|
|
53
|
-
|
|
70
|
+
EveryApp apps (OpenSEO, OpenCRM, OpenAnalytics, Agentic Inbox) are
|
|
54
71
|
installed from their GitHub releases by [gateway.md](gateway.md)'s last
|
|
55
72
|
step, each unless the person skips it; the keys they then ask for are the
|
|
56
73
|
files below. From any
|
|
@@ -15,10 +15,14 @@ team lacks it), an allow policy with every member's and invitee's email, two
|
|
|
15
15
|
Access applications, and the three Gateway settings, in one run. At the end
|
|
16
16
|
the person has Access's login before the Gateway, MCP clients, the Gateway's
|
|
17
17
|
`/health` and each app's public routes still reachable without it, and
|
|
18
|
-
optionally no passwords.
|
|
19
|
-
|
|
20
|
-
front of your Gateway?", Yes by default); under
|
|
21
|
-
|
|
18
|
+
optionally no passwords. `pnpm everyapp setup` does the same by
|
|
19
|
+
default once the owner exists: a person at a terminal is asked ("Put
|
|
20
|
+
Cloudflare Access in front of your Gateway?", Yes by default); under
|
|
21
|
+
`--json` or `--yes` it goes on without a question when the account can (a
|
|
22
|
+
Zero Trust team exists, the Cloudflare login is an owner or admin of the
|
|
23
|
+
Gateway), and otherwise the warning `ACCESS_SETUP_SKIPPED` says why and
|
|
24
|
+
names `access on`: then the offer is yours. `setup --no-access` leaves it
|
|
25
|
+
off. This
|
|
22
26
|
is the "outer ring" of `docs/runbooks/cloudflare-access.md`; the other way,
|
|
23
27
|
a "Continue with Company SSO" button, is
|
|
24
28
|
the runbook's "OIDC sign-in" and isn't covered here.
|
|
@@ -127,9 +131,9 @@ one-time PIN expires 10 minutes after it is sent.
|
|
|
127
131
|
| `ACCESS_HOSTED` | a hosted Gateway ignores Access | not available there |
|
|
128
132
|
| `ACCESS_SESSION_INVALID`, `ACCESS_EMAIL_INVALID`, `ACCESS_DOMAIN_INVALID` | a flag's value | durations like `720h` or `2h30m`, a full address, the part after the `@` |
|
|
129
133
|
| warning `ACCESS_EMAIL_NOT_INVITED` | `--email` names someone without an account; the Gateway refuses them | invite them in **Manage workspace → Users** |
|
|
130
|
-
| warning `ACCESS_IDP_UNVERIFIED_EMAILS` | a generic OIDC or SAML provider lets people pick their own email; `on` leaves it off the
|
|
134
|
+
| warning `ACCESS_IDP_UNVERIFIED_EMAILS` | a generic OIDC or SAML provider lets people pick their own email; `on` leaves it off the EveryApp applications | nothing, unless the person wants it allowed anyway: then they allow it in the dashboard, knowing the risk (runbook: "Verified emails") |
|
|
131
135
|
| warning `ACCESS_ONE_APPLICATION` | one Access application covers both hosts | kept and updated; an app host's Access cookie is then a Gateway login too |
|
|
132
|
-
| warning `ACCESS_FOREIGN_DESTINATION` | the application also covers something that isn't
|
|
136
|
+
| warning `ACCESS_FOREIGN_DESTINATION` | the application also covers something that isn't EveryApp's | kept as found; `access off` won't delete that application |
|
|
133
137
|
| warning `ACCESS_INLINE_POLICIES` | the application has policies of its own, so the shared policy can't attach | the person makes them reusable in the dashboard, or manages it there |
|
|
134
138
|
| warning `ACCESS_ROUTE_NOT_EXPRESSIBLE`, `ACCESS_TOO_MANY_PUBLIC_ROUTES` | a public route stays behind Access: no Access pattern for it (one `*` per path segment), or past the 25 overrides per host the command allows (the runbook's limit; Cloudflare's account limits page lists none) | people signed in still reach it; merge routes under one prefix |
|
|
135
139
|
| warning `ACCESS_APP_OUTSIDE_BASE` | the app's host isn't one label under `<app base domain>`, so the apps' application on `*.<app base domain>` doesn't cover it: Access isn't in front of that app at all (the Gateway still signs people in there); merging routes changes nothing | the person moves the app under the base domain, or makes an Access application for that host by hand in **Zero Trust → Access controls → Applications** |
|
|
@@ -146,7 +150,7 @@ one-time PIN expires 10 minutes after it is sent.
|
|
|
146
150
|
| MCP clients get an HTML login page | a bypass is missing | `pnpm everyapp access on --json` |
|
|
147
151
|
| teammates can't log in at Access at all | the team's only provider is Sign in with Cloudflare, restricted to the account's members | `pnpm everyapp access on --json` adds the one-time PIN beside it, on the applications too |
|
|
148
152
|
| Access's forbidden page after **Sign in with Cloudflare** | the person's Cloudflare account email isn't a Gateway member (`ACCESS_LOGIN_NOT_MEMBER`) | use **Send login code** with the Gateway address, or invite the Cloudflare one |
|
|
149
|
-
| `ACCESS_IDP_ANY_CLOUDFLARE_ACCOUNT` from `on` or `show` | a Cloudflare provider's **Restrict to account members** is off; `on` leaves it off the
|
|
153
|
+
| `ACCESS_IDP_ANY_CLOUDFLARE_ACCOUNT` from `on` or `show` | a Cloudflare provider's **Restrict to account members** is off; `on` leaves it off the EveryApp applications (the PIN alone signs people in) | the person turns it on: Zero Trust → Settings → Authentication → the provider; then `pnpm everyapp access on --json` allows it |
|
|
150
154
|
| the 51st person is blocked by Access | the Free plan's 50 seats | the person removes inactive users (Zero Trust → **Team & Resources → Users** → **Action → Remove users**; `cf zero-trust seats edit` does it from the shell, only when they ask), or pays |
|
|
151
155
|
| `EMAIL_PASSWORD_SIGN_IN=false` changes nothing | no owner has linked Access yet (no log line for it: `pnpm everyapp access show --json` still lists the owner in `data.linkPending`), or Access isn't configured (`access.config_problem` in the logs) | an owner signs in through Access once; or fix the settings |
|
|
152
156
|
| locked out: nobody passes Access | the policy or the one-time PIN is wrong at Cloudflare's edge; the Worker's settings can't let anyone in | when the person asks: `pnpm everyapp access off --json --yes` (deletes the two applications too); or the person fixes the policy, or deletes the applications, in **Zero Trust → Access controls → Applications** |
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Cloudflare AI Gateway in front of the model providers
|
|
2
|
+
|
|
3
|
+
Cloudflare AI Gateway is a proxy for model calls in the Gateway's own
|
|
4
|
+
Cloudflare account: a log of every call, caching, rate limits, retries
|
|
5
|
+
and a spend view, at `https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/…`.
|
|
6
|
+
It has no models of its own and never replaces a provider's key: with it
|
|
7
|
+
on, every call the Gateway makes to an AI provider that has an AI Gateway
|
|
8
|
+
slug (OpenRouter, OpenAI, Anthropic, Groq, Mistral, Workers AI) goes
|
|
9
|
+
through it with the provider's own key still on the call; Gemini and
|
|
10
|
+
Cohere go direct (Cloudflare documents no form for their OpenAI-shaped
|
|
11
|
+
paths). It changes no app and no manifest. At the end the person has an
|
|
12
|
+
AI Gateway named `everyapp` in their account, `AI_GATEWAY_ID` on the
|
|
13
|
+
Gateway, and every model call showing up in the AI Gateway dashboard.
|
|
14
|
+
|
|
15
|
+
## Settings
|
|
16
|
+
|
|
17
|
+
| NAME | Required or optional | What it holds |
|
|
18
|
+
| -------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `AI_GATEWAY_ID` | optional (set by `pnpm everyapp ai gateway on`) | the AI Gateway's id, its name in the dashboard (`everyapp`); with `CLOUDFLARE_ACCOUNT_ID`, which `everyapp setup` set |
|
|
20
|
+
| `AI_GATEWAY_API_KEY` | optional, only for an authenticated gateway | an AI Gateway token with Run permission, sent as `cf-aig-authorization: Bearer <token>` |
|
|
21
|
+
|
|
22
|
+
The Gateway sends the gateway token only to `gateway.ai.cloudflare.com`
|
|
23
|
+
and, for Workers AI, `api.cloudflare.com` with `cf-aig-gateway-id`. Apps
|
|
24
|
+
can't set `cf-aig-*` headers: the proxy strips them.
|
|
25
|
+
|
|
26
|
+
## Costs and plans
|
|
27
|
+
|
|
28
|
+
As of October 2026: AI Gateway is included with a Cloudflare account on
|
|
29
|
+
every plan, with logs kept per plan (Workers Free: 100,000 logs per
|
|
30
|
+
gateway; Paid: 10 million) and no per-request charge of its own; the
|
|
31
|
+
provider's prices are unchanged (BYOK: the provider's key is the
|
|
32
|
+
provider's bill). Unified Billing (paying providers through Cloudflare
|
|
33
|
+
credits) is not used: the Gateway always sends the provider's key, and
|
|
34
|
+
`cf-aig-no-wholesale` is not needed because a key on the request wins.
|
|
35
|
+
|
|
36
|
+
## Only a person can
|
|
37
|
+
|
|
38
|
+
- Decide to turn it on (it adds a hop and a log of every prompt's
|
|
39
|
+
metadata, never the prompt itself unless logging of payloads is turned
|
|
40
|
+
on in the dashboard).
|
|
41
|
+
- Create a gateway token in the dashboard when they want an
|
|
42
|
+
authenticated gateway, and paste it into the terminal.
|
|
43
|
+
- Turn on caching, rate limits or payload logging in the AI Gateway
|
|
44
|
+
dashboard: the Gateway sends no `cf-aig-*` options; the gateway's own
|
|
45
|
+
settings apply to every call.
|
|
46
|
+
|
|
47
|
+
## Before you start
|
|
48
|
+
|
|
49
|
+
1. `pnpm everyapp status --json`: `data.installed` and `data.claimed`
|
|
50
|
+
true; otherwise [gateway.md](gateway.md) first.
|
|
51
|
+
2. `pnpm everyapp ai show --json`: `done.data.aiGateway.on` false means
|
|
52
|
+
it isn't on; `done.data.via` says `direct`. At least one model
|
|
53
|
+
provider configured ([ai.md](ai.md)) so there is something to front.
|
|
54
|
+
3. The Cloudflare login the CLI uses can create AI Gateways in the
|
|
55
|
+
account (the account owner's login, or a token with AI Gateway Write).
|
|
56
|
+
|
|
57
|
+
## Steps
|
|
58
|
+
|
|
59
|
+
1. **Turn it on**: `pnpm everyapp ai gateway on --json`. The CLI finds or
|
|
60
|
+
creates an AI Gateway named `everyapp` in the Gateway's account
|
|
61
|
+
(`--id <name>` for another name, or one the person made already) and
|
|
62
|
+
sets `AI_GATEWAY_ID` on the Gateway. `done.data` is `{ id, url,
|
|
63
|
+
created }`. From the Gateway's next request every slugged provider
|
|
64
|
+
call goes through it.
|
|
65
|
+
2. **Authenticated gateway (optional).** In the dashboard, AI → AI
|
|
66
|
+
Gateway → the gateway → Settings → Authenticated Gateway: on; create
|
|
67
|
+
an AI Gateway token (Run permission). Then from the person's
|
|
68
|
+
terminal: `pnpm everyapp secret set AI_GATEWAY_API_KEY` (the value by
|
|
69
|
+
pipe or prompt, never on the command line). Without the token on an
|
|
70
|
+
authenticated gateway every model call answers 401 from AI Gateway.
|
|
71
|
+
3. Nothing else: no app changes, no key moves.
|
|
72
|
+
|
|
73
|
+
## Verify
|
|
74
|
+
|
|
75
|
+
1. `pnpm everyapp ai show --json`: `done.data.aiGateway` is `{ on: true,
|
|
76
|
+
id: "everyapp" }` and `done.data.via` is `ai-gateway`.
|
|
77
|
+
2. Make one model call in an app (ask Agentic Inbox's agent something),
|
|
78
|
+
then open the dashboard: AI → AI Gateway → `everyapp` → Logs shows
|
|
79
|
+
the request with the provider's name.
|
|
80
|
+
3. Manage workspace → Usage still counts the call under its provider:
|
|
81
|
+
AI Gateway adds a hop, not a provider.
|
|
82
|
+
|
|
83
|
+
## If it fails
|
|
84
|
+
|
|
85
|
+
- `ai gateway on` fails with a Cloudflare error: the login can't create
|
|
86
|
+
AI Gateways (a scoped token without AI Gateway Write); use the
|
|
87
|
+
account owner's login, or make the gateway in the dashboard and run
|
|
88
|
+
`ai gateway on --id <its name>`.
|
|
89
|
+
- A model call answers 401 after turning it on: the gateway is
|
|
90
|
+
authenticated and `AI_GATEWAY_API_KEY` isn't set (step 2) or is wrong
|
|
91
|
+
(rotate it).
|
|
92
|
+
- A model call answers 404 or 400 from `gateway.ai.cloudflare.com`: a
|
|
93
|
+
provider path AI Gateway doesn't serve the way the catalog recorded
|
|
94
|
+
it (the entry's `aig` block; `landing-page/content/agent/ai.md` lists
|
|
95
|
+
each). `pnpm everyapp ai gateway off` sends everything direct again
|
|
96
|
+
while it is looked at.
|
|
97
|
+
- Calls work but don't appear in the dashboard: the provider has no
|
|
98
|
+
slug (Gemini, Cohere) and went direct.
|
|
99
|
+
|
|
100
|
+
## Rerun, rotate, remove
|
|
101
|
+
|
|
102
|
+
- `ai gateway on` again is safe: it finds the existing gateway and
|
|
103
|
+
rewrites the same id.
|
|
104
|
+
- Rotate the gateway token in the dashboard, then `pnpm everyapp secret
|
|
105
|
+
set AI_GATEWAY_API_KEY` again.
|
|
106
|
+
- `pnpm everyapp ai gateway off` unsets `AI_GATEWAY_ID`: every call goes
|
|
107
|
+
direct from the next request; the AI Gateway resource and its logs
|
|
108
|
+
stay in the account (delete it in the dashboard). `pnpm everyapp secret
|
|
109
|
+
unset AI_GATEWAY_API_KEY` removes the token.
|
|
110
|
+
|
|
111
|
+
## Sources
|
|
112
|
+
|
|
113
|
+
- https://developers.cloudflare.com/ai-gateway/ (overview; provider
|
|
114
|
+
pages under usage/providers/; authentication under
|
|
115
|
+
configuration/authentication/; the header glossary), read 2026-10-25.
|
|
116
|
+
- https://everyapp.com/docs/agent/ai (in the checkout
|
|
117
|
+
`landing-page/content/agent/ai.md`): how each provider is fronted.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# The default model provider
|
|
2
|
+
|
|
3
|
+
An app that says `ai: { chat: true }` in its manifest (Agentic Inbox,
|
|
4
|
+
OpenCRM's research agent) takes whichever chat model the Gateway's
|
|
5
|
+
operator made the default, through `/api/ai/default/…`; it never names a
|
|
6
|
+
provider. One key, set once, makes every such app work. This file sets
|
|
7
|
+
that one key and, when the person wants a particular provider or model,
|
|
8
|
+
the choice. At the end the person has one model provider's key on the
|
|
9
|
+
Gateway, `pnpm everyapp ai show` saying what the default resolves to,
|
|
10
|
+
and every app that needs "a chat model" open.
|
|
11
|
+
|
|
12
|
+
## Settings
|
|
13
|
+
|
|
14
|
+
| NAME | Required or optional | What it holds |
|
|
15
|
+
| -------------------------------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| one provider's key (`OPENROUTER_API_KEY` first in the order) | required by every app whose manifest says `ai.chat` | the provider's key as its file says ([openrouter.md](openrouter.md), [workers-ai.md](workers-ai.md), [openai.md](openai.md), or [providers.md](providers.md) under `anthropic`, `groq`, `mistral`, `gemini`) |
|
|
17
|
+
| the choice (`pnpm everyapp ai default <provider>[/<model>]`) | optional | which provider (and model) the default is; without it the order decides |
|
|
18
|
+
|
|
19
|
+
The order the Gateway resolves the default in, the first whose settings
|
|
20
|
+
are set: `openrouter` (one key, many models, the price in every answer),
|
|
21
|
+
`workers-ai` (no provider account: the Gateway's own Cloudflare token;
|
|
22
|
+
weaker models), `openai`, `anthropic`, `groq`, `mistral`, `gemini`. The
|
|
23
|
+
operator's choice wins over the order. Cloudflare AI Gateway is never
|
|
24
|
+
the default: it fronts whichever provider resolves
|
|
25
|
+
([ai-gateway.md](ai-gateway.md)).
|
|
26
|
+
|
|
27
|
+
## Costs and plans
|
|
28
|
+
|
|
29
|
+
The provider's: OpenRouter is prepaid in USD at the providers' own
|
|
30
|
+
prices ([openrouter.md](openrouter.md)); Workers AI bills the Cloudflare
|
|
31
|
+
account per token ([workers-ai.md](workers-ai.md)); the others each have
|
|
32
|
+
a plan of their own, named in their section of
|
|
33
|
+
[providers.md](providers.md). The model the Gateway fills in when an app
|
|
34
|
+
names none is a cheap capable one per provider (OpenRouter
|
|
35
|
+
`anthropic/claude-haiku-5.5`, Workers AI
|
|
36
|
+
`@cf/meta/llama-4-scout-17b-16e-instruct`, OpenAI `gpt-6-luna`, Groq
|
|
37
|
+
`openai/gpt-oss-20b`, Mistral `mistral-small-latest`, Gemini
|
|
38
|
+
`gemini-3.1-flash-lite`); `pnpm everyapp ai default <provider>/<model>`
|
|
39
|
+
picks another. Usage by provider and model is on Manage workspace →
|
|
40
|
+
Usage and in `pnpm everyapp workspace usage --json` (`byModel`).
|
|
41
|
+
|
|
42
|
+
## Only a person can
|
|
43
|
+
|
|
44
|
+
- Open an account with the provider and buy credits or add a card.
|
|
45
|
+
- Create the key there and paste it into the terminal (the value never
|
|
46
|
+
goes through the chat or the command line).
|
|
47
|
+
- Decide the provider and model when the shipped order isn't what they
|
|
48
|
+
want (a model they trust, a region, a budget).
|
|
49
|
+
|
|
50
|
+
## Before you start
|
|
51
|
+
|
|
52
|
+
1. `pnpm everyapp status --json`: `data.installed` and `data.claimed`
|
|
53
|
+
true; otherwise [gateway.md](gateway.md) first.
|
|
54
|
+
2. Whether this file applies: an app in `data.apps[]` whose `setup.unmet`
|
|
55
|
+
has a setting with `kind: "ai-default"` (label "A chat model (…)"),
|
|
56
|
+
or whose `setup.settings` lists one. Its `names` is the key the order
|
|
57
|
+
asks for first and its `hint` names the alternatives. No such app:
|
|
58
|
+
nothing to do here; an app that pins a provider is that provider's
|
|
59
|
+
file.
|
|
60
|
+
3. `pnpm everyapp ai show --json`: `done.data.configured` false means no
|
|
61
|
+
provider is set yet; `done.data.provider` and `done.data.settings`
|
|
62
|
+
say which key the default would use; `done.data.candidates[]` lists
|
|
63
|
+
every provider with `configured`.
|
|
64
|
+
|
|
65
|
+
## Steps
|
|
66
|
+
|
|
67
|
+
1. **Pick the provider with the person.** Default to OpenRouter unless
|
|
68
|
+
they say otherwise: it is one key for every model and the only one
|
|
69
|
+
whose spend the Gateway counts in dollars. Workers AI when they want
|
|
70
|
+
no provider account at all. A single vendor when they already have a
|
|
71
|
+
key there.
|
|
72
|
+
2. **Set that provider's key**, by its own file: [openrouter.md](openrouter.md)
|
|
73
|
+
(`OPENROUTER_API_KEY`), [workers-ai.md](workers-ai.md)
|
|
74
|
+
(`WORKERS_AI_API_TOKEN`), [openai.md](openai.md) (`OPENAI_API_KEY`),
|
|
75
|
+
or the provider's section of [providers.md](providers.md)
|
|
76
|
+
(`ANTHROPIC_API_KEY`, `GROQ_API_KEY`, `MISTRAL_API_KEY`,
|
|
77
|
+
`GEMINI_API_KEY`). Each ends with `pnpm everyapp secret set <NAME>`,
|
|
78
|
+
the value from the person's terminal, or `pnpm everyapp configure
|
|
79
|
+
<app id>`, which asks for exactly the one key the app's `ai-default`
|
|
80
|
+
setting names.
|
|
81
|
+
3. **Store the choice when it isn't the order's.** The first configured
|
|
82
|
+
provider of the order is the default by itself; nothing to store when
|
|
83
|
+
it is OpenRouter and the person is happy with its model. Otherwise:
|
|
84
|
+
`pnpm everyapp ai default <provider>` (`groq`), or with a model,
|
|
85
|
+
`pnpm everyapp ai default openrouter/anthropic/claude-sonnet-5.5`
|
|
86
|
+
(the provider id up to the first slash, the model after it). `pnpm
|
|
87
|
+
everyapp ai default off` clears it. Each change queues the
|
|
88
|
+
`ai.default_changed` event.
|
|
89
|
+
4. **Optionally put AI Gateway in front**: [ai-gateway.md](ai-gateway.md)
|
|
90
|
+
(`pnpm everyapp ai gateway on`), for logs, caching and limits on
|
|
91
|
+
every model call; it changes no app and no key.
|
|
92
|
+
|
|
93
|
+
## Verify
|
|
94
|
+
|
|
95
|
+
1. `pnpm everyapp ai show --json`: `done.data.configured` true,
|
|
96
|
+
`done.data.provider` the one the person picked (or the order's
|
|
97
|
+
first), `done.data.model` the model that will be filled in,
|
|
98
|
+
`done.data.via` `ai-gateway` once the gateway is on, else `direct`.
|
|
99
|
+
2. `pnpm everyapp status --json`: every app that said `ai.chat` has
|
|
100
|
+
`setup.ready` true and no `ai-default` setting in `setup.unmet`.
|
|
101
|
+
3. In the app: a chat answer arrives (Agentic Inbox: open a thread and
|
|
102
|
+
ask the agent; OpenCRM: run research on a company). Manage workspace
|
|
103
|
+
→ Usage → By provider shows the provider and, under it, the model.
|
|
104
|
+
|
|
105
|
+
## If it fails
|
|
106
|
+
|
|
107
|
+
- `ai_default_not_configured` in an app's error (503): no provider's
|
|
108
|
+
key is set, or the chosen one's isn't. The message names the key;
|
|
109
|
+
step 2. `pnpm everyapp ai show` says the same with `configured: false`.
|
|
110
|
+
- `AI_DEFAULT_INVALID` from `ai default`: the id isn't a provider the
|
|
111
|
+
Gateway knows, is an API provider (Stripe), has no OpenAI-shaped chat
|
|
112
|
+
route (Cohere: an app pins it under `providers`), or is `ai-gateway`
|
|
113
|
+
(turn it on with `ai gateway on` and pick a model provider). The
|
|
114
|
+
message names the candidates.
|
|
115
|
+
- `ai_route_not_allowed` on the default route: the app called something
|
|
116
|
+
other than chat completions or embeddings; it lists the provider under
|
|
117
|
+
`providers` for that.
|
|
118
|
+
- `model_not_allowed` never comes from the default route; an app seeing
|
|
119
|
+
it pinned a provider and limited `ai.models`.
|
|
120
|
+
- A provider's own error (401, 429, a model name it doesn't serve): the
|
|
121
|
+
provider's file, "If it fails".
|
|
122
|
+
|
|
123
|
+
## Rerun, rotate, remove
|
|
124
|
+
|
|
125
|
+
- Rerunning any step is safe: `ai default` overwrites the choice, a key
|
|
126
|
+
set again replaces the old one on the next request.
|
|
127
|
+
- Rotate a key as its provider's file says; the default resolves to the
|
|
128
|
+
same provider.
|
|
129
|
+
- Remove the choice with `pnpm everyapp ai default off`; remove a key
|
|
130
|
+
with `pnpm everyapp secret unset <NAME>`, after which the order moves
|
|
131
|
+
to the next configured provider, or apps that need the default show
|
|
132
|
+
"Needs setup" again.
|
|
133
|
+
|
|
134
|
+
## Sources
|
|
135
|
+
|
|
136
|
+
- https://everyapp.com/docs/agent/ai (in the checkout
|
|
137
|
+
`landing-page/content/agent/ai.md`): the route, the order, the
|
|
138
|
+
commands and their `--json`, the event.
|
|
139
|
+
- `packages/sdk/src/internal/aiDefault.ts`: the resolution rule.
|
|
@@ -54,12 +54,12 @@ As of October 2026 (context.dev/pricing and docs.context.dev/account):
|
|
|
54
54
|
2. **Person:** Sign up, or sign in. Page: https://www.context.dev/signup. Type their email in **Email** and click **Continue** (an email sign-up sets a password and asks them to verify the address by email), or pick Google or GitHub under "or continue with" (no verification step). Continuing accepts the Terms of Service and Privacy Policy. An existing account signs in from "Already have an account? Sign in" (https://context.dev/login). It worked when the dashboard opens.
|
|
55
55
|
|
|
56
56
|
Alternative, only with their say-so (it registers a key in their name): Context.dev's agent flow (context.dev/auth.md, read today, not run). Everything in one shell, with `EMAIL` set to the address they gave (an existing account's email, when they have one). Register:
|
|
57
|
-
`reg=$(jq -n --arg email "$EMAIL" '{type:"service_auth",login_hint:$email,api_key_name:"
|
|
57
|
+
`reg=$(jq -n --arg email "$EMAIL" '{type:"service_auth",login_hint:$email,api_key_name:"EveryApp Gateway",client_name:"everyapp setup"}' | curl -sS -X POST https://www.context.dev/agent/identity -H 'Content-Type: application/json' -d @-); printf '%s' "$reg" | jq '{claim, offer}'`
|
|
58
58
|
Show them `claim.verification_uri` and `claim.user_code` in full, and that both expire in `claim.expires_in` seconds (600: ten minutes). If the response has `offer`, say it includes 1,000 extra credits (deadline `offer.claim_by`); promise nothing otherwise. A 429 `rate_limit_exceeded` from the registration means stop, no retry: step 3 instead. They open the link, enter the 6-digit code, and sign up or sign in with that email (a password sign-up also verifies the address). Then poll every `claim.interval` seconds, for ten minutes at most, until the key arrives or an error ends it, and pipe the key straight in (README's "a command produced it"); `claim_token` is read from `$reg` in the same shell, never typed (it is short-lived and not the key):
|
|
59
59
|
`CLAIM=$(printf '%s' "$reg" | jq -r .claim_token); INTERVAL=$(printf '%s' "$reg" | jq -r '.claim.interval // 5'); for i in $(seq 1 120); do r=$(curl -sS -X POST https://www.context.dev/oauth2/token -d "grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=$CLAIM"); e=$(printf '%s' "$r" | jq -r '.error // empty'); case "$e" in '') break;; authorization_pending) ;; slow_down) sleep 10;; *) printf '%s' "$r" | jq '{error, error_description}'; break;; esac; sleep "$INTERVAL"; done; printf '%s' "$r" | jq -e .access_token >/dev/null && printf '%s' "$r" | jq -r .access_token | pnpm everyapp secret set CONTEXT_API_KEY --json`
|
|
60
60
|
`authorization_pending` means they haven't finished (`email_verification_required`: the address isn't verified yet); `expired_token` means the ten minutes passed: register again, only with their agreement; `invalid_grant` means stop and show its `error_description`. When the loop ends without a key, nothing is set and the printed error says why (none printed: curl itself failed). Never print `$r`: the key is in it. This replaces steps 3 and 4: go to Verify (nobody but the Gateway holds the key now, so its smoke check is skipped; 1 and 3 there are the check).
|
|
61
61
|
|
|
62
|
-
3. **Person:** Create the key. Page: https://context.dev/dashboard/api-keys. Create a new key with the page's button (its label isn't in the docs); if the dialog shows the key once, copy it before closing. Keep **Full access** (the default; it covers the Data APIs the apps use and the usage read the smoke check uses), or choose **Restricted** with Data APIs = Use and "Account logs, usage, and browser profiles" = Read. Name it "
|
|
62
|
+
3. **Person:** Create the key. Page: https://context.dev/dashboard/api-keys. Create a new key with the page's button (its label isn't in the docs); if the dialog shows the key once, copy it before closing. Keep **Full access** (the default; it covers the Data APIs the apps use and the usage read the smoke check uses), or choose **Restricted** with Data APIs = Use and "Account logs, usage, and browser profiles" = Read. Name it "EveryApp Gateway" when the form asks (renaming later needs a paid plan). It worked when the key is listed on that page. A Free organization holds at most two keys: with two there already, delete one they no longer use (whether a disabled key still counts toward the two isn't in the docs), or use the agent flow above, which needs no button.
|
|
63
63
|
|
|
64
64
|
4. **Person, then You:** Put it on the Gateway. README's first way (the key is on a screen they are looking at): they open a second terminal in the `every-app` folder and run `pnpm everyapp configure crm` (or `pnpm everyapp configure openseo`; no `--json`: with it nothing prompts), which asks for this key among the app's other unset settings (for OpenSEO it is optional: Yes to `Set the N optional settings too?` (`Set the optional setting too?` for one), then `CONTEXT_API_KEY (Enter to skip)`; once an app is ready, its menu `Everything <app> needs is set. Change one?` lists it), or `pnpm everyapp secret set CONTEXT_API_KEY` for just this one. The prompt is hidden (`CONTEXT_API_KEY`, under lines saying what it is for and where it comes from; `secret set` asks `Value for CONTEXT_API_KEY`); they paste, press Enter, and see `Set CONTEXT_API_KEY on the Gateway` (configure then says `crm is ready.` or what the app still needs, and `Live now. No deploy needed.`; `secret set` ends `Live now on <gateway host>. No deploy needed.`). Nothing is exported afterwards, so offer the smoke check (Verify 2, 0 credits) in that same terminal: the hidden read (README's third way: `read -rs CONTEXT_API_KEY && export CONTEXT_API_KEY`, paste, Enter), Verify 2's curl, they tell you its last line (200) and `credits_remaining`, then `unset CONTEXT_API_KEY`; declined, the app (Verify 3) is the check. README's third way from the start, when they read it in hidden instead (never a typed `export …=`, which the shell history keeps; run the smoke check below first): `pnpm everyapp configure <app id> --from-env --json`, whose `done` event has `data.set` `["CONTEXT_API_KEY"]` and `data.changed.gateway.names`, or `pnpm everyapp secret set CONTEXT_API_KEY --from-env CONTEXT_API_KEY --json`, whose `done` event has `data.gateway.names` `["CONTEXT_API_KEY"]`; then `unset CONTEXT_API_KEY`. Never on a command line, never in the chat (a key seen in the chat gets rotated: step 3 again). Wait for "done".
|
|
65
65
|
|
|
@@ -112,7 +112,7 @@ value is set.
|
|
|
112
112
|
Confirm the email if asked: until it is confirmed the API answers 40104.
|
|
113
113
|
When it worked, the dashboard at app.dataforseo.com opens and shows a $1
|
|
114
114
|
balance. Already registered: sign in at https://app.dataforseo.com. Ran
|
|
115
|
-
elsewhere: an OpenSEO deployed outside
|
|
115
|
+
elsewhere: an OpenSEO deployed outside EveryApp held the base64 form of
|
|
116
116
|
the same credential, which works unchanged here (step 4's note), and if
|
|
117
117
|
that deployment whitelisted its server's IP on API Access, the list has
|
|
118
118
|
to be emptied (step 2). Wait for "I'm in".
|
|
@@ -197,7 +197,7 @@ value is set.
|
|
|
197
197
|
(`https://<gateway host>/?setup=openseo`) means a required setting is
|
|
198
198
|
still unset: Verify 1 says which. OpenSEO shows one message for a
|
|
199
199
|
missing key and a rejected one alike: "DataForSEO isn't set up on your
|
|
200
|
-
|
|
200
|
+
EveryApp gateway yet, or rejected the key it holds. An owner or admin
|
|
201
201
|
sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." Tell them apart with
|
|
202
202
|
`pnpm everyapp secret list --json`: `DATAFORSEO_API_KEY` absent from
|
|
203
203
|
`data.gateway.names` means step 4 did not land (behind the Gateway's
|
|
@@ -227,7 +227,7 @@ value is set.
|
|
|
227
227
|
| `SECRET_SEVERAL_NAMES` | `--from-env` or a pipe with more than one name | One name per call |
|
|
228
228
|
| `SECRET_EMPTY` | The `--from-env` variable is empty or not exported in that shell | The hidden read (README's third way) in that same terminal first, then rerun |
|
|
229
229
|
| A browser opening OpenSEO is sent to `https://<gateway host>/?setup=openseo`; any other request gets 503 with the body `{"error":"app_needs_setup","app":"openseo"}` | The Gateway's setup gate: a required setting of OpenSEO is still unset (this key or the OpenRouter one; Context is optional) | `data.app.setup.command`, `pnpm everyapp configure openseo` (step 4), then Verify 1 |
|
|
230
|
-
| `integration_not_configured` (503, header `x-everyapp-error`) | The Gateway holds no `DATAFORSEO_API_KEY`; OpenSEO shows it as "DataForSEO isn't set up on your
|
|
230
|
+
| `integration_not_configured` (503, header `x-everyapp-error`) | The Gateway holds no `DATAFORSEO_API_KEY`; OpenSEO shows it as "DataForSEO isn't set up on your EveryApp gateway yet, or rejected the key it holds. An owner or admin sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." (behind the setup gate the app doesn't open in this state) | Steps 4 and 5; check the name's spelling in `data.gateway.names` |
|
|
231
231
|
| `capability_not_requested` (403) | The deployed manifest does not list `capabilities.dataforseo` | OpenSEO's does; a fork must add it to `everyapp.config.ts` and deploy again |
|
|
232
232
|
| `integration_path_not_allowed` (403) | The app called an account-wide path (`appendix/user_data`, `appendix/webhook_resend`, `tasks_ready`, `id_list`, `errors`) | By design: one key serves every app. Check balance and limits from the terminal (Verify 2) |
|
|
233
233
|
| `integration_method_not_allowed` (405) | A method other than GET or POST | DataForSEO reads with GET and POST only |
|
|
@@ -278,7 +278,7 @@ recipient address (20 on a hosted Gateway) and 5,000 a day per Gateway.
|
|
|
278
278
|
is `false`).
|
|
279
279
|
3. The provider alone, Cloudflare, with the person's yes (one counted
|
|
280
280
|
email, unless `to` is a verified destination address):
|
|
281
|
-
`cf email-sending send --body '{"from":"noreply@<your domain>","to":"<address>","subject":"
|
|
281
|
+
`cf email-sending send --body '{"from":"noreply@<your domain>","to":"<address>","subject":"EveryApp test","text":"It works."}'`
|
|
282
282
|
sends without the Gateway. It arriving means the provider is done and a
|
|
283
283
|
failed invite is the Gateway's side (its settings; the logs below). A
|
|
284
284
|
refusal is the REST API's numeric code (`10203` sending disabled,
|