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

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 (120) hide show
  1. package/README.md +67 -67
  2. package/dist/cloudflare/catalogSettings.generated.d.ts +7 -4
  3. package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -1
  4. package/dist/cloudflare/catalogSettings.generated.js +7 -6
  5. package/dist/cloudflare/index.d.ts +1 -1
  6. package/dist/cloudflare/index.d.ts.map +1 -1
  7. package/dist/cloudflare/worker.d.ts +36 -2
  8. package/dist/cloudflare/worker.d.ts.map +1 -1
  9. package/dist/cloudflare/worker.js +32 -6
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/internal/aiDefault.d.ts +36 -79
  13. package/dist/internal/aiDefault.d.ts.map +1 -1
  14. package/dist/internal/aiDefault.js +47 -149
  15. package/dist/internal/aiProviders.d.ts.map +1 -1
  16. package/dist/internal/aiProviders.js +2 -2
  17. package/dist/internal/appSettings.d.ts +14 -17
  18. package/dist/internal/appSettings.d.ts.map +1 -1
  19. package/dist/internal/appSettings.js +2 -16
  20. package/dist/internal/capabilities.d.ts +106 -489
  21. package/dist/internal/capabilities.d.ts.map +1 -1
  22. package/dist/internal/capabilities.js +49 -550
  23. package/dist/internal/catalog/commerce.js +1 -1
  24. package/dist/internal/catalog/index.d.ts +35 -0
  25. package/dist/internal/catalog/index.d.ts.map +1 -1
  26. package/dist/internal/catalog/infra.d.ts +35 -0
  27. package/dist/internal/catalog/infra.d.ts.map +1 -1
  28. package/dist/internal/catalog/infra.js +65 -0
  29. package/dist/internal/events.d.ts +52 -1
  30. package/dist/internal/events.d.ts.map +1 -1
  31. package/dist/internal/events.js +65 -3
  32. package/dist/internal/gatewaySettings.d.ts +2 -2
  33. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  34. package/dist/internal/gatewaySettings.js +0 -6
  35. package/dist/internal/oauthBuiltIns.d.ts +101 -0
  36. package/dist/internal/oauthBuiltIns.d.ts.map +1 -0
  37. package/dist/internal/oauthBuiltIns.js +122 -0
  38. package/dist/internal/oauthClient.d.ts +7 -47
  39. package/dist/internal/oauthClient.d.ts.map +1 -1
  40. package/dist/internal/oauthClient.js +1 -115
  41. package/dist/internal/oauthPresets.d.ts +14 -15
  42. package/dist/internal/oauthPresets.d.ts.map +1 -1
  43. package/dist/internal/oauthPresets.js +42 -853
  44. package/dist/internal/oauthScopes.d.ts +43 -155
  45. package/dist/internal/oauthScopes.d.ts.map +1 -1
  46. package/dist/internal/oauthScopes.js +82 -475
  47. package/dist/internal/oauthSpec.d.ts +72 -71
  48. package/dist/internal/oauthSpec.d.ts.map +1 -1
  49. package/dist/internal/oauthSpec.js +178 -447
  50. package/dist/internal/protocol.d.ts +24 -14
  51. package/dist/internal/protocol.d.ts.map +1 -1
  52. package/dist/internal/protocol.js +26 -7
  53. package/dist/internal/providerRegistry.d.ts +1 -61
  54. package/dist/internal/providerRegistry.d.ts.map +1 -1
  55. package/dist/internal/providerRegistry.js +8 -115
  56. package/dist/internal/providerSpec.d.ts.map +1 -1
  57. package/dist/internal/providerSpec.js +14 -36
  58. package/dist/internal/remoteMcp.d.ts +4 -6
  59. package/dist/internal/remoteMcp.d.ts.map +1 -1
  60. package/dist/internal/remoteMcp.js +8 -15
  61. package/dist/internal/tools.d.ts +12 -7
  62. package/dist/internal/tools.d.ts.map +1 -1
  63. package/dist/internal/tools.js +9 -8
  64. package/dist/manifest.d.ts +41 -28
  65. package/dist/manifest.d.ts.map +1 -1
  66. package/dist/manifest.js +1 -1
  67. package/dist/server/ai.d.ts +1 -93
  68. package/dist/server/ai.d.ts.map +1 -1
  69. package/dist/server/ai.js +17 -111
  70. package/dist/server/attribution.d.ts +10 -1
  71. package/dist/server/attribution.d.ts.map +1 -1
  72. package/dist/server/attribution.js +7 -1
  73. package/dist/server/connectUrl.d.ts +4 -9
  74. package/dist/server/connectUrl.d.ts.map +1 -1
  75. package/dist/server/connectUrl.js +17 -14
  76. package/dist/server/context.d.ts +12 -14
  77. package/dist/server/context.d.ts.map +1 -1
  78. package/dist/server/context.js +23 -25
  79. package/dist/server/eventsEndpoint.d.ts +48 -0
  80. package/dist/server/eventsEndpoint.d.ts.map +1 -0
  81. package/dist/server/eventsEndpoint.js +145 -0
  82. package/dist/server/everyApp.d.ts +16 -26
  83. package/dist/server/everyApp.d.ts.map +1 -1
  84. package/dist/server/everyApp.js +22 -46
  85. package/dist/server/gatewayFetch.d.ts +1 -1
  86. package/dist/server/gatewayFetch.js +1 -1
  87. package/dist/server/index.d.ts +6 -9
  88. package/dist/server/index.d.ts.map +1 -1
  89. package/dist/server/index.js +4 -8
  90. package/dist/server/integrationFetch.d.ts +60 -72
  91. package/dist/server/integrationFetch.d.ts.map +1 -1
  92. package/dist/server/integrationFetch.js +117 -392
  93. package/dist/server/refusal.d.ts +0 -9
  94. package/dist/server/refusal.d.ts.map +1 -1
  95. package/dist/server/refusal.js +0 -22
  96. package/dist/server/tools.d.ts +13 -5
  97. package/dist/server/tools.d.ts.map +1 -1
  98. package/dist/server/tools.js +10 -3
  99. package/dist/server/toolsEndpoint.d.ts +2 -1
  100. package/dist/server/toolsEndpoint.d.ts.map +1 -1
  101. package/dist/server/toolsEndpoint.js +4 -3
  102. package/dist/server/webhooks.d.ts +56 -0
  103. package/dist/server/webhooks.d.ts.map +1 -0
  104. package/dist/server/webhooks.js +205 -0
  105. package/dist/testing/index.d.ts +2 -3
  106. package/dist/testing/index.d.ts.map +1 -1
  107. package/dist/testing/index.js +2 -6
  108. package/package.json +1 -1
  109. package/dist/server/aiBinding.d.ts +0 -68
  110. package/dist/server/aiBinding.d.ts.map +0 -1
  111. package/dist/server/aiBinding.js +0 -294
  112. package/dist/server/authorizeUrl.d.ts +0 -43
  113. package/dist/server/authorizeUrl.d.ts.map +0 -1
  114. package/dist/server/authorizeUrl.js +0 -106
  115. package/dist/server/emailBinding.d.ts +0 -72
  116. package/dist/server/emailBinding.d.ts.map +0 -1
  117. package/dist/server/emailBinding.js +0 -124
  118. package/dist/server/fromMcpTools.d.ts +0 -89
  119. package/dist/server/fromMcpTools.d.ts.map +0 -1
  120. package/dist/server/fromMcpTools.js +0 -192
@@ -1,21 +1,20 @@
1
1
  /**
2
- * OAuth presets (docs/design/2026-10-oauth-providers.md section 4): ready
3
- * `oauth2-user` specs, in the JSON shape `everyapp provider add` takes,
4
- * for the providers people most often want a "Connect" button for. A
5
- * preset becomes a `providers` row when added (`everyapp provider add
6
- * --oauth github`, the Admin dialog, `everyapp_provider_add` with
7
- * `preset`), so an operator may `provider show` and edit a copy; nothing
8
- * here is a hidden catalog entry, and no gateway reserves a preset's
9
- * setting names until it is added. Google, Microsoft and Slack are
10
- * catalog clients with a `flow` and are offered in the same picker. The
11
- * social networks' presets carry the tag `social`.
2
+ * OAuth presets: ready `oauth2-user` specs, in the JSON shape `everyapp
3
+ * provider add` takes, for providers people most often want a "Connect"
4
+ * button for. A preset becomes a `providers` row when added (`everyapp
5
+ * provider add --oauth github-oauth`, the Admin dialog,
6
+ * `everyapp_provider_add` with `preset`), so an operator may `provider
7
+ * show` and edit a copy; nothing here is a hidden catalog entry, and no
8
+ * gateway reserves a preset's setting names until it is added. Google,
9
+ * Microsoft and Slack are built in (./oauthBuiltIns.ts) and are offered
10
+ * in the same picker.
12
11
  *
13
- * Each preset parses with `parseProviderSpec` (oauthPresets.test.ts),
12
+ * Each preset parses with `parseProviderSpec` (oauthPresets.test.ts) and
14
13
  * names a console where the client is made, the API hosts the token is
15
- * sent to, and scope sets an app declares under `scopes`. Scope strings
16
- * are the providers' own, verified against their docs on 2026-10-25;
17
- * the social presets' come from the catalog entries Postiz used, and
18
- * none of them has been tried against a live connect yet.
14
+ * sent to, and how the provider spells scopes. Which scopes a connection
15
+ * holds is each app's `everyapp.config.ts`, never the preset. Endpoints
16
+ * were checked against the providers' docs on 2026-10-25; only GitHub's
17
+ * and Google's have been tried against a live connect.
19
18
  */
20
19
  import { parseProviderSpec, } from "./providerSpec.js";
21
20
  export const OAUTH_PRESETS = {
@@ -24,7 +23,7 @@ export const OAUTH_PRESETS = {
24
23
  name: "GitHub",
25
24
  description: "Repositories, issues, pull requests and the person's profile.",
26
25
  docs: "https://github.com/settings/developers",
27
- api: "https://docs.github.com/rest",
26
+ api: "https://docs.github.com/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps",
28
27
  hosts: ["api.github.com"],
29
28
  auth: {
30
29
  kind: "oauth2-user",
@@ -36,38 +35,16 @@ export const OAUTH_PRESETS = {
36
35
  clientSecret: "GITHUB_OAUTH_CLIENT_SECRET",
37
36
  inject: "form",
38
37
  pkce: true,
39
- scopeDialect: "comma",
38
+ scopeSeparator: "comma",
40
39
  tokenHeaders: { Accept: "application/json" },
40
+ // GET /user answers with the public profile under any token.
41
41
  identity: {
42
42
  url: "https://api.github.com/user",
43
43
  label: "login",
44
44
  id: "id",
45
45
  },
46
46
  },
47
- scopeSets: {
48
- identity: {
49
- label: "GitHub account (who you are)",
50
- access: "read",
51
- scopes: ["read:user", "user:email"],
52
- },
53
- "repo.read": {
54
- label: "Repositories (read, private ones included)",
55
- access: "read",
56
- scopes: ["repo"],
57
- },
58
- "repo.write": {
59
- label: "Repositories (read and write)",
60
- access: "write",
61
- scopes: ["repo"],
62
- },
63
- "org.read": {
64
- label: "Organizations and teams (read)",
65
- access: "read",
66
- scopes: ["read:org"],
67
- },
68
- },
69
- access: ["read", "write"],
70
- hint: "Register an OAuth App (not a GitHub App) and paste its client id and secret. GitHub's `repo` scope is read and write; a read set with it still lets the Gateway refuse writes.",
47
+ hint: "Register an OAuth App (not a GitHub App) and paste its client id and secret. GitHub's `repo` scope is read and write.",
71
48
  },
72
49
  "notion-oauth": {
73
50
  id: "notion-oauth",
@@ -84,40 +61,10 @@ export const OAUTH_PRESETS = {
84
61
  clientSecret: "NOTION_OAUTH_CLIENT_SECRET",
85
62
  inject: "basic",
86
63
  pkce: false,
87
- scopeDialect: "space",
64
+ scopeSeparator: "space",
88
65
  authorizeParams: { owner: "user" },
89
66
  },
90
- // Notion reads with GET, and with POST for a search and a database
91
- // query; the read set lists those two, so a read grant never creates
92
- // or changes a page (POST /v1/pages, PATCH, DELETE need the write set).
93
- scopeSets: {
94
- "workspace.read": {
95
- label: "Shared pages and databases (read)",
96
- access: "read",
97
- scopes: [],
98
- routes: [
99
- {
100
- host: "api.notion.com",
101
- paths: [
102
- "/v1/users",
103
- "/v1/users/",
104
- "/v1/pages/",
105
- "/v1/databases/",
106
- "/v1/blocks/",
107
- "/v1/comments",
108
- ],
109
- posts: ["^/v1/search$", "^/v1/databases/[^/]+/query$"],
110
- },
111
- ],
112
- },
113
- "workspace.write": {
114
- label: "Shared pages and databases (read and write)",
115
- access: "write",
116
- scopes: [],
117
- },
118
- },
119
- access: ["read", "write"],
120
- hint: "Notion has no scopes: the person picks the pages to share on Notion's consent screen. Make the integration public to get a client id and secret. Apps send `Notion-Version` on every call.",
67
+ hint: "Notion has no scopes: an app lists scopes: [] and the person picks the pages to share on Notion's consent screen. Make the integration public to get a client id and secret. Apps send `Notion-Version` on every call.",
121
68
  },
122
69
  "linear-oauth": {
123
70
  id: "linear-oauth",
@@ -135,30 +82,16 @@ export const OAUTH_PRESETS = {
135
82
  clientSecret: "LINEAR_OAUTH_CLIENT_SECRET",
136
83
  inject: "form",
137
84
  pkce: false,
138
- scopeDialect: "comma",
139
- },
140
- // Linear's API is one GraphQL POST, which the Gateway can't tell a
141
- // query from a mutation on, so the one set is a write set: an app
142
- // lists Linear with access: "write", and Linear's own `read` scope is
143
- // what a read-only token holds.
144
- scopeSets: {
145
- "workspace.write": {
146
- label: "Issues, projects and teams (read and write)",
147
- access: "write",
148
- scopes: ["read", "write"],
149
- },
85
+ scopeSeparator: "comma",
150
86
  },
151
- access: ["write"],
152
- readMethods: [],
153
- writeMethods: ["POST"],
154
- hint: "Linear's API is GraphQL over POST, so every call needs access: \"write\"; a token's scopes decide what a query may change.",
87
+ hint: "Linear's scopes are read, write, issues:create and comments:create; its API is GraphQL over POST.",
155
88
  },
156
89
  "gitlab-oauth": {
157
90
  id: "gitlab-oauth",
158
91
  name: "GitLab",
159
92
  description: "Projects, merge requests and issues on gitlab.com.",
160
93
  docs: "https://gitlab.com/-/user_settings/applications",
161
- api: "https://docs.gitlab.com/api/rest/",
94
+ api: "https://docs.gitlab.com/integration/oauth_provider/#view-all-authorized-applications",
162
95
  hosts: ["gitlab.com"],
163
96
  auth: {
164
97
  kind: "oauth2-user",
@@ -169,31 +102,14 @@ export const OAUTH_PRESETS = {
169
102
  clientSecret: "GITLAB_OAUTH_CLIENT_SECRET",
170
103
  inject: "form",
171
104
  pkce: true,
172
- scopeDialect: "space",
105
+ scopeSeparator: "space",
173
106
  identity: {
174
107
  url: "https://gitlab.com/api/v4/user",
175
108
  label: "username",
176
109
  id: "id",
177
- },
178
- },
179
- scopeSets: {
180
- identity: {
181
- label: "GitLab account (who you are)",
182
- access: "read",
183
110
  scopes: ["read_user"],
184
111
  },
185
- "api.read": {
186
- label: "Projects, issues and merge requests (read)",
187
- access: "read",
188
- scopes: ["read_api"],
189
- },
190
- "api.write": {
191
- label: "Projects, issues and merge requests (read and write)",
192
- access: "write",
193
- scopes: ["api"],
194
- },
195
112
  },
196
- access: ["read", "write"],
197
113
  hint: "A self-managed GitLab needs a custom provider with its own host in place of gitlab.com.",
198
114
  },
199
115
  atlassian: {
@@ -201,7 +117,7 @@ export const OAUTH_PRESETS = {
201
117
  name: "Atlassian (Jira and Confluence)",
202
118
  description: "Jira issues and Confluence pages on the sites the person grants.",
203
119
  docs: "https://developer.atlassian.com/console/myapps/",
204
- api: "https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/",
120
+ api: "https://developer.atlassian.com/cloud/jira/platform/scopes-for-oauth-2-3LO-and-forge-apps/",
205
121
  hosts: ["api.atlassian.com"],
206
122
  auth: {
207
123
  kind: "oauth2-user",
@@ -211,41 +127,16 @@ export const OAUTH_PRESETS = {
211
127
  clientSecret: "ATLASSIAN_CLIENT_SECRET",
212
128
  inject: "form",
213
129
  pkce: false,
214
- scopeDialect: "space",
130
+ scopeSeparator: "space",
215
131
  authorizeParams: { audience: "api.atlassian.com", prompt: "consent" },
216
132
  identity: {
217
133
  url: "https://api.atlassian.com/me",
218
134
  label: "email",
219
135
  id: "account_id",
136
+ // `offline_access` is what yields a refresh token.
137
+ scopes: ["read:me", "offline_access"],
220
138
  },
221
139
  },
222
- scopeSets: {
223
- identity: {
224
- label: "Atlassian account (who you are)",
225
- access: "read",
226
- scopes: ["read:me"],
227
- },
228
- "jira.read": {
229
- label: "Jira issues and projects (read)",
230
- access: "read",
231
- scopes: ["read:jira-work", "read:jira-user"],
232
- },
233
- "jira.write": {
234
- label: "Jira issues (read and write)",
235
- access: "write",
236
- scopes: ["read:jira-work", "write:jira-work"],
237
- },
238
- "confluence.read": {
239
- label: "Confluence pages (read)",
240
- access: "read",
241
- scopes: [
242
- "read:confluence-content.all",
243
- "read:confluence-space.summary",
244
- ],
245
- },
246
- },
247
- alwaysRequested: ["offline_access"],
248
- access: ["read", "write"],
249
140
  hint: "Calls go to api.atlassian.com/ex/jira/<cloud id>/… ; the cloud id comes from /oauth/token/accessible-resources.",
250
141
  },
251
142
  "hubspot-oauth": {
@@ -253,7 +144,7 @@ export const OAUTH_PRESETS = {
253
144
  name: "HubSpot",
254
145
  description: "CRM contacts, companies and deals.",
255
146
  docs: "https://developers.hubspot.com/",
256
- api: "https://developers.hubspot.com/docs/reference/api",
147
+ api: "https://developers.hubspot.com/docs/api/working-with-oauth",
257
148
  hosts: ["api.hubapi.com"],
258
149
  auth: {
259
150
  kind: "oauth2-user",
@@ -263,39 +154,16 @@ export const OAUTH_PRESETS = {
263
154
  clientSecret: "HUBSPOT_OAUTH_CLIENT_SECRET",
264
155
  inject: "form",
265
156
  pkce: false,
266
- scopeDialect: "space",
267
- },
268
- scopeSets: {
269
- "contacts.read": {
270
- label: "Contacts (read)",
271
- access: "read",
272
- scopes: ["crm.objects.contacts.read"],
273
- },
274
- "contacts.write": {
275
- label: "Contacts (read and write)",
276
- access: "write",
277
- scopes: ["crm.objects.contacts.read", "crm.objects.contacts.write"],
278
- },
279
- "companies.read": {
280
- label: "Companies (read)",
281
- access: "read",
282
- scopes: ["crm.objects.companies.read"],
283
- },
284
- "deals.read": {
285
- label: "Deals (read)",
286
- access: "read",
287
- scopes: ["crm.objects.deals.read"],
288
- },
157
+ scopeSeparator: "space",
289
158
  },
290
- access: ["read", "write"],
291
- hint: "The app's scopes in HubSpot's developer console must match the sets apps declare, or HubSpot refuses the authorize URL.",
159
+ hint: "The app's scopes in HubSpot's developer console must include the scopes apps list, or HubSpot refuses the authorize URL.",
292
160
  },
293
161
  zoom: {
294
162
  id: "zoom",
295
163
  name: "Zoom",
296
164
  description: "The person's meetings and recordings.",
297
165
  docs: "https://marketplace.zoom.us/develop/create",
298
- api: "https://developers.zoom.us/docs/api/",
166
+ api: "https://developers.zoom.us/docs/integrations/oauth-scopes-overview/",
299
167
  hosts: ["api.zoom.us"],
300
168
  auth: {
301
169
  kind: "oauth2-user",
@@ -306,48 +174,22 @@ export const OAUTH_PRESETS = {
306
174
  clientSecret: "ZOOM_CLIENT_SECRET",
307
175
  inject: "basic",
308
176
  pkce: true,
309
- scopeDialect: "space",
177
+ scopeSeparator: "space",
310
178
  identity: {
311
179
  url: "https://api.zoom.us/v2/users/me",
312
180
  label: "email",
313
181
  id: "id",
314
- },
315
- },
316
- scopeSets: {
317
- identity: {
318
- label: "Zoom account (who you are)",
319
- access: "read",
320
182
  scopes: ["user:read:user"],
321
183
  },
322
- "meetings.read": {
323
- label: "Meetings (read)",
324
- access: "read",
325
- scopes: ["meeting:read:meeting", "meeting:read:list_meetings"],
326
- },
327
- "meetings.write": {
328
- label: "Meetings (read, create and change)",
329
- access: "write",
330
- scopes: [
331
- "meeting:read:meeting",
332
- "meeting:write:meeting",
333
- "meeting:update:meeting",
334
- ],
335
- },
336
- "recordings.read": {
337
- label: "Cloud recordings (read)",
338
- access: "read",
339
- scopes: ["cloud_recording:read:list_user_recordings"],
340
- },
341
184
  },
342
- access: ["read", "write"],
343
- hint: "A General app with user-managed OAuth; Zoom's granular scope names are the ones listed here.",
185
+ hint: "A General app with user-managed OAuth, using Zoom's granular scope names.",
344
186
  },
345
187
  dropbox: {
346
188
  id: "dropbox",
347
189
  name: "Dropbox",
348
190
  description: "Files and folders in the person's Dropbox.",
349
191
  docs: "https://www.dropbox.com/developers/apps",
350
- api: "https://www.dropbox.com/developers/documentation/http/documentation",
192
+ api: "https://developers.dropbox.com/oauth-guide",
351
193
  hosts: ["api.dropboxapi.com", "content.dropboxapi.com"],
352
194
  auth: {
353
195
  kind: "oauth2-user",
@@ -358,76 +200,19 @@ export const OAUTH_PRESETS = {
358
200
  clientSecret: "DROPBOX_OAUTH_CLIENT_SECRET",
359
201
  inject: "form",
360
202
  pkce: true,
361
- scopeDialect: "space",
203
+ scopeSeparator: "space",
362
204
  authorizeParams: { token_access_type: "offline" },
205
+ // Dropbox grants account_info.read to every app.
206
+ tolerated: ["account_info.read"],
363
207
  },
364
- // Dropbox is POST throughout, so each read set lists the read calls
365
- // it unlocks under `posts`; everything else needs the write set.
366
- scopeSets: {
367
- identity: {
368
- label: "Dropbox account (who you are)",
369
- access: "read",
370
- scopes: ["account_info.read"],
371
- routes: [
372
- {
373
- host: "api.dropboxapi.com",
374
- paths: [],
375
- posts: [
376
- "^/2/users/get_current_account$",
377
- "^/2/users/get_space_usage$",
378
- ],
379
- },
380
- ],
381
- },
382
- "files.read": {
383
- label: "Files and folders (read)",
384
- access: "read",
385
- scopes: ["files.metadata.read", "files.content.read"],
386
- routes: [
387
- {
388
- host: "api.dropboxapi.com",
389
- paths: [],
390
- posts: [
391
- "^/2/files/list_folder$",
392
- "^/2/files/list_folder/continue$",
393
- "^/2/files/get_metadata$",
394
- "^/2/files/search_v2$",
395
- "^/2/files/search/continue_v2$",
396
- "^/2/files/get_temporary_link$",
397
- "^/2/files/list_revisions$",
398
- ],
399
- },
400
- {
401
- host: "content.dropboxapi.com",
402
- paths: [],
403
- posts: [
404
- "^/2/files/download$",
405
- "^/2/files/get_thumbnail_v2$",
406
- "^/2/files/export$",
407
- ],
408
- },
409
- ],
410
- },
411
- "files.write": {
412
- label: "Files and folders (read and write)",
413
- access: "write",
414
- scopes: [
415
- "files.metadata.read",
416
- "files.content.read",
417
- "files.metadata.write",
418
- "files.content.write",
419
- ],
420
- },
421
- },
422
- access: ["read", "write"],
423
- hint: 'Dropbox\'s API is POST throughout: a read set unlocks its listed read calls, anything else needs access: "write". `token_access_type=offline` is what yields a refresh token.',
208
+ hint: "`token_access_type=offline` is what yields a refresh token.",
424
209
  },
425
210
  spotify: {
426
211
  id: "spotify",
427
212
  name: "Spotify",
428
213
  description: "The person's library, playlists and listening.",
429
214
  docs: "https://developer.spotify.com/dashboard",
430
- api: "https://developer.spotify.com/documentation/web-api",
215
+ api: "https://developer.spotify.com/documentation/web-api/concepts/scopes",
431
216
  hosts: ["api.spotify.com"],
432
217
  auth: {
433
218
  kind: "oauth2-user",
@@ -437,610 +222,14 @@ export const OAUTH_PRESETS = {
437
222
  clientSecret: "SPOTIFY_CLIENT_SECRET",
438
223
  inject: "basic",
439
224
  pkce: true,
440
- scopeDialect: "space",
225
+ scopeSeparator: "space",
226
+ // GET /v1/me names the account under any token.
441
227
  identity: {
442
228
  url: "https://api.spotify.com/v1/me",
443
229
  label: "display_name",
444
230
  id: "id",
445
231
  },
446
232
  },
447
- scopeSets: {
448
- identity: {
449
- label: "Spotify account (who you are)",
450
- access: "read",
451
- scopes: ["user-read-email", "user-read-private"],
452
- },
453
- "library.read": {
454
- label: "Saved tracks and albums (read)",
455
- access: "read",
456
- scopes: ["user-library-read"],
457
- },
458
- "playlists.read": {
459
- label: "Playlists (read)",
460
- access: "read",
461
- scopes: ["playlist-read-private", "playlist-read-collaborative"],
462
- },
463
- "playlists.write": {
464
- label: "Playlists (read and change)",
465
- access: "write",
466
- scopes: [
467
- "playlist-read-private",
468
- "playlist-modify-private",
469
- "playlist-modify-public",
470
- ],
471
- },
472
- },
473
- access: ["read", "write"],
474
- },
475
- "x-oauth": {
476
- id: "x-oauth",
477
- name: "X (OAuth 2)",
478
- description: "Posts and the person's profile on X, over the v2 API.",
479
- docs: "https://developer.x.com/en/portal/dashboard",
480
- api: "https://docs.x.com/x-api",
481
- hosts: ["api.x.com"],
482
- auth: {
483
- kind: "oauth2-user",
484
- authorizeUrl: "https://x.com/i/oauth2/authorize",
485
- tokenUrl: "https://api.x.com/2/oauth2/token",
486
- revokeUrl: "https://api.x.com/2/oauth2/revoke",
487
- clientId: "X_OAUTH_CLIENT_ID",
488
- clientSecret: "X_OAUTH_CLIENT_SECRET",
489
- inject: "basic",
490
- pkce: true,
491
- scopeDialect: "space",
492
- identity: {
493
- url: "https://api.x.com/2/users/me",
494
- label: "data.username",
495
- id: "data.id",
496
- },
497
- },
498
- scopeSets: {
499
- identity: {
500
- label: "X account (who you are)",
501
- access: "read",
502
- scopes: ["users.read", "tweet.read"],
503
- },
504
- "posts.read": {
505
- label: "Posts and timelines (read)",
506
- access: "read",
507
- scopes: ["tweet.read", "users.read"],
508
- },
509
- "posts.write": {
510
- label: "Posts (read and publish)",
511
- access: "write",
512
- scopes: ["tweet.read", "users.read", "tweet.write"],
513
- },
514
- },
515
- alwaysRequested: ["offline.access"],
516
- access: ["read", "write"],
517
- hint: "OAuth 2 with PKCE on X's v2 API; X's OAuth 1.0a (the v1.1 media upload) stays an app secret.",
518
- },
519
- // Social networks, tagged `social`: the clients Postiz connected its
520
- // channels with, once catalog entries every gateway reserved names for.
521
- linkedin: {
522
- id: "linkedin",
523
- name: "LinkedIn",
524
- description: "Posts on a member's profile and on the company pages they admin.",
525
- docs: "https://www.linkedin.com/developers/apps",
526
- api: "https://learn.microsoft.com/linkedin/",
527
- hosts: ["api.linkedin.com"],
528
- auth: {
529
- kind: "oauth2-user",
530
- authorizeUrl: "https://www.linkedin.com/oauth/v2/authorization",
531
- tokenUrl: "https://www.linkedin.com/oauth/v2/accessToken",
532
- clientId: "LINKEDIN_CLIENT_ID",
533
- clientSecret: "LINKEDIN_CLIENT_SECRET",
534
- inject: "form",
535
- // PKCE is for LinkedIn's native apps; a web client sends the secret.
536
- pkce: false,
537
- scopeDialect: "space",
538
- identity: {
539
- url: "https://api.linkedin.com/v2/userinfo",
540
- label: "name",
541
- id: "sub",
542
- },
543
- },
544
- scopeSets: {
545
- identity: {
546
- label: "LinkedIn account (who you are)",
547
- access: "read",
548
- scopes: ["openid", "profile"],
549
- },
550
- "posts.write": {
551
- label: "Posts on your profile",
552
- access: "write",
553
- scopes: ["openid", "profile", "w_member_social"],
554
- },
555
- "pages.write": {
556
- label: "Company pages you admin (read and post)",
557
- access: "write",
558
- scopes: [
559
- "rw_organization_admin",
560
- "w_organization_social",
561
- "r_organization_social",
562
- ],
563
- },
564
- },
565
- access: ["read", "write"],
566
- tags: ["social"],
567
- hint: "Add the Sign In with LinkedIn and Share on LinkedIn products to the app; company pages need the Community Management API. Refresh tokens come to approved partners only. Not yet tried against a live connect.",
568
- },
569
- facebook: {
570
- id: "facebook",
571
- name: "Facebook Pages",
572
- description: "Facebook pages, and the Instagram business accounts linked to them.",
573
- docs: "https://developers.facebook.com/apps",
574
- api: "https://developers.facebook.com/docs/graph-api",
575
- hosts: ["graph.facebook.com"],
576
- auth: {
577
- kind: "oauth2-user",
578
- authorizeUrl: "https://www.facebook.com/v25.0/dialog/oauth",
579
- tokenUrl: "https://graph.facebook.com/v25.0/oauth/access_token",
580
- clientId: "FACEBOOK_APP_ID",
581
- clientSecret: "FACEBOOK_APP_SECRET",
582
- inject: "form",
583
- pkce: false,
584
- scopeDialect: "comma",
585
- identity: {
586
- url: "https://graph.facebook.com/v25.0/me",
587
- label: "name",
588
- id: "id",
589
- },
590
- },
591
- scopeSets: {
592
- identity: {
593
- label: "Facebook account (who you are)",
594
- access: "read",
595
- scopes: ["public_profile"],
596
- },
597
- "pages.read": {
598
- label: "Pages you manage and their insights (read)",
599
- access: "read",
600
- scopes: ["pages_show_list", "pages_read_engagement", "read_insights"],
601
- },
602
- "pages.write": {
603
- label: "Pages you manage (read, post and reply)",
604
- access: "write",
605
- scopes: [
606
- "pages_show_list",
607
- "business_management",
608
- "pages_manage_posts",
609
- "pages_manage_engagement",
610
- "pages_read_engagement",
611
- ],
612
- },
613
- "instagram.write": {
614
- label: "Instagram business accounts linked to your pages",
615
- access: "write",
616
- scopes: [
617
- "pages_show_list",
618
- "instagram_basic",
619
- "instagram_content_publish",
620
- "instagram_manage_comments",
621
- "instagram_manage_insights",
622
- ],
623
- },
624
- },
625
- access: ["read", "write"],
626
- tags: ["social"],
627
- hint: "A Business app with Facebook Login. Meta issues no refresh token: a person reconnects when the token expires. Not yet tried against a live connect.",
628
- },
629
- instagram: {
630
- id: "instagram",
631
- name: "Instagram",
632
- description: "An Instagram professional account on its own, without a Facebook page.",
633
- docs: "https://developers.facebook.com/apps",
634
- api: "https://developers.facebook.com/docs/instagram-platform",
635
- hosts: ["graph.instagram.com"],
636
- auth: {
637
- kind: "oauth2-user",
638
- authorizeUrl: "https://www.instagram.com/oauth/authorize",
639
- tokenUrl: "https://api.instagram.com/oauth/access_token",
640
- clientId: "INSTAGRAM_APP_ID",
641
- clientSecret: "INSTAGRAM_APP_SECRET",
642
- inject: "form",
643
- pkce: false,
644
- scopeDialect: "comma",
645
- identity: {
646
- url: "https://graph.instagram.com/me?fields=username",
647
- label: "username",
648
- id: "id",
649
- },
650
- },
651
- scopeSets: {
652
- identity: {
653
- label: "Instagram account (who you are)",
654
- access: "read",
655
- scopes: ["instagram_business_basic"],
656
- },
657
- "insights.read": {
658
- label: "Insights (read)",
659
- access: "read",
660
- scopes: [
661
- "instagram_business_basic",
662
- "instagram_business_manage_insights",
663
- ],
664
- },
665
- "posts.write": {
666
- label: "Posts and comments",
667
- access: "write",
668
- scopes: [
669
- "instagram_business_basic",
670
- "instagram_business_content_publish",
671
- "instagram_business_manage_comments",
672
- ],
673
- },
674
- },
675
- access: ["read", "write"],
676
- tags: ["social"],
677
- hint: "The Instagram API with Instagram Login, on a Meta app. The token lasts an hour; the long-lived exchange isn't done by the Gateway. Not yet tried against a live connect.",
678
- },
679
- threads: {
680
- id: "threads",
681
- name: "Threads",
682
- description: "Posts, replies and insights on a Threads profile.",
683
- docs: "https://developers.facebook.com/apps",
684
- api: "https://developers.facebook.com/docs/threads",
685
- hosts: ["graph.threads.net"],
686
- auth: {
687
- kind: "oauth2-user",
688
- authorizeUrl: "https://threads.net/oauth/authorize",
689
- tokenUrl: "https://graph.threads.net/oauth/access_token",
690
- clientId: "THREADS_APP_ID",
691
- clientSecret: "THREADS_APP_SECRET",
692
- inject: "form",
693
- pkce: false,
694
- scopeDialect: "comma",
695
- identity: {
696
- url: "https://graph.threads.net/v1.0/me?fields=id,username",
697
- label: "username",
698
- id: "id",
699
- },
700
- },
701
- scopeSets: {
702
- identity: {
703
- label: "Threads profile (who you are)",
704
- access: "read",
705
- scopes: ["threads_basic"],
706
- },
707
- "insights.read": {
708
- label: "Insights (read)",
709
- access: "read",
710
- scopes: ["threads_basic", "threads_manage_insights"],
711
- },
712
- "posts.write": {
713
- label: "Posts and replies",
714
- access: "write",
715
- scopes: [
716
- "threads_basic",
717
- "threads_content_publish",
718
- "threads_manage_replies",
719
- ],
720
- },
721
- },
722
- access: ["read", "write"],
723
- tags: ["social"],
724
- hint: "The Threads use case on a Meta app. The token lasts an hour; the long-lived exchange isn't done by the Gateway. Not yet tried against a live connect.",
725
- },
726
- youtube: {
727
- id: "youtube",
728
- name: "YouTube",
729
- description: "YouTube channels and their analytics, and Google Business Profiles.",
730
- docs: "https://console.cloud.google.com/apis/credentials",
731
- api: "https://developers.google.com/youtube/v3",
732
- hosts: [
733
- "www.googleapis.com",
734
- "youtubeanalytics.googleapis.com",
735
- "mybusinessaccountmanagement.googleapis.com",
736
- "mybusinessbusinessinformation.googleapis.com",
737
- "mybusiness.googleapis.com",
738
- "businessprofileperformance.googleapis.com",
739
- "openidconnect.googleapis.com",
740
- ],
741
- auth: {
742
- kind: "oauth2-user",
743
- authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
744
- tokenUrl: "https://oauth2.googleapis.com/token",
745
- revokeUrl: "https://oauth2.googleapis.com/revoke",
746
- clientId: "YOUTUBE_CLIENT_ID",
747
- clientSecret: "YOUTUBE_CLIENT_SECRET",
748
- inject: "form",
749
- pkce: true,
750
- scopeDialect: "google",
751
- authorizeParams: { access_type: "offline", prompt: "consent" },
752
- identity: {
753
- url: "https://openidconnect.googleapis.com/v1/userinfo",
754
- label: "email",
755
- id: "sub",
756
- },
757
- },
758
- scopeSets: {
759
- identity: {
760
- label: "Google account (who you are)",
761
- access: "read",
762
- scopes: [
763
- "https://www.googleapis.com/auth/userinfo.profile",
764
- "https://www.googleapis.com/auth/userinfo.email",
765
- ],
766
- },
767
- "youtube.read": {
768
- label: "YouTube channels and analytics (read)",
769
- access: "read",
770
- scopes: [
771
- "https://www.googleapis.com/auth/youtube.readonly",
772
- "https://www.googleapis.com/auth/yt-analytics.readonly",
773
- ],
774
- },
775
- "youtube.write": {
776
- label: "YouTube channels (upload and manage)",
777
- access: "write",
778
- scopes: [
779
- "https://www.googleapis.com/auth/youtube",
780
- "https://www.googleapis.com/auth/youtube.upload",
781
- "https://www.googleapis.com/auth/youtube.force-ssl",
782
- ],
783
- },
784
- "business.write": {
785
- label: "Google Business Profiles (read and post)",
786
- access: "write",
787
- scopes: ["https://www.googleapis.com/auth/business.manage"],
788
- },
789
- },
790
- access: ["read", "write"],
791
- tags: ["social"],
792
- hint: "Its own Google Cloud project, apart from google-workspace's: YouTube's scopes need their own verification. Not yet tried against a live connect.",
793
- },
794
- reddit: {
795
- id: "reddit",
796
- name: "Reddit",
797
- description: "Reading subreddits and posting as the person.",
798
- docs: "https://www.reddit.com/prefs/apps",
799
- api: "https://www.reddit.com/dev/api",
800
- hosts: ["oauth.reddit.com"],
801
- auth: {
802
- kind: "oauth2-user",
803
- authorizeUrl: "https://www.reddit.com/api/v1/authorize",
804
- tokenUrl: "https://www.reddit.com/api/v1/access_token",
805
- clientId: "REDDIT_CLIENT_ID",
806
- clientSecret: "REDDIT_CLIENT_SECRET",
807
- inject: "basic",
808
- pkce: false,
809
- scopeDialect: "space",
810
- // A refresh token only with a permanent grant.
811
- authorizeParams: { duration: "permanent" },
812
- identity: {
813
- url: "https://oauth.reddit.com/api/v1/me",
814
- label: "name",
815
- id: "id",
816
- },
817
- },
818
- scopeSets: {
819
- identity: {
820
- label: "Reddit account (who you are)",
821
- access: "read",
822
- scopes: ["identity"],
823
- },
824
- "posts.read": {
825
- label: "Subreddits and posts (read)",
826
- access: "read",
827
- scopes: ["identity", "read"],
828
- },
829
- "posts.write": {
830
- label: "Posts (read, submit and flair)",
831
- access: "write",
832
- scopes: ["identity", "read", "submit", "flair"],
833
- },
834
- },
835
- access: ["read", "write"],
836
- tags: ["social"],
837
- hint: "A web app on Reddit's apps page. Not yet tried against a live connect.",
838
- },
839
- pinterest: {
840
- id: "pinterest",
841
- name: "Pinterest",
842
- description: "Boards and pins on a Pinterest account.",
843
- docs: "https://developers.pinterest.com/apps/",
844
- api: "https://developers.pinterest.com/docs/api/v5/",
845
- hosts: ["api.pinterest.com"],
846
- auth: {
847
- kind: "oauth2-user",
848
- authorizeUrl: "https://www.pinterest.com/oauth/",
849
- tokenUrl: "https://api.pinterest.com/v5/oauth/token",
850
- clientId: "PINTEREST_CLIENT_ID",
851
- clientSecret: "PINTEREST_CLIENT_SECRET",
852
- inject: "basic",
853
- pkce: false,
854
- scopeDialect: "comma",
855
- identity: {
856
- url: "https://api.pinterest.com/v5/user_account",
857
- label: "username",
858
- id: "id",
859
- },
860
- },
861
- scopeSets: {
862
- identity: {
863
- label: "Pinterest account (who you are)",
864
- access: "read",
865
- scopes: ["user_accounts:read"],
866
- },
867
- "pins.read": {
868
- label: "Boards and pins (read)",
869
- access: "read",
870
- scopes: ["user_accounts:read", "boards:read", "pins:read"],
871
- },
872
- "pins.write": {
873
- label: "Boards and pins (read and write)",
874
- access: "write",
875
- scopes: [
876
- "user_accounts:read",
877
- "boards:read",
878
- "boards:write",
879
- "pins:read",
880
- "pins:write",
881
- ],
882
- },
883
- },
884
- access: ["read", "write"],
885
- tags: ["social"],
886
- hint: "Not yet tried against a live connect.",
887
- },
888
- discord: {
889
- id: "discord",
890
- name: "Discord",
891
- description: "The person's Discord account and servers, and installing a bot in a server.",
892
- docs: "https://discord.com/developers/applications",
893
- api: "https://discord.com/developers/docs/reference",
894
- hosts: ["discord.com"],
895
- auth: {
896
- kind: "oauth2-user",
897
- authorizeUrl: "https://discord.com/oauth2/authorize",
898
- tokenUrl: "https://discord.com/api/oauth2/token",
899
- revokeUrl: "https://discord.com/api/oauth2/token/revoke",
900
- clientId: "DISCORD_CLIENT_ID",
901
- clientSecret: "DISCORD_CLIENT_SECRET",
902
- inject: "basic",
903
- pkce: true,
904
- scopeDialect: "space",
905
- identity: {
906
- url: "https://discord.com/api/users/@me",
907
- label: "username",
908
- id: "id",
909
- },
910
- },
911
- scopeSets: {
912
- identity: {
913
- label: "Discord account (who you are)",
914
- access: "read",
915
- scopes: ["identify"],
916
- },
917
- "servers.read": {
918
- label: "Servers you are in (read)",
919
- access: "read",
920
- scopes: ["identify", "guilds"],
921
- },
922
- "bot.write": {
923
- label: "Add the bot to a server",
924
- access: "write",
925
- scopes: ["identify", "guilds", "bot"],
926
- },
927
- },
928
- access: ["read", "write"],
929
- tags: ["social"],
930
- hint: "Posting as the bot uses the bot's own token, an app secret the Gateway doesn't hold. Not yet tried against a live connect.",
931
- },
932
- kick: {
933
- id: "kick",
934
- name: "Kick",
935
- description: "A Kick channel and its chat.",
936
- docs: "https://kick.com/settings/developer",
937
- api: "https://docs.kick.com/",
938
- hosts: ["api.kick.com"],
939
- auth: {
940
- kind: "oauth2-user",
941
- authorizeUrl: "https://id.kick.com/oauth/authorize",
942
- tokenUrl: "https://id.kick.com/oauth/token",
943
- clientId: "KICK_CLIENT_ID",
944
- clientSecret: "KICK_CLIENT_SECRET",
945
- inject: "form",
946
- pkce: true,
947
- scopeDialect: "space",
948
- },
949
- scopeSets: {
950
- identity: {
951
- label: "Kick account (who you are)",
952
- access: "read",
953
- scopes: ["user:read"],
954
- },
955
- "channel.read": {
956
- label: "Your channel (read)",
957
- access: "read",
958
- scopes: ["user:read", "channel:read"],
959
- },
960
- "chat.write": {
961
- label: "Chat messages",
962
- access: "write",
963
- scopes: ["user:read", "chat:write"],
964
- },
965
- },
966
- access: ["read", "write"],
967
- tags: ["social"],
968
- hint: "Not yet tried against a live connect.",
969
- },
970
- twitch: {
971
- id: "twitch",
972
- name: "Twitch",
973
- description: "A Twitch channel's chat and announcements.",
974
- docs: "https://dev.twitch.tv/console/apps",
975
- api: "https://dev.twitch.tv/docs/api/",
976
- hosts: ["api.twitch.tv"],
977
- auth: {
978
- kind: "oauth2-user",
979
- authorizeUrl: "https://id.twitch.tv/oauth2/authorize",
980
- tokenUrl: "https://id.twitch.tv/oauth2/token",
981
- clientId: "TWITCH_CLIENT_ID",
982
- clientSecret: "TWITCH_CLIENT_SECRET",
983
- inject: "form",
984
- pkce: false,
985
- scopeDialect: "space",
986
- },
987
- scopeSets: {
988
- "chat.read": {
989
- label: "Chat (read)",
990
- access: "read",
991
- scopes: ["user:read:chat"],
992
- },
993
- "chat.write": {
994
- label: "Chat messages and announcements",
995
- access: "write",
996
- scopes: [
997
- "user:read:chat",
998
- "user:write:chat",
999
- "moderator:manage:announcements",
1000
- ],
1001
- },
1002
- },
1003
- access: ["read", "write"],
1004
- tags: ["social"],
1005
- hint: "Helix also wants the public client id as a Client-Id header on every call. Not yet tried against a live connect.",
1006
- },
1007
- tumblr: {
1008
- id: "tumblr",
1009
- name: "Tumblr",
1010
- description: "Posting to the person's Tumblr blogs.",
1011
- docs: "https://www.tumblr.com/oauth/apps",
1012
- api: "https://www.tumblr.com/docs/en/api/v2",
1013
- hosts: ["api.tumblr.com"],
1014
- auth: {
1015
- kind: "oauth2-user",
1016
- authorizeUrl: "https://www.tumblr.com/oauth2/authorize",
1017
- tokenUrl: "https://api.tumblr.com/v2/oauth2/token",
1018
- clientId: "TUMBLR_CLIENT_ID",
1019
- clientSecret: "TUMBLR_CLIENT_SECRET",
1020
- inject: "form",
1021
- pkce: false,
1022
- scopeDialect: "space",
1023
- identity: {
1024
- url: "https://api.tumblr.com/v2/user/info",
1025
- label: "response.user.name",
1026
- },
1027
- },
1028
- scopeSets: {
1029
- identity: {
1030
- label: "Tumblr account (who you are)",
1031
- access: "read",
1032
- scopes: ["basic"],
1033
- },
1034
- "posts.write": {
1035
- label: "Posts on your blogs",
1036
- access: "write",
1037
- scopes: ["basic", "write"],
1038
- },
1039
- },
1040
- alwaysRequested: ["offline_access"],
1041
- access: ["read", "write"],
1042
- tags: ["social"],
1043
- hint: "Not yet tried against a live connect.",
1044
233
  },
1045
234
  };
1046
235
  /** The preset ids, in the order the picker shows them. */