@opengeni/api-router 0.30.3 → 1.0.0
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 +3 -0
- package/dist/admission-trace.d.ts +8 -0
- package/dist/api-websocket.d.ts +18 -0
- package/dist/app.d.ts +50 -2
- package/dist/app.js +12 -1
- package/dist/artifact-catalog-cursor.d.ts +27 -0
- package/dist/auth/managed-auth-attempt-context.d.ts +10 -0
- package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
- package/dist/auth/managed-auth.d.ts +102 -3
- package/dist/auth/managed-email.d.ts +29 -0
- package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
- package/dist/auth/new-signups-gate.d.ts +23 -0
- package/dist/auth/organization-user-setup.d.ts +80 -0
- package/dist/auth/signup-funnel-metrics.d.ts +44 -0
- package/dist/browser-controller-authority.d.ts +1 -61
- package/dist/browser-ephemeral.d.ts +5 -0
- package/dist/browser-state-authority.d.ts +1 -27
- package/dist/chunk-6TZ5LG3O.js +123948 -0
- package/dist/chunk-6TZ5LG3O.js.map +1 -0
- package/dist/chunk-OGQDXVXY.js +18 -0
- package/dist/chunk-OGQDXVXY.js.map +1 -0
- package/dist/claude-subscription-account-conflicts.d.ts +1 -0
- package/dist/claude-subscription-account-usage.d.ts +39 -0
- package/dist/claude-subscription-oauth.d.ts +25 -0
- package/dist/claude-subscription-profile.d.ts +9 -0
- package/dist/claude-subscription-usage.d.ts +44 -0
- package/dist/claude-workspace-connection.d.ts +15 -0
- package/dist/codemode.d.ts +32 -1
- package/dist/company-brain-okf.d.ts +24 -0
- package/dist/connected-machine-computer-access.d.ts +11 -0
- package/dist/connection-ownership.d.ts +54 -0
- package/dist/controller-data-plane.d.ts +32 -0
- package/dist/editable-artifact-source-session.d.ts +3 -0
- package/dist/editable-artifact-websocket.d.ts +10 -18
- package/dist/editable-artifact-workspace-files.d.ts +1 -1
- package/dist/fatal-process-boundary.d.ts +25 -0
- package/dist/github-access.d.ts +31 -2
- package/dist/http/acting-person.d.ts +24 -0
- package/dist/http/api-contract-compat.d.ts +19 -0
- package/dist/http/api-error.d.ts +27 -1
- package/dist/http/cors.d.ts +17 -0
- package/dist/http/deprecation.d.ts +42 -0
- package/dist/http/interaction-control-error.d.ts +13 -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/request-body.d.ts +27 -0
- package/dist/http/request-source.d.ts +52 -0
- package/dist/http/sse.d.ts +17 -6
- package/dist/http/unmatched-route.d.ts +18 -0
- package/dist/http/user-content.d.ts +36 -0
- package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +1091 -162
- package/dist/index.js.map +1 -1
- package/dist/integration-endpoint-test.d.ts +20 -0
- package/dist/integrations/api-integrations.d.ts +2 -2
- package/dist/integrations/atlassian.d.ts +29 -3
- package/dist/integrations/connect-authority.d.ts +1 -0
- package/dist/integrations/connect-callback-return.d.ts +6 -0
- package/dist/integrations/fiken.d.ts +16 -2
- package/dist/integrations/github-app-connect.d.ts +47 -0
- package/dist/integrations/github-installation-proof.d.ts +3 -0
- package/dist/integrations/github-lens-connect.d.ts +116 -0
- package/dist/integrations/github-skill-source.d.ts +1 -5
- package/dist/integrations/google-drive.d.ts +24 -0
- package/dist/integrations/integration-commit-authority.d.ts +13 -0
- package/dist/integrations/marketplace-plugin.d.ts +2691 -0
- package/dist/integrations/oauth-client.d.ts +178 -18
- package/dist/integrations/oauth-profiles.d.ts +222 -0
- package/dist/integrations/oauth-return-path.d.ts +1 -0
- package/dist/integrations/personal-github-repositories.d.ts +84 -0
- package/dist/integrations/personal-github.d.ts +42 -0
- package/dist/integrations/pr-review-provider.d.ts +22 -0
- package/dist/integrations/provider-oauth.d.ts +19 -1
- package/dist/integrations/slack-app-home.d.ts +24 -0
- package/dist/integrations/slack-bot.d.ts +367 -12
- package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
- package/dist/integrations/slack-install.d.ts +20 -0
- package/dist/integrations/slack-interactions.d.ts +107 -9
- package/dist/integrations/slack-mrkdwn.d.ts +41 -0
- package/dist/integrations/slack-routing.d.ts +120 -0
- package/dist/integrations/slack-session-defaults.d.ts +58 -0
- package/dist/integrations/slack-task-file-upload.d.ts +36 -0
- package/dist/integrations/social-oauth.d.ts +7 -0
- package/dist/interaction-agent-access.d.ts +15 -0
- package/dist/interaction-frame-proxy.d.ts +68 -0
- package/dist/interaction-metrics.d.ts +2 -0
- package/dist/mcp/action-catalog-types.d.ts +9 -0
- package/dist/mcp/action-catalog.gen.d.ts +2 -0
- package/dist/mcp/capability-account-readiness.d.ts +7 -0
- package/dist/mcp/child-read-evidence.d.ts +9 -0
- package/dist/mcp/command-read.d.ts +24 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
- package/dist/mcp/contract-input.d.ts +11 -0
- package/dist/mcp/documents.d.ts +7 -5
- package/dist/mcp/files.d.ts +2 -2
- package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
- package/dist/mcp/knowledge-entries.d.ts +5 -0
- package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
- package/dist/mcp/receipts.d.ts +9 -0
- package/dist/mcp/request-abort.d.ts +19 -0
- package/dist/mcp/scheduled-task-input.d.ts +1189 -0
- package/dist/mcp/scheduled-task-view.d.ts +22 -4
- package/dist/mcp/server.d.ts +255 -3
- package/dist/mcp/session-event-view.d.ts +94 -0
- package/dist/mcp/session-view.d.ts +66 -2
- package/dist/mcp/session-wait.d.ts +174 -0
- package/dist/mcp-oauth.d.ts +52 -0
- package/dist/model-catalog.d.ts +11 -31
- package/dist/observability.d.ts +3 -1
- package/dist/organization-mcp.d.ts +52 -0
- package/dist/organization-recovery-notifications.d.ts +41 -0
- package/dist/routes/analytics-consent.d.ts +26 -0
- package/dist/routes/api-integrations.d.ts +25 -0
- package/dist/routes/api-keys.d.ts +17 -0
- package/dist/routes/artifact-catalog.d.ts +6 -0
- package/dist/routes/automations.d.ts +19 -0
- package/dist/routes/billing.d.ts +49 -0
- package/dist/routes/browser-sessions.d.ts +59 -2
- 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 +146 -0
- package/dist/routes/codex.d.ts +76 -2
- package/dist/routes/company-brain.d.ts +3 -0
- package/dist/routes/company-profile.d.ts +2 -1
- package/dist/routes/computer-sessions.d.ts +14 -0
- package/dist/routes/connect.d.ts +5 -0
- package/dist/routes/editable-artifacts.d.ts +2 -0
- package/dist/routes/external-identity-links.d.ts +6 -0
- package/dist/routes/feedback.d.ts +3 -0
- package/dist/routes/github-browser-pages.d.ts +12 -0
- package/dist/routes/github.d.ts +2 -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 +30 -1
- package/dist/routes/knowledge.d.ts +3 -0
- package/dist/routes/managed-auth-session-sets.d.ts +26 -0
- package/dist/routes/managed-onboarding.d.ts +29 -0
- package/dist/routes/managed-sign-in-methods.d.ts +6 -0
- package/dist/routes/model-connection-access.d.ts +3 -0
- package/dist/routes/organization-integration-policy.d.ts +3 -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-providers.d.ts +3 -0
- package/dist/routes/organization-recovery.d.ts +19 -0
- package/dist/routes/organization-service-accounts.d.ts +3 -0
- package/dist/routes/organization-sessions.d.ts +26 -0
- package/dist/routes/personal-github-git-broker.d.ts +29 -0
- package/dist/routes/personal-github.d.ts +3 -0
- package/dist/routes/pr-review-github.d.ts +3 -0
- package/dist/routes/pr-review.d.ts +3 -0
- package/dist/routes/session-artifact-associations.d.ts +4 -0
- package/dist/routes/session-history-imports.d.ts +9 -0
- package/dist/routes/sessions.d.ts +44 -6
- package/dist/routes/skill-content.d.ts +4 -0
- package/dist/routes/skill-install-authority.d.ts +6 -0
- package/dist/routes/subscription-account-pools.d.ts +34 -0
- package/dist/routes/subscription-pool-access.d.ts +16 -0
- package/dist/routes/supergrok.d.ts +6 -1
- package/dist/routes/usage-allowances.d.ts +12 -0
- package/dist/routes/user-resource-authorities.d.ts +3 -0
- package/dist/routes/workspace-artifacts.d.ts +2 -1
- package/dist/routes/workspace-capture.d.ts +10 -2
- package/dist/routes/workspace-integrations.d.ts +35 -0
- package/dist/routes/workspace-learning.d.ts +3 -0
- package/dist/routes/workspace-model-providers.d.ts +4 -0
- package/dist/routes/workspaces.d.ts +29 -0
- package/dist/sandbox/auth-callout.d.ts +8 -0
- package/dist/sandbox/channel-a.d.ts +19 -1
- package/dist/sandbox/connection-authority.d.ts +3 -0
- package/dist/sandbox/enrollment.d.ts +9 -1
- package/dist/sandbox/machines.d.ts +5 -2
- package/dist/sandbox/metrics-ingestion.d.ts +28 -20
- package/dist/sandbox/rematerialize.d.ts +2 -0
- package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
- package/dist/sandbox/viewer.d.ts +26 -1
- package/dist/sandbox-file-artifacts.d.ts +50 -0
- package/dist/scheduled-task-deletion.d.ts +13 -0
- package/dist/session-create-observability.d.ts +5 -0
- package/dist/session-event-view-MPS2OZD3.js +292 -0
- package/dist/session-event-view-MPS2OZD3.js.map +1 -0
- package/dist/site-session-origin.d.ts +13 -0
- package/dist/site-uploads.d.ts +41 -0
- package/dist/slack-reaction-files.d.ts +1 -1
- package/dist/temporal-schedule-cleanup.d.ts +1 -0
- package/dist/temporal-schedule-sync.d.ts +26 -0
- package/dist/transcription/providers/xai-subscription.d.ts +1 -0
- package/dist/transcription/service.d.ts +2 -1
- package/dist/work-discovery-observability.d.ts +33 -0
- package/dist/workspace-artifact-content.d.ts +28 -0
- package/dist/workspace-artifact-provenance.d.ts +7 -0
- package/dist/workspace-delete-observability.d.ts +10 -0
- package/dist/workspace-deletion.d.ts +6 -0
- package/dist/workspace-state-projection.d.ts +3 -5
- package/dist/workspace-tool-gateway-observability.d.ts +17 -0
- package/dist/workspace-tool-gateway.d.ts +123 -0
- package/dist/workspace-webhook-dispatch.d.ts +26 -0
- package/package.json +23 -19
- package/src/access-grant-rls.ts +75 -0
- package/src/admission-trace.ts +14 -0
- package/src/api-websocket.ts +23 -0
- package/src/app.ts +1967 -252
- package/src/artifact-catalog-cursor.ts +60 -0
- package/src/auth/managed-auth-attempt-context.ts +70 -0
- package/src/auth/managed-auth-rate-limits.ts +252 -0
- package/src/auth/managed-auth-session-adapter.ts +222 -0
- package/src/auth/managed-auth.ts +536 -60
- package/src/auth/managed-email.ts +174 -0
- 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 +307 -0
- package/src/auth/signup-funnel-metrics.ts +164 -0
- package/src/browser-controller-authority.ts +1 -186
- package/src/browser-ephemeral.ts +39 -0
- package/src/browser-state-authority.ts +1 -236
- package/src/claude-subscription-account-conflicts.ts +12 -0
- package/src/claude-subscription-account-usage.ts +48 -0
- package/src/claude-subscription-oauth.ts +362 -0
- package/src/claude-subscription-profile.ts +73 -0
- package/src/claude-subscription-usage.ts +97 -0
- package/src/claude-workspace-connection.ts +82 -0
- package/src/codemode.ts +194 -30
- package/src/codex-realtime.ts +8 -2
- package/src/company-brain-okf.ts +340 -0
- package/src/connected-machine-computer-access.ts +33 -0
- package/src/connection-ownership.ts +199 -0
- package/src/controller-data-plane.ts +81 -0
- package/src/editable-artifact-native-kernel.ts +26 -15
- package/src/editable-artifact-office-import.ts +28 -3
- package/src/editable-artifact-production.ts +23 -7
- package/src/editable-artifact-source-session.ts +123 -0
- package/src/editable-artifact-websocket.ts +27 -21
- package/src/editable-artifact-workspace-files.ts +31 -6
- package/src/fatal-process-boundary.ts +246 -0
- package/src/gateway-realtime.ts +1 -1
- package/src/github-access.ts +147 -5
- package/src/http/acting-person.ts +71 -0
- package/src/http/api-contract-compat.ts +49 -0
- package/src/http/api-error.ts +115 -1
- package/src/http/auth.ts +48 -0
- package/src/http/cors.ts +67 -0
- package/src/http/deprecation.ts +118 -0
- package/src/http/interaction-control-error.ts +164 -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/request-body.ts +127 -0
- package/src/http/request-source.ts +178 -0
- package/src/http/sse.ts +486 -132
- package/src/http/unmatched-route.ts +60 -0
- package/src/http/user-content.ts +158 -0
- package/src/http/websocket-upgrade-dispatch.ts +29 -0
- package/src/index.ts +157 -52
- package/src/integration-endpoint-test.ts +291 -0
- package/src/integrations/api-integrations.ts +98 -11
- package/src/integrations/atlassian.ts +474 -136
- package/src/integrations/connect-authority.ts +2 -0
- package/src/integrations/connect-callback-return.ts +86 -0
- package/src/integrations/fiken.ts +354 -64
- package/src/integrations/github-app-connect.ts +435 -0
- package/src/integrations/github-installation-proof.ts +60 -0
- package/src/integrations/github-lens-connect.ts +76 -0
- package/src/integrations/github-skill-source.ts +2 -142
- package/src/integrations/google-drive.ts +717 -225
- package/src/integrations/integration-commit-authority.ts +69 -0
- package/src/integrations/marketplace-plugin.ts +48 -0
- package/src/integrations/oauth-client.ts +1356 -459
- package/src/integrations/oauth-profiles.ts +537 -0
- package/src/integrations/oauth-return-path.ts +18 -0
- package/src/integrations/personal-github-repositories.ts +674 -0
- package/src/integrations/personal-github.ts +825 -0
- package/src/integrations/pr-review-provider.ts +246 -0
- package/src/integrations/provider-oauth.ts +250 -22
- package/src/integrations/slack-app-home.ts +300 -0
- package/src/integrations/slack-bot.ts +963 -239
- package/src/integrations/slack-file-upload-flow.ts +147 -0
- package/src/integrations/slack-install.ts +83 -0
- package/src/integrations/slack-interactions.ts +3167 -474
- package/src/integrations/slack-mrkdwn.ts +193 -0
- package/src/integrations/slack-routing.ts +337 -0
- package/src/integrations/slack-session-defaults.ts +224 -0
- package/src/integrations/slack-task-file-upload.ts +323 -0
- package/src/integrations/social-oauth.ts +213 -15
- package/src/interaction-agent-access.ts +48 -0
- package/src/interaction-frame-proxy.ts +409 -0
- package/src/interaction-metrics.ts +30 -0
- package/src/mcp/action-catalog-types.ts +9 -0
- package/src/mcp/action-catalog.gen.ts +6696 -0
- package/src/mcp/capability-account-readiness.ts +17 -0
- package/src/mcp/child-read-evidence.ts +19 -0
- package/src/mcp/command-read.ts +109 -0
- package/src/mcp/company-profile-agent-admin.ts +198 -0
- package/src/mcp/contract-input.ts +141 -0
- package/src/mcp/documents.ts +65 -377
- package/src/mcp/editable-artifacts.ts +12 -6
- package/src/mcp/files.ts +80 -43
- package/src/mcp/first-party-tool-permissions.ts +305 -0
- package/src/mcp/knowledge-entries.ts +322 -0
- package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
- package/src/mcp/receipts.ts +34 -0
- package/src/mcp/request-abort.ts +44 -0
- package/src/mcp/scheduled-task-input.ts +76 -0
- package/src/mcp/scheduled-task-view.ts +44 -3
- package/src/mcp/server.ts +3030 -1267
- package/src/mcp/session-event-view.ts +383 -0
- package/src/mcp/session-view.ts +84 -3
- package/src/mcp/session-wait.ts +665 -0
- package/src/mcp-oauth.ts +1123 -0
- package/src/model-catalog.ts +91 -333
- package/src/observability.ts +15 -1
- package/src/organization-mcp.ts +397 -0
- package/src/organization-recovery-notifications.ts +103 -0
- package/src/routes/analytics-consent.ts +123 -0
- package/src/routes/api-integrations.ts +364 -74
- package/src/routes/api-keys.ts +336 -10
- package/src/routes/artifact-catalog.ts +279 -0
- package/src/routes/automations.ts +768 -0
- package/src/routes/billing.ts +461 -46
- package/src/routes/browser-sessions.ts +780 -112
- package/src/routes/canonical-human-identities.ts +90 -14
- package/src/routes/capabilities.ts +107 -6
- package/src/routes/channels.ts +20 -3
- package/src/routes/claude-subscription-accounts.ts +334 -0
- package/src/routes/claude-subscription-oauth.ts +142 -0
- package/src/routes/client-errors.ts +362 -0
- package/src/routes/codex.ts +833 -116
- package/src/routes/company-brain.ts +322 -0
- package/src/routes/company-profile.ts +65 -1
- package/src/routes/computer-sessions.ts +557 -106
- package/src/routes/connect.ts +1938 -0
- package/src/routes/connections.ts +1039 -349
- package/src/routes/documents.ts +488 -287
- package/src/routes/editable-artifacts.ts +79 -9
- package/src/routes/enrollments.ts +135 -39
- package/src/routes/environments.ts +154 -58
- package/src/routes/external-identity-links.ts +201 -0
- package/src/routes/feedback.ts +113 -0
- package/src/routes/files.ts +267 -33
- package/src/routes/github-browser-pages.ts +162 -0
- package/src/routes/github.ts +446 -144
- package/src/routes/insights-response-cache.ts +83 -0
- package/src/routes/insights-usage.ts +345 -0
- package/src/routes/insights.ts +153 -19
- package/src/routes/install.ts +133 -38
- package/src/routes/integration-facets.ts +107 -33
- package/src/routes/interaction-resources.ts +30 -3
- package/src/routes/knowledge.ts +488 -0
- package/src/routes/machines.ts +313 -15
- package/src/routes/managed-auth-session-sets.ts +1218 -0
- package/src/routes/managed-onboarding.ts +337 -0
- package/src/routes/managed-sign-in-methods.ts +433 -0
- package/src/routes/memory-slack-publications.ts +3 -2
- package/src/routes/model-connection-access.ts +208 -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 +178 -0
- package/src/routes/organization-memberships.ts +1019 -27
- package/src/routes/organization-model-providers.ts +293 -0
- package/src/routes/organization-recovery.ts +325 -0
- package/src/routes/organization-service-accounts.ts +128 -0
- package/src/routes/organization-sessions.ts +350 -0
- package/src/routes/personal-github-git-broker.ts +785 -0
- package/src/routes/personal-github.ts +370 -0
- package/src/routes/plugins.ts +251 -22
- package/src/routes/pr-review-github.ts +881 -0
- package/src/routes/pr-review.ts +622 -0
- package/src/routes/preference-registry.ts +12 -93
- package/src/routes/rigs.ts +167 -50
- package/src/routes/scheduled-tasks.ts +346 -41
- package/src/routes/session-artifact-associations.ts +87 -0
- package/src/routes/session-history-imports.ts +280 -0
- package/src/routes/sessions.ts +2306 -264
- package/src/routes/skill-content.ts +297 -0
- package/src/routes/skill-install-authority.ts +64 -0
- package/src/routes/skills.ts +62 -9
- package/src/routes/social.ts +46 -18
- package/src/routes/subscription-account-pools.ts +436 -0
- package/src/routes/subscription-pool-access.ts +126 -0
- package/src/routes/supergrok.ts +219 -303
- package/src/routes/transcription-recordings.ts +38 -3
- package/src/routes/transcriptions.ts +4 -0
- package/src/routes/usage-allowances.ts +378 -0
- package/src/routes/user-resource-authorities.ts +138 -0
- package/src/routes/video-generation.ts +14 -10
- package/src/routes/workspace-artifacts.ts +275 -69
- package/src/routes/workspace-capture.ts +21 -0
- package/src/routes/workspace-integrations.ts +576 -0
- package/src/routes/workspace-learning.ts +159 -0
- package/src/routes/workspace-model-providers.ts +148 -0
- package/src/routes/workspace-state.ts +38 -25
- package/src/routes/workspaces.ts +1004 -127
- package/src/sandbox/auth-callout.ts +96 -19
- package/src/sandbox/channel-a.ts +323 -24
- package/src/sandbox/connection-authority.ts +3 -0
- package/src/sandbox/enrollment.ts +81 -6
- package/src/sandbox/machines.ts +218 -76
- package/src/sandbox/metrics-ingestion.ts +411 -62
- package/src/sandbox/rematerialize.ts +121 -19
- package/src/sandbox/session-attach-variable-sets.ts +131 -0
- package/src/sandbox/viewer.ts +280 -25
- package/src/sandbox-file-artifacts.ts +413 -0
- package/src/scheduled-task-deletion.ts +138 -0
- package/src/session-create-observability.ts +46 -0
- package/src/session-realtime-context.ts +6 -3
- package/src/site-session-origin.ts +48 -0
- package/src/site-uploads.ts +224 -0
- package/src/slack-reaction-files.ts +16 -5
- package/src/temporal-schedule-cleanup.ts +13 -0
- package/src/temporal-schedule-sync.ts +74 -0
- package/src/transcription/providers/codex-subscription.ts +2 -0
- package/src/transcription/providers/openai.ts +1 -0
- package/src/transcription/providers/xai-subscription.ts +49 -3
- package/src/transcription/segmenter.ts +8 -2
- package/src/transcription/service.ts +43 -6
- package/src/work-discovery-observability.ts +121 -0
- package/src/workspace-artifact-content.ts +144 -0
- package/src/workspace-artifact-provenance.ts +104 -0
- package/src/workspace-delete-observability.ts +75 -0
- package/src/workspace-deletion.ts +73 -0
- package/src/workspace-state-projection.ts +18 -148
- package/src/workspace-tool-gateway-observability.ts +100 -0
- package/src/workspace-tool-gateway.ts +870 -0
- package/src/workspace-webhook-dispatch.ts +190 -0
- package/src/xai-subscription-auth.ts +9 -2
- package/dist/chunk-WCXHI3FF.js +0 -49920
- package/dist/chunk-WCXHI3FF.js.map +0 -1
- package/dist/routes/packs.d.ts +0 -3
- package/src/routes/packs.ts +0 -729
|
@@ -1,4 +1,26 @@
|
|
|
1
|
+
type AtlassianRequestAuthority = {
|
|
2
|
+
connectionUseContext?: Omit<AcceptedConnectionUseContext, "physicalRequestId" | "usePhase">;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
import type { AcceptedConnectionUseContext } from "@opengeni/db";
|
|
6
|
+
import {
|
|
7
|
+
withOrganizationIntegrationAcquisition,
|
|
8
|
+
withOrganizationIntegrationPolicyFence,
|
|
9
|
+
} from "@opengeni/db/organization-integration-policy";
|
|
10
|
+
import {
|
|
11
|
+
claimOAuthAcquisition,
|
|
12
|
+
integrationSourceSelectionRequiresAcquisition,
|
|
13
|
+
} from "./oauth-client";
|
|
14
|
+
import {
|
|
15
|
+
scheduledTaskKnowledgeSource,
|
|
16
|
+
requireScheduledTaskKnowledgeSource,
|
|
17
|
+
knowledgeSourceAgentConfig,
|
|
18
|
+
assertOrganizationIntegrationAllowed,
|
|
19
|
+
} from "@opengeni/contracts";
|
|
1
20
|
import type { Settings } from "@opengeni/config";
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
|
|
23
|
+
import { requireConnectOwnerAuthority } from "./connect-authority";
|
|
2
24
|
import type { AccessGrant, ScheduledTask, ScheduledTaskScheduleSpec } from "@opengeni/contracts";
|
|
3
25
|
import {
|
|
4
26
|
ATLASSIAN_CREDENTIAL_LABEL,
|
|
@@ -24,7 +46,6 @@ import {
|
|
|
24
46
|
import {
|
|
25
47
|
captureScheduledTaskRestoreState,
|
|
26
48
|
createValidatedScheduledTask,
|
|
27
|
-
hasPermission,
|
|
28
49
|
manualScheduledTaskTriggerWorkflowId,
|
|
29
50
|
requireEnvironmentEncryption,
|
|
30
51
|
syncCreatedScheduledTask,
|
|
@@ -37,6 +58,10 @@ import {
|
|
|
37
58
|
ConnectionDisconnectGenerationError,
|
|
38
59
|
ConnectionDisconnectIdempotencyError,
|
|
39
60
|
consumeIntegrationOAuthStateNonce,
|
|
61
|
+
finishConnectOperation,
|
|
62
|
+
getConnectAttempt,
|
|
63
|
+
decryptEnvironmentValue,
|
|
64
|
+
type Database,
|
|
40
65
|
createConnection,
|
|
41
66
|
deauthorizeKnowledgeSourceRetrieval,
|
|
42
67
|
disconnectConnectionIdempotently,
|
|
@@ -44,7 +69,6 @@ import {
|
|
|
44
69
|
getConnectionMetadata,
|
|
45
70
|
getKnowledgeSourceByExternalIdentityForSyncAuthority,
|
|
46
71
|
getKnowledgeSourceForSyncAuthority,
|
|
47
|
-
getWorkspaceGrant,
|
|
48
72
|
listKnowledgeSourceSyncTasksForConnection,
|
|
49
73
|
loadConnectionCredentialForBroker,
|
|
50
74
|
recordKnowledgeLifecycleEvent,
|
|
@@ -53,17 +77,36 @@ import {
|
|
|
53
77
|
updateScheduledTask,
|
|
54
78
|
upsertKnowledgeProvider,
|
|
55
79
|
upsertKnowledgeSource,
|
|
80
|
+
withWorkspaceSubjectRls,
|
|
56
81
|
} from "@opengeni/db";
|
|
57
82
|
import { atlassianKnowledgeSourceIdentity } from "@opengeni/documents/atlassian";
|
|
58
83
|
import { createSignedState, readSignedState } from "@opengeni/github";
|
|
59
84
|
import { readResponseJsonBounded, type FetchLike } from "@opengeni/network";
|
|
60
85
|
import { HTTPException } from "hono/http-exception";
|
|
86
|
+
import {
|
|
87
|
+
ATLASSIAN_NATIVE_RETIRED_MESSAGE,
|
|
88
|
+
ATLASSIAN_NATIVE_RETIRED_REASON,
|
|
89
|
+
} from "@opengeni/contracts/atlassian-native-retirement";
|
|
90
|
+
import {
|
|
91
|
+
personalOnlyConnectionPrincipalMessage,
|
|
92
|
+
personalOwnerStateAccepted,
|
|
93
|
+
personalOwnerVerifiedInState,
|
|
94
|
+
PERSONAL_OWNER_VERIFIED_STATE_CLAIM,
|
|
95
|
+
} from "../connection-ownership";
|
|
61
96
|
import {
|
|
62
97
|
integrationBaseUrl,
|
|
98
|
+
oauthStateFailureReturn,
|
|
63
99
|
oauthStateTtlMs,
|
|
64
100
|
requireIntegrationsStateSecret,
|
|
65
101
|
} from "./oauth-client";
|
|
66
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Flow discriminator for this connector's signed OAuth state. See the matching
|
|
105
|
+
* constant in `google-drive.ts`: this state carries no `ownership` field and no
|
|
106
|
+
* provider identity, and its return path is byte-identical to one the MCP OAuth
|
|
107
|
+
* start signs from caller input, so the flow kind is what binds a state here.
|
|
108
|
+
*/
|
|
109
|
+
const ATLASSIAN_OAUTH_STATE_KIND = "atlassian_oauth";
|
|
67
110
|
const ATLASSIAN_AUTHORIZATION_URL = "https://auth.atlassian.com/authorize";
|
|
68
111
|
const ATLASSIAN_TOKEN_URL = "https://auth.atlassian.com/oauth/token";
|
|
69
112
|
const ATLASSIAN_RESOURCES_URL = "https://api.atlassian.com/oauth/token/accessible-resources";
|
|
@@ -73,9 +116,13 @@ const ATLASSIAN_REQUEST_TIMEOUT_MS = 15_000;
|
|
|
73
116
|
const ATLASSIAN_RETURN_PATH = (workspaceId: string) => `/workspaces/${workspaceId}/capabilities`;
|
|
74
117
|
|
|
75
118
|
type AtlassianOAuthState = {
|
|
119
|
+
connectAttemptId?: string;
|
|
120
|
+
externalContinuation?: ExternalActorContinuation;
|
|
76
121
|
accountId: string;
|
|
77
122
|
workspaceId: string;
|
|
78
123
|
subjectId: string;
|
|
124
|
+
/** Signed proof that a live managed human started this personal-only flow. */
|
|
125
|
+
personalOwnerVerified: boolean;
|
|
79
126
|
returnPath: string;
|
|
80
127
|
connectionId?: string;
|
|
81
128
|
connectionVersion?: number;
|
|
@@ -99,8 +146,12 @@ export async function startAtlassianOAuth(
|
|
|
99
146
|
subjectId: string;
|
|
100
147
|
requestUrl: string;
|
|
101
148
|
payload: AtlassianOAuthStartRequest;
|
|
149
|
+
connectAttemptId?: string;
|
|
150
|
+
externalContinuation?: ExternalActorContinuation;
|
|
102
151
|
},
|
|
103
152
|
): Promise<AtlassianOAuthStartResponse> {
|
|
153
|
+
assertNativeAtlassianAvailable();
|
|
154
|
+
await withOrganizationIntegrationAcquisition(deps.db, input, ["atlassian"], async () => {});
|
|
104
155
|
const oauth = requireAtlassianSettings(deps.settings);
|
|
105
156
|
const existing = input.payload.connectionId
|
|
106
157
|
? await getConnectionMetadata(
|
|
@@ -121,6 +172,19 @@ export async function startAtlassianOAuth(
|
|
|
121
172
|
accountId: input.accountId,
|
|
122
173
|
workspaceId: input.workspaceId,
|
|
123
174
|
subjectId: input.subjectId,
|
|
175
|
+
kind: ATLASSIAN_OAUTH_STATE_KIND,
|
|
176
|
+
...(input.connectAttemptId ? { connectAttemptId: input.connectAttemptId } : {}),
|
|
177
|
+
...(input.externalContinuation
|
|
178
|
+
? {
|
|
179
|
+
encryptedExternalContinuation: encryptEnvironmentValue(
|
|
180
|
+
requireEnvironmentEncryption(deps.settings),
|
|
181
|
+
JSON.stringify(ExternalActorContinuation.parse(input.externalContinuation)),
|
|
182
|
+
),
|
|
183
|
+
}
|
|
184
|
+
: {}),
|
|
185
|
+
// The route admits only a managed human, so reaching here is the proof; the
|
|
186
|
+
// callback has no live principal and enforces exactly this claim.
|
|
187
|
+
[PERSONAL_OWNER_VERIFIED_STATE_CLAIM]: true,
|
|
124
188
|
returnPath: ATLASSIAN_RETURN_PATH(input.workspaceId),
|
|
125
189
|
...(existing ? { connectionId: existing.id, connectionVersion: existing.version } : {}),
|
|
126
190
|
});
|
|
@@ -146,12 +210,39 @@ export async function completeAtlassianOAuthCallback(
|
|
|
146
210
|
error?: string | undefined;
|
|
147
211
|
requestUrl: string;
|
|
148
212
|
},
|
|
149
|
-
): Promise<{ redirectTo: string }> {
|
|
213
|
+
): Promise<{ redirectTo: string; exactReturn?: boolean }> {
|
|
150
214
|
const baseUrl = integrationBaseUrl(deps.settings.publicBaseUrl, input.requestUrl);
|
|
151
215
|
const returnBaseUrl = deps.settings.webBaseUrl?.replace(/\/+$/, "") ?? baseUrl;
|
|
152
216
|
let state: AtlassianOAuthState | null = null;
|
|
217
|
+
let exactReturnUrl: string | undefined;
|
|
218
|
+
let operation: { attemptId: string; operationId: string; inputDigest: string } | undefined;
|
|
153
219
|
try {
|
|
154
220
|
state = readAtlassianOAuthState(input.state, deps.settings);
|
|
221
|
+
if (state.connectAttemptId) {
|
|
222
|
+
const stored = await getConnectAttempt(deps.db, state, state.connectAttemptId);
|
|
223
|
+
if (stored.attempt.providerId !== "atlassian" || stored.attempt.ownership !== "personal")
|
|
224
|
+
throw new AtlassianCallbackError("connection_conflict");
|
|
225
|
+
exactReturnUrl = stored.returnUrl;
|
|
226
|
+
operation = {
|
|
227
|
+
attemptId: state.connectAttemptId,
|
|
228
|
+
operationId: `oauth:${state.nonce}`,
|
|
229
|
+
inputDigest: createHash("sha256").update(input.state!).digest("hex"),
|
|
230
|
+
};
|
|
231
|
+
const claim = await claimOAuthAcquisition(
|
|
232
|
+
deps.db,
|
|
233
|
+
state,
|
|
234
|
+
{
|
|
235
|
+
...operation,
|
|
236
|
+
expectedRevision: stored.attempt.revision,
|
|
237
|
+
authorize: (tx, _attempt, origin) =>
|
|
238
|
+
requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
|
|
239
|
+
},
|
|
240
|
+
"atlassian",
|
|
241
|
+
// Retirement only settles this attempt; it acquires no integration.
|
|
242
|
+
false,
|
|
243
|
+
);
|
|
244
|
+
if (claim.status === "replayed") return { redirectTo: exactReturnUrl, exactReturn: true };
|
|
245
|
+
}
|
|
155
246
|
await requireCallbackGrant(deps, state);
|
|
156
247
|
const consumed = await consumeIntegrationOAuthStateNonce(deps.db, {
|
|
157
248
|
accountId: state.accountId,
|
|
@@ -161,9 +252,52 @@ export async function completeAtlassianOAuthCallback(
|
|
|
161
252
|
expiresAt: new Date(state.iat * 1000 + oauthStateTtlMs),
|
|
162
253
|
now: new Date(),
|
|
163
254
|
});
|
|
164
|
-
if (!consumed) throw new AtlassianCallbackError("
|
|
255
|
+
if (!consumed) throw new AtlassianCallbackError("state_replayed");
|
|
256
|
+
if (operation && (input.error || !input.code)) {
|
|
257
|
+
await finishConnectOperation(deps.db, state, {
|
|
258
|
+
...operation,
|
|
259
|
+
authorize: (tx, _attempt, origin) =>
|
|
260
|
+
requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
|
|
261
|
+
commit: async (_tx, current) => ({
|
|
262
|
+
...current,
|
|
263
|
+
revision: current.revision + 1,
|
|
264
|
+
state: input.error === "access_denied" ? "cancelled" : "failed",
|
|
265
|
+
nextAction: { type: "none" },
|
|
266
|
+
error: {
|
|
267
|
+
code: input.error ? "provider_denied" : "missing_code",
|
|
268
|
+
message: "Authorization was not completed. Start a new connection attempt.",
|
|
269
|
+
retryable: false,
|
|
270
|
+
},
|
|
271
|
+
}),
|
|
272
|
+
});
|
|
273
|
+
return { redirectTo: exactReturnUrl!, exactReturn: true };
|
|
274
|
+
}
|
|
165
275
|
if (input.error) throw new AtlassianCallbackError("provider_denied");
|
|
166
276
|
if (!input.code) throw new AtlassianCallbackError("missing_code");
|
|
277
|
+
// An authorization started on an older release must not exchange or save
|
|
278
|
+
// a native grant after retirement. Preserve exact Connect navigation and
|
|
279
|
+
// settle the pending attempt so it does not remain stuck authorizing.
|
|
280
|
+
if (operation) {
|
|
281
|
+
await finishConnectOperation(deps.db, state, {
|
|
282
|
+
...operation,
|
|
283
|
+
authorize: (tx, _attempt, origin) =>
|
|
284
|
+
requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
|
|
285
|
+
commit: async (_tx, current) => ({
|
|
286
|
+
...current,
|
|
287
|
+
revision: current.revision + 1,
|
|
288
|
+
state: "failed",
|
|
289
|
+
nextAction: { type: "none" },
|
|
290
|
+
error: {
|
|
291
|
+
code: ATLASSIAN_NATIVE_RETIRED_REASON,
|
|
292
|
+
message: ATLASSIAN_NATIVE_RETIRED_MESSAGE,
|
|
293
|
+
retryable: false,
|
|
294
|
+
},
|
|
295
|
+
}),
|
|
296
|
+
});
|
|
297
|
+
return { redirectTo: exactReturnUrl!, exactReturn: true };
|
|
298
|
+
}
|
|
299
|
+
assertNativeAtlassianAvailable();
|
|
300
|
+
await withOrganizationIntegrationAcquisition(deps.db, state, ["atlassian"], async () => {});
|
|
167
301
|
|
|
168
302
|
const oauth = requireAtlassianSettings(deps.settings);
|
|
169
303
|
const redirectUri = `${baseUrl}/v1/integrations/atlassian/callback`;
|
|
@@ -242,45 +376,64 @@ export async function completeAtlassianOAuthCallback(
|
|
|
242
376
|
: {}),
|
|
243
377
|
selectedSources: previousMetadata?.selectedSources ?? [],
|
|
244
378
|
});
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
379
|
+
const acceptedState = state;
|
|
380
|
+
const persist = (tx: Database) => {
|
|
381
|
+
// Capture the validated immutable callback state for delayed persistence.
|
|
382
|
+
// eslint-disable-next-line no-shadow
|
|
383
|
+
const state = acceptedState;
|
|
384
|
+
return existing
|
|
385
|
+
? updateConnection(tx, {
|
|
386
|
+
workspaceId: state.workspaceId,
|
|
387
|
+
connectionId: existing.id,
|
|
388
|
+
visibleToSubjectId: state.subjectId,
|
|
389
|
+
expectedVersion: existing.version,
|
|
390
|
+
subjectId: state.subjectId,
|
|
391
|
+
providerDomain: ATLASSIAN_PROVIDER_DOMAIN,
|
|
392
|
+
kind: "oauth2",
|
|
393
|
+
status: "active",
|
|
394
|
+
credentialEncrypted,
|
|
395
|
+
grantedScopes: token.scopes,
|
|
396
|
+
expiresAt: token.expiresAt,
|
|
397
|
+
metadata,
|
|
398
|
+
updatedBySubjectId: state.subjectId,
|
|
399
|
+
})
|
|
400
|
+
: createConnection(tx, {
|
|
401
|
+
accountId: state.accountId,
|
|
402
|
+
workspaceId: state.workspaceId,
|
|
403
|
+
subjectId: state.subjectId,
|
|
404
|
+
providerDomain: ATLASSIAN_PROVIDER_DOMAIN,
|
|
405
|
+
kind: "oauth2",
|
|
406
|
+
credentialEncrypted,
|
|
407
|
+
grantedScopes: token.scopes,
|
|
408
|
+
expiresAt: token.expiresAt,
|
|
409
|
+
metadata,
|
|
410
|
+
createdBySubjectId: state.subjectId,
|
|
411
|
+
});
|
|
412
|
+
};
|
|
413
|
+
const connection = await withOrganizationIntegrationAcquisition(
|
|
414
|
+
deps.db,
|
|
415
|
+
acceptedState,
|
|
416
|
+
["atlassian"],
|
|
417
|
+
async (tx) => {
|
|
418
|
+
await requireCallbackGrant({ ...deps, db: tx }, acceptedState);
|
|
419
|
+
return persist(tx);
|
|
420
|
+
},
|
|
421
|
+
);
|
|
273
422
|
if (!connection) throw new AtlassianCallbackError("connection_conflict");
|
|
274
423
|
return {
|
|
275
424
|
redirectTo: returnUrl(returnBaseUrl, state.returnPath, "connected", connection.id),
|
|
276
425
|
};
|
|
277
426
|
} catch (error) {
|
|
427
|
+
if (exactReturnUrl) return { redirectTo: exactReturnUrl, exactReturn: true };
|
|
428
|
+
// Reading the state is the first step, so no state means the link itself
|
|
429
|
+
// was unusable: say whether it expired rather than blame configuration.
|
|
430
|
+
const failure = state ? null : oauthStateFailureReturn(deps.settings, input.state);
|
|
278
431
|
return {
|
|
279
432
|
redirectTo: returnUrl(
|
|
280
433
|
returnBaseUrl,
|
|
281
|
-
state?.returnPath ??
|
|
434
|
+
state?.returnPath ?? failure!.returnPath,
|
|
282
435
|
"error",
|
|
283
|
-
errorReason(error),
|
|
436
|
+
failure?.reason ?? errorReason(error),
|
|
284
437
|
),
|
|
285
438
|
};
|
|
286
439
|
}
|
|
@@ -288,8 +441,13 @@ export async function completeAtlassianOAuthCallback(
|
|
|
288
441
|
|
|
289
442
|
export async function browseAtlassianSources(
|
|
290
443
|
deps: ApiRouteDeps,
|
|
291
|
-
input:
|
|
444
|
+
input: AtlassianRequestAuthority & {
|
|
445
|
+
workspaceId: string;
|
|
446
|
+
subjectId: string;
|
|
447
|
+
connectionId: string;
|
|
448
|
+
},
|
|
292
449
|
) {
|
|
450
|
+
assertNativeAtlassianAvailable();
|
|
293
451
|
const connection = await getConnectionMetadata(
|
|
294
452
|
deps.db,
|
|
295
453
|
input.workspaceId,
|
|
@@ -329,7 +487,7 @@ export async function browseAtlassianSources(
|
|
|
329
487
|
|
|
330
488
|
export async function searchAtlassianLive(
|
|
331
489
|
deps: ApiRouteDeps,
|
|
332
|
-
input: {
|
|
490
|
+
input: AtlassianRequestAuthority & {
|
|
333
491
|
workspaceId: string;
|
|
334
492
|
subjectId: string;
|
|
335
493
|
connectionId: string;
|
|
@@ -338,6 +496,7 @@ export async function searchAtlassianLive(
|
|
|
338
496
|
limit: number;
|
|
339
497
|
},
|
|
340
498
|
) {
|
|
499
|
+
assertNativeAtlassianAvailable();
|
|
341
500
|
const connection = await getConnectionMetadata(
|
|
342
501
|
deps.db,
|
|
343
502
|
input.workspaceId,
|
|
@@ -432,7 +591,7 @@ export async function searchAtlassianLive(
|
|
|
432
591
|
|
|
433
592
|
export async function getAtlassianLiveItem(
|
|
434
593
|
deps: ApiRouteDeps,
|
|
435
|
-
input: {
|
|
594
|
+
input: AtlassianRequestAuthority & {
|
|
436
595
|
workspaceId: string;
|
|
437
596
|
subjectId: string;
|
|
438
597
|
connectionId: string;
|
|
@@ -440,6 +599,7 @@ export async function getAtlassianLiveItem(
|
|
|
440
599
|
id: string;
|
|
441
600
|
},
|
|
442
601
|
) {
|
|
602
|
+
assertNativeAtlassianAvailable();
|
|
443
603
|
const connection = await getConnectionMetadata(
|
|
444
604
|
deps.db,
|
|
445
605
|
input.workspaceId,
|
|
@@ -553,6 +713,7 @@ export async function saveAtlassianSources(
|
|
|
553
713
|
canManagePersonalDestination: boolean;
|
|
554
714
|
},
|
|
555
715
|
) {
|
|
716
|
+
assertNativeAtlassianAvailable();
|
|
556
717
|
const parsed = SaveAtlassianSourcesRequest.safeParse(input.payload);
|
|
557
718
|
if (!parsed.success) {
|
|
558
719
|
throw new HTTPException(400, { message: "invalid Atlassian source selection" });
|
|
@@ -584,9 +745,28 @@ export async function saveAtlassianSources(
|
|
|
584
745
|
initiatingSubjectId: input.subjectId,
|
|
585
746
|
});
|
|
586
747
|
|
|
587
|
-
const
|
|
748
|
+
const requestedSelection = parsed.data.sources.map((source) => ({
|
|
749
|
+
...source,
|
|
750
|
+
destination,
|
|
751
|
+
syncCadence: parsed.data.syncCadence,
|
|
752
|
+
syncEnabled: parsed.data.syncEnabled,
|
|
753
|
+
readPolicy: parsed.data.readPolicy,
|
|
754
|
+
}));
|
|
755
|
+
const acquiring = integrationSourceSelectionRequiresAcquisition(
|
|
756
|
+
metadata.selectedSources,
|
|
757
|
+
requestedSelection,
|
|
758
|
+
);
|
|
759
|
+
if (acquiring) {
|
|
760
|
+
await withOrganizationIntegrationAcquisition(deps.db, input, ["atlassian"], async () => {});
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const available =
|
|
764
|
+
acquiring && parsed.data.sources.length
|
|
765
|
+
? (await browseAtlassianSources(deps, input)).items
|
|
766
|
+
: [];
|
|
588
767
|
const availableById = new Map(available.map((source) => [source.id, source]));
|
|
589
768
|
const verified = parsed.data.sources.map((source) => {
|
|
769
|
+
if (!acquiring) return source;
|
|
590
770
|
const current = availableById.get(source.id);
|
|
591
771
|
if (
|
|
592
772
|
!current ||
|
|
@@ -603,28 +783,39 @@ export async function saveAtlassianSources(
|
|
|
603
783
|
return current;
|
|
604
784
|
});
|
|
605
785
|
const previousSources = metadata.selectedSources;
|
|
606
|
-
const updated = await
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
786
|
+
const updated = await withOrganizationIntegrationPolicyFence(
|
|
787
|
+
deps.db,
|
|
788
|
+
input,
|
|
789
|
+
async (tx, policy) => {
|
|
790
|
+
// The exact generation checked by transitionConnectionState binds this
|
|
791
|
+
// comparison to the row being changed. Provider verification already ended.
|
|
792
|
+
if (integrationSourceSelectionRequiresAcquisition(previousSources, requestedSelection)) {
|
|
793
|
+
assertOrganizationIntegrationAllowed(policy, "atlassian");
|
|
794
|
+
}
|
|
795
|
+
return transitionConnectionState(tx, {
|
|
796
|
+
workspaceId: input.workspaceId,
|
|
797
|
+
connectionId: existing.id,
|
|
798
|
+
visibleToSubjectId: input.subjectId,
|
|
799
|
+
expectedVersion: existing.version,
|
|
800
|
+
metadata: AtlassianConnectionMetadata.parse({
|
|
801
|
+
...metadata,
|
|
802
|
+
documentDestination: destination,
|
|
803
|
+
selectedSources: verified.map((source) => ({
|
|
804
|
+
...source,
|
|
805
|
+
destination,
|
|
806
|
+
syncCadence: parsed.data.syncCadence,
|
|
807
|
+
syncEnabled: parsed.data.syncEnabled,
|
|
808
|
+
configGeneration:
|
|
809
|
+
(previousSources.find((previous) => previous.id === source.id)?.configGeneration ??
|
|
810
|
+
0) + 1,
|
|
811
|
+
readPolicy: parsed.data.readPolicy,
|
|
812
|
+
selectedAt: new Date().toISOString(),
|
|
813
|
+
})),
|
|
814
|
+
}),
|
|
815
|
+
updatedBySubjectId: input.subjectId,
|
|
816
|
+
});
|
|
817
|
+
},
|
|
818
|
+
);
|
|
628
819
|
if (!updated) throw new HTTPException(409, { message: "Atlassian connection changed" });
|
|
629
820
|
await materializeSchedules(deps, {
|
|
630
821
|
...input,
|
|
@@ -644,6 +835,7 @@ export async function transitionAtlassianLifecycle(
|
|
|
644
835
|
payload: AtlassianLifecycleActionRequest;
|
|
645
836
|
},
|
|
646
837
|
) {
|
|
838
|
+
if (input.payload.action === "resume") assertNativeAtlassianAvailable();
|
|
647
839
|
const connection = await getConnectionMetadata(
|
|
648
840
|
deps.db,
|
|
649
841
|
input.workspaceId,
|
|
@@ -662,14 +854,27 @@ export async function transitionAtlassianLifecycle(
|
|
|
662
854
|
if (target === "paused") {
|
|
663
855
|
await deauthorizeConnectionSources(deps, connection, input.subjectId, "connection_paused");
|
|
664
856
|
}
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
857
|
+
const persist = (tx: Database) =>
|
|
858
|
+
transitionConnectionState(tx, {
|
|
859
|
+
workspaceId: input.workspaceId,
|
|
860
|
+
connectionId: input.connectionId,
|
|
861
|
+
visibleToSubjectId: input.subjectId,
|
|
862
|
+
expectedVersion: connection.version,
|
|
863
|
+
metadata: AtlassianConnectionMetadata.parse({ ...metadata, lifecycle: lifecycle(target) }),
|
|
864
|
+
updatedBySubjectId: input.subjectId,
|
|
865
|
+
});
|
|
866
|
+
const updated =
|
|
867
|
+
target === "active"
|
|
868
|
+
? await withOrganizationIntegrationPolicyFence(
|
|
869
|
+
deps.db,
|
|
870
|
+
{ accountId: connection.accountId, workspaceId: input.workspaceId },
|
|
871
|
+
(tx, policy) => {
|
|
872
|
+
if ((metadata.lifecycle?.state ?? "active") !== "active")
|
|
873
|
+
assertOrganizationIntegrationAllowed(policy, "atlassian");
|
|
874
|
+
return persist(tx);
|
|
875
|
+
},
|
|
876
|
+
)
|
|
877
|
+
: await persist(deps.db);
|
|
673
878
|
if (!updated) throw new HTTPException(409, { message: "Atlassian connection changed" });
|
|
674
879
|
await setSchedulePause(deps, input.workspaceId, input.connectionId, target === "paused");
|
|
675
880
|
return updated;
|
|
@@ -719,15 +924,15 @@ export async function disconnectAtlassian(
|
|
|
719
924
|
}
|
|
720
925
|
}
|
|
721
926
|
|
|
722
|
-
export async function
|
|
927
|
+
export async function preflightAtlassianScheduleAuthorization(
|
|
723
928
|
deps: ApiRouteDeps,
|
|
724
929
|
input: { task: ScheduledTask; subjectId: string },
|
|
725
930
|
): Promise<void> {
|
|
726
931
|
const { task } = input;
|
|
727
|
-
if (task
|
|
932
|
+
if (!scheduledTaskKnowledgeSource(task)) return;
|
|
728
933
|
if (
|
|
729
|
-
task.
|
|
730
|
-
task.
|
|
934
|
+
requireScheduledTaskKnowledgeSource(task).initiatingSubjectId !== input.subjectId ||
|
|
935
|
+
requireScheduledTaskKnowledgeSource(task).connection.ownerSubjectId !== input.subjectId
|
|
731
936
|
) {
|
|
732
937
|
throw new HTTPException(403, {
|
|
733
938
|
message: "knowledge source schedule requires the exact initiating subject",
|
|
@@ -737,50 +942,118 @@ export async function revokeAtlassianScheduleAuthorization(
|
|
|
737
942
|
if (!externalSourceId) {
|
|
738
943
|
throw new HTTPException(409, { message: "Atlassian schedule identity is incomplete" });
|
|
739
944
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
const
|
|
749
|
-
|
|
750
|
-
if (
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
metadata: AtlassianConnectionMetadata.parse({
|
|
757
|
-
...metadata,
|
|
758
|
-
selectedSources: metadata.selectedSources.map((source) =>
|
|
759
|
-
source.id === externalSourceId
|
|
760
|
-
? { ...source, syncEnabled: false, configGeneration: source.configGeneration + 1 }
|
|
761
|
-
: source,
|
|
762
|
-
),
|
|
763
|
-
}),
|
|
764
|
-
updatedBySubjectId: input.subjectId,
|
|
945
|
+
// Current connector attachment is not deletion authority. The frozen task
|
|
946
|
+
// revision and generation-fenced cleanup descriptor are.
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
export async function revokeAtlassianScheduleAuthorization(
|
|
950
|
+
deps: ApiRouteDeps,
|
|
951
|
+
input: { task: ScheduledTask; subjectId: string },
|
|
952
|
+
): Promise<void> {
|
|
953
|
+
const { task } = input;
|
|
954
|
+
if (!scheduledTaskKnowledgeSource(task)) return;
|
|
955
|
+
if (
|
|
956
|
+
requireScheduledTaskKnowledgeSource(task).initiatingSubjectId !== input.subjectId ||
|
|
957
|
+
requireScheduledTaskKnowledgeSource(task).connection.ownerSubjectId !== input.subjectId
|
|
958
|
+
) {
|
|
959
|
+
throw new HTTPException(403, {
|
|
960
|
+
message: "knowledge source schedule requires the exact initiating subject",
|
|
765
961
|
});
|
|
766
|
-
if (!updated) throw new HTTPException(409, { message: "Atlassian connection changed" });
|
|
767
|
-
authorityVersion = updated.version;
|
|
768
962
|
}
|
|
769
|
-
const
|
|
963
|
+
const externalSourceId = optionalString(task.metadata.externalSourceId);
|
|
964
|
+
if (!externalSourceId) {
|
|
965
|
+
throw new HTTPException(409, { message: "Atlassian schedule identity is incomplete" });
|
|
966
|
+
}
|
|
967
|
+
await cleanupAtlassianScheduleAuthorization(deps, {
|
|
968
|
+
taskId: task.id,
|
|
770
969
|
accountId: task.accountId,
|
|
771
970
|
workspaceId: task.workspaceId,
|
|
772
|
-
|
|
971
|
+
connectionId: requireScheduledTaskKnowledgeSource(task).connection.connectionId,
|
|
972
|
+
connectionVersion: requireScheduledTaskKnowledgeSource(task).connection.connectionVersion,
|
|
973
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
974
|
+
sourceLifecycleGeneration: requireScheduledTaskKnowledgeSource(task).sourceLifecycleGeneration,
|
|
975
|
+
sourceConfigGeneration: requireScheduledTaskKnowledgeSource(task).sourceConfigGeneration,
|
|
976
|
+
externalSourceId,
|
|
977
|
+
subjectId: input.subjectId,
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
export async function cleanupAtlassianScheduleAuthorization(
|
|
982
|
+
deps: ApiRouteDeps,
|
|
983
|
+
input: {
|
|
984
|
+
taskId: string;
|
|
985
|
+
accountId: string;
|
|
986
|
+
workspaceId: string;
|
|
987
|
+
connectionId: string;
|
|
988
|
+
connectionVersion: number;
|
|
989
|
+
sourceId: string;
|
|
990
|
+
sourceLifecycleGeneration: number;
|
|
991
|
+
sourceConfigGeneration: number;
|
|
992
|
+
externalSourceId: string;
|
|
993
|
+
subjectId: string;
|
|
994
|
+
},
|
|
995
|
+
): Promise<void> {
|
|
996
|
+
const resolvedBeforeLock = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
997
|
+
accountId: input.accountId,
|
|
998
|
+
workspaceId: input.workspaceId,
|
|
999
|
+
sourceId: input.sourceId,
|
|
773
1000
|
initiatingSubjectId: input.subjectId,
|
|
774
1001
|
});
|
|
775
|
-
if (
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
1002
|
+
if (
|
|
1003
|
+
resolvedBeforeLock?.source.lifecycleState !== "active" ||
|
|
1004
|
+
resolvedBeforeLock.source.lifecycleGeneration !== input.sourceLifecycleGeneration
|
|
1005
|
+
) {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
await withWorkspaceSubjectRls(deps.db, input.workspaceId, input.subjectId, async (tx) => {
|
|
1009
|
+
const connection = await getConnectionMetadata(
|
|
1010
|
+
tx,
|
|
1011
|
+
input.workspaceId,
|
|
1012
|
+
input.connectionId,
|
|
1013
|
+
input.subjectId,
|
|
1014
|
+
);
|
|
1015
|
+
let authorityVersion = input.connectionVersion;
|
|
1016
|
+
if (connection) {
|
|
1017
|
+
const metadata = requireAtlassianConnection(connection, input.subjectId);
|
|
1018
|
+
authorityVersion = connection.version;
|
|
1019
|
+
const updated = await transitionConnectionState(tx, {
|
|
1020
|
+
workspaceId: input.workspaceId,
|
|
1021
|
+
connectionId: connection.id,
|
|
1022
|
+
visibleToSubjectId: input.subjectId,
|
|
1023
|
+
expectedVersion: connection.version,
|
|
1024
|
+
metadata: AtlassianConnectionMetadata.parse({
|
|
1025
|
+
...metadata,
|
|
1026
|
+
selectedSources: metadata.selectedSources.map((source) =>
|
|
1027
|
+
source.id === input.externalSourceId
|
|
1028
|
+
? { ...source, syncEnabled: false, configGeneration: source.configGeneration + 1 }
|
|
1029
|
+
: source,
|
|
1030
|
+
),
|
|
1031
|
+
}),
|
|
1032
|
+
updatedBySubjectId: input.subjectId,
|
|
1033
|
+
});
|
|
1034
|
+
if (!updated) throw new HTTPException(409, { message: "Atlassian connection changed" });
|
|
1035
|
+
authorityVersion = updated.version;
|
|
1036
|
+
}
|
|
1037
|
+
const resolved = await getKnowledgeSourceForSyncAuthority(tx, {
|
|
1038
|
+
accountId: input.accountId,
|
|
1039
|
+
workspaceId: input.workspaceId,
|
|
1040
|
+
sourceId: input.sourceId,
|
|
1041
|
+
initiatingSubjectId: input.subjectId,
|
|
1042
|
+
});
|
|
1043
|
+
if (
|
|
1044
|
+
resolved?.source.lifecycleState !== "active" ||
|
|
1045
|
+
resolved.source.lifecycleGeneration !== input.sourceLifecycleGeneration
|
|
1046
|
+
) {
|
|
1047
|
+
throw new HTTPException(409, { message: "Atlassian knowledge source changed" });
|
|
1048
|
+
}
|
|
1049
|
+
await recordKnowledgeLifecycleEvent(tx, {
|
|
1050
|
+
accountId: input.accountId,
|
|
1051
|
+
workspaceId: input.workspaceId,
|
|
779
1052
|
targetKind: "source",
|
|
780
1053
|
targetId: resolved.source.id,
|
|
781
1054
|
eventType: "deleted",
|
|
782
1055
|
expectedGeneration: resolved.source.lifecycleGeneration,
|
|
783
|
-
operationId: `atlassian-schedule-delete:${
|
|
1056
|
+
operationId: `atlassian-schedule-delete:${input.taskId}:${authorityVersion}`,
|
|
784
1057
|
reasonCode: "schedule_deleted",
|
|
785
1058
|
actor: {
|
|
786
1059
|
kind: "human",
|
|
@@ -788,7 +1061,7 @@ export async function revokeAtlassianScheduleAuthorization(
|
|
|
788
1061
|
initiatingHumanSubjectId: input.subjectId,
|
|
789
1062
|
},
|
|
790
1063
|
});
|
|
791
|
-
}
|
|
1064
|
+
});
|
|
792
1065
|
}
|
|
793
1066
|
|
|
794
1067
|
async function materializeSchedules(
|
|
@@ -830,11 +1103,11 @@ async function materializeSchedules(
|
|
|
830
1103
|
let source = null as Awaited<ReturnType<typeof upsertKnowledgeSource>> | null;
|
|
831
1104
|
let existingTask: (typeof tasks)[number] | null = null;
|
|
832
1105
|
for (const task of tasks) {
|
|
833
|
-
if (task
|
|
1106
|
+
if (!scheduledTaskKnowledgeSource(task)) continue;
|
|
834
1107
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
835
1108
|
accountId: input.accountId,
|
|
836
1109
|
workspaceId: input.workspaceId,
|
|
837
|
-
sourceId: task.
|
|
1110
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
838
1111
|
initiatingSubjectId: input.subjectId,
|
|
839
1112
|
});
|
|
840
1113
|
if (resolved?.source.externalSourceId !== identity.externalSourceId) continue;
|
|
@@ -941,7 +1214,10 @@ async function materializeSchedules(
|
|
|
941
1214
|
name: `Sync ${selectedSource.kind === "jira_project" ? "Jira" : "Confluence"}: ${selectedSource.name}`,
|
|
942
1215
|
schedule: atlassianSchedule(selectedSource.syncCadence),
|
|
943
1216
|
overlapPolicy: "buffer_one",
|
|
944
|
-
action,
|
|
1217
|
+
action: { kind: "agent_turn" },
|
|
1218
|
+
agentConfig: knowledgeSourceAgentConfig(action, existingTask.agentConfig),
|
|
1219
|
+
refreshPersonalResourceAuthority: true,
|
|
1220
|
+
authorityUpdatedBy: { kind: "subject", subjectId: input.subjectId },
|
|
945
1221
|
metadata: {
|
|
946
1222
|
...existingTask.metadata,
|
|
947
1223
|
connectorKind: "atlassian",
|
|
@@ -969,15 +1245,11 @@ async function materializeSchedules(
|
|
|
969
1245
|
status: "active",
|
|
970
1246
|
schedule: atlassianSchedule(selectedSource.syncCadence),
|
|
971
1247
|
overlapPolicy: "buffer_one",
|
|
972
|
-
action,
|
|
1248
|
+
action: { kind: "agent_turn" },
|
|
973
1249
|
runMode: "new_session_per_run",
|
|
974
1250
|
targetSessionId: null,
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
resources: [],
|
|
978
|
-
tools: [],
|
|
979
|
-
metadata: {},
|
|
980
|
-
},
|
|
1251
|
+
connectionAccounts: [],
|
|
1252
|
+
agentConfig: knowledgeSourceAgentConfig(action),
|
|
981
1253
|
variableSetId: null,
|
|
982
1254
|
environmentId: null,
|
|
983
1255
|
rigId: null,
|
|
@@ -1002,11 +1274,11 @@ async function materializeSchedules(
|
|
|
1002
1274
|
}
|
|
1003
1275
|
|
|
1004
1276
|
for (const task of tasks) {
|
|
1005
|
-
if (task
|
|
1277
|
+
if (!scheduledTaskKnowledgeSource(task)) continue;
|
|
1006
1278
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
1007
1279
|
accountId: input.accountId,
|
|
1008
1280
|
workspaceId: input.workspaceId,
|
|
1009
|
-
sourceId: task.
|
|
1281
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1010
1282
|
initiatingSubjectId: input.subjectId,
|
|
1011
1283
|
});
|
|
1012
1284
|
if (!resolved || enabledIds.has(resolved.source.externalSourceId)) continue;
|
|
@@ -1041,7 +1313,11 @@ async function materializeSchedules(
|
|
|
1041
1313
|
|
|
1042
1314
|
async function browseJiraProjects(
|
|
1043
1315
|
deps: ApiRouteDeps,
|
|
1044
|
-
input:
|
|
1316
|
+
input: AtlassianRequestAuthority & {
|
|
1317
|
+
workspaceId: string;
|
|
1318
|
+
subjectId: string;
|
|
1319
|
+
connectionId: string;
|
|
1320
|
+
},
|
|
1045
1321
|
site: AtlassianSite,
|
|
1046
1322
|
): Promise<AtlassianBrowseItem[]> {
|
|
1047
1323
|
const items: AtlassianBrowseItem[] = [];
|
|
@@ -1087,7 +1363,11 @@ async function browseJiraProjects(
|
|
|
1087
1363
|
|
|
1088
1364
|
async function browseConfluenceSpaces(
|
|
1089
1365
|
deps: ApiRouteDeps,
|
|
1090
|
-
input:
|
|
1366
|
+
input: AtlassianRequestAuthority & {
|
|
1367
|
+
workspaceId: string;
|
|
1368
|
+
subjectId: string;
|
|
1369
|
+
connectionId: string;
|
|
1370
|
+
},
|
|
1091
1371
|
site: AtlassianSite,
|
|
1092
1372
|
): Promise<AtlassianBrowseItem[]> {
|
|
1093
1373
|
const items: AtlassianBrowseItem[] = [];
|
|
@@ -1145,7 +1425,7 @@ export function confluenceNextUrl(cloudId: string, next: string): URL {
|
|
|
1145
1425
|
|
|
1146
1426
|
async function atlassianApiRequest(
|
|
1147
1427
|
deps: ApiRouteDeps,
|
|
1148
|
-
input: {
|
|
1428
|
+
input: AtlassianRequestAuthority & {
|
|
1149
1429
|
workspaceId: string;
|
|
1150
1430
|
subjectId: string;
|
|
1151
1431
|
connectionId: string;
|
|
@@ -1153,6 +1433,7 @@ async function atlassianApiRequest(
|
|
|
1153
1433
|
label: string;
|
|
1154
1434
|
},
|
|
1155
1435
|
): Promise<unknown> {
|
|
1436
|
+
assertNativeAtlassianAvailable();
|
|
1156
1437
|
const current = await getConnectionMetadata(
|
|
1157
1438
|
deps.db,
|
|
1158
1439
|
input.workspaceId,
|
|
@@ -1166,9 +1447,20 @@ async function atlassianApiRequest(
|
|
|
1166
1447
|
});
|
|
1167
1448
|
const resolve = async (forceRefresh: boolean) =>
|
|
1168
1449
|
await resolver({
|
|
1169
|
-
workspaceId: input.workspaceId,
|
|
1450
|
+
workspaceId: input.connectionUseContext?.workspaceId ?? input.workspaceId,
|
|
1451
|
+
...(input.connectionUseContext
|
|
1452
|
+
? {
|
|
1453
|
+
connectionUseContext: {
|
|
1454
|
+
...input.connectionUseContext,
|
|
1455
|
+
physicalRequestId: crypto.randomUUID(),
|
|
1456
|
+
usePhase: "credential_resolution" as const,
|
|
1457
|
+
},
|
|
1458
|
+
}
|
|
1459
|
+
: {}),
|
|
1170
1460
|
subjectId: input.subjectId,
|
|
1171
|
-
serverId:
|
|
1461
|
+
serverId: input.connectionUseContext
|
|
1462
|
+
? `atlassian:${input.connectionId}`
|
|
1463
|
+
: "atlassian-source-browser",
|
|
1172
1464
|
toolName: input.label,
|
|
1173
1465
|
connectionRef: {
|
|
1174
1466
|
providerDomain: ATLASSIAN_PROVIDER_DOMAIN,
|
|
@@ -1325,20 +1617,20 @@ async function deauthorizeConnectionSources(
|
|
|
1325
1617
|
connection.id,
|
|
1326
1618
|
);
|
|
1327
1619
|
for (const task of tasks) {
|
|
1328
|
-
if (task
|
|
1620
|
+
if (!scheduledTaskKnowledgeSource(task)) continue;
|
|
1329
1621
|
const resolved = await getKnowledgeSourceForSyncAuthority(deps.db, {
|
|
1330
1622
|
accountId: task.accountId,
|
|
1331
1623
|
workspaceId: task.workspaceId,
|
|
1332
|
-
sourceId: task.
|
|
1624
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1333
1625
|
initiatingSubjectId: subjectId,
|
|
1334
1626
|
});
|
|
1335
1627
|
if (!resolved || resolved.source.lifecycleState !== "active") continue;
|
|
1336
1628
|
await deauthorizeKnowledgeSourceRetrieval(deps.db, {
|
|
1337
1629
|
accountId: task.accountId,
|
|
1338
1630
|
workspaceId: task.workspaceId,
|
|
1339
|
-
sourceId: task.
|
|
1340
|
-
audience: task.
|
|
1341
|
-
operationId: `atlassian-deauthorize:${connection.id}:${connection.version + 1}:${task.
|
|
1631
|
+
sourceId: requireScheduledTaskKnowledgeSource(task).sourceId,
|
|
1632
|
+
audience: requireScheduledTaskKnowledgeSource(task).destination,
|
|
1633
|
+
operationId: `atlassian-deauthorize:${connection.id}:${connection.version + 1}:${requireScheduledTaskKnowledgeSource(task).sourceId}:${reasonCode}`,
|
|
1342
1634
|
reasonCode,
|
|
1343
1635
|
actor: { kind: "human", subjectId, initiatingHumanSubjectId: subjectId },
|
|
1344
1636
|
});
|
|
@@ -1409,6 +1701,12 @@ function readAtlassianOAuthState(raw: string | undefined, settings: Settings): A
|
|
|
1409
1701
|
if (iat === undefined || now < iat || now - iat > oauthStateTtlMs / 1_000) {
|
|
1410
1702
|
throw new HTTPException(400, { message: "expired Atlassian OAuth state" });
|
|
1411
1703
|
}
|
|
1704
|
+
// Reject a state minted by any other flow before reading anything else. A
|
|
1705
|
+
// pre-cutover state carries no kind and is refused; that is the same bounded
|
|
1706
|
+
// `oauthStateTtlMs` window the personal-owner claim already fails closed on.
|
|
1707
|
+
if (payload.kind !== ATLASSIAN_OAUTH_STATE_KIND) {
|
|
1708
|
+
throw new HTTPException(400, { message: "invalid Atlassian OAuth state" });
|
|
1709
|
+
}
|
|
1412
1710
|
const accountId = requiredString(payload.accountId, "state.accountId");
|
|
1413
1711
|
const workspaceId = requiredString(payload.workspaceId, "state.workspaceId");
|
|
1414
1712
|
const subjectId = requiredString(payload.subjectId, "state.subjectId");
|
|
@@ -1425,6 +1723,22 @@ function readAtlassianOAuthState(raw: string | undefined, settings: Settings): A
|
|
|
1425
1723
|
accountId,
|
|
1426
1724
|
workspaceId,
|
|
1427
1725
|
subjectId,
|
|
1726
|
+
personalOwnerVerified: personalOwnerVerifiedInState(payload),
|
|
1727
|
+
...(typeof payload.connectAttemptId === "string"
|
|
1728
|
+
? { connectAttemptId: payload.connectAttemptId }
|
|
1729
|
+
: {}),
|
|
1730
|
+
...(typeof payload.encryptedExternalContinuation === "string"
|
|
1731
|
+
? {
|
|
1732
|
+
externalContinuation: ExternalActorContinuation.parse(
|
|
1733
|
+
JSON.parse(
|
|
1734
|
+
decryptEnvironmentValue(
|
|
1735
|
+
requireEnvironmentEncryption(settings),
|
|
1736
|
+
payload.encryptedExternalContinuation,
|
|
1737
|
+
),
|
|
1738
|
+
),
|
|
1739
|
+
),
|
|
1740
|
+
}
|
|
1741
|
+
: {}),
|
|
1428
1742
|
returnPath,
|
|
1429
1743
|
...(connectionId ? { connectionId, connectionVersion: connectionVersion! } : {}),
|
|
1430
1744
|
nonce: requiredString(payload.nonce, "state.nonce"),
|
|
@@ -1433,14 +1747,22 @@ function readAtlassianOAuthState(raw: string | undefined, settings: Settings): A
|
|
|
1433
1747
|
}
|
|
1434
1748
|
|
|
1435
1749
|
async function requireCallbackGrant(deps: ApiRouteDeps, state: AtlassianOAuthState): Promise<void> {
|
|
1436
|
-
|
|
1750
|
+
// This connector is personal-only by construction: its start request carries
|
|
1751
|
+
// no ownership and the callback always writes `subjectId: state.subjectId`.
|
|
1752
|
+
// A state minted before the start-side principal fence existed carries no
|
|
1753
|
+
// `personalOwnerVerified` claim and must not land a personal Connection.
|
|
1437
1754
|
if (
|
|
1438
|
-
!
|
|
1439
|
-
|
|
1440
|
-
|
|
1755
|
+
!personalOwnerStateAccepted({
|
|
1756
|
+
ownership: "personal",
|
|
1757
|
+
subjectId: state.subjectId,
|
|
1758
|
+
personalOwnerVerified: state.personalOwnerVerified,
|
|
1759
|
+
})
|
|
1441
1760
|
) {
|
|
1442
|
-
throw new HTTPException(
|
|
1761
|
+
throw new HTTPException(422, {
|
|
1762
|
+
message: personalOnlyConnectionPrincipalMessage("Atlassian"),
|
|
1763
|
+
});
|
|
1443
1764
|
}
|
|
1765
|
+
await requireConnectOwnerAuthority(deps.db, state, "connections:write");
|
|
1444
1766
|
}
|
|
1445
1767
|
|
|
1446
1768
|
function requireAtlassianSettings(settings: Settings) {
|
|
@@ -1489,11 +1811,17 @@ function returnUrl(base: string, path: string, status: "connected" | "error", va
|
|
|
1489
1811
|
}
|
|
1490
1812
|
|
|
1491
1813
|
function errorReason(error: unknown): string {
|
|
1814
|
+
if (error instanceof HTTPException && error.status === 410)
|
|
1815
|
+
return ATLASSIAN_NATIVE_RETIRED_REASON;
|
|
1492
1816
|
if (error instanceof AtlassianCallbackError) return error.reason;
|
|
1493
1817
|
if (error instanceof HTTPException) return `http_${error.status}`;
|
|
1494
1818
|
return "connection_failed";
|
|
1495
1819
|
}
|
|
1496
1820
|
|
|
1821
|
+
function assertNativeAtlassianAvailable(): void {
|
|
1822
|
+
throw new HTTPException(410, { message: ATLASSIAN_NATIVE_RETIRED_MESSAGE });
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1497
1825
|
class AtlassianCallbackError extends Error {
|
|
1498
1826
|
constructor(readonly reason: string) {
|
|
1499
1827
|
super(reason);
|
|
@@ -1545,7 +1873,12 @@ function cqlString(value: string): string {
|
|
|
1545
1873
|
|
|
1546
1874
|
async function readJiraLiveComments(
|
|
1547
1875
|
deps: ApiRouteDeps,
|
|
1548
|
-
input:
|
|
1876
|
+
input: AtlassianRequestAuthority & {
|
|
1877
|
+
workspaceId: string;
|
|
1878
|
+
subjectId: string;
|
|
1879
|
+
connectionId: string;
|
|
1880
|
+
id: string;
|
|
1881
|
+
},
|
|
1549
1882
|
cloudId: string,
|
|
1550
1883
|
) {
|
|
1551
1884
|
const comments: Array<{ author: string | null; createdAt: string | null; body: string }> = [];
|
|
@@ -1578,7 +1911,12 @@ async function readJiraLiveComments(
|
|
|
1578
1911
|
|
|
1579
1912
|
async function readConfluenceLiveComments(
|
|
1580
1913
|
deps: ApiRouteDeps,
|
|
1581
|
-
input:
|
|
1914
|
+
input: AtlassianRequestAuthority & {
|
|
1915
|
+
workspaceId: string;
|
|
1916
|
+
subjectId: string;
|
|
1917
|
+
connectionId: string;
|
|
1918
|
+
id: string;
|
|
1919
|
+
},
|
|
1582
1920
|
cloudId: string,
|
|
1583
1921
|
) {
|
|
1584
1922
|
const comments: Array<{ createdAt: string | null; content: string }> = [];
|