@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
@@ -16,18 +16,14 @@
16
16
  * proxy's rules: inference routes only, the model allowlist, the usage
17
17
  * tap. Never a capability: its key would reach the account's credits
18
18
  * and keys on the same host.
19
- * - `oauth_client`: a provider's OAuth client (Google's, Microsoft's,
20
- * Slack's), held the same way. The gateway completes the token endpoint
21
- * calls only, putting the client on them; the app builds the authorize
22
- * URL from the public client id (`integrationStatus()`), sends people
23
- * back to a callback its manifest entry lists (`redirectPaths`), keeps
24
- * the user tokens and calls the provider's API with them itself. Other
25
- * providers' clients (GitHub, the social networks) are presets
26
- * (./oauthPresets.ts): rows only once an operator adds one. A client shared by apps
27
- * with different needs carries a `scopePolicy`: named scope sets an app
28
- * lists in its manifest entry's `scopes`, which ./oauthScopes.ts turns
29
- * into what the app may ask the provider for and what a token it is
30
- * handed may carry.
19
+ * - `oauth_client`: an OAuth provider (Google, Microsoft, Slack built
20
+ * in; GitHub and others as presets, ./oauthPresets.ts) whose client the
21
+ * gateway holds and whose flow it runs. A person connects in an app,
22
+ * for exactly the provider scopes the app's manifest entry lists
23
+ * (`scopes`); the gateway keeps the tokens and puts the person's token
24
+ * on the app's calls to the provider's `api` hosts. Each provider is a
25
+ * spec (./oauthSpec.ts), its differences data; ./oauthScopes.ts holds
26
+ * the one set of scope rules.
31
27
  * - `service`: something the gateway does itself, with no upstream host
32
28
  * an app names (sending email, listing the organization's members). An
33
29
  * app lists it the same way and calls its routes,
@@ -47,6 +43,22 @@ export const DEFAULT_WRITE_METHODS = [
47
43
  "DELETE",
48
44
  ];
49
45
  import { CATALOG_ENTRIES } from "./catalog/index.js";
46
+ import { BUILT_IN_OAUTH_SPECS } from "./oauthBuiltIns.js";
47
+ import { oauthCapabilityFromSpec } from "./oauthSpec.js";
48
+ /**
49
+ * A built-in OAuth provider's catalog entry from its spec, its setting
50
+ * names kept literal so `CapabilitySecret` names them.
51
+ */
52
+ function builtInOAuth(spec, setup) {
53
+ return {
54
+ ...oauthCapabilityFromSpec(spec),
55
+ secrets: {
56
+ clientId: spec.auth.clientId,
57
+ clientSecret: spec.auth.clientSecret,
58
+ },
59
+ setup,
60
+ };
61
+ }
50
62
  export const CAPABILITIES = {
51
63
  // The AI providers, in the order the AI proxy's table had them.
52
64
  openai: {
@@ -70,8 +82,8 @@ export const CAPABILITIES = {
70
82
  "/v1/images/variations",
71
83
  ],
72
84
  unstoredRoutes: ["/v1/responses", "/v1/chat/completions"],
73
- // More than inference needs both the operator (AI_PROXY_EXTRA_ROUTES)
74
- // and the app's manifest (`ai.routes`): OpenAI paths only.
85
+ // More than inference needs the app's manifest (`ai.routes`): OpenAI
86
+ // paths only.
75
87
  extraRoutes: true,
76
88
  // Tool calling on chat completions needs `reasoning_effort: "none"`
77
89
  // on the GPT-6 family (the responses route takes tools as it is).
@@ -296,518 +308,11 @@ export const CAPABILITIES = {
296
308
  settings: [],
297
309
  optionalSettings: [],
298
310
  },
299
- slack: {
300
- mode: "oauth_client",
301
- label: "Slack",
302
- secrets: {
303
- clientId: "SLACK_CLIENT_ID",
304
- clientSecret: "SLACK_CLIENT_SECRET",
305
- },
306
- token: [
307
- {
308
- host: "slack.com",
309
- path: "/api/oauth.v2.access",
310
- methods: ["POST"],
311
- inject: "form",
312
- },
313
- ],
314
- access: ["write"],
315
- console: "https://api.slack.com/apps",
316
- setup: "setup/slack.md",
317
- authorize: {
318
- url: "https://slack.com/oauth/v2/authorize",
319
- // The sets' union: the bot scopes in set order, then the one user
320
- // scope no bot set carries. Postiz lists `channels.read`,
321
- // `users.read`, `messages.write`, `channels.join` and `files.write`
322
- // (it posts to channels it joins and uploads images); Open CRM adds
323
- // `users.email`, `channels.manage` and `user-channels.manage` (its
324
- // team agents match members to Slack users by email, create and
325
- // manage the channels they post to, open DMs, and the installing
326
- // person invites the bot to private channels).
327
- scopes: [
328
- "channels:read",
329
- "groups:read",
330
- "users:read",
331
- "users:read.email",
332
- "chat:write",
333
- "chat:write.public",
334
- "chat:write.customize",
335
- "channels:join",
336
- "channels:manage",
337
- "groups:write",
338
- "channels:write.invites",
339
- "groups:write.invites",
340
- "im:write",
341
- "files:write",
342
- "channels:write",
343
- ],
344
- },
345
- scopePolicy: {
346
- dialect: "slack",
347
- // `oauth.v2.access` takes `code` with no `grant_type`; an app that
348
- // opts into token rotation refreshes with `refresh_token`.
349
- grantTypes: [null, "refresh_token"],
350
- // Slack reads no `scope` on the token leg (the installation's scopes
351
- // were fixed on the authorize URL), so the request goes as sent.
352
- requestScope: "keep",
353
- alwaysRequested: [],
354
- tolerated: [],
355
- deniedSegments: [],
356
- // Slack authorizes by method name, not verb, and every method is one
357
- // exact `/api/<method>` path on the host that also serves the token
358
- // endpoint (which no set lists). A read set names its methods in
359
- // `paths` (GET) and `posts` (POST) alike; a write set in `posts`
360
- // only, with the one listing among them in `paths` too.
361
- sets: {
362
- "channels.read": {
363
- label: "Channels (read-only)",
364
- access: "read",
365
- scopes: ["channels:read", "groups:read"],
366
- routes: [
367
- {
368
- host: "slack.com",
369
- paths: [
370
- "/api/conversations.list",
371
- "/api/conversations.info",
372
- "/api/conversations.members",
373
- "/api/users.conversations",
374
- ],
375
- posts: [
376
- /^\/api\/conversations\.(list|info|members)$/,
377
- /^\/api\/users\.conversations$/,
378
- ],
379
- },
380
- ],
381
- },
382
- "users.read": {
383
- label: "Members (read-only)",
384
- access: "read",
385
- scopes: ["users:read"],
386
- routes: [
387
- {
388
- host: "slack.com",
389
- paths: [
390
- "/api/users.list",
391
- "/api/users.info",
392
- "/api/users.getPresence",
393
- "/api/bots.info",
394
- ],
395
- posts: [
396
- /^\/api\/users\.(list|info|getPresence)$/,
397
- /^\/api\/bots\.info$/,
398
- ],
399
- },
400
- ],
401
- },
402
- "users.email": {
403
- // Slack grants it only beside `users:read`: declare both sets.
404
- label: "Members' email addresses (read-only)",
405
- access: "read",
406
- scopes: ["users:read.email"],
407
- routes: [
408
- {
409
- host: "slack.com",
410
- paths: ["/api/users.lookupByEmail"],
411
- posts: [/^\/api\/users\.lookupByEmail$/],
412
- },
413
- ],
414
- },
415
- "messages.write": {
416
- label: "Messages (post as the app)",
417
- access: "write",
418
- scopes: ["chat:write", "chat:write.public", "chat:write.customize"],
419
- routes: [
420
- {
421
- host: "slack.com",
422
- // The scheduled-messages listing is the one read among them.
423
- paths: ["/api/chat.scheduledMessages.list"],
424
- posts: [
425
- /^\/api\/chat\.(postMessage|postEphemeral|update|delete|scheduleMessage|deleteScheduledMessage|scheduledMessages\.list)$/,
426
- ],
427
- },
428
- ],
429
- },
430
- "channels.join": {
431
- label: "Channels (join public ones)",
432
- access: "write",
433
- scopes: ["channels:join"],
434
- routes: [
435
- {
436
- host: "slack.com",
437
- paths: [],
438
- posts: [/^\/api\/conversations\.join$/],
439
- },
440
- ],
441
- },
442
- "channels.manage": {
443
- label: "Channels (create, manage and invite to; open DMs)",
444
- access: "write",
445
- scopes: [
446
- "channels:manage",
447
- "groups:write",
448
- "channels:write.invites",
449
- "groups:write.invites",
450
- "im:write",
451
- ],
452
- routes: [
453
- {
454
- host: "slack.com",
455
- paths: [],
456
- posts: [
457
- /^\/api\/conversations\.(create|rename|archive|unarchive|invite|open|kick|leave|setTopic|setPurpose|mark)$/,
458
- ],
459
- },
460
- ],
461
- },
462
- "files.write": {
463
- label: "Files (upload)",
464
- access: "write",
465
- scopes: ["files:write"],
466
- routes: [
467
- {
468
- host: "slack.com",
469
- paths: [],
470
- // The external upload flow; the retired `files.upload` stays off.
471
- posts: [
472
- /^\/api\/files\.(getUploadURLExternal|completeUploadExternal)$/,
473
- ],
474
- },
475
- ],
476
- },
477
- "user-channels.manage": {
478
- label: "Channels (as the installing person: read, manage, invite to)",
479
- access: "write",
480
- scopes: [],
481
- // Slack's `user_scope`: issued to the person's own token, which
482
- // Open CRM uses to invite the bot to private channels.
483
- userScopes: [
484
- "channels:read",
485
- "channels:write",
486
- "groups:read",
487
- "groups:write",
488
- ],
489
- routes: [
490
- {
491
- host: "slack.com",
492
- paths: [
493
- "/api/conversations.list",
494
- "/api/conversations.info",
495
- "/api/conversations.members",
496
- "/api/users.conversations",
497
- ],
498
- posts: [
499
- /^\/api\/conversations\.(list|info|members|create|rename|archive|unarchive|invite|kick|leave|setTopic|setPurpose|mark)$/,
500
- /^\/api\/users\.conversations$/,
501
- ],
502
- },
503
- ],
504
- },
505
- },
506
- },
507
- api: ["slack.com"],
508
- // Slack refuses `code_challenge`; `auth.test` names the workspace and
509
- // the installing person.
510
- flow: {
511
- tokenUrl: "https://slack.com/api/oauth.v2.access",
512
- revokeUrl: "https://slack.com/api/auth.revoke",
513
- pkce: false,
514
- scopeDialect: "slack",
515
- identity: {
516
- url: "https://slack.com/api/auth.test",
517
- label: "team",
518
- id: "user_id",
519
- },
520
- },
521
- },
522
- // Clients several apps share with different needs, so each app lists the
523
- // scope sets it uses: Open CRM reads a member's own mailbox and calendar
524
- // (`gmail.read`, `calendar.read`; `identity`, `mail.read`), OpenSEO reads
525
- // a site's Search Console and Analytics (`identity`, `search-console.read`,
526
- // `analytics.read`). Every set is read-only. Google's is one Cloud project
527
- // apart from the YouTube preset's; Gmail's read scope is restricted and needs
528
- // Google's verification, and whether Search Console and Analytics share
529
- // that consent screen with it is the operator's choice when verifying.
530
- "google-workspace": {
531
- mode: "oauth_client",
532
- label: "Google (Gmail, Calendar, Search Console and Analytics)",
533
- secrets: {
534
- clientId: "GOOGLE_WORKSPACE_CLIENT_ID",
535
- clientSecret: "GOOGLE_WORKSPACE_CLIENT_SECRET",
536
- },
537
- token: [
538
- {
539
- host: "oauth2.googleapis.com",
540
- path: "/token",
541
- methods: ["POST"],
542
- inject: "form",
543
- },
544
- ],
545
- access: ["write"],
546
- console: "https://console.cloud.google.com/apis/credentials",
547
- setup: "setup/google.md",
548
- authorize: {
549
- url: "https://accounts.google.com/o/oauth2/v2/auth",
550
- scopes: [
551
- "openid",
552
- "email",
553
- "profile",
554
- "https://www.googleapis.com/auth/gmail.readonly",
555
- "https://www.googleapis.com/auth/calendar.readonly",
556
- "https://www.googleapis.com/auth/webmasters.readonly",
557
- "https://www.googleapis.com/auth/analytics.readonly",
558
- ],
559
- },
560
- scopePolicy: {
561
- dialect: "google",
562
- // Google documents no scope on either token leg: the response's
563
- // `scope` is where the gateway learns what a token holds.
564
- grantTypes: ["authorization_code", "refresh_token"],
565
- requestScope: "drop",
566
- alwaysRequested: [],
567
- // Both spellings: Google documents `email`/`profile` and the userinfo
568
- // URLs as the same grants, and echoes the URLs.
569
- tolerated: [
570
- "openid",
571
- "email",
572
- "profile",
573
- "https://www.googleapis.com/auth/userinfo.email",
574
- "https://www.googleapis.com/auth/userinfo.profile",
575
- ],
576
- // Gmail's and Calendar's `/batch/<api>/<version>` carry whole requests
577
- // inside; Analytics' measurement protocol secrets are readable under
578
- // the read-only scope.
579
- deniedSegments: ["batch", "measurementProtocolSecrets"],
580
- sets: {
581
- identity: {
582
- label: "Google account (who you are)",
583
- access: "read",
584
- scopes: ["openid", "email", "profile"],
585
- routes: [
586
- { host: "openidconnect.googleapis.com", paths: ["/v1/userinfo"] },
587
- {
588
- host: "www.googleapis.com",
589
- paths: [
590
- "/oauth2/v2/userinfo",
591
- "/oauth2/v3/userinfo",
592
- "/userinfo/v2/me",
593
- ],
594
- },
595
- ],
596
- },
597
- "gmail.read": {
598
- label: "Gmail (read-only)",
599
- access: "read",
600
- scopes: ["https://www.googleapis.com/auth/gmail.readonly"],
601
- routes: [
602
- {
603
- host: "gmail.googleapis.com",
604
- paths: ["/gmail/v1/users/"],
605
- // Push notifications start and stop; `messages/send`, drafts
606
- // and `modify` are the other POSTs under the same prefix.
607
- posts: [/^\/gmail\/v1\/users\/[^/]+\/(watch|stop)$/],
608
- },
609
- ],
610
- },
611
- "calendar.read": {
612
- label: "Google Calendar (read-only)",
613
- access: "read",
614
- scopes: ["https://www.googleapis.com/auth/calendar.readonly"],
615
- routes: [
616
- {
617
- host: "www.googleapis.com",
618
- paths: ["/calendar/v3/"],
619
- posts: [
620
- /^\/calendar\/v3\/freeBusy$/,
621
- /^\/calendar\/v3\/calendars\/[^/]+\/events\/watch$/,
622
- /^\/calendar\/v3\/users\/me\/calendarList\/watch$/,
623
- /^\/calendar\/v3\/channels\/stop$/,
624
- ],
625
- },
626
- ],
627
- },
628
- "search-console.read": {
629
- label: "Search Console (read-only)",
630
- access: "read",
631
- scopes: ["https://www.googleapis.com/auth/webmasters.readonly"],
632
- // The v3 paths answer on both hosts; URL inspection is v1 only.
633
- // A site is one segment, `https%3A%2F%2Fexample.com%2F` or
634
- // `sc-domain%3Aexample.com`, which is why routes match the raw path.
635
- routes: [
636
- {
637
- host: "www.googleapis.com",
638
- paths: ["/webmasters/v3/"],
639
- posts: [
640
- /^\/webmasters\/v3\/sites\/[^/]+\/searchAnalytics\/query$/,
641
- ],
642
- },
643
- {
644
- host: "searchconsole.googleapis.com",
645
- paths: ["/webmasters/v3/"],
646
- posts: [
647
- /^\/webmasters\/v3\/sites\/[^/]+\/searchAnalytics\/query$/,
648
- /^\/v1\/urlInspection\/index:inspect$/,
649
- ],
650
- },
651
- ],
652
- },
653
- "analytics.read": {
654
- label: "Google Analytics (read-only)",
655
- access: "read",
656
- scopes: ["https://www.googleapis.com/auth/analytics.readonly"],
657
- routes: [
658
- {
659
- // Reports are POSTed queries.
660
- host: "analyticsdata.googleapis.com",
661
- paths: ["/v1beta/properties/"],
662
- posts: [
663
- /^\/v1beta\/properties\/[^/]+:(runReport|batchRunReports|runPivotReport|batchRunPivotReports|runRealtimeReport|checkCompatibility)$/,
664
- ],
665
- },
666
- {
667
- // Reads only: accounts, properties and what hangs off them
668
- // (data streams, key events, enhanced measurement settings).
669
- host: "analyticsadmin.googleapis.com",
670
- paths: [
671
- "/v1beta/accountSummaries",
672
- "/v1beta/accounts",
673
- "/v1beta/accounts/",
674
- "/v1beta/properties",
675
- "/v1beta/properties/",
676
- "/v1alpha/properties/",
677
- ],
678
- },
679
- ],
680
- },
681
- },
682
- },
683
- api: [
684
- "gmail.googleapis.com",
685
- "www.googleapis.com",
686
- "searchconsole.googleapis.com",
687
- "analyticsdata.googleapis.com",
688
- "analyticsadmin.googleapis.com",
689
- "openidconnect.googleapis.com",
690
- ],
691
- // The Gateway-owned flow: a refresh token needs `access_type=offline`,
692
- // and `prompt=consent` makes Google issue one on every connect. No
693
- // revoke: Google's `/revoke` is per person per client and would also
694
- // kill the tokens an app-driven OpenSEO holds under the same client.
695
- flow: {
696
- tokenUrl: "https://oauth2.googleapis.com/token",
697
- pkce: true,
698
- scopeDialect: "google",
699
- authorizeParams: { access_type: "offline", prompt: "consent" },
700
- identity: {
701
- url: "https://openidconnect.googleapis.com/v1/userinfo",
702
- label: "email",
703
- id: "sub",
704
- },
705
- },
706
- },
707
- "microsoft-365": {
708
- mode: "oauth_client",
709
- label: "Microsoft 365 (Outlook mail and calendar)",
710
- secrets: {
711
- clientId: "MICROSOFT_365_CLIENT_ID",
712
- clientSecret: "MICROSOFT_365_CLIENT_SECRET",
713
- },
714
- token: [
715
- {
716
- // The multi-tenant endpoint: register the client for accounts in
717
- // any organization and personal accounts. A single-tenant client
718
- // answers only on its tenant's path, which this does not complete.
719
- host: "login.microsoftonline.com",
720
- path: "/common/oauth2/v2.0/token",
721
- methods: ["POST"],
722
- inject: "form",
723
- },
724
- ],
725
- access: ["write"],
726
- console: "https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade",
727
- setup: "setup/microsoft-365.md",
728
- authorize: {
729
- url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
730
- scopes: ["offline_access", "User.Read", "Mail.Read", "Calendars.Read"],
731
- },
732
- scopePolicy: {
733
- dialect: "microsoft",
734
- grantTypes: ["authorization_code", "refresh_token"],
735
- // The token leg's `scope` is Microsoft's extension for naming the
736
- // resource; the gateway sets it to the app's list so a token covers
737
- // Graph alone.
738
- requestScope: "replace",
739
- // Refresh tokens; Entra never echoes it in a response's `scope`.
740
- alwaysRequested: ["offline_access"],
741
- // Compared case-insensitively, as Microsoft spells them both ways.
742
- tolerated: ["openid", "profile", "email", "offline_access", "User.Read"],
743
- // `$batch` carries whole requests inside; `beta` is another API.
744
- deniedSegments: ["$batch", "beta"],
745
- // `/v1.0/me/…` only: `/v1.0/users/<id>/…` reaches shared mailboxes
746
- // and calendars under the same permissions.
747
- sets: {
748
- identity: {
749
- label: "Microsoft account (who you are)",
750
- access: "read",
751
- scopes: ["User.Read"],
752
- routes: [
753
- {
754
- host: "graph.microsoft.com",
755
- paths: ["/v1.0/me", "/oidc/userinfo"],
756
- },
757
- ],
758
- },
759
- "mail.read": {
760
- label: "Outlook mail (read-only)",
761
- access: "read",
762
- scopes: ["Mail.Read"],
763
- routes: [
764
- {
765
- host: "graph.microsoft.com",
766
- paths: [
767
- "/v1.0/me/messages",
768
- "/v1.0/me/messages/",
769
- "/v1.0/me/mailFolders",
770
- "/v1.0/me/mailFolders/",
771
- ],
772
- },
773
- ],
774
- },
775
- "calendar.read": {
776
- label: "Outlook calendar (read-only)",
777
- access: "read",
778
- scopes: ["Calendars.Read"],
779
- routes: [
780
- {
781
- host: "graph.microsoft.com",
782
- paths: [
783
- "/v1.0/me/calendar",
784
- "/v1.0/me/calendar/",
785
- "/v1.0/me/calendars",
786
- "/v1.0/me/calendars/",
787
- "/v1.0/me/events",
788
- "/v1.0/me/events/",
789
- "/v1.0/me/calendarView",
790
- "/v1.0/me/calendarView/",
791
- ],
792
- // Free/busy is a POSTed query.
793
- posts: [/^\/v1\.0\/me\/calendar\/getSchedule$/],
794
- },
795
- ],
796
- },
797
- },
798
- },
799
- api: ["graph.microsoft.com"],
800
- flow: {
801
- tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
802
- pkce: true,
803
- scopeDialect: "microsoft",
804
- identity: {
805
- url: "https://graph.microsoft.com/v1.0/me",
806
- label: "userPrincipalName",
807
- id: "id",
808
- },
809
- },
810
- },
311
+ // The OAuth providers every Gateway has built in, from their JSON specs
312
+ // (./oauthBuiltIns.ts): what differs between them is data there.
313
+ slack: builtInOAuth(BUILT_IN_OAUTH_SPECS.slack, "setup/slack.md"),
314
+ "google-workspace": builtInOAuth(BUILT_IN_OAUTH_SPECS["google-workspace"], "setup/google.md"),
315
+ "microsoft-365": builtInOAuth(BUILT_IN_OAUTH_SPECS["microsoft-365"], "setup/microsoft-365.md"),
811
316
  // The first fifty beyond the entries above (catalog/*.ts, PR 3 of the
812
317
  // provider registry), after them so the derived lists keep their order.
813
318
  ...CATALOG_ENTRIES,
@@ -831,7 +336,12 @@ export function capabilitySettings(capability) {
831
336
  ...(capability.hostSetting ? [capability.hostSetting] : []),
832
337
  ];
833
338
  case "ai":
834
- return [capability.auth.secret, ...(capability.alsoNeeds ?? [])];
339
+ // Every setting `auth` names, as for an API-key provider: a Basic
340
+ // username held as a setting is checked, bound and listed like the key.
341
+ return [
342
+ ...authSettingNames(capability.auth),
343
+ ...(capability.alsoNeeds ?? []),
344
+ ];
835
345
  case "service":
836
346
  return [...capability.settings, ...capability.optionalSettings];
837
347
  case "oauth_client":
@@ -864,7 +374,13 @@ export function capabilityIdsOf(lookup) {
864
374
  * to encode, or taken as already encoded when it holds no colon.
865
375
  */
866
376
  export function authorizationFor(capability, settings) {
867
- return authorizationOf(capability.auth, settings);
377
+ // Only the settings the entry declares are visible: whatever `auth` reads
378
+ // beyond them (a setting no row check saw) is unset, and the call fails closed.
379
+ const declared = {};
380
+ for (const name of capabilitySettings(capability)) {
381
+ declared[name] = settings[name];
382
+ }
383
+ return authorizationOf(capability.auth, declared);
868
384
  }
869
385
  /** {@link authorizationFor} for an `auth` block alone (the SDK's AI fallback holds the block, not the entry). */
870
386
  export function authorizationOf(auth, settings) {
@@ -959,9 +475,8 @@ export function serviceRoutePath(id, route) {
959
475
  /**
960
476
  * The capability call an AppGateway path names, or null when the path names
961
477
  * an unknown capability (or an AI provider, which has its own route), a
962
- * host outside the provider's, a path that could leave that host, a path
963
- * that isn't one of an OAuth client's token endpoints, or a route a service
964
- * capability doesn't declare. `settings` resolves an API-key provider's
478
+ * host outside the provider's, a path that could leave that host, or a
479
+ * route a service capability doesn't declare. `settings` resolves an API-key provider's
965
480
  * `hostSetting` (a Shopify store's host); `lookup` is where the id is found,
966
481
  * the catalog alone by default.
967
482
  */
@@ -986,13 +501,12 @@ export function parseIntegrationProxyPath(pathname, search, settings, lookup = C
986
501
  };
987
502
  }
988
503
  if (capability.mode === "oauth_client") {
989
- const upstream = capability.token.some((rule) => rule.host === host)
504
+ const upstream = capability.api.includes(host)
990
505
  ? httpsUpstream(host, path ?? "/", search)
991
506
  : null;
992
- const endpoint = upstream && tokenEndpointFor(capability, upstream);
993
- if (!upstream || !endpoint)
507
+ if (!upstream)
994
508
  return null;
995
- return { kind: "oauth_client", id, capability, upstream, endpoint };
509
+ return { kind: "oauth_client", id, capability, upstream };
996
510
  }
997
511
  if (capability.mode !== "api_key")
998
512
  return null;
@@ -1013,21 +527,6 @@ function httpsUpstream(host, path, search) {
1013
527
  return null;
1014
528
  return upstream;
1015
529
  }
1016
- /**
1017
- * The token endpoint `upstream` is, by its exact host and path, or null.
1018
- * Its method is the caller's to check.
1019
- */
1020
- export function tokenEndpointFor(capability, upstream) {
1021
- if (upstream.protocol !== "https:")
1022
- return null;
1023
- return (capability.token.find(({ host, path }) => host === upstream.host && path === upstream.pathname) ?? null);
1024
- }
1025
- /** An OAuth client's token endpoints, as `POST https://host/path` lines for messages. */
1026
- export function describeTokenEndpoints(capability) {
1027
- return capability.token
1028
- .map(({ host, path, methods }) => `${methods.join("/")} https://${host}${path}`)
1029
- .join(", ");
1030
- }
1031
530
  /**
1032
531
  * The command that gives the gateway `capability`'s client:
1033
532
  * `pnpm everyapp secret set <ID> <SECRET>`.
@@ -146,7 +146,7 @@ export const COMMERCE = {
146
146
  },
147
147
  tags: ["payments", "billing"],
148
148
  },
149
- // Legacy admin custom apps only: Shopify no longer lets a store create one, and Dev Dashboard tokens expire daily (the hint says so; shipping it is Ben's call, TODO.md).
149
+ // Legacy admin custom apps only: Shopify no longer lets a store create one, and Dev Dashboard tokens expire daily (the hint says so; shipping it is Ben's call).
150
150
  shopify: {
151
151
  mode: "api_key",
152
152
  label: "Shopify",