@opengeni/api-router 0.30.1 → 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-office-import.d.ts +7 -4
- 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 +1129 -166
- 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 +25 -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 +113 -10
- 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 +71 -3
- 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/files.d.ts +4 -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 +1974 -256
- 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 +77 -9
- 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 +820 -222
- 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 +3170 -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 -38
- 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 +839 -131
- 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 +1040 -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 +337 -39
- 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-2PQMSRCB.js +0 -49759
- package/dist/chunk-2PQMSRCB.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,22 +15,27 @@ 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,
|
|
34
|
+
getFilesForSubject,
|
|
35
|
+
listFilesForSubject,
|
|
27
36
|
getRetainedFileArtifact,
|
|
28
37
|
getRetainedScreenshotArtifact,
|
|
29
|
-
|
|
38
|
+
requireFileForSubject,
|
|
30
39
|
type RetainedFileArtifact,
|
|
31
40
|
type GeneratedImageArtifact,
|
|
32
41
|
type GeneratedVideoArtifact,
|
|
@@ -34,24 +43,111 @@ import {
|
|
|
34
43
|
} from "@opengeni/db";
|
|
35
44
|
import type { Context, Hono } from "hono";
|
|
36
45
|
import { HTTPException } from "hono/http-exception";
|
|
37
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
fileOwnerContextForAccess,
|
|
48
|
+
requireAccessGrantAuthorization,
|
|
49
|
+
grantHasAgentAttemptAuthority,
|
|
50
|
+
requireAccessGrant,
|
|
51
|
+
requireLiveAgentAttemptAuthorization,
|
|
52
|
+
requireSessionAuthorization,
|
|
53
|
+
SessionAuthorizationDeniedError,
|
|
54
|
+
SessionAuthorizationUnavailableError,
|
|
55
|
+
} from "@opengeni/core";
|
|
38
56
|
import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
|
|
39
57
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
58
|
+
import { retryWhileMissing } from "@opengeni/storage";
|
|
40
59
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
41
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";
|
|
42
69
|
|
|
43
70
|
export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
44
71
|
const { db, objectStorage } = deps;
|
|
45
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
|
+
|
|
46
99
|
app.all("/v1/workspaces/:workspaceId/mcp/files", async (c) => {
|
|
47
100
|
const workspaceId = c.req.param("workspaceId");
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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);
|
|
51
150
|
});
|
|
52
|
-
const server = buildFilesMcpServer(deps, grant);
|
|
53
|
-
await server.connect(transport);
|
|
54
|
-
return await transport.handleRequest(c.req.raw);
|
|
55
151
|
});
|
|
56
152
|
|
|
57
153
|
app.post("/v1/workspaces/:workspaceId/files/uploads", async (c) => {
|
|
@@ -62,7 +158,13 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
62
158
|
message: "object storage is not configured",
|
|
63
159
|
});
|
|
64
160
|
}
|
|
65
|
-
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" });
|
|
66
168
|
await requireLimit(deps, {
|
|
67
169
|
accountId: grant.accountId,
|
|
68
170
|
workspaceId,
|
|
@@ -86,6 +188,7 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
86
188
|
accountId: grant.accountId,
|
|
87
189
|
workspaceId,
|
|
88
190
|
fileId,
|
|
191
|
+
privateOwnerSubjectId: personal ? privateOwner : null,
|
|
89
192
|
filename: payload.filename,
|
|
90
193
|
safeFilename,
|
|
91
194
|
contentType: payload.contentType,
|
|
@@ -95,6 +198,23 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
95
198
|
objectKey,
|
|
96
199
|
expiresAt: signed.expiresAt,
|
|
97
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
|
+
});
|
|
98
218
|
return c.json(
|
|
99
219
|
CreateFileUploadResponse.parse({
|
|
100
220
|
fileId: upload.file.id,
|
|
@@ -226,11 +346,13 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
226
346
|
const file = await rejectAndCleanObject(409, "file upload has expired", "expired");
|
|
227
347
|
return c.json(CompleteFileUploadResponse.parse({ file }));
|
|
228
348
|
}
|
|
229
|
-
const head = await
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
});
|
|
349
|
+
const head = await retryWhileMissing(async () => {
|
|
350
|
+
if (!(await objectStorage.fileExists(upload.file))) return null;
|
|
351
|
+
return await objectStorage.headFile(upload.file);
|
|
233
352
|
});
|
|
353
|
+
if (!head) {
|
|
354
|
+
throw new HTTPException(409, { message: "uploaded object is not available" });
|
|
355
|
+
}
|
|
234
356
|
if (Number(head.ContentLength ?? -1) !== upload.file.sizeBytes) {
|
|
235
357
|
const file = await rejectAndCleanObject(
|
|
236
358
|
422,
|
|
@@ -263,10 +385,53 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
263
385
|
return c.json(CompleteFileUploadResponse.parse({ file }));
|
|
264
386
|
});
|
|
265
387
|
|
|
388
|
+
app.get("/v1/workspaces/:workspaceId/files", async (c) => {
|
|
389
|
+
const workspaceId = c.req.param("workspaceId");
|
|
390
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
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, {
|
|
424
|
+
accountId: grant.accountId,
|
|
425
|
+
workspaceId: grant.workspaceId,
|
|
426
|
+
subjectId: grant.subjectId,
|
|
427
|
+
fileId,
|
|
428
|
+
}).catch(() => null);
|
|
429
|
+
};
|
|
430
|
+
|
|
266
431
|
app.get("/v1/workspaces/:workspaceId/files/:fileId", async (c) => {
|
|
267
432
|
const workspaceId = c.req.param("workspaceId");
|
|
268
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
269
|
-
const file = await
|
|
433
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
434
|
+
const file = await readRequestedFile(c, grant).catch(() => null);
|
|
270
435
|
if (!file) {
|
|
271
436
|
throw new HTTPException(404, { message: "file not found" });
|
|
272
437
|
}
|
|
@@ -275,9 +440,14 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
275
440
|
|
|
276
441
|
app.get("/v1/workspaces/:workspaceId/artifacts/:artifactId", async (c) => {
|
|
277
442
|
const workspaceId = c.req.param("workspaceId");
|
|
278
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
443
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
279
444
|
const artifactId = retainedArtifactId(c.req.param("artifactId"));
|
|
280
|
-
const artifact = await getWorkspaceArtifact(db,
|
|
445
|
+
const artifact = await getWorkspaceArtifact(db, {
|
|
446
|
+
accountId: grant.accountId,
|
|
447
|
+
workspaceId,
|
|
448
|
+
subjectId: await fileAuthoritySubjectIdForGrant(deps, grant),
|
|
449
|
+
artifactId,
|
|
450
|
+
});
|
|
281
451
|
if (!artifact) {
|
|
282
452
|
return c.json(retainedArtifactUnavailable(artifactId, "deleted"), 404);
|
|
283
453
|
}
|
|
@@ -286,9 +456,14 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
286
456
|
|
|
287
457
|
app.get("/v1/workspaces/:workspaceId/artifacts/:artifactId/content", async (c) => {
|
|
288
458
|
const workspaceId = c.req.param("workspaceId");
|
|
289
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
459
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
290
460
|
const artifactId = retainedArtifactId(c.req.param("artifactId"));
|
|
291
|
-
const artifact = await getWorkspaceArtifact(db,
|
|
461
|
+
const artifact = await getWorkspaceArtifact(db, {
|
|
462
|
+
accountId: grant.accountId,
|
|
463
|
+
workspaceId,
|
|
464
|
+
subjectId: await fileAuthoritySubjectIdForGrant(deps, grant),
|
|
465
|
+
artifactId,
|
|
466
|
+
});
|
|
292
467
|
if (!artifact) {
|
|
293
468
|
return c.json(retainedArtifactUnavailable(artifactId, "deleted"), 404);
|
|
294
469
|
}
|
|
@@ -304,7 +479,7 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
304
479
|
|
|
305
480
|
app.post("/v1/workspaces/:workspaceId/artifacts/:artifactId/playback-source", async (c) => {
|
|
306
481
|
const workspaceId = c.req.param("workspaceId");
|
|
307
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
482
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
308
483
|
if (!objectStorage) {
|
|
309
484
|
throw new HTTPException(503, { message: "object storage is not configured" });
|
|
310
485
|
}
|
|
@@ -317,10 +492,29 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
317
492
|
throw new HTTPException(409, { message: `generated video is ${retained.file.status}` });
|
|
318
493
|
}
|
|
319
494
|
const file = generatedVideoFileAsset(retained.file);
|
|
320
|
-
|
|
495
|
+
const videoPresent = await retryWhileMissing(async () =>
|
|
496
|
+
(await objectStorage.fileExists(file)) ? true : null,
|
|
497
|
+
);
|
|
498
|
+
if (!videoPresent) {
|
|
321
499
|
throw new HTTPException(410, { message: "generated video bytes are unavailable" });
|
|
322
500
|
}
|
|
323
|
-
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
|
+
});
|
|
324
518
|
return c.json(
|
|
325
519
|
VideoArtifactPlaybackSource.parse({
|
|
326
520
|
schemaVersion: 1,
|
|
@@ -335,9 +529,40 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
335
529
|
);
|
|
336
530
|
});
|
|
337
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
|
+
|
|
338
562
|
app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/artifacts/:artifactId", async (c) => {
|
|
339
563
|
const workspaceId = c.req.param("workspaceId");
|
|
340
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
564
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
565
|
+
await requireAgentSessionAccess(c, grant);
|
|
341
566
|
const artifactId = retainedArtifactId(c.req.param("artifactId"));
|
|
342
567
|
const artifact = await getRetainedScreenshotArtifact(
|
|
343
568
|
db,
|
|
@@ -355,7 +580,8 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
355
580
|
"/v1/workspaces/:workspaceId/sessions/:sessionId/artifacts/:artifactId/content",
|
|
356
581
|
async (c) => {
|
|
357
582
|
const workspaceId = c.req.param("workspaceId");
|
|
358
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
583
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
584
|
+
await requireAgentSessionAccess(c, grant);
|
|
359
585
|
const artifactId = retainedArtifactId(c.req.param("artifactId"));
|
|
360
586
|
const artifact = await getRetainedScreenshotArtifact(
|
|
361
587
|
db,
|
|
@@ -378,20 +604,42 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
378
604
|
|
|
379
605
|
app.post("/v1/workspaces/:workspaceId/files/:fileId/download-url", async (c) => {
|
|
380
606
|
const workspaceId = c.req.param("workspaceId");
|
|
381
|
-
await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
607
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
|
|
382
608
|
if (!objectStorage) {
|
|
383
609
|
throw new HTTPException(503, {
|
|
384
610
|
message: "object storage is not configured",
|
|
385
611
|
});
|
|
386
612
|
}
|
|
387
|
-
const file = await
|
|
613
|
+
const file = await readRequestedFile(c, grant).catch(() => null);
|
|
388
614
|
if (!file) {
|
|
389
615
|
throw new HTTPException(404, { message: "file not found" });
|
|
390
616
|
}
|
|
391
617
|
if (file.status !== "ready") {
|
|
392
618
|
throw new HTTPException(409, { message: `file is ${file.status}` });
|
|
393
619
|
}
|
|
394
|
-
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
|
+
});
|
|
395
643
|
return c.json(
|
|
396
644
|
FileDownloadUrlResponse.parse({
|
|
397
645
|
url: signed.url,
|
|
@@ -446,25 +694,33 @@ async function serveRetainedArtifactContent(
|
|
|
446
694
|
);
|
|
447
695
|
}
|
|
448
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.
|
|
449
700
|
const headers = {
|
|
450
701
|
"Accept-Ranges": range.acceptRanges,
|
|
451
702
|
"Cache-Control": "private, no-store",
|
|
452
703
|
"Content-Length": String(range.length),
|
|
453
704
|
"Content-Type": metadata.contentType,
|
|
454
|
-
|
|
705
|
+
...userContentResponseHeaders(metadata.contentType, file.filename),
|
|
455
706
|
...(range.contentRange ? { "Content-Range": range.contentRange } : {}),
|
|
456
707
|
};
|
|
457
708
|
if (range.kind === "empty") {
|
|
458
|
-
|
|
709
|
+
const present = await retryWhileMissing(async () =>
|
|
710
|
+
(await objectStorage.fileExists(file)) ? true : null,
|
|
711
|
+
);
|
|
712
|
+
if (!present) {
|
|
459
713
|
return c.json(retainedArtifactUnavailable(artifactId, "missing_storage"), 410);
|
|
460
714
|
}
|
|
461
715
|
return c.body(null, 200, headers);
|
|
462
716
|
}
|
|
463
717
|
|
|
464
|
-
const bytes = await
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
718
|
+
const bytes = await retryWhileMissing(async () =>
|
|
719
|
+
objectStorage.getFileRange(file, {
|
|
720
|
+
start: range.start,
|
|
721
|
+
end: range.end,
|
|
722
|
+
}),
|
|
723
|
+
);
|
|
468
724
|
if (!bytes) {
|
|
469
725
|
return c.json(retainedArtifactUnavailable(artifactId, "missing_storage"), 410);
|
|
470
726
|
}
|
|
@@ -537,14 +793,18 @@ function retainedArtifactMetadata(artifact: RetainedFileArtifact): RetainedArtif
|
|
|
537
793
|
|
|
538
794
|
async function getWorkspaceArtifact(
|
|
539
795
|
db: ApiRouteDeps["db"],
|
|
540
|
-
|
|
541
|
-
|
|
796
|
+
input: {
|
|
797
|
+
accountId: string;
|
|
798
|
+
workspaceId: string;
|
|
799
|
+
subjectId: string | null;
|
|
800
|
+
artifactId: string;
|
|
801
|
+
},
|
|
542
802
|
): Promise<{ file: RetainedFileArtifact["file"]; metadata: RetainedArtifactMetadata } | null> {
|
|
543
|
-
const generated = await getGeneratedImageArtifact(db, workspaceId, artifactId);
|
|
803
|
+
const generated = await getGeneratedImageArtifact(db, input.workspaceId, input.artifactId);
|
|
544
804
|
if (generated) {
|
|
545
805
|
return { file: generated.file, metadata: retainedGeneratedImageMetadata(generated) };
|
|
546
806
|
}
|
|
547
|
-
const generatedVideo = await getGeneratedVideoArtifact(db, workspaceId, artifactId);
|
|
807
|
+
const generatedVideo = await getGeneratedVideoArtifact(db, input.workspaceId, input.artifactId);
|
|
548
808
|
if (generatedVideo && !generatedVideo.artifact.deletedAt) {
|
|
549
809
|
const file = generatedVideoFileAsset(generatedVideo.file);
|
|
550
810
|
return {
|
|
@@ -555,8 +815,43 @@ async function getWorkspaceArtifact(
|
|
|
555
815
|
}),
|
|
556
816
|
};
|
|
557
817
|
}
|
|
558
|
-
const artifact = await getRetainedFileArtifact(db, workspaceId, artifactId);
|
|
559
|
-
|
|
818
|
+
const artifact = await getRetainedFileArtifact(db, input.workspaceId, input.artifactId);
|
|
819
|
+
if (!artifact) return null;
|
|
820
|
+
const [authorizedFile] = await getFilesForSubject(db, {
|
|
821
|
+
accountId: input.accountId,
|
|
822
|
+
workspaceId: input.workspaceId,
|
|
823
|
+
subjectId: input.subjectId,
|
|
824
|
+
fileIds: [artifact.file.id],
|
|
825
|
+
});
|
|
826
|
+
if (!authorizedFile) return null;
|
|
827
|
+
const authorizedArtifact = { ...artifact, file: authorizedFile };
|
|
828
|
+
return {
|
|
829
|
+
file: authorizedFile,
|
|
830
|
+
metadata: retainedArtifactMetadata(authorizedArtifact),
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/** Human and stable service grants already carry their immutable subject.
|
|
835
|
+
* Agent tokens carry only a technical worker subject, so resolve the exact
|
|
836
|
+
* live attempt and use the initiating human frozen on its durable turn. */
|
|
837
|
+
export async function fileAuthoritySubjectIdForGrant(
|
|
838
|
+
deps: Pick<ApiRouteDeps, "db">,
|
|
839
|
+
grant: import("@opengeni/contracts").AccessGrant,
|
|
840
|
+
): Promise<string | null> {
|
|
841
|
+
if (grant.principalKind !== "agent_attempt") return grant.subjectId;
|
|
842
|
+
const sessionId = grant.metadata?.["sessionId"];
|
|
843
|
+
if (typeof sessionId !== "string") {
|
|
844
|
+
throw new HTTPException(404, { message: "file not found" });
|
|
845
|
+
}
|
|
846
|
+
try {
|
|
847
|
+
const actor = await requireLiveAgentAttemptAuthorization(deps.db, grant, sessionId);
|
|
848
|
+
return actor.initiatingHumanSubjectId;
|
|
849
|
+
} catch (error) {
|
|
850
|
+
if (error instanceof SessionAuthorizationDeniedError) {
|
|
851
|
+
throw new HTTPException(404, { message: "file not found", cause: error });
|
|
852
|
+
}
|
|
853
|
+
throw error;
|
|
854
|
+
}
|
|
560
855
|
}
|
|
561
856
|
|
|
562
857
|
function generatedVideoFileAsset(
|
|
@@ -605,12 +900,15 @@ function retainedScreenshotMetadata(
|
|
|
605
900
|
artifact.sessionId &&
|
|
606
901
|
artifact.retentionExpiresAt.getTime() > Date.now()
|
|
607
902
|
) {
|
|
903
|
+
const kind = retainedSessionScreenshotKindFromObjectKey(artifact.file.objectKey);
|
|
904
|
+
if (!kind) return retainedArtifactUnavailable(artifact.artifactId, "invalid_content");
|
|
608
905
|
const reference = retainedScreenshotReferenceFromFile({
|
|
609
906
|
...artifact.file,
|
|
610
907
|
sessionId: artifact.sessionId,
|
|
611
908
|
width: artifact.width,
|
|
612
909
|
height: artifact.height,
|
|
613
910
|
expiresAt: artifact.retentionExpiresAt.toISOString(),
|
|
911
|
+
kind,
|
|
614
912
|
});
|
|
615
913
|
if (reference) return reference;
|
|
616
914
|
}
|