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

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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -1
  3. package/dist/cloudflare/catalogSettings.generated.d.ts +81 -0
  4. package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
  5. package/dist/cloudflare/catalogSettings.generated.js +174 -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 +357 -63
  13. package/dist/cloudflare/worker.d.ts.map +1 -1
  14. package/dist/cloudflare/worker.js +363 -104
  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 +204 -0
  25. package/dist/internal/aiDefault.d.ts.map +1 -0
  26. package/dist/internal/aiDefault.js +334 -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 +17 -4
  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 +2268 -438
  37. package/dist/internal/capabilities.d.ts.map +1 -1
  38. package/dist/internal/capabilities.js +128 -539
  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 +1973 -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 +423 -0
  52. package/dist/internal/catalog/infra.d.ts.map +1 -0
  53. package/dist/internal/catalog/infra.js +543 -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 +13 -2
  61. package/dist/internal/events.d.ts.map +1 -1
  62. package/dist/internal/events.js +47 -4
  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/oauthClient.d.ts +10 -12
  75. package/dist/internal/oauthClient.d.ts.map +1 -1
  76. package/dist/internal/oauthClient.js +20 -81
  77. package/dist/internal/oauthPresets.d.ts +33 -0
  78. package/dist/internal/oauthPresets.d.ts.map +1 -0
  79. package/dist/internal/oauthPresets.js +1064 -0
  80. package/dist/internal/oauthScopes.d.ts +56 -1
  81. package/dist/internal/oauthScopes.d.ts.map +1 -1
  82. package/dist/internal/oauthScopes.js +98 -4
  83. package/dist/internal/oauthSpec.d.ts +102 -0
  84. package/dist/internal/oauthSpec.d.ts.map +1 -0
  85. package/dist/internal/oauthSpec.js +789 -0
  86. package/dist/internal/protocol.d.ts +64 -1
  87. package/dist/internal/protocol.d.ts.map +1 -1
  88. package/dist/internal/protocol.js +51 -1
  89. package/dist/internal/providerRegistry.d.ts +77 -6
  90. package/dist/internal/providerRegistry.d.ts.map +1 -1
  91. package/dist/internal/providerRegistry.js +178 -8
  92. package/dist/internal/providerSpec.d.ts +18 -7
  93. package/dist/internal/providerSpec.d.ts.map +1 -1
  94. package/dist/internal/providerSpec.js +442 -34
  95. package/dist/internal/signInProviders.d.ts +104 -0
  96. package/dist/internal/signInProviders.d.ts.map +1 -0
  97. package/dist/internal/signInProviders.js +245 -0
  98. package/dist/manifest.d.ts +238 -0
  99. package/dist/manifest.d.ts.map +1 -0
  100. package/dist/manifest.js +30 -0
  101. package/dist/server/ai.d.ts +111 -0
  102. package/dist/server/ai.d.ts.map +1 -0
  103. package/dist/server/ai.js +145 -0
  104. package/dist/server/aiBinding.d.ts +68 -0
  105. package/dist/server/aiBinding.d.ts.map +1 -0
  106. package/dist/server/aiBinding.js +294 -0
  107. package/dist/server/attribution.d.ts +9 -5
  108. package/dist/server/attribution.d.ts.map +1 -1
  109. package/dist/server/attribution.js +12 -4
  110. package/dist/server/authorizeUrl.d.ts +1 -1
  111. package/dist/server/authorizeUrl.d.ts.map +1 -1
  112. package/dist/server/authorizeUrl.js +2 -6
  113. package/dist/server/connectUrl.d.ts +21 -0
  114. package/dist/server/connectUrl.d.ts.map +1 -0
  115. package/dist/server/connectUrl.js +37 -0
  116. package/dist/server/context.d.ts +68 -0
  117. package/dist/server/context.d.ts.map +1 -0
  118. package/dist/server/context.js +117 -0
  119. package/dist/server/emailBinding.d.ts +72 -0
  120. package/dist/server/emailBinding.d.ts.map +1 -0
  121. package/dist/server/emailBinding.js +124 -0
  122. package/dist/server/emit.d.ts +1 -1
  123. package/dist/server/emit.d.ts.map +1 -1
  124. package/dist/server/emit.js +7 -7
  125. package/dist/server/env.d.ts +44 -0
  126. package/dist/server/env.d.ts.map +1 -0
  127. package/dist/server/env.js +1 -0
  128. package/dist/server/everyApp.d.ts +27 -7
  129. package/dist/server/everyApp.d.ts.map +1 -1
  130. package/dist/server/everyApp.js +106 -5
  131. package/dist/server/fromMcpTools.d.ts +89 -0
  132. package/dist/server/fromMcpTools.d.ts.map +1 -0
  133. package/dist/server/fromMcpTools.js +192 -0
  134. package/dist/server/gatewayFetch.d.ts +42 -18
  135. package/dist/server/gatewayFetch.d.ts.map +1 -1
  136. package/dist/server/gatewayFetch.js +138 -19
  137. package/dist/server/gatewayUrl.d.ts +13 -0
  138. package/dist/server/gatewayUrl.d.ts.map +1 -0
  139. package/dist/server/gatewayUrl.js +34 -0
  140. package/dist/server/index.d.ts +15 -4
  141. package/dist/server/index.d.ts.map +1 -1
  142. package/dist/server/index.js +12 -2
  143. package/dist/server/integrationFetch.d.ts +70 -39
  144. package/dist/server/integrationFetch.d.ts.map +1 -1
  145. package/dist/server/integrationFetch.js +244 -82
  146. package/dist/server/listMembers.d.ts +1 -1
  147. package/dist/server/listMembers.d.ts.map +1 -1
  148. package/dist/server/listMembers.js +8 -11
  149. package/dist/server/refusal.d.ts +55 -0
  150. package/dist/server/refusal.d.ts.map +1 -0
  151. package/dist/server/refusal.js +193 -0
  152. package/dist/server/sendEmail.d.ts +1 -1
  153. package/dist/server/sendEmail.d.ts.map +1 -1
  154. package/dist/server/sendEmail.js +7 -7
  155. package/dist/server/toolsEndpoint.d.ts.map +1 -1
  156. package/dist/server/toolsEndpoint.js +11 -5
  157. package/dist/tanstack-start/index.d.ts +9 -0
  158. package/dist/tanstack-start/index.d.ts.map +1 -0
  159. package/dist/tanstack-start/index.js +29 -0
  160. package/dist/testing/index.d.ts +93 -0
  161. package/dist/testing/index.d.ts.map +1 -0
  162. package/dist/testing/index.js +156 -0
  163. package/package.json +35 -14
@@ -16,11 +16,14 @@
16
16
  * proxy's rules: inference routes only, the model allowlist, the usage
17
17
  * tap. Never a capability: its key would reach the account's credits
18
18
  * and keys on the same host.
19
- * - `oauth_client`: a provider's OAuth client (a social network's), held
20
- * the same way. The gateway completes the token endpoint calls only,
21
- * putting the client on them; the app builds the authorize URL from the
22
- * public client id (`integrationStatus()`), keeps the user tokens and
23
- * calls the provider's API with them itself. A client shared by apps
19
+ * - `oauth_client`: a provider's OAuth client (Google's, Microsoft's,
20
+ * Slack's), held the same way. The gateway completes the token endpoint
21
+ * calls only, putting the client on them; the app builds the authorize
22
+ * URL from the public client id (`integrationStatus()`), sends people
23
+ * back to a callback its manifest entry lists (`redirectPaths`), keeps
24
+ * the user tokens and calls the provider's API with them itself. Other
25
+ * providers' clients (GitHub, the social networks) are presets
26
+ * (./oauthPresets.ts): rows only once an operator adds one. A client shared by apps
24
27
  * with different needs carries a `scopePolicy`: named scope sets an app
25
28
  * lists in its manifest entry's `scopes`, which ./oauthScopes.ts turns
26
29
  * into what the app may ask the provider for and what a token it is
@@ -43,6 +46,7 @@ export const DEFAULT_WRITE_METHODS = [
43
46
  "PATCH",
44
47
  "DELETE",
45
48
  ];
49
+ import { CATALOG_ENTRIES } from "./catalog/index.js";
46
50
  export const CAPABILITIES = {
47
51
  // The AI providers, in the order the AI proxy's table had them.
48
52
  openai: {
@@ -69,6 +73,12 @@ export const CAPABILITIES = {
69
73
  // More than inference needs both the operator (AI_PROXY_EXTRA_ROUTES)
70
74
  // and the app's manifest (`ai.routes`): OpenAI paths only.
71
75
  extraRoutes: true,
76
+ // Tool calling on chat completions needs `reasoning_effort: "none"`
77
+ // on the GPT-6 family (the responses route takes tools as it is).
78
+ chat: { path: "/v1/chat/completions", seedModel: "gpt-6-luna" },
79
+ embeddings: { path: "/v1/embeddings", seedModel: "text-embedding-3-small" },
80
+ // AI Gateway's OpenAI endpoint takes the path without `/v1`.
81
+ aig: { slug: "openai", trim: "/v1" },
72
82
  hint: "A secret key from the API keys page; the Gateway puts it on every call as a bearer token.",
73
83
  verified: { on: "2026-10-25", how: "live" },
74
84
  },
@@ -89,7 +99,24 @@ export const CAPABILITIES = {
89
99
  "/api/v1/responses",
90
100
  "/api/v1/embeddings",
91
101
  ],
102
+ chat: {
103
+ path: "/api/v1/chat/completions",
104
+ seedModel: "anthropic/claude-haiku-5.5",
105
+ },
106
+ embeddings: {
107
+ path: "/api/v1/embeddings",
108
+ seedModel: "openai/text-embedding-3-small",
109
+ },
110
+ // AI Gateway's OpenRouter endpoint takes the path without `/api`.
111
+ aig: { slug: "openrouter", trim: "/api" },
92
112
  hint: "An API key from OpenRouter's keys page; the Gateway puts it on every call as a bearer token.",
113
+ // Every answer's `usage.cost` is the call's price in USD, what
114
+ // OpenRouter charges the key: read off the tail with the tokens.
115
+ cost: {
116
+ unit: "token",
117
+ responseField: "usage.cost",
118
+ note: "The answer's usage.cost is the call's price in USD, per model.",
119
+ },
93
120
  verified: { on: "2026-10-25", how: "live" },
94
121
  },
95
122
  // Workers AI's OpenAI-compatible REST API, in the gateway's own account
@@ -114,9 +141,55 @@ export const CAPABILITIES = {
114
141
  "/v1/responses",
115
142
  ],
116
143
  normalize: "workers-ai",
144
+ chat: {
145
+ path: "/v1/chat/completions",
146
+ seedModel: "@cf/meta/llama-4-scout-17b-16e-instruct",
147
+ },
148
+ embeddings: { path: "/v1/embeddings", seedModel: "@cf/baai/bge-m3" },
149
+ // Workers AI's REST API is AI Gateway's own host: the URL stays and
150
+ // `cf-aig-gateway-id` names the gateway.
151
+ aig: { slug: "workers-ai", header: true },
117
152
  hint: "A Cloudflare API token with Workers AI Read (the Workers AI page's prefilled token); the account is the Gateway's own.",
118
153
  verified: { on: "2026-10-25", how: "live" },
119
154
  },
155
+ // Cloudflare AI Gateway: a proxy in front of the other AI providers
156
+ // (logs, caching, rate limits, spend), with no models of its own. With
157
+ // AI_GATEWAY_ID set, every call to an `ai` entry that has an `aig` slug
158
+ // goes through it, the provider's own key still on the call; the
159
+ // gateway's own token is optional (an authenticated gateway requires
160
+ // it). Never listed under `providers`: it fronts the entries apps list.
161
+ "ai-gateway": {
162
+ mode: "ai",
163
+ kind: "proxy",
164
+ label: "Cloudflare AI Gateway",
165
+ description: "Cloudflare's proxy in front of the other AI providers: logs, caching, rate limits and spend, with each provider's own key.",
166
+ auth: {
167
+ kind: "header",
168
+ header: "cf-aig-authorization",
169
+ prefix: "Bearer ",
170
+ secret: "AI_GATEWAY_API_KEY",
171
+ },
172
+ authOptional: true,
173
+ docs: "https://dash.cloudflare.com/?to=/:account/ai/ai-gateway",
174
+ api: "https://developers.cloudflare.com/ai-gateway/",
175
+ setup: "setup/ai-gateway.md",
176
+ origin: "https://gateway.ai.cloudflare.com",
177
+ alsoNeeds: ["CLOUDFLARE_ACCOUNT_ID", "AI_GATEWAY_ID"],
178
+ basePath: "/v1/{CLOUDFLARE_ACCOUNT_ID}/{AI_GATEWAY_ID}",
179
+ inferenceRoutes: [],
180
+ hint: "The gateway's id from the AI Gateway page (everyapp ai gateway on makes one named everyapp); AI_GATEWAY_API_KEY only for an authenticated gateway, sent as cf-aig-authorization.",
181
+ secrets: [
182
+ {
183
+ name: "AI_GATEWAY_ID",
184
+ hint: "The AI Gateway's id (its name in the dashboard), in the Gateway's own Cloudflare account.",
185
+ },
186
+ {
187
+ name: "AI_GATEWAY_API_KEY",
188
+ hint: "An AI Gateway token with Run permission, only when the gateway is authenticated; leave unset for an open gateway.",
189
+ },
190
+ ],
191
+ verified: { on: "2026-10-25", how: "docs" },
192
+ },
120
193
  dataforseo: {
121
194
  mode: "api_key",
122
195
  label: "DataForSEO",
@@ -223,348 +296,6 @@ export const CAPABILITIES = {
223
296
  settings: [],
224
297
  optionalSettings: [],
225
298
  },
226
- // Social networks' OAuth clients (docs/capabilities.md "OAuth clients"):
227
- // the gateway completes their token endpoints only. Postiz connects its
228
- // channels with them; one client serves every organization on the gateway.
229
- linkedin: {
230
- mode: "oauth_client",
231
- label: "LinkedIn",
232
- secrets: {
233
- clientId: "LINKEDIN_CLIENT_ID",
234
- clientSecret: "LINKEDIN_CLIENT_SECRET",
235
- },
236
- token: [
237
- {
238
- host: "www.linkedin.com",
239
- path: "/oauth/v2/accessToken",
240
- methods: ["POST"],
241
- inject: "form",
242
- },
243
- ],
244
- access: ["write"],
245
- console: "https://www.linkedin.com/developers/apps",
246
- authorize: {
247
- url: "https://www.linkedin.com/oauth/v2/authorization",
248
- scopes: [
249
- "openid",
250
- "profile",
251
- "w_member_social",
252
- "r_basicprofile",
253
- "rw_organization_admin",
254
- "w_organization_social",
255
- "r_organization_social",
256
- ],
257
- },
258
- api: ["api.linkedin.com"],
259
- // Profiles and pages: both callbacks complete on the one client.
260
- redirectPaths: [
261
- "/integrations/social/linkedin",
262
- "/integrations/social/linkedin-page",
263
- ],
264
- },
265
- facebook: {
266
- mode: "oauth_client",
267
- label: "Facebook",
268
- secrets: {
269
- clientId: "FACEBOOK_APP_ID",
270
- clientSecret: "FACEBOOK_APP_SECRET",
271
- },
272
- token: [
273
- {
274
- // The code exchange and the long-lived token exchange, both on a
275
- // versioned path (/v25.0/oauth/access_token).
276
- host: "graph.facebook.com",
277
- path: /^\/v\d+\.\d+\/oauth\/access_token$/,
278
- methods: ["GET", "POST"],
279
- inject: "query",
280
- },
281
- ],
282
- access: ["write"],
283
- console: "https://developers.facebook.com/apps",
284
- authorize: {
285
- url: "https://www.facebook.com/v25.0/dialog/oauth",
286
- // Facebook pages, and Instagram business accounts through them.
287
- scopes: [
288
- "pages_show_list",
289
- "business_management",
290
- "pages_manage_posts",
291
- "pages_manage_engagement",
292
- "pages_read_engagement",
293
- "read_insights",
294
- "instagram_basic",
295
- "instagram_content_publish",
296
- "instagram_manage_comments",
297
- "instagram_manage_insights",
298
- ],
299
- },
300
- api: ["graph.facebook.com", "graph.instagram.com"],
301
- redirectPaths: [
302
- "/integrations/social/facebook",
303
- "/integrations/social/instagram",
304
- ],
305
- },
306
- instagram: {
307
- mode: "oauth_client",
308
- label: "Instagram",
309
- secrets: {
310
- clientId: "INSTAGRAM_APP_ID",
311
- clientSecret: "INSTAGRAM_APP_SECRET",
312
- },
313
- token: [
314
- // The code exchange, then the long-lived token exchange.
315
- {
316
- host: "api.instagram.com",
317
- path: "/oauth/access_token",
318
- methods: ["POST"],
319
- inject: "form",
320
- },
321
- {
322
- host: "graph.instagram.com",
323
- path: "/access_token",
324
- methods: ["GET"],
325
- inject: "query",
326
- },
327
- ],
328
- access: ["write"],
329
- console: "https://developers.facebook.com/apps",
330
- authorize: {
331
- url: "https://www.instagram.com/oauth/authorize",
332
- scopes: [
333
- "instagram_business_basic",
334
- "instagram_business_content_publish",
335
- "instagram_business_manage_comments",
336
- "instagram_business_manage_insights",
337
- ],
338
- },
339
- api: ["graph.instagram.com"],
340
- redirectPaths: ["/integrations/social/instagram-standalone"],
341
- },
342
- threads: {
343
- mode: "oauth_client",
344
- label: "Threads",
345
- secrets: { clientId: "THREADS_APP_ID", clientSecret: "THREADS_APP_SECRET" },
346
- token: [
347
- // The code exchange, then the long-lived token exchange.
348
- {
349
- host: "graph.threads.net",
350
- path: "/oauth/access_token",
351
- methods: ["GET", "POST"],
352
- inject: "query",
353
- },
354
- {
355
- host: "graph.threads.net",
356
- path: "/access_token",
357
- methods: ["GET"],
358
- inject: "query",
359
- },
360
- ],
361
- access: ["write"],
362
- console: "https://developers.facebook.com/apps",
363
- authorize: {
364
- url: "https://threads.net/oauth/authorize",
365
- scopes: [
366
- "threads_basic",
367
- "threads_content_publish",
368
- "threads_manage_replies",
369
- "threads_manage_insights",
370
- ],
371
- },
372
- api: ["graph.threads.net"],
373
- redirectPaths: ["/integrations/social/threads"],
374
- },
375
- youtube: {
376
- mode: "oauth_client",
377
- label: "YouTube",
378
- secrets: {
379
- clientId: "YOUTUBE_CLIENT_ID",
380
- clientSecret: "YOUTUBE_CLIENT_SECRET",
381
- },
382
- token: [
383
- {
384
- host: "oauth2.googleapis.com",
385
- path: "/token",
386
- methods: ["POST"],
387
- inject: "form",
388
- },
389
- ],
390
- access: ["write"],
391
- console: "https://console.cloud.google.com/apis/credentials",
392
- setup: "setup/google.md",
393
- authorize: {
394
- url: "https://accounts.google.com/o/oauth2/v2/auth",
395
- // YouTube channels, and Google Business Profiles on the same client.
396
- scopes: [
397
- "https://www.googleapis.com/auth/userinfo.profile",
398
- "https://www.googleapis.com/auth/userinfo.email",
399
- "https://www.googleapis.com/auth/youtube",
400
- "https://www.googleapis.com/auth/youtube.force-ssl",
401
- "https://www.googleapis.com/auth/youtube.readonly",
402
- "https://www.googleapis.com/auth/youtube.upload",
403
- "https://www.googleapis.com/auth/youtubepartner",
404
- "https://www.googleapis.com/auth/yt-analytics.readonly",
405
- "https://www.googleapis.com/auth/business.manage",
406
- ],
407
- },
408
- api: [
409
- "www.googleapis.com",
410
- "youtubeanalytics.googleapis.com",
411
- "mybusinessaccountmanagement.googleapis.com",
412
- "mybusinessbusinessinformation.googleapis.com",
413
- "mybusiness.googleapis.com",
414
- "businessprofileperformance.googleapis.com",
415
- ],
416
- redirectPaths: ["/integrations/social/youtube", "/integrations/social/gmb"],
417
- },
418
- tiktok: {
419
- mode: "oauth_client",
420
- label: "TikTok",
421
- secrets: {
422
- clientId: "TIKTOK_CLIENT_ID",
423
- clientSecret: "TIKTOK_CLIENT_SECRET",
424
- },
425
- token: [
426
- {
427
- host: "open.tiktokapis.com",
428
- path: "/v2/oauth/token/",
429
- methods: ["POST"],
430
- inject: "form",
431
- // TikTok calls the client id its client key.
432
- params: { clientId: "client_key" },
433
- },
434
- ],
435
- access: ["write"],
436
- console: "https://developers.tiktok.com/apps",
437
- authorize: {
438
- url: "https://www.tiktok.com/v2/auth/authorize/",
439
- scopes: [
440
- "video.list",
441
- "user.info.basic",
442
- "video.publish",
443
- "video.upload",
444
- "user.info.profile",
445
- "user.info.stats",
446
- ],
447
- },
448
- api: ["open.tiktokapis.com"],
449
- redirectPaths: ["/integrations/social/tiktok"],
450
- },
451
- "tiktok-business": {
452
- mode: "oauth_client",
453
- label: "TikTok Business",
454
- secrets: {
455
- clientId: "TIKTOK_BUSINESS_CLIENT_ID",
456
- clientSecret: "TIKTOK_BUSINESS_CLIENT_SECRET",
457
- },
458
- token: [
459
- {
460
- host: "business-api.tiktok.com",
461
- path: "/open_api/v1.3/tt_user/oauth2/token/",
462
- methods: ["POST"],
463
- inject: "json",
464
- },
465
- {
466
- host: "business-api.tiktok.com",
467
- path: "/open_api/v1.3/tt_user/oauth2/refresh_token/",
468
- methods: ["POST"],
469
- inject: "json",
470
- },
471
- ],
472
- access: ["write"],
473
- console: "https://business-api.tiktok.com/portal/apps",
474
- authorize: {
475
- url: "https://www.tiktok.com/v2/auth/authorize/",
476
- scopes: [
477
- "user.info.basic",
478
- "user.info.username",
479
- "user.info.stats",
480
- "video.list",
481
- "video.upload",
482
- "video.publish",
483
- ],
484
- },
485
- api: ["business-api.tiktok.com"],
486
- // TikTok Business wants the trailing slash.
487
- redirectPaths: ["/integrations/social/tiktok-business/"],
488
- },
489
- reddit: {
490
- mode: "oauth_client",
491
- label: "Reddit",
492
- secrets: {
493
- clientId: "REDDIT_CLIENT_ID",
494
- clientSecret: "REDDIT_CLIENT_SECRET",
495
- },
496
- token: [
497
- {
498
- host: "www.reddit.com",
499
- path: "/api/v1/access_token",
500
- methods: ["POST"],
501
- inject: "basic",
502
- },
503
- ],
504
- access: ["write"],
505
- console: "https://www.reddit.com/prefs/apps",
506
- authorize: {
507
- url: "https://www.reddit.com/api/v1/authorize",
508
- scopes: ["read", "identity", "submit", "flair"],
509
- },
510
- api: ["oauth.reddit.com"],
511
- redirectPaths: ["/integrations/social/reddit"],
512
- },
513
- pinterest: {
514
- mode: "oauth_client",
515
- label: "Pinterest",
516
- secrets: {
517
- clientId: "PINTEREST_CLIENT_ID",
518
- clientSecret: "PINTEREST_CLIENT_SECRET",
519
- },
520
- token: [
521
- {
522
- host: "api.pinterest.com",
523
- path: "/v5/oauth/token",
524
- methods: ["POST"],
525
- inject: "basic",
526
- },
527
- ],
528
- access: ["write"],
529
- console: "https://developers.pinterest.com/apps/",
530
- authorize: {
531
- url: "https://www.pinterest.com/oauth/",
532
- scopes: [
533
- "boards:read",
534
- "boards:write",
535
- "pins:read",
536
- "pins:write",
537
- "user_accounts:read",
538
- ],
539
- },
540
- api: ["api.pinterest.com"],
541
- redirectPaths: ["/integrations/social/pinterest"],
542
- },
543
- discord: {
544
- mode: "oauth_client",
545
- label: "Discord",
546
- secrets: {
547
- clientId: "DISCORD_CLIENT_ID",
548
- clientSecret: "DISCORD_CLIENT_SECRET",
549
- },
550
- token: [
551
- {
552
- host: "discord.com",
553
- path: "/api/oauth2/token",
554
- methods: ["POST"],
555
- inject: "basic",
556
- },
557
- ],
558
- access: ["write"],
559
- console: "https://discord.com/developers/applications",
560
- // The server install; posting uses the bot's token, an app secret.
561
- authorize: {
562
- url: "https://discord.com/oauth2/authorize",
563
- scopes: ["bot", "identify", "guilds"],
564
- },
565
- api: ["discord.com"],
566
- redirectPaths: ["/integrations/social/discord"],
567
- },
568
299
  slack: {
569
300
  mode: "oauth_client",
570
301
  label: "Slack",
@@ -774,136 +505,26 @@ export const CAPABILITIES = {
774
505
  },
775
506
  },
776
507
  api: ["slack.com"],
777
- redirectPaths: [
778
- "/integrations/social/slack",
779
- "/integrations/slack/callback",
780
- ],
781
- },
782
- mastodon: {
783
- mode: "oauth_client",
784
- label: "Mastodon",
785
- secrets: {
786
- clientId: "MASTODON_CLIENT_ID",
787
- clientSecret: "MASTODON_CLIENT_SECRET",
788
- },
789
- // On the instance the operator registered the client on.
790
- token: [{ path: "/oauth/token", methods: ["POST"], inject: "form" }],
791
- hostSetting: "MASTODON_URL",
792
- defaultOrigin: "https://mastodon.social",
793
- access: ["write"],
794
- console: "/settings/applications",
795
- authorize: {
796
- url: "/oauth/authorize",
797
- scopes: ["write:statuses", "profile", "write:media"],
798
- },
799
- api: [],
800
- redirectPaths: ["/integrations/social/mastodon"],
801
- },
802
- kick: {
803
- mode: "oauth_client",
804
- label: "Kick",
805
- secrets: { clientId: "KICK_CLIENT_ID", clientSecret: "KICK_CLIENT_SECRET" },
806
- token: [
807
- {
808
- host: "id.kick.com",
809
- path: "/oauth/token",
810
- methods: ["POST"],
811
- inject: "form",
508
+ // Slack refuses `code_challenge`; `auth.test` names the workspace and
509
+ // the installing person.
510
+ flow: {
511
+ tokenUrl: "https://slack.com/api/oauth.v2.access",
512
+ revokeUrl: "https://slack.com/api/auth.revoke",
513
+ pkce: false,
514
+ scopeDialect: "slack",
515
+ identity: {
516
+ url: "https://slack.com/api/auth.test",
517
+ label: "team",
518
+ id: "user_id",
812
519
  },
813
- ],
814
- access: ["write"],
815
- console: "https://kick.com/settings/developer",
816
- authorize: {
817
- url: "https://id.kick.com/oauth/authorize",
818
- scopes: ["chat:write", "user:read", "channel:read"],
819
520
  },
820
- api: ["api.kick.com"],
821
- redirectPaths: ["/integrations/social/kick"],
822
- },
823
- twitch: {
824
- mode: "oauth_client",
825
- label: "Twitch",
826
- secrets: {
827
- clientId: "TWITCH_CLIENT_ID",
828
- clientSecret: "TWITCH_CLIENT_SECRET",
829
- },
830
- token: [
831
- {
832
- host: "id.twitch.tv",
833
- path: "/oauth2/token",
834
- methods: ["POST"],
835
- inject: "form",
836
- },
837
- ],
838
- access: ["write"],
839
- console: "https://dev.twitch.tv/console/apps",
840
- authorize: {
841
- url: "https://id.twitch.tv/oauth2/authorize",
842
- scopes: [
843
- "user:write:chat",
844
- "user:read:chat",
845
- "moderator:manage:announcements",
846
- ],
847
- },
848
- // Helix also wants the public client id, as Client-Id, on every call.
849
- api: ["api.twitch.tv"],
850
- redirectPaths: ["/integrations/social/twitch"],
851
- },
852
- tumblr: {
853
- mode: "oauth_client",
854
- label: "Tumblr",
855
- secrets: {
856
- clientId: "TUMBLR_CLIENT_ID",
857
- clientSecret: "TUMBLR_CLIENT_SECRET",
858
- },
859
- token: [
860
- {
861
- host: "api.tumblr.com",
862
- path: "/v2/oauth2/token",
863
- methods: ["POST"],
864
- inject: "form",
865
- },
866
- ],
867
- access: ["write"],
868
- console: "https://www.tumblr.com/oauth/apps",
869
- authorize: {
870
- url: "https://www.tumblr.com/oauth2/authorize",
871
- scopes: ["write", "offline_access"],
872
- },
873
- api: ["api.tumblr.com"],
874
- redirectPaths: ["/integrations/social/tumblr"],
875
- },
876
- dribbble: {
877
- mode: "oauth_client",
878
- label: "Dribbble",
879
- secrets: {
880
- clientId: "DRIBBBLE_CLIENT_ID",
881
- clientSecret: "DRIBBBLE_CLIENT_SECRET",
882
- },
883
- token: [
884
- {
885
- // A POST that takes the client in its query string.
886
- host: "dribbble.com",
887
- path: "/oauth/token",
888
- methods: ["POST"],
889
- inject: "query",
890
- },
891
- ],
892
- access: ["write"],
893
- console: "https://dribbble.com/account/applications",
894
- authorize: {
895
- url: "https://dribbble.com/oauth/authorize",
896
- scopes: ["public", "upload"],
897
- },
898
- api: ["api.dribbble.com"],
899
- redirectPaths: ["/integrations/social/dribbble"],
900
521
  },
901
522
  // Clients several apps share with different needs, so each app lists the
902
523
  // scope sets it uses: Open CRM reads a member's own mailbox and calendar
903
524
  // (`gmail.read`, `calendar.read`; `identity`, `mail.read`), OpenSEO reads
904
525
  // a site's Search Console and Analytics (`identity`, `search-console.read`,
905
526
  // `analytics.read`). Every set is read-only. Google's is one Cloud project
906
- // apart from YouTube's; Gmail's read scope is restricted and needs
527
+ // apart from the YouTube preset's; Gmail's read scope is restricted and needs
907
528
  // Google's verification, and whether Search Console and Analytics share
908
529
  // that consent screen with it is the operator's choice when verifying.
909
530
  "google-workspace": {
@@ -1067,7 +688,21 @@ export const CAPABILITIES = {
1067
688
  "analyticsadmin.googleapis.com",
1068
689
  "openidconnect.googleapis.com",
1069
690
  ],
1070
- redirectPaths: ["/integrations/google/callback"],
691
+ // The Gateway-owned flow: a refresh token needs `access_type=offline`,
692
+ // and `prompt=consent` makes Google issue one on every connect. No
693
+ // revoke: Google's `/revoke` is per person per client and would also
694
+ // kill the tokens an app-driven OpenSEO holds under the same client.
695
+ flow: {
696
+ tokenUrl: "https://oauth2.googleapis.com/token",
697
+ pkce: true,
698
+ scopeDialect: "google",
699
+ authorizeParams: { access_type: "offline", prompt: "consent" },
700
+ identity: {
701
+ url: "https://openidconnect.googleapis.com/v1/userinfo",
702
+ label: "email",
703
+ id: "sub",
704
+ },
705
+ },
1071
706
  },
1072
707
  "microsoft-365": {
1073
708
  mode: "oauth_client",
@@ -1162,8 +797,20 @@ export const CAPABILITIES = {
1162
797
  },
1163
798
  },
1164
799
  api: ["graph.microsoft.com"],
1165
- redirectPaths: ["/integrations/microsoft/callback"],
800
+ flow: {
801
+ tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
802
+ pkce: true,
803
+ scopeDialect: "microsoft",
804
+ identity: {
805
+ url: "https://graph.microsoft.com/v1.0/me",
806
+ label: "userPrincipalName",
807
+ id: "id",
808
+ },
809
+ },
1166
810
  },
811
+ // The first fifty beyond the entries above (catalog/*.ts, PR 3 of the
812
+ // provider registry), after them so the derived lists keep their order.
813
+ ...CATALOG_ENTRIES,
1167
814
  };
1168
815
  /** The Gateway settings an API-key or AI provider's `auth` names: the key first, then a Basic username held as a setting. */
1169
816
  export function authSettingNames(auth) {
@@ -1193,7 +840,6 @@ export function capabilitySettings(capability) {
1193
840
  ...(capability.secrets.clientSecret
1194
841
  ? [capability.secrets.clientSecret]
1195
842
  : []),
1196
- ...(capability.hostSetting ? [capability.hostSetting] : []),
1197
843
  ];
1198
844
  }
1199
845
  }
@@ -1315,8 +961,8 @@ export function serviceRoutePath(id, route) {
1315
961
  * an unknown capability (or an AI provider, which has its own route), a
1316
962
  * host outside the provider's, a path that could leave that host, a path
1317
963
  * that isn't one of an OAuth client's token endpoints, or a route a service
1318
- * capability doesn't declare. `settings` resolves an OAuth client's
1319
- * `hostSetting` (Mastodon's instance); `lookup` is where the id is found,
964
+ * capability doesn't declare. `settings` resolves an API-key provider's
965
+ * `hostSetting` (a Shopify store's host); `lookup` is where the id is found,
1320
966
  * the catalog alone by default.
1321
967
  */
1322
968
  export function parseIntegrationProxyPath(pathname, search, settings, lookup = CATALOG) {
@@ -1340,10 +986,10 @@ export function parseIntegrationProxyPath(pathname, search, settings, lookup = C
1340
986
  };
1341
987
  }
1342
988
  if (capability.mode === "oauth_client") {
1343
- const upstream = oauthClientHosts(capability, settings).includes(host)
989
+ const upstream = capability.token.some((rule) => rule.host === host)
1344
990
  ? httpsUpstream(host, path ?? "/", search)
1345
991
  : null;
1346
- const endpoint = upstream && tokenEndpointFor(capability, upstream, settings);
992
+ const endpoint = upstream && tokenEndpointFor(capability, upstream);
1347
993
  if (!upstream || !endpoint)
1348
994
  return null;
1349
995
  return { kind: "oauth_client", id, capability, upstream, endpoint };
@@ -1368,80 +1014,23 @@ function httpsUpstream(host, path, search) {
1368
1014
  return upstream;
1369
1015
  }
1370
1016
  /**
1371
- * The origin an OAuth client's `hostSetting` names (its default when the
1372
- * setting is unset), or null for a client with a fixed host or a setting
1373
- * that isn't an https origin. A bare host is taken as https.
1017
+ * The token endpoint `upstream` is, by its exact host and path, or null.
1018
+ * Its method is the caller's to check.
1374
1019
  */
1375
- export function oauthClientOrigin(capability, settings) {
1376
- if (!capability.hostSetting)
1377
- return null;
1378
- const set = settings?.[capability.hostSetting];
1379
- const value = typeof set === "string" && set.trim()
1380
- ? set.trim()
1381
- : capability.defaultOrigin;
1382
- if (!value)
1383
- return null;
1384
- let url;
1385
- try {
1386
- url = new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(value) ? value : `https://${value}`);
1387
- }
1388
- catch {
1389
- return null;
1390
- }
1391
- // An origin and nothing else: the paths are the provider's own.
1392
- const bare = url.pathname === "/" &&
1393
- !url.search &&
1394
- !url.hash &&
1395
- !url.username &&
1396
- !url.password;
1397
- return url.protocol === "https:" && bare ? url.origin : null;
1398
- }
1399
- /** The host an OAuth client's `hostSetting` names, or null. */
1400
- function settingHost(capability, settings) {
1401
- const origin = oauthClientOrigin(capability, settings);
1402
- return origin ? new URL(origin).host : null;
1403
- }
1404
- /** The hosts an OAuth client's token endpoints are on. */
1405
- function oauthClientHosts(capability, settings) {
1406
- const fromSetting = settingHost(capability, settings);
1407
- return [
1408
- ...new Set(capability.token.flatMap(({ host }) => {
1409
- const resolved = host ?? fromSetting;
1410
- return resolved ? [resolved] : [];
1411
- })),
1412
- ];
1413
- }
1414
- /**
1415
- * The token endpoint `upstream` is, by its exact host and path (a RegExp
1416
- * for a versioned one), or null. Its method is the caller's to check.
1417
- */
1418
- export function tokenEndpointFor(capability, upstream, settings) {
1020
+ export function tokenEndpointFor(capability, upstream) {
1419
1021
  if (upstream.protocol !== "https:")
1420
1022
  return null;
1421
- const fromSetting = settingHost(capability, settings);
1422
- return (capability.token.find(({ host, path }) => (host ?? fromSetting) === upstream.host &&
1423
- (typeof path === "string"
1424
- ? path === upstream.pathname
1425
- : path.test(upstream.pathname))) ?? null);
1023
+ return (capability.token.find(({ host, path }) => host === upstream.host && path === upstream.pathname) ?? null);
1426
1024
  }
1427
1025
  /** An OAuth client's token endpoints, as `POST https://host/path` lines for messages. */
1428
1026
  export function describeTokenEndpoints(capability) {
1429
1027
  return capability.token
1430
- .map(({ host, path, methods }) => `${methods.join("/")} https://${host ?? `<${capability.hostSetting}>`}${tokenPathText(path)}`)
1028
+ .map(({ host, path, methods }) => `${methods.join("/")} https://${host}${path}`)
1431
1029
  .join(", ");
1432
1030
  }
1433
- /** A token endpoint's path as people read it: a versioned one as `/v<version>/oauth/access_token`. */
1434
- export function tokenPathText(path) {
1435
- if (typeof path === "string")
1436
- return path;
1437
- return path.source
1438
- .replace(/^\^|\$$/g, "")
1439
- .replace(/v\\d\+\\\.\\d\+/g, "v<version>")
1440
- .replace(/\\(.)/g, "$1");
1441
- }
1442
1031
  /**
1443
1032
  * The command that gives the gateway `capability`'s client:
1444
- * `pnpm everyapp secret set <ID> <SECRET> [<HOST SETTING>]`.
1033
+ * `pnpm everyapp secret set <ID> <SECRET>`.
1445
1034
  */
1446
1035
  export function oauthClientSecretSetCommand(capability) {
1447
1036
  return `pnpm everyapp secret set ${capabilitySettings(capability).join(" ")}`;