@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
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
resolveWorkspaceVoiceInputEnabled,
|
|
6
6
|
TRANSCRIPTION_RECORDING_PROVIDER_SEGMENT_SECONDS,
|
|
7
7
|
TRANSCRIPTION_RECORDING_RECOVERY_RETRY_AFTER_MILLISECONDS,
|
|
8
|
+
WorkspaceVoiceInputSettings,
|
|
8
9
|
type TranscriptionRecordingErrorCode,
|
|
9
10
|
type TranscriptionRecordingResponse,
|
|
10
11
|
type UploadTranscriptionRecordingChunkResponse,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
TranscriptionServiceError,
|
|
18
19
|
type ApiRouteDeps,
|
|
19
20
|
} from "@opengeni/core";
|
|
21
|
+
import { retryWhileMissing } from "@opengeni/storage";
|
|
20
22
|
import {
|
|
21
23
|
claimNextTranscriptionRecordingSegment,
|
|
22
24
|
claimTranscriptionRecordingAssembly,
|
|
@@ -57,6 +59,7 @@ type RecordingAuthority = {
|
|
|
57
59
|
accountId: string;
|
|
58
60
|
workspaceId: string;
|
|
59
61
|
subjectId: string;
|
|
62
|
+
voiceInput?: WorkspaceVoiceInputSettings | undefined;
|
|
60
63
|
};
|
|
61
64
|
|
|
62
65
|
class RecordingProcessingError extends Error {
|
|
@@ -356,6 +359,8 @@ export function registerResumableTranscriptionRoutes(app: Hono, deps: ApiRouteDe
|
|
|
356
359
|
let authority: RecordingAuthority | null = null;
|
|
357
360
|
let attemptId: string | null = null;
|
|
358
361
|
let segmentNumber: number | null = null;
|
|
362
|
+
let attemptedProvider: string | null = null;
|
|
363
|
+
let fallbackAllowed = false;
|
|
359
364
|
try {
|
|
360
365
|
authority = await requireRecordingAuthority(c, deps, true);
|
|
361
366
|
const service = deps.transcription;
|
|
@@ -371,6 +376,8 @@ export function registerResumableTranscriptionRoutes(app: Hono, deps: ApiRouteDe
|
|
|
371
376
|
}
|
|
372
377
|
const selectedProvider = service.selectProvider
|
|
373
378
|
? await service.selectProvider({
|
|
379
|
+
preferredProvider: authority.voiceInput?.preferredProvider,
|
|
380
|
+
fallbackEnabled: authority.voiceInput?.fallbackEnabled,
|
|
374
381
|
workspaceId: authority.workspaceId,
|
|
375
382
|
subjectId: authority.subjectId,
|
|
376
383
|
})
|
|
@@ -394,7 +401,12 @@ export function registerResumableTranscriptionRoutes(app: Hono, deps: ApiRouteDe
|
|
|
394
401
|
);
|
|
395
402
|
}
|
|
396
403
|
segmentNumber = claim.segment.segmentNumber;
|
|
397
|
-
|
|
404
|
+
attemptedProvider = claim.segment.providerId;
|
|
405
|
+
fallbackAllowed = claim.fallbackAllowed === true;
|
|
406
|
+
const segmentKey = claim.segment.objectKey;
|
|
407
|
+
const stored = deps.objectStorage
|
|
408
|
+
? await retryWhileMissing(async () => deps.objectStorage!.getObjectBytes(segmentKey))
|
|
409
|
+
: null;
|
|
398
410
|
if (!stored) {
|
|
399
411
|
throw new RecordingProcessingError("Provider segment is missing", "invalid_audio", false);
|
|
400
412
|
}
|
|
@@ -448,12 +460,32 @@ export function registerResumableTranscriptionRoutes(app: Hono, deps: ApiRouteDe
|
|
|
448
460
|
} catch (error) {
|
|
449
461
|
if (authority && attemptId && segmentNumber !== null) {
|
|
450
462
|
const failure = processingFailure(error);
|
|
463
|
+
// Only an explicit rejection may move an untouched recording to another vendor.
|
|
464
|
+
// The database fences this against stale attempts and completed segments.
|
|
465
|
+
const fallbackProviderId =
|
|
466
|
+
fallbackAllowed &&
|
|
467
|
+
error instanceof TranscriptionServiceError &&
|
|
468
|
+
error.fallbackSafe &&
|
|
469
|
+
authority.voiceInput?.fallbackEnabled !== false &&
|
|
470
|
+
attemptedProvider &&
|
|
471
|
+
deps.transcription?.selectProvider
|
|
472
|
+
? await Promise.resolve(
|
|
473
|
+
deps.transcription.selectProvider({
|
|
474
|
+
workspaceId: authority.workspaceId,
|
|
475
|
+
subjectId: authority.subjectId,
|
|
476
|
+
preferredProvider: authority.voiceInput?.preferredProvider,
|
|
477
|
+
excludedProviders: [attemptedProvider],
|
|
478
|
+
afterProvider: attemptedProvider,
|
|
479
|
+
}),
|
|
480
|
+
).catch(() => null)
|
|
481
|
+
: null;
|
|
451
482
|
const persisted = await failTranscriptionRecordingSegment(deps.db, {
|
|
452
483
|
workspaceId: authority.workspaceId,
|
|
453
484
|
subjectId: authority.subjectId,
|
|
454
485
|
recordingId: uuidParam(c, "recordingId"),
|
|
455
486
|
segmentNumber,
|
|
456
487
|
attemptId,
|
|
488
|
+
fallbackProviderId,
|
|
457
489
|
errorCode: failure.code,
|
|
458
490
|
retryable: failure.retryable,
|
|
459
491
|
}).catch(() => null);
|
|
@@ -501,14 +533,15 @@ async function requireRecordingAuthority(
|
|
|
501
533
|
throw new TranscriptionRecordingNotFoundError("Workspace not found");
|
|
502
534
|
}
|
|
503
535
|
const grant = await requireAccessGrant(c, deps, workspaceId, "sessions:create");
|
|
536
|
+
const workspace = await getWorkspace(deps.db, workspaceId);
|
|
504
537
|
if (requirePolicy) {
|
|
505
|
-
const workspace = await getWorkspace(deps.db, workspaceId);
|
|
506
538
|
if (!workspace) throw new TranscriptionRecordingNotFoundError("Workspace not found");
|
|
507
539
|
if (resolveWorkspaceVoiceInputEnabled(workspace.settings) === false) {
|
|
508
540
|
throw new RecordingProcessingError("Voice input is disabled", "policy_blocked", false);
|
|
509
541
|
}
|
|
510
542
|
}
|
|
511
543
|
return {
|
|
544
|
+
voiceInput: WorkspaceVoiceInputSettings.safeParse(workspace?.settings.voiceInput).data,
|
|
512
545
|
accountId: grant.accountId,
|
|
513
546
|
workspaceId,
|
|
514
547
|
subjectId: grant.subjectId,
|
|
@@ -578,7 +611,9 @@ async function* verifiedChunkBytes(
|
|
|
578
611
|
}
|
|
579
612
|
let stored: Awaited<ReturnType<NonNullable<ApiRouteDeps["objectStorage"]>["getObjectBytes"]>>;
|
|
580
613
|
try {
|
|
581
|
-
stored = await
|
|
614
|
+
stored = await retryWhileMissing(async () =>
|
|
615
|
+
deps.objectStorage!.getObjectBytes(chunk.objectKey),
|
|
616
|
+
);
|
|
582
617
|
} catch {
|
|
583
618
|
throw new RecordingProcessingError("Chunk download failed", "network", true);
|
|
584
619
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveWorkspaceVoiceInputEnabled,
|
|
3
|
+
WorkspaceVoiceInputSettings,
|
|
3
4
|
type TranscribeAudioResponse,
|
|
4
5
|
} from "@opengeni/contracts";
|
|
5
6
|
import { type ApiRouteDeps, requireAccessGrant, TranscriptionServiceError } from "@opengeni/core";
|
|
@@ -17,6 +18,7 @@ export function registerTranscriptionRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
17
18
|
if (resolveWorkspaceVoiceInputEnabled(workspace.settings) === false) {
|
|
18
19
|
return c.json({ code: "policy_blocked" }, 403);
|
|
19
20
|
}
|
|
21
|
+
const preferences = WorkspaceVoiceInputSettings.safeParse(workspace.settings.voiceInput).data;
|
|
20
22
|
const service = deps.transcription;
|
|
21
23
|
if (!service || !(await service.available({ workspaceId, subjectId: grant.subjectId }))) {
|
|
22
24
|
return c.json({ code: "unavailable" }, 503);
|
|
@@ -24,6 +26,8 @@ export function registerTranscriptionRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
24
26
|
try {
|
|
25
27
|
const body = await audioRequest(c.req.raw, service.limits().maxSizeBytes);
|
|
26
28
|
const result = await service.transcribe({
|
|
29
|
+
preferredProvider: preferences?.preferredProvider,
|
|
30
|
+
fallbackEnabled: preferences?.fallbackEnabled,
|
|
27
31
|
workspaceId,
|
|
28
32
|
accountId: grant.accountId,
|
|
29
33
|
subjectId: grant.subjectId,
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import type { Context, Hono } from "hono";
|
|
2
|
+
import { HTTPException } from "hono/http-exception";
|
|
3
|
+
import {
|
|
4
|
+
ClearWorkspaceAllowanceRequest,
|
|
5
|
+
GetMyUsageRequest,
|
|
6
|
+
GetUsageRequest,
|
|
7
|
+
GrantWorkspaceCreditsRequest,
|
|
8
|
+
SetMemberAllowanceRequest,
|
|
9
|
+
SetWorkspaceAllowanceRequest,
|
|
10
|
+
WorkspaceAllowance,
|
|
11
|
+
WorkspaceAllowanceState,
|
|
12
|
+
WorkspaceCreditGrant,
|
|
13
|
+
WorkspaceUsageResponse,
|
|
14
|
+
MemberAllowance,
|
|
15
|
+
ClearWorkspaceAllowanceResponse,
|
|
16
|
+
type AccessContext,
|
|
17
|
+
} from "@opengeni/contracts";
|
|
18
|
+
import { ExternalIdentityReference } from "@opengeni/contracts/external-identities";
|
|
19
|
+
import {
|
|
20
|
+
accountScopedApiKeyWorkspaceAuthority,
|
|
21
|
+
accessGrantAuthorizationFromContext,
|
|
22
|
+
requireAccountAdminAuthorizationStamp,
|
|
23
|
+
requireAccessContext,
|
|
24
|
+
requireAccessGrantAuthorization,
|
|
25
|
+
requireResolvedAccessGrantAuthorization,
|
|
26
|
+
type AccessGrantAuthorization,
|
|
27
|
+
type ApiRouteDeps,
|
|
28
|
+
} from "@opengeni/core";
|
|
29
|
+
import {
|
|
30
|
+
clearWorkspaceAllowance,
|
|
31
|
+
findWorkspaceByExternalIdentity,
|
|
32
|
+
getWorkspaceAllowance,
|
|
33
|
+
getWorkspaceAllowanceState,
|
|
34
|
+
getWorkspace,
|
|
35
|
+
getWorkspaceUsage,
|
|
36
|
+
grantWorkspaceCredits,
|
|
37
|
+
nestedPostgresSqlState,
|
|
38
|
+
setMemberAllowance,
|
|
39
|
+
setWorkspaceAllowance,
|
|
40
|
+
withAccountRls,
|
|
41
|
+
UsageAllowanceVersionConflictError,
|
|
42
|
+
} from "@opengeni/db";
|
|
43
|
+
import { parseRequestBody, parseRequestJson } from "../http/request-body";
|
|
44
|
+
|
|
45
|
+
type Authority = { authorization: AccessGrantAuthorization; context: AccessContext };
|
|
46
|
+
type Operation = "read" | "budget-read" | "workspace-write" | "member-write" | "own";
|
|
47
|
+
|
|
48
|
+
/** No workspace wildcard, service initiator, or actor label can confer budget authority. */
|
|
49
|
+
export function requireAllowanceAuthority(
|
|
50
|
+
{ authorization, context }: Authority,
|
|
51
|
+
operation: Operation,
|
|
52
|
+
): void {
|
|
53
|
+
const grant = requireResolvedAccessGrantAuthorization(
|
|
54
|
+
authorization,
|
|
55
|
+
authorization.grant.workspaceId,
|
|
56
|
+
);
|
|
57
|
+
if (grant.principalKind === "agent_attempt" || grant.metadata?.sessionId !== undefined) {
|
|
58
|
+
throw new HTTPException(403, { message: "Agents cannot access usage allowances" });
|
|
59
|
+
}
|
|
60
|
+
const organizationKey = accountScopedApiKeyWorkspaceAuthority(context);
|
|
61
|
+
const sameOrganizationKey =
|
|
62
|
+
organizationKey?.accountId === grant.accountId ? organizationKey : null;
|
|
63
|
+
const accountAdmin =
|
|
64
|
+
authorization.accountGrant?.permissions.includes("account:admin") === true &&
|
|
65
|
+
requireAccountAdminAuthorizationStamp(authorization).accountId === grant.accountId;
|
|
66
|
+
const workspaceAdmin = grant.permissions.includes("workspace:admin");
|
|
67
|
+
if (
|
|
68
|
+
sameOrganizationKey?.permissionMode === "explicit" &&
|
|
69
|
+
(operation === "workspace-write" || operation === "member-write")
|
|
70
|
+
) {
|
|
71
|
+
if (authorization.accountGrant?.permissions.includes("usage_allowances:manage")) return;
|
|
72
|
+
throw new HTTPException(403, { message: "missing permission: usage_allowances:manage" });
|
|
73
|
+
}
|
|
74
|
+
if (operation === "own") {
|
|
75
|
+
if (grant.principalKind !== "human_session" || grant.serviceInitiator) {
|
|
76
|
+
throw new HTTPException(403, { message: "Own usage requires an authenticated member" });
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (operation === "read" || operation === "budget-read") {
|
|
81
|
+
if (
|
|
82
|
+
workspaceAdmin ||
|
|
83
|
+
accountAdmin ||
|
|
84
|
+
sameOrganizationKey?.permissions.includes("workspace:read")
|
|
85
|
+
)
|
|
86
|
+
return;
|
|
87
|
+
} else if (operation === "workspace-write") {
|
|
88
|
+
// Existing full keys retain key-control budget authority. Setup keys use
|
|
89
|
+
// literal account allowance authority plus the canonical organization-key
|
|
90
|
+
// workspace stamp; asUser carries neither account scope nor key stamp.
|
|
91
|
+
if (sameOrganizationKey) {
|
|
92
|
+
if (
|
|
93
|
+
sameOrganizationKey.permissions.includes("workspace:admin") &&
|
|
94
|
+
(sameOrganizationKey.permissions.includes("api_keys:manage") ||
|
|
95
|
+
authorization.accountGrant?.permissions.includes("usage_allowances:manage"))
|
|
96
|
+
)
|
|
97
|
+
return;
|
|
98
|
+
} else if (
|
|
99
|
+
accountAdmin &&
|
|
100
|
+
grant.principalKind === "human_session" &&
|
|
101
|
+
!grant.serviceInitiator &&
|
|
102
|
+
!context.subjectId.startsWith("api_key:")
|
|
103
|
+
)
|
|
104
|
+
return;
|
|
105
|
+
} else if (operation === "member-write") {
|
|
106
|
+
if (
|
|
107
|
+
sameOrganizationKey?.permissions.includes("workspace:admin") ||
|
|
108
|
+
(workspaceAdmin &&
|
|
109
|
+
grant.principalKind === "human_session" &&
|
|
110
|
+
!grant.serviceInitiator &&
|
|
111
|
+
!context.subjectId.startsWith("api_key:"))
|
|
112
|
+
)
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
throw new HTTPException(403, { message: `Missing usage allowance authority: ${operation}` });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function resolveWorkspace(c: Context, deps: ApiRouteDeps): Promise<string> {
|
|
119
|
+
const workspaceId = c.req.param("workspaceId");
|
|
120
|
+
if (workspaceId) return workspaceId;
|
|
121
|
+
const context = await requireAccessContext(c, deps);
|
|
122
|
+
const organizationKey = accountScopedApiKeyWorkspaceAuthority(context);
|
|
123
|
+
// External tenant paths are host-service lookups, never caller-selected
|
|
124
|
+
// default-account guesses or provisioning.
|
|
125
|
+
const externalAccountId =
|
|
126
|
+
c.req.header("x-opengeni-external-actor") !== undefined ? context.defaultAccountId : null;
|
|
127
|
+
const accountId = organizationKey?.accountId ?? externalAccountId;
|
|
128
|
+
if (!accountId)
|
|
129
|
+
throw new HTTPException(403, { message: "Organization API key or external user required" });
|
|
130
|
+
const reference = parseRequestBody(ExternalIdentityReference, {
|
|
131
|
+
source: c.req.param("workspaceSource"),
|
|
132
|
+
externalId: c.req.param("workspaceExternalId"),
|
|
133
|
+
});
|
|
134
|
+
const workspace = await withAccountRls(deps.db, accountId, (tx) =>
|
|
135
|
+
findWorkspaceByExternalIdentity(tx, {
|
|
136
|
+
accountId,
|
|
137
|
+
externalSource: reference.source,
|
|
138
|
+
externalId: reference.externalId,
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
if (!workspace) throw new HTTPException(404, { message: "Workspace not found" });
|
|
142
|
+
return workspace.id;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function authorize(c: Context, deps: ApiRouteDeps, operation: Operation) {
|
|
146
|
+
const workspaceId = await resolveWorkspace(c, deps);
|
|
147
|
+
const context = await requireAccessContext(c, deps);
|
|
148
|
+
if (
|
|
149
|
+
(operation === "workspace-write" || operation === "budget-read") &&
|
|
150
|
+
!c.req.header("x-opengeni-external-actor") &&
|
|
151
|
+
!context.subjectId.startsWith("api_key:")
|
|
152
|
+
) {
|
|
153
|
+
// Organization budget management does not require membership in the
|
|
154
|
+
// target shared workspace. Validate the existing authenticated account
|
|
155
|
+
// authority on its own anchor, never manufacture a target workspace grant.
|
|
156
|
+
for (const account of context.accountGrants) {
|
|
157
|
+
if (account.subjectId !== context.subjectId || !account.permissions.includes("account:admin"))
|
|
158
|
+
continue;
|
|
159
|
+
const anchor = context.workspaceGrants.find(
|
|
160
|
+
(grant) =>
|
|
161
|
+
grant.accountId === account.accountId &&
|
|
162
|
+
grant.subjectId === context.subjectId &&
|
|
163
|
+
grant.principalKind === "human_session" &&
|
|
164
|
+
!grant.serviceInitiator &&
|
|
165
|
+
grant.metadata?.sessionId === undefined,
|
|
166
|
+
);
|
|
167
|
+
if (!anchor) continue;
|
|
168
|
+
const stamp = requireAccountAdminAuthorizationStamp(
|
|
169
|
+
accessGrantAuthorizationFromContext(context, anchor),
|
|
170
|
+
);
|
|
171
|
+
const target = await withAccountRls(deps.db, stamp.accountId, (tx) =>
|
|
172
|
+
getWorkspace(tx, workspaceId),
|
|
173
|
+
);
|
|
174
|
+
if (!target || target.accountId !== stamp.accountId) continue;
|
|
175
|
+
if (target.kind !== "shared") {
|
|
176
|
+
throw new HTTPException(403, {
|
|
177
|
+
message: "Organization budgets require a shared workspace",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
accountId: stamp.accountId,
|
|
182
|
+
workspaceId,
|
|
183
|
+
actorSubjectId: stamp.actorSubjectId,
|
|
184
|
+
actorType: "human_session" as const,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// An administrator of another organization may still hold an ordinary
|
|
188
|
+
// target grant. A budget lookup miss grants nothing and must not mask it.
|
|
189
|
+
}
|
|
190
|
+
const authorization = await requireAccessGrantAuthorization(c, deps, workspaceId);
|
|
191
|
+
requireAllowanceAuthority({ authorization, context }, operation);
|
|
192
|
+
return {
|
|
193
|
+
accountId: authorization.grant.accountId,
|
|
194
|
+
workspaceId,
|
|
195
|
+
actorSubjectId: authorization.grant.subjectId,
|
|
196
|
+
actorType: authorization.grant.principalKind ?? "subject",
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function usageQuery(c: Context, own: boolean) {
|
|
201
|
+
const input = c.req.query();
|
|
202
|
+
const parsed = parseRequestBody(own ? GetMyUsageRequest : GetUsageRequest, {
|
|
203
|
+
...input,
|
|
204
|
+
...(input.limit !== undefined ? { limit: Number(input.limit) } : {}),
|
|
205
|
+
});
|
|
206
|
+
return {
|
|
207
|
+
...(parsed.period === undefined ? {} : { period: parsed.period }),
|
|
208
|
+
...("limit" in parsed && parsed.limit !== undefined ? { limit: parsed.limit as number } : {}),
|
|
209
|
+
...("cursor" in parsed && parsed.cursor !== undefined
|
|
210
|
+
? { cursor: parsed.cursor as string }
|
|
211
|
+
: {}),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function requireAllowanceProducersEnabled(deps: ApiRouteDeps): void {
|
|
216
|
+
if (!deps.settings.usageAllowancesEnabled) {
|
|
217
|
+
throw new HTTPException(409, {
|
|
218
|
+
message:
|
|
219
|
+
"Usage allowance changes are not enabled. Upgrade every API and worker before enabling usage allowances.",
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Map lifecycle refusals without treating a failed CAS as an applied mutation. */
|
|
225
|
+
async function lifecycle<T>(operation: () => Promise<T>): Promise<T> {
|
|
226
|
+
try {
|
|
227
|
+
return await operation();
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (error instanceof UsageAllowanceVersionConflictError)
|
|
230
|
+
throw new HTTPException(409, { message: error.message });
|
|
231
|
+
const state = nestedPostgresSqlState(error);
|
|
232
|
+
if (state === "40001" || state === "23505")
|
|
233
|
+
throw new HTTPException(409, { message: "Allowance version or operation conflicts" });
|
|
234
|
+
if (state === "42501")
|
|
235
|
+
throw new HTTPException(403, { message: "Allowance operation is not authorized" });
|
|
236
|
+
if (state === "P0002" || state === "23503")
|
|
237
|
+
throw new HTTPException(404, { message: "Allowance or member not found" });
|
|
238
|
+
if (state === "22023" || state === "23514")
|
|
239
|
+
throw new HTTPException(400, { message: "Invalid allowance request" });
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function registerUsageAllowanceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
245
|
+
const bases = [
|
|
246
|
+
"/v1/workspaces/:workspaceId",
|
|
247
|
+
"/v1/workspaces/external/:workspaceSource/:workspaceExternalId",
|
|
248
|
+
];
|
|
249
|
+
for (const base of bases) {
|
|
250
|
+
app.get(`${base}/allowance`, async (c) => {
|
|
251
|
+
const scope = await authorize(c, deps, "budget-read");
|
|
252
|
+
const result = await getWorkspaceAllowance(deps.db, scope);
|
|
253
|
+
return c.json(result === null ? null : WorkspaceAllowance.parse(result));
|
|
254
|
+
});
|
|
255
|
+
app.get(`${base}/allowance/state`, async (c) => {
|
|
256
|
+
const scope = await authorize(c, deps, "budget-read");
|
|
257
|
+
return c.json(
|
|
258
|
+
WorkspaceAllowanceState.parse(await getWorkspaceAllowanceState(deps.db, scope)),
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
app.put(`${base}/allowance`, async (c) => {
|
|
262
|
+
const scope = await authorize(c, deps, "workspace-write");
|
|
263
|
+
const request = await parseRequestJson(c, SetWorkspaceAllowanceRequest);
|
|
264
|
+
requireAllowanceProducersEnabled(deps);
|
|
265
|
+
return c.json(
|
|
266
|
+
WorkspaceAllowance.parse(
|
|
267
|
+
await lifecycle(() =>
|
|
268
|
+
setWorkspaceAllowance(deps.db, {
|
|
269
|
+
...scope,
|
|
270
|
+
expectedVersion: request.expectedVersion,
|
|
271
|
+
includedCredits: request.includedCredits,
|
|
272
|
+
period: request.period,
|
|
273
|
+
...(request.anchorDay === undefined ? {} : { anchorDay: request.anchorDay }),
|
|
274
|
+
...(request.memberDefault === undefined
|
|
275
|
+
? {}
|
|
276
|
+
: { memberDefault: request.memberDefault }),
|
|
277
|
+
...(request.thresholds === undefined
|
|
278
|
+
? {}
|
|
279
|
+
: {
|
|
280
|
+
thresholds: {
|
|
281
|
+
...(request.thresholds.workspace === undefined
|
|
282
|
+
? {}
|
|
283
|
+
: { workspace: request.thresholds.workspace }),
|
|
284
|
+
...(request.thresholds.member === undefined
|
|
285
|
+
? {}
|
|
286
|
+
: { member: request.thresholds.member }),
|
|
287
|
+
},
|
|
288
|
+
}),
|
|
289
|
+
}),
|
|
290
|
+
),
|
|
291
|
+
),
|
|
292
|
+
);
|
|
293
|
+
});
|
|
294
|
+
app.delete(`${base}/allowance`, async (c) => {
|
|
295
|
+
const scope = await authorize(c, deps, "workspace-write");
|
|
296
|
+
const request = await parseRequestJson(c, ClearWorkspaceAllowanceRequest);
|
|
297
|
+
return c.json(
|
|
298
|
+
ClearWorkspaceAllowanceResponse.parse(
|
|
299
|
+
await lifecycle(() =>
|
|
300
|
+
clearWorkspaceAllowance(deps.db, {
|
|
301
|
+
...scope,
|
|
302
|
+
expectedVersion: request.expectedVersion,
|
|
303
|
+
...(request.operationId === undefined ? {} : { operationId: request.operationId }),
|
|
304
|
+
}),
|
|
305
|
+
),
|
|
306
|
+
),
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
app.post(`${base}/allowance/grants`, async (c) => {
|
|
310
|
+
const scope = await authorize(c, deps, "workspace-write");
|
|
311
|
+
const request = await parseRequestJson(c, GrantWorkspaceCreditsRequest);
|
|
312
|
+
requireAllowanceProducersEnabled(deps);
|
|
313
|
+
return c.json(
|
|
314
|
+
WorkspaceCreditGrant.parse(
|
|
315
|
+
await lifecycle(() =>
|
|
316
|
+
grantWorkspaceCredits(deps.db, {
|
|
317
|
+
...scope,
|
|
318
|
+
operationId: request.operationId,
|
|
319
|
+
credits: request.credits,
|
|
320
|
+
...(request.expiresAt === undefined ? {} : { expiresAt: request.expiresAt }),
|
|
321
|
+
}),
|
|
322
|
+
),
|
|
323
|
+
),
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
for (const memberPath of [
|
|
327
|
+
"members/:subjectId",
|
|
328
|
+
"members/external/:memberSource/:memberExternalId",
|
|
329
|
+
]) {
|
|
330
|
+
app.put(`${base}/${memberPath}/allowance`, async (c) => {
|
|
331
|
+
const scope = await authorize(c, deps, "member-write");
|
|
332
|
+
const request = await parseRequestJson(c, SetMemberAllowanceRequest);
|
|
333
|
+
if (request.rule !== null) requireAllowanceProducersEnabled(deps);
|
|
334
|
+
const subjectId = c.req.param("subjectId");
|
|
335
|
+
const target = subjectId
|
|
336
|
+
? { subjectId }
|
|
337
|
+
: {
|
|
338
|
+
externalIdentity: parseRequestBody(ExternalIdentityReference, {
|
|
339
|
+
source: c.req.param("memberSource"),
|
|
340
|
+
externalId: c.req.param("memberExternalId"),
|
|
341
|
+
}),
|
|
342
|
+
};
|
|
343
|
+
// Resolve the exact external identity and live member inside the same
|
|
344
|
+
// protected mutation; never enumerate a roster or provision an identity.
|
|
345
|
+
return c.json(
|
|
346
|
+
MemberAllowance.parse(
|
|
347
|
+
await lifecycle(() => setMemberAllowance(deps.db, { ...scope, ...target, ...request })),
|
|
348
|
+
),
|
|
349
|
+
);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
app.get(`${base}/usage`, async (c) => {
|
|
353
|
+
const scope = await authorize(c, deps, "read");
|
|
354
|
+
return c.json(
|
|
355
|
+
WorkspaceUsageResponse.parse(
|
|
356
|
+
await getWorkspaceUsage(deps.db, { ...scope, ...usageQuery(c, false) }),
|
|
357
|
+
),
|
|
358
|
+
);
|
|
359
|
+
});
|
|
360
|
+
app.get(`${base}/usage/me`, async (c) => {
|
|
361
|
+
const scope = await authorize(c, deps, "own");
|
|
362
|
+
const result = WorkspaceUsageResponse.parse(
|
|
363
|
+
await getWorkspaceUsage(deps.db, {
|
|
364
|
+
...scope,
|
|
365
|
+
...usageQuery(c, true),
|
|
366
|
+
subjectId: scope.actorSubjectId,
|
|
367
|
+
}),
|
|
368
|
+
);
|
|
369
|
+
// Defense in depth: the own endpoint never serializes another member or a
|
|
370
|
+
// roster cursor even if the storage adapter returns an overbroad page.
|
|
371
|
+
return c.json({
|
|
372
|
+
...result,
|
|
373
|
+
members: result.members.filter((member) => member.subjectId === scope.actorSubjectId),
|
|
374
|
+
nextCursor: null,
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IssueUserResourceGrantRequest,
|
|
3
|
+
ListUserResourceAuthoritiesQuery,
|
|
4
|
+
ListUserResourceAuthoritiesResponse,
|
|
5
|
+
RevokeUserResourceGrantQuery,
|
|
6
|
+
UserResourceGrantMutationResponse,
|
|
7
|
+
UserResourceGrantRevocationResponse,
|
|
8
|
+
} from "@opengeni/contracts";
|
|
9
|
+
import {
|
|
10
|
+
issueManagedHumanUserResourceGrant,
|
|
11
|
+
listManagedHumanUserResourceAuthorities,
|
|
12
|
+
requireAccessGrantAuthorization,
|
|
13
|
+
revokeManagedHumanUserResourceGrant,
|
|
14
|
+
SessionAuthorizationDeniedError,
|
|
15
|
+
SessionAuthorizationUnavailableError,
|
|
16
|
+
SessionTenancyManagedHumanRequiredError,
|
|
17
|
+
type ApiRouteDeps,
|
|
18
|
+
} from "@opengeni/core";
|
|
19
|
+
import { nestedPostgresSqlState, SessionTenancyNotActivatedError } from "@opengeni/db";
|
|
20
|
+
import type { Context, Hono } from "hono";
|
|
21
|
+
import { HTTPException } from "hono/http-exception";
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
|
|
24
|
+
async function body<S extends z.ZodType>(context: Context, schema: S): Promise<z.infer<S>> {
|
|
25
|
+
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
26
|
+
if (!parsed.success) throw new HTTPException(422, { message: "invalid user-resource request" });
|
|
27
|
+
return parsed.data;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function lifecycleError(error: unknown): never {
|
|
31
|
+
if (
|
|
32
|
+
error instanceof SessionAuthorizationDeniedError ||
|
|
33
|
+
error instanceof SessionTenancyManagedHumanRequiredError ||
|
|
34
|
+
error instanceof SessionTenancyNotActivatedError
|
|
35
|
+
) {
|
|
36
|
+
throw new HTTPException(403, { message: "user-resource authority denied" });
|
|
37
|
+
}
|
|
38
|
+
if (error instanceof SessionAuthorizationUnavailableError) {
|
|
39
|
+
throw new HTTPException(503, { message: "session authorization unavailable" });
|
|
40
|
+
}
|
|
41
|
+
if (nestedPostgresSqlState(error) === "42501") {
|
|
42
|
+
throw new HTTPException(403, { message: "user-resource authority denied" });
|
|
43
|
+
}
|
|
44
|
+
if (nestedPostgresSqlState(error) === "22023") {
|
|
45
|
+
throw new HTTPException(422, { message: "invalid user-resource request" });
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function registerUserResourceAuthorityRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
51
|
+
const base = "/v1/workspaces/:workspaceId/user-resource-authorities";
|
|
52
|
+
app.get(base, async (context) => {
|
|
53
|
+
const query = ListUserResourceAuthoritiesQuery.safeParse({
|
|
54
|
+
scope: context.req.query("scope"),
|
|
55
|
+
resourceKind: context.req.query("resourceKind"),
|
|
56
|
+
cursor: context.req.query("cursor"),
|
|
57
|
+
limit: context.req.query("limit"),
|
|
58
|
+
});
|
|
59
|
+
if (!query.success) throw new HTTPException(422, { message: "explicit scope=user required" });
|
|
60
|
+
const workspaceId = context.req.param("workspaceId");
|
|
61
|
+
const access = await requireAccessGrantAuthorization(
|
|
62
|
+
context,
|
|
63
|
+
deps,
|
|
64
|
+
workspaceId,
|
|
65
|
+
"workspace:read",
|
|
66
|
+
);
|
|
67
|
+
try {
|
|
68
|
+
const page = await listManagedHumanUserResourceAuthorities(
|
|
69
|
+
deps,
|
|
70
|
+
access,
|
|
71
|
+
workspaceId,
|
|
72
|
+
query.data,
|
|
73
|
+
);
|
|
74
|
+
return context.json(
|
|
75
|
+
ListUserResourceAuthoritiesResponse.parse({
|
|
76
|
+
scope: "user",
|
|
77
|
+
...page,
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return lifecycleError(error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
app.post(`${base}/:authorityId/grants`, async (context) => {
|
|
86
|
+
const workspaceId = context.req.param("workspaceId");
|
|
87
|
+
const authorityId = z.string().uuid().safeParse(context.req.param("authorityId"));
|
|
88
|
+
if (!authorityId.success) throw new HTTPException(422, { message: "invalid authority id" });
|
|
89
|
+
const request = await body(context, IssueUserResourceGrantRequest);
|
|
90
|
+
const access = await requireAccessGrantAuthorization(
|
|
91
|
+
context,
|
|
92
|
+
deps,
|
|
93
|
+
workspaceId,
|
|
94
|
+
"workspace:read",
|
|
95
|
+
);
|
|
96
|
+
try {
|
|
97
|
+
return context.json(
|
|
98
|
+
UserResourceGrantMutationResponse.parse({
|
|
99
|
+
scope: "user",
|
|
100
|
+
grant: await issueManagedHumanUserResourceGrant(
|
|
101
|
+
deps,
|
|
102
|
+
access,
|
|
103
|
+
workspaceId,
|
|
104
|
+
authorityId.data,
|
|
105
|
+
request,
|
|
106
|
+
"http",
|
|
107
|
+
),
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return lifecycleError(error);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
app.delete(`${base}/grants/:grantId`, async (context) => {
|
|
116
|
+
const query = RevokeUserResourceGrantQuery.safeParse({ scope: context.req.query("scope") });
|
|
117
|
+
if (!query.success) throw new HTTPException(422, { message: "explicit scope=user required" });
|
|
118
|
+
const grantId = z.string().uuid().safeParse(context.req.param("grantId"));
|
|
119
|
+
if (!grantId.success) throw new HTTPException(422, { message: "invalid grant id" });
|
|
120
|
+
const workspaceId = context.req.param("workspaceId");
|
|
121
|
+
const access = await requireAccessGrantAuthorization(
|
|
122
|
+
context,
|
|
123
|
+
deps,
|
|
124
|
+
workspaceId,
|
|
125
|
+
"workspace:read",
|
|
126
|
+
);
|
|
127
|
+
try {
|
|
128
|
+
return context.json(
|
|
129
|
+
UserResourceGrantRevocationResponse.parse({
|
|
130
|
+
scope: "user",
|
|
131
|
+
grant: await revokeManagedHumanUserResourceGrant(deps, access, workspaceId, grantId.data),
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return lifecycleError(error);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|