@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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Settings } from "@opengeni/config";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
|
|
4
|
+
import { requireAccessKey } from "./auth";
|
|
5
|
+
|
|
6
|
+
type PrometheusSource = { prometheusMetrics(): Promise<string> };
|
|
7
|
+
|
|
8
|
+
/** Register the Prometheus exposition route on one Hono app. */
|
|
9
|
+
export function registerPrometheusMetricsRoute(app: Hono, observability: PrometheusSource): void {
|
|
10
|
+
app.get("/metrics", async (c) =>
|
|
11
|
+
c.text(await observability.prometheusMetrics(), 200, {
|
|
12
|
+
"content-type": "text/plain; version=0.0.4; charset=utf-8",
|
|
13
|
+
}),
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Whether the public API listener serves `GET /metrics`. A deployment that sets
|
|
19
|
+
* `OPENGENI_API_METRICS_PORT` moves the exposition to a dedicated internal
|
|
20
|
+
* listener, so an ingress that forwards every path to the API cannot publish it.
|
|
21
|
+
*/
|
|
22
|
+
export function publicListenerServesMetrics(settings: Pick<Settings, "apiMetricsPort">): boolean {
|
|
23
|
+
return settings.apiMetricsPort === undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The dedicated metrics listener: only `GET /metrics`, behind the same
|
|
28
|
+
* deployment-key perimeter rules as the public listener. Nothing else is
|
|
29
|
+
* routed here, so exposing this port to a scraper exposes nothing else.
|
|
30
|
+
*/
|
|
31
|
+
export function createMetricsListenerApp(
|
|
32
|
+
settings: Settings,
|
|
33
|
+
observability: PrometheusSource,
|
|
34
|
+
): Hono {
|
|
35
|
+
const app = new Hono();
|
|
36
|
+
app.use("*", requireAccessKey(settings));
|
|
37
|
+
registerPrometheusMetricsRoute(app, observability);
|
|
38
|
+
return app;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Start the dedicated listener when configured; returns undefined otherwise. */
|
|
42
|
+
export function startApiMetricsListener(
|
|
43
|
+
settings: Settings,
|
|
44
|
+
observability: PrometheusSource,
|
|
45
|
+
): ReturnType<typeof Bun.serve> | undefined {
|
|
46
|
+
const port = settings.apiMetricsPort;
|
|
47
|
+
if (port === undefined) return undefined;
|
|
48
|
+
if (port === settings.apiPort) {
|
|
49
|
+
throw new Error("OPENGENI_API_METRICS_PORT must differ from OPENGENI_API_PORT");
|
|
50
|
+
}
|
|
51
|
+
const app = createMetricsListenerApp(settings, observability);
|
|
52
|
+
return Bun.serve({
|
|
53
|
+
hostname: settings.apiHost,
|
|
54
|
+
port,
|
|
55
|
+
fetch: (request) => app.fetch(request),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ApiHttpError } from "./api-error";
|
|
2
|
+
|
|
3
|
+
const UUID_SYNTAX_MESSAGE = /invalid input syntax for type uuid: "([^"]*)"/u;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A resource id taken verbatim from the URL path that is not a UUID cannot
|
|
7
|
+
* name any row. Routes pass such ids straight to PostgreSQL, which rejects the
|
|
8
|
+
* cast (SQLSTATE 22P02); that is a client addressing error, so answer 404
|
|
9
|
+
* instead of an unhandled 500.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately narrow: only a uuid-syntax failure whose rejected text is
|
|
12
|
+
* exactly one of this request's own path segments qualifies. A malformed UUID
|
|
13
|
+
* produced by server code (a stored value, a derived subject, a body field)
|
|
14
|
+
* does not appear as a path segment and stays a genuine 500.
|
|
15
|
+
*
|
|
16
|
+
* Typical cause: a client built against a newer API calls a literal route
|
|
17
|
+
* (for example `.../scheduled-tasks/attention`) that an older server does not
|
|
18
|
+
* have, so the literal falls through to `.../scheduled-tasks/:taskId`.
|
|
19
|
+
*/
|
|
20
|
+
export function invalidPathIdentifierHttpError(
|
|
21
|
+
error: unknown,
|
|
22
|
+
pathname: string,
|
|
23
|
+
): ApiHttpError | null {
|
|
24
|
+
const rejected = rejectedUuidText(error);
|
|
25
|
+
if (rejected === null || rejected.length === 0) return null;
|
|
26
|
+
const segments = pathname.split("/").map((segment) => {
|
|
27
|
+
try {
|
|
28
|
+
return decodeURIComponent(segment);
|
|
29
|
+
} catch {
|
|
30
|
+
return segment;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
if (!segments.includes(rejected)) return null;
|
|
34
|
+
return new ApiHttpError(404, {
|
|
35
|
+
code: "not_found",
|
|
36
|
+
message: `Not found: "${rejected.slice(0, 64)}" is not a valid resource id for this route.`,
|
|
37
|
+
retryable: false,
|
|
38
|
+
details: { code: "invalid_path_identifier" },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The text PostgreSQL refused to cast to uuid, found through a bounded cause chain. */
|
|
43
|
+
function rejectedUuidText(error: unknown): string | null {
|
|
44
|
+
let current: unknown = error;
|
|
45
|
+
for (let depth = 0; depth < 6 && current instanceof Error; depth += 1) {
|
|
46
|
+
const code = (current as { code?: unknown }).code;
|
|
47
|
+
if (code === "22P02") {
|
|
48
|
+
const match = UUID_SYNTAX_MESSAGE.exec(current.message);
|
|
49
|
+
if (match) return match[1] ?? null;
|
|
50
|
+
}
|
|
51
|
+
current = current.cause;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import { matchedRoutes } from "hono/route";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The registered Hono path template of the handler that answers this request.
|
|
6
|
+
*
|
|
7
|
+
* Hono resolves every route matching a request before the first middleware
|
|
8
|
+
* runs, so the metrics middleware can read the exact code-owned template (for
|
|
9
|
+
* example `/v1/workspaces/:workspaceId/rigs/:rigId`) without re-implementing
|
|
10
|
+
* routing. Templates come only from route registrations, never from request
|
|
11
|
+
* data, so they are a closed and bounded metric/log label set.
|
|
12
|
+
*
|
|
13
|
+
* Middleware (arity two: `(context, next)`) and global catch-alls never name a
|
|
14
|
+
* route; the first matched terminal handler is the one Hono dispatches to.
|
|
15
|
+
*/
|
|
16
|
+
export function registeredHandlerRoutePath(context: Context): string | null {
|
|
17
|
+
try {
|
|
18
|
+
for (const route of matchedRoutes(context)) {
|
|
19
|
+
if (typeof route.handler !== "function" || route.handler.length >= 2) continue;
|
|
20
|
+
const label = boundedRegisteredRouteLabel(route.path);
|
|
21
|
+
if (label) return label;
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
// Route labels are observational and must never affect request handling.
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Normalize a registered template; drop catch-alls that would name nothing. */
|
|
30
|
+
export function boundedRegisteredRouteLabel(path: string | null | undefined): string | null {
|
|
31
|
+
if (!path || !path.startsWith("/") || path === "/*") return null;
|
|
32
|
+
// `:param{regex}` constraints are routing detail, not part of the label.
|
|
33
|
+
return path.replace(/(:[A-Za-z0-9_]+)\{[^/]*\}/g, "$1");
|
|
34
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
import { ZodError } from "zod";
|
|
4
|
+
|
|
5
|
+
import { ApiHttpError } from "./api-error";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validation failures produced by parsing a client-supplied request body.
|
|
9
|
+
*
|
|
10
|
+
* The same `ZodError` instance is rethrown so route-local handlers that
|
|
11
|
+
* already map `instanceof ZodError` keep their exact behavior; only the
|
|
12
|
+
* central `app.onError` consults this set to answer a client error instead of
|
|
13
|
+
* a 500. A ZodError that is NOT registered here (for example, a server-side
|
|
14
|
+
* response projection that no longer matches its contract) remains a genuine
|
|
15
|
+
* server fault.
|
|
16
|
+
*/
|
|
17
|
+
const requestBodyValidationErrors = new WeakSet<ZodError>();
|
|
18
|
+
/** Malformed-JSON failures raised by reading a client request body. */
|
|
19
|
+
const requestBodyJsonErrors = new WeakSet<SyntaxError>();
|
|
20
|
+
|
|
21
|
+
const MAX_REPORTED_ISSUES = 10;
|
|
22
|
+
|
|
23
|
+
/** Parse an already-read request body against its public request schema. */
|
|
24
|
+
export function parseRequestBody<Schema extends z.ZodType>(
|
|
25
|
+
schema: Schema,
|
|
26
|
+
body: unknown,
|
|
27
|
+
): z.output<Schema> {
|
|
28
|
+
const parsed = schema.safeParse(body);
|
|
29
|
+
if (parsed.success) return parsed.data;
|
|
30
|
+
requestBodyValidationErrors.add(parsed.error);
|
|
31
|
+
throw parsed.error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Read the request body as JSON. Malformed JSON is a client error (400), never
|
|
36
|
+
* an unhandled `SyntaxError` 500. Other failures (body limit, aborted stream)
|
|
37
|
+
* keep their own mapping.
|
|
38
|
+
*/
|
|
39
|
+
export async function readRequestJson(c: Context): Promise<unknown> {
|
|
40
|
+
try {
|
|
41
|
+
return await c.req.json();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof SyntaxError) {
|
|
44
|
+
throw new ApiHttpError(400, {
|
|
45
|
+
code: "validation_failed",
|
|
46
|
+
message: "Request body must be valid JSON.",
|
|
47
|
+
retryable: false,
|
|
48
|
+
details: { code: "invalid_json" },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Read and validate a JSON request body in one step. */
|
|
56
|
+
export async function parseRequestJson<Schema extends z.ZodType>(
|
|
57
|
+
c: Context,
|
|
58
|
+
schema: Schema,
|
|
59
|
+
): Promise<z.output<Schema>> {
|
|
60
|
+
return parseRequestBody(schema, await readRequestJson(c));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Tag malformed-JSON failures of `c.req.json()` for this request. The same
|
|
65
|
+
* `SyntaxError` instance is rethrown so routes that already catch it keep
|
|
66
|
+
* their own mapping; an uncaught one becomes a 400 in `app.onError` instead of
|
|
67
|
+
* a 500. A `SyntaxError` from parsing stored or upstream data is never tagged.
|
|
68
|
+
*/
|
|
69
|
+
export function tagRequestJsonParseErrors(c: Context): void {
|
|
70
|
+
const original = c.req.json.bind(c.req);
|
|
71
|
+
const tagged = async () => {
|
|
72
|
+
try {
|
|
73
|
+
return await original();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error instanceof SyntaxError) requestBodyJsonErrors.add(error);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
(c.req as { json: typeof tagged }).json = tagged;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function isRequestBodyValidationError(error: unknown): error is ZodError | SyntaxError {
|
|
83
|
+
return requestBodyInputError(error) !== null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Walk a bounded cause chain for a tagged client-input failure. */
|
|
87
|
+
function requestBodyInputError(error: unknown): ZodError | SyntaxError | null {
|
|
88
|
+
let current: unknown = error;
|
|
89
|
+
for (let depth = 0; depth < 5 && current instanceof Error; depth += 1) {
|
|
90
|
+
if (current instanceof ZodError && requestBodyValidationErrors.has(current)) return current;
|
|
91
|
+
if (current instanceof SyntaxError && requestBodyJsonErrors.has(current)) return current;
|
|
92
|
+
current = current.cause;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Public 400 for a registered request-body validation failure, naming every
|
|
99
|
+
* offending field (bounded) so API callers can correct the request.
|
|
100
|
+
*/
|
|
101
|
+
export function requestBodyValidationHttpError(error: unknown): ApiHttpError | null {
|
|
102
|
+
const inputError = requestBodyInputError(error);
|
|
103
|
+
if (!inputError) return null;
|
|
104
|
+
if (inputError instanceof SyntaxError) {
|
|
105
|
+
return new ApiHttpError(400, {
|
|
106
|
+
code: "validation_failed",
|
|
107
|
+
message: "Request body must be valid JSON.",
|
|
108
|
+
retryable: false,
|
|
109
|
+
details: { code: "invalid_json" },
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const zodError = inputError;
|
|
113
|
+
const issues = zodError.issues.slice(0, MAX_REPORTED_ISSUES).map((issue) => ({
|
|
114
|
+
path: issue.path.map(String).join("."),
|
|
115
|
+
message: issue.message,
|
|
116
|
+
}));
|
|
117
|
+
const summary = issues.map((issue) => `${issue.path || "request"}: ${issue.message}`).join("; ");
|
|
118
|
+
const omittedIssueCount = Math.max(0, zodError.issues.length - issues.length);
|
|
119
|
+
return new ApiHttpError(400, {
|
|
120
|
+
code: "validation_failed",
|
|
121
|
+
message: `Invalid request body: ${summary}${
|
|
122
|
+
omittedIssueCount > 0 ? `; and ${omittedIssueCount} more` : ""
|
|
123
|
+
}`,
|
|
124
|
+
retryable: false,
|
|
125
|
+
details: { code: "invalid_request_body", issues, omittedIssueCount },
|
|
126
|
+
});
|
|
127
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { BlockList, isIP } from "node:net";
|
|
2
|
+
import { trustedProxyCidrEntries, type Settings } from "@opengeni/config";
|
|
3
|
+
import type { Context } from "hono";
|
|
4
|
+
|
|
5
|
+
const TRANSPORT_PEER_ADDRESS_BINDING = "opengeniTransportPeerAddress";
|
|
6
|
+
|
|
7
|
+
/** Returned when the runtime supplied no transport peer (for example an
|
|
8
|
+
* in-process test request). Callers treat it as one shared bucket. */
|
|
9
|
+
export const UNKNOWN_REQUEST_SOURCE_ADDRESS = "unknown";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* App-owned request header carrying the server-resolved client address into
|
|
13
|
+
* Better Auth, which reads client addresses only from request headers. It is
|
|
14
|
+
* removed from every inbound request and re-stamped on managed-auth routes, so
|
|
15
|
+
* a caller-supplied value never reaches the auth rate limiter or session IP.
|
|
16
|
+
*/
|
|
17
|
+
export const TRUSTED_CLIENT_ADDRESS_HEADER = "x-opengeni-trusted-client-address";
|
|
18
|
+
|
|
19
|
+
type ApiRequestBindings = {
|
|
20
|
+
[TRANSPORT_PEER_ADDRESS_BINDING]?: string | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** The deployment's declared proxy chain (`OPENGENI_API_TRUSTED_PROXY_*`). */
|
|
24
|
+
export type RequestSourceTrust = Pick<Settings, "apiTrustedProxyHops" | "apiTrustedProxyCidrs">;
|
|
25
|
+
|
|
26
|
+
export function apiRequestBindingsForTransportPeer(
|
|
27
|
+
address: string | null | undefined,
|
|
28
|
+
): ApiRequestBindings {
|
|
29
|
+
return { [TRANSPORT_PEER_ADDRESS_BINDING]: address ?? null };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve a quota/audit source from the server-owned transport peer. Forwarded
|
|
34
|
+
* values are considered only when the operator declares an exact trusted proxy
|
|
35
|
+
* hop count (`OPENGENI_API_TRUSTED_PROXY_HOPS`) and, when
|
|
36
|
+
* `OPENGENI_API_TRUSTED_PROXY_CIDRS` is set, only for a transport peer inside
|
|
37
|
+
* those ranges. Each trusted proxy must append the address of the peer that
|
|
38
|
+
* connected to it, or overwrite the header with the original client address;
|
|
39
|
+
* the chain is then walked from the server side so caller-prepended values
|
|
40
|
+
* cannot replace the address observed by the trusted edge. A missing, short,
|
|
41
|
+
* or malformed chain falls back to the transport peer.
|
|
42
|
+
*/
|
|
43
|
+
export function trustedRequestSourceAddress(c: Context, trust: RequestSourceTrust): string {
|
|
44
|
+
const bindings = c.env as ApiRequestBindings | undefined;
|
|
45
|
+
const peer =
|
|
46
|
+
normalizedAddress(bindings?.[TRANSPORT_PEER_ADDRESS_BINDING]) ?? UNKNOWN_REQUEST_SOURCE_ADDRESS;
|
|
47
|
+
const trustedProxyHops = trust.apiTrustedProxyHops;
|
|
48
|
+
if (
|
|
49
|
+
!Number.isInteger(trustedProxyHops) ||
|
|
50
|
+
trustedProxyHops <= 0 ||
|
|
51
|
+
peer === UNKNOWN_REQUEST_SOURCE_ADDRESS ||
|
|
52
|
+
!peerIsTrustedProxy(peer, trust.apiTrustedProxyCidrs)
|
|
53
|
+
) {
|
|
54
|
+
return peer;
|
|
55
|
+
}
|
|
56
|
+
const header = c.req.header("x-forwarded-for");
|
|
57
|
+
if (!header) return peer;
|
|
58
|
+
// Entries are never filtered: dropping a malformed value would shift the
|
|
59
|
+
// server-side position and could promote a caller-prepended value.
|
|
60
|
+
const forwarded = header.split(",");
|
|
61
|
+
const sourceIndex = forwarded.length - trustedProxyHops;
|
|
62
|
+
if (sourceIndex < 0) return peer;
|
|
63
|
+
return normalizedAddress(forwarded[sourceIndex]) ?? peer;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Rate-limit key for a trusted source address. An IPv6 address keys on its
|
|
68
|
+
* /64, the block one subscriber normally holds and the granularity Better
|
|
69
|
+
* Auth applies to the same stamped address, so rotating addresses inside one
|
|
70
|
+
* /64 cannot mint fresh buckets. An IPv4-mapped IPv6 address (how a
|
|
71
|
+
* dual-stack listener reports an IPv4 peer) keys on the embedded IPv4 address
|
|
72
|
+
* instead of collapsing every IPv4 client into one /64. Anything else,
|
|
73
|
+
* including {@link UNKNOWN_REQUEST_SOURCE_ADDRESS}, is returned unchanged.
|
|
74
|
+
*/
|
|
75
|
+
export function requestSourceRateLimitKey(address: string): string {
|
|
76
|
+
if (isIP(address) !== 6) return address;
|
|
77
|
+
const groups = ipv6Groups(address);
|
|
78
|
+
if (!groups) return address;
|
|
79
|
+
if (groups.slice(0, 5).every((group) => group === 0) && groups[5] === 0xffff) {
|
|
80
|
+
const high = groups[6]!;
|
|
81
|
+
const low = groups[7]!;
|
|
82
|
+
return `${high >> 8}.${high & 0xff}.${low >> 8}.${low & 0xff}`;
|
|
83
|
+
}
|
|
84
|
+
return `${groups
|
|
85
|
+
.slice(0, 4)
|
|
86
|
+
.map((group) => group.toString(16))
|
|
87
|
+
.join(":")}::/64`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** {@link trustedRequestSourceAddress} as an application rate-limit key. */
|
|
91
|
+
export function trustedRequestSourceRateLimitKey(c: Context, trust: RequestSourceTrust): string {
|
|
92
|
+
return requestSourceRateLimitKey(trustedRequestSourceAddress(c, trust));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Replace any caller-supplied {@link TRUSTED_CLIENT_ADDRESS_HEADER}. When
|
|
97
|
+
* `stamp` is true the header is set to the trusted source address, so every
|
|
98
|
+
* Better Auth request derived from this request (direct handler calls,
|
|
99
|
+
* isolated provider requests, and `auth.api.*` calls built from its headers)
|
|
100
|
+
* keys rate limits and session records on the real client.
|
|
101
|
+
*/
|
|
102
|
+
export function replaceTrustedClientAddressHeader(
|
|
103
|
+
c: Context,
|
|
104
|
+
trust: RequestSourceTrust,
|
|
105
|
+
stamp: boolean,
|
|
106
|
+
): void {
|
|
107
|
+
const headers = c.req.raw.headers;
|
|
108
|
+
headers.delete(TRUSTED_CLIENT_ADDRESS_HEADER);
|
|
109
|
+
if (!stamp) return;
|
|
110
|
+
const address = trustedRequestSourceAddress(c, trust);
|
|
111
|
+
if (address !== UNKNOWN_REQUEST_SOURCE_ADDRESS) {
|
|
112
|
+
headers.set(TRUSTED_CLIENT_ADDRESS_HEADER, address);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const trustedProxyRanges = new Map<string, BlockList | null>();
|
|
117
|
+
|
|
118
|
+
function peerIsTrustedProxy(peer: string, cidrs: string): boolean {
|
|
119
|
+
let ranges = trustedProxyRanges.get(cidrs);
|
|
120
|
+
if (ranges === undefined) {
|
|
121
|
+
// Settings validation already rejected malformed entries at boot.
|
|
122
|
+
const entries = trustedProxyCidrEntries(cidrs);
|
|
123
|
+
ranges = entries.length === 0 ? null : new BlockList();
|
|
124
|
+
for (const entry of entries) ranges?.addSubnet(entry.address, entry.prefix, entry.family);
|
|
125
|
+
trustedProxyRanges.set(cidrs, ranges);
|
|
126
|
+
}
|
|
127
|
+
if (!ranges) return true;
|
|
128
|
+
return ranges.check(peer, isIP(peer) === 6 ? "ipv6" : "ipv4");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Canonical IP text, or null. Accepts the `ipv4:port` and `[ipv6]:port` forms
|
|
133
|
+
* some load balancers write into X-Forwarded-For, and drops IPv6 zone ids.
|
|
134
|
+
*/
|
|
135
|
+
function normalizedAddress(value: string | null | undefined): string | null {
|
|
136
|
+
let candidate = value?.trim();
|
|
137
|
+
if (!candidate || candidate.length > 64) return null;
|
|
138
|
+
const bracketed = /^\[([^\]]+)\](?::\d{1,5})?$/u.exec(candidate);
|
|
139
|
+
if (bracketed) {
|
|
140
|
+
candidate = bracketed[1]!;
|
|
141
|
+
} else {
|
|
142
|
+
const ipv4WithPort = /^(\d{1,3}(?:\.\d{1,3}){3}):\d{1,5}$/u.exec(candidate);
|
|
143
|
+
if (ipv4WithPort) candidate = ipv4WithPort[1]!;
|
|
144
|
+
}
|
|
145
|
+
const zone = candidate.indexOf("%");
|
|
146
|
+
if (zone >= 0) candidate = candidate.slice(0, zone);
|
|
147
|
+
const family = isIP(candidate);
|
|
148
|
+
if (family === 4) return candidate;
|
|
149
|
+
if (family === 6) return candidate.toLowerCase();
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** The eight 16-bit groups of a valid IPv6 address, or null. */
|
|
154
|
+
function ipv6Groups(address: string): number[] | null {
|
|
155
|
+
let text = address;
|
|
156
|
+
const tail: number[] = [];
|
|
157
|
+
const lastColon = text.lastIndexOf(":");
|
|
158
|
+
const last = text.slice(lastColon + 1);
|
|
159
|
+
if (last.includes(".")) {
|
|
160
|
+
const octets = last.split(".").map(Number);
|
|
161
|
+
if (octets.length !== 4 || octets.some((octet) => !Number.isInteger(octet))) return null;
|
|
162
|
+
tail.push((octets[0]! << 8) | octets[1]!, (octets[2]! << 8) | octets[3]!);
|
|
163
|
+
text = text.slice(0, lastColon + 1);
|
|
164
|
+
if (!text.endsWith("::")) text = text.slice(0, -1);
|
|
165
|
+
}
|
|
166
|
+
const halves = text.split("::");
|
|
167
|
+
if (halves.length > 2) return null;
|
|
168
|
+
const parse = (part: string): number[] =>
|
|
169
|
+
part ? part.split(":").map((group) => Number.parseInt(group, 16)) : [];
|
|
170
|
+
const head = parse(halves[0]!);
|
|
171
|
+
const rest = halves.length === 2 ? parse(halves[1]!) : [];
|
|
172
|
+
const fill = halves.length === 2 ? 8 - head.length - rest.length - tail.length : 0;
|
|
173
|
+
if (fill < 0) return null;
|
|
174
|
+
const groups = [...head, ...Array.from({ length: fill }, () => 0), ...rest, ...tail];
|
|
175
|
+
if (groups.length !== 8) return null;
|
|
176
|
+
if (groups.some((group) => !Number.isInteger(group) || group < 0 || group > 0xffff)) return null;
|
|
177
|
+
return groups;
|
|
178
|
+
}
|