@opengeni/api-router 3.0.1 → 3.0.2-canary.38894290271001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-grant-rls.d.ts +8 -1
- package/dist/admission-trace.d.ts +8 -0
- package/dist/app.d.ts +50 -3
- package/dist/app.js +10 -1
- package/dist/artifact-catalog-cursor.d.ts +1 -0
- package/dist/attached-browser-viewer.d.ts +11 -0
- package/dist/auth/managed-auth-attempt-context.d.ts +2 -0
- package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +9 -1
- package/dist/auth/managed-auth.d.ts +72 -5
- package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
- package/dist/auth/new-signups-gate.d.ts +23 -0
- package/dist/auth/signup-funnel-metrics.d.ts +44 -0
- package/dist/azure-live.d.ts +28 -0
- package/dist/browser-action-fences.d.ts +13 -0
- package/dist/browser-controller-authority.d.ts +1 -61
- package/dist/browser-ephemeral.d.ts +5 -0
- package/dist/browser-lost-runtime-end.d.ts +24 -0
- package/dist/browser-state-authority.d.ts +1 -27
- package/dist/{chunk-AJ62T5QN.js → chunk-GBUJ2DYZ.js} +80507 -39878
- package/dist/chunk-GBUJ2DYZ.js.map +1 -0
- package/dist/chunk-OGQDXVXY.js +18 -0
- package/dist/chunk-OGQDXVXY.js.map +1 -0
- package/dist/claude-default-models.d.ts +19 -0
- package/dist/claude-subscription-core.d.ts +50 -0
- package/dist/claude-subscription-oauth.d.ts +36 -0
- package/dist/claude-subscription-profile.d.ts +9 -0
- package/dist/claude-subscription-usage.d.ts +6 -0
- package/dist/claude-workspace-connection.d.ts +15 -0
- package/dist/codemode.d.ts +40 -5
- package/dist/codex-realtime.d.ts +29 -3
- package/dist/codex-session-pointers.d.ts +19 -0
- package/dist/connection-ownership.d.ts +6 -3
- package/dist/controller-data-plane.d.ts +16 -0
- package/dist/credit-promotion-snapshot.d.ts +6 -0
- package/dist/editable-artifact-source-session.d.ts +3 -0
- package/dist/editable-artifact-websocket.d.ts +5 -3
- package/dist/fatal-process-boundary.d.ts +9 -1
- package/dist/github-access.d.ts +7 -1
- package/dist/github-browser-flow.d.ts +1 -1
- package/dist/http/acting-person.d.ts +36 -0
- package/dist/http/api-contract-compat.d.ts +19 -0
- package/dist/http/api-error.d.ts +50 -1
- package/dist/http/auth.d.ts +8 -1
- package/dist/http/cors.d.ts +12 -0
- package/dist/http/deprecation.d.ts +42 -0
- package/dist/http/keyed-admission.d.ts +12 -0
- package/dist/http/local-browser-boundary.d.ts +72 -0
- package/dist/http/metrics-listener.d.ts +22 -0
- package/dist/http/path-identifier.d.ts +17 -0
- package/dist/http/registered-route-label.d.ts +16 -0
- package/dist/http/rejection-telemetry.d.ts +34 -0
- package/dist/http/request-body.d.ts +27 -0
- package/dist/http/request-source.d.ts +41 -3
- package/dist/http/sse.d.ts +12 -1
- package/dist/http/unmatched-route.d.ts +18 -0
- package/dist/http/user-content.d.ts +36 -0
- package/dist/http/variable-sets-removed.d.ts +41 -0
- package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
- package/dist/index.js +1279 -108
- package/dist/index.js.map +1 -1
- package/dist/integration-connect-metrics.d.ts +35 -0
- package/dist/integration-endpoint-test.d.ts +20 -0
- package/dist/integrations/api-integrations.d.ts +2 -2
- package/dist/integrations/atlassian.d.ts +8 -3
- package/dist/integrations/fiken.d.ts +3 -1
- package/dist/integrations/github-lens-connect.d.ts +37 -66
- package/dist/integrations/google-drive.d.ts +2 -0
- package/dist/integrations/integration-commit-authority.d.ts +13 -0
- package/dist/integrations/marketplace-plugin.d.ts +43 -43
- package/dist/integrations/oauth-client.d.ts +171 -12
- package/dist/integrations/oauth-profiles.d.ts +42 -13
- package/dist/integrations/onepassword-vault-provider.d.ts +9 -0
- package/dist/integrations/slack-bot.d.ts +285 -7
- package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
- package/dist/integrations/slack-interactions.d.ts +89 -10
- package/dist/integrations/slack-mrkdwn.d.ts +41 -0
- package/dist/integrations/slack-routing.d.ts +1 -1
- package/dist/integrations/slack-session-defaults.d.ts +58 -0
- package/dist/integrations/slack-task-file-upload.d.ts +36 -0
- package/dist/integrations/slack-tool-review.d.ts +5 -0
- package/dist/interaction-agent-access.d.ts +13 -8
- package/dist/interaction-agent-version.d.ts +21 -0
- package/dist/interaction-holder-heartbeat.d.ts +5 -0
- package/dist/interaction-metrics.d.ts +27 -2
- package/dist/interaction-route-stages.d.ts +25 -0
- package/dist/interaction-secrets-vault.d.ts +20 -0
- package/dist/interaction-secrets.d.ts +103 -0
- package/dist/mcp/action-catalog-types.d.ts +14 -0
- package/dist/mcp/action-catalog.gen.d.ts +2 -0
- package/dist/mcp/action-descriptions.d.ts +4 -0
- package/dist/mcp/action-schema-imports.gen.d.ts +1 -0
- package/dist/mcp/capability-account-readiness.d.ts +7 -0
- package/dist/mcp/child-read-evidence.d.ts +9 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +12 -0
- package/dist/mcp/contract-input.d.ts +13 -0
- package/dist/mcp/documents.d.ts +2 -0
- package/dist/mcp/first-party-tool-permissions.d.ts +3 -606
- package/dist/mcp/notification-tools.d.ts +26 -0
- package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
- package/dist/mcp/scheduled-task-input.d.ts +1309 -0
- package/dist/mcp/scheduled-task-view.d.ts +217 -3
- package/dist/mcp/secret-tools.d.ts +14 -0
- package/dist/mcp/server.d.ts +108 -5
- package/dist/mcp/session-admin-access-tools.d.ts +33 -0
- package/dist/mcp/session-event-errors.d.ts +28 -0
- package/dist/mcp/session-event-view.d.ts +62 -0
- package/dist/mcp/session-view.d.ts +15 -0
- package/dist/mcp/session-wait.d.ts +18 -0
- package/dist/mcp/site-agent-clamp-tools.d.ts +38 -0
- package/dist/mcp/site-data-tools.d.ts +22 -0
- package/dist/mcp/site-publication-tools.d.ts +19 -0
- package/dist/mcp/site-sql-tools.d.ts +27 -0
- package/dist/mcp-oauth.d.ts +37 -4
- package/dist/model-calls/chat-completions.d.ts +87 -0
- package/dist/model-calls/pending-usage-settlements.d.ts +57 -0
- package/dist/model-calls/service.d.ts +36 -0
- package/dist/model-catalog.d.ts +9 -3
- package/dist/native-push-dispatch.d.ts +69 -0
- package/dist/observability.d.ts +3 -1
- package/dist/organization-mcp.d.ts +82 -0
- package/dist/prepared-mcp-actor.d.ts +77 -0
- package/dist/prepared-mcp-permissions.d.ts +8 -0
- package/dist/routes/analytics-consent.d.ts +26 -0
- package/dist/routes/api-integrations.d.ts +11 -0
- package/dist/routes/api-keys.d.ts +7 -7
- package/dist/routes/automations.d.ts +2 -0
- package/dist/routes/billing.d.ts +59 -0
- package/dist/routes/browser-sessions.d.ts +65 -2
- package/dist/routes/chat-completions.d.ts +11 -0
- package/dist/routes/claude-core-usage.d.ts +20 -0
- package/dist/routes/claude-subscription-accounts.d.ts +3 -0
- package/dist/routes/claude-subscription-oauth.d.ts +3 -0
- package/dist/routes/client-errors.d.ts +182 -0
- package/dist/routes/codex-core.d.ts +401 -0
- package/dist/routes/codex.d.ts +64 -2
- package/dist/routes/computer-sessions.d.ts +6 -1
- package/dist/routes/desktop-distribution.d.ts +32 -0
- package/dist/routes/github-browser-fonts.gen.d.ts +2 -0
- package/dist/routes/github-browser-pages.d.ts +27 -0
- package/dist/routes/inbox.d.ts +13 -0
- package/dist/routes/insights-response-cache.d.ts +10 -0
- package/dist/routes/insights-usage.d.ts +12 -0
- package/dist/routes/insights.d.ts +28 -0
- package/dist/routes/interaction-placement-message.d.ts +7 -0
- package/dist/routes/managed-sign-in-methods.d.ts +6 -0
- package/dist/routes/model-account-choice.d.ts +13 -0
- package/dist/routes/model-account-settings.d.ts +19 -0
- package/dist/routes/native-app-auth.d.ts +12 -0
- package/dist/routes/organization-integration-policy.d.ts +8 -0
- package/dist/routes/organization-integrations.d.ts +6 -0
- package/dist/routes/organization-mcp-connections.d.ts +3 -0
- package/dist/routes/organization-model-defaults.d.ts +9 -0
- package/dist/routes/organization-service-accounts.d.ts +3 -0
- package/dist/routes/personal-api-keys.d.ts +3 -0
- package/dist/routes/session-admin-access.d.ts +3 -0
- package/dist/routes/session-artifact-associations.d.ts +4 -0
- package/dist/routes/session-history-imports.d.ts +20 -0
- package/dist/routes/sessions.d.ts +38 -2
- package/dist/routes/site-data.d.ts +18 -0
- package/dist/routes/site-publishing.d.ts +5 -0
- package/dist/routes/site-runtime.d.ts +31 -0
- package/dist/routes/site-sql.d.ts +16 -0
- package/dist/routes/slack-bot-access.d.ts +3 -0
- package/dist/routes/subscription-account-pools.d.ts +21 -0
- package/dist/routes/subscription-core-http.d.ts +5 -0
- package/dist/routes/subscription-pool-access.d.ts +37 -0
- package/dist/routes/subscription-pool-core.d.ts +153 -0
- package/dist/routes/supergrok.d.ts +6 -6
- package/dist/routes/usage-allowances.d.ts +12 -0
- package/dist/routes/variable-sets-removed.d.ts +2 -0
- package/dist/routes/vault-providers.d.ts +34 -0
- package/dist/routes/vaults.d.ts +3 -0
- package/dist/routes/workspace-integrations.d.ts +35 -0
- package/dist/routes/workspace-model-providers.d.ts +4 -0
- package/dist/routes/workspaces.d.ts +11 -2
- package/dist/sandbox/agent-update.d.ts +64 -0
- package/dist/sandbox/auth-callout.d.ts +8 -0
- package/dist/sandbox/channel-a.d.ts +15 -1
- package/dist/sandbox/enrollment.d.ts +26 -4
- package/dist/sandbox/machine-audience.d.ts +29 -0
- package/dist/sandbox/machines.d.ts +15 -2
- package/dist/sandbox/metrics-ingestion.d.ts +6 -2
- package/dist/sandbox/screen-control.d.ts +85 -0
- package/dist/sandbox/viewer.d.ts +5 -1
- package/dist/sandbox-file-artifacts.d.ts +17 -1
- package/dist/scheduled-task-blockers.d.ts +39 -0
- package/dist/scheduled-task-deletion.d.ts +3 -4
- package/dist/scheduled-task-lifecycle.d.ts +14 -0
- package/dist/secret-answers.d.ts +40 -0
- package/dist/session-create-observability.d.ts +5 -0
- package/dist/session-event-view-36GO3IL7.js +528 -0
- package/dist/session-event-view-36GO3IL7.js.map +1 -0
- package/dist/signed-state-secret.d.ts +18 -0
- package/dist/site-agent-guard.d.ts +53 -0
- package/dist/site-data/operations.d.ts +103 -0
- package/dist/site-data/service.d.ts +203 -0
- package/dist/site-frame-guard.d.ts +32 -0
- package/dist/site-publishing/agent-data-tools.d.ts +84 -0
- package/dist/site-publishing/agent-model.d.ts +58 -0
- package/dist/site-publishing/agent-session.d.ts +16 -0
- package/dist/site-publishing/content-scanner.d.ts +31 -0
- package/dist/site-publishing/edge-sync.d.ts +48 -0
- package/dist/site-publishing/gateway-signature.d.ts +56 -0
- package/dist/site-publishing/limits.d.ts +63 -0
- package/dist/site-publishing/page-tool-authority.d.ts +30 -0
- package/dist/site-publishing/platform-usage.d.ts +113 -0
- package/dist/site-publishing/platform.d.ts +21 -0
- package/dist/site-publishing/readiness.d.ts +62 -0
- package/dist/site-publishing/runtime.d.ts +179 -0
- package/dist/site-publishing/service.d.ts +264 -0
- package/dist/site-publishing/session-events.d.ts +22 -0
- package/dist/site-publishing/session-views.d.ts +36 -0
- package/dist/site-publishing/viewer.d.ts +40 -0
- package/dist/site-session-origin.d.ts +31 -6
- package/dist/site-sql/format.d.ts +47 -0
- package/dist/site-sql/gateway.d.ts +50 -0
- package/dist/site-sql/service.d.ts +213 -0
- package/dist/temporal-schedule-sync.d.ts +26 -0
- package/dist/transcription/billing-refusal.d.ts +8 -0
- package/dist/transcription/normalize.d.ts +23 -0
- package/dist/transcription/providers/azure-mai.d.ts +12 -0
- package/dist/transcription/providers/azure-openai.d.ts +4 -0
- package/dist/transcription/providers/openai.d.ts +16 -0
- package/dist/transcription/providers/subscription-core.d.ts +39 -0
- package/dist/transcription/service.d.ts +10 -1
- package/dist/transcription/wav-duration.d.ts +8 -0
- package/dist/workspace-tool-gateway-attestations.d.ts +37 -0
- package/dist/workspace-tool-gateway-observability.d.ts +16 -0
- package/dist/workspace-tool-gateway-secrets.d.ts +27 -0
- package/dist/workspace-tool-gateway.d.ts +102 -6
- package/dist/workspace-tool-target-fence.d.ts +116 -0
- package/dist/workspace-tool-target.d.ts +225 -0
- package/dist/workspace-webhook-dispatch.d.ts +26 -0
- package/dist/xai-subscription-core.d.ts +60 -0
- package/package.json +25 -20
- package/src/access-grant-rls.ts +64 -14
- package/src/admission-trace.ts +14 -0
- package/src/app.ts +1684 -252
- package/src/artifact-catalog-cursor.ts +1 -0
- package/src/attached-browser-viewer.ts +26 -0
- package/src/auth/managed-auth-attempt-context.ts +9 -0
- package/src/auth/managed-auth-rate-limits.ts +252 -0
- package/src/auth/managed-auth-session-adapter.ts +64 -29
- package/src/auth/managed-auth.ts +303 -35
- package/src/auth/managed-email.ts +18 -1
- package/src/auth/managed-sign-in-notifications.ts +81 -0
- package/src/auth/new-signups-gate.ts +49 -0
- package/src/auth/organization-user-setup.ts +3 -3
- package/src/auth/signup-funnel-metrics.ts +164 -0
- package/src/azure-live.ts +103 -0
- package/src/browser-action-fences.ts +45 -0
- package/src/browser-controller-authority.ts +1 -186
- package/src/browser-ephemeral.ts +39 -0
- package/src/browser-lost-runtime-end.ts +71 -0
- package/src/browser-state-authority.ts +1 -236
- package/src/claude-default-models.ts +37 -0
- package/src/claude-subscription-core.ts +154 -0
- package/src/claude-subscription-oauth.ts +352 -0
- package/src/claude-subscription-profile.ts +73 -0
- package/src/claude-subscription-usage.ts +51 -0
- package/src/claude-workspace-connection.ts +82 -0
- package/src/codemode.ts +236 -39
- package/src/codex-realtime.ts +181 -53
- package/src/codex-session-pointers.ts +80 -0
- package/src/company-brain-okf.ts +1 -1
- package/src/connected-machine-computer-access.ts +2 -1
- package/src/connection-ownership.ts +19 -13
- package/src/controller-data-plane.ts +34 -0
- package/src/credit-promotion-snapshot.ts +36 -0
- package/src/editable-artifact-production.ts +10 -0
- package/src/editable-artifact-source-session.ts +123 -0
- package/src/editable-artifact-websocket.ts +16 -3
- package/src/fatal-process-boundary.ts +67 -3
- package/src/gateway-realtime.ts +12 -8
- package/src/github-access.ts +31 -5
- package/src/github-browser-flow.ts +1 -1
- package/src/http/acting-person.ts +105 -0
- package/src/http/api-contract-compat.ts +49 -0
- package/src/http/api-error.ts +210 -2
- package/src/http/auth.ts +68 -4
- package/src/http/cors.ts +37 -5
- package/src/http/deprecation.ts +118 -0
- package/src/http/keyed-admission.ts +29 -0
- package/src/http/local-browser-boundary.ts +343 -0
- package/src/http/metrics-listener.ts +57 -0
- package/src/http/path-identifier.ts +54 -0
- package/src/http/registered-route-label.ts +34 -0
- package/src/http/rejection-telemetry.ts +120 -0
- package/src/http/request-body.ts +127 -0
- package/src/http/request-source.ts +154 -13
- package/src/http/sse.ts +88 -28
- package/src/http/unmatched-route.ts +60 -0
- package/src/http/user-content.ts +158 -0
- package/src/http/variable-sets-removed.ts +122 -0
- package/src/http/websocket-upgrade-dispatch.ts +29 -0
- package/src/index.ts +107 -57
- package/src/integration-connect-metrics.ts +228 -0
- package/src/integration-endpoint-test.ts +291 -0
- package/src/integrations/api-integrations.ts +81 -7
- package/src/integrations/atlassian.ts +202 -83
- package/src/integrations/fiken.ts +66 -38
- package/src/integrations/github-app-connect.ts +64 -18
- package/src/integrations/github-lens-connect.ts +8 -29
- package/src/integrations/google-drive.ts +448 -152
- package/src/integrations/integration-commit-authority.ts +69 -0
- package/src/integrations/marketplace-plugin.ts +1 -1
- package/src/integrations/oauth-client.ts +733 -193
- package/src/integrations/oauth-profiles.ts +116 -45
- package/src/integrations/onepassword-vault-provider.ts +16 -0
- package/src/integrations/personal-github-repositories.ts +1 -1
- package/src/integrations/personal-github.ts +61 -35
- package/src/integrations/provider-oauth.ts +67 -85
- package/src/integrations/slack-app-home.ts +5 -5
- package/src/integrations/slack-bot.ts +893 -201
- package/src/integrations/slack-file-upload-flow.ts +147 -0
- package/src/integrations/slack-install.ts +7 -7
- package/src/integrations/slack-interactions.ts +1589 -299
- package/src/integrations/slack-mrkdwn.ts +193 -0
- package/src/integrations/slack-routing.ts +2 -2
- package/src/integrations/slack-session-defaults.ts +224 -0
- package/src/integrations/slack-task-file-upload.ts +323 -0
- package/src/integrations/slack-tool-review.ts +65 -0
- package/src/integrations/social-oauth.ts +90 -58
- package/src/interaction-agent-access.ts +40 -20
- package/src/interaction-agent-version.ts +85 -0
- package/src/interaction-holder-heartbeat.ts +24 -8
- package/src/interaction-metrics.ts +164 -2
- package/src/interaction-route-stages.ts +94 -0
- package/src/interaction-secrets-vault.ts +213 -0
- package/src/interaction-secrets.ts +426 -0
- package/src/mcp/action-catalog-types.ts +14 -0
- package/src/mcp/action-catalog.gen.ts +8014 -0
- package/src/mcp/action-descriptions.ts +299 -0
- package/src/mcp/action-schema-imports.gen.ts +304 -0
- package/src/mcp/capability-account-readiness.ts +17 -0
- package/src/mcp/child-read-evidence.ts +19 -0
- package/src/mcp/company-profile-agent-admin.ts +13 -0
- package/src/mcp/contract-input.ts +165 -0
- package/src/mcp/documents.ts +9 -2
- package/src/mcp/editable-artifacts.ts +29 -11
- package/src/mcp/files.ts +5 -1
- package/src/mcp/first-party-tool-permissions.ts +7 -285
- package/src/mcp/knowledge-entries.ts +108 -34
- package/src/mcp/notification-tools.ts +480 -0
- package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
- package/src/mcp/scheduled-task-input.ts +89 -0
- package/src/mcp/scheduled-task-view.ts +63 -3
- package/src/mcp/secret-tools.ts +185 -0
- package/src/mcp/server.ts +2213 -1116
- package/src/mcp/session-admin-access-tools.ts +163 -0
- package/src/mcp/session-event-errors.ts +123 -0
- package/src/mcp/session-event-view.ts +395 -13
- package/src/mcp/session-view.ts +3 -0
- package/src/mcp/session-wait.ts +67 -7
- package/src/mcp/site-agent-clamp-tools.ts +140 -0
- package/src/mcp/site-data-tools.ts +339 -0
- package/src/mcp/site-publication-tools.ts +201 -0
- package/src/mcp/site-sql-tools.ts +427 -0
- package/src/mcp-oauth.ts +635 -45
- package/src/model-calls/chat-completions.ts +352 -0
- package/src/model-calls/pending-usage-settlements.ts +125 -0
- package/src/model-calls/service.ts +694 -0
- package/src/model-catalog.ts +68 -17
- package/src/native-push-dispatch.ts +457 -0
- package/src/observability.ts +15 -1
- package/src/organization-mcp.ts +485 -0
- package/src/prepared-mcp-actor.ts +154 -0
- package/src/prepared-mcp-permissions.ts +38 -0
- package/src/routes/analytics-consent.ts +123 -0
- package/src/routes/api-integrations.ts +268 -9
- package/src/routes/api-keys.ts +164 -25
- package/src/routes/artifact-catalog.ts +111 -0
- package/src/routes/automations.ts +163 -90
- package/src/routes/billing.ts +535 -59
- package/src/routes/browser-sessions.ts +966 -342
- package/src/routes/canonical-human-identities.ts +61 -0
- package/src/routes/capabilities.ts +112 -5
- package/src/routes/channels.ts +4 -3
- package/src/routes/chat-completions.ts +235 -0
- package/src/routes/claude-core-usage.ts +147 -0
- package/src/routes/claude-subscription-accounts.ts +233 -0
- package/src/routes/claude-subscription-oauth.ts +175 -0
- package/src/routes/client-errors.ts +394 -0
- package/src/routes/codex-core.ts +687 -0
- package/src/routes/codex.ts +1277 -1119
- package/src/routes/company-brain.ts +4 -0
- package/src/routes/computer-sessions.ts +319 -85
- package/src/routes/connect.ts +277 -175
- package/src/routes/connections.ts +690 -363
- package/src/routes/desktop-distribution.ts +217 -0
- package/src/routes/documents.ts +13 -7
- package/src/routes/editable-artifacts.ts +74 -4
- package/src/routes/enrollments.ts +110 -16
- package/src/routes/external-identity-links.ts +13 -12
- package/src/routes/files.ts +41 -16
- package/src/routes/github-browser-fonts.gen.ts +12 -0
- package/src/routes/github-browser-pages.ts +258 -0
- package/src/routes/github.ts +200 -75
- package/src/routes/inbox.ts +353 -0
- package/src/routes/insights-response-cache.ts +83 -0
- package/src/routes/insights-usage.ts +345 -0
- package/src/routes/insights.ts +103 -8
- package/src/routes/install.ts +51 -10
- package/src/routes/integration-facets.ts +70 -28
- package/src/routes/interaction-placement-message.ts +12 -0
- package/src/routes/knowledge.ts +136 -27
- package/src/routes/machines.ts +189 -135
- package/src/routes/managed-auth-session-sets.ts +43 -10
- package/src/routes/managed-onboarding.ts +39 -19
- package/src/routes/managed-sign-in-methods.ts +433 -0
- package/src/routes/memory-slack-publications.ts +3 -2
- package/src/routes/model-account-choice.ts +194 -0
- package/src/routes/model-account-settings.ts +235 -0
- package/src/routes/model-connection-access.ts +210 -48
- package/src/routes/native-app-auth.ts +296 -0
- package/src/routes/organization-integration-policy.ts +116 -0
- package/src/routes/organization-integrations.ts +326 -0
- package/src/routes/organization-mcp-connections.ts +183 -0
- package/src/routes/organization-memberships.ts +205 -19
- package/src/routes/organization-model-defaults.ts +110 -0
- package/src/routes/organization-model-providers.ts +74 -3
- package/src/routes/organization-service-accounts.ts +128 -0
- package/src/routes/organization-sessions.ts +21 -5
- package/src/routes/personal-api-keys.ts +184 -0
- package/src/routes/personal-github-git-broker.ts +2 -2
- package/src/routes/personal-github.ts +9 -4
- package/src/routes/plugins.ts +46 -7
- package/src/routes/pr-review-github.ts +105 -118
- package/src/routes/pr-review.ts +88 -82
- package/src/routes/preference-registry.ts +18 -0
- package/src/routes/rigs.ts +48 -13
- package/src/routes/scheduled-tasks.ts +347 -27
- package/src/routes/session-admin-access.ts +271 -0
- package/src/routes/session-artifact-associations.ts +164 -0
- package/src/routes/session-history-imports.ts +305 -0
- package/src/routes/sessions.ts +1682 -363
- package/src/routes/site-data.ts +340 -0
- package/src/routes/site-publishing.ts +584 -0
- package/src/routes/site-runtime.ts +1944 -0
- package/src/routes/site-sql.ts +263 -0
- package/src/routes/skill-content.ts +72 -7
- package/src/routes/skills.ts +5 -4
- package/src/routes/slack-bot-access.ts +108 -0
- package/src/routes/social.ts +27 -15
- package/src/routes/subscription-account-pools.ts +209 -0
- package/src/routes/subscription-core-http.ts +18 -0
- package/src/routes/subscription-pool-access.ts +180 -0
- package/src/routes/subscription-pool-core.ts +552 -0
- package/src/routes/supergrok.ts +174 -655
- package/src/routes/transcription-recordings.ts +71 -4
- package/src/routes/transcriptions.ts +26 -2
- package/src/routes/usage-allowances.ts +381 -0
- package/src/routes/variable-sets-removed.ts +19 -0
- package/src/routes/vault-providers.ts +782 -0
- package/src/routes/vaults.ts +421 -0
- package/src/routes/video-generation.ts +30 -12
- package/src/routes/workspace-artifacts.ts +165 -53
- package/src/routes/workspace-integrations.ts +576 -0
- package/src/routes/workspace-model-providers.ts +148 -0
- package/src/routes/workspace-state.ts +4 -0
- package/src/routes/workspaces.ts +625 -117
- package/src/sandbox/agent-update.ts +315 -0
- package/src/sandbox/auth-callout.ts +104 -21
- package/src/sandbox/channel-a.ts +150 -49
- package/src/sandbox/enrollment.ts +131 -22
- package/src/sandbox/machine-audience.ts +192 -0
- package/src/sandbox/machines.ts +75 -31
- package/src/sandbox/metrics-ingestion.ts +133 -2
- package/src/sandbox/rematerialize.ts +51 -21
- package/src/sandbox/screen-control.ts +289 -0
- package/src/sandbox/viewer.ts +33 -58
- package/src/sandbox-file-artifacts.ts +53 -27
- package/src/scheduled-task-blockers.ts +293 -0
- package/src/scheduled-task-deletion.ts +13 -3
- package/src/scheduled-task-lifecycle.ts +84 -0
- package/src/secret-answers.ts +119 -0
- package/src/session-create-observability.ts +46 -0
- package/src/session-realtime-context.ts +26 -4
- package/src/signed-state-secret.ts +58 -0
- package/src/site-agent-guard.ts +261 -0
- package/src/site-data/operations.ts +777 -0
- package/src/site-data/service.ts +1043 -0
- package/src/site-frame-guard.ts +107 -0
- package/src/site-publishing/agent-data-tools.ts +250 -0
- package/src/site-publishing/agent-model.ts +127 -0
- package/src/site-publishing/agent-session.ts +148 -0
- package/src/site-publishing/content-scanner.ts +93 -0
- package/src/site-publishing/edge-sync.ts +148 -0
- package/src/site-publishing/gateway-signature.ts +198 -0
- package/src/site-publishing/limits.ts +315 -0
- package/src/site-publishing/page-tool-authority.ts +111 -0
- package/src/site-publishing/platform-usage.ts +479 -0
- package/src/site-publishing/platform.ts +98 -0
- package/src/site-publishing/readiness.ts +330 -0
- package/src/site-publishing/runtime.ts +831 -0
- package/src/site-publishing/service.ts +1282 -0
- package/src/site-publishing/session-events.ts +262 -0
- package/src/site-publishing/session-views.ts +262 -0
- package/src/site-publishing/viewer.ts +75 -0
- package/src/site-session-origin.ts +91 -6
- package/src/site-sql/format.ts +134 -0
- package/src/site-sql/gateway.ts +168 -0
- package/src/site-sql/service.ts +1636 -0
- package/src/site-uploads.ts +27 -5
- package/src/temporal-schedule-sync.ts +74 -0
- package/src/transcription/billing-refusal.ts +14 -0
- package/src/transcription/normalize.ts +77 -0
- package/src/transcription/providers/azure-mai.ts +98 -0
- package/src/transcription/providers/azure-openai.ts +16 -7
- package/src/transcription/providers/codex-subscription.ts +190 -67
- package/src/transcription/providers/openai.ts +79 -5
- package/src/transcription/providers/subscription-core.ts +125 -0
- package/src/transcription/providers/xai-subscription.ts +81 -83
- package/src/transcription/segmenter.ts +7 -1
- package/src/transcription/service.ts +239 -10
- package/src/transcription/wav-duration.ts +39 -0
- package/src/workspace-deletion.ts +25 -1
- package/src/workspace-tool-gateway-attestations.ts +133 -0
- package/src/workspace-tool-gateway-observability.ts +64 -0
- package/src/workspace-tool-gateway-secrets.ts +104 -0
- package/src/workspace-tool-gateway.ts +921 -103
- package/src/workspace-tool-target-fence.ts +380 -0
- package/src/workspace-tool-target.ts +554 -0
- package/src/workspace-webhook-dispatch.ts +225 -0
- package/src/xai-realtime.ts +85 -81
- package/src/xai-subscription-core.ts +172 -0
- package/dist/browser-auth-broker.d.ts +0 -20
- package/dist/chunk-AJ62T5QN.js.map +0 -1
- package/dist/connection-authority-owner.d.ts +0 -59
- package/dist/routes/connection-authorities.d.ts +0 -3
- package/dist/routes/environments.d.ts +0 -5
- package/dist/routes/host-mcp-bindings.d.ts +0 -5
- package/dist/routes/host-mcp-resolvers.d.ts +0 -3
- package/dist/routes/packs.d.ts +0 -3
- package/dist/session-event-view-MU3HMNV3.js +0 -264
- package/dist/session-event-view-MU3HMNV3.js.map +0 -1
- package/dist/xai-subscription-auth.d.ts +0 -23
- package/src/browser-auth-broker.ts +0 -155
- package/src/connection-authority-owner.ts +0 -61
- package/src/routes/connection-authorities.ts +0 -139
- package/src/routes/environments.ts +0 -388
- package/src/routes/host-mcp-bindings.ts +0 -122
- package/src/routes/host-mcp-resolvers.ts +0 -41
- package/src/routes/packs.ts +0 -784
- package/src/xai-subscription-auth.ts +0 -139
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import { createHash, createHmac } from "node:crypto";
|
|
2
|
+
import { verifyDirectModelAccess } from "@opengeni/core";
|
|
3
|
+
import {
|
|
4
|
+
assertClaudeWorkspaceCredential,
|
|
5
|
+
prepareClaudeWorkspaceCredential,
|
|
6
|
+
} from "../claude-workspace-connection";
|
|
7
|
+
import {
|
|
8
|
+
createConnectionIdempotently,
|
|
9
|
+
upsertWorkspaceProviderApiKeyConnection,
|
|
10
|
+
rotateWorkspaceProviderApiKeyConnection,
|
|
11
|
+
revokeWorkspaceProviderApiKeyConnections,
|
|
12
|
+
workspaceProviderApiKeyConnectionSpec,
|
|
13
|
+
type WorkspaceProviderApiKeyConnectionKind,
|
|
14
|
+
getConnectionCreationResult,
|
|
15
|
+
ConnectionCreateIdempotencyError,
|
|
16
|
+
} from "@opengeni/db";
|
|
17
|
+
import { sql } from "drizzle-orm";
|
|
18
|
+
import { assertOrganizationIntegrationAllowed } from "@opengeni/contracts";
|
|
19
|
+
import {
|
|
20
|
+
withOrganizationIntegrationAcquisition,
|
|
21
|
+
withOrganizationIntegrationPolicyFence,
|
|
22
|
+
} from "@opengeni/db/organization-integration-policy";
|
|
2
23
|
import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
|
|
3
24
|
import {
|
|
4
25
|
startSlackBotInstall,
|
|
@@ -7,6 +28,9 @@ import {
|
|
|
7
28
|
import {
|
|
8
29
|
WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
|
|
9
30
|
WORKSPACE_OPENROUTER_CONNECTION_ROLE,
|
|
31
|
+
WORKSPACE_OPPER_CONNECTION_DOMAIN,
|
|
32
|
+
WORKSPACE_OPPER_CONNECTION_ROLE,
|
|
33
|
+
opperCredentialProblem,
|
|
10
34
|
VERCEL_AI_GATEWAY_CONNECTION_DOMAIN,
|
|
11
35
|
VERCEL_AI_GATEWAY_CONNECTION_ROLE,
|
|
12
36
|
} from "@opengeni/config";
|
|
@@ -21,6 +45,7 @@ import {
|
|
|
21
45
|
import {
|
|
22
46
|
API_INTEGRATION_OAUTH_CREDENTIAL_ROLE,
|
|
23
47
|
ApiIntegrationOAuthStartRequest,
|
|
48
|
+
DirectModelProviderMetadata,
|
|
24
49
|
ConnectionResponse,
|
|
25
50
|
CreateConnectionRequest,
|
|
26
51
|
FikenInstallRequest,
|
|
@@ -31,6 +56,8 @@ import {
|
|
|
31
56
|
ListConnectionsResponse,
|
|
32
57
|
OPENROUTER_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY,
|
|
33
58
|
OPENROUTER_CREDENTIAL_OPERATION_ID_METADATA_KEY,
|
|
59
|
+
OPPER_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY,
|
|
60
|
+
OPPER_CREDENTIAL_OPERATION_ID_METADATA_KEY,
|
|
34
61
|
OpenGeniSlackBotInstallRequest,
|
|
35
62
|
OAuthStartRequest,
|
|
36
63
|
OAuthStartResponse,
|
|
@@ -59,7 +86,9 @@ import {
|
|
|
59
86
|
} from "@opengeni/contracts/personal-github";
|
|
60
87
|
import {
|
|
61
88
|
hasPermission,
|
|
89
|
+
listOwnConnectionAccountsForGrant,
|
|
62
90
|
hasReservedFikenMetadata,
|
|
91
|
+
ONEPASSWORD_CREDENTIAL_ROLE,
|
|
63
92
|
hasReservedOpenGeniSlackBotMetadata,
|
|
64
93
|
isOpenGeniSlackBotConnection,
|
|
65
94
|
openGeniSlackBotMetadata,
|
|
@@ -74,27 +103,21 @@ import {
|
|
|
74
103
|
finishConnectOperation,
|
|
75
104
|
getConnectAttempt,
|
|
76
105
|
decryptEnvironmentValue,
|
|
77
|
-
createConnection,
|
|
78
106
|
withWorkspaceSubjectRls,
|
|
79
107
|
type Database,
|
|
80
108
|
encryptEnvironmentValue,
|
|
109
|
+
brokeredCredentialBundleProblem,
|
|
81
110
|
getConnectionMetadata,
|
|
82
111
|
listConnectionsMetadata,
|
|
83
112
|
listSlackInstallationBindings,
|
|
84
113
|
persistSlackBotInstallationWithSuccessAudit,
|
|
85
114
|
recordSlackBotInstallCallbackFailure,
|
|
86
115
|
revokeConnection,
|
|
87
|
-
revokeWorkspaceOpenRouterConnections,
|
|
88
|
-
revokeWorkspaceVercelAiGatewayConnections,
|
|
89
116
|
revokeConnectionWithSlackBotSuccessAudit,
|
|
90
|
-
rotateWorkspaceVercelAiGatewayConnection,
|
|
91
|
-
rotateWorkspaceOpenRouterConnection,
|
|
92
117
|
SlackBotLifecycleSuccessAuditError,
|
|
93
118
|
SlackInstallationBindingConflictError,
|
|
94
119
|
updateConnection,
|
|
95
120
|
updateSlackBotDocumentDestination,
|
|
96
|
-
upsertWorkspaceVercelAiGatewayConnection,
|
|
97
|
-
upsertWorkspaceOpenRouterConnection,
|
|
98
121
|
type SlackBotInstallCallbackFailureReason,
|
|
99
122
|
type SlackBotInstallCallbackFailureStage,
|
|
100
123
|
} from "@opengeni/db";
|
|
@@ -114,6 +137,7 @@ import {
|
|
|
114
137
|
isApiIntegrationProviderOAuthState,
|
|
115
138
|
startApiIntegrationProviderOAuth,
|
|
116
139
|
} from "../integrations/provider-oauth";
|
|
140
|
+
import { offerClaudeDefaultModels } from "../claude-default-models";
|
|
117
141
|
import { disconnectPersonalGitHub } from "../integrations/personal-github";
|
|
118
142
|
import {
|
|
119
143
|
browseAtlassianSources,
|
|
@@ -126,12 +150,14 @@ import {
|
|
|
126
150
|
import {
|
|
127
151
|
completeMcpOAuthCallback,
|
|
128
152
|
integrationBaseUrl,
|
|
153
|
+
oauthStateFailureReturn,
|
|
129
154
|
startMcpOAuth,
|
|
130
155
|
} from "../integrations/oauth-client";
|
|
131
156
|
import {
|
|
132
157
|
assertPersonalConnectionOwnerPrincipal,
|
|
133
158
|
isPersonalConnectionOwnerPrincipal,
|
|
134
159
|
requireLegacyOAuthActor,
|
|
160
|
+
requireProviderConsentInBrowser,
|
|
135
161
|
} from "../connection-ownership";
|
|
136
162
|
import { canonicalProviderDomain } from "../integrations/provider-domain";
|
|
137
163
|
import { externalActorContinuationForAuthorization } from "@opengeni/core";
|
|
@@ -152,6 +178,7 @@ import {
|
|
|
152
178
|
} from "@opengeni/contracts";
|
|
153
179
|
import { readSignedState } from "@opengeni/github";
|
|
154
180
|
import { oauthStateTtlMs, requireIntegrationsStateSecret } from "../integrations/oauth-client";
|
|
181
|
+
import { parseRequestJson } from "../http/request-body";
|
|
155
182
|
|
|
156
183
|
type OpenGeniSlackInstallState = {
|
|
157
184
|
connectAttemptId?: string;
|
|
@@ -185,6 +212,26 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
185
212
|
);
|
|
186
213
|
});
|
|
187
214
|
|
|
215
|
+
app.get("/v1/workspaces/:workspaceId/connections/accounts", async (c) => {
|
|
216
|
+
const workspaceId = c.req.param("workspaceId");
|
|
217
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "connections:read");
|
|
218
|
+
const includeInactive = c.req.query("includeInactive");
|
|
219
|
+
if (
|
|
220
|
+
includeInactive !== undefined &&
|
|
221
|
+
includeInactive !== "true" &&
|
|
222
|
+
includeInactive !== "false"
|
|
223
|
+
) {
|
|
224
|
+
throw new HTTPException(400, { message: "includeInactive must be true or false" });
|
|
225
|
+
}
|
|
226
|
+
return c.json(
|
|
227
|
+
ListConnectionsResponse.parse({
|
|
228
|
+
connections: await listOwnConnectionAccountsForGrant(db, grant, {
|
|
229
|
+
includeInactive: includeInactive === "true",
|
|
230
|
+
}),
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
188
235
|
app.get("/v1/workspaces/:workspaceId/connections/slack-bot/bindings", async (c) => {
|
|
189
236
|
const workspaceId = c.req.param("workspaceId");
|
|
190
237
|
const grant = await requireAccessGrant(c, deps, workspaceId, "connections:read");
|
|
@@ -198,17 +245,72 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
198
245
|
);
|
|
199
246
|
});
|
|
200
247
|
|
|
248
|
+
app.get("/v1/workspaces/:workspaceId/connections/operations/:operationId", async (c) => {
|
|
249
|
+
const workspaceId = c.req.param("workspaceId");
|
|
250
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "connections:read");
|
|
251
|
+
const operationId = CreateConnectionRequest.shape.operationId
|
|
252
|
+
.unwrap()
|
|
253
|
+
.parse(c.req.param("operationId"));
|
|
254
|
+
const connection = await getConnectionCreationResult(db, {
|
|
255
|
+
accountId: grant.accountId,
|
|
256
|
+
workspaceId,
|
|
257
|
+
subjectId: grant.subjectId,
|
|
258
|
+
operationId,
|
|
259
|
+
});
|
|
260
|
+
if (!connection) throw new HTTPException(404, { message: "Connection operation not found" });
|
|
261
|
+
return c.json(ConnectionResponse.parse({ connection }));
|
|
262
|
+
});
|
|
263
|
+
|
|
201
264
|
app.post("/v1/workspaces/:workspaceId/connections", async (c) => {
|
|
202
265
|
const workspaceId = c.req.param("workspaceId");
|
|
203
266
|
const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "connections:write");
|
|
204
267
|
const grant = access.grant;
|
|
205
268
|
const beforeCommit = externalContinuationCommitAuthorizer(access);
|
|
206
|
-
const payload =
|
|
269
|
+
const payload = await parseRequestJson(c, CreateConnectionRequest);
|
|
270
|
+
if (
|
|
271
|
+
payload.metadata?.directModelProvider !== undefined ||
|
|
272
|
+
String(payload.metadata?.credentialRole ?? "").startsWith("direct_")
|
|
273
|
+
) {
|
|
274
|
+
const config = DirectModelProviderMetadata.safeParse(payload.metadata?.directModelProvider);
|
|
275
|
+
const domain = config.success
|
|
276
|
+
? new URL(config.data.endpoint ?? "https://api.openai.com/v1").hostname
|
|
277
|
+
: null;
|
|
278
|
+
if (
|
|
279
|
+
!config.success ||
|
|
280
|
+
payload.subjectId !== null ||
|
|
281
|
+
payload.kind !== "api_key" ||
|
|
282
|
+
canonicalProviderDomain(payload.providerDomain) !== domain ||
|
|
283
|
+
payload.metadata?.credentialRole !== `direct_${config.data.provider}` ||
|
|
284
|
+
typeof payload.credential.apiKey !== "string" ||
|
|
285
|
+
!payload.credential.apiKey.trim() ||
|
|
286
|
+
!payload.operationId ||
|
|
287
|
+
payload.expiresAt
|
|
288
|
+
) {
|
|
289
|
+
throw new HTTPException(422, {
|
|
290
|
+
message:
|
|
291
|
+
"Invalid OpenAI or Azure OpenAI connection: provide a key, model, and official provider endpoint",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
payload.metadata = { ...payload.metadata, directModelProvider: config.data };
|
|
295
|
+
if (payload.verifyModelAccess) {
|
|
296
|
+
await verifyDirectModelAccess(
|
|
297
|
+
config.data,
|
|
298
|
+
payload.credential.apiKey as string,
|
|
299
|
+
deps.directModelFetch,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
} else if (payload.verifyModelAccess) {
|
|
303
|
+
throw new HTTPException(422, {
|
|
304
|
+
message: "Model checks require an OpenAI or Azure OpenAI connection",
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
const directModelKey = payload.metadata?.directModelProvider !== undefined;
|
|
207
308
|
// All writes in this closure must use the caller-owned scoped transaction.
|
|
208
309
|
// eslint-disable-next-line no-shadow
|
|
209
310
|
const persist = async (db: Database) => {
|
|
210
311
|
assertNotReservedSlackBotMetadata(payload.metadata);
|
|
211
312
|
assertNotReservedFikenMetadata(payload.metadata);
|
|
313
|
+
assertNotReservedOnePasswordMetadata(payload.metadata);
|
|
212
314
|
assertNotReservedApiIntegrationOAuthMetadata(payload.metadata);
|
|
213
315
|
assertNotReservedPersonalGitHubMetadata(payload.metadata);
|
|
214
316
|
const key = requireEnvironmentEncryption(settings);
|
|
@@ -217,6 +319,19 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
217
319
|
assertPersonalConnectionOwnerPrincipal(access);
|
|
218
320
|
}
|
|
219
321
|
const providerDomain = canonicalProviderDomain(payload.providerDomain);
|
|
322
|
+
payload.credential = prepareClaudeWorkspaceCredential(
|
|
323
|
+
settings,
|
|
324
|
+
workspaceId,
|
|
325
|
+
payload.metadata,
|
|
326
|
+
payload.credential,
|
|
327
|
+
);
|
|
328
|
+
assertClaudeWorkspaceCredential(settings, {
|
|
329
|
+
subjectId,
|
|
330
|
+
providerDomain,
|
|
331
|
+
kind: payload.kind,
|
|
332
|
+
metadata: payload.metadata,
|
|
333
|
+
credential: payload.credential,
|
|
334
|
+
});
|
|
220
335
|
assertNotDirectPersonalSlackOAuth(providerDomain, payload.kind);
|
|
221
336
|
assertNotDirectGoogleDriveOAuth(providerDomain, payload.kind, payload.metadata);
|
|
222
337
|
assertNotDirectAtlassianOAuth(providerDomain, payload.kind, payload.metadata);
|
|
@@ -228,6 +343,11 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
228
343
|
kind: payload.kind,
|
|
229
344
|
metadata: payload.metadata,
|
|
230
345
|
});
|
|
346
|
+
// Validated model keys store `{ apiKey }` for model execution. Generic
|
|
347
|
+
// integration credentials still require an explicit broker destination.
|
|
348
|
+
if (!workspaceProviderKind && !directModelKey)
|
|
349
|
+
assertBrokeredApiKeyCredential(payload.kind, payload.credential);
|
|
350
|
+
if (workspaceProviderKind === "opper") assertOpperCredential(payload.credential);
|
|
231
351
|
const connection = workspaceProviderKind
|
|
232
352
|
? await (async () => {
|
|
233
353
|
const provider = workspaceProviderApiKeyConnectionSpec(workspaceProviderKind);
|
|
@@ -261,18 +381,26 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
261
381
|
metadata,
|
|
262
382
|
updatedBySubjectId: grant.subjectId,
|
|
263
383
|
};
|
|
264
|
-
const created =
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
384
|
+
const created = await upsertWorkspaceProviderApiKeyConnection(
|
|
385
|
+
db,
|
|
386
|
+
workspaceProviderKind,
|
|
387
|
+
input,
|
|
388
|
+
);
|
|
268
389
|
if (!created || created.status === "revoked") {
|
|
269
390
|
throw new HTTPException(409, {
|
|
270
391
|
message: `${provider.label} is already connected; reload before replacing its key`,
|
|
271
392
|
});
|
|
272
393
|
}
|
|
394
|
+
if (workspaceProviderKind === "anthropic")
|
|
395
|
+
await offerClaudeDefaultModels(deps, {
|
|
396
|
+
accountId: grant.accountId,
|
|
397
|
+
workspaceId,
|
|
398
|
+
actorSubjectId: grant.subjectId,
|
|
399
|
+
providerKind: "anthropic",
|
|
400
|
+
});
|
|
273
401
|
return created;
|
|
274
402
|
})()
|
|
275
|
-
: await
|
|
403
|
+
: await createConnectionIdempotently(db, {
|
|
276
404
|
accountId: grant.accountId,
|
|
277
405
|
workspaceId,
|
|
278
406
|
subjectId,
|
|
@@ -283,15 +411,56 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
283
411
|
expiresAt: payload.expiresAt ? new Date(payload.expiresAt) : null,
|
|
284
412
|
metadata: payload.metadata,
|
|
285
413
|
createdBySubjectId: grant.subjectId,
|
|
414
|
+
...(payload.operationId
|
|
415
|
+
? {
|
|
416
|
+
operation: {
|
|
417
|
+
id: payload.operationId,
|
|
418
|
+
requestDigest: workspaceProviderCredentialRequestDigest(key, {
|
|
419
|
+
action: "create",
|
|
420
|
+
providerDomain,
|
|
421
|
+
kind: payload.kind,
|
|
422
|
+
subjectId,
|
|
423
|
+
credential: payload.credential,
|
|
424
|
+
grantedScopes: payload.grantedScopes,
|
|
425
|
+
expiresAt: payload.expiresAt
|
|
426
|
+
? new Date(payload.expiresAt).toISOString()
|
|
427
|
+
: null,
|
|
428
|
+
metadata: payload.metadata,
|
|
429
|
+
}),
|
|
430
|
+
},
|
|
431
|
+
}
|
|
432
|
+
: {}),
|
|
433
|
+
}).catch((error: unknown) => {
|
|
434
|
+
if (error instanceof ConnectionCreateIdempotencyError) {
|
|
435
|
+
throw new HTTPException(409, { message: error.message });
|
|
436
|
+
}
|
|
437
|
+
throw error;
|
|
286
438
|
});
|
|
287
439
|
return c.json(ConnectionResponse.parse({ connection }), 201);
|
|
288
440
|
};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
441
|
+
const commit = (database: Database) =>
|
|
442
|
+
beforeCommit
|
|
443
|
+
? withWorkspaceSubjectRls(database, workspaceId, grant.subjectId, async (tx) => {
|
|
444
|
+
await beforeCommit(tx);
|
|
445
|
+
return persist(tx);
|
|
446
|
+
})
|
|
447
|
+
: persist(database);
|
|
448
|
+
// Only the canonical workspace model-key lane is outside integration policy.
|
|
449
|
+
// Generic credentials have no validated protocol/curated target in this contract.
|
|
450
|
+
const modelKey = workspaceProviderApiKeyConnectionKind({
|
|
451
|
+
subjectId: createConnectionSubjectId(payload, grant.subjectId),
|
|
452
|
+
providerDomain: canonicalProviderDomain(payload.providerDomain),
|
|
453
|
+
kind: payload.kind,
|
|
454
|
+
metadata: payload.metadata,
|
|
455
|
+
});
|
|
456
|
+
return modelKey || directModelKey
|
|
457
|
+
? commit(db)
|
|
458
|
+
: withOrganizationIntegrationAcquisition(
|
|
459
|
+
db,
|
|
460
|
+
{ accountId: grant.accountId, workspaceId },
|
|
461
|
+
[null],
|
|
462
|
+
commit,
|
|
463
|
+
);
|
|
295
464
|
});
|
|
296
465
|
|
|
297
466
|
app.post("/v1/workspaces/:workspaceId/connections/slack-bot/install", async (c) => {
|
|
@@ -299,15 +468,24 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
299
468
|
const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "connections:write");
|
|
300
469
|
requireLegacyOAuthActor(access);
|
|
301
470
|
const grant = access.grant;
|
|
302
|
-
const payload =
|
|
471
|
+
const payload = await parseRequestJson(c, OpenGeniSlackBotInstallRequest);
|
|
303
472
|
return c.json(
|
|
304
|
-
await
|
|
305
|
-
|
|
306
|
-
workspaceId,
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
473
|
+
await withOrganizationIntegrationAcquisition(
|
|
474
|
+
db,
|
|
475
|
+
{ accountId: grant.accountId, workspaceId },
|
|
476
|
+
["slack-bot"],
|
|
477
|
+
(tx) =>
|
|
478
|
+
startSlackBotInstall(
|
|
479
|
+
{ ...deps, db: tx },
|
|
480
|
+
{
|
|
481
|
+
accountId: grant.accountId,
|
|
482
|
+
workspaceId,
|
|
483
|
+
subjectId: grant.subjectId,
|
|
484
|
+
requestUrl: c.req.url,
|
|
485
|
+
...(payload.connectionId ? { connectionId: payload.connectionId } : {}),
|
|
486
|
+
},
|
|
487
|
+
),
|
|
488
|
+
),
|
|
311
489
|
);
|
|
312
490
|
});
|
|
313
491
|
|
|
@@ -329,12 +507,25 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
329
507
|
operationId: `oauth:${state.nonce}`,
|
|
330
508
|
inputDigest: createHash("sha256").update(c.req.query("state")!).digest("hex"),
|
|
331
509
|
};
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
510
|
+
const callbackState = state;
|
|
511
|
+
const callbackOperation = operation;
|
|
512
|
+
const claim = await withOrganizationIntegrationPolicyFence(
|
|
513
|
+
db,
|
|
514
|
+
callbackState,
|
|
515
|
+
(policyTx, policy) =>
|
|
516
|
+
claimConnectOperation(policyTx, callbackState, {
|
|
517
|
+
...callbackOperation,
|
|
518
|
+
expectedRevision: stored.attempt.revision,
|
|
519
|
+
authorize: (tx, _attempt, origin) =>
|
|
520
|
+
requireConnectOwnerAuthority(tx, callbackState, "connections:write", origin),
|
|
521
|
+
authorizeAcquisition: async () => {
|
|
522
|
+
// Provider denial/cancellation acquires nothing; completed exact
|
|
523
|
+
// receipts replay before this callback is invoked.
|
|
524
|
+
if (!c.req.query("error") && c.req.query("code"))
|
|
525
|
+
assertOrganizationIntegrationAllowed(policy, "slack-bot");
|
|
526
|
+
},
|
|
527
|
+
}),
|
|
528
|
+
);
|
|
338
529
|
if (claim.status === "replayed")
|
|
339
530
|
return new Response(null, { status: 302, headers: { Location: exactReturnUrl } });
|
|
340
531
|
}
|
|
@@ -391,6 +582,7 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
391
582
|
"Slack installation callback is missing code",
|
|
392
583
|
);
|
|
393
584
|
}
|
|
585
|
+
await withOrganizationIntegrationAcquisition(db, state, ["slack-bot"], async () => {});
|
|
394
586
|
const slack = requireOpenGeniSlackOAuthSettings(settings);
|
|
395
587
|
const redirectUri = `${baseUrl}/v1/integrations/slack/callback`;
|
|
396
588
|
const authorization = await exchangeOpenGeniSlackAuthorizationCode(
|
|
@@ -424,35 +616,44 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
424
616
|
verified,
|
|
425
617
|
});
|
|
426
618
|
if (operation) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
619
|
+
const acceptedOperation = operation;
|
|
620
|
+
await withOrganizationIntegrationPolicyFence(db, acceptedState, (policyTx, policy) =>
|
|
621
|
+
finishConnectOperation(policyTx, acceptedState, {
|
|
622
|
+
...acceptedOperation,
|
|
623
|
+
authorize: (tx, _attempt, origin) =>
|
|
624
|
+
requireConnectOwnerAuthority(tx, acceptedState, "connections:write", origin),
|
|
625
|
+
commit: async (tx, current) => {
|
|
626
|
+
assertOrganizationIntegrationAllowed(policy, "slack-bot");
|
|
627
|
+
const connection = await persist(tx);
|
|
628
|
+
return {
|
|
629
|
+
...current,
|
|
630
|
+
revision: current.revision + 1,
|
|
631
|
+
state: "complete",
|
|
632
|
+
credentialsCommitted: true,
|
|
633
|
+
nextAction: { type: "none" },
|
|
634
|
+
account: {
|
|
635
|
+
id: connection.id,
|
|
636
|
+
version: connection.version,
|
|
637
|
+
providerId: "slack-bot",
|
|
638
|
+
label: "Slack workspace bot",
|
|
639
|
+
ownership: "workspace",
|
|
640
|
+
status: "connected",
|
|
641
|
+
},
|
|
642
|
+
};
|
|
643
|
+
},
|
|
644
|
+
}),
|
|
645
|
+
);
|
|
450
646
|
return new Response(null, { status: 302, headers: { Location: exactReturnUrl! } });
|
|
451
647
|
}
|
|
452
|
-
const connection = await
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
648
|
+
const connection = await withOrganizationIntegrationAcquisition(
|
|
649
|
+
db,
|
|
650
|
+
acceptedState,
|
|
651
|
+
["slack-bot"],
|
|
652
|
+
async (tx) => {
|
|
653
|
+
await requireSlackInstallCallbackGrant(tx, acceptedState);
|
|
654
|
+
return persist(tx);
|
|
655
|
+
},
|
|
656
|
+
);
|
|
456
657
|
return c.redirect(
|
|
457
658
|
slackInstallReturnUrl(baseUrl, state.returnPath, "connected", connection.id),
|
|
458
659
|
302,
|
|
@@ -480,7 +681,12 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
480
681
|
}
|
|
481
682
|
const reason = slackInstallErrorReason(error);
|
|
482
683
|
return c.redirect(
|
|
483
|
-
slackInstallReturnUrl(
|
|
684
|
+
slackInstallReturnUrl(
|
|
685
|
+
baseUrl,
|
|
686
|
+
state?.returnPath ?? oauthStateFailureReturn(settings, c.req.query("state")).returnPath,
|
|
687
|
+
"error",
|
|
688
|
+
reason,
|
|
689
|
+
),
|
|
484
690
|
302,
|
|
485
691
|
);
|
|
486
692
|
}
|
|
@@ -499,19 +705,24 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
499
705
|
"connections:write",
|
|
500
706
|
);
|
|
501
707
|
const grant = authorization.grant;
|
|
502
|
-
const payload =
|
|
708
|
+
const payload = await parseRequestJson(c, FikenInstallRequest);
|
|
503
709
|
const persist = await prepareFikenTokenInstall(deps, grant, payload);
|
|
504
710
|
const continuation = externalActorContinuationForAuthorization(authorization);
|
|
505
|
-
const connection = await
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
711
|
+
const connection = await withOrganizationIntegrationAcquisition(
|
|
712
|
+
db,
|
|
713
|
+
{ accountId: grant.accountId, workspaceId },
|
|
714
|
+
["fiken"],
|
|
715
|
+
async (tx) => {
|
|
716
|
+
await requireConnectOwnerAuthority(tx, {
|
|
717
|
+
accountId: grant.accountId,
|
|
718
|
+
workspaceId,
|
|
719
|
+
subjectId: grant.subjectId,
|
|
720
|
+
personalOwnerVerified: isPersonalConnectionOwnerPrincipal(authorization),
|
|
721
|
+
...(continuation ? { externalContinuation: continuation } : {}),
|
|
722
|
+
});
|
|
723
|
+
return persist(tx);
|
|
724
|
+
},
|
|
725
|
+
);
|
|
515
726
|
return c.json(ConnectionResponse.parse({ connection }), payload.connectionId ? 200 : 201);
|
|
516
727
|
});
|
|
517
728
|
|
|
@@ -523,7 +734,7 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
523
734
|
const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "connections:write");
|
|
524
735
|
requireLegacyOAuthActor(access);
|
|
525
736
|
const grant = access.grant;
|
|
526
|
-
const payload =
|
|
737
|
+
const payload = await parseRequestJson(c, FikenOAuthStartRequest);
|
|
527
738
|
requireEnvironmentEncryption(settings);
|
|
528
739
|
return c.json(
|
|
529
740
|
FikenOAuthStartResponse.parse(
|
|
@@ -788,259 +999,356 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
788
999
|
"connections:write",
|
|
789
1000
|
);
|
|
790
1001
|
const { grant } = authorization;
|
|
791
|
-
const payload =
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
const existing = await getConnectionMetadata(
|
|
1002
|
+
const payload = await parseRequestJson(c, UpdateConnectionRequest);
|
|
1003
|
+
// Policy first, then the existing tenancy prefix and exact credential row.
|
|
1004
|
+
// The locked live identity makes unchanged/reducing updates safe against a
|
|
1005
|
+
// concurrent replacement; all final writes below use this same transaction.
|
|
1006
|
+
return withOrganizationIntegrationPolicyFence(
|
|
797
1007
|
db,
|
|
798
|
-
workspaceId,
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1008
|
+
{ accountId: grant.accountId, workspaceId },
|
|
1009
|
+
(policyTx, policy) =>
|
|
1010
|
+
// eslint-disable-next-line no-shadow -- every existing final writer must use this exact scoped transaction
|
|
1011
|
+
withWorkspaceSubjectRls(policyTx, workspaceId, grant.subjectId, async (db) => {
|
|
1012
|
+
const initial = await getConnectionMetadata(
|
|
1013
|
+
db,
|
|
1014
|
+
workspaceId,
|
|
1015
|
+
c.req.param("connectionId"),
|
|
1016
|
+
grant.subjectId,
|
|
1017
|
+
);
|
|
1018
|
+
if (
|
|
1019
|
+
initial?.metadata.directModelProvider !== undefined ||
|
|
1020
|
+
payload.metadata?.directModelProvider !== undefined ||
|
|
1021
|
+
String(payload.metadata?.credentialRole ?? "").startsWith("direct_")
|
|
1022
|
+
) {
|
|
1023
|
+
throw new HTTPException(422, {
|
|
1024
|
+
message:
|
|
1025
|
+
"Disconnect and reconnect OpenAI or Azure OpenAI to replace its key or deployment",
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
// Model-key rotation owns an advisory -> credential-row prefix. Do
|
|
1029
|
+
// not take its row first or change that excluded lane's lock order.
|
|
1030
|
+
if (!initial || !workspaceProviderApiKeyConnectionKind(initial)) {
|
|
1031
|
+
await db.execute(
|
|
1032
|
+
sql`select id from connections where workspace_id = ${workspaceId}::uuid and id = ${c.req.param("connectionId")}::uuid and (subject_id is null or subject_id = ${grant.subjectId}) for update`,
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
assertNotReservedSlackBotMetadata(payload.metadata);
|
|
1036
|
+
assertNotReservedFikenMetadata(payload.metadata);
|
|
1037
|
+
assertNotReservedOnePasswordMetadata(payload.metadata);
|
|
1038
|
+
assertNotReservedApiIntegrationOAuthMetadata(payload.metadata);
|
|
1039
|
+
assertNotReservedPersonalGitHubMetadata(payload.metadata);
|
|
1040
|
+
const existing = await getConnectionMetadata(
|
|
1041
|
+
db,
|
|
1042
|
+
workspaceId,
|
|
1043
|
+
c.req.param("connectionId"),
|
|
1044
|
+
grant.subjectId,
|
|
1045
|
+
);
|
|
1046
|
+
if (existing && isOpenGeniSlackBotConnection(existing)) {
|
|
1047
|
+
const destination = ConnectorDocumentDestinationSelection.safeParse(
|
|
1048
|
+
payload.metadata?.documentDestination,
|
|
1049
|
+
);
|
|
1050
|
+
const destinationOnlyUpdate =
|
|
1051
|
+
payload.metadata !== undefined &&
|
|
1052
|
+
Object.keys(payload.metadata).length === 1 &&
|
|
1053
|
+
payload.providerDomain === undefined &&
|
|
1054
|
+
payload.subjectId === undefined &&
|
|
1055
|
+
payload.kind === undefined &&
|
|
1056
|
+
payload.status === undefined &&
|
|
1057
|
+
payload.credential === undefined &&
|
|
1058
|
+
payload.grantedScopes === undefined &&
|
|
1059
|
+
payload.expiresAt === undefined;
|
|
1060
|
+
if (!destination.success || !destinationOnlyUpdate) {
|
|
1061
|
+
throw new HTTPException(422, {
|
|
1062
|
+
message:
|
|
1063
|
+
"use the dedicated Opengeni Slack bot reinstall flow to update this connection",
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
const destinationSelection = destination.data;
|
|
1067
|
+
if (
|
|
1068
|
+
destinationSelection.authorityKind === "organization" &&
|
|
1069
|
+
authorization.accountGrant?.permissions.includes("account:admin") !== true
|
|
1070
|
+
) {
|
|
1071
|
+
throw new HTTPException(403, { message: "missing permission: account:admin" });
|
|
1072
|
+
}
|
|
1073
|
+
if (
|
|
1074
|
+
destinationSelection.authorityKind === "workspace" &&
|
|
1075
|
+
!hasPermission(grant.permissions, "workspace:admin")
|
|
1076
|
+
) {
|
|
1077
|
+
throw new HTTPException(403, { message: "missing permission: workspace:admin" });
|
|
1078
|
+
}
|
|
1079
|
+
if (
|
|
1080
|
+
destinationSelection.authorityKind === "personal" &&
|
|
1081
|
+
(!authorization.contextIntegrity ||
|
|
1082
|
+
authorization.authenticatedSubjectId !== grant.subjectId)
|
|
1083
|
+
) {
|
|
1084
|
+
throw new HTTPException(403, {
|
|
1085
|
+
message: "personal destination requires the exact actor",
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
const metadata = {
|
|
1089
|
+
...existing.metadata,
|
|
1090
|
+
documentDestination: bindConnectorDocumentDestination(destinationSelection, {
|
|
1091
|
+
accountId: grant.accountId,
|
|
1092
|
+
workspaceId,
|
|
1093
|
+
initiatingSubjectId: grant.subjectId,
|
|
1094
|
+
}),
|
|
1095
|
+
};
|
|
1096
|
+
if (
|
|
1097
|
+
stableJson(metadata.documentDestination) !==
|
|
1098
|
+
stableJson(existing.metadata.documentDestination)
|
|
1099
|
+
) {
|
|
1100
|
+
assertOrganizationIntegrationAllowed(policy, "slack-bot");
|
|
1101
|
+
}
|
|
1102
|
+
const connection = await updateSlackBotDocumentDestination(db, {
|
|
1103
|
+
accountId: grant.accountId,
|
|
1104
|
+
workspaceId,
|
|
1105
|
+
connectionId: existing.id,
|
|
1106
|
+
visibleToSubjectId: grant.subjectId,
|
|
1107
|
+
expectedVersion: existing.version,
|
|
1108
|
+
metadata,
|
|
1109
|
+
updatedBySubjectId: grant.subjectId,
|
|
1110
|
+
});
|
|
1111
|
+
if (!connection) {
|
|
1112
|
+
throw new HTTPException(409, {
|
|
1113
|
+
message: "Slack bot connection changed; reload before saving its destination",
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
return c.json(ConnectionResponse.parse({ connection }));
|
|
1117
|
+
}
|
|
1118
|
+
if (existing && GoogleDriveConnectionMetadata.safeParse(existing.metadata).success) {
|
|
1119
|
+
throw new HTTPException(422, {
|
|
1120
|
+
message: "use the dedicated Google Drive reconnect or source-selection flow",
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
if (existing?.metadata.credentialRole === API_INTEGRATION_OAUTH_CREDENTIAL_ROLE) {
|
|
1124
|
+
throw new HTTPException(422, {
|
|
1125
|
+
message:
|
|
1126
|
+
"use the dedicated Integration provider OAuth flow to update this connection",
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
if (existing && AtlassianConnectionMetadata.safeParse(existing.metadata).success) {
|
|
1130
|
+
throw new HTTPException(422, {
|
|
1131
|
+
message: "use the dedicated Atlassian reconnect or source-selection flow",
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
if (existing && isPersonalGitHubConnection(existing)) {
|
|
1135
|
+
throw new HTTPException(422, {
|
|
1136
|
+
message: "use the dedicated personal GitHub reconnect flow to update this connection",
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
if (existing) {
|
|
1140
|
+
const providerDomain = canonicalProviderDomain(
|
|
1141
|
+
payload.providerDomain ?? existing.providerDomain,
|
|
1142
|
+
);
|
|
1143
|
+
const kind = payload.kind ?? existing.kind;
|
|
1144
|
+
const subjectId =
|
|
1145
|
+
payload.subjectId === undefined
|
|
1146
|
+
? existing.subjectId
|
|
1147
|
+
: writableSubjectId(payload.subjectId, grant.subjectId);
|
|
1148
|
+
const targetWorkspaceProviderKind = workspaceProviderApiKeyConnectionKind({
|
|
1149
|
+
subjectId,
|
|
1150
|
+
providerDomain,
|
|
1151
|
+
kind,
|
|
1152
|
+
metadata: payload.metadata ?? existing.metadata,
|
|
1153
|
+
});
|
|
1154
|
+
if (!workspaceProviderApiKeyConnectionKind(existing) && targetWorkspaceProviderKind) {
|
|
1155
|
+
throw new HTTPException(422, {
|
|
1156
|
+
message:
|
|
1157
|
+
targetWorkspaceProviderKind === "vercel_gateway"
|
|
1158
|
+
? "use the Vercel AI Gateway connect flow to create this connection"
|
|
1159
|
+
: targetWorkspaceProviderKind === "opper"
|
|
1160
|
+
? "use the Opper connect flow to create this connection"
|
|
1161
|
+
: "use the OpenRouter connect flow to create this connection",
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
assertNotDirectPersonalSlackOAuth(providerDomain, kind);
|
|
1165
|
+
assertNotDirectGoogleDriveOAuth(
|
|
1166
|
+
providerDomain,
|
|
1167
|
+
kind,
|
|
1168
|
+
payload.metadata ?? existing.metadata,
|
|
1169
|
+
);
|
|
1170
|
+
assertNotDirectAtlassianOAuth(
|
|
1171
|
+
providerDomain,
|
|
1172
|
+
kind,
|
|
1173
|
+
payload.metadata ?? existing.metadata,
|
|
1174
|
+
);
|
|
1175
|
+
assertNotDirectPersonalGitHubOAuth(
|
|
1176
|
+
providerDomain,
|
|
1177
|
+
kind,
|
|
1178
|
+
payload.metadata ?? existing.metadata,
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
// Status is not a free-form field: revocation goes through DELETE, and the
|
|
1182
|
+
// broker owns needs_reauth/error. Reactivating a connection is only
|
|
1183
|
+
// meaningful together with a fresh credential bundle — otherwise a PATCH
|
|
1184
|
+
// could clear the broker's re-auth signal while stale tokens stay in place.
|
|
1185
|
+
if (payload.status !== undefined) {
|
|
1186
|
+
if (payload.status !== "active") {
|
|
1187
|
+
throw new HTTPException(400, {
|
|
1188
|
+
message: 'status can only be set to "active"; use DELETE to revoke',
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
if (payload.credential === undefined) {
|
|
1192
|
+
throw new HTTPException(400, {
|
|
1193
|
+
message: "reactivating a connection requires a new credential",
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
const existingWorkspaceProviderKind = existing
|
|
1198
|
+
? workspaceProviderApiKeyConnectionKind(existing)
|
|
978
1199
|
: null;
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1200
|
+
if (existing && existingWorkspaceProviderKind) {
|
|
1201
|
+
const provider = workspaceProviderApiKeyConnectionSpec(existingWorkspaceProviderKind);
|
|
1202
|
+
const providerDomain = canonicalProviderDomain(
|
|
1203
|
+
payload.providerDomain ?? existing.providerDomain,
|
|
1204
|
+
);
|
|
1205
|
+
const subjectId =
|
|
1206
|
+
payload.subjectId === undefined
|
|
1207
|
+
? existing.subjectId
|
|
1208
|
+
: writableSubjectId(payload.subjectId, grant.subjectId);
|
|
1209
|
+
const kind = payload.kind ?? existing.kind;
|
|
1210
|
+
if (
|
|
1211
|
+
subjectId !== null ||
|
|
1212
|
+
providerDomain !== provider.providerDomain ||
|
|
1213
|
+
kind !== "api_key" ||
|
|
1214
|
+
(payload.metadata?.credentialRole !== undefined &&
|
|
1215
|
+
payload.metadata.credentialRole !== provider.credentialRole)
|
|
1216
|
+
) {
|
|
1217
|
+
throw new HTTPException(422, {
|
|
1218
|
+
message: `${provider.label} connection identity cannot be changed`,
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
if (payload.credential === undefined) {
|
|
1222
|
+
throw new HTTPException(400, {
|
|
1223
|
+
message: `updating a ${provider.label} connection requires a new credential`,
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
if (payload.expectedVersion === undefined || payload.operationId === undefined) {
|
|
1227
|
+
throw new HTTPException(400, {
|
|
1228
|
+
message: `updating a ${provider.label} connection requires expectedVersion and operationId`,
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
payload.credential = prepareClaudeWorkspaceCredential(
|
|
1232
|
+
settings,
|
|
1233
|
+
workspaceId,
|
|
1234
|
+
existing.metadata,
|
|
1235
|
+
payload.credential,
|
|
1236
|
+
);
|
|
1237
|
+
assertClaudeWorkspaceCredential(settings, {
|
|
1238
|
+
subjectId,
|
|
1239
|
+
providerDomain,
|
|
1240
|
+
kind,
|
|
1241
|
+
metadata: existing.metadata,
|
|
1242
|
+
credential: payload.credential,
|
|
1243
|
+
});
|
|
1244
|
+
if (existingWorkspaceProviderKind === "opper")
|
|
1245
|
+
assertOpperCredential(payload.credential);
|
|
1246
|
+
const key = requireEnvironmentEncryption(settings);
|
|
1247
|
+
const grantedScopes = payload.grantedScopes ?? existing.grantedScopes;
|
|
1248
|
+
const expiresAt =
|
|
1249
|
+
payload.expiresAt !== undefined
|
|
1250
|
+
? payload.expiresAt
|
|
1251
|
+
? new Date(payload.expiresAt)
|
|
1252
|
+
: null
|
|
1253
|
+
: existing.expiresAt
|
|
1254
|
+
? new Date(existing.expiresAt)
|
|
1255
|
+
: null;
|
|
1256
|
+
const metadata = workspaceProviderCredentialMetadata(existingWorkspaceProviderKind, {
|
|
1257
|
+
...existing.metadata,
|
|
1258
|
+
...(payload.metadata ?? {}),
|
|
1259
|
+
});
|
|
1260
|
+
const input = {
|
|
1261
|
+
accountId: grant.accountId,
|
|
1262
|
+
workspaceId,
|
|
1263
|
+
connectionId: existing.id,
|
|
1264
|
+
expectedVersion: payload.expectedVersion,
|
|
1265
|
+
operationId: payload.operationId,
|
|
1266
|
+
requestDigest: workspaceProviderCredentialRequestDigest(key, {
|
|
1267
|
+
action: "rotate",
|
|
1268
|
+
connectionId: existing.id,
|
|
1269
|
+
expectedVersion: payload.expectedVersion,
|
|
1270
|
+
credential: payload.credential,
|
|
1271
|
+
grantedScopes,
|
|
1272
|
+
expiresAt: expiresAt?.toISOString() ?? null,
|
|
1273
|
+
metadata,
|
|
1274
|
+
}),
|
|
1275
|
+
credentialEncrypted: encryptCredentialBundle(key, payload.credential),
|
|
1276
|
+
grantedScopes,
|
|
1277
|
+
expiresAt,
|
|
1278
|
+
metadata,
|
|
1279
|
+
updatedBySubjectId: grant.subjectId,
|
|
1280
|
+
};
|
|
1281
|
+
const connection = await rotateWorkspaceProviderApiKeyConnection(
|
|
1282
|
+
db,
|
|
1283
|
+
existingWorkspaceProviderKind,
|
|
1284
|
+
input,
|
|
1285
|
+
);
|
|
1286
|
+
if (!connection) {
|
|
1287
|
+
throw new HTTPException(409, {
|
|
1288
|
+
message: `${provider.label} connection changed; reload before replacing its key`,
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
return c.json(ConnectionResponse.parse({ connection }));
|
|
1292
|
+
}
|
|
1293
|
+
const key =
|
|
1294
|
+
payload.credential === undefined ? null : requireEnvironmentEncryption(settings);
|
|
1295
|
+
const subjectId =
|
|
1296
|
+
payload.subjectId === undefined
|
|
1297
|
+
? undefined
|
|
1298
|
+
: writableSubjectId(payload.subjectId, grant.subjectId);
|
|
1299
|
+
if (
|
|
1300
|
+
existing &&
|
|
1301
|
+
(payload.credential !== undefined ||
|
|
1302
|
+
(payload.providerDomain !== undefined &&
|
|
1303
|
+
canonicalProviderDomain(payload.providerDomain) !== existing.providerDomain) ||
|
|
1304
|
+
(payload.kind !== undefined && payload.kind !== existing.kind) ||
|
|
1305
|
+
(subjectId !== undefined && subjectId !== existing.subjectId) ||
|
|
1306
|
+
payload.grantedScopes?.some((scope) => !existing.grantedScopes.includes(scope)) ||
|
|
1307
|
+
(payload.metadata !== undefined &&
|
|
1308
|
+
["mcpUrl", "resource", "credentialRole"].some(
|
|
1309
|
+
(field) =>
|
|
1310
|
+
stableJson(payload.metadata?.[field] ?? null) !==
|
|
1311
|
+
stableJson(existing.metadata[field] ?? null),
|
|
1312
|
+
)) ||
|
|
1313
|
+
(payload.expiresAt !== undefined &&
|
|
1314
|
+
existing.expiresAt !== null &&
|
|
1315
|
+
(payload.expiresAt === null ||
|
|
1316
|
+
Date.parse(payload.expiresAt) > Date.parse(existing.expiresAt))) ||
|
|
1317
|
+
(payload.status === "active" && existing.status !== "active"))
|
|
1318
|
+
) {
|
|
1319
|
+
assertOrganizationIntegrationAllowed(policy, null);
|
|
1320
|
+
}
|
|
1321
|
+
if (payload.credential !== undefined) {
|
|
1322
|
+
assertBrokeredApiKeyCredential(payload.kind ?? existing?.kind, payload.credential);
|
|
1323
|
+
}
|
|
1324
|
+
const connection = await updateConnection(db, {
|
|
1325
|
+
workspaceId,
|
|
1326
|
+
connectionId: c.req.param("connectionId"),
|
|
1327
|
+
visibleToSubjectId: grant.subjectId,
|
|
1328
|
+
updatedBySubjectId: grant.subjectId,
|
|
1329
|
+
...(payload.providerDomain !== undefined
|
|
1330
|
+
? { providerDomain: canonicalProviderDomain(payload.providerDomain) }
|
|
1331
|
+
: {}),
|
|
1332
|
+
...(subjectId !== undefined ? { subjectId } : {}),
|
|
1333
|
+
...(payload.kind !== undefined ? { kind: payload.kind } : {}),
|
|
1334
|
+
...(payload.status !== undefined ? { status: payload.status } : {}),
|
|
1335
|
+
...(payload.credential !== undefined && key
|
|
1336
|
+
? { credentialEncrypted: encryptCredentialBundle(key, payload.credential) }
|
|
1337
|
+
: {}),
|
|
1338
|
+
...(payload.grantedScopes !== undefined
|
|
1339
|
+
? { grantedScopes: payload.grantedScopes }
|
|
1340
|
+
: {}),
|
|
1341
|
+
...(payload.expiresAt !== undefined
|
|
1342
|
+
? { expiresAt: payload.expiresAt ? new Date(payload.expiresAt) : null }
|
|
1343
|
+
: {}),
|
|
1344
|
+
...(payload.metadata !== undefined ? { metadata: payload.metadata } : {}),
|
|
1345
|
+
});
|
|
1346
|
+
if (!connection) {
|
|
1347
|
+
throw new HTTPException(404, { message: "connection not found" });
|
|
1348
|
+
}
|
|
1349
|
+
return c.json(ConnectionResponse.parse({ connection }));
|
|
997
1350
|
}),
|
|
998
|
-
|
|
999
|
-
grantedScopes,
|
|
1000
|
-
expiresAt,
|
|
1001
|
-
metadata,
|
|
1002
|
-
updatedBySubjectId: grant.subjectId,
|
|
1003
|
-
};
|
|
1004
|
-
const connection =
|
|
1005
|
-
existingWorkspaceProviderKind === "vercel_gateway"
|
|
1006
|
-
? await rotateWorkspaceVercelAiGatewayConnection(db, input)
|
|
1007
|
-
: await rotateWorkspaceOpenRouterConnection(db, input);
|
|
1008
|
-
if (!connection) {
|
|
1009
|
-
throw new HTTPException(409, {
|
|
1010
|
-
message: `${provider.label} connection changed; reload before replacing its key`,
|
|
1011
|
-
});
|
|
1012
|
-
}
|
|
1013
|
-
return c.json(ConnectionResponse.parse({ connection }));
|
|
1014
|
-
}
|
|
1015
|
-
const key = payload.credential === undefined ? null : requireEnvironmentEncryption(settings);
|
|
1016
|
-
const subjectId =
|
|
1017
|
-
payload.subjectId === undefined
|
|
1018
|
-
? undefined
|
|
1019
|
-
: writableSubjectId(payload.subjectId, grant.subjectId);
|
|
1020
|
-
const connection = await updateConnection(db, {
|
|
1021
|
-
workspaceId,
|
|
1022
|
-
connectionId: c.req.param("connectionId"),
|
|
1023
|
-
visibleToSubjectId: grant.subjectId,
|
|
1024
|
-
updatedBySubjectId: grant.subjectId,
|
|
1025
|
-
...(payload.providerDomain !== undefined
|
|
1026
|
-
? { providerDomain: canonicalProviderDomain(payload.providerDomain) }
|
|
1027
|
-
: {}),
|
|
1028
|
-
...(subjectId !== undefined ? { subjectId } : {}),
|
|
1029
|
-
...(payload.kind !== undefined ? { kind: payload.kind } : {}),
|
|
1030
|
-
...(payload.status !== undefined ? { status: payload.status } : {}),
|
|
1031
|
-
...(payload.credential !== undefined && key
|
|
1032
|
-
? { credentialEncrypted: encryptCredentialBundle(key, payload.credential) }
|
|
1033
|
-
: {}),
|
|
1034
|
-
...(payload.grantedScopes !== undefined ? { grantedScopes: payload.grantedScopes } : {}),
|
|
1035
|
-
...(payload.expiresAt !== undefined
|
|
1036
|
-
? { expiresAt: payload.expiresAt ? new Date(payload.expiresAt) : null }
|
|
1037
|
-
: {}),
|
|
1038
|
-
...(payload.metadata !== undefined ? { metadata: payload.metadata } : {}),
|
|
1039
|
-
});
|
|
1040
|
-
if (!connection) {
|
|
1041
|
-
throw new HTTPException(404, { message: "connection not found" });
|
|
1042
|
-
}
|
|
1043
|
-
return c.json(ConnectionResponse.parse({ connection }));
|
|
1351
|
+
);
|
|
1044
1352
|
});
|
|
1045
1353
|
|
|
1046
1354
|
app.delete("/v1/workspaces/:workspaceId/connections/:connectionId", async (c) => {
|
|
@@ -1154,21 +1462,13 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
1154
1462
|
slackTeamId: openGeniSlackBotMetadata(existing.metadata)!.slackTeamId,
|
|
1155
1463
|
})
|
|
1156
1464
|
: workspaceProviderKind
|
|
1157
|
-
? workspaceProviderKind
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
})
|
|
1165
|
-
: await revokeWorkspaceOpenRouterConnections(db, {
|
|
1166
|
-
accountId: grant.accountId,
|
|
1167
|
-
workspaceId,
|
|
1168
|
-
connectionId,
|
|
1169
|
-
expectedVersion: existing.version,
|
|
1170
|
-
updatedBySubjectId: grant.subjectId,
|
|
1171
|
-
})
|
|
1465
|
+
? await revokeWorkspaceProviderApiKeyConnections(db, workspaceProviderKind, {
|
|
1466
|
+
accountId: grant.accountId,
|
|
1467
|
+
workspaceId,
|
|
1468
|
+
connectionId,
|
|
1469
|
+
expectedVersion: existing.version,
|
|
1470
|
+
updatedBySubjectId: grant.subjectId,
|
|
1471
|
+
})
|
|
1172
1472
|
: await revokeConnection(
|
|
1173
1473
|
db,
|
|
1174
1474
|
workspaceId,
|
|
@@ -1186,6 +1486,7 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
1186
1486
|
assertIntegrationsEnabled();
|
|
1187
1487
|
const workspaceId = c.req.param("workspaceId");
|
|
1188
1488
|
const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "connections:write");
|
|
1489
|
+
requireProviderConsentInBrowser(access);
|
|
1189
1490
|
const grant = access.grant;
|
|
1190
1491
|
const parsed = OAuthStartRequest.safeParse(await c.req.json());
|
|
1191
1492
|
if (!parsed.success) {
|
|
@@ -1215,6 +1516,7 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
1215
1516
|
assertIntegrationsEnabled();
|
|
1216
1517
|
const workspaceId = c.req.param("workspaceId");
|
|
1217
1518
|
const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "connections:write");
|
|
1519
|
+
requireProviderConsentInBrowser(access);
|
|
1218
1520
|
const grant = access.grant;
|
|
1219
1521
|
const parsed = ApiIntegrationOAuthStartRequest.safeParse(await c.req.json());
|
|
1220
1522
|
if (!parsed.success) {
|
|
@@ -1280,7 +1582,7 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
1280
1582
|
return c.json(
|
|
1281
1583
|
IntegrationClientMetadata.parse({
|
|
1282
1584
|
client_id: metadataUrl,
|
|
1283
|
-
client_name: "
|
|
1585
|
+
client_name: "Opengeni",
|
|
1284
1586
|
redirect_uris: [`${baseUrl}/v1/integrations/oauth/callback`],
|
|
1285
1587
|
token_endpoint_auth_method: "none",
|
|
1286
1588
|
grant_types: ["authorization_code", "refresh_token"],
|
|
@@ -1319,7 +1621,7 @@ async function persistOpenGeniSlackBotConnection(input: {
|
|
|
1319
1621
|
throw new SlackInstallCallbackError(
|
|
1320
1622
|
422,
|
|
1321
1623
|
"connection_conflict",
|
|
1322
|
-
"connectionId is not an
|
|
1624
|
+
"connectionId is not an Opengeni Slack bot connection",
|
|
1323
1625
|
"principal_validation",
|
|
1324
1626
|
);
|
|
1325
1627
|
}
|
|
@@ -1598,28 +1900,6 @@ function assertNotReservedPersonalGitHubMetadata(
|
|
|
1598
1900
|
}
|
|
1599
1901
|
}
|
|
1600
1902
|
|
|
1601
|
-
type WorkspaceProviderApiKeyConnectionKind = "vercel_gateway" | "openrouter";
|
|
1602
|
-
|
|
1603
|
-
function workspaceProviderApiKeyConnectionSpec(
|
|
1604
|
-
providerKind: WorkspaceProviderApiKeyConnectionKind,
|
|
1605
|
-
): {
|
|
1606
|
-
providerDomain: string;
|
|
1607
|
-
credentialRole: string;
|
|
1608
|
-
label: string;
|
|
1609
|
-
} {
|
|
1610
|
-
return providerKind === "vercel_gateway"
|
|
1611
|
-
? {
|
|
1612
|
-
providerDomain: VERCEL_AI_GATEWAY_CONNECTION_DOMAIN,
|
|
1613
|
-
credentialRole: VERCEL_AI_GATEWAY_CONNECTION_ROLE,
|
|
1614
|
-
label: "Vercel AI Gateway",
|
|
1615
|
-
}
|
|
1616
|
-
: {
|
|
1617
|
-
providerDomain: WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
|
|
1618
|
-
credentialRole: WORKSPACE_OPENROUTER_CONNECTION_ROLE,
|
|
1619
|
-
label: "OpenRouter",
|
|
1620
|
-
};
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
1903
|
function workspaceProviderApiKeyConnectionKind(input: {
|
|
1624
1904
|
subjectId?: string | null;
|
|
1625
1905
|
providerDomain: string;
|
|
@@ -1628,6 +1908,12 @@ function workspaceProviderApiKeyConnectionKind(input: {
|
|
|
1628
1908
|
}): WorkspaceProviderApiKeyConnectionKind | null {
|
|
1629
1909
|
if (input.subjectId != null || input.kind !== "api_key") return null;
|
|
1630
1910
|
const providerDomain = input.providerDomain.toLowerCase();
|
|
1911
|
+
if (
|
|
1912
|
+
providerDomain === "api.anthropic.com" &&
|
|
1913
|
+
(input.metadata?.credentialRole === "anthropic" ||
|
|
1914
|
+
input.metadata?.credentialRole === "claude_subscription")
|
|
1915
|
+
)
|
|
1916
|
+
return input.metadata.credentialRole;
|
|
1631
1917
|
if (
|
|
1632
1918
|
providerDomain === VERCEL_AI_GATEWAY_CONNECTION_DOMAIN &&
|
|
1633
1919
|
input.metadata?.credentialRole === VERCEL_AI_GATEWAY_CONNECTION_ROLE
|
|
@@ -1640,13 +1926,40 @@ function workspaceProviderApiKeyConnectionKind(input: {
|
|
|
1640
1926
|
) {
|
|
1641
1927
|
return "openrouter";
|
|
1642
1928
|
}
|
|
1929
|
+
if (
|
|
1930
|
+
providerDomain === WORKSPACE_OPPER_CONNECTION_DOMAIN &&
|
|
1931
|
+
input.metadata?.credentialRole === WORKSPACE_OPPER_CONNECTION_ROLE
|
|
1932
|
+
) {
|
|
1933
|
+
return "opper";
|
|
1934
|
+
}
|
|
1643
1935
|
return null;
|
|
1644
1936
|
}
|
|
1645
1937
|
|
|
1938
|
+
/** Opper management keys are refused by inference routes; never store one as a model key. */
|
|
1939
|
+
function assertOpperCredential(credential: Record<string, unknown>): void {
|
|
1940
|
+
const apiKey = typeof credential.apiKey === "string" ? credential.apiKey : "";
|
|
1941
|
+
const problem = opperCredentialProblem(apiKey);
|
|
1942
|
+
if (problem) throw new HTTPException(422, { message: problem });
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
/**
|
|
1946
|
+
* A brokered api_key Connection is usable only through `headers` or
|
|
1947
|
+
* `placements`; anything else was previously accepted and then failed every
|
|
1948
|
+
* tool call with a misleading "connect an account" auth-needed notice.
|
|
1949
|
+
*/
|
|
1950
|
+
function assertBrokeredApiKeyCredential(
|
|
1951
|
+
kind: string | undefined,
|
|
1952
|
+
credential: Record<string, unknown>,
|
|
1953
|
+
): void {
|
|
1954
|
+
if (kind !== "api_key") return;
|
|
1955
|
+
const problem = brokeredCredentialBundleProblem(credential);
|
|
1956
|
+
if (problem) throw new HTTPException(422, { message: problem });
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1646
1959
|
function assertNotReservedSlackBotMetadata(metadata: Record<string, unknown> | undefined): void {
|
|
1647
1960
|
if (hasReservedOpenGeniSlackBotMetadata(metadata)) {
|
|
1648
1961
|
throw new HTTPException(422, {
|
|
1649
|
-
message: "
|
|
1962
|
+
message: "Opengeni Slack bot metadata is reserved for the dedicated connection flow",
|
|
1650
1963
|
});
|
|
1651
1964
|
}
|
|
1652
1965
|
}
|
|
@@ -1659,6 +1972,14 @@ function assertNotReservedFikenMetadata(metadata: Record<string, unknown> | unde
|
|
|
1659
1972
|
}
|
|
1660
1973
|
}
|
|
1661
1974
|
|
|
1975
|
+
function assertNotReservedOnePasswordMetadata(metadata: Record<string, unknown> | undefined): void {
|
|
1976
|
+
if (metadata?.credentialRole === ONEPASSWORD_CREDENTIAL_ROLE) {
|
|
1977
|
+
throw new HTTPException(422, {
|
|
1978
|
+
message: "1Password connections are created by the verified 1Password install flow",
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1662
1983
|
function assertNotReservedApiIntegrationOAuthMetadata(
|
|
1663
1984
|
metadata: Record<string, unknown> | undefined,
|
|
1664
1985
|
): void {
|
|
@@ -1711,8 +2032,14 @@ function workspaceProviderCredentialMetadata(
|
|
|
1711
2032
|
[OPENROUTER_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY]: _openRouterOperationDigest,
|
|
1712
2033
|
[VERCEL_AI_GATEWAY_CREDENTIAL_OPERATION_ID_METADATA_KEY]: _operationId,
|
|
1713
2034
|
[VERCEL_AI_GATEWAY_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY]: _operationDigest,
|
|
2035
|
+
[OPPER_CREDENTIAL_OPERATION_ID_METADATA_KEY]: _opperOperationId,
|
|
2036
|
+
[OPPER_CREDENTIAL_OPERATION_DIGEST_METADATA_KEY]: _opperOperationDigest,
|
|
1714
2037
|
...effectiveMetadata
|
|
1715
2038
|
} = metadata ?? {};
|
|
2039
|
+
for (const role of ["anthropic", "claude_subscription"]) {
|
|
2040
|
+
delete effectiveMetadata[`${role}CredentialOperationId`];
|
|
2041
|
+
delete effectiveMetadata[`${role}CredentialOperationDigest`];
|
|
2042
|
+
}
|
|
1716
2043
|
return {
|
|
1717
2044
|
...effectiveMetadata,
|
|
1718
2045
|
credentialRole: workspaceProviderApiKeyConnectionSpec(providerKind).credentialRole,
|