@opengeni/api-router 0.30.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-grant-rls.d.ts +3 -0
- package/dist/admission-trace.d.ts +8 -0
- package/dist/api-websocket.d.ts +18 -0
- package/dist/app.d.ts +50 -2
- package/dist/app.js +12 -1
- package/dist/artifact-catalog-cursor.d.ts +27 -0
- package/dist/auth/managed-auth-attempt-context.d.ts +10 -0
- package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
- package/dist/auth/managed-auth.d.ts +102 -3
- package/dist/auth/managed-email.d.ts +29 -0
- package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
- package/dist/auth/new-signups-gate.d.ts +23 -0
- package/dist/auth/organization-user-setup.d.ts +80 -0
- package/dist/auth/signup-funnel-metrics.d.ts +44 -0
- package/dist/browser-controller-authority.d.ts +1 -61
- package/dist/browser-ephemeral.d.ts +5 -0
- package/dist/browser-state-authority.d.ts +1 -27
- package/dist/chunk-6TZ5LG3O.js +123948 -0
- package/dist/chunk-6TZ5LG3O.js.map +1 -0
- package/dist/chunk-OGQDXVXY.js +18 -0
- package/dist/chunk-OGQDXVXY.js.map +1 -0
- package/dist/claude-subscription-account-conflicts.d.ts +1 -0
- package/dist/claude-subscription-account-usage.d.ts +39 -0
- package/dist/claude-subscription-oauth.d.ts +25 -0
- package/dist/claude-subscription-profile.d.ts +9 -0
- package/dist/claude-subscription-usage.d.ts +44 -0
- package/dist/claude-workspace-connection.d.ts +15 -0
- package/dist/codemode.d.ts +32 -1
- package/dist/company-brain-okf.d.ts +24 -0
- package/dist/connected-machine-computer-access.d.ts +11 -0
- package/dist/connection-ownership.d.ts +54 -0
- package/dist/controller-data-plane.d.ts +32 -0
- package/dist/editable-artifact-office-import.d.ts +7 -4
- package/dist/editable-artifact-source-session.d.ts +3 -0
- package/dist/editable-artifact-websocket.d.ts +10 -18
- package/dist/editable-artifact-workspace-files.d.ts +1 -1
- package/dist/fatal-process-boundary.d.ts +25 -0
- package/dist/github-access.d.ts +31 -2
- package/dist/http/acting-person.d.ts +24 -0
- package/dist/http/api-contract-compat.d.ts +19 -0
- package/dist/http/api-error.d.ts +27 -1
- package/dist/http/cors.d.ts +17 -0
- package/dist/http/deprecation.d.ts +42 -0
- package/dist/http/interaction-control-error.d.ts +13 -0
- package/dist/http/keyed-admission.d.ts +12 -0
- package/dist/http/local-browser-boundary.d.ts +72 -0
- package/dist/http/metrics-listener.d.ts +22 -0
- package/dist/http/path-identifier.d.ts +17 -0
- package/dist/http/registered-route-label.d.ts +16 -0
- package/dist/http/request-body.d.ts +27 -0
- package/dist/http/request-source.d.ts +52 -0
- package/dist/http/sse.d.ts +17 -6
- package/dist/http/unmatched-route.d.ts +18 -0
- package/dist/http/user-content.d.ts +36 -0
- package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +1129 -166
- package/dist/index.js.map +1 -1
- package/dist/integration-endpoint-test.d.ts +20 -0
- package/dist/integrations/api-integrations.d.ts +2 -2
- package/dist/integrations/atlassian.d.ts +29 -3
- package/dist/integrations/connect-authority.d.ts +1 -0
- package/dist/integrations/connect-callback-return.d.ts +6 -0
- package/dist/integrations/fiken.d.ts +16 -2
- package/dist/integrations/github-app-connect.d.ts +47 -0
- package/dist/integrations/github-installation-proof.d.ts +3 -0
- package/dist/integrations/github-lens-connect.d.ts +116 -0
- package/dist/integrations/github-skill-source.d.ts +1 -5
- package/dist/integrations/google-drive.d.ts +25 -0
- package/dist/integrations/integration-commit-authority.d.ts +13 -0
- package/dist/integrations/marketplace-plugin.d.ts +2691 -0
- package/dist/integrations/oauth-client.d.ts +178 -18
- package/dist/integrations/oauth-profiles.d.ts +222 -0
- package/dist/integrations/oauth-return-path.d.ts +1 -0
- package/dist/integrations/personal-github-repositories.d.ts +84 -0
- package/dist/integrations/personal-github.d.ts +42 -0
- package/dist/integrations/pr-review-provider.d.ts +22 -0
- package/dist/integrations/provider-oauth.d.ts +19 -1
- package/dist/integrations/slack-app-home.d.ts +24 -0
- package/dist/integrations/slack-bot.d.ts +367 -12
- package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
- package/dist/integrations/slack-install.d.ts +20 -0
- package/dist/integrations/slack-interactions.d.ts +113 -10
- package/dist/integrations/slack-mrkdwn.d.ts +41 -0
- package/dist/integrations/slack-routing.d.ts +120 -0
- package/dist/integrations/slack-session-defaults.d.ts +58 -0
- package/dist/integrations/slack-task-file-upload.d.ts +36 -0
- package/dist/integrations/social-oauth.d.ts +7 -0
- package/dist/interaction-agent-access.d.ts +15 -0
- package/dist/interaction-frame-proxy.d.ts +68 -0
- package/dist/interaction-metrics.d.ts +2 -0
- package/dist/mcp/action-catalog-types.d.ts +9 -0
- package/dist/mcp/action-catalog.gen.d.ts +2 -0
- package/dist/mcp/capability-account-readiness.d.ts +7 -0
- package/dist/mcp/child-read-evidence.d.ts +9 -0
- package/dist/mcp/command-read.d.ts +24 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
- package/dist/mcp/contract-input.d.ts +11 -0
- package/dist/mcp/documents.d.ts +7 -5
- package/dist/mcp/files.d.ts +2 -2
- package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
- package/dist/mcp/knowledge-entries.d.ts +5 -0
- package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
- package/dist/mcp/receipts.d.ts +9 -0
- package/dist/mcp/request-abort.d.ts +19 -0
- package/dist/mcp/scheduled-task-input.d.ts +1189 -0
- package/dist/mcp/scheduled-task-view.d.ts +22 -4
- package/dist/mcp/server.d.ts +255 -3
- package/dist/mcp/session-event-view.d.ts +94 -0
- package/dist/mcp/session-view.d.ts +66 -2
- package/dist/mcp/session-wait.d.ts +174 -0
- package/dist/mcp-oauth.d.ts +52 -0
- package/dist/model-catalog.d.ts +11 -31
- package/dist/observability.d.ts +3 -1
- package/dist/organization-mcp.d.ts +52 -0
- package/dist/organization-recovery-notifications.d.ts +41 -0
- package/dist/routes/analytics-consent.d.ts +26 -0
- package/dist/routes/api-integrations.d.ts +25 -0
- package/dist/routes/api-keys.d.ts +17 -0
- package/dist/routes/artifact-catalog.d.ts +6 -0
- package/dist/routes/automations.d.ts +19 -0
- package/dist/routes/billing.d.ts +49 -0
- package/dist/routes/browser-sessions.d.ts +71 -3
- package/dist/routes/claude-subscription-accounts.d.ts +3 -0
- package/dist/routes/claude-subscription-oauth.d.ts +3 -0
- package/dist/routes/client-errors.d.ts +146 -0
- package/dist/routes/codex.d.ts +76 -2
- package/dist/routes/company-brain.d.ts +3 -0
- package/dist/routes/company-profile.d.ts +2 -1
- package/dist/routes/computer-sessions.d.ts +14 -0
- package/dist/routes/connect.d.ts +5 -0
- package/dist/routes/editable-artifacts.d.ts +2 -0
- package/dist/routes/external-identity-links.d.ts +6 -0
- package/dist/routes/feedback.d.ts +3 -0
- package/dist/routes/files.d.ts +4 -0
- package/dist/routes/github-browser-pages.d.ts +12 -0
- package/dist/routes/github.d.ts +2 -0
- package/dist/routes/insights-response-cache.d.ts +10 -0
- package/dist/routes/insights-usage.d.ts +12 -0
- package/dist/routes/insights.d.ts +30 -1
- package/dist/routes/knowledge.d.ts +3 -0
- package/dist/routes/managed-auth-session-sets.d.ts +26 -0
- package/dist/routes/managed-onboarding.d.ts +29 -0
- package/dist/routes/managed-sign-in-methods.d.ts +6 -0
- package/dist/routes/model-connection-access.d.ts +3 -0
- package/dist/routes/organization-integration-policy.d.ts +3 -0
- package/dist/routes/organization-integrations.d.ts +6 -0
- package/dist/routes/organization-mcp-connections.d.ts +3 -0
- package/dist/routes/organization-model-providers.d.ts +3 -0
- package/dist/routes/organization-recovery.d.ts +19 -0
- package/dist/routes/organization-service-accounts.d.ts +3 -0
- package/dist/routes/organization-sessions.d.ts +26 -0
- package/dist/routes/personal-github-git-broker.d.ts +29 -0
- package/dist/routes/personal-github.d.ts +3 -0
- package/dist/routes/pr-review-github.d.ts +3 -0
- package/dist/routes/pr-review.d.ts +3 -0
- package/dist/routes/session-artifact-associations.d.ts +4 -0
- package/dist/routes/session-history-imports.d.ts +9 -0
- package/dist/routes/sessions.d.ts +44 -6
- package/dist/routes/skill-content.d.ts +4 -0
- package/dist/routes/skill-install-authority.d.ts +6 -0
- package/dist/routes/subscription-account-pools.d.ts +34 -0
- package/dist/routes/subscription-pool-access.d.ts +16 -0
- package/dist/routes/supergrok.d.ts +6 -1
- package/dist/routes/usage-allowances.d.ts +12 -0
- package/dist/routes/user-resource-authorities.d.ts +3 -0
- package/dist/routes/workspace-artifacts.d.ts +2 -1
- package/dist/routes/workspace-capture.d.ts +10 -2
- package/dist/routes/workspace-integrations.d.ts +35 -0
- package/dist/routes/workspace-learning.d.ts +3 -0
- package/dist/routes/workspace-model-providers.d.ts +4 -0
- package/dist/routes/workspaces.d.ts +29 -0
- package/dist/sandbox/auth-callout.d.ts +8 -0
- package/dist/sandbox/channel-a.d.ts +19 -1
- package/dist/sandbox/connection-authority.d.ts +3 -0
- package/dist/sandbox/enrollment.d.ts +9 -1
- package/dist/sandbox/machines.d.ts +5 -2
- package/dist/sandbox/metrics-ingestion.d.ts +28 -20
- package/dist/sandbox/rematerialize.d.ts +2 -0
- package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
- package/dist/sandbox/viewer.d.ts +26 -1
- package/dist/sandbox-file-artifacts.d.ts +50 -0
- package/dist/scheduled-task-deletion.d.ts +13 -0
- package/dist/session-create-observability.d.ts +5 -0
- package/dist/session-event-view-MPS2OZD3.js +292 -0
- package/dist/session-event-view-MPS2OZD3.js.map +1 -0
- package/dist/site-session-origin.d.ts +13 -0
- package/dist/site-uploads.d.ts +41 -0
- package/dist/slack-reaction-files.d.ts +1 -1
- package/dist/temporal-schedule-cleanup.d.ts +1 -0
- package/dist/temporal-schedule-sync.d.ts +26 -0
- package/dist/transcription/providers/xai-subscription.d.ts +1 -0
- package/dist/transcription/service.d.ts +2 -1
- package/dist/work-discovery-observability.d.ts +33 -0
- package/dist/workspace-artifact-content.d.ts +28 -0
- package/dist/workspace-artifact-provenance.d.ts +7 -0
- package/dist/workspace-delete-observability.d.ts +10 -0
- package/dist/workspace-deletion.d.ts +6 -0
- package/dist/workspace-state-projection.d.ts +3 -5
- package/dist/workspace-tool-gateway-observability.d.ts +17 -0
- package/dist/workspace-tool-gateway.d.ts +123 -0
- package/dist/workspace-webhook-dispatch.d.ts +26 -0
- package/package.json +23 -19
- package/src/access-grant-rls.ts +75 -0
- package/src/admission-trace.ts +14 -0
- package/src/api-websocket.ts +23 -0
- package/src/app.ts +1974 -256
- package/src/artifact-catalog-cursor.ts +60 -0
- package/src/auth/managed-auth-attempt-context.ts +70 -0
- package/src/auth/managed-auth-rate-limits.ts +252 -0
- package/src/auth/managed-auth-session-adapter.ts +222 -0
- package/src/auth/managed-auth.ts +536 -60
- package/src/auth/managed-email.ts +174 -0
- package/src/auth/managed-sign-in-notifications.ts +81 -0
- package/src/auth/new-signups-gate.ts +49 -0
- package/src/auth/organization-user-setup.ts +307 -0
- package/src/auth/signup-funnel-metrics.ts +164 -0
- package/src/browser-controller-authority.ts +1 -186
- package/src/browser-ephemeral.ts +39 -0
- package/src/browser-state-authority.ts +1 -236
- package/src/claude-subscription-account-conflicts.ts +12 -0
- package/src/claude-subscription-account-usage.ts +48 -0
- package/src/claude-subscription-oauth.ts +362 -0
- package/src/claude-subscription-profile.ts +73 -0
- package/src/claude-subscription-usage.ts +97 -0
- package/src/claude-workspace-connection.ts +82 -0
- package/src/codemode.ts +194 -30
- package/src/codex-realtime.ts +8 -2
- package/src/company-brain-okf.ts +340 -0
- package/src/connected-machine-computer-access.ts +33 -0
- package/src/connection-ownership.ts +199 -0
- package/src/controller-data-plane.ts +81 -0
- package/src/editable-artifact-native-kernel.ts +26 -15
- package/src/editable-artifact-office-import.ts +77 -9
- package/src/editable-artifact-production.ts +23 -7
- package/src/editable-artifact-source-session.ts +123 -0
- package/src/editable-artifact-websocket.ts +27 -21
- package/src/editable-artifact-workspace-files.ts +31 -6
- package/src/fatal-process-boundary.ts +246 -0
- package/src/gateway-realtime.ts +1 -1
- package/src/github-access.ts +147 -5
- package/src/http/acting-person.ts +71 -0
- package/src/http/api-contract-compat.ts +49 -0
- package/src/http/api-error.ts +115 -1
- package/src/http/auth.ts +48 -0
- package/src/http/cors.ts +67 -0
- package/src/http/deprecation.ts +118 -0
- package/src/http/interaction-control-error.ts +164 -0
- package/src/http/keyed-admission.ts +29 -0
- package/src/http/local-browser-boundary.ts +343 -0
- package/src/http/metrics-listener.ts +57 -0
- package/src/http/path-identifier.ts +54 -0
- package/src/http/registered-route-label.ts +34 -0
- package/src/http/request-body.ts +127 -0
- package/src/http/request-source.ts +178 -0
- package/src/http/sse.ts +486 -132
- package/src/http/unmatched-route.ts +60 -0
- package/src/http/user-content.ts +158 -0
- package/src/http/websocket-upgrade-dispatch.ts +29 -0
- package/src/index.ts +157 -52
- package/src/integration-endpoint-test.ts +291 -0
- package/src/integrations/api-integrations.ts +98 -11
- package/src/integrations/atlassian.ts +474 -136
- package/src/integrations/connect-authority.ts +2 -0
- package/src/integrations/connect-callback-return.ts +86 -0
- package/src/integrations/fiken.ts +354 -64
- package/src/integrations/github-app-connect.ts +435 -0
- package/src/integrations/github-installation-proof.ts +60 -0
- package/src/integrations/github-lens-connect.ts +76 -0
- package/src/integrations/github-skill-source.ts +2 -142
- package/src/integrations/google-drive.ts +820 -222
- package/src/integrations/integration-commit-authority.ts +69 -0
- package/src/integrations/marketplace-plugin.ts +48 -0
- package/src/integrations/oauth-client.ts +1356 -459
- package/src/integrations/oauth-profiles.ts +537 -0
- package/src/integrations/oauth-return-path.ts +18 -0
- package/src/integrations/personal-github-repositories.ts +674 -0
- package/src/integrations/personal-github.ts +825 -0
- package/src/integrations/pr-review-provider.ts +246 -0
- package/src/integrations/provider-oauth.ts +250 -22
- package/src/integrations/slack-app-home.ts +300 -0
- package/src/integrations/slack-bot.ts +963 -239
- package/src/integrations/slack-file-upload-flow.ts +147 -0
- package/src/integrations/slack-install.ts +83 -0
- package/src/integrations/slack-interactions.ts +3170 -474
- package/src/integrations/slack-mrkdwn.ts +193 -0
- package/src/integrations/slack-routing.ts +337 -0
- package/src/integrations/slack-session-defaults.ts +224 -0
- package/src/integrations/slack-task-file-upload.ts +323 -0
- package/src/integrations/social-oauth.ts +213 -15
- package/src/interaction-agent-access.ts +48 -0
- package/src/interaction-frame-proxy.ts +409 -0
- package/src/interaction-metrics.ts +30 -0
- package/src/mcp/action-catalog-types.ts +9 -0
- package/src/mcp/action-catalog.gen.ts +6696 -0
- package/src/mcp/capability-account-readiness.ts +17 -0
- package/src/mcp/child-read-evidence.ts +19 -0
- package/src/mcp/command-read.ts +109 -0
- package/src/mcp/company-profile-agent-admin.ts +198 -0
- package/src/mcp/contract-input.ts +141 -0
- package/src/mcp/documents.ts +65 -377
- package/src/mcp/editable-artifacts.ts +12 -6
- package/src/mcp/files.ts +80 -38
- package/src/mcp/first-party-tool-permissions.ts +305 -0
- package/src/mcp/knowledge-entries.ts +322 -0
- package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
- package/src/mcp/receipts.ts +34 -0
- package/src/mcp/request-abort.ts +44 -0
- package/src/mcp/scheduled-task-input.ts +76 -0
- package/src/mcp/scheduled-task-view.ts +44 -3
- package/src/mcp/server.ts +3030 -1267
- package/src/mcp/session-event-view.ts +383 -0
- package/src/mcp/session-view.ts +84 -3
- package/src/mcp/session-wait.ts +665 -0
- package/src/mcp-oauth.ts +1123 -0
- package/src/model-catalog.ts +91 -333
- package/src/observability.ts +15 -1
- package/src/organization-mcp.ts +397 -0
- package/src/organization-recovery-notifications.ts +103 -0
- package/src/routes/analytics-consent.ts +123 -0
- package/src/routes/api-integrations.ts +364 -74
- package/src/routes/api-keys.ts +336 -10
- package/src/routes/artifact-catalog.ts +279 -0
- package/src/routes/automations.ts +768 -0
- package/src/routes/billing.ts +461 -46
- package/src/routes/browser-sessions.ts +839 -131
- package/src/routes/canonical-human-identities.ts +90 -14
- package/src/routes/capabilities.ts +107 -6
- package/src/routes/channels.ts +20 -3
- package/src/routes/claude-subscription-accounts.ts +334 -0
- package/src/routes/claude-subscription-oauth.ts +142 -0
- package/src/routes/client-errors.ts +362 -0
- package/src/routes/codex.ts +833 -116
- package/src/routes/company-brain.ts +322 -0
- package/src/routes/company-profile.ts +65 -1
- package/src/routes/computer-sessions.ts +557 -106
- package/src/routes/connect.ts +1938 -0
- package/src/routes/connections.ts +1040 -349
- package/src/routes/documents.ts +488 -287
- package/src/routes/editable-artifacts.ts +79 -9
- package/src/routes/enrollments.ts +135 -39
- package/src/routes/environments.ts +154 -58
- package/src/routes/external-identity-links.ts +201 -0
- package/src/routes/feedback.ts +113 -0
- package/src/routes/files.ts +337 -39
- package/src/routes/github-browser-pages.ts +162 -0
- package/src/routes/github.ts +446 -144
- package/src/routes/insights-response-cache.ts +83 -0
- package/src/routes/insights-usage.ts +345 -0
- package/src/routes/insights.ts +153 -19
- package/src/routes/install.ts +133 -38
- package/src/routes/integration-facets.ts +107 -33
- package/src/routes/interaction-resources.ts +30 -3
- package/src/routes/knowledge.ts +488 -0
- package/src/routes/machines.ts +313 -15
- package/src/routes/managed-auth-session-sets.ts +1218 -0
- package/src/routes/managed-onboarding.ts +337 -0
- package/src/routes/managed-sign-in-methods.ts +433 -0
- package/src/routes/memory-slack-publications.ts +3 -2
- package/src/routes/model-connection-access.ts +208 -0
- package/src/routes/organization-integration-policy.ts +116 -0
- package/src/routes/organization-integrations.ts +326 -0
- package/src/routes/organization-mcp-connections.ts +178 -0
- package/src/routes/organization-memberships.ts +1019 -27
- package/src/routes/organization-model-providers.ts +293 -0
- package/src/routes/organization-recovery.ts +325 -0
- package/src/routes/organization-service-accounts.ts +128 -0
- package/src/routes/organization-sessions.ts +350 -0
- package/src/routes/personal-github-git-broker.ts +785 -0
- package/src/routes/personal-github.ts +370 -0
- package/src/routes/plugins.ts +251 -22
- package/src/routes/pr-review-github.ts +881 -0
- package/src/routes/pr-review.ts +622 -0
- package/src/routes/preference-registry.ts +12 -93
- package/src/routes/rigs.ts +167 -50
- package/src/routes/scheduled-tasks.ts +346 -41
- package/src/routes/session-artifact-associations.ts +87 -0
- package/src/routes/session-history-imports.ts +280 -0
- package/src/routes/sessions.ts +2306 -264
- package/src/routes/skill-content.ts +297 -0
- package/src/routes/skill-install-authority.ts +64 -0
- package/src/routes/skills.ts +62 -9
- package/src/routes/social.ts +46 -18
- package/src/routes/subscription-account-pools.ts +436 -0
- package/src/routes/subscription-pool-access.ts +126 -0
- package/src/routes/supergrok.ts +219 -303
- package/src/routes/transcription-recordings.ts +38 -3
- package/src/routes/transcriptions.ts +4 -0
- package/src/routes/usage-allowances.ts +378 -0
- package/src/routes/user-resource-authorities.ts +138 -0
- package/src/routes/video-generation.ts +14 -10
- package/src/routes/workspace-artifacts.ts +275 -69
- package/src/routes/workspace-capture.ts +21 -0
- package/src/routes/workspace-integrations.ts +576 -0
- package/src/routes/workspace-learning.ts +159 -0
- package/src/routes/workspace-model-providers.ts +148 -0
- package/src/routes/workspace-state.ts +38 -25
- package/src/routes/workspaces.ts +1004 -127
- package/src/sandbox/auth-callout.ts +96 -19
- package/src/sandbox/channel-a.ts +323 -24
- package/src/sandbox/connection-authority.ts +3 -0
- package/src/sandbox/enrollment.ts +81 -6
- package/src/sandbox/machines.ts +218 -76
- package/src/sandbox/metrics-ingestion.ts +411 -62
- package/src/sandbox/rematerialize.ts +121 -19
- package/src/sandbox/session-attach-variable-sets.ts +131 -0
- package/src/sandbox/viewer.ts +280 -25
- package/src/sandbox-file-artifacts.ts +413 -0
- package/src/scheduled-task-deletion.ts +138 -0
- package/src/session-create-observability.ts +46 -0
- package/src/session-realtime-context.ts +6 -3
- package/src/site-session-origin.ts +48 -0
- package/src/site-uploads.ts +224 -0
- package/src/slack-reaction-files.ts +16 -5
- package/src/temporal-schedule-cleanup.ts +13 -0
- package/src/temporal-schedule-sync.ts +74 -0
- package/src/transcription/providers/codex-subscription.ts +2 -0
- package/src/transcription/providers/openai.ts +1 -0
- package/src/transcription/providers/xai-subscription.ts +49 -3
- package/src/transcription/segmenter.ts +8 -2
- package/src/transcription/service.ts +43 -6
- package/src/work-discovery-observability.ts +121 -0
- package/src/workspace-artifact-content.ts +144 -0
- package/src/workspace-artifact-provenance.ts +104 -0
- package/src/workspace-delete-observability.ts +75 -0
- package/src/workspace-deletion.ts +73 -0
- package/src/workspace-state-projection.ts +18 -148
- package/src/workspace-tool-gateway-observability.ts +100 -0
- package/src/workspace-tool-gateway.ts +870 -0
- package/src/workspace-webhook-dispatch.ts +190 -0
- package/src/xai-subscription-auth.ts +9 -2
- package/dist/chunk-2PQMSRCB.js +0 -49759
- package/dist/chunk-2PQMSRCB.js.map +0 -1
- package/dist/routes/packs.d.ts +0 -3
- package/src/routes/packs.ts +0 -729
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
editableArtifactScope,
|
|
12
12
|
hasPermission,
|
|
13
13
|
requireAccessGrant,
|
|
14
|
+
requireAccessGrantAuthorization,
|
|
15
|
+
externalActorContinuationForAuthorization,
|
|
16
|
+
requirePermission,
|
|
14
17
|
requireSessionAuthorization,
|
|
15
18
|
SessionAuthorizationDeniedError,
|
|
16
19
|
SessionAuthorizationUnavailableError,
|
|
@@ -25,11 +28,13 @@ import {
|
|
|
25
28
|
type EditableArtifactPinnedVersion,
|
|
26
29
|
type EditableArtifactModality,
|
|
27
30
|
} from "@opengeni/core";
|
|
28
|
-
import { listEditableArtifactIdsForSession } from "@opengeni/db";
|
|
31
|
+
import { hasEditableArtifactSessionLink, listEditableArtifactIdsForSession } from "@opengeni/db";
|
|
29
32
|
import type { Context, Hono } from "hono";
|
|
30
33
|
import { z } from "zod";
|
|
31
34
|
|
|
32
35
|
import { ApiHttpError } from "../http/api-error";
|
|
36
|
+
import { USER_CONTENT_SECURITY_HEADERS } from "../http/user-content";
|
|
37
|
+
import { withAccessGrantSessionRlsContext } from "../access-grant-rls";
|
|
33
38
|
|
|
34
39
|
export const EDITABLE_ARTIFACT_HTTP_REQUEST_MAX_BYTES = 4 * 1024;
|
|
35
40
|
export const EDITABLE_ARTIFACT_EXPORT_REQUEST_MAX_BYTES = 260 * 1024;
|
|
@@ -77,10 +82,15 @@ const ListEditableArtifactsQuery = z
|
|
|
77
82
|
|
|
78
83
|
const MintEditableArtifactLiveTicketRequest = z
|
|
79
84
|
.object({
|
|
85
|
+
sourceSessionId: SessionId.optional(),
|
|
80
86
|
replicaId: ReplicaId,
|
|
81
|
-
|
|
87
|
+
modality: z.enum(["document", "spreadsheet", "presentation"]),
|
|
88
|
+
liveProtocolVersion: PositiveProtocolVersion,
|
|
82
89
|
kernelVersion: VersionName,
|
|
83
90
|
modelSchemaVersion: PositiveModelSchemaVersion,
|
|
91
|
+
snapshotVersion: PositiveProtocolVersion,
|
|
92
|
+
commandProtocolVersion: PositiveProtocolVersion,
|
|
93
|
+
committedTransactionProtocolVersion: PositiveProtocolVersion,
|
|
84
94
|
})
|
|
85
95
|
.strict();
|
|
86
96
|
|
|
@@ -467,7 +477,13 @@ export function registerEditableArtifactRoutes(
|
|
|
467
477
|
|
|
468
478
|
// Workspace authentication precedes path/body parsing. Exact artifact
|
|
469
479
|
// permission is intentionally enforced inside the injected application.
|
|
470
|
-
const
|
|
480
|
+
const authorization = await requireAccessGrantAuthorization(
|
|
481
|
+
context,
|
|
482
|
+
deps,
|
|
483
|
+
workspaceId,
|
|
484
|
+
"artifacts:read",
|
|
485
|
+
);
|
|
486
|
+
const grant = authorization.grant;
|
|
471
487
|
const artifactId = editableArtifactId(parseArtifactId(context.req.param("artifactId")));
|
|
472
488
|
const body = await parseBoundedTicketRequest(context);
|
|
473
489
|
const actor = editableArtifactActorForGrant(grant, body.replicaId);
|
|
@@ -475,6 +491,18 @@ export function registerEditableArtifactRoutes(
|
|
|
475
491
|
accountId: grant.accountId,
|
|
476
492
|
workspaceId,
|
|
477
493
|
});
|
|
494
|
+
if (body.sourceSessionId) {
|
|
495
|
+
requirePermission(grant, "sessions:read");
|
|
496
|
+
await withAccessGrantSessionRlsContext(deps, grant, async () => {
|
|
497
|
+
await authorizeSourceSession(deps, grant, body.sourceSessionId!);
|
|
498
|
+
if (
|
|
499
|
+
!(await hasEditableArtifactSessionLink(deps.db, scope, body.sourceSessionId!, artifactId))
|
|
500
|
+
) {
|
|
501
|
+
throw applicationHttpError("not_found");
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
const externalContinuation = externalActorContinuationForAuthorization(authorization);
|
|
478
506
|
|
|
479
507
|
let rawTicket: MintEditableArtifactLiveTicketOutput;
|
|
480
508
|
try {
|
|
@@ -482,10 +510,52 @@ export function registerEditableArtifactRoutes(
|
|
|
482
510
|
scope,
|
|
483
511
|
actor,
|
|
484
512
|
artifactId,
|
|
485
|
-
|
|
513
|
+
modality: body.modality,
|
|
514
|
+
liveProtocolVersion: body.liveProtocolVersion,
|
|
486
515
|
kernelVersion: body.kernelVersion,
|
|
487
516
|
modelSchemaVersion: body.modelSchemaVersion,
|
|
517
|
+
snapshotVersion: body.snapshotVersion,
|
|
518
|
+
commandProtocolVersion: body.commandProtocolVersion,
|
|
519
|
+
committedTransactionProtocolVersion: body.committedTransactionProtocolVersion,
|
|
488
520
|
allowEdit: hasPermission(grant.permissions, "artifacts:publish"),
|
|
521
|
+
...(body.sourceSessionId
|
|
522
|
+
? {
|
|
523
|
+
sourceSessionAuthority: {
|
|
524
|
+
sessionId: body.sourceSessionId,
|
|
525
|
+
grant: {
|
|
526
|
+
accountId: grant.accountId,
|
|
527
|
+
workspaceId: grant.workspaceId,
|
|
528
|
+
subjectId: grant.subjectId,
|
|
529
|
+
...(grant.principalKind ? { principalKind: grant.principalKind } : {}),
|
|
530
|
+
...(grant.metadata
|
|
531
|
+
? {
|
|
532
|
+
metadata: Object.fromEntries(
|
|
533
|
+
Object.entries(grant.metadata).filter(([key]) =>
|
|
534
|
+
[
|
|
535
|
+
"externalActor",
|
|
536
|
+
"delegated",
|
|
537
|
+
"sessionId",
|
|
538
|
+
"turnId",
|
|
539
|
+
"attemptId",
|
|
540
|
+
"executionGeneration",
|
|
541
|
+
].includes(key),
|
|
542
|
+
),
|
|
543
|
+
),
|
|
544
|
+
}
|
|
545
|
+
: {}),
|
|
546
|
+
// Bind only editor capabilities, not a broad delegated wildcard.
|
|
547
|
+
permissions: [
|
|
548
|
+
"sessions:read",
|
|
549
|
+
"artifacts:read",
|
|
550
|
+
...(hasPermission(grant.permissions, "artifacts:publish")
|
|
551
|
+
? ["artifacts:publish" as const]
|
|
552
|
+
: []),
|
|
553
|
+
],
|
|
554
|
+
},
|
|
555
|
+
...(externalContinuation ? { externalContinuation } : {}),
|
|
556
|
+
},
|
|
557
|
+
}
|
|
558
|
+
: {}),
|
|
489
559
|
});
|
|
490
560
|
} catch (error) {
|
|
491
561
|
throw editableArtifactHttpError(error);
|
|
@@ -496,7 +566,7 @@ export function registerEditableArtifactRoutes(
|
|
|
496
566
|
!parsedTicket.success ||
|
|
497
567
|
parsedTicket.data.artifactId !== artifactId ||
|
|
498
568
|
parsedTicket.data.replicaId !== actor.replicaId ||
|
|
499
|
-
parsedTicket.data.protocolVersion !== body.
|
|
569
|
+
parsedTicket.data.protocolVersion !== body.liveProtocolVersion ||
|
|
500
570
|
Date.parse(parsedTicket.data.expiresAt) <= Date.now()
|
|
501
571
|
) {
|
|
502
572
|
throw new Error("Editable artifact application returned an invalid live ticket");
|
|
@@ -608,7 +678,7 @@ export function registerEditableArtifactRoutes(
|
|
|
608
678
|
"content-length": String(download.byteSize),
|
|
609
679
|
"content-type": download.mimeType,
|
|
610
680
|
etag: `"${download.contentHash}"`,
|
|
611
|
-
|
|
681
|
+
...USER_CONTENT_SECURITY_HEADERS,
|
|
612
682
|
},
|
|
613
683
|
});
|
|
614
684
|
});
|
|
@@ -901,7 +971,7 @@ function normalizeArtifactIdList(value: readonly string[]): readonly string[] {
|
|
|
901
971
|
return ids;
|
|
902
972
|
}
|
|
903
973
|
|
|
904
|
-
function isInvisibleListCandidate(error: unknown): boolean {
|
|
974
|
+
export function isInvisibleListCandidate(error: unknown): boolean {
|
|
905
975
|
return (
|
|
906
976
|
(error instanceof EditableArtifactDomainError &&
|
|
907
977
|
(error.code === "forbidden" || error.code === "not_found")) ||
|
|
@@ -1042,7 +1112,7 @@ async function readBoundedJson(request: Request, maxBytes: number): Promise<unkn
|
|
|
1042
1112
|
}
|
|
1043
1113
|
}
|
|
1044
1114
|
|
|
1045
|
-
function editableArtifactHttpError(error: unknown): unknown {
|
|
1115
|
+
export function editableArtifactHttpError(error: unknown): unknown {
|
|
1046
1116
|
if (error instanceof EditableArtifactDurableExportError) {
|
|
1047
1117
|
switch (error.code) {
|
|
1048
1118
|
case "invalid_request":
|
|
@@ -1059,7 +1129,7 @@ function editableArtifactHttpError(error: unknown): unknown {
|
|
|
1059
1129
|
case "unsupported_format":
|
|
1060
1130
|
return new ApiHttpError(422, {
|
|
1061
1131
|
code: "validation_failed",
|
|
1062
|
-
message:
|
|
1132
|
+
message: error.message,
|
|
1063
1133
|
});
|
|
1064
1134
|
case "not_ready":
|
|
1065
1135
|
return new ApiHttpError(409, {
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
MintEnrollTokenRequest,
|
|
38
38
|
MintEnrollTokenResponse,
|
|
39
39
|
RemoveEnrollmentRequest,
|
|
40
|
+
RenewEnrollmentRequest,
|
|
40
41
|
RevokeEnrollmentResponse,
|
|
41
42
|
type EnrollmentArch,
|
|
42
43
|
type EnrollmentOs,
|
|
@@ -50,7 +51,12 @@ import {
|
|
|
50
51
|
} from "@opengeni/db";
|
|
51
52
|
import type { Context, Hono } from "hono";
|
|
52
53
|
import { HTTPException } from "hono/http-exception";
|
|
53
|
-
import {
|
|
54
|
+
import { trustedRequestSourceRateLimitKey } from "../http/request-source";
|
|
55
|
+
import {
|
|
56
|
+
requireAccessGrant,
|
|
57
|
+
requireAccessGrantAuthorization,
|
|
58
|
+
requirePermission,
|
|
59
|
+
} from "@opengeni/core";
|
|
54
60
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
55
61
|
import {
|
|
56
62
|
approveDeviceEnrollment,
|
|
@@ -60,6 +66,7 @@ import {
|
|
|
60
66
|
mintEnrollToken,
|
|
61
67
|
pollDeviceEnrollment,
|
|
62
68
|
startDeviceEnrollment,
|
|
69
|
+
renewEnrollmentCredentials,
|
|
63
70
|
toLookupResponse,
|
|
64
71
|
} from "../sandbox/enrollment";
|
|
65
72
|
|
|
@@ -88,15 +95,33 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
88
95
|
const lookupLimiter = new TokenBucket({ capacity: 30, refillPerSecond: 1 });
|
|
89
96
|
// The headless token exchange (UNAUTHENTICATED — the token is the auth). Bounded
|
|
90
97
|
// against an enroll-token brute force; the `oget_` HMAC is the real boundary.
|
|
91
|
-
const exchangeLimiter = new TokenBucket({
|
|
98
|
+
const exchangeLimiter = new TokenBucket({
|
|
99
|
+
capacity: 20,
|
|
100
|
+
refillPerSecond: 0.5,
|
|
101
|
+
});
|
|
92
102
|
|
|
93
103
|
function rateLimit(c: Context, limiter: TokenBucket): void {
|
|
94
|
-
const ip =
|
|
104
|
+
const ip = trustedRequestSourceRateLimitKey(c, settings);
|
|
95
105
|
if (!limiter.take(ip)) {
|
|
96
106
|
throw new HTTPException(429, { message: "too many requests; slow down" });
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
|
|
110
|
+
// Machine authentication: signed install-key proof + current enrollment grant.
|
|
111
|
+
app.post("/v1/enrollments/renew", async (c) => {
|
|
112
|
+
assertSelfhostedEnabled();
|
|
113
|
+
rateLimit(c, exchangeLimiter);
|
|
114
|
+
const parsed = RenewEnrollmentRequest.safeParse(await c.req.json().catch(() => null));
|
|
115
|
+
if (!parsed.success) throw new HTTPException(400, { message: "invalid renewal request" });
|
|
116
|
+
const credentials = await renewEnrollmentCredentials({ db, settings }, parsed.data);
|
|
117
|
+
if (!credentials)
|
|
118
|
+
throw new HTTPException(401, {
|
|
119
|
+
message: "machine renewal not authorized",
|
|
120
|
+
});
|
|
121
|
+
c.header("Cache-Control", "no-store");
|
|
122
|
+
return c.json(EnrollTokenExchangeResponse.parse({ credentials }), 200);
|
|
123
|
+
});
|
|
124
|
+
|
|
100
125
|
// ── POST /enrollments/device/start (agent-side, user-unauthenticated) ───────
|
|
101
126
|
app.post("/v1/enrollments/device/start", async (c) => {
|
|
102
127
|
assertSelfhostedEnabled();
|
|
@@ -123,8 +148,10 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
123
148
|
machineName: body.machineName ?? null,
|
|
124
149
|
canOfferDisplay: body.canOfferDisplay,
|
|
125
150
|
requestsScreenControl: body.requestsScreenControl,
|
|
126
|
-
//
|
|
127
|
-
|
|
151
|
+
// Prefer the externally configured deployment origin. Behind a TLS-
|
|
152
|
+
// terminating proxy c.req.url can carry the internal http scheme, which
|
|
153
|
+
// must never leak into the device-flow command shown to the operator.
|
|
154
|
+
verificationOrigin: settings.publicBaseUrl ?? new URL(c.req.url).origin,
|
|
128
155
|
},
|
|
129
156
|
);
|
|
130
157
|
return c.json(result, 201);
|
|
@@ -156,7 +183,9 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
156
183
|
rateLimit(c, lookupLimiter);
|
|
157
184
|
const parsed = DeviceEnrollmentLookupRequest.safeParse(await c.req.json().catch(() => null));
|
|
158
185
|
if (!parsed.success) {
|
|
159
|
-
throw new HTTPException(400, {
|
|
186
|
+
throw new HTTPException(400, {
|
|
187
|
+
message: "invalid device-lookup request",
|
|
188
|
+
});
|
|
160
189
|
}
|
|
161
190
|
const record = await lookupDeviceEnrollment(
|
|
162
191
|
{ db, settings },
|
|
@@ -165,7 +194,9 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
165
194
|
if (!record) {
|
|
166
195
|
// Unknown / terminal / expired code → 404 (indistinguishable from an
|
|
167
196
|
// unauthorized one below, by design).
|
|
168
|
-
throw new HTTPException(404, {
|
|
197
|
+
throw new HTTPException(404, {
|
|
198
|
+
message: "no pending enrollment for that code",
|
|
199
|
+
});
|
|
169
200
|
}
|
|
170
201
|
// Authorize the caller against the RESOLVED workspace. A missing grant throws
|
|
171
202
|
// 403/404 from requireAccessGrant; we normalize that to 404 so a caller cannot
|
|
@@ -173,7 +204,9 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
173
204
|
try {
|
|
174
205
|
await requireAccessGrant(c, deps, record.workspaceId, "enrollments:read");
|
|
175
206
|
} catch {
|
|
176
|
-
throw new HTTPException(404, {
|
|
207
|
+
throw new HTTPException(404, {
|
|
208
|
+
message: "no pending enrollment for that code",
|
|
209
|
+
});
|
|
177
210
|
}
|
|
178
211
|
return c.json(DeviceEnrollmentLookupResponse.parse(toLookupResponse(record)), 200);
|
|
179
212
|
});
|
|
@@ -189,7 +222,9 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
189
222
|
rateLimit(c, exchangeLimiter);
|
|
190
223
|
const parsed = EnrollTokenExchangeRequest.safeParse(await c.req.json().catch(() => null));
|
|
191
224
|
if (!parsed.success) {
|
|
192
|
-
throw new HTTPException(400, {
|
|
225
|
+
throw new HTTPException(400, {
|
|
226
|
+
message: "invalid enroll-token-exchange request",
|
|
227
|
+
});
|
|
193
228
|
}
|
|
194
229
|
const body = parsed.data;
|
|
195
230
|
const result = await exchangeEnrollToken(
|
|
@@ -206,10 +241,14 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
206
241
|
if (!result.ok) {
|
|
207
242
|
if (result.reason === "disabled") {
|
|
208
243
|
// The credential plane is off for this deployment (no signing secret).
|
|
209
|
-
throw new HTTPException(503, {
|
|
244
|
+
throw new HTTPException(503, {
|
|
245
|
+
message: "enrollment credential plane is not configured",
|
|
246
|
+
});
|
|
210
247
|
}
|
|
211
248
|
// An invalid / expired / wrong-typ token — the token is the auth, so 401.
|
|
212
|
-
throw new HTTPException(401, {
|
|
249
|
+
throw new HTTPException(401, {
|
|
250
|
+
message: "invalid or expired enroll token",
|
|
251
|
+
});
|
|
213
252
|
}
|
|
214
253
|
return c.json(EnrollTokenExchangeResponse.parse({ credentials: result.credentials }), 201);
|
|
215
254
|
});
|
|
@@ -218,18 +257,48 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
218
257
|
// The LOUD CONSENT step. requireAccessGrant BEFORE the Zod parse.
|
|
219
258
|
app.post("/v1/workspaces/:workspaceId/enrollments/device/approve", async (c) => {
|
|
220
259
|
const workspaceId = c.req.param("workspaceId");
|
|
221
|
-
const
|
|
260
|
+
const authorization = await requireAccessGrantAuthorization(c, deps, workspaceId);
|
|
261
|
+
const grant = authorization.grant;
|
|
262
|
+
requirePermission(grant, "enrollments:manage");
|
|
222
263
|
assertSelfhostedEnabled();
|
|
223
|
-
const
|
|
264
|
+
const rawBody = await c.req.json().catch(() => null);
|
|
265
|
+
const parsed = DeviceEnrollmentApproveRequest.safeParse(rawBody);
|
|
224
266
|
if (!parsed.success) {
|
|
225
|
-
throw new HTTPException(400, {
|
|
267
|
+
throw new HTTPException(400, {
|
|
268
|
+
message: "invalid device-approve request",
|
|
269
|
+
});
|
|
226
270
|
}
|
|
227
271
|
const body = parsed.data;
|
|
272
|
+
const scopeWasExplicit =
|
|
273
|
+
typeof rawBody === "object" &&
|
|
274
|
+
rawBody !== null &&
|
|
275
|
+
Object.prototype.hasOwnProperty.call(rawBody, "scope");
|
|
276
|
+
// Managed browser humans get the personal default. Legacy/delegated
|
|
277
|
+
// principals predate organization-user ownership and retain the compatible
|
|
278
|
+
// workspace default when the caller omitted scope; an explicit scope is
|
|
279
|
+
// never silently rewritten.
|
|
280
|
+
const effectiveScope =
|
|
281
|
+
scopeWasExplicit ||
|
|
282
|
+
(grant.principalKind === "human_session" &&
|
|
283
|
+
authorization.contextIntegrity &&
|
|
284
|
+
grant.metadata?.delegated !== true)
|
|
285
|
+
? body.scope
|
|
286
|
+
: undefined;
|
|
287
|
+
const allowOrganization =
|
|
288
|
+
effectiveScope === "organization" &&
|
|
289
|
+
authorization.accountGrant?.permissions.includes("account:admin") === true;
|
|
290
|
+
if (effectiveScope === "organization" && !allowOrganization) {
|
|
291
|
+
throw new HTTPException(403, {
|
|
292
|
+
message: "missing permission: account:admin",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
228
295
|
const approved = await approveDeviceEnrollment(
|
|
229
296
|
{ db, settings },
|
|
230
297
|
{
|
|
231
298
|
accountId: grant.accountId,
|
|
232
299
|
workspaceId,
|
|
300
|
+
...(effectiveScope ? { scope: effectiveScope } : {}),
|
|
301
|
+
allowOrganization,
|
|
233
302
|
userCode: body.userCode,
|
|
234
303
|
allowScreenControl: body.allowScreenControl,
|
|
235
304
|
// The LOUD consent record: WHO consented (the authenticated subject + label).
|
|
@@ -239,7 +308,9 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
239
308
|
);
|
|
240
309
|
if (!approved) {
|
|
241
310
|
// An unknown / expired / already-terminal user_code in this workspace.
|
|
242
|
-
throw new HTTPException(404, {
|
|
311
|
+
throw new HTTPException(404, {
|
|
312
|
+
message: "no pending enrollment for that code",
|
|
313
|
+
});
|
|
243
314
|
}
|
|
244
315
|
return c.json(
|
|
245
316
|
DeviceEnrollmentApproveResponse.parse({
|
|
@@ -286,7 +357,9 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
286
357
|
// allowScreenControl=false). Coalesce a missing/empty body to {} before parse.
|
|
287
358
|
const parsed = MintEnrollTokenRequest.safeParse(await c.req.json().catch(() => ({})));
|
|
288
359
|
if (!parsed.success) {
|
|
289
|
-
throw new HTTPException(400, {
|
|
360
|
+
throw new HTTPException(400, {
|
|
361
|
+
message: "invalid mint-enroll-token request",
|
|
362
|
+
});
|
|
290
363
|
}
|
|
291
364
|
const minted = await mintEnrollToken(
|
|
292
365
|
{ db, settings },
|
|
@@ -298,7 +371,9 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
298
371
|
);
|
|
299
372
|
if (!minted) {
|
|
300
373
|
// The credential plane is off (no signing secret) — mirror poll's disabled path.
|
|
301
|
-
throw new HTTPException(503, {
|
|
374
|
+
throw new HTTPException(503, {
|
|
375
|
+
message: "enrollment credential plane is not configured",
|
|
376
|
+
});
|
|
302
377
|
}
|
|
303
378
|
return c.json(MintEnrollTokenResponse.parse(minted), 201);
|
|
304
379
|
});
|
|
@@ -306,13 +381,15 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
306
381
|
// ── GET /workspaces/:workspaceId/enrollments (user-authed) ──────────────────
|
|
307
382
|
app.get("/v1/workspaces/:workspaceId/enrollments", async (c) => {
|
|
308
383
|
const workspaceId = c.req.param("workspaceId");
|
|
309
|
-
await requireAccessGrant(c, deps, workspaceId, "enrollments:read");
|
|
384
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "enrollments:read");
|
|
310
385
|
assertSelfhostedEnabled();
|
|
311
386
|
const statusFilter = c.req.query("status");
|
|
312
387
|
if (statusFilter !== undefined && statusFilter !== "active" && statusFilter !== "revoked") {
|
|
313
|
-
throw new HTTPException(400, {
|
|
388
|
+
throw new HTTPException(400, {
|
|
389
|
+
message: "status must be active or revoked",
|
|
390
|
+
});
|
|
314
391
|
}
|
|
315
|
-
const rows = await listEnrollments(db,
|
|
392
|
+
const rows = await listEnrollments(db, grant, {
|
|
316
393
|
status: statusFilter === "revoked" ? "revoked" : "active",
|
|
317
394
|
});
|
|
318
395
|
return c.json(
|
|
@@ -320,6 +397,8 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
320
397
|
enrollments: rows.map((row) =>
|
|
321
398
|
EnrollmentSummary.parse({
|
|
322
399
|
id: row.id,
|
|
400
|
+
scope: row.scope,
|
|
401
|
+
generation: row.generation,
|
|
323
402
|
pubkey: row.pubkey,
|
|
324
403
|
exposure: row.exposure,
|
|
325
404
|
hasDisplay: row.hasDisplay,
|
|
@@ -340,19 +419,40 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
340
419
|
// ── POST /workspaces/:workspaceId/enrollments/:id/revoke (user-authed) ──────
|
|
341
420
|
app.post("/v1/workspaces/:workspaceId/enrollments/:enrollmentId/revoke", async (c) => {
|
|
342
421
|
const workspaceId = c.req.param("workspaceId");
|
|
343
|
-
const
|
|
422
|
+
const authorization = await requireAccessGrantAuthorization(c, deps, workspaceId);
|
|
423
|
+
const grant = authorization.grant;
|
|
424
|
+
requirePermission(grant, "enrollments:manage");
|
|
344
425
|
assertSelfhostedEnabled();
|
|
345
426
|
const parsed = RemoveEnrollmentRequest.safeParse(await c.req.json().catch(() => ({})));
|
|
346
427
|
if (!parsed.success) {
|
|
347
|
-
throw new HTTPException(400, {
|
|
428
|
+
throw new HTTPException(400, {
|
|
429
|
+
message: "invalid machine removal request",
|
|
430
|
+
});
|
|
348
431
|
}
|
|
349
432
|
const idempotencyKey =
|
|
350
433
|
parsed.data.idempotencyKey?.trim() || c.req.header("idempotency-key")?.trim() || randomUUID();
|
|
351
434
|
try {
|
|
435
|
+
const enrollmentId = c.req.param("enrollmentId");
|
|
436
|
+
const enrollment = (await listEnrollments(db, grant, { status: "active" })).find(
|
|
437
|
+
(candidate) => candidate.id === enrollmentId,
|
|
438
|
+
);
|
|
439
|
+
if (!enrollment) {
|
|
440
|
+
throw new HTTPException(404, {
|
|
441
|
+
message: "machine enrollment not found",
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (
|
|
445
|
+
enrollment.scope === "organization" &&
|
|
446
|
+
authorization.accountGrant?.permissions.includes("account:admin") !== true
|
|
447
|
+
) {
|
|
448
|
+
throw new HTTPException(403, {
|
|
449
|
+
message: "missing permission: account:admin",
|
|
450
|
+
});
|
|
451
|
+
}
|
|
352
452
|
const result = await removeEnrollment(db, {
|
|
353
453
|
accountId: grant.accountId,
|
|
354
|
-
workspaceId,
|
|
355
|
-
enrollmentId
|
|
454
|
+
workspaceId: enrollment.workspaceId,
|
|
455
|
+
enrollmentId,
|
|
356
456
|
operationKey: idempotencyKey,
|
|
357
457
|
...(parsed.data.expectedUpdatedAt
|
|
358
458
|
? { expectedUpdatedAt: parsed.data.expectedUpdatedAt }
|
|
@@ -360,7 +460,9 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
360
460
|
subjectId: grant.subjectId,
|
|
361
461
|
});
|
|
362
462
|
if (!result) {
|
|
363
|
-
throw new HTTPException(404, {
|
|
463
|
+
throw new HTTPException(404, {
|
|
464
|
+
message: "machine enrollment not found",
|
|
465
|
+
});
|
|
364
466
|
}
|
|
365
467
|
return c.json(
|
|
366
468
|
RevokeEnrollmentResponse.parse({
|
|
@@ -376,7 +478,10 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
376
478
|
if (error instanceof MachineRemovalRevisionConflictError) {
|
|
377
479
|
throw new HTTPException(409, {
|
|
378
480
|
message: error.message,
|
|
379
|
-
cause: {
|
|
481
|
+
cause: {
|
|
482
|
+
code: "stale_revision",
|
|
483
|
+
currentUpdatedAt: error.currentUpdatedAt,
|
|
484
|
+
},
|
|
380
485
|
});
|
|
381
486
|
}
|
|
382
487
|
throw error;
|
|
@@ -384,18 +489,6 @@ export function registerEnrollmentRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
384
489
|
});
|
|
385
490
|
}
|
|
386
491
|
|
|
387
|
-
// The remote client IP for the per-IP rate-limit bucket. Honors the proxy's
|
|
388
|
-
// X-Forwarded-For (the first hop) when present, falling back to a constant key when
|
|
389
|
-
// neither is available (the bucket then caps the whole edge — still a useful cap).
|
|
390
|
-
function clientIp(c: Context): string {
|
|
391
|
-
const xff = c.req.header("x-forwarded-for");
|
|
392
|
-
if (xff) {
|
|
393
|
-
const first = xff.split(",")[0]?.trim();
|
|
394
|
-
if (first) return first;
|
|
395
|
-
}
|
|
396
|
-
return c.req.header("x-real-ip")?.trim() || "unknown";
|
|
397
|
-
}
|
|
398
|
-
|
|
399
492
|
// A minimal per-key token bucket. capacity = burst; refillPerSecond = sustained
|
|
400
493
|
// rate. Buckets are created lazily and reset their tokens by elapsed time on each
|
|
401
494
|
// take, so an idle key fully refills without a background timer.
|
|
@@ -410,7 +503,10 @@ class TokenBucket {
|
|
|
410
503
|
}
|
|
411
504
|
|
|
412
505
|
take(key: string, now = Date.now()): boolean {
|
|
413
|
-
const bucket = this.buckets.get(key) ?? {
|
|
506
|
+
const bucket = this.buckets.get(key) ?? {
|
|
507
|
+
tokens: this.capacity,
|
|
508
|
+
updatedAt: now,
|
|
509
|
+
};
|
|
414
510
|
const elapsedSeconds = Math.max(0, (now - bucket.updatedAt) / 1000);
|
|
415
511
|
bucket.tokens = Math.min(this.capacity, bucket.tokens + elapsedSeconds * this.refillPerSecond);
|
|
416
512
|
bucket.updatedAt = now;
|