@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.
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/cloudflare/catalogSettings.generated.d.ts +81 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
- package/dist/cloudflare/catalogSettings.generated.js +174 -0
- package/dist/cloudflare/index.d.ts +3 -2
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +4 -1
- package/dist/cloudflare/sentinel.d.ts +60 -2
- package/dist/cloudflare/sentinel.d.ts.map +1 -1
- package/dist/cloudflare/sentinel.js +37 -2
- package/dist/cloudflare/worker.d.ts +357 -63
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +363 -104
- package/dist/dev/index.d.ts +1 -1
- package/dist/dev/index.d.ts.map +1 -1
- package/dist/dev/index.js +1 -1
- package/dist/dev/keypair.d.ts +2 -0
- package/dist/dev/keypair.d.ts.map +1 -1
- package/dist/dev/keypair.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/internal/aiDefault.d.ts +204 -0
- package/dist/internal/aiDefault.d.ts.map +1 -0
- package/dist/internal/aiDefault.js +334 -0
- package/dist/internal/aiProviders.d.ts +7 -2
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +17 -4
- package/dist/internal/appSettings.d.ts +82 -4
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +204 -29
- package/dist/internal/audit.d.ts +108 -0
- package/dist/internal/audit.d.ts.map +1 -0
- package/dist/internal/audit.js +235 -0
- package/dist/internal/capabilities.d.ts +2268 -438
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +128 -539
- package/dist/internal/catalog/commerce.d.ts +394 -0
- package/dist/internal/catalog/commerce.d.ts.map +1 -0
- package/dist/internal/catalog/commerce.js +477 -0
- package/dist/internal/catalog/comms.d.ts +375 -0
- package/dist/internal/catalog/comms.d.ts.map +1 -0
- package/dist/internal/catalog/comms.js +520 -0
- package/dist/internal/catalog/index.d.ts +1973 -0
- package/dist/internal/catalog/index.d.ts.map +1 -0
- package/dist/internal/catalog/index.js +19 -0
- package/dist/internal/catalog/inference.d.ts +393 -0
- package/dist/internal/catalog/inference.d.ts.map +1 -0
- package/dist/internal/catalog/inference.js +460 -0
- package/dist/internal/catalog/infra.d.ts +423 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -0
- package/dist/internal/catalog/infra.js +543 -0
- package/dist/internal/catalog/search.d.ts +400 -0
- package/dist/internal/catalog/search.d.ts.map +1 -0
- package/dist/internal/catalog/search.js +463 -0
- package/dist/internal/devProviders.d.ts +2 -2
- package/dist/internal/devProviders.d.ts.map +1 -1
- package/dist/internal/devProviders.js +19 -1
- package/dist/internal/events.d.ts +13 -2
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +47 -4
- package/dist/internal/gatewaySettings.d.ts +3 -2
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +12 -6
- package/dist/internal/identity.d.ts +41 -4
- package/dist/internal/identity.d.ts.map +1 -1
- package/dist/internal/identity.js +48 -10
- package/dist/internal/index.d.ts +6 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +6 -1
- package/dist/internal/members.d.ts +3 -2
- package/dist/internal/members.d.ts.map +1 -1
- package/dist/internal/oauthClient.d.ts +10 -12
- package/dist/internal/oauthClient.d.ts.map +1 -1
- package/dist/internal/oauthClient.js +20 -81
- package/dist/internal/oauthPresets.d.ts +33 -0
- package/dist/internal/oauthPresets.d.ts.map +1 -0
- package/dist/internal/oauthPresets.js +1064 -0
- package/dist/internal/oauthScopes.d.ts +56 -1
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +98 -4
- package/dist/internal/oauthSpec.d.ts +102 -0
- package/dist/internal/oauthSpec.d.ts.map +1 -0
- package/dist/internal/oauthSpec.js +789 -0
- package/dist/internal/protocol.d.ts +64 -1
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +51 -1
- package/dist/internal/providerRegistry.d.ts +77 -6
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +178 -8
- package/dist/internal/providerSpec.d.ts +18 -7
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +442 -34
- package/dist/internal/signInProviders.d.ts +104 -0
- package/dist/internal/signInProviders.d.ts.map +1 -0
- package/dist/internal/signInProviders.js +245 -0
- package/dist/manifest.d.ts +238 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +30 -0
- package/dist/server/ai.d.ts +111 -0
- package/dist/server/ai.d.ts.map +1 -0
- package/dist/server/ai.js +145 -0
- package/dist/server/aiBinding.d.ts +68 -0
- package/dist/server/aiBinding.d.ts.map +1 -0
- package/dist/server/aiBinding.js +294 -0
- package/dist/server/attribution.d.ts +9 -5
- package/dist/server/attribution.d.ts.map +1 -1
- package/dist/server/attribution.js +12 -4
- package/dist/server/authorizeUrl.d.ts +1 -1
- package/dist/server/authorizeUrl.d.ts.map +1 -1
- package/dist/server/authorizeUrl.js +2 -6
- package/dist/server/connectUrl.d.ts +21 -0
- package/dist/server/connectUrl.d.ts.map +1 -0
- package/dist/server/connectUrl.js +37 -0
- package/dist/server/context.d.ts +68 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +117 -0
- package/dist/server/emailBinding.d.ts +72 -0
- package/dist/server/emailBinding.d.ts.map +1 -0
- package/dist/server/emailBinding.js +124 -0
- package/dist/server/emit.d.ts +1 -1
- package/dist/server/emit.d.ts.map +1 -1
- package/dist/server/emit.js +7 -7
- package/dist/server/env.d.ts +44 -0
- package/dist/server/env.d.ts.map +1 -0
- package/dist/server/env.js +1 -0
- package/dist/server/everyApp.d.ts +27 -7
- package/dist/server/everyApp.d.ts.map +1 -1
- package/dist/server/everyApp.js +106 -5
- package/dist/server/fromMcpTools.d.ts +89 -0
- package/dist/server/fromMcpTools.d.ts.map +1 -0
- package/dist/server/fromMcpTools.js +192 -0
- package/dist/server/gatewayFetch.d.ts +42 -18
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +138 -19
- package/dist/server/gatewayUrl.d.ts +13 -0
- package/dist/server/gatewayUrl.d.ts.map +1 -0
- package/dist/server/gatewayUrl.js +34 -0
- package/dist/server/index.d.ts +15 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +12 -2
- package/dist/server/integrationFetch.d.ts +70 -39
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +244 -82
- package/dist/server/listMembers.d.ts +1 -1
- package/dist/server/listMembers.d.ts.map +1 -1
- package/dist/server/listMembers.js +8 -11
- package/dist/server/refusal.d.ts +55 -0
- package/dist/server/refusal.d.ts.map +1 -0
- package/dist/server/refusal.js +193 -0
- package/dist/server/sendEmail.d.ts +1 -1
- package/dist/server/sendEmail.d.ts.map +1 -1
- package/dist/server/sendEmail.js +7 -7
- package/dist/server/toolsEndpoint.d.ts.map +1 -1
- package/dist/server/toolsEndpoint.js +11 -5
- package/dist/tanstack-start/index.d.ts +9 -0
- package/dist/tanstack-start/index.d.ts.map +1 -0
- package/dist/tanstack-start/index.js +29 -0
- package/dist/testing/index.d.ts +93 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +156 -0
- 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 (
|
|
20
|
-
* the same way. The gateway completes the token endpoint
|
|
21
|
-
* putting the client on them; the app builds the authorize
|
|
22
|
-
* public client id (`integrationStatus()`),
|
|
23
|
-
*
|
|
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
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1319
|
-
* `hostSetting` (
|
|
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 =
|
|
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
|
|
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
|
|
1372
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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(" ")}`;
|