@every-app/sdk 0.3.0-rc.1 → 0.3.0-rc.11

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/LICENSE +21 -0
  2. package/README.md +62 -64
  3. package/dist/cloudflare/catalogSettings.generated.d.ts +84 -0
  4. package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
  5. package/dist/cloudflare/catalogSettings.generated.js +175 -0
  6. package/dist/cloudflare/index.d.ts +3 -2
  7. package/dist/cloudflare/index.d.ts.map +1 -1
  8. package/dist/cloudflare/index.js +4 -1
  9. package/dist/cloudflare/sentinel.d.ts +60 -2
  10. package/dist/cloudflare/sentinel.d.ts.map +1 -1
  11. package/dist/cloudflare/sentinel.js +37 -2
  12. package/dist/cloudflare/worker.d.ts +391 -63
  13. package/dist/cloudflare/worker.d.ts.map +1 -1
  14. package/dist/cloudflare/worker.js +394 -108
  15. package/dist/dev/index.d.ts +1 -1
  16. package/dist/dev/index.d.ts.map +1 -1
  17. package/dist/dev/index.js +1 -1
  18. package/dist/dev/keypair.d.ts +2 -0
  19. package/dist/dev/keypair.d.ts.map +1 -1
  20. package/dist/dev/keypair.js +2 -0
  21. package/dist/index.d.ts +6 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +5 -0
  24. package/dist/internal/aiDefault.d.ts +205 -0
  25. package/dist/internal/aiDefault.d.ts.map +1 -0
  26. package/dist/internal/aiDefault.js +335 -0
  27. package/dist/internal/aiProviders.d.ts +7 -2
  28. package/dist/internal/aiProviders.d.ts.map +1 -1
  29. package/dist/internal/aiProviders.js +19 -6
  30. package/dist/internal/appSettings.d.ts +82 -4
  31. package/dist/internal/appSettings.d.ts.map +1 -1
  32. package/dist/internal/appSettings.js +204 -29
  33. package/dist/internal/audit.d.ts +108 -0
  34. package/dist/internal/audit.d.ts.map +1 -0
  35. package/dist/internal/audit.js +235 -0
  36. package/dist/internal/capabilities.d.ts +2261 -815
  37. package/dist/internal/capabilities.d.ts.map +1 -1
  38. package/dist/internal/capabilities.js +122 -1034
  39. package/dist/internal/catalog/commerce.d.ts +394 -0
  40. package/dist/internal/catalog/commerce.d.ts.map +1 -0
  41. package/dist/internal/catalog/commerce.js +477 -0
  42. package/dist/internal/catalog/comms.d.ts +375 -0
  43. package/dist/internal/catalog/comms.d.ts.map +1 -0
  44. package/dist/internal/catalog/comms.js +520 -0
  45. package/dist/internal/catalog/index.d.ts +2008 -0
  46. package/dist/internal/catalog/index.d.ts.map +1 -0
  47. package/dist/internal/catalog/index.js +19 -0
  48. package/dist/internal/catalog/inference.d.ts +393 -0
  49. package/dist/internal/catalog/inference.d.ts.map +1 -0
  50. package/dist/internal/catalog/inference.js +460 -0
  51. package/dist/internal/catalog/infra.d.ts +458 -0
  52. package/dist/internal/catalog/infra.d.ts.map +1 -0
  53. package/dist/internal/catalog/infra.js +608 -0
  54. package/dist/internal/catalog/search.d.ts +400 -0
  55. package/dist/internal/catalog/search.d.ts.map +1 -0
  56. package/dist/internal/catalog/search.js +463 -0
  57. package/dist/internal/devProviders.d.ts +2 -2
  58. package/dist/internal/devProviders.d.ts.map +1 -1
  59. package/dist/internal/devProviders.js +19 -1
  60. package/dist/internal/events.d.ts +65 -3
  61. package/dist/internal/events.d.ts.map +1 -1
  62. package/dist/internal/events.js +110 -5
  63. package/dist/internal/gatewaySettings.d.ts +3 -2
  64. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  65. package/dist/internal/gatewaySettings.js +12 -6
  66. package/dist/internal/identity.d.ts +41 -4
  67. package/dist/internal/identity.d.ts.map +1 -1
  68. package/dist/internal/identity.js +48 -10
  69. package/dist/internal/index.d.ts +6 -1
  70. package/dist/internal/index.d.ts.map +1 -1
  71. package/dist/internal/index.js +6 -1
  72. package/dist/internal/members.d.ts +3 -2
  73. package/dist/internal/members.d.ts.map +1 -1
  74. package/dist/internal/oauthBuiltIns.d.ts +101 -0
  75. package/dist/internal/oauthBuiltIns.d.ts.map +1 -0
  76. package/dist/internal/oauthBuiltIns.js +122 -0
  77. package/dist/internal/oauthClient.d.ts +8 -50
  78. package/dist/internal/oauthClient.d.ts.map +1 -1
  79. package/dist/internal/oauthClient.js +2 -177
  80. package/dist/internal/oauthPresets.d.ts +32 -0
  81. package/dist/internal/oauthPresets.d.ts.map +1 -0
  82. package/dist/internal/oauthPresets.js +253 -0
  83. package/dist/internal/oauthScopes.d.ts +43 -100
  84. package/dist/internal/oauthScopes.d.ts.map +1 -1
  85. package/dist/internal/oauthScopes.js +83 -382
  86. package/dist/internal/oauthSpec.d.ts +103 -0
  87. package/dist/internal/oauthSpec.d.ts.map +1 -0
  88. package/dist/internal/oauthSpec.js +520 -0
  89. package/dist/internal/protocol.d.ts +97 -5
  90. package/dist/internal/protocol.d.ts.map +1 -1
  91. package/dist/internal/protocol.js +89 -1
  92. package/dist/internal/providerRegistry.d.ts +17 -6
  93. package/dist/internal/providerRegistry.d.ts.map +1 -1
  94. package/dist/internal/providerRegistry.js +71 -8
  95. package/dist/internal/providerSpec.d.ts +18 -7
  96. package/dist/internal/providerSpec.d.ts.map +1 -1
  97. package/dist/internal/providerSpec.js +420 -34
  98. package/dist/internal/remoteMcp.d.ts.map +1 -1
  99. package/dist/internal/remoteMcp.js +1 -5
  100. package/dist/internal/signInProviders.d.ts +104 -0
  101. package/dist/internal/signInProviders.d.ts.map +1 -0
  102. package/dist/internal/signInProviders.js +245 -0
  103. package/dist/internal/tools.d.ts +5 -4
  104. package/dist/internal/tools.d.ts.map +1 -1
  105. package/dist/internal/tools.js +4 -3
  106. package/dist/manifest.d.ts +267 -0
  107. package/dist/manifest.d.ts.map +1 -0
  108. package/dist/manifest.js +30 -0
  109. package/dist/server/ai.d.ts +111 -0
  110. package/dist/server/ai.d.ts.map +1 -0
  111. package/dist/server/ai.js +145 -0
  112. package/dist/server/aiBinding.d.ts +68 -0
  113. package/dist/server/aiBinding.d.ts.map +1 -0
  114. package/dist/server/aiBinding.js +294 -0
  115. package/dist/server/attribution.d.ts +18 -5
  116. package/dist/server/attribution.d.ts.map +1 -1
  117. package/dist/server/attribution.js +19 -5
  118. package/dist/server/connectUrl.d.ts +16 -0
  119. package/dist/server/connectUrl.d.ts.map +1 -0
  120. package/dist/server/connectUrl.js +40 -0
  121. package/dist/server/context.d.ts +68 -0
  122. package/dist/server/context.d.ts.map +1 -0
  123. package/dist/server/context.js +117 -0
  124. package/dist/server/emailBinding.d.ts +72 -0
  125. package/dist/server/emailBinding.d.ts.map +1 -0
  126. package/dist/server/emailBinding.js +124 -0
  127. package/dist/server/emit.d.ts +1 -1
  128. package/dist/server/emit.d.ts.map +1 -1
  129. package/dist/server/emit.js +7 -7
  130. package/dist/server/env.d.ts +44 -0
  131. package/dist/server/env.d.ts.map +1 -0
  132. package/dist/server/env.js +1 -0
  133. package/dist/server/eventsEndpoint.d.ts +48 -0
  134. package/dist/server/eventsEndpoint.d.ts.map +1 -0
  135. package/dist/server/eventsEndpoint.js +145 -0
  136. package/dist/server/everyApp.d.ts +40 -11
  137. package/dist/server/everyApp.d.ts.map +1 -1
  138. package/dist/server/everyApp.js +116 -6
  139. package/dist/server/fromMcpTools.d.ts +89 -0
  140. package/dist/server/fromMcpTools.d.ts.map +1 -0
  141. package/dist/server/fromMcpTools.js +192 -0
  142. package/dist/server/gatewayFetch.d.ts +42 -18
  143. package/dist/server/gatewayFetch.d.ts.map +1 -1
  144. package/dist/server/gatewayFetch.js +138 -19
  145. package/dist/server/gatewayUrl.d.ts +13 -0
  146. package/dist/server/gatewayUrl.d.ts.map +1 -0
  147. package/dist/server/gatewayUrl.js +34 -0
  148. package/dist/server/index.d.ts +16 -4
  149. package/dist/server/index.d.ts.map +1 -1
  150. package/dist/server/index.js +13 -3
  151. package/dist/server/integrationFetch.d.ts +92 -73
  152. package/dist/server/integrationFetch.d.ts.map +1 -1
  153. package/dist/server/integrationFetch.js +206 -319
  154. package/dist/server/listMembers.d.ts +1 -1
  155. package/dist/server/listMembers.d.ts.map +1 -1
  156. package/dist/server/listMembers.js +8 -11
  157. package/dist/server/refusal.d.ts +55 -0
  158. package/dist/server/refusal.d.ts.map +1 -0
  159. package/dist/server/refusal.js +193 -0
  160. package/dist/server/sendEmail.d.ts +1 -1
  161. package/dist/server/sendEmail.d.ts.map +1 -1
  162. package/dist/server/sendEmail.js +7 -7
  163. package/dist/server/tools.d.ts +7 -2
  164. package/dist/server/tools.d.ts.map +1 -1
  165. package/dist/server/tools.js +2 -1
  166. package/dist/server/toolsEndpoint.d.ts +3 -1
  167. package/dist/server/toolsEndpoint.d.ts.map +1 -1
  168. package/dist/server/toolsEndpoint.js +19 -7
  169. package/dist/server/webhooks.d.ts +56 -0
  170. package/dist/server/webhooks.d.ts.map +1 -0
  171. package/dist/server/webhooks.js +205 -0
  172. package/dist/tanstack-start/index.d.ts +9 -0
  173. package/dist/tanstack-start/index.d.ts.map +1 -0
  174. package/dist/tanstack-start/index.js +29 -0
  175. package/dist/testing/index.d.ts +93 -0
  176. package/dist/testing/index.d.ts.map +1 -0
  177. package/dist/testing/index.js +156 -0
  178. package/package.json +35 -14
  179. package/dist/server/authorizeUrl.d.ts +0 -43
  180. package/dist/server/authorizeUrl.d.ts.map +0 -1
  181. package/dist/server/authorizeUrl.js +0 -110
@@ -0,0 +1,520 @@
1
+ const SEPARATORS = ["space", "comma"];
2
+ const INJECTIONS = ["form", "basic"];
3
+ const HEADER_NAME = /^[A-Za-z0-9-]{1,64}$/;
4
+ /** Headers the Gateway sets on the token request itself, or never sends. */
5
+ const RESERVED_TOKEN_HEADERS = new Set([
6
+ "authorization",
7
+ "content-type",
8
+ "content-length",
9
+ "host",
10
+ "cookie",
11
+ ]);
12
+ /**
13
+ * Authorize parameters the Gateway sets itself, and
14
+ * `include_granted_scopes`, which would widen a token to every scope the
15
+ * person ever granted the client (the mint check would refuse it anyway).
16
+ */
17
+ const RESERVED_AUTHORIZE_PARAMS = [
18
+ "client_id",
19
+ "redirect_uri",
20
+ "response_type",
21
+ "scope",
22
+ "state",
23
+ "code_challenge",
24
+ "code_challenge_method",
25
+ "include_granted_scopes",
26
+ ];
27
+ const PARAM_NAME = /^[A-Za-z0-9_.-]{1,64}$/;
28
+ /** A dot path into a JSON answer, at most four segments. */
29
+ const DOT_PATH = /^[A-Za-z_][A-Za-z0-9_]{0,63}(?:\.[A-Za-z_][A-Za-z0-9_]{0,63}){0,3}$/;
30
+ const UNPRINTABLE = /[\p{Cc}\p{Cf}]/u;
31
+ const MAX_SCOPES = 40;
32
+ const MAX_PARAMS = 10;
33
+ /** The fields of `auth` when its kind is `oauth2-user`. */
34
+ export const OAUTH_AUTH_FIELDS = [
35
+ "kind",
36
+ "authorizeUrl",
37
+ "tokenUrl",
38
+ "revokeUrl",
39
+ "revokeMethod",
40
+ "clientId",
41
+ "clientSecret",
42
+ "inject",
43
+ "pkce",
44
+ "scopeSeparator",
45
+ "scopePrefix",
46
+ "authorizeParams",
47
+ "tokenHeaders",
48
+ "identity",
49
+ "tolerated",
50
+ ];
51
+ /**
52
+ * Fields an `oauth2-user` spec had before scopes moved to each app's
53
+ * `everyapp.config.ts`, each with what to write instead: refused with
54
+ * that message rather than "unknown field".
55
+ */
56
+ export const REMOVED_OAUTH_FIELDS = {
57
+ scopeSets: "scope sets are gone: each app lists the provider's own scopes in its everyapp.config.ts. Remove scopeSets.",
58
+ alwaysRequested: "alwaysRequested is gone: put scopes every connect needs (a refresh token's, the identity URL's) in auth.identity.scopes.",
59
+ tolerated: "tolerated moved into auth: auth.tolerated lists the scopes a token may carry beyond what was asked.",
60
+ access: "access is gone for an OAuth provider: the scopes an app lists are the grant, for every method.",
61
+ readMethods: "readMethods is gone for an OAuth provider: the scopes an app lists are the grant, for every method.",
62
+ writeMethods: "writeMethods is gone for an OAuth provider: the scopes an app lists are the grant, for every method.",
63
+ };
64
+ /** `auth` fields renamed, each with its new name. */
65
+ const RENAMED_AUTH_FIELDS = {
66
+ scopeDialect: "renamed auth.scopeSeparator: space or comma (a provider's prefix goes in auth.scopePrefix)",
67
+ };
68
+ function isRecord(value) {
69
+ return typeof value === "object" && value !== null && !Array.isArray(value);
70
+ }
71
+ function printable(value, path, problems, max) {
72
+ if (value === undefined)
73
+ return undefined;
74
+ if (typeof value !== "string" || value.length === 0 || value.length > max) {
75
+ problems.push({
76
+ path,
77
+ message: `must be a string of 1 to ${max} characters`,
78
+ });
79
+ return undefined;
80
+ }
81
+ if (UNPRINTABLE.test(value)) {
82
+ problems.push({ path, message: "holds a control character" });
83
+ return undefined;
84
+ }
85
+ return value;
86
+ }
87
+ /**
88
+ * An https URL with a host a row may name (the caller runs the gateway's
89
+ * host rules on `host` afterwards). `{client_id}` may stand in a path
90
+ * (GitHub's revoke URL names the client there).
91
+ */
92
+ function endpoint(value, path, problems, { query }) {
93
+ if (value === undefined)
94
+ return undefined;
95
+ const url = printable(value, path, problems, 500);
96
+ if (url === undefined)
97
+ return undefined;
98
+ let parsed;
99
+ try {
100
+ parsed = new URL(url);
101
+ }
102
+ catch {
103
+ problems.push({ path, message: "must be an https URL" });
104
+ return undefined;
105
+ }
106
+ if (parsed.protocol !== "https:" ||
107
+ parsed.username ||
108
+ parsed.password ||
109
+ parsed.port ||
110
+ parsed.hash) {
111
+ problems.push({
112
+ path,
113
+ message: "must be an https URL with no port, credentials or fragment",
114
+ });
115
+ return undefined;
116
+ }
117
+ if (!query && parsed.search) {
118
+ problems.push({ path, message: "may not carry a query string" });
119
+ return undefined;
120
+ }
121
+ return { url, host: parsed.hostname.toLowerCase() };
122
+ }
123
+ function settingName(value, path, problems) {
124
+ if (typeof value !== "string" || !/^[A-Z][A-Z0-9_]{2,63}$/.test(value)) {
125
+ problems.push({
126
+ path,
127
+ message: "must name a Gateway setting: upper case letters, digits and underscores, a letter first, 3 to 64 characters (GITHUB_CLIENT_ID)",
128
+ });
129
+ return undefined;
130
+ }
131
+ return value;
132
+ }
133
+ function stringMap(value, path, problems, each) {
134
+ if (value === undefined)
135
+ return undefined;
136
+ if (!isRecord(value)) {
137
+ problems.push({ path, message: "must be an object of strings" });
138
+ return undefined;
139
+ }
140
+ const entries = Object.entries(value);
141
+ if (entries.length > MAX_PARAMS) {
142
+ problems.push({ path, message: `at most ${MAX_PARAMS} entries` });
143
+ return undefined;
144
+ }
145
+ const out = {};
146
+ for (const [name, raw] of entries) {
147
+ const at = `${path}.${name}`;
148
+ each(name, at);
149
+ const text = printable(raw, at, problems, 200);
150
+ if (text !== undefined)
151
+ out[name] = text;
152
+ }
153
+ return out;
154
+ }
155
+ /** A list of provider scope strings: each one scope, with no space or comma, each once. */
156
+ export function scopeList(value, path, problems) {
157
+ if (value === undefined)
158
+ return undefined;
159
+ if (!Array.isArray(value) || value.length > MAX_SCOPES) {
160
+ problems.push({
161
+ path,
162
+ message: `must be a list of at most ${MAX_SCOPES} scopes`,
163
+ });
164
+ return undefined;
165
+ }
166
+ const out = [];
167
+ value.forEach((item, index) => {
168
+ const scope = printable(item, `${path}.${index}`, problems, 200);
169
+ if (scope === undefined)
170
+ return;
171
+ if (/\s/.test(scope) || scope.includes(",")) {
172
+ problems.push({
173
+ path: `${path}.${index}`,
174
+ message: "one scope, with no space or comma",
175
+ });
176
+ return;
177
+ }
178
+ out.push(scope);
179
+ });
180
+ if (new Set(out).size !== out.length) {
181
+ problems.push({ path, message: "lists a scope twice" });
182
+ }
183
+ return out;
184
+ }
185
+ function identity(value, hosts, problems) {
186
+ if (value === undefined)
187
+ return undefined;
188
+ if (!isRecord(value)) {
189
+ problems.push({
190
+ path: "auth.identity",
191
+ message: "must be { url, label, id?, scopes? }",
192
+ });
193
+ return undefined;
194
+ }
195
+ for (const field of Object.keys(value)) {
196
+ if (!["url", "label", "id", "scopes"].includes(field)) {
197
+ problems.push({
198
+ path: `auth.identity.${field}`,
199
+ message: "unknown field",
200
+ });
201
+ }
202
+ }
203
+ const url = endpoint(value["url"], "auth.identity.url", problems, {
204
+ query: true,
205
+ });
206
+ if (value["url"] === undefined) {
207
+ problems.push({ path: "auth.identity.url", message: "required" });
208
+ }
209
+ else if (url && !hosts.includes(url.host)) {
210
+ problems.push({
211
+ path: "auth.identity.url",
212
+ message: "must be on one of hosts (the token is sent there)",
213
+ });
214
+ }
215
+ const label = printable(value["label"], "auth.identity.label", problems, 128);
216
+ if (label !== undefined && !DOT_PATH.test(label)) {
217
+ problems.push({
218
+ path: "auth.identity.label",
219
+ message: "must be a JSON field name, or a dotted path to a nested one",
220
+ });
221
+ }
222
+ if (value["label"] === undefined) {
223
+ problems.push({ path: "auth.identity.label", message: "required" });
224
+ }
225
+ const id = printable(value["id"], "auth.identity.id", problems, 128);
226
+ if (id !== undefined && !DOT_PATH.test(id)) {
227
+ problems.push({
228
+ path: "auth.identity.id",
229
+ message: "must be a JSON field name, or a dotted path to a nested one",
230
+ });
231
+ }
232
+ const scopes = scopeList(value["scopes"], "auth.identity.scopes", problems);
233
+ if (!url || label === undefined)
234
+ return undefined;
235
+ return {
236
+ identity: { url: url.url, label, ...(id === undefined ? {} : { id }) },
237
+ scopes: scopes ?? [],
238
+ };
239
+ }
240
+ /**
241
+ * Parse the OAuth half of a spec whose `auth.kind` is `oauth2-user`, with
242
+ * the common fields already parsed. Every problem is reported; on success
243
+ * the capability with its `flow`.
244
+ */
245
+ export function parseOAuthUserSpec(input, common, problems) {
246
+ const auth = input["auth"];
247
+ for (const field of Object.keys(auth)) {
248
+ const renamed = RENAMED_AUTH_FIELDS[field];
249
+ if (renamed) {
250
+ problems.push({ path: `auth.${field}`, message: renamed });
251
+ }
252
+ else if (!OAUTH_AUTH_FIELDS.includes(field)) {
253
+ problems.push({
254
+ path: `auth.${field}`,
255
+ message: "not a field of auth.kind oauth2-user",
256
+ });
257
+ }
258
+ }
259
+ const authorize = endpoint(auth["authorizeUrl"], "auth.authorizeUrl", problems, { query: true });
260
+ if (auth["authorizeUrl"] === undefined) {
261
+ problems.push({ path: "auth.authorizeUrl", message: "required" });
262
+ }
263
+ const token = endpoint(auth["tokenUrl"], "auth.tokenUrl", problems, {
264
+ query: false,
265
+ });
266
+ if (auth["tokenUrl"] === undefined) {
267
+ problems.push({ path: "auth.tokenUrl", message: "required" });
268
+ }
269
+ const revoke = endpoint(auth["revokeUrl"], "auth.revokeUrl", problems, {
270
+ query: false,
271
+ });
272
+ const revokeMethod = auth["revokeMethod"];
273
+ if (revokeMethod !== undefined &&
274
+ revokeMethod !== "POST" &&
275
+ revokeMethod !== "DELETE") {
276
+ problems.push({
277
+ path: "auth.revokeMethod",
278
+ message: "must be POST or DELETE",
279
+ });
280
+ }
281
+ if (revokeMethod !== undefined && revoke === undefined) {
282
+ problems.push({ path: "auth.revokeMethod", message: "needs a revokeUrl" });
283
+ }
284
+ const clientId = settingName(auth["clientId"], "auth.clientId", problems);
285
+ const clientSecret = auth["clientSecret"] === undefined
286
+ ? undefined
287
+ : settingName(auth["clientSecret"], "auth.clientSecret", problems);
288
+ if (clientSecret !== undefined && clientSecret === clientId) {
289
+ problems.push({
290
+ path: "auth.clientSecret",
291
+ message: "must be another setting than auth.clientId",
292
+ });
293
+ }
294
+ const inject = auth["inject"] ?? "form";
295
+ if (!INJECTIONS.includes(inject)) {
296
+ problems.push({
297
+ path: "auth.inject",
298
+ message: "must be form (the client in the body) or basic (HTTP Basic)",
299
+ });
300
+ }
301
+ const pkce = auth["pkce"] ?? true;
302
+ if (typeof pkce !== "boolean") {
303
+ problems.push({ path: "auth.pkce", message: "must be true or false" });
304
+ }
305
+ const separator = auth["scopeSeparator"] ?? "space";
306
+ if (!SEPARATORS.includes(separator)) {
307
+ problems.push({
308
+ path: "auth.scopeSeparator",
309
+ message: `must be one of ${SEPARATORS.join(", ")}`,
310
+ });
311
+ }
312
+ const prefix = endpoint(auth["scopePrefix"], "auth.scopePrefix", problems, {
313
+ query: false,
314
+ });
315
+ if (prefix && !prefix.url.endsWith("/")) {
316
+ problems.push({
317
+ path: "auth.scopePrefix",
318
+ message: "must end in / (https://www.googleapis.com/auth/): an app's scope is joined onto it",
319
+ });
320
+ }
321
+ const authorizeParams = stringMap(auth["authorizeParams"], "auth.authorizeParams", problems, (name, at) => {
322
+ if (!PARAM_NAME.test(name)) {
323
+ problems.push({ path: at, message: "must be a query parameter name" });
324
+ }
325
+ if (RESERVED_AUTHORIZE_PARAMS.includes(name)) {
326
+ problems.push({
327
+ path: at,
328
+ message: name === "include_granted_scopes"
329
+ ? "include_granted_scopes would widen the token beyond the app's scopes; the Gateway never sends it"
330
+ : `${name} is a parameter the Gateway sets itself`,
331
+ });
332
+ }
333
+ });
334
+ const tokenHeaders = stringMap(auth["tokenHeaders"], "auth.tokenHeaders", problems, (name, at) => {
335
+ if (!HEADER_NAME.test(name)) {
336
+ problems.push({ path: at, message: "must be a header name" });
337
+ }
338
+ else if (RESERVED_TOKEN_HEADERS.has(name.toLowerCase()) ||
339
+ name.toLowerCase().startsWith("x-everyapp-")) {
340
+ problems.push({
341
+ path: at,
342
+ message: `${name} is a header the Gateway sets itself`,
343
+ });
344
+ }
345
+ });
346
+ const parsedIdentity = identity(auth["identity"], common.hosts, problems);
347
+ const tolerated = scopeList(auth["tolerated"], "auth.tolerated", problems);
348
+ if (!authorize ||
349
+ !token ||
350
+ clientId === undefined ||
351
+ typeof pkce !== "boolean" ||
352
+ !SEPARATORS.includes(separator) ||
353
+ !INJECTIONS.includes(inject)) {
354
+ return undefined;
355
+ }
356
+ const flow = {
357
+ authorizeUrl: authorize.url,
358
+ tokenUrl: token.url,
359
+ ...(revoke === undefined ? {} : { revokeUrl: revoke.url }),
360
+ ...(revokeMethod === undefined
361
+ ? {}
362
+ : { revokeMethod: revokeMethod }),
363
+ inject: inject,
364
+ pkce,
365
+ scopeSeparator: separator,
366
+ ...(prefix === undefined ? {} : { scopePrefix: prefix.url }),
367
+ ...(authorizeParams === undefined ? {} : { authorizeParams }),
368
+ ...(tokenHeaders === undefined ? {} : { tokenHeaders }),
369
+ ...(parsedIdentity === undefined
370
+ ? {}
371
+ : { identity: parsedIdentity.identity }),
372
+ identityScopes: parsedIdentity?.scopes ?? [],
373
+ tolerated: tolerated ?? [],
374
+ };
375
+ const capability = oauthCapabilityOf(common, {
376
+ clientId,
377
+ ...(clientSecret === undefined ? {} : { clientSecret }),
378
+ }, flow);
379
+ const hosts = [...common.hosts];
380
+ for (const host of [authorize.host, token.host, revoke?.host]) {
381
+ if (host && !hosts.includes(host))
382
+ hosts.push(host);
383
+ }
384
+ return { capability, hosts };
385
+ }
386
+ function oauthCapabilityOf(common, secrets, flow) {
387
+ return {
388
+ mode: "oauth_client",
389
+ label: common.name,
390
+ secrets,
391
+ console: common.docs,
392
+ api: [...common.hosts],
393
+ flow,
394
+ ...(common.description === undefined
395
+ ? {}
396
+ : { description: common.description }),
397
+ ...(common.reference === undefined ? {} : { reference: common.reference }),
398
+ ...(common.hint === undefined ? {} : { hint: common.hint }),
399
+ ...(common.example === undefined ? {} : { example: common.example }),
400
+ ...(common.tags === undefined ? {} : { tags: common.tags }),
401
+ };
402
+ }
403
+ /**
404
+ * A spec already known to be good (a built-in, checked by its test) as
405
+ * its capability, without the parse: what the catalog runs at load, where
406
+ * calling the full parser would be an import cycle.
407
+ */
408
+ export function oauthCapabilityFromSpec(spec) {
409
+ const { auth } = spec;
410
+ const flow = {
411
+ authorizeUrl: auth.authorizeUrl,
412
+ tokenUrl: auth.tokenUrl,
413
+ ...(auth.revokeUrl === undefined ? {} : { revokeUrl: auth.revokeUrl }),
414
+ ...(auth.revokeMethod === undefined
415
+ ? {}
416
+ : { revokeMethod: auth.revokeMethod }),
417
+ inject: auth.inject ?? "form",
418
+ pkce: auth.pkce ?? true,
419
+ scopeSeparator: auth.scopeSeparator ?? "space",
420
+ ...(auth.scopePrefix === undefined
421
+ ? {}
422
+ : { scopePrefix: auth.scopePrefix }),
423
+ ...(auth.authorizeParams === undefined
424
+ ? {}
425
+ : { authorizeParams: { ...auth.authorizeParams } }),
426
+ ...(auth.tokenHeaders === undefined
427
+ ? {}
428
+ : { tokenHeaders: { ...auth.tokenHeaders } }),
429
+ ...(auth.identity === undefined
430
+ ? {}
431
+ : {
432
+ identity: {
433
+ url: auth.identity.url,
434
+ label: auth.identity.label,
435
+ ...(auth.identity.id === undefined ? {} : { id: auth.identity.id }),
436
+ },
437
+ }),
438
+ identityScopes: [...(auth.identity?.scopes ?? [])],
439
+ tolerated: [...(auth.tolerated ?? [])],
440
+ };
441
+ return oauthCapabilityOf({
442
+ id: spec.id,
443
+ name: spec.name,
444
+ docs: spec.docs,
445
+ hosts: spec.hosts,
446
+ ...(spec.description === undefined
447
+ ? {}
448
+ : { description: spec.description }),
449
+ ...(spec.api === undefined ? {} : { reference: spec.api }),
450
+ ...(spec.hint === undefined ? {} : { hint: spec.hint }),
451
+ ...(spec.example === undefined ? {} : { example: spec.example }),
452
+ ...(spec.tags === undefined ? {} : { tags: spec.tags }),
453
+ }, {
454
+ clientId: auth.clientId,
455
+ ...(auth.clientSecret === undefined
456
+ ? {}
457
+ : { clientSecret: auth.clientSecret }),
458
+ }, flow);
459
+ }
460
+ /**
461
+ * The JSON spec of an OAuth capability (`provider show`, a preset's round
462
+ * trip): `parseProviderSpec` of it gives the capability back.
463
+ */
464
+ export function oauthSpecOf(id, capability) {
465
+ const { flow } = capability;
466
+ const identity = flow.identity === undefined
467
+ ? undefined
468
+ : {
469
+ ...flow.identity,
470
+ ...(flow.identityScopes.length === 0
471
+ ? {}
472
+ : { scopes: [...flow.identityScopes] }),
473
+ };
474
+ return {
475
+ id,
476
+ name: capability.label,
477
+ ...(capability.description === undefined
478
+ ? {}
479
+ : { description: capability.description }),
480
+ docs: capability.console,
481
+ ...(capability.reference === undefined
482
+ ? {}
483
+ : { api: capability.reference }),
484
+ hosts: [...capability.api],
485
+ auth: {
486
+ kind: "oauth2-user",
487
+ authorizeUrl: flow.authorizeUrl,
488
+ tokenUrl: flow.tokenUrl,
489
+ ...(flow.revokeUrl === undefined ? {} : { revokeUrl: flow.revokeUrl }),
490
+ ...(flow.revokeMethod === undefined
491
+ ? {}
492
+ : { revokeMethod: flow.revokeMethod }),
493
+ clientId: capability.secrets.clientId,
494
+ ...(capability.secrets.clientSecret === undefined
495
+ ? {}
496
+ : { clientSecret: capability.secrets.clientSecret }),
497
+ inject: flow.inject,
498
+ pkce: flow.pkce,
499
+ scopeSeparator: flow.scopeSeparator,
500
+ ...(flow.scopePrefix === undefined
501
+ ? {}
502
+ : { scopePrefix: flow.scopePrefix }),
503
+ ...(flow.authorizeParams === undefined
504
+ ? {}
505
+ : { authorizeParams: { ...flow.authorizeParams } }),
506
+ ...(flow.tokenHeaders === undefined
507
+ ? {}
508
+ : { tokenHeaders: { ...flow.tokenHeaders } }),
509
+ ...(identity === undefined ? {} : { identity }),
510
+ ...(flow.tolerated.length === 0
511
+ ? {}
512
+ : { tolerated: [...flow.tolerated] }),
513
+ },
514
+ ...(capability.hint === undefined ? {} : { hint: capability.hint }),
515
+ ...(capability.example === undefined
516
+ ? {}
517
+ : { example: capability.example }),
518
+ ...(capability.tags === undefined ? {} : { tags: [...capability.tags] }),
519
+ };
520
+ }
@@ -39,6 +39,15 @@ export declare const GATEWAY_ERROR_HEADER = "x-everyapp-error";
39
39
  * from what browsers send.
40
40
  */
41
41
  export declare const ORG_HEADER = "x-everyapp-org";
42
+ /**
43
+ * On background work's call to an OAuth provider's API: the member whose
44
+ * connection to this app the call uses. Set by `for: { background: true,
45
+ * userId }`; the gateway reads it on the held-token branch only, for an
46
+ * organization's own app, and the perimeter strips it, with every
47
+ * `x-everyapp-*`, from browsers. It addresses a connection; it is not a
48
+ * permission: connecting in the app already lets the app use it.
49
+ */
50
+ export declare const USER_HEADER = "x-everyapp-user";
42
51
  /** 120-second lifetime — caps revocation staleness while surviving clock skew. */
43
52
  export declare const IDENTITY_TTL_SECONDS = 120;
44
53
  /** System tokens are minted per call and live at most a minute. */
@@ -63,14 +72,48 @@ export declare const ISSUER_ENV = "EVERYAPP_IDENTITY_ISSUER";
63
72
  export declare const SHARED_ENV = "EVERYAPP_SHARED";
64
73
  /** When set to `1` or `true` in the worker env, the dev identity kid is accepted. */
65
74
  export declare const DEV_ENV = "EVERYAPP_DEV";
75
+ /**
76
+ * The organization an app's Worker is pinned to (a secret the deploy sends;
77
+ * in development the stub gateway's organization). `everyApp()` refuses an
78
+ * identity for any other organization; absent on a shared app's Workers.
79
+ */
80
+ export declare const ORG_ID_ENV = "EVERY_APP_ORG_ID";
81
+ /**
82
+ * The organization the dev gateway's seeded user belongs to. The same value
83
+ * as `@every-app/sdk/dev`'s, kept apart like {@link DEV_ISSUER} (the same
84
+ * test pins the two together).
85
+ */
86
+ export declare const DEV_ORG_ID = "dev-org";
66
87
  /** `sub` claim the gateway mints public-route markers with. */
67
88
  export declare const PUBLIC_MARKER_SUB = "public";
68
89
  /** `sub` claim of gateway system tokens. */
69
90
  export declare const SYSTEM_TOKEN_SUB = "system";
91
+ /**
92
+ * Linked dev (`everyapp dev --gateway`, docs/design/linked-dev.md): the
93
+ * dev Worker's env names the gateway-lite's loopback forwarder and the
94
+ * run's nonce, and the SDK builds a `{ fetch }` in place of the missing
95
+ * `EVERY_APP_GATEWAY` binding from them. Set by `everyAppWorker()` in a
96
+ * development build from the process environment `everyapp dev` starts
97
+ * the app with; never deployed.
98
+ */
99
+ export declare const LINKED_DEV_URL_ENV = "EVERYAPP_LINKED_DEV_URL";
100
+ export declare const LINKED_DEV_NONCE_ENV = "EVERYAPP_LINKED_DEV_NONCE";
101
+ /** The header the forwarder requires, with the run's nonce: no simple cross-origin request can send it. */
102
+ export declare const LINKED_DEV_HEADER = "x-everyapp-linked-dev";
103
+ /** Where the gateway-lite serves the forwarder, under the app's own host. */
104
+ export declare const LINKED_DEV_FORWARD_PATH = "/__everyapp/gateway";
70
105
  export type IdentityChannel = "web" | "api" | "mcp" | "agent";
106
+ /**
107
+ * The channels a tool call arrives on: `mcp` for an AI client's MCP
108
+ * connection, `agent` for an agent principal the Gateway runs a call for.
109
+ * Only the ToolBus mints either with a `tc` claim.
110
+ */
111
+ export declare const TOOL_CALL_CHANNELS: readonly IdentityChannel[];
112
+ /** `act.sub` of an agent principal: `agent:<agt_id>`; `sub` stays its sponsor. */
113
+ export declare const AGENT_ACTOR_PREFIX = "agent:";
71
114
  export type IdentityTokenType = "user" | "public" | "system";
72
115
  /** What a system token may open. One purpose per token. */
73
- export type SystemTokenPurpose = "tools.list";
116
+ export type SystemTokenPurpose = "tools.list" | "events.deliver";
74
117
  /** The actor on whose behalf the request is made (user, MCP client, agent). */
75
118
  export interface IdentityActor {
76
119
  sub: string;
@@ -80,21 +123,59 @@ export interface IdentitySubject {
80
123
  email: string;
81
124
  orgId: string;
82
125
  orgRole: string;
126
+ /**
127
+ * The organization's name as its owners set it (`org_name`), so an app
128
+ * can show "Acme" rather than an id. Left out when the minter has none.
129
+ */
130
+ orgName?: string;
83
131
  }
132
+ /**
133
+ * A member's role in the organization, as the identity token carries it.
134
+ * Owners and admins manage; anything the Gateway might mint that an app
135
+ * doesn't know is read as `member`.
136
+ */
137
+ export type OrgRole = "owner" | "admin" | "member";
138
+ /** `role` as an {@link OrgRole}: `owner` and `admin` as they are, anything else `member`. */
139
+ export declare function toOrgRole(role: unknown): OrgRole;
140
+ /** Whether `user` manages the organization: an owner or an admin. */
141
+ export declare function isAdmin(user: {
142
+ orgRole: OrgRole | string;
143
+ }): boolean;
144
+ /**
145
+ * Whether an agent principal made this call: `chan: "agent"` with
146
+ * `act.sub` `agent:<agt_id>`. `user.id` is still the sponsoring member (the
147
+ * person accountable for the agent), so key rows by it as for anyone;
148
+ * `user.actor.sub` names the agent.
149
+ */
150
+ export declare function isAgentCall(user: {
151
+ channel: IdentityChannel;
152
+ actor: IdentityActor;
153
+ }): boolean;
84
154
  export interface EveryAppUser {
85
155
  /** User id (the `sub` claim). */
86
156
  id: string;
87
157
  email: string;
88
158
  orgId: string;
89
- orgRole: string;
159
+ orgRole: OrgRole;
160
+ /**
161
+ * The organization's display name (the `org_name` claim), as its owners
162
+ * set it: trimmed, at most 64 characters, no control characters. Show
163
+ * it; never key data by it (names change, `orgId` doesn't). Undefined
164
+ * when the token carries none.
165
+ */
166
+ orgName?: string;
90
167
  channel: IdentityChannel;
91
- /** The acting principal (user, or `mcp:<client>` / agent). */
168
+ /**
169
+ * The acting principal: the user id, the credential acting for them
170
+ * (`pat:<id>`, `oauth:<id>`, `access:<email>`), or an agent
171
+ * (`agent:<agt_id>`, on `channel: "agent"`; {@link isAgentCall}).
172
+ */
92
173
  actor: IdentityActor;
93
174
  scopes: string[];
94
175
  jti: string;
95
176
  /**
96
- * Set only on an MCP tool call (`channel: "mcp"`): the one call this
97
- * identity was minted for.
177
+ * Set only on a tool call (`channel: "mcp"` or `"agent"`): the one call
178
+ * this identity was minted for.
98
179
  */
99
180
  toolCall?: ToolCallClaim;
100
181
  }
@@ -103,6 +184,8 @@ export interface IdentityClaims extends JWTPayload {
103
184
  email: string;
104
185
  org_id: string;
105
186
  org_role: string;
187
+ /** The organization's display name; absent when the minter had none. */
188
+ org_name?: string;
106
189
  chan: IdentityChannel;
107
190
  act: IdentityActor;
108
191
  scopes: string[];
@@ -114,6 +197,11 @@ export interface SystemClaims extends JWTPayload {
114
197
  typ: "system";
115
198
  purpose: SystemTokenPurpose;
116
199
  jti: string;
200
+ /**
201
+ * `events.deliver` only: the SHA-256 (base64url) of the request body the
202
+ * token was minted for, so it opens that one delivery and nothing else.
203
+ */
204
+ bh?: string;
117
205
  }
118
206
  export interface PublicMarkerClaims extends JWTPayload {
119
207
  typ: "public";
@@ -185,7 +273,11 @@ export interface MintSystemTokenOptions {
185
273
  now?: number;
186
274
  /** Header kid. Defaults to the production kid; dev mints with the dev kid. */
187
275
  keyId?: string;
276
+ /** `events.deliver`: the body's SHA-256, base64url ({@link bodyHash}). */
277
+ bodyHash?: string;
188
278
  }
279
+ /** SHA-256 of a request body, base64url: what an `events.deliver` token's `bh` names. */
280
+ export declare function bodyHash(body: string): Promise<string>;
189
281
  /**
190
282
  * Mint the gateway's system token for `x-everyapp-system`: no user, one
191
283
  * purpose, one app, one minute. The SDK only accepts it on the endpoint its