@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
package/src/routes/files.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { readSessionAttachmentFiles } from "@opengeni/core";
|
|
2
|
+
import { ZodError } from "zod";
|
|
1
3
|
import {
|
|
2
4
|
CompleteFileUploadResponse,
|
|
3
5
|
CreateFileUploadRequest,
|
|
4
6
|
CreateFileUploadResponse,
|
|
5
7
|
FileAsset,
|
|
8
|
+
FileListRequest,
|
|
9
|
+
FileListResponse,
|
|
6
10
|
FileDownloadUrlResponse,
|
|
7
11
|
RETAINED_OUTPUT_DEFAULT_PAGE_BYTES,
|
|
8
12
|
RETAINED_OUTPUT_MAX_PAGE_BYTES,
|
|
@@ -11,20 +15,24 @@ import {
|
|
|
11
15
|
retainedGeneratedImageReferenceFromFile,
|
|
12
16
|
retainedGeneratedVideoReferenceFromFile,
|
|
13
17
|
retainedScreenshotReferenceFromFile,
|
|
18
|
+
retainedSessionScreenshotKindFromObjectKey,
|
|
14
19
|
VideoArtifactPlaybackSource,
|
|
15
20
|
resolveRetainedOutputRange,
|
|
16
21
|
type RetainedArtifactMetadata,
|
|
17
22
|
type RetainedOutputUnavailableReason,
|
|
18
23
|
} from "@opengeni/contracts";
|
|
19
24
|
import {
|
|
25
|
+
withSessionRlsActorContext,
|
|
20
26
|
claimFileUploadCleanup,
|
|
21
27
|
completeFileUploadCleanup,
|
|
22
28
|
completeFileUpload,
|
|
23
29
|
createFileUpload,
|
|
24
30
|
getFileUpload,
|
|
25
31
|
getGeneratedImageArtifact,
|
|
32
|
+
recordAuditEvent,
|
|
26
33
|
getGeneratedVideoArtifact,
|
|
27
34
|
getFilesForSubject,
|
|
35
|
+
listFilesForSubject,
|
|
28
36
|
getRetainedFileArtifact,
|
|
29
37
|
getRetainedScreenshotArtifact,
|
|
30
38
|
requireFileForSubject,
|
|
@@ -36,27 +44,110 @@ import {
|
|
|
36
44
|
import type { Context, Hono } from "hono";
|
|
37
45
|
import { HTTPException } from "hono/http-exception";
|
|
38
46
|
import {
|
|
47
|
+
fileOwnerContextForAccess,
|
|
48
|
+
requireAccessGrantAuthorization,
|
|
49
|
+
grantHasAgentAttemptAuthority,
|
|
39
50
|
requireAccessGrant,
|
|
40
51
|
requireLiveAgentAttemptAuthorization,
|
|
52
|
+
requireSessionAuthorization,
|
|
41
53
|
SessionAuthorizationDeniedError,
|
|
54
|
+
SessionAuthorizationUnavailableError,
|
|
42
55
|
} from "@opengeni/core";
|
|
43
56
|
import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
|
|
44
57
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
58
|
+
import { retryWhileMissing } from "@opengeni/storage";
|
|
45
59
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
46
60
|
import { buildFilesMcpServer } from "../mcp/files";
|
|
61
|
+
import { userContentResponseHeaders, userContentSignedGetUrlOptions } from "../http/user-content";
|
|
62
|
+
import { mcpOAuthAuthenticateHeader, resolveMcpOAuthRouteAccess } from "../mcp-oauth";
|
|
63
|
+
import { withAccessGrantSessionRlsContext } from "../access-grant-rls";
|
|
64
|
+
import {
|
|
65
|
+
buildWorkspaceToolGatewayMcpServer,
|
|
66
|
+
prepareMcpOAuthWorkspaceToolGateway,
|
|
67
|
+
} from "../workspace-tool-gateway";
|
|
68
|
+
import { parseRequestJson } from "../http/request-body";
|
|
47
69
|
|
|
48
70
|
export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
49
71
|
const { db, objectStorage } = deps;
|
|
50
72
|
|
|
73
|
+
const fileRequestAuthority = new WeakMap<Request, { owner: string | null; agent: boolean }>();
|
|
74
|
+
// This scope covers metadata, original bytes, range reads and finalization.
|
|
75
|
+
// A service/agent subject string alone cannot unlock a personal attachment.
|
|
76
|
+
for (const path of [
|
|
77
|
+
"/v1/workspaces/:workspaceId/files",
|
|
78
|
+
"/v1/workspaces/:workspaceId/files/*",
|
|
79
|
+
"/v1/workspaces/:workspaceId/artifacts/*",
|
|
80
|
+
"/v1/workspaces/:workspaceId/sessions/:sessionId/artifacts/*",
|
|
81
|
+
]) {
|
|
82
|
+
app.use(path, async (c, next) => {
|
|
83
|
+
const permission = c.req.path.includes("/files/uploads") ? "files:upload" : "files:read";
|
|
84
|
+
const access = await requireAccessGrantAuthorization(
|
|
85
|
+
c,
|
|
86
|
+
deps,
|
|
87
|
+
c.req.param("workspaceId") ?? "",
|
|
88
|
+
permission,
|
|
89
|
+
);
|
|
90
|
+
const actor = await fileOwnerContextForAccess(deps, access, permission);
|
|
91
|
+
fileRequestAuthority.set(c.req.raw, {
|
|
92
|
+
owner: actor.privateFileOwnerSubjectId ?? null,
|
|
93
|
+
agent: access.grant.principalKind === "agent_attempt",
|
|
94
|
+
});
|
|
95
|
+
return withSessionRlsActorContext(actor, next);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
51
99
|
app.all("/v1/workspaces/:workspaceId/mcp/files", async (c) => {
|
|
52
100
|
const workspaceId = c.req.param("workspaceId");
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
101
|
+
let oauthAccess: Awaited<ReturnType<typeof resolveMcpOAuthRouteAccess>>;
|
|
102
|
+
try {
|
|
103
|
+
oauthAccess = await resolveMcpOAuthRouteAccess(deps, c.req.raw, workspaceId);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error instanceof HTTPException && error.status === 401) {
|
|
106
|
+
c.header("www-authenticate", mcpOAuthAuthenticateHeader(deps, new URL(c.req.url).pathname));
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
if (oauthAccess) {
|
|
111
|
+
return await withAccessGrantSessionRlsContext(deps, oauthAccess.grant, async () => {
|
|
112
|
+
const prepared = await prepareMcpOAuthWorkspaceToolGateway(
|
|
113
|
+
deps,
|
|
114
|
+
oauthAccess.grant,
|
|
115
|
+
oauthAccess.allowedToolIdentities,
|
|
116
|
+
);
|
|
117
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
118
|
+
enableJsonResponse: true,
|
|
119
|
+
});
|
|
120
|
+
const server = buildWorkspaceToolGatewayMcpServer(
|
|
121
|
+
prepared,
|
|
122
|
+
oauthAccess.grant,
|
|
123
|
+
deps.observability,
|
|
124
|
+
);
|
|
125
|
+
try {
|
|
126
|
+
await server.connect(transport);
|
|
127
|
+
return await transport.handleRequest(c.req.raw);
|
|
128
|
+
} finally {
|
|
129
|
+
await Promise.allSettled([server.close(), prepared.close()]);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
let access: Awaited<ReturnType<typeof requireAccessGrantAuthorization>>;
|
|
134
|
+
try {
|
|
135
|
+
access = await requireAccessGrantAuthorization(c, deps, workspaceId, "files:read");
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (deps.settings.mcpOauthEnabled && error instanceof HTTPException && error.status === 401) {
|
|
138
|
+
c.header("www-authenticate", mcpOAuthAuthenticateHeader(deps, new URL(c.req.url).pathname));
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
const { grant } = access;
|
|
143
|
+
return await withAccessGrantSessionRlsContext(deps, grant, async () => {
|
|
144
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
145
|
+
enableJsonResponse: true,
|
|
146
|
+
});
|
|
147
|
+
const server = buildFilesMcpServer(deps, access);
|
|
148
|
+
await server.connect(transport);
|
|
149
|
+
return await transport.handleRequest(c.req.raw);
|
|
56
150
|
});
|
|
57
|
-
const server = buildFilesMcpServer(deps, grant);
|
|
58
|
-
await server.connect(transport);
|
|
59
|
-
return await transport.handleRequest(c.req.raw);
|
|
60
151
|
});
|
|
61
152
|
|
|
62
153
|
app.post("/v1/workspaces/:workspaceId/files/uploads", async (c) => {
|
|
@@ -67,7 +158,13 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
67
158
|
message: "object storage is not configured",
|
|
68
159
|
});
|
|
69
160
|
}
|
|
70
|
-
const payload =
|
|
161
|
+
const payload = await parseRequestJson(c, CreateFileUploadRequest);
|
|
162
|
+
const privateOwner = fileRequestAuthority.get(c.req.raw)?.owner ?? null;
|
|
163
|
+
const personal =
|
|
164
|
+
payload.scope === "personal" ||
|
|
165
|
+
(fileRequestAuthority.get(c.req.raw)?.agent && privateOwner !== null);
|
|
166
|
+
if (personal && !privateOwner)
|
|
167
|
+
throw new HTTPException(403, { message: "Personal uploads require the authenticated owner" });
|
|
71
168
|
await requireLimit(deps, {
|
|
72
169
|
accountId: grant.accountId,
|
|
73
170
|
workspaceId,
|
|
@@ -91,6 +188,7 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
91
188
|
accountId: grant.accountId,
|
|
92
189
|
workspaceId,
|
|
93
190
|
fileId,
|
|
191
|
+
privateOwnerSubjectId: personal ? privateOwner : null,
|
|
94
192
|
filename: payload.filename,
|
|
95
193
|
safeFilename,
|
|
96
194
|
contentType: payload.contentType,
|
|
@@ -100,6 +198,23 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
100
198
|
objectKey,
|
|
101
199
|
expiresAt: signed.expiresAt,
|
|
102
200
|
});
|
|
201
|
+
// Every principal-facing signed URL issuance is a
|
|
202
|
+
// metadata-only audit fact. Awaited before the URL leaves the platform:
|
|
203
|
+
// no recorded fact, no bearer capability. Never the URL or object key.
|
|
204
|
+
await recordAuditEvent(db, {
|
|
205
|
+
accountId: grant.accountId,
|
|
206
|
+
workspaceId,
|
|
207
|
+
subjectId: grant.subjectId,
|
|
208
|
+
action: "file.signed_upload.issued",
|
|
209
|
+
targetType: "workspace_file",
|
|
210
|
+
targetId: fileId,
|
|
211
|
+
metadata: {
|
|
212
|
+
fileId,
|
|
213
|
+
contentType: payload.contentType,
|
|
214
|
+
sizeBytes: payload.sizeBytes,
|
|
215
|
+
expiresAt: signed.expiresAt.toISOString(),
|
|
216
|
+
},
|
|
217
|
+
});
|
|
103
218
|
return c.json(
|
|
104
219
|
CreateFileUploadResponse.parse({
|
|
105
220
|
fileId: upload.file.id,
|
|
@@ -231,11 +346,13 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
231
346
|
const file = await rejectAndCleanObject(409, "file upload has expired", "expired");
|
|
232
347
|
return c.json(CompleteFileUploadResponse.parse({ file }));
|
|
233
348
|
}
|
|
234
|
-
const head = await
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
});
|
|
349
|
+
const head = await retryWhileMissing(async () => {
|
|
350
|
+
if (!(await objectStorage.fileExists(upload.file))) return null;
|
|
351
|
+
return await objectStorage.headFile(upload.file);
|
|
238
352
|
});
|
|
353
|
+
if (!head) {
|
|
354
|
+
throw new HTTPException(409, { message: "uploaded object is not available" });
|
|
355
|
+
}
|
|
239
356
|
if (Number(head.ContentLength ?? -1) !== upload.file.sizeBytes) {
|
|
240
357
|
const file = await rejectAndCleanObject(
|
|
241
358
|
422,
|
|
@@ -268,15 +385,53 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
268
385
|
return c.json(CompleteFileUploadResponse.parse({ file }));
|
|
269
386
|
});
|
|
270
387
|
|
|
271
|
-
app.get("/v1/workspaces/:workspaceId/files
|
|
388
|
+
app.get("/v1/workspaces/:workspaceId/files", async (c) => {
|
|
272
389
|
const workspaceId = c.req.param("workspaceId");
|
|
273
390
|
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
274
|
-
const
|
|
391
|
+
const parsed = FileListRequest.safeParse({
|
|
392
|
+
...c.req.query(),
|
|
393
|
+
...(c.req.query("limit") !== undefined ? { limit: Number(c.req.query("limit")) } : {}),
|
|
394
|
+
});
|
|
395
|
+
if (!parsed.success) throw new HTTPException(422, { message: "Invalid file list request" });
|
|
396
|
+
c.header("cache-control", "private, no-store");
|
|
397
|
+
return c.json(
|
|
398
|
+
FileListResponse.parse(
|
|
399
|
+
await listFilesForSubject(db, {
|
|
400
|
+
accountId: grant.accountId,
|
|
401
|
+
workspaceId,
|
|
402
|
+
subjectId: grant.subjectId,
|
|
403
|
+
...parsed.data,
|
|
404
|
+
}).catch((error: unknown) => {
|
|
405
|
+
if (error instanceof ZodError)
|
|
406
|
+
throw new HTTPException(422, { message: "Invalid file cursor" });
|
|
407
|
+
throw error;
|
|
408
|
+
}),
|
|
409
|
+
),
|
|
410
|
+
);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const readRequestedFile = async (
|
|
414
|
+
c: Context,
|
|
415
|
+
grant: Awaited<ReturnType<typeof requireAccessGrant>>,
|
|
416
|
+
) => {
|
|
417
|
+
const fileId = c.req.param("fileId") ?? "";
|
|
418
|
+
const sessionId = c.req.query("sessionId");
|
|
419
|
+
if (sessionId) {
|
|
420
|
+
if (!/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/iu.test(sessionId)) return null;
|
|
421
|
+
return (await readSessionAttachmentFiles(deps, grant, sessionId, [fileId]))[0] ?? null;
|
|
422
|
+
}
|
|
423
|
+
return requireFileForSubject(db, {
|
|
275
424
|
accountId: grant.accountId,
|
|
276
|
-
workspaceId,
|
|
425
|
+
workspaceId: grant.workspaceId,
|
|
277
426
|
subjectId: grant.subjectId,
|
|
278
|
-
fileId
|
|
427
|
+
fileId,
|
|
279
428
|
}).catch(() => null);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
app.get("/v1/workspaces/:workspaceId/files/:fileId", async (c) => {
|
|
432
|
+
const workspaceId = c.req.param("workspaceId");
|
|
433
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
434
|
+
const file = await readRequestedFile(c, grant).catch(() => null);
|
|
280
435
|
if (!file) {
|
|
281
436
|
throw new HTTPException(404, { message: "file not found" });
|
|
282
437
|
}
|
|
@@ -324,7 +479,7 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
324
479
|
|
|
325
480
|
app.post("/v1/workspaces/:workspaceId/artifacts/:artifactId/playback-source", async (c) => {
|
|
326
481
|
const workspaceId = c.req.param("workspaceId");
|
|
327
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
482
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
328
483
|
if (!objectStorage) {
|
|
329
484
|
throw new HTTPException(503, { message: "object storage is not configured" });
|
|
330
485
|
}
|
|
@@ -337,10 +492,29 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
337
492
|
throw new HTTPException(409, { message: `generated video is ${retained.file.status}` });
|
|
338
493
|
}
|
|
339
494
|
const file = generatedVideoFileAsset(retained.file);
|
|
340
|
-
|
|
495
|
+
const videoPresent = await retryWhileMissing(async () =>
|
|
496
|
+
(await objectStorage.fileExists(file)) ? true : null,
|
|
497
|
+
);
|
|
498
|
+
if (!videoPresent) {
|
|
341
499
|
throw new HTTPException(410, { message: "generated video bytes are unavailable" });
|
|
342
500
|
}
|
|
343
|
-
const signed = await objectStorage.createGetUrl({
|
|
501
|
+
const signed = await objectStorage.createGetUrl({
|
|
502
|
+
key: file.objectKey,
|
|
503
|
+
...userContentSignedGetUrlOptions(file.contentType, file.filename),
|
|
504
|
+
});
|
|
505
|
+
await recordAuditEvent(db, {
|
|
506
|
+
accountId: grant.accountId,
|
|
507
|
+
workspaceId,
|
|
508
|
+
subjectId: grant.subjectId,
|
|
509
|
+
action: "file.signed_url.issued",
|
|
510
|
+
targetType: "retained_artifact",
|
|
511
|
+
targetId: artifactId,
|
|
512
|
+
metadata: {
|
|
513
|
+
artifactId,
|
|
514
|
+
kind: "video_playback",
|
|
515
|
+
expiresAt: signed.expiresAt.toISOString(),
|
|
516
|
+
},
|
|
517
|
+
});
|
|
344
518
|
return c.json(
|
|
345
519
|
VideoArtifactPlaybackSource.parse({
|
|
346
520
|
schemaVersion: 1,
|
|
@@ -355,9 +529,40 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
355
529
|
);
|
|
356
530
|
});
|
|
357
531
|
|
|
532
|
+
// These two retained-screenshot routes are registered before the session
|
|
533
|
+
// route module, so its `/sessions/:sessionId/*` authorization middleware
|
|
534
|
+
// does not cover them: an agent attempt must pass the same core seam here
|
|
535
|
+
// (private sessions, Slack ownership, and the 0426 agent-access scope) so a
|
|
536
|
+
// known artifact id cannot read into a tree the attempt may not reach.
|
|
537
|
+
const requireAgentSessionAccess = async (
|
|
538
|
+
c: Context,
|
|
539
|
+
grant: Awaited<ReturnType<typeof requireAccessGrant>>,
|
|
540
|
+
): Promise<void> => {
|
|
541
|
+
if (!grantHasAgentAttemptAuthority(grant)) return;
|
|
542
|
+
try {
|
|
543
|
+
await requireSessionAuthorization(deps, grant, {
|
|
544
|
+
sessionId: c.req.param("sessionId") ?? "",
|
|
545
|
+
operation: "session.read",
|
|
546
|
+
surface: "http",
|
|
547
|
+
});
|
|
548
|
+
} catch (error) {
|
|
549
|
+
if (error instanceof SessionAuthorizationDeniedError) {
|
|
550
|
+
throw new HTTPException(404, { message: "session not found", cause: error });
|
|
551
|
+
}
|
|
552
|
+
if (error instanceof SessionAuthorizationUnavailableError) {
|
|
553
|
+
throw new HTTPException(503, {
|
|
554
|
+
message: "session authorization is unavailable",
|
|
555
|
+
cause: error,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
throw error;
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
358
562
|
app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/artifacts/:artifactId", async (c) => {
|
|
359
563
|
const workspaceId = c.req.param("workspaceId");
|
|
360
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
564
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
565
|
+
await requireAgentSessionAccess(c, grant);
|
|
361
566
|
const artifactId = retainedArtifactId(c.req.param("artifactId"));
|
|
362
567
|
const artifact = await getRetainedScreenshotArtifact(
|
|
363
568
|
db,
|
|
@@ -375,7 +580,8 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
375
580
|
"/v1/workspaces/:workspaceId/sessions/:sessionId/artifacts/:artifactId/content",
|
|
376
581
|
async (c) => {
|
|
377
582
|
const workspaceId = c.req.param("workspaceId");
|
|
378
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
583
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
584
|
+
await requireAgentSessionAccess(c, grant);
|
|
379
585
|
const artifactId = retainedArtifactId(c.req.param("artifactId"));
|
|
380
586
|
const artifact = await getRetainedScreenshotArtifact(
|
|
381
587
|
db,
|
|
@@ -404,19 +610,36 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
404
610
|
message: "object storage is not configured",
|
|
405
611
|
});
|
|
406
612
|
}
|
|
407
|
-
const file = await
|
|
408
|
-
accountId: grant.accountId,
|
|
409
|
-
workspaceId,
|
|
410
|
-
subjectId: grant.subjectId,
|
|
411
|
-
fileId: c.req.param("fileId"),
|
|
412
|
-
}).catch(() => null);
|
|
613
|
+
const file = await readRequestedFile(c, grant).catch(() => null);
|
|
413
614
|
if (!file) {
|
|
414
615
|
throw new HTTPException(404, { message: "file not found" });
|
|
415
616
|
}
|
|
416
617
|
if (file.status !== "ready") {
|
|
417
618
|
throw new HTTPException(409, { message: `file is ${file.status}` });
|
|
418
619
|
}
|
|
419
|
-
const
|
|
620
|
+
const present = await retryWhileMissing(async () =>
|
|
621
|
+
(await objectStorage.fileExists(file)) ? true : null,
|
|
622
|
+
);
|
|
623
|
+
if (!present) {
|
|
624
|
+
throw new HTTPException(410, { message: "file bytes are unavailable" });
|
|
625
|
+
}
|
|
626
|
+
const signed = await objectStorage.createGetUrl({
|
|
627
|
+
key: file.objectKey,
|
|
628
|
+
...userContentSignedGetUrlOptions(file.contentType, file.filename),
|
|
629
|
+
});
|
|
630
|
+
await recordAuditEvent(db, {
|
|
631
|
+
accountId: grant.accountId,
|
|
632
|
+
workspaceId,
|
|
633
|
+
subjectId: grant.subjectId,
|
|
634
|
+
action: "file.signed_url.issued",
|
|
635
|
+
targetType: "workspace_file",
|
|
636
|
+
targetId: file.id,
|
|
637
|
+
metadata: {
|
|
638
|
+
fileId: file.id,
|
|
639
|
+
kind: "download",
|
|
640
|
+
expiresAt: signed.expiresAt.toISOString(),
|
|
641
|
+
},
|
|
642
|
+
});
|
|
420
643
|
return c.json(
|
|
421
644
|
FileDownloadUrlResponse.parse({
|
|
422
645
|
url: signed.url,
|
|
@@ -471,25 +694,33 @@ async function serveRetainedArtifactContent(
|
|
|
471
694
|
);
|
|
472
695
|
}
|
|
473
696
|
|
|
697
|
+
// Stored bytes and their content type are user- or agent-chosen; this origin
|
|
698
|
+
// also serves the console, so the response is a sandboxed, non-embeddable
|
|
699
|
+
// document and active markup downloads instead of rendering.
|
|
474
700
|
const headers = {
|
|
475
701
|
"Accept-Ranges": range.acceptRanges,
|
|
476
702
|
"Cache-Control": "private, no-store",
|
|
477
703
|
"Content-Length": String(range.length),
|
|
478
704
|
"Content-Type": metadata.contentType,
|
|
479
|
-
|
|
705
|
+
...userContentResponseHeaders(metadata.contentType, file.filename),
|
|
480
706
|
...(range.contentRange ? { "Content-Range": range.contentRange } : {}),
|
|
481
707
|
};
|
|
482
708
|
if (range.kind === "empty") {
|
|
483
|
-
|
|
709
|
+
const present = await retryWhileMissing(async () =>
|
|
710
|
+
(await objectStorage.fileExists(file)) ? true : null,
|
|
711
|
+
);
|
|
712
|
+
if (!present) {
|
|
484
713
|
return c.json(retainedArtifactUnavailable(artifactId, "missing_storage"), 410);
|
|
485
714
|
}
|
|
486
715
|
return c.body(null, 200, headers);
|
|
487
716
|
}
|
|
488
717
|
|
|
489
|
-
const bytes = await
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
718
|
+
const bytes = await retryWhileMissing(async () =>
|
|
719
|
+
objectStorage.getFileRange(file, {
|
|
720
|
+
start: range.start,
|
|
721
|
+
end: range.end,
|
|
722
|
+
}),
|
|
723
|
+
);
|
|
493
724
|
if (!bytes) {
|
|
494
725
|
return c.json(retainedArtifactUnavailable(artifactId, "missing_storage"), 410);
|
|
495
726
|
}
|
|
@@ -669,12 +900,15 @@ function retainedScreenshotMetadata(
|
|
|
669
900
|
artifact.sessionId &&
|
|
670
901
|
artifact.retentionExpiresAt.getTime() > Date.now()
|
|
671
902
|
) {
|
|
903
|
+
const kind = retainedSessionScreenshotKindFromObjectKey(artifact.file.objectKey);
|
|
904
|
+
if (!kind) return retainedArtifactUnavailable(artifact.artifactId, "invalid_content");
|
|
672
905
|
const reference = retainedScreenshotReferenceFromFile({
|
|
673
906
|
...artifact.file,
|
|
674
907
|
sessionId: artifact.sessionId,
|
|
675
908
|
width: artifact.width,
|
|
676
909
|
height: artifact.height,
|
|
677
910
|
expiresAt: artifact.retentionExpiresAt.toISOString(),
|
|
911
|
+
kind,
|
|
678
912
|
});
|
|
679
913
|
if (reference) return reference;
|
|
680
914
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { GitHubInstallationBindingCandidate } from "@opengeni/contracts";
|
|
2
|
+
|
|
3
|
+
// GitHub redirects can land on an API-only host. Keep these pages self-contained
|
|
4
|
+
// while using the same dark palette and typography as @opengeni/react.
|
|
5
|
+
const pageStyle = `
|
|
6
|
+
:root{color-scheme:dark;--bg:oklch(.155 .012 260);--surface:oklch(.19 .014 260);--raised:oklch(.225 .015 260);--border:oklch(.3 .014 260);--border-strong:oklch(.42 .016 260);--fg:oklch(.955 .005 260);--muted:oklch(.73 .012 260);--blue:oklch(.54 .18 255);--blue-light:oklch(.72 .15 255)}
|
|
7
|
+
*{box-sizing:border-box}
|
|
8
|
+
html{min-height:100%;background:var(--bg)}
|
|
9
|
+
body{margin:0;min-height:100vh;min-height:100dvh;color:var(--fg);background:var(--bg);font-family:"Inter Variable",Inter,ui-sans-serif,system-ui,sans-serif;font-feature-settings:"cv11","ss01","ss03";-webkit-font-smoothing:antialiased}
|
|
10
|
+
.shell{width:min(100% - 48px,700px);margin:0 auto}
|
|
11
|
+
header{height:84px;display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid var(--border)}
|
|
12
|
+
.brand{font-size:16px;font-weight:670;letter-spacing:-.045em;color:var(--fg);text-decoration:none}
|
|
13
|
+
.context{font-size:12px;color:var(--muted)}
|
|
14
|
+
main{padding:clamp(48px,10vh,100px) 0 96px}
|
|
15
|
+
h1{margin:0 0 14px;font-size:clamp(27px,4vw,36px);font-weight:640;letter-spacing:-.05em;line-height:1.17}
|
|
16
|
+
p{margin:0;color:var(--muted);font-size:14px;line-height:1.65;max-width:59ch}
|
|
17
|
+
.intro{margin-bottom:32px}
|
|
18
|
+
.options{display:grid;gap:10px;margin:0 0 24px;padding:0;border:0;max-height:min(390px,52vh);overflow:auto}
|
|
19
|
+
.option{display:flex;gap:14px;align-items:center;min-height:70px;padding:14px 16px;border:1px solid var(--border);border-radius:10px;background:var(--surface);cursor:pointer;transition:background .15s,border-color .15s}
|
|
20
|
+
.option:hover{background:var(--raised)}
|
|
21
|
+
.option:has(input:checked){border-color:var(--blue-light);background:var(--raised)}
|
|
22
|
+
.option:has(input:focus-visible){outline:2px solid var(--blue-light);outline-offset:3px}
|
|
23
|
+
.option input{margin:0;flex:none;width:17px;height:17px;accent-color:var(--blue-light)}
|
|
24
|
+
.option-text{min-width:0;display:grid;gap:3px}
|
|
25
|
+
.option strong{overflow-wrap:anywhere;font-size:14px;font-weight:600}
|
|
26
|
+
.option small{font-size:12px;color:var(--muted)}
|
|
27
|
+
.actions{display:flex;align-items:center;flex-wrap:wrap;gap:12px;margin-top:24px}
|
|
28
|
+
.button{display:inline-flex;min-height:42px;align-items:center;justify-content:center;padding:0 17px;border:1px solid var(--blue);border-radius:8px;background:var(--blue);color:#fff;font-family:inherit;font-size:13px;font-weight:600;text-decoration:none;cursor:pointer}
|
|
29
|
+
.button:hover{background:oklch(.6 .18 255)}
|
|
30
|
+
.button:disabled{cursor:wait;opacity:.7}
|
|
31
|
+
.secondary{border-color:var(--border);background:transparent;color:var(--fg)}
|
|
32
|
+
.secondary:hover{background:var(--raised)}
|
|
33
|
+
:is(button,a):focus-visible{outline:2px solid var(--blue-light);outline-offset:3px}
|
|
34
|
+
.note{margin-top:30px;padding-top:20px;border-top:1px solid var(--border);font-size:12px}
|
|
35
|
+
.env-header{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-top:36px}
|
|
36
|
+
h2{margin:0;font-size:14px;font-weight:600}
|
|
37
|
+
pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:380px;overflow:auto;padding:18px;border:1px solid var(--border);border-radius:10px;background:var(--surface);font:12px/1.6 ui-monospace,SFMono-Regular,Menlo,monospace}
|
|
38
|
+
@media(max-width:480px){.shell{width:calc(100% - 32px)}header{height:70px}main{padding-top:56px}.actions{align-items:stretch;flex-direction:column}.actions form,.actions .button{width:100%}}
|
|
39
|
+
@media(prefers-reduced-motion:reduce){.option{transition:none}}
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
function page(title: string, content: string): string {
|
|
43
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeHtml(title)} · Opengeni</title><style>${pageStyle}</style></head><body><div class="shell"><header><span class="brand">Opengeni</span><span class="context">GitHub connection</span></header><main>${content}</main></div></body></html>`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function githubInstallationChooserHtml(
|
|
47
|
+
candidates: GitHubInstallationBindingCandidate[],
|
|
48
|
+
state: string,
|
|
49
|
+
workspaceId: string,
|
|
50
|
+
baseUrl: string,
|
|
51
|
+
): string {
|
|
52
|
+
const action = `${baseUrl}/v1/workspaces/${encodeURIComponent(workspaceId)}/github/installations/select`;
|
|
53
|
+
const options = candidates
|
|
54
|
+
.map(({ installation, authorityKind }, index) => {
|
|
55
|
+
const account = escapeHtml(
|
|
56
|
+
installation.accountLogin ?? `installation ${installation.installationId}`,
|
|
57
|
+
);
|
|
58
|
+
const label = authorityKind === "personal_owner" ? "Personal account" : "Organization owner";
|
|
59
|
+
return `<label class="option"><input type="radio" name="installation_id" value="${installation.installationId}" required${candidates.length === 1 && index === 0 ? " checked" : ""}><span class="option-text"><strong>${account}</strong><small>${label}</small></span></label>`;
|
|
60
|
+
})
|
|
61
|
+
.join("");
|
|
62
|
+
return page(
|
|
63
|
+
"Choose a GitHub account",
|
|
64
|
+
`<h1>Choose a GitHub account</h1><p class="intro">Connect an account where the Opengeni GitHub App is already installed, or install it on another account.</p><form id="existing-account" method="get" action="${escapeHtml(action)}"><input type="hidden" name="state" value="${escapeHtml(state)}"><fieldset class="options" aria-label="Available GitHub accounts">${options}</fieldset></form><div class="actions"><button class="button" type="submit" form="existing-account">Connect selected account</button><form method="get" action="${escapeHtml(action)}"><input type="hidden" name="state" value="${escapeHtml(state)}"><input type="hidden" name="installation_id" value="new"><button class="button secondary" type="submit">Install on another account</button></form></div><p class="note">Only accounts GitHub has confirmed you own or manage appear here.</p>`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function githubSetupSuccessHtml(account: string, returnUrl: string): string {
|
|
69
|
+
return page(
|
|
70
|
+
"GitHub connected",
|
|
71
|
+
`<h1>GitHub connected</h1><p class="intro"><strong>${escapeHtml(account)}</strong> is now available in this Opengeni workspace. Opengeni can access only the repositories allowed for this installation.</p><a class="button" href="${escapeHtml(returnUrl)}">Back to Opengeni</a>`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function githubSetupPendingHtml(): string {
|
|
76
|
+
return page(
|
|
77
|
+
"GitHub approval pending",
|
|
78
|
+
`<h1>Waiting for an organization owner</h1><p>An owner needs to approve your GitHub App request before you can connect. Opengeni has not created a workspace binding.</p>`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Why a GitHub browser step could not continue. Browser navigation (a stale
|
|
84
|
+
* page-load link, GitHub's Cancel button, a non-owner) must land on a readable
|
|
85
|
+
* page with a way back, never on a raw JSON error body.
|
|
86
|
+
*/
|
|
87
|
+
export type GitHubConnectFailure =
|
|
88
|
+
| "expired"
|
|
89
|
+
| "cancelled"
|
|
90
|
+
| "not_owner"
|
|
91
|
+
| "forbidden"
|
|
92
|
+
| "policy_denied"
|
|
93
|
+
| "signed_out"
|
|
94
|
+
| "failed";
|
|
95
|
+
|
|
96
|
+
const GITHUB_CONNECT_FAILURE_COPY: Record<GitHubConnectFailure, { title: string; body: string }> = {
|
|
97
|
+
expired: {
|
|
98
|
+
title: "This GitHub link expired",
|
|
99
|
+
body: "GitHub connection links stay valid for 10 minutes, and each one works once. Go back to Opengeni and select Connect again to get a fresh link.",
|
|
100
|
+
},
|
|
101
|
+
cancelled: {
|
|
102
|
+
title: "GitHub connection cancelled",
|
|
103
|
+
body: "You cancelled on GitHub, so nothing was connected. Go back to Opengeni and select Connect when you are ready.",
|
|
104
|
+
},
|
|
105
|
+
not_owner: {
|
|
106
|
+
title: "An owner needs to connect this account",
|
|
107
|
+
body: "Only the owner of the GitHub account, or an owner of the GitHub organization, can connect it to Opengeni. Ask an owner to connect it, or install the app on an account you own.",
|
|
108
|
+
},
|
|
109
|
+
forbidden: {
|
|
110
|
+
title: "You can't manage GitHub here",
|
|
111
|
+
body: "Your Opengeni access doesn't allow connecting GitHub for this workspace. Ask a workspace admin to connect it.",
|
|
112
|
+
},
|
|
113
|
+
policy_denied: {
|
|
114
|
+
title: "GitHub is turned off for your organization",
|
|
115
|
+
body: "Your organization's integration policy doesn't allow connecting GitHub. Ask an organization admin to allow it, then select Connect again.",
|
|
116
|
+
},
|
|
117
|
+
signed_out: {
|
|
118
|
+
title: "Sign in to continue",
|
|
119
|
+
body: "Your Opengeni sign-in wasn't available when GitHub sent you back. Sign in to Opengeni and select Connect again.",
|
|
120
|
+
},
|
|
121
|
+
failed: {
|
|
122
|
+
title: "GitHub couldn't finish connecting",
|
|
123
|
+
body: "Nothing was connected. Go back to Opengeni and select Connect to try again.",
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export function githubConnectFailureHtml(
|
|
128
|
+
failure: GitHubConnectFailure,
|
|
129
|
+
returnUrl: string,
|
|
130
|
+
detail?: string | null,
|
|
131
|
+
): string {
|
|
132
|
+
const copy = GITHUB_CONNECT_FAILURE_COPY[failure];
|
|
133
|
+
const note = detail?.trim()
|
|
134
|
+
? `<p class="note">Details: ${escapeHtml(detail.trim().slice(0, 300))}</p>`
|
|
135
|
+
: "";
|
|
136
|
+
return page(
|
|
137
|
+
copy.title,
|
|
138
|
+
`<h1>${escapeHtml(copy.title)}</h1><p class="intro">${escapeHtml(copy.body)}</p><a class="button" href="${escapeHtml(returnUrl)}">Back to Opengeni</a>${note}`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function githubSuccessHtml(envLines: string[]): string {
|
|
143
|
+
const escaped = escapeHtml(envLines.join("\n"));
|
|
144
|
+
return page(
|
|
145
|
+
"GitHub App created",
|
|
146
|
+
`<h1>GitHub App created</h1><p>Add these values to .env, then restart API and worker.</p><div class="env-header"><h2>Environment variables</h2><button class="button secondary" id="copy-env" type="button">Copy env</button></div><pre id="env-lines">${escaped}</pre><script>(()=>{const button=document.getElementById("copy-env");const env=document.getElementById("env-lines");async function copyText(text){if(navigator.clipboard&&window.isSecureContext){await navigator.clipboard.writeText(text);return;}const area=document.createElement("textarea");area.value=text;area.setAttribute("readonly","");area.style.position="fixed";area.style.inset="-9999px";document.body.append(area);area.select();document.execCommand("copy");area.remove();}button?.addEventListener("click",async()=>{try{await copyText(env?.textContent||"");button.textContent="Copied";setTimeout(()=>button.textContent="Copy env",1600);}catch{button.textContent="Copy failed";setTimeout(()=>button.textContent="Copy env",2200);}});})();</script>`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function escapeHtml(value: string): string {
|
|
151
|
+
return value.replace(
|
|
152
|
+
/[&<>"']/g,
|
|
153
|
+
(char) =>
|
|
154
|
+
({
|
|
155
|
+
"&": "&",
|
|
156
|
+
"<": "<",
|
|
157
|
+
">": ">",
|
|
158
|
+
'"': """,
|
|
159
|
+
"'": "'",
|
|
160
|
+
})[char] ?? char,
|
|
161
|
+
);
|
|
162
|
+
}
|