@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/api-keys.ts
CHANGED
|
@@ -1,16 +1,112 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
CreateApiKeyRequest,
|
|
3
|
+
CreateApiKeyResponse,
|
|
4
|
+
CreateOrganizationApiKeyRequest,
|
|
5
|
+
UpdateOrganizationApiKeyRequest,
|
|
6
|
+
normalizeOrganizationAccessPolicy,
|
|
7
|
+
type OrganizationAccessPolicy,
|
|
8
|
+
DEVELOPER_SETUP_API_KEY_PRESET,
|
|
9
|
+
Permission,
|
|
10
|
+
type AccessContext,
|
|
11
|
+
type ApiKey,
|
|
12
|
+
type OrganizationApiKeyAccess,
|
|
13
|
+
} from "@opengeni/contracts";
|
|
14
|
+
import {
|
|
15
|
+
createApiKey,
|
|
16
|
+
createOrganizationApiKey as createOrganizationApiKeyRecord,
|
|
17
|
+
listApiKeys,
|
|
18
|
+
listOrganizationApiKeys,
|
|
19
|
+
getOrganizationApiKey,
|
|
20
|
+
updateOrganizationApiKey,
|
|
21
|
+
OrganizationApiKeyWorkspaceScopeError,
|
|
22
|
+
OrganizationApiKeyLimitExceededError,
|
|
23
|
+
OrganizationServiceAccountNotFoundError,
|
|
24
|
+
OrganizationServiceAccountRoleError,
|
|
25
|
+
revokeApiKey,
|
|
26
|
+
revokeOrganizationApiKey,
|
|
27
|
+
} from "@opengeni/db";
|
|
28
|
+
import { configuredStaticUsageLimits } from "@opengeni/config";
|
|
3
29
|
import { zValidator } from "@hono/zod-validator";
|
|
4
30
|
import type { Hono } from "hono";
|
|
5
31
|
import { HTTPException } from "hono/http-exception";
|
|
6
32
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
7
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
accountScopedApiKeyWorkspaceAuthority,
|
|
35
|
+
organizationApiKeyAccess,
|
|
36
|
+
hasPermission,
|
|
37
|
+
organizationWorkspaceInScope,
|
|
38
|
+
requireAccessContext,
|
|
39
|
+
requireAccessGrantAuthorization,
|
|
40
|
+
requireApiKeyManagementContext,
|
|
41
|
+
requireExplicitPermissionDelegation,
|
|
42
|
+
type AccessGrantAuthorization,
|
|
43
|
+
} from "@opengeni/core";
|
|
8
44
|
import { requireLimit } from "@opengeni/core";
|
|
9
45
|
|
|
46
|
+
export { organizationApiKeyAccess } from "@opengeni/core";
|
|
47
|
+
|
|
48
|
+
/** Permissions minted onto a `full` organization API key. */
|
|
49
|
+
export const organizationApiKeyPermissions: Permission[] = [
|
|
50
|
+
"account:read",
|
|
51
|
+
"workspace:create",
|
|
52
|
+
"workspace:read",
|
|
53
|
+
"workspace:admin",
|
|
54
|
+
"api_keys:manage",
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Permissions minted onto a `read` organization API key. The key inventories
|
|
59
|
+
* shared workspaces and reads their sessions, events, and files; it holds no
|
|
60
|
+
* `workspace:admin` wildcard, so every control, create, and key-management
|
|
61
|
+
* route denies it, and no `api_keys:manage`, so it cannot mint keys.
|
|
62
|
+
*/
|
|
63
|
+
export const organizationReadApiKeyPermissions: Permission[] = [
|
|
64
|
+
"account:read",
|
|
65
|
+
"workspace:read",
|
|
66
|
+
"sessions:read",
|
|
67
|
+
"files:read",
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
export function organizationApiKeyPermissionsForAccess(
|
|
71
|
+
access: OrganizationApiKeyAccess,
|
|
72
|
+
): Permission[] {
|
|
73
|
+
return access === "developer_setup"
|
|
74
|
+
? [...DEVELOPER_SETUP_API_KEY_PRESET.permissions]
|
|
75
|
+
: access === "read"
|
|
76
|
+
? [...organizationReadApiKeyPermissions]
|
|
77
|
+
: [...organizationApiKeyPermissions];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Existing callers retain their expiry; setup keys default to one day. */
|
|
81
|
+
export function organizationApiKeyExpiryDate(
|
|
82
|
+
request: Pick<CreateOrganizationApiKeyRequest, "preset" | "expiresAt"> &
|
|
83
|
+
Partial<Pick<CreateOrganizationApiKeyRequest, "access">>,
|
|
84
|
+
now: Date = new Date(),
|
|
85
|
+
): Date | null {
|
|
86
|
+
if (request.expiresAt !== undefined) return new Date(request.expiresAt);
|
|
87
|
+
return request.preset === "developer_setup" || request.access === "developer_setup"
|
|
88
|
+
? new Date(now.getTime() + DEVELOPER_SETUP_API_KEY_PRESET.defaultExpiryHours * 60 * 60 * 1000)
|
|
89
|
+
: null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function withOrganizationApiKeyAccess(apiKey: ApiKey): ApiKey {
|
|
93
|
+
const workspaceScope = apiKey.workspaceScope ?? { kind: "all" as const };
|
|
94
|
+
return {
|
|
95
|
+
...apiKey,
|
|
96
|
+
access: organizationApiKeyAccess(apiKey.permissions),
|
|
97
|
+
workspaceScope,
|
|
98
|
+
policy: normalizeOrganizationAccessPolicy({
|
|
99
|
+
preset: "custom",
|
|
100
|
+
permissions: apiKey.permissions,
|
|
101
|
+
workspaceScope,
|
|
102
|
+
}),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
10
106
|
export function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
11
107
|
app.get("/v1/workspaces/:workspaceId/api-keys", async (c) => {
|
|
12
108
|
const workspaceId = c.req.param("workspaceId");
|
|
13
|
-
await
|
|
109
|
+
await requireWorkspaceApiKeyControl(c, deps, workspaceId);
|
|
14
110
|
return c.json({ apiKeys: await listApiKeys(deps.db, workspaceId) });
|
|
15
111
|
});
|
|
16
112
|
|
|
@@ -19,11 +115,12 @@ export function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
19
115
|
zValidator("json", CreateApiKeyRequest.omit({ workspaceId: true })),
|
|
20
116
|
async (c) => {
|
|
21
117
|
const workspaceId = c.req.param("workspaceId");
|
|
22
|
-
const
|
|
118
|
+
const authorization = await requireWorkspaceApiKeyControl(c, deps, workspaceId);
|
|
119
|
+
const grant = authorization.grant;
|
|
23
120
|
const body = c.req.valid("json");
|
|
24
121
|
const permissions: Permission[] =
|
|
25
122
|
body.permissions.length > 0 ? (body.permissions as Permission[]) : ["workspace:read"];
|
|
26
|
-
ensureDelegablePermissions(
|
|
123
|
+
ensureDelegablePermissions(authorization, permissions);
|
|
27
124
|
await requireLimit(deps, {
|
|
28
125
|
accountId: grant.accountId,
|
|
29
126
|
workspaceId,
|
|
@@ -36,6 +133,7 @@ export function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
36
133
|
accountId: grant.accountId,
|
|
37
134
|
workspaceId: grant.workspaceId,
|
|
38
135
|
name: body.name,
|
|
136
|
+
description: body.description ?? null,
|
|
39
137
|
prefix,
|
|
40
138
|
keyHash: await sha256Hex(token),
|
|
41
139
|
permissions,
|
|
@@ -47,15 +145,172 @@ export function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
47
145
|
|
|
48
146
|
app.delete("/v1/workspaces/:workspaceId/api-keys/:apiKeyId", async (c) => {
|
|
49
147
|
const workspaceId = c.req.param("workspaceId");
|
|
50
|
-
await
|
|
148
|
+
await requireWorkspaceApiKeyControl(c, deps, workspaceId);
|
|
51
149
|
return c.json(await revokeApiKey(deps.db, workspaceId, c.req.param("apiKeyId")));
|
|
52
150
|
});
|
|
151
|
+
|
|
152
|
+
app.get("/v1/organizations/:organizationId/api-keys", async (c) => {
|
|
153
|
+
const organizationId = c.req.param("organizationId");
|
|
154
|
+
const context = await requireAccessContext(c, deps);
|
|
155
|
+
requireOrganizationApiKeyControlPermission(context, organizationId);
|
|
156
|
+
return c.json({
|
|
157
|
+
apiKeys: (await listOrganizationApiKeys(deps.db, organizationId)).map(
|
|
158
|
+
withOrganizationApiKeyAccess,
|
|
159
|
+
),
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
app.post(
|
|
164
|
+
"/v1/organizations/:organizationId/api-keys",
|
|
165
|
+
zValidator("json", CreateOrganizationApiKeyRequest),
|
|
166
|
+
async (c) => {
|
|
167
|
+
const organizationId = c.req.param("organizationId");
|
|
168
|
+
const context = await requireAccessContext(c, deps);
|
|
169
|
+
requireOrganizationApiKeyControlPermission(context, organizationId);
|
|
170
|
+
const body = c.req.valid("json");
|
|
171
|
+
const rawBody: Record<string, unknown> = await c.req.json();
|
|
172
|
+
if (body.policy && (rawBody.access !== undefined || rawBody.preset !== undefined))
|
|
173
|
+
throw new HTTPException(400, {
|
|
174
|
+
message: "Choose either a policy or a legacy access tier/preset",
|
|
175
|
+
});
|
|
176
|
+
const token = generateApiKeyToken();
|
|
177
|
+
const permissions =
|
|
178
|
+
body.policy?.permissions ??
|
|
179
|
+
(body.preset === "developer_setup"
|
|
180
|
+
? [...DEVELOPER_SETUP_API_KEY_PRESET.permissions]
|
|
181
|
+
: organizationApiKeyPermissionsForAccess(body.access));
|
|
182
|
+
ensureOrganizationPolicyDelegable(
|
|
183
|
+
context,
|
|
184
|
+
body.policy ?? {
|
|
185
|
+
preset: "custom",
|
|
186
|
+
permissions,
|
|
187
|
+
workspaceScope: { kind: "all" },
|
|
188
|
+
},
|
|
189
|
+
!body.policy,
|
|
190
|
+
);
|
|
191
|
+
try {
|
|
192
|
+
const apiKey = await createOrganizationApiKeyRecord(deps.db, {
|
|
193
|
+
accountId: organizationId,
|
|
194
|
+
name: body.name,
|
|
195
|
+
description: body.description ?? null,
|
|
196
|
+
prefix: token.slice(0, 14),
|
|
197
|
+
keyHash: await sha256Hex(token),
|
|
198
|
+
permissions,
|
|
199
|
+
...(body.policy ? { policy: body.policy } : {}),
|
|
200
|
+
expiresAt: organizationApiKeyExpiryDate(body),
|
|
201
|
+
maxActiveKeys: organizationApiKeyLimit(deps),
|
|
202
|
+
rotationSourceApiKeyId: authenticatedApiKeyId(context),
|
|
203
|
+
serviceAccountId: body.serviceAccountId ?? null,
|
|
204
|
+
createdBySubjectId: context.subjectId,
|
|
205
|
+
});
|
|
206
|
+
return c.json(
|
|
207
|
+
CreateApiKeyResponse.parse({ apiKey: withOrganizationApiKeyAccess(apiKey), token }),
|
|
208
|
+
201,
|
|
209
|
+
);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if (error instanceof OrganizationApiKeyLimitExceededError) {
|
|
212
|
+
throw new HTTPException(429, { message: error.message });
|
|
213
|
+
}
|
|
214
|
+
if (error instanceof OrganizationApiKeyWorkspaceScopeError)
|
|
215
|
+
throw new HTTPException(400, { message: error.message });
|
|
216
|
+
throwServiceAccountError(error);
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
app.get("/v1/organizations/:organizationId/api-keys/:apiKeyId", async (c) => {
|
|
223
|
+
const organizationId = c.req.param("organizationId");
|
|
224
|
+
requireOrganizationApiKeyControlPermission(await requireAccessContext(c, deps), organizationId);
|
|
225
|
+
const apiKey = await getOrganizationApiKey(deps.db, organizationId, c.req.param("apiKeyId"));
|
|
226
|
+
if (!apiKey) throw new HTTPException(404, { message: "API key not found" });
|
|
227
|
+
return c.json(withOrganizationApiKeyAccess(apiKey));
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
app.patch(
|
|
231
|
+
"/v1/organizations/:organizationId/api-keys/:apiKeyId",
|
|
232
|
+
zValidator("json", UpdateOrganizationApiKeyRequest),
|
|
233
|
+
async (c) => {
|
|
234
|
+
const organizationId = c.req.param("organizationId");
|
|
235
|
+
const context = await requireAccessContext(c, deps);
|
|
236
|
+
requireOrganizationApiKeyControlPermission(context, organizationId);
|
|
237
|
+
const body = c.req.valid("json");
|
|
238
|
+
if (body.policy) ensureOrganizationPolicyDelegable(context, body.policy);
|
|
239
|
+
try {
|
|
240
|
+
const apiKey = await updateOrganizationApiKey(
|
|
241
|
+
deps.db,
|
|
242
|
+
organizationId,
|
|
243
|
+
c.req.param("apiKeyId"),
|
|
244
|
+
body,
|
|
245
|
+
);
|
|
246
|
+
if (!apiKey) throw new HTTPException(404, { message: "API key not found" });
|
|
247
|
+
return c.json(withOrganizationApiKeyAccess(apiKey));
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error instanceof OrganizationApiKeyWorkspaceScopeError)
|
|
250
|
+
throw new HTTPException(400, { message: error.message });
|
|
251
|
+
throwServiceAccountError(error);
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
app.delete("/v1/organizations/:organizationId/api-keys/:apiKeyId", async (c) => {
|
|
258
|
+
const organizationId = c.req.param("organizationId");
|
|
259
|
+
const context = await requireAccessContext(c, deps);
|
|
260
|
+
requireOrganizationApiKeyControlPermission(context, organizationId);
|
|
261
|
+
const apiKey = await revokeOrganizationApiKey(deps.db, organizationId, c.req.param("apiKeyId"));
|
|
262
|
+
if (!apiKey) {
|
|
263
|
+
throw new HTTPException(404, { message: "API key not found" });
|
|
264
|
+
}
|
|
265
|
+
return c.json(withOrganizationApiKeyAccess(apiKey));
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Service account problems read as the person would expect. */
|
|
270
|
+
export function throwServiceAccountError(error: unknown): void {
|
|
271
|
+
if (error instanceof OrganizationServiceAccountNotFoundError)
|
|
272
|
+
throw new HTTPException(404, { message: error.message });
|
|
273
|
+
if (error instanceof OrganizationServiceAccountRoleError)
|
|
274
|
+
throw new HTTPException(400, { message: error.message });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function requireAccountPermission(
|
|
278
|
+
context: AccessContext,
|
|
279
|
+
accountId: string,
|
|
280
|
+
permission: Permission,
|
|
281
|
+
): void {
|
|
282
|
+
const grant = context.accountGrants.find((candidate) => candidate.accountId === accountId);
|
|
283
|
+
if (
|
|
284
|
+
!grant ||
|
|
285
|
+
(!grant.permissions.includes(permission) && !grant.permissions.includes("account:admin"))
|
|
286
|
+
) {
|
|
287
|
+
throw new HTTPException(403, { message: `missing permission: ${permission}` });
|
|
288
|
+
}
|
|
53
289
|
}
|
|
54
290
|
|
|
55
|
-
function ensureDelegablePermissions(
|
|
56
|
-
|
|
291
|
+
function ensureDelegablePermissions(
|
|
292
|
+
authorization: AccessGrantAuthorization,
|
|
293
|
+
requested: Permission[],
|
|
294
|
+
): void {
|
|
295
|
+
requireExplicitPermissionDelegation(authorization.grant, requested);
|
|
296
|
+
const grantPermissions = authorization.grant.permissions;
|
|
297
|
+
if (
|
|
298
|
+
grantPermissions.includes("workspace:admin") &&
|
|
299
|
+
authorization.grant.permissionMode !== "explicit"
|
|
300
|
+
) {
|
|
301
|
+
const accountLiteralPermissions = new Set<Permission>([
|
|
302
|
+
"account:read",
|
|
303
|
+
"account:admin",
|
|
304
|
+
"workspace:create",
|
|
305
|
+
"billing:read",
|
|
306
|
+
"billing:manage",
|
|
307
|
+
]);
|
|
308
|
+
const workspaceLiteralPermissions = new Set<Permission>(["members:manage", "secrets:read"]);
|
|
57
309
|
const highTrustMissing = requested.filter(
|
|
58
|
-
(permission) =>
|
|
310
|
+
(permission) =>
|
|
311
|
+
(accountLiteralPermissions.has(permission) &&
|
|
312
|
+
!authorization.accountGrant?.permissions.includes(permission)) ||
|
|
313
|
+
(workspaceLiteralPermissions.has(permission) && !grantPermissions.includes(permission)),
|
|
59
314
|
);
|
|
60
315
|
if (highTrustMissing.length === 0) return;
|
|
61
316
|
throw new HTTPException(403, {
|
|
@@ -70,6 +325,77 @@ function ensureDelegablePermissions(grantPermissions: Permission[], requested: P
|
|
|
70
325
|
}
|
|
71
326
|
}
|
|
72
327
|
|
|
328
|
+
/** Key management is not a way for a policy credential to amplify itself. */
|
|
329
|
+
function ensureOrganizationPolicyDelegable(
|
|
330
|
+
context: AccessContext,
|
|
331
|
+
policy: OrganizationAccessPolicy,
|
|
332
|
+
legacy = false,
|
|
333
|
+
): void {
|
|
334
|
+
const authority = accountScopedApiKeyWorkspaceAuthority(context);
|
|
335
|
+
const source = context.credential?.policy;
|
|
336
|
+
if (!authority || authority.permissionMode !== "explicit" || !source) return;
|
|
337
|
+
const requested = legacy
|
|
338
|
+
? Permission.options.filter((permission) => hasPermission(policy.permissions, permission))
|
|
339
|
+
: policy.permissions;
|
|
340
|
+
if (requested.some((permission) => !source.permissions.includes(permission)))
|
|
341
|
+
throw new HTTPException(403, {
|
|
342
|
+
message: "cannot delegate permissions beyond organization key policy",
|
|
343
|
+
});
|
|
344
|
+
if (
|
|
345
|
+
source.workspaceScope.kind === "selected" &&
|
|
346
|
+
(policy.workspaceScope.kind === "all" ||
|
|
347
|
+
policy.workspaceScope.workspaceIds.some(
|
|
348
|
+
(id) => !organizationWorkspaceInScope(source.workspaceScope, id),
|
|
349
|
+
))
|
|
350
|
+
)
|
|
351
|
+
throw new HTTPException(403, {
|
|
352
|
+
message: "cannot delegate workspaces beyond organization key scope",
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function requireOrganizationApiKeyControlPermission(
|
|
357
|
+
context: AccessContext,
|
|
358
|
+
organizationId: string,
|
|
359
|
+
): void {
|
|
360
|
+
requireApiKeyManagementContext(context);
|
|
361
|
+
requireAccountPermission(context, organizationId, "api_keys:manage");
|
|
362
|
+
if (!context.subjectId.startsWith("api_key:")) return;
|
|
363
|
+
const authority = accountScopedApiKeyWorkspaceAuthority(context);
|
|
364
|
+
if (
|
|
365
|
+
!authority ||
|
|
366
|
+
authority.accountId !== organizationId ||
|
|
367
|
+
(!authority.permissions.includes("api_keys:manage") &&
|
|
368
|
+
!context.accountGrants.some(
|
|
369
|
+
(grant) =>
|
|
370
|
+
grant.accountId === organizationId && grant.permissions.includes("account:admin"),
|
|
371
|
+
))
|
|
372
|
+
) {
|
|
373
|
+
throw new HTTPException(403, { message: "organization API key authority required" });
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function requireWorkspaceApiKeyControl(
|
|
378
|
+
c: Parameters<typeof requireAccessContext>[0],
|
|
379
|
+
deps: ApiRouteDeps,
|
|
380
|
+
workspaceId: string,
|
|
381
|
+
): Promise<AccessGrantAuthorization> {
|
|
382
|
+
requireApiKeyManagementContext(await requireAccessContext(c, deps));
|
|
383
|
+
return await requireAccessGrantAuthorization(c, deps, workspaceId, "api_keys:manage");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function authenticatedApiKeyId(context: AccessContext): string | null {
|
|
387
|
+
return context.subjectId.startsWith("api_key:")
|
|
388
|
+
? context.subjectId.slice("api_key:".length)
|
|
389
|
+
: null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function organizationApiKeyLimit(deps: ApiRouteDeps): number | null {
|
|
393
|
+
if (deps.settings.usageLimitsMode !== "static" && deps.settings.usageLimitsMode !== "managed") {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
return configuredStaticUsageLimits(deps.settings).maxApiKeysPerWorkspace ?? null;
|
|
397
|
+
}
|
|
398
|
+
|
|
73
399
|
function generateApiKeyToken(): string {
|
|
74
400
|
const bytes = new Uint8Array(32);
|
|
75
401
|
crypto.getRandomValues(bytes);
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { resolveFirstPartyDelegationSecret } from "@opengeni/config";
|
|
2
|
+
import {
|
|
3
|
+
ArtifactCatalogItem,
|
|
4
|
+
ArtifactCatalogKind,
|
|
5
|
+
ArtifactCatalogListQuery,
|
|
6
|
+
ArtifactCatalogListResponse,
|
|
7
|
+
retainedArtifactReferenceFromFile,
|
|
8
|
+
retainedGeneratedImageReferenceFromFile,
|
|
9
|
+
type AccessGrant,
|
|
10
|
+
} from "@opengeni/contracts";
|
|
11
|
+
import {
|
|
12
|
+
editableArtifactId,
|
|
13
|
+
editableArtifactScope,
|
|
14
|
+
fileOwnerContextForAccess,
|
|
15
|
+
grantHasAgentAttemptAuthority,
|
|
16
|
+
hasPermission,
|
|
17
|
+
requireAccessGrantAuthorization,
|
|
18
|
+
requireLiveAgentAttemptAuthorization,
|
|
19
|
+
requireSessionAuthorization,
|
|
20
|
+
SessionAuthorizationDeniedError,
|
|
21
|
+
SessionAuthorizationUnavailableError,
|
|
22
|
+
type ApiRouteDeps,
|
|
23
|
+
} from "@opengeni/core";
|
|
24
|
+
import {
|
|
25
|
+
getFilesForSubject,
|
|
26
|
+
getGeneratedImageArtifact,
|
|
27
|
+
getSessionAuthorityProjection,
|
|
28
|
+
listArtifactCatalogCandidates,
|
|
29
|
+
withSessionRlsActorContext,
|
|
30
|
+
type ArtifactCatalogCandidate,
|
|
31
|
+
type ArtifactCatalogPosition,
|
|
32
|
+
type SessionRlsActorContext,
|
|
33
|
+
} from "@opengeni/db";
|
|
34
|
+
import type { Hono } from "hono";
|
|
35
|
+
import { HTTPException } from "hono/http-exception";
|
|
36
|
+
import { artifactCatalogCursorCodec } from "../artifact-catalog-cursor";
|
|
37
|
+
import {
|
|
38
|
+
editableArtifactActorForGrant,
|
|
39
|
+
editableArtifactHttpError,
|
|
40
|
+
isInvisibleListCandidate,
|
|
41
|
+
type EditableArtifactRouteDependencies,
|
|
42
|
+
} from "./editable-artifacts";
|
|
43
|
+
import { fileAuthoritySubjectIdForGrant } from "./files";
|
|
44
|
+
|
|
45
|
+
type Dependencies = ApiRouteDeps & EditableArtifactRouteDependencies;
|
|
46
|
+
const editableKinds = new Set(["document", "spreadsheet", "presentation"]);
|
|
47
|
+
const CANDIDATE_BATCH = 200;
|
|
48
|
+
const MAX_BATCHES = 10;
|
|
49
|
+
const iso = (value: Date | string) => new Date(value).toISOString();
|
|
50
|
+
const position = (row: ArtifactCatalogCandidate): ArtifactCatalogPosition => ({
|
|
51
|
+
key: row.sort_key,
|
|
52
|
+
kind: row.kind,
|
|
53
|
+
id: row.id,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export function registerArtifactCatalogRoutes(app: Hono, deps: Dependencies): void {
|
|
57
|
+
app.get("/v1/workspaces/:workspaceId/artifact-catalog", async (context) => {
|
|
58
|
+
const workspaceId = context.req.param("workspaceId");
|
|
59
|
+
const authorization = await requireAccessGrantAuthorization(context, deps, workspaceId);
|
|
60
|
+
const grant = authorization.grant;
|
|
61
|
+
const parsed = ArtifactCatalogListQuery.safeParse(context.req.query());
|
|
62
|
+
if (!parsed.success)
|
|
63
|
+
throw new HTTPException(422, { message: "Invalid artifact catalog query" });
|
|
64
|
+
const query = parsed.data;
|
|
65
|
+
const artifactsAllowed = hasPermission(grant.permissions, "artifacts:read");
|
|
66
|
+
const filesAllowed = hasPermission(grant.permissions, "files:read");
|
|
67
|
+
if (!artifactsAllowed && !filesAllowed)
|
|
68
|
+
throw new HTTPException(403, { message: "Artifact catalog read access required" });
|
|
69
|
+
let kinds = ArtifactCatalogKind.options.filter(
|
|
70
|
+
(kind) =>
|
|
71
|
+
(kind === "image" || kind === "file" ? filesAllowed : artifactsAllowed) &&
|
|
72
|
+
(!query.kind || kind === query.kind),
|
|
73
|
+
);
|
|
74
|
+
if (!deps.editableArtifacts) {
|
|
75
|
+
if (query.kind && editableKinds.has(query.kind) && artifactsAllowed)
|
|
76
|
+
throw new HTTPException(503, { message: "Editable artifacts are unavailable" });
|
|
77
|
+
kinds = kinds.filter((kind) => !editableKinds.has(kind));
|
|
78
|
+
}
|
|
79
|
+
const includesFiles = kinds.includes("file") || kinds.includes("image");
|
|
80
|
+
const actor: SessionRlsActorContext = includesFiles
|
|
81
|
+
? await fileOwnerContextForAccess(deps, authorization, "files:read")
|
|
82
|
+
: { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
|
|
83
|
+
// Session visibility follows the frozen live initiator independently of
|
|
84
|
+
// which artifact domains were selected. Never infer it from token labels
|
|
85
|
+
// or require file ownership merely to discover Sites/editable artifacts.
|
|
86
|
+
if (grantHasAgentAttemptAuthority(grant) && actor.initiatingHumanSubjectId === undefined) {
|
|
87
|
+
const callerSessionId = grant.metadata?.["sessionId"];
|
|
88
|
+
try {
|
|
89
|
+
if (typeof callerSessionId !== "string")
|
|
90
|
+
throw new SessionAuthorizationDeniedError("caller_stale");
|
|
91
|
+
const attempt = await requireLiveAgentAttemptAuthorization(deps.db, grant, callerSessionId);
|
|
92
|
+
actor.initiatingHumanSubjectId = attempt.initiatingHumanSubjectId;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error instanceof SessionAuthorizationDeniedError)
|
|
95
|
+
throw new HTTPException(404, { message: "Session not found", cause: error });
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const sourceDecisions = new Map<string, boolean>();
|
|
100
|
+
const canReadSession = async (sessionId: string): Promise<boolean> => {
|
|
101
|
+
if (!hasPermission(grant.permissions, "sessions:read")) return false;
|
|
102
|
+
if (sourceDecisions.has(sessionId)) return sourceDecisions.get(sessionId)!;
|
|
103
|
+
try {
|
|
104
|
+
await requireSessionAuthorization(deps, grant, {
|
|
105
|
+
sessionId,
|
|
106
|
+
operation: "session.read",
|
|
107
|
+
surface: "http",
|
|
108
|
+
});
|
|
109
|
+
// The optional-host seam can return null for both a shared standalone
|
|
110
|
+
// session and an RLS-hidden/missing row. Prove visibility independently
|
|
111
|
+
// AFTER it, so a permissive null is never itself provenance authority.
|
|
112
|
+
if (!(await getSessionAuthorityProjection(deps.db, workspaceId, sessionId))) {
|
|
113
|
+
sourceDecisions.set(sessionId, false);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
sourceDecisions.set(sessionId, true);
|
|
117
|
+
return true;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error instanceof SessionAuthorizationDeniedError) {
|
|
120
|
+
sourceDecisions.set(sessionId, false);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (error instanceof SessionAuthorizationUnavailableError)
|
|
124
|
+
throw new HTTPException(503, { message: "Session authorization is unavailable" });
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
context.header("cache-control", "private, no-store");
|
|
129
|
+
return withSessionRlsActorContext(actor, async () => {
|
|
130
|
+
if (query.sourceSessionId && !(await canReadSession(query.sourceSessionId)))
|
|
131
|
+
throw new HTTPException(404, { message: "Session not found" });
|
|
132
|
+
const secret = resolveFirstPartyDelegationSecret(deps.settings);
|
|
133
|
+
if (!secret)
|
|
134
|
+
throw new HTTPException(503, { message: "Artifact catalog cursor signing is unavailable" });
|
|
135
|
+
const filters = { ...query, cursor: undefined };
|
|
136
|
+
const codec = artifactCatalogCursorCodec(
|
|
137
|
+
secret,
|
|
138
|
+
JSON.stringify({
|
|
139
|
+
accountId: grant.accountId,
|
|
140
|
+
workspaceId,
|
|
141
|
+
subjectId: grant.subjectId,
|
|
142
|
+
principalKind: grant.principalKind,
|
|
143
|
+
metadata: grant.metadata,
|
|
144
|
+
permissions: [...grant.permissions].sort(),
|
|
145
|
+
initiatingHumanSubjectId: actor.initiatingHumanSubjectId ?? null,
|
|
146
|
+
owner: actor.privateFileOwnerSubjectId ?? null,
|
|
147
|
+
kinds,
|
|
148
|
+
filters,
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
const prior = query.cursor ? codec.decode(query.cursor) : undefined;
|
|
152
|
+
const snapshotAt = prior?.snapshotAt ?? new Date().toISOString();
|
|
153
|
+
const expiresAt = prior?.expiresAt ?? Date.now() + 60 * 60_000;
|
|
154
|
+
let after = prior?.after;
|
|
155
|
+
const items: ArtifactCatalogItem[] = [];
|
|
156
|
+
let lastReturned: ArtifactCatalogPosition | undefined;
|
|
157
|
+
let nextCursor: string | null = null;
|
|
158
|
+
const fileSubject = includesFiles ? await fileAuthoritySubjectIdForGrant(deps, grant) : null;
|
|
159
|
+
for (let batch = 0; batch < MAX_BATCHES; batch++) {
|
|
160
|
+
const candidates = await listArtifactCatalogCandidates(
|
|
161
|
+
deps.db,
|
|
162
|
+
{ accountId: grant.accountId, workspaceId },
|
|
163
|
+
{
|
|
164
|
+
query,
|
|
165
|
+
kinds,
|
|
166
|
+
snapshotAt,
|
|
167
|
+
...(after ? { after } : {}),
|
|
168
|
+
limit: CANDIDATE_BATCH,
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
for (const candidate of candidates) {
|
|
172
|
+
after = position(candidate);
|
|
173
|
+
const item = await projectCandidate(deps, grant, candidate, fileSubject);
|
|
174
|
+
if (
|
|
175
|
+
!item ||
|
|
176
|
+
item.status !== query.status ||
|
|
177
|
+
(query.q && !item.title.toLowerCase().includes(query.q.toLowerCase()))
|
|
178
|
+
)
|
|
179
|
+
continue;
|
|
180
|
+
if (items.length === query.limit) {
|
|
181
|
+
nextCursor = codec.encode({ version: 1, snapshotAt, expiresAt, after: lastReturned! });
|
|
182
|
+
return context.json(ArtifactCatalogListResponse.parse({ items, nextCursor }));
|
|
183
|
+
}
|
|
184
|
+
if (candidate.source_session_id && (await canReadSession(candidate.source_session_id)))
|
|
185
|
+
item.sourceSessionId = candidate.source_session_id;
|
|
186
|
+
items.push(ArtifactCatalogItem.parse(item));
|
|
187
|
+
lastReturned = after;
|
|
188
|
+
}
|
|
189
|
+
if (candidates.length < CANDIDATE_BATCH) break;
|
|
190
|
+
// Bounded scanning may produce an empty page; the encrypted frontier
|
|
191
|
+
// reveals neither denied identity nor provenance and can be continued.
|
|
192
|
+
if (batch === MAX_BATCHES - 1 && after)
|
|
193
|
+
nextCursor = codec.encode({ version: 1, snapshotAt, expiresAt, after });
|
|
194
|
+
}
|
|
195
|
+
return context.json(ArtifactCatalogListResponse.parse({ items, nextCursor }));
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function projectCandidate(
|
|
201
|
+
deps: Dependencies,
|
|
202
|
+
grant: AccessGrant,
|
|
203
|
+
row: ArtifactCatalogCandidate,
|
|
204
|
+
fileSubject: string | null,
|
|
205
|
+
): Promise<ArtifactCatalogItem | null> {
|
|
206
|
+
const item: ArtifactCatalogItem = {
|
|
207
|
+
id: row.id,
|
|
208
|
+
kind: row.kind,
|
|
209
|
+
title: row.title,
|
|
210
|
+
status: row.status,
|
|
211
|
+
createdAt: iso(row.created_at),
|
|
212
|
+
updatedAt: iso(row.updated_at),
|
|
213
|
+
};
|
|
214
|
+
if (row.version_id) item.versionId = row.version_id;
|
|
215
|
+
if (editableKinds.has(row.kind)) {
|
|
216
|
+
const scope = editableArtifactScope({
|
|
217
|
+
accountId: grant.accountId,
|
|
218
|
+
workspaceId: grant.workspaceId,
|
|
219
|
+
});
|
|
220
|
+
try {
|
|
221
|
+
// This is the HTTP application's read-only domain.getArtifact seam, NOT
|
|
222
|
+
// EditableArtifactAgentApplication.read (which touches session links).
|
|
223
|
+
const artifact = await deps.editableArtifacts!.readArtifact({
|
|
224
|
+
scope,
|
|
225
|
+
actor: editableArtifactActorForGrant(grant, "0000000000000001"),
|
|
226
|
+
artifactId: editableArtifactId(row.id),
|
|
227
|
+
});
|
|
228
|
+
if (
|
|
229
|
+
artifact.scope.accountId !== scope.accountId ||
|
|
230
|
+
artifact.scope.workspaceId !== scope.workspaceId ||
|
|
231
|
+
artifact.id !== row.id ||
|
|
232
|
+
artifact.modality !== row.kind
|
|
233
|
+
)
|
|
234
|
+
throw new Error("Artifact application returned mismatched catalog metadata");
|
|
235
|
+
return {
|
|
236
|
+
...item,
|
|
237
|
+
title: artifact.title,
|
|
238
|
+
status: artifact.lifecycle,
|
|
239
|
+
createdAt: artifact.createdAt,
|
|
240
|
+
updatedAt: artifact.updatedAt,
|
|
241
|
+
};
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (isInvisibleListCandidate(error)) return null;
|
|
244
|
+
throw editableArtifactHttpError(error);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (row.kind === "file" || row.kind === "image") {
|
|
248
|
+
const [file] = await getFilesForSubject(deps.db, {
|
|
249
|
+
accountId: grant.accountId,
|
|
250
|
+
workspaceId: grant.workspaceId,
|
|
251
|
+
subjectId: fileSubject,
|
|
252
|
+
fileIds: [row.id],
|
|
253
|
+
});
|
|
254
|
+
if (!file || file.status !== "ready") return null;
|
|
255
|
+
const generated =
|
|
256
|
+
row.origin === "generated_image"
|
|
257
|
+
? await getGeneratedImageArtifact(deps.db, grant.workspaceId, row.id)
|
|
258
|
+
: null;
|
|
259
|
+
const reference =
|
|
260
|
+
row.origin === "generated_image"
|
|
261
|
+
? generated?.status === "ready"
|
|
262
|
+
? retainedGeneratedImageReferenceFromFile({
|
|
263
|
+
...file,
|
|
264
|
+
width: generated.width,
|
|
265
|
+
height: generated.height,
|
|
266
|
+
})
|
|
267
|
+
: null
|
|
268
|
+
: retainedArtifactReferenceFromFile(file, "file");
|
|
269
|
+
if (!reference) return null;
|
|
270
|
+
return {
|
|
271
|
+
...item,
|
|
272
|
+
file: reference,
|
|
273
|
+
filename: file.filename,
|
|
274
|
+
contentType: reference.contentType,
|
|
275
|
+
sizeBytes: file.sizeBytes,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return item;
|
|
279
|
+
}
|