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

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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cloudflare/index.d.ts +2 -2
  3. package/dist/cloudflare/index.d.ts.map +1 -1
  4. package/dist/cloudflare/index.js +1 -1
  5. package/dist/cloudflare/sentinel.d.ts +39 -1
  6. package/dist/cloudflare/sentinel.d.ts.map +1 -1
  7. package/dist/cloudflare/sentinel.js +22 -1
  8. package/dist/cloudflare/worker.d.ts +205 -7
  9. package/dist/cloudflare/worker.d.ts.map +1 -1
  10. package/dist/cloudflare/worker.js +174 -11
  11. package/dist/internal/aiDefault.d.ts +141 -0
  12. package/dist/internal/aiDefault.d.ts.map +1 -0
  13. package/dist/internal/aiDefault.js +204 -0
  14. package/dist/internal/aiProviders.d.ts +4 -1
  15. package/dist/internal/aiProviders.d.ts.map +1 -1
  16. package/dist/internal/aiProviders.js +5 -1
  17. package/dist/internal/appSettings.d.ts +20 -2
  18. package/dist/internal/appSettings.d.ts.map +1 -1
  19. package/dist/internal/appSettings.js +53 -7
  20. package/dist/internal/audit.d.ts +108 -0
  21. package/dist/internal/audit.d.ts.map +1 -0
  22. package/dist/internal/audit.js +235 -0
  23. package/dist/internal/capabilities.d.ts +2315 -141
  24. package/dist/internal/capabilities.d.ts.map +1 -1
  25. package/dist/internal/capabilities.js +122 -0
  26. package/dist/internal/catalog/commerce.d.ts +394 -0
  27. package/dist/internal/catalog/commerce.d.ts.map +1 -0
  28. package/dist/internal/catalog/commerce.js +477 -0
  29. package/dist/internal/catalog/comms.d.ts +338 -0
  30. package/dist/internal/catalog/comms.d.ts.map +1 -0
  31. package/dist/internal/catalog/comms.js +477 -0
  32. package/dist/internal/catalog/index.d.ts +1936 -0
  33. package/dist/internal/catalog/index.d.ts.map +1 -0
  34. package/dist/internal/catalog/index.js +19 -0
  35. package/dist/internal/catalog/inference.d.ts +393 -0
  36. package/dist/internal/catalog/inference.d.ts.map +1 -0
  37. package/dist/internal/catalog/inference.js +457 -0
  38. package/dist/internal/catalog/infra.d.ts +423 -0
  39. package/dist/internal/catalog/infra.d.ts.map +1 -0
  40. package/dist/internal/catalog/infra.js +543 -0
  41. package/dist/internal/catalog/search.d.ts +400 -0
  42. package/dist/internal/catalog/search.d.ts.map +1 -0
  43. package/dist/internal/catalog/search.js +463 -0
  44. package/dist/internal/devProviders.d.ts +2 -2
  45. package/dist/internal/devProviders.d.ts.map +1 -1
  46. package/dist/internal/devProviders.js +19 -1
  47. package/dist/internal/events.d.ts +13 -2
  48. package/dist/internal/events.d.ts.map +1 -1
  49. package/dist/internal/events.js +21 -3
  50. package/dist/internal/gatewaySettings.d.ts +2 -1
  51. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  52. package/dist/internal/gatewaySettings.js +10 -2
  53. package/dist/internal/index.d.ts +5 -0
  54. package/dist/internal/index.d.ts.map +1 -1
  55. package/dist/internal/index.js +5 -0
  56. package/dist/internal/oauthPresets.d.ts +29 -0
  57. package/dist/internal/oauthPresets.d.ts.map +1 -0
  58. package/dist/internal/oauthPresets.js +534 -0
  59. package/dist/internal/oauthScopes.d.ts +56 -1
  60. package/dist/internal/oauthScopes.d.ts.map +1 -1
  61. package/dist/internal/oauthScopes.js +98 -4
  62. package/dist/internal/oauthSpec.d.ts +59 -0
  63. package/dist/internal/oauthSpec.d.ts.map +1 -0
  64. package/dist/internal/oauthSpec.js +676 -0
  65. package/dist/internal/protocol.d.ts +11 -0
  66. package/dist/internal/protocol.d.ts.map +1 -1
  67. package/dist/internal/protocol.js +9 -0
  68. package/dist/internal/providerRegistry.d.ts +17 -6
  69. package/dist/internal/providerRegistry.d.ts.map +1 -1
  70. package/dist/internal/providerRegistry.js +51 -2
  71. package/dist/internal/providerSpec.d.ts +11 -7
  72. package/dist/internal/providerSpec.d.ts.map +1 -1
  73. package/dist/internal/providerSpec.js +142 -33
  74. package/dist/internal/signInProviders.d.ts +104 -0
  75. package/dist/internal/signInProviders.d.ts.map +1 -0
  76. package/dist/internal/signInProviders.js +245 -0
  77. package/dist/server/ai.d.ts +45 -0
  78. package/dist/server/ai.d.ts.map +1 -0
  79. package/dist/server/ai.js +40 -0
  80. package/dist/server/connectUrl.d.ts +21 -0
  81. package/dist/server/connectUrl.d.ts.map +1 -0
  82. package/dist/server/connectUrl.js +37 -0
  83. package/dist/server/gatewayFetch.d.ts.map +1 -1
  84. package/dist/server/gatewayFetch.js +72 -8
  85. package/dist/server/index.d.ts +4 -2
  86. package/dist/server/index.d.ts.map +1 -1
  87. package/dist/server/index.js +3 -1
  88. package/dist/server/integrationFetch.d.ts +39 -2
  89. package/dist/server/integrationFetch.d.ts.map +1 -1
  90. package/dist/server/integrationFetch.js +177 -13
  91. package/package.json +12 -12
@@ -2,6 +2,7 @@ export * from "./protocol.js";
2
2
  export * from "./tools.js";
3
3
  export * from "./capabilities.js";
4
4
  export * from "./aiProviders.js";
5
+ export * from "./aiDefault.js";
5
6
  export * from "./gatewaySettings.js";
6
7
  export * from "./appSettings.js";
7
8
  export * from "./email.js";
@@ -9,11 +10,15 @@ export * from "./members.js";
9
10
  export * from "./organizationName.js";
10
11
  export * from "./oauthClient.js";
11
12
  export * from "./oauthScopes.js";
13
+ export * from "./oauthSpec.js";
14
+ export * from "./oauthPresets.js";
12
15
  export * from "./providerApps.js";
13
16
  export * from "./events.js";
17
+ export * from "./audit.js";
14
18
  export * from "./providerSpec.js";
15
19
  export * from "./providerRegistry.js";
16
20
  export * from "./remoteMcp.js";
21
+ export * from "./signInProviders.js";
17
22
  export * from "./devProviders.js";
18
23
  export { ConfigurationError, IdentityError, getIdentityFromRequest, parsePublicKeys, resolveVerifyOptions, verifyIdentityJwt, verifyPublicMarkerJwt, verifySystemJwt, type IdentityResult, type RequestIdentityOptions, type VerifyOptions, } from "./identity.js";
19
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/internal/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/internal/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC"}
@@ -2,6 +2,7 @@ export * from "./protocol.js";
2
2
  export * from "./tools.js";
3
3
  export * from "./capabilities.js";
4
4
  export * from "./aiProviders.js";
5
+ export * from "./aiDefault.js";
5
6
  export * from "./gatewaySettings.js";
6
7
  export * from "./appSettings.js";
7
8
  export * from "./email.js";
@@ -9,10 +10,14 @@ export * from "./members.js";
9
10
  export * from "./organizationName.js";
10
11
  export * from "./oauthClient.js";
11
12
  export * from "./oauthScopes.js";
13
+ export * from "./oauthSpec.js";
14
+ export * from "./oauthPresets.js";
12
15
  export * from "./providerApps.js";
13
16
  export * from "./events.js";
17
+ export * from "./audit.js";
14
18
  export * from "./providerSpec.js";
15
19
  export * from "./providerRegistry.js";
16
20
  export * from "./remoteMcp.js";
21
+ export * from "./signInProviders.js";
17
22
  export * from "./devProviders.js";
18
23
  export { ConfigurationError, IdentityError, getIdentityFromRequest, parsePublicKeys, resolveVerifyOptions, verifyIdentityJwt, verifyPublicMarkerJwt, verifySystemJwt, } from "./identity.js";
@@ -0,0 +1,29 @@
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. Google, Microsoft, Slack and Discord
9
+ * are catalog clients with a `flow` and are offered in the same picker.
10
+ *
11
+ * Each preset parses with `parseProviderSpec` (oauthPresets.test.ts),
12
+ * names a console where the client is made, the API hosts the token is
13
+ * sent to, and scope sets an app declares under `scopes`. Scope strings
14
+ * are the providers' own, verified against their docs on 2026-10-25.
15
+ */
16
+ import { type RegistrableCapability } from "./providerSpec.js";
17
+ /** A preset's spec as JSON: the shape of ./oauthSpec.ts. */
18
+ export type OAuthPresetSpec = Readonly<Record<string, unknown>>;
19
+ export declare const OAUTH_PRESETS: Readonly<Record<string, OAuthPresetSpec>>;
20
+ /** The preset ids, in the order the picker shows them. */
21
+ export declare function oauthPresetIds(): string[];
22
+ /** A preset's parsed capability, for the picker's labels; throws for an unknown id (the test parses every preset). */
23
+ export declare function oauthPreset(id: string): RegistrableCapability | null;
24
+ /** Whether `capability` is an OAuth provider the Gateway runs the flow for. */
25
+ export declare function isOAuthProvider(capability: {
26
+ mode: string;
27
+ flow?: unknown;
28
+ } | undefined): boolean;
29
+ //# sourceMappingURL=oauthPresets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthPresets.d.ts","sourceRoot":"","sources":["../../src/internal/oauthPresets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,mBAAmB,CAAC;AAE3B,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhE,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAofnE,CAAC;AAEF,0DAA0D;AAC1D,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAED,sHAAsH;AACtH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAUpE;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAC7B,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GACvD,OAAO,CAET"}
@@ -0,0 +1,534 @@
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. Google, Microsoft, Slack and Discord
9
+ * are catalog clients with a `flow` and are offered in the same picker.
10
+ *
11
+ * Each preset parses with `parseProviderSpec` (oauthPresets.test.ts),
12
+ * names a console where the client is made, the API hosts the token is
13
+ * sent to, and scope sets an app declares under `scopes`. Scope strings
14
+ * are the providers' own, verified against their docs on 2026-10-25.
15
+ */
16
+ import { parseProviderSpec, } from "./providerSpec.js";
17
+ export const OAUTH_PRESETS = {
18
+ "github-oauth": {
19
+ id: "github-oauth",
20
+ name: "GitHub",
21
+ description: "Repositories, issues, pull requests and the person's profile.",
22
+ docs: "https://github.com/settings/developers",
23
+ api: "https://docs.github.com/rest",
24
+ hosts: ["api.github.com"],
25
+ auth: {
26
+ kind: "oauth2-user",
27
+ authorizeUrl: "https://github.com/login/oauth/authorize",
28
+ tokenUrl: "https://github.com/login/oauth/access_token",
29
+ revokeUrl: "https://api.github.com/applications/{client_id}/grant",
30
+ revokeMethod: "DELETE",
31
+ clientId: "GITHUB_OAUTH_CLIENT_ID",
32
+ clientSecret: "GITHUB_OAUTH_CLIENT_SECRET",
33
+ inject: "form",
34
+ pkce: true,
35
+ scopeDialect: "comma",
36
+ tokenHeaders: { Accept: "application/json" },
37
+ identity: {
38
+ url: "https://api.github.com/user",
39
+ label: "login",
40
+ id: "id",
41
+ },
42
+ },
43
+ scopeSets: {
44
+ identity: {
45
+ label: "GitHub account (who you are)",
46
+ access: "read",
47
+ scopes: ["read:user", "user:email"],
48
+ },
49
+ "repo.read": {
50
+ label: "Repositories (read, private ones included)",
51
+ access: "read",
52
+ scopes: ["repo"],
53
+ },
54
+ "repo.write": {
55
+ label: "Repositories (read and write)",
56
+ access: "write",
57
+ scopes: ["repo"],
58
+ },
59
+ "org.read": {
60
+ label: "Organizations and teams (read)",
61
+ access: "read",
62
+ scopes: ["read:org"],
63
+ },
64
+ },
65
+ access: ["read", "write"],
66
+ 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.",
67
+ },
68
+ "notion-oauth": {
69
+ id: "notion-oauth",
70
+ name: "Notion",
71
+ description: "Pages and databases the person shares with the integration.",
72
+ docs: "https://www.notion.so/my-integrations",
73
+ api: "https://developers.notion.com/reference",
74
+ hosts: ["api.notion.com"],
75
+ auth: {
76
+ kind: "oauth2-user",
77
+ authorizeUrl: "https://api.notion.com/v1/oauth/authorize",
78
+ tokenUrl: "https://api.notion.com/v1/oauth/token",
79
+ clientId: "NOTION_OAUTH_CLIENT_ID",
80
+ clientSecret: "NOTION_OAUTH_CLIENT_SECRET",
81
+ inject: "basic",
82
+ pkce: false,
83
+ scopeDialect: "space",
84
+ authorizeParams: { owner: "user" },
85
+ },
86
+ // Notion reads with GET, and with POST for a search and a database
87
+ // query; the read set lists those two, so a read grant never creates
88
+ // or changes a page (POST /v1/pages, PATCH, DELETE need the write set).
89
+ scopeSets: {
90
+ "workspace.read": {
91
+ label: "Shared pages and databases (read)",
92
+ access: "read",
93
+ scopes: [],
94
+ routes: [
95
+ {
96
+ host: "api.notion.com",
97
+ paths: [
98
+ "/v1/users",
99
+ "/v1/users/",
100
+ "/v1/pages/",
101
+ "/v1/databases/",
102
+ "/v1/blocks/",
103
+ "/v1/comments",
104
+ ],
105
+ posts: ["^/v1/search$", "^/v1/databases/[^/]+/query$"],
106
+ },
107
+ ],
108
+ },
109
+ "workspace.write": {
110
+ label: "Shared pages and databases (read and write)",
111
+ access: "write",
112
+ scopes: [],
113
+ },
114
+ },
115
+ access: ["read", "write"],
116
+ 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.",
117
+ },
118
+ "linear-oauth": {
119
+ id: "linear-oauth",
120
+ name: "Linear",
121
+ description: "Issues, projects and teams, over Linear's GraphQL API.",
122
+ docs: "https://linear.app/settings/api/applications",
123
+ api: "https://developers.linear.app/docs/oauth/authentication",
124
+ hosts: ["api.linear.app"],
125
+ auth: {
126
+ kind: "oauth2-user",
127
+ authorizeUrl: "https://linear.app/oauth/authorize",
128
+ tokenUrl: "https://api.linear.app/oauth/token",
129
+ revokeUrl: "https://api.linear.app/oauth/revoke",
130
+ clientId: "LINEAR_OAUTH_CLIENT_ID",
131
+ clientSecret: "LINEAR_OAUTH_CLIENT_SECRET",
132
+ inject: "form",
133
+ pkce: false,
134
+ scopeDialect: "comma",
135
+ },
136
+ // Linear's API is one GraphQL POST, which the Gateway can't tell a
137
+ // query from a mutation on, so the one set is a write set: an app
138
+ // lists Linear with access: "write", and Linear's own `read` scope is
139
+ // what a read-only token holds.
140
+ scopeSets: {
141
+ "workspace.write": {
142
+ label: "Issues, projects and teams (read and write)",
143
+ access: "write",
144
+ scopes: ["read", "write"],
145
+ },
146
+ },
147
+ access: ["write"],
148
+ readMethods: [],
149
+ writeMethods: ["POST"],
150
+ hint: "Linear's API is GraphQL over POST, so every call needs access: \"write\"; a token's scopes decide what a query may change.",
151
+ },
152
+ "gitlab-oauth": {
153
+ id: "gitlab-oauth",
154
+ name: "GitLab",
155
+ description: "Projects, merge requests and issues on gitlab.com.",
156
+ docs: "https://gitlab.com/-/user_settings/applications",
157
+ api: "https://docs.gitlab.com/api/rest/",
158
+ hosts: ["gitlab.com"],
159
+ auth: {
160
+ kind: "oauth2-user",
161
+ authorizeUrl: "https://gitlab.com/oauth/authorize",
162
+ tokenUrl: "https://gitlab.com/oauth/token",
163
+ revokeUrl: "https://gitlab.com/oauth/revoke",
164
+ clientId: "GITLAB_OAUTH_CLIENT_ID",
165
+ clientSecret: "GITLAB_OAUTH_CLIENT_SECRET",
166
+ inject: "form",
167
+ pkce: true,
168
+ scopeDialect: "space",
169
+ identity: {
170
+ url: "https://gitlab.com/api/v4/user",
171
+ label: "username",
172
+ id: "id",
173
+ },
174
+ },
175
+ scopeSets: {
176
+ identity: {
177
+ label: "GitLab account (who you are)",
178
+ access: "read",
179
+ scopes: ["read_user"],
180
+ },
181
+ "api.read": {
182
+ label: "Projects, issues and merge requests (read)",
183
+ access: "read",
184
+ scopes: ["read_api"],
185
+ },
186
+ "api.write": {
187
+ label: "Projects, issues and merge requests (read and write)",
188
+ access: "write",
189
+ scopes: ["api"],
190
+ },
191
+ },
192
+ access: ["read", "write"],
193
+ hint: "A self-managed GitLab needs a custom provider with its own host in place of gitlab.com.",
194
+ },
195
+ atlassian: {
196
+ id: "atlassian",
197
+ name: "Atlassian (Jira and Confluence)",
198
+ description: "Jira issues and Confluence pages on the sites the person grants.",
199
+ docs: "https://developer.atlassian.com/console/myapps/",
200
+ api: "https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/",
201
+ hosts: ["api.atlassian.com"],
202
+ auth: {
203
+ kind: "oauth2-user",
204
+ authorizeUrl: "https://auth.atlassian.com/authorize",
205
+ tokenUrl: "https://auth.atlassian.com/oauth/token",
206
+ clientId: "ATLASSIAN_CLIENT_ID",
207
+ clientSecret: "ATLASSIAN_CLIENT_SECRET",
208
+ inject: "form",
209
+ pkce: false,
210
+ scopeDialect: "space",
211
+ authorizeParams: { audience: "api.atlassian.com", prompt: "consent" },
212
+ identity: {
213
+ url: "https://api.atlassian.com/me",
214
+ label: "email",
215
+ id: "account_id",
216
+ },
217
+ },
218
+ scopeSets: {
219
+ identity: {
220
+ label: "Atlassian account (who you are)",
221
+ access: "read",
222
+ scopes: ["read:me"],
223
+ },
224
+ "jira.read": {
225
+ label: "Jira issues and projects (read)",
226
+ access: "read",
227
+ scopes: ["read:jira-work", "read:jira-user"],
228
+ },
229
+ "jira.write": {
230
+ label: "Jira issues (read and write)",
231
+ access: "write",
232
+ scopes: ["read:jira-work", "write:jira-work"],
233
+ },
234
+ "confluence.read": {
235
+ label: "Confluence pages (read)",
236
+ access: "read",
237
+ scopes: [
238
+ "read:confluence-content.all",
239
+ "read:confluence-space.summary",
240
+ ],
241
+ },
242
+ },
243
+ alwaysRequested: ["offline_access"],
244
+ access: ["read", "write"],
245
+ hint: "Calls go to api.atlassian.com/ex/jira/<cloud id>/… ; the cloud id comes from /oauth/token/accessible-resources.",
246
+ },
247
+ "hubspot-oauth": {
248
+ id: "hubspot-oauth",
249
+ name: "HubSpot",
250
+ description: "CRM contacts, companies and deals.",
251
+ docs: "https://developers.hubspot.com/",
252
+ api: "https://developers.hubspot.com/docs/reference/api",
253
+ hosts: ["api.hubapi.com"],
254
+ auth: {
255
+ kind: "oauth2-user",
256
+ authorizeUrl: "https://app.hubspot.com/oauth/authorize",
257
+ tokenUrl: "https://api.hubapi.com/oauth/v1/token",
258
+ clientId: "HUBSPOT_OAUTH_CLIENT_ID",
259
+ clientSecret: "HUBSPOT_OAUTH_CLIENT_SECRET",
260
+ inject: "form",
261
+ pkce: false,
262
+ scopeDialect: "space",
263
+ },
264
+ scopeSets: {
265
+ "contacts.read": {
266
+ label: "Contacts (read)",
267
+ access: "read",
268
+ scopes: ["crm.objects.contacts.read"],
269
+ },
270
+ "contacts.write": {
271
+ label: "Contacts (read and write)",
272
+ access: "write",
273
+ scopes: ["crm.objects.contacts.read", "crm.objects.contacts.write"],
274
+ },
275
+ "companies.read": {
276
+ label: "Companies (read)",
277
+ access: "read",
278
+ scopes: ["crm.objects.companies.read"],
279
+ },
280
+ "deals.read": {
281
+ label: "Deals (read)",
282
+ access: "read",
283
+ scopes: ["crm.objects.deals.read"],
284
+ },
285
+ },
286
+ access: ["read", "write"],
287
+ hint: "The app's scopes in HubSpot's developer console must match the sets apps declare, or HubSpot refuses the authorize URL.",
288
+ },
289
+ zoom: {
290
+ id: "zoom",
291
+ name: "Zoom",
292
+ description: "The person's meetings and recordings.",
293
+ docs: "https://marketplace.zoom.us/develop/create",
294
+ api: "https://developers.zoom.us/docs/api/",
295
+ hosts: ["api.zoom.us"],
296
+ auth: {
297
+ kind: "oauth2-user",
298
+ authorizeUrl: "https://zoom.us/oauth/authorize",
299
+ tokenUrl: "https://zoom.us/oauth/token",
300
+ revokeUrl: "https://zoom.us/oauth/revoke",
301
+ clientId: "ZOOM_CLIENT_ID",
302
+ clientSecret: "ZOOM_CLIENT_SECRET",
303
+ inject: "basic",
304
+ pkce: true,
305
+ scopeDialect: "space",
306
+ identity: {
307
+ url: "https://api.zoom.us/v2/users/me",
308
+ label: "email",
309
+ id: "id",
310
+ },
311
+ },
312
+ scopeSets: {
313
+ identity: {
314
+ label: "Zoom account (who you are)",
315
+ access: "read",
316
+ scopes: ["user:read:user"],
317
+ },
318
+ "meetings.read": {
319
+ label: "Meetings (read)",
320
+ access: "read",
321
+ scopes: ["meeting:read:meeting", "meeting:read:list_meetings"],
322
+ },
323
+ "meetings.write": {
324
+ label: "Meetings (read, create and change)",
325
+ access: "write",
326
+ scopes: [
327
+ "meeting:read:meeting",
328
+ "meeting:write:meeting",
329
+ "meeting:update:meeting",
330
+ ],
331
+ },
332
+ "recordings.read": {
333
+ label: "Cloud recordings (read)",
334
+ access: "read",
335
+ scopes: ["cloud_recording:read:list_user_recordings"],
336
+ },
337
+ },
338
+ access: ["read", "write"],
339
+ hint: "A General app with user-managed OAuth; Zoom's granular scope names are the ones listed here.",
340
+ },
341
+ dropbox: {
342
+ id: "dropbox",
343
+ name: "Dropbox",
344
+ description: "Files and folders in the person's Dropbox.",
345
+ docs: "https://www.dropbox.com/developers/apps",
346
+ api: "https://www.dropbox.com/developers/documentation/http/documentation",
347
+ hosts: ["api.dropboxapi.com", "content.dropboxapi.com"],
348
+ auth: {
349
+ kind: "oauth2-user",
350
+ authorizeUrl: "https://www.dropbox.com/oauth2/authorize",
351
+ tokenUrl: "https://api.dropboxapi.com/oauth2/token",
352
+ revokeUrl: "https://api.dropboxapi.com/2/auth/token/revoke",
353
+ clientId: "DROPBOX_OAUTH_CLIENT_ID",
354
+ clientSecret: "DROPBOX_OAUTH_CLIENT_SECRET",
355
+ inject: "form",
356
+ pkce: true,
357
+ scopeDialect: "space",
358
+ authorizeParams: { token_access_type: "offline" },
359
+ },
360
+ // Dropbox is POST throughout, so each read set lists the read calls
361
+ // it unlocks under `posts`; everything else needs the write set.
362
+ scopeSets: {
363
+ identity: {
364
+ label: "Dropbox account (who you are)",
365
+ access: "read",
366
+ scopes: ["account_info.read"],
367
+ routes: [
368
+ {
369
+ host: "api.dropboxapi.com",
370
+ paths: [],
371
+ posts: [
372
+ "^/2/users/get_current_account$",
373
+ "^/2/users/get_space_usage$",
374
+ ],
375
+ },
376
+ ],
377
+ },
378
+ "files.read": {
379
+ label: "Files and folders (read)",
380
+ access: "read",
381
+ scopes: ["files.metadata.read", "files.content.read"],
382
+ routes: [
383
+ {
384
+ host: "api.dropboxapi.com",
385
+ paths: [],
386
+ posts: [
387
+ "^/2/files/list_folder$",
388
+ "^/2/files/list_folder/continue$",
389
+ "^/2/files/get_metadata$",
390
+ "^/2/files/search_v2$",
391
+ "^/2/files/search/continue_v2$",
392
+ "^/2/files/get_temporary_link$",
393
+ "^/2/files/list_revisions$",
394
+ ],
395
+ },
396
+ {
397
+ host: "content.dropboxapi.com",
398
+ paths: [],
399
+ posts: [
400
+ "^/2/files/download$",
401
+ "^/2/files/get_thumbnail_v2$",
402
+ "^/2/files/export$",
403
+ ],
404
+ },
405
+ ],
406
+ },
407
+ "files.write": {
408
+ label: "Files and folders (read and write)",
409
+ access: "write",
410
+ scopes: [
411
+ "files.metadata.read",
412
+ "files.content.read",
413
+ "files.metadata.write",
414
+ "files.content.write",
415
+ ],
416
+ },
417
+ },
418
+ access: ["read", "write"],
419
+ 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.',
420
+ },
421
+ spotify: {
422
+ id: "spotify",
423
+ name: "Spotify",
424
+ description: "The person's library, playlists and listening.",
425
+ docs: "https://developer.spotify.com/dashboard",
426
+ api: "https://developer.spotify.com/documentation/web-api",
427
+ hosts: ["api.spotify.com"],
428
+ auth: {
429
+ kind: "oauth2-user",
430
+ authorizeUrl: "https://accounts.spotify.com/authorize",
431
+ tokenUrl: "https://accounts.spotify.com/api/token",
432
+ clientId: "SPOTIFY_CLIENT_ID",
433
+ clientSecret: "SPOTIFY_CLIENT_SECRET",
434
+ inject: "basic",
435
+ pkce: true,
436
+ scopeDialect: "space",
437
+ identity: {
438
+ url: "https://api.spotify.com/v1/me",
439
+ label: "display_name",
440
+ id: "id",
441
+ },
442
+ },
443
+ scopeSets: {
444
+ identity: {
445
+ label: "Spotify account (who you are)",
446
+ access: "read",
447
+ scopes: ["user-read-email", "user-read-private"],
448
+ },
449
+ "library.read": {
450
+ label: "Saved tracks and albums (read)",
451
+ access: "read",
452
+ scopes: ["user-library-read"],
453
+ },
454
+ "playlists.read": {
455
+ label: "Playlists (read)",
456
+ access: "read",
457
+ scopes: ["playlist-read-private", "playlist-read-collaborative"],
458
+ },
459
+ "playlists.write": {
460
+ label: "Playlists (read and change)",
461
+ access: "write",
462
+ scopes: [
463
+ "playlist-read-private",
464
+ "playlist-modify-private",
465
+ "playlist-modify-public",
466
+ ],
467
+ },
468
+ },
469
+ access: ["read", "write"],
470
+ },
471
+ "x-oauth": {
472
+ id: "x-oauth",
473
+ name: "X (OAuth 2)",
474
+ description: "Posts and the person's profile on X, over the v2 API.",
475
+ docs: "https://developer.x.com/en/portal/dashboard",
476
+ api: "https://docs.x.com/x-api",
477
+ hosts: ["api.x.com"],
478
+ auth: {
479
+ kind: "oauth2-user",
480
+ authorizeUrl: "https://x.com/i/oauth2/authorize",
481
+ tokenUrl: "https://api.x.com/2/oauth2/token",
482
+ revokeUrl: "https://api.x.com/2/oauth2/revoke",
483
+ clientId: "X_OAUTH_CLIENT_ID",
484
+ clientSecret: "X_OAUTH_CLIENT_SECRET",
485
+ inject: "basic",
486
+ pkce: true,
487
+ scopeDialect: "space",
488
+ identity: {
489
+ url: "https://api.x.com/2/users/me",
490
+ label: "data.username",
491
+ id: "data.id",
492
+ },
493
+ },
494
+ scopeSets: {
495
+ identity: {
496
+ label: "X account (who you are)",
497
+ access: "read",
498
+ scopes: ["users.read", "tweet.read"],
499
+ },
500
+ "posts.read": {
501
+ label: "Posts and timelines (read)",
502
+ access: "read",
503
+ scopes: ["tweet.read", "users.read"],
504
+ },
505
+ "posts.write": {
506
+ label: "Posts (read and publish)",
507
+ access: "write",
508
+ scopes: ["tweet.read", "users.read", "tweet.write"],
509
+ },
510
+ },
511
+ alwaysRequested: ["offline.access"],
512
+ access: ["read", "write"],
513
+ hint: "OAuth 2 with PKCE on X's v2 API; X's OAuth 1.0a (the v1.1 media upload) stays an app secret.",
514
+ },
515
+ };
516
+ /** The preset ids, in the order the picker shows them. */
517
+ export function oauthPresetIds() {
518
+ return Object.keys(OAUTH_PRESETS);
519
+ }
520
+ /** A preset's parsed capability, for the picker's labels; throws for an unknown id (the test parses every preset). */
521
+ export function oauthPreset(id) {
522
+ const spec = OAUTH_PRESETS[id];
523
+ if (!spec)
524
+ return null;
525
+ const parsed = parseProviderSpec(spec);
526
+ if (!parsed.ok) {
527
+ throw new Error(`preset ${id} doesn't parse: ${JSON.stringify(parsed.problems)}`);
528
+ }
529
+ return parsed.capability;
530
+ }
531
+ /** Whether `capability` is an OAuth provider the Gateway runs the flow for. */
532
+ export function isOAuthProvider(capability) {
533
+ return capability?.mode === "oauth_client" && capability.flow !== undefined;
534
+ }