@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
@@ -1 +1 @@
1
- {"version":3,"file":"integrationFetch.d.ts","sourceRoot":"","sources":["../../src/server/integrationFetch.ts"],"names":[],"mappings":"AAAA,OAAO,EA2BL,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAE1B,KAAK,uBAAuB,EAO7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAO1B,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,QAAQ,EAAE,kBAAkB,GAAG,uBAAuB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACvE;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,QAAQ,EACR,GAAG,EAAE,MAAM,GACZ,EAAE,uBAAuB,GAAG,OAAO,KAAK,CAkGxC;AA+XD,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,UAAU,EAAE,OAAO,CAAC;IACpB,8BAA8B;IAC9B,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GACpD,IAAI,CAAC,qBAAqB,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,4BAA6B,SAAQ,iBAAiB;IACrE,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,4DAA4D;IAC5D,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,yFAAyF;AACzF,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,OAAO,GACjE,sBAAsB,GACtB,CAAC,SAAS,uBAAuB,GAC/B,4BAA4B,GAC5B,iBAAiB,CAAC;AAExB;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,CAAC,SAAS,YAAY,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,EACtC,EACA,GAAG,EACH,QAAQ,GACT,EAAE;IACD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,CAAC,CAAC;CACb,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAoElC;AA2FD,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,KAAM,YAAW,cAAc;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,cAAc;CAMtD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAWhC"}
1
+ {"version":3,"file":"integrationFetch.d.ts","sourceRoot":"","sources":["../../src/server/integrationFetch.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { CATALOG, ConfigurationError, GATEWAY_ERROR_HEADER, INTEGRATION_PROXY_PREFIX, OAUTH_TOKEN_BODY_LIMIT, allowedScopes, apiKeyHosts, authorizationFor, capabilitySettings, carriesToken, declaredScopeSets, describeTokenEndpoints, devProviderFile, grantedScopes, injectClientCredentials, integrationProxyPath, oauthClientCredentials, oauthClientOrigin, requestedScopes, requestedTokenScopes, resolveDevProvider, rewriteTokenRequestScope, scopeExcess, tokenEndpointFor, tokenGrantType, EMAIL_SENDERS_ENV, chooseEmailProvider, parseEmailList, parseEmailSender, } from "../internal/index.js";
1
+ import { ACCOUNT_HEADER, CATALOG, ConfigurationError, GATEWAY_ERROR_HEADER, INTEGRATION_PROXY_PREFIX, OAUTH_TOKEN_BODY_LIMIT, allowedScopes, apiKeyHosts, authorizationFor, capabilitySettings, carriesToken, declaredScopeSets, describeTokenEndpoints, devProviderFile, grantedScopes, heldTokenCallAllowed, injectClientCredentials, integrationProxyPath, oauthClientCredentials, oauthClientOrigin, oauthReadMethods, oauthWriteMethods, providerSettingPrefix, requestedScopes, requestedTokenScopes, resolveDevProvider, rewriteTokenRequestScope, scopeExcess, tokenEndpointFor, tokenGrantType, EMAIL_SENDERS_ENV, chooseEmailProvider, parseEmailList, parseEmailSender, } from "../internal/index.js";
2
2
  import { applyAttribution, checkAttribution, } from "./attribution.js";
3
3
  import { BINDING_ORIGIN, streamingRequest, } from "./gatewayFetch.js";
4
4
  /**
@@ -23,7 +23,7 @@ import { BINDING_ORIGIN, streamingRequest, } from "./gatewayFetch.js";
23
23
  * provider show <id> --save` writes it), put on the call as the gateway's
24
24
  * spec says.
25
25
  */
26
- export function integrationFetch({ env, provider, for: target, }) {
26
+ export function integrationFetch({ env, provider, for: target, account, }) {
27
27
  const catalogEntry = fetchableCapability(provider);
28
28
  checkAttribution(env, target, `integrationFetch("${provider}")`);
29
29
  const values = env;
@@ -36,8 +36,23 @@ export function integrationFetch({ env, provider, for: target, }) {
36
36
  const integration = catalogEntry ??
37
37
  (binding ? undefined : devRegisteredProvider(provider, values));
38
38
  const local = binding || !integration ? null : localCredentials(integration, values);
39
- if (!binding && !local && integration) {
40
- throw new ConfigurationError(`${integration.label} is unavailable: deploy with the EVERY_APP_GATEWAY binding, or set ${capabilitySettings(integration).join(", ")} in .dev.vars for local development.`);
39
+ // In dev an OAuth provider with a held token in `.dev.vars` serves API
40
+ // calls without the client (the token stands in for a connection).
41
+ const devToken = !binding && integration?.mode === "oauth_client" && integration.flow
42
+ ? stringValue(values[devAccessTokenName(provider)])
43
+ : undefined;
44
+ // An OAuth provider with a Gateway-owned flow answers an API call
45
+ // without the client (`integration_not_connected`, below); a token
46
+ // endpoint call without the client throws when it is made.
47
+ const decidesLater = integration?.mode === "oauth_client" && integration.flow !== undefined;
48
+ if (!binding && !local && !devToken && integration && !decidesLater) {
49
+ const names = [
50
+ ...capabilitySettings(integration),
51
+ ...(integration.mode === "oauth_client" && integration.flow
52
+ ? [devAccessTokenName(provider)]
53
+ : []),
54
+ ];
55
+ throw new ConfigurationError(`${integration.label} is unavailable: deploy with the EVERY_APP_GATEWAY binding, or set ${names.join(", ")} in .dev.vars for local development.`);
41
56
  }
42
57
  return async (input, init) => {
43
58
  const outbound = new Request(input, init);
@@ -55,6 +70,30 @@ export function integrationFetch({ env, provider, for: target, }) {
55
70
  return binding.fetch(streamingRequest(`${BINDING_ORIGIN}${integrationProxyPath(provider, upstream)}`, outbound, headers));
56
71
  }
57
72
  if (integration.mode === "oauth_client") {
73
+ // A call to the provider's API with the token the gateway holds
74
+ // (docs/design/2026-10-oauth-providers.md section 7), for an entry
75
+ // that supports the Gateway-owned flow; a token endpoint stays the
76
+ // app-driven call it always was.
77
+ const toApi = integration.flow !== undefined &&
78
+ upstream.protocol === "https:" &&
79
+ integration.api.includes(upstream.host) &&
80
+ !tokenEndpointFor(integration, upstream, binding && integration.hostSetting ? undefined : values);
81
+ if (toApi) {
82
+ if (binding) {
83
+ if (account)
84
+ headers.set(ACCOUNT_HEADER, account);
85
+ applyAttribution(headers, target);
86
+ return binding.fetch(streamingRequest(`${BINDING_ORIGIN}${integrationProxyPath(provider, upstream)}`, outbound, headers));
87
+ }
88
+ return devHeldTokenCall({
89
+ provider,
90
+ capability: integration,
91
+ outbound,
92
+ upstream,
93
+ headers,
94
+ values,
95
+ });
96
+ }
58
97
  return oauthClientCall({
59
98
  provider: provider,
60
99
  capability: integration,
@@ -92,6 +131,93 @@ export function integrationFetch({ env, provider, for: target, }) {
92
131
  return fetch(streamingRequest(upstream.toString(), outbound, headers));
93
132
  };
94
133
  }
134
+ /**
135
+ * The `.dev.vars` name of a held token for `provider` in local dev
136
+ * (`GOOGLE_WORKSPACE_ACCESS_TOKEN`, `GITHUB_ACCESS_TOKEN`): a token the
137
+ * developer obtained themselves, standing in for the connection the
138
+ * gateway would hold. Dev-only: the gateway never reads it.
139
+ */
140
+ export function devAccessTokenName(provider) {
141
+ return `${providerSettingPrefix(provider)}ACCESS_TOKEN`;
142
+ }
143
+ /**
144
+ * An app's call to an OAuth provider's API in local dev, with no
145
+ * gateway: the gateway's held-token checks in its order (the app's
146
+ * declared sets, the method, the route), then the developer's own token
147
+ * from `.dev.vars` as the bearer, straight to the provider.
148
+ */
149
+ async function devHeldTokenCall({ provider, capability, outbound, upstream, headers, values, }) {
150
+ const token = stringValue(values[devAccessTokenName(provider)]);
151
+ if (!token) {
152
+ return devRefusal(409, "integration_not_connected", `${capability.label} isn't connected here. In local dev, set ${devAccessTokenName(provider)} in .dev.vars to a token you obtained yourself; deployed, the person connects at /connect/${provider}.`, { connectPath: `/connect/${provider}`, reconnect: false });
153
+ }
154
+ const policy = capability.scopePolicy;
155
+ const grant = policy
156
+ ? devScopeSets(policy, provider, capability.label, values, "any")
157
+ : null;
158
+ if (grant && "problem" in grant) {
159
+ return devRefusal(403, grant.code, grant.problem);
160
+ }
161
+ const method = outbound.method.toUpperCase();
162
+ const reads = oauthReadMethods(capability).includes(method);
163
+ const writes = !reads &&
164
+ capability.access.includes("write") &&
165
+ oauthWriteMethods(capability).includes(method) &&
166
+ devDeclaredAccess(provider, values) === "write";
167
+ if (policy && grant) {
168
+ const allowed = heldTokenCallAllowed({
169
+ policy,
170
+ declaredSets: grant.sets,
171
+ // The developer's token stands for a connection granted every set.
172
+ connectionSets: grant.sets,
173
+ hosts: capability.api,
174
+ method,
175
+ upstream,
176
+ readMethods: oauthReadMethods(capability),
177
+ writes,
178
+ });
179
+ if (!allowed.ok) {
180
+ const status = {
181
+ integration_not_connected: 409,
182
+ integration_method_not_allowed: 405,
183
+ integration_scope_not_allowed: 403,
184
+ }[allowed.code];
185
+ return devRefusal(status, allowed.code, allowed.code === "integration_not_connected"
186
+ ? `${capability.label} is connected without ${allowed.missingSets.join(", ")}.`
187
+ : allowed.message);
188
+ }
189
+ }
190
+ else if (!reads && !writes) {
191
+ return devRefusal(405, "integration_method_not_allowed", `${capability.label} calls may use ${oauthReadMethods(capability).join(" or ")} with access: "read"; ${method} needs access: "write" in the app's capabilities entry, on a provider that offers it.`);
192
+ }
193
+ applyAttribution(headers, undefined);
194
+ headers.set("authorization", `Bearer ${token}`);
195
+ for (const name of ["access_token", "token"]) {
196
+ upstream.searchParams.delete(name);
197
+ }
198
+ headers.delete("x-http-method-override");
199
+ return fetch(new Request(upstream.toString(), {
200
+ method,
201
+ headers,
202
+ body: method === "GET" || method === "HEAD" ? null : outbound.body,
203
+ signal: outbound.signal,
204
+ // As the gateway: the token never follows a redirect.
205
+ redirect: "manual",
206
+ ...(method === "GET" || method === "HEAD" ? {} : { duplex: "half" }),
207
+ }));
208
+ }
209
+ /** The `access` the manifest entry for `provider` declares in dev (EVERYAPP_DEV_CAPABILITIES), or null. */
210
+ function devDeclaredAccess(provider, values) {
211
+ const raw = values["EVERYAPP_DEV_CAPABILITIES"];
212
+ try {
213
+ const capabilities = typeof raw === "string" ? jsonRecord(JSON.parse(raw)) : undefined;
214
+ const entry = jsonRecord(capabilities?.[provider]);
215
+ return typeof entry?.["access"] === "string" ? entry["access"] : null;
216
+ }
217
+ catch {
218
+ return null;
219
+ }
220
+ }
95
221
  /**
96
222
  * The developer's own key or client from `.dev.vars` (with EVERYAPP_DEV
97
223
  * set), under the gateway's names; null until every setting it needs is.
@@ -125,6 +251,9 @@ async function oauthClientCall({ provider, capability, outbound, upstream, heade
125
251
  return binding.fetch(streamingRequest(`${BINDING_ORIGIN}${integrationProxyPath(provider, upstream)}`, outbound, headers));
126
252
  }
127
253
  applyAttribution(headers, undefined);
254
+ if (!client) {
255
+ throw new ConfigurationError(`${capability.label}'s token endpoints need the client in .dev.vars for local development: set ${capabilitySettings(capability).join(", ")}.`);
256
+ }
128
257
  // The gateway's scope checks, in its order, answering its refusals: an
129
258
  // app sees in `everyapp dev` what the gateway will say.
130
259
  const policy = capability.scopePolicy;
@@ -173,13 +302,15 @@ async function oauthClientCall({ provider, capability, outbound, upstream, heade
173
302
  * `x-everyapp-error`) from the local-dev twin, so {@link gatewayRefusal}
174
303
  * reads it the same way.
175
304
  */
176
- function devRefusal(status, code, message) {
305
+ function devRefusal(status, code, message, extra = {}) {
177
306
  const labels = {
178
307
  400: "Bad Request",
179
308
  403: "Forbidden",
309
+ 405: "Method Not Allowed",
310
+ 409: "Conflict",
180
311
  502: "Bad Gateway",
181
312
  };
182
- return Response.json({ error: labels[status] ?? "Error", code, message }, { status, headers: { [GATEWAY_ERROR_HEADER]: code } });
313
+ return Response.json({ error: labels[status] ?? "Error", code, message, ...extra }, { status, headers: { [GATEWAY_ERROR_HEADER]: code } });
183
314
  }
184
315
  /**
185
316
  * The scope sets the manifest declares for `id`, read from
@@ -191,7 +322,9 @@ function devRefusal(status, code, message) {
191
322
  * doesn't offer). Fails closed like the gateway: a hand-rolled dev harness
192
323
  * that passes no capabilities gets nothing.
193
324
  */
194
- function devScopeSets(policy, id, label, values) {
325
+ function devScopeSets(policy, id, label, values,
326
+ /** The grant the entry needs: `write` for a token-endpoint call (the app-driven flow), `any` for a held-token call. */
327
+ grant = "write") {
195
328
  const offered = Object.keys(policy.sets).join(", ");
196
329
  const raw = values["EVERYAPP_DEV_CAPABILITIES"];
197
330
  let capabilities;
@@ -211,11 +344,16 @@ function devScopeSets(policy, id, label, values) {
211
344
  const entry = Object.hasOwn(capabilities, id)
212
345
  ? jsonRecord(capabilities[id])
213
346
  : undefined;
214
- // The gateway's check 2: listing the client for writing is the grant.
215
- if (entry?.["access"] !== "write") {
347
+ // The gateway's check 2: listing the client for writing is the grant
348
+ // of the app-driven flow; a held-token call takes any listing.
349
+ const access = entry?.["access"];
350
+ if (!entry ||
351
+ (grant === "write"
352
+ ? access !== "write"
353
+ : access !== "read" && access !== "write")) {
216
354
  return {
217
355
  code: "capability_not_requested",
218
- problem: `This app doesn't list ${label} in its capabilities. Add capabilities: { ${JSON.stringify(id)}: { access: "write" } } to everyapp.config.ts and start everyapp dev again.`,
356
+ problem: `This app doesn't list ${label} in its capabilities. Add capabilities: { ${JSON.stringify(id)}: { access: "${grant === "write" ? "write" : "read"}" } } to everyapp.config.ts and start everyapp dev again.`,
219
357
  };
220
358
  }
221
359
  const sets = declaredScopeSets(policy, entry);
@@ -315,6 +453,8 @@ async function checkTokenResponse(policy, capability, sets, response) {
315
453
  google: "Ask for only the scopes integrationStatus() returns, without include_granted_scopes, and connect again; if the app needs more, add the scope set to everyapp.config.ts and deploy.",
316
454
  microsoft: "On a shared Microsoft client a token carries everything the person consented to this client: have them remove this app's consent at https://myapplications.microsoft.com and connect again, or add the scope set to everyapp.config.ts and deploy.",
317
455
  slack: "Slack scopes add up per workspace installation: reinstall the app in the workspace with only the status's scopes, or add the scope set to everyapp.config.ts and deploy.",
456
+ space: "Ask for only the scopes integrationStatus() returns and connect again, or add the scope set to everyapp.config.ts and deploy.",
457
+ comma: "Ask for only the scopes integrationStatus() returns and connect again, or add the scope set to everyapp.config.ts and deploy.",
318
458
  }[policy.dialect];
319
459
  return devRefusal(403, "integration_scope_exceeded", `${capability.label} granted scopes this app doesn't declare: ${excess.join(", ")}. It declares ${sets.join(", ")} (${requestedScopes(policy, sets).scopes.join(", ")}). Nothing was passed on. ${remedy}`);
320
460
  }
@@ -431,9 +571,31 @@ function devOAuthClientStatus(id, capability, values) {
431
571
  : null;
432
572
  const origin = oauthClientOrigin(capability, values);
433
573
  const policy = capability.scopePolicy;
574
+ // An entry with a Gateway-owned flow is listed for reading or writing;
575
+ // the app-driven flow alone needs the write grant.
434
576
  const grant = policy
435
- ? devScopeSets(policy, id, capability.label, values)
577
+ ? devScopeSets(policy, id, capability.label, values, capability.flow ? "any" : "write")
436
578
  : null;
579
+ // The Gateway-owned flow's fields, in dev: a token in `.dev.vars`
580
+ // stands for a connection granted every declared set.
581
+ const flow = capability.flow ? "both" : "app";
582
+ const devToken = capability.flow
583
+ ? stringValue(values[devAccessTokenName(id)])
584
+ : undefined;
585
+ const gateway = (sets) => capability.flow
586
+ ? {
587
+ flow,
588
+ connectPath: `/connect/${id}`,
589
+ connection: devToken
590
+ ? {
591
+ account: "user",
592
+ label: devAccessTokenName(id),
593
+ scopeSets: sets,
594
+ ready: true,
595
+ }
596
+ : null,
597
+ }
598
+ : { flow };
437
599
  if (grant && "problem" in grant) {
438
600
  if (grant.code === "capability_not_requested") {
439
601
  return { requested: false, configured: false, ready: false };
@@ -443,16 +605,17 @@ function devOAuthClientStatus(id, capability, values) {
443
605
  configured: Boolean(client),
444
606
  ready: false,
445
607
  scopeSets: [],
608
+ ...gateway([]),
446
609
  };
447
610
  }
448
611
  const asked = policy && grant ? requestedScopes(policy, grant.sets) : null;
449
612
  return {
450
613
  requested: true,
451
614
  configured: Boolean(client),
452
- ready: Boolean(client),
615
+ ready: Boolean(client) || Boolean(devToken),
453
616
  ...(client ? { clientId: client.clientId } : {}),
454
617
  ...(client && origin ? { origin } : {}),
455
- ...(client && grant && asked
618
+ ...((client || devToken) && grant && asked
456
619
  ? {
457
620
  scopeSets: grant.sets,
458
621
  scopes: asked.scopes,
@@ -461,6 +624,7 @@ function devOAuthClientStatus(id, capability, values) {
461
624
  : {}),
462
625
  }
463
626
  : {}),
627
+ ...gateway(grant?.sets ?? []),
464
628
  };
465
629
  }
466
630
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@every-app/sdk",
3
- "version": "0.3.0-rc.1",
3
+ "version": "0.3.0-rc.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -30,16 +30,6 @@
30
30
  "dist",
31
31
  "README.md"
32
32
  ],
33
- "scripts": {
34
- "prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
35
- "build": "tsc -p tsconfig.build.json",
36
- "types:check": "tsc --noEmit",
37
- "format:check": "prettier --check .",
38
- "format:write": "prettier . --write",
39
- "test": "vitest run --silent",
40
- "test:watch": "vitest",
41
- "check:esm-relative-imports": "node ./scripts/check-esm-relative-imports.mjs"
42
- },
43
33
  "peerDependencies": {
44
34
  "jose": "^6.2.5"
45
35
  },
@@ -53,5 +43,15 @@
53
43
  "vite-tsconfig-paths": "^5.1.4",
54
44
  "vitest": "^3.2.6",
55
45
  "zod": "^4.2.0"
46
+ },
47
+ "scripts": {
48
+ "prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
49
+ "build": "tsc -p tsconfig.build.json",
50
+ "types:check": "tsc --noEmit",
51
+ "format:check": "prettier --check .",
52
+ "format:write": "prettier . --write",
53
+ "test": "vitest run --silent",
54
+ "test:watch": "vitest",
55
+ "check:esm-relative-imports": "node ./scripts/check-esm-relative-imports.mjs"
56
56
  }
57
- }
57
+ }