@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
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { claudeAccountMutationFailure } from "./claude-subscription-account-conflicts";
|
|
2
|
+
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
CLAUDE_OAUTH_CLIENT_ID,
|
|
5
|
+
CLAUDE_OAUTH_REDIRECT_URL,
|
|
6
|
+
CLAUDE_OAUTH_TOKEN_URL,
|
|
7
|
+
CLAUDE_OAUTH_SCOPES,
|
|
8
|
+
ClaudeOAuthTokenResponse,
|
|
9
|
+
ClaudeSubscriptionCredential,
|
|
10
|
+
claudeAuthorizationUrl,
|
|
11
|
+
} from "@opengeni/config";
|
|
12
|
+
import {
|
|
13
|
+
ClaudeSubscriptionOAuthStartResponse,
|
|
14
|
+
ClaudeSubscriptionOAuthCompleteResponse,
|
|
15
|
+
ClaudeProviderAccountAuthoritySnapshotV1,
|
|
16
|
+
} from "@opengeni/contracts";
|
|
17
|
+
import { requireEnvironmentEncryption, type ApiRouteDeps } from "@opengeni/core";
|
|
18
|
+
import {
|
|
19
|
+
consumeIntegrationOAuthPendingState,
|
|
20
|
+
decryptEnvironmentValue,
|
|
21
|
+
encryptEnvironmentValue,
|
|
22
|
+
loadIntegrationOAuthPendingState,
|
|
23
|
+
storeIntegrationOAuthPendingState,
|
|
24
|
+
withRlsContext,
|
|
25
|
+
getClaudeSubscriptionAccountAuthoritySnapshot,
|
|
26
|
+
getClaudeSubscriptionAccountMetadata,
|
|
27
|
+
listOrganizationClaudeSubscriptions,
|
|
28
|
+
upsertOrganizationClaudeSubscription,
|
|
29
|
+
upsertClaudeSubscriptionAccount,
|
|
30
|
+
setInitialActiveClaudeCredential,
|
|
31
|
+
wakeClaudeCapacityWaiters,
|
|
32
|
+
} from "@opengeni/db";
|
|
33
|
+
import { readResponseJsonBounded } from "@opengeni/network";
|
|
34
|
+
import { HTTPException } from "hono/http-exception";
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
import { prepareClaudeSubscriptionCredential } from "./claude-workspace-connection";
|
|
37
|
+
import {
|
|
38
|
+
fetchClaudeSubscriptionProfile,
|
|
39
|
+
parseClaudeSubscriptionProfile,
|
|
40
|
+
} from "./claude-subscription-profile";
|
|
41
|
+
|
|
42
|
+
export type ClaudeOAuthScope = {
|
|
43
|
+
accountId: string;
|
|
44
|
+
workspaceId: string | null;
|
|
45
|
+
actorSubjectId: string;
|
|
46
|
+
browserSessionHash: string;
|
|
47
|
+
};
|
|
48
|
+
const AttemptScope = z
|
|
49
|
+
.object({
|
|
50
|
+
purpose: z.literal("claude-subscription-oauth"),
|
|
51
|
+
accountId: z.string().uuid(),
|
|
52
|
+
workspaceId: z.string().uuid().nullable(),
|
|
53
|
+
actorSubjectId: z.string().min(1),
|
|
54
|
+
browserSessionHash: z.string().min(1),
|
|
55
|
+
poolScope: z.enum(["organization", "workspace", "user"]),
|
|
56
|
+
credentialId: z.string().uuid().nullable(),
|
|
57
|
+
credentialVersion: z.number().int().nonnegative(),
|
|
58
|
+
authoritySnapshot: ClaudeProviderAccountAuthoritySnapshotV1.nullable(),
|
|
59
|
+
})
|
|
60
|
+
.strict();
|
|
61
|
+
const Attempt = z.discriminatedUnion("stage", [
|
|
62
|
+
AttemptScope.extend({
|
|
63
|
+
stage: z.literal("pending"),
|
|
64
|
+
state: z.string().min(32).max(128),
|
|
65
|
+
verifier: z.string().min(32).max(128),
|
|
66
|
+
}),
|
|
67
|
+
AttemptScope.extend({ stage: z.literal("complete") }),
|
|
68
|
+
]);
|
|
69
|
+
const Tokens = ClaudeOAuthTokenResponse.extend({
|
|
70
|
+
refresh_token: z.string().min(1).max(16384),
|
|
71
|
+
account: z.object({ uuid: z.string().uuid() }).passthrough().optional(),
|
|
72
|
+
}).passthrough();
|
|
73
|
+
function same(left: string, right: string) {
|
|
74
|
+
const a = Buffer.from(left),
|
|
75
|
+
b = Buffer.from(right);
|
|
76
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
77
|
+
}
|
|
78
|
+
const changed = () =>
|
|
79
|
+
new HTTPException(409, { message: "Claude account changed during sign-in. Start again." });
|
|
80
|
+
async function target(deps: ApiRouteDeps, scope: ClaudeOAuthScope, credentialId: string) {
|
|
81
|
+
if (!scope.workspaceId) {
|
|
82
|
+
const pool = await listOrganizationClaudeSubscriptions(deps.db, {
|
|
83
|
+
organizationId: scope.accountId,
|
|
84
|
+
actorSubjectId: scope.actorSubjectId,
|
|
85
|
+
});
|
|
86
|
+
const account = pool.accounts.find((candidate) => candidate.id === credentialId);
|
|
87
|
+
return account
|
|
88
|
+
? { account, authoritySnapshot: { version: 1, scope: "organization" } as const }
|
|
89
|
+
: null;
|
|
90
|
+
}
|
|
91
|
+
const input = { workspaceId: scope.workspaceId, subjectId: scope.actorSubjectId, credentialId };
|
|
92
|
+
const authoritySnapshot = await getClaudeSubscriptionAccountAuthoritySnapshot(deps.db, input);
|
|
93
|
+
if (!authoritySnapshot) return null;
|
|
94
|
+
const account = await getClaudeSubscriptionAccountMetadata(deps.db, input);
|
|
95
|
+
return account ? { account, authoritySnapshot } : null;
|
|
96
|
+
}
|
|
97
|
+
export async function startClaudeSubscriptionOAuth(
|
|
98
|
+
deps: ApiRouteDeps,
|
|
99
|
+
scope: ClaudeOAuthScope,
|
|
100
|
+
input: { scope?: "workspace" | "user" | undefined; reconnectAccountId?: string | undefined } = {},
|
|
101
|
+
) {
|
|
102
|
+
const key = requireEnvironmentEncryption(deps.settings);
|
|
103
|
+
const poolScope = scope.workspaceId ? (input.scope ?? "workspace") : "organization";
|
|
104
|
+
const current = input.reconnectAccountId
|
|
105
|
+
? await target(deps, scope, input.reconnectAccountId)
|
|
106
|
+
: null;
|
|
107
|
+
if (input.reconnectAccountId && (!current || current.account.scope !== poolScope))
|
|
108
|
+
throw changed();
|
|
109
|
+
const verifier = randomBytes(32).toString("base64url"),
|
|
110
|
+
state = randomBytes(32).toString("base64url");
|
|
111
|
+
const id = randomUUID(),
|
|
112
|
+
expiresAt = new Date(Date.now() + 10 * 60_000);
|
|
113
|
+
const attempt = Attempt.parse({
|
|
114
|
+
stage: "pending",
|
|
115
|
+
purpose: "claude-subscription-oauth",
|
|
116
|
+
...scope,
|
|
117
|
+
poolScope,
|
|
118
|
+
state,
|
|
119
|
+
verifier,
|
|
120
|
+
credentialId: current?.account.id ?? null,
|
|
121
|
+
credentialVersion: current?.account.version ?? 0,
|
|
122
|
+
authoritySnapshot: current?.authoritySnapshot ?? null,
|
|
123
|
+
});
|
|
124
|
+
await storeIntegrationOAuthPendingState(deps.db, {
|
|
125
|
+
...scope,
|
|
126
|
+
id,
|
|
127
|
+
expiresAt,
|
|
128
|
+
stateEncrypted: encryptEnvironmentValue(key, JSON.stringify(attempt)),
|
|
129
|
+
});
|
|
130
|
+
return ClaudeSubscriptionOAuthStartResponse.parse({
|
|
131
|
+
attemptId: id,
|
|
132
|
+
expiresAt: expiresAt.toISOString(),
|
|
133
|
+
authorizationUrl: claudeAuthorizationUrl({
|
|
134
|
+
state,
|
|
135
|
+
challenge: createHash("sha256").update(verifier).digest("base64url"),
|
|
136
|
+
}),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Each attempt adds one account or reconnects one exact, generation-fenced target. */
|
|
141
|
+
export async function completeClaudeSubscriptionOAuth(
|
|
142
|
+
deps: ApiRouteDeps,
|
|
143
|
+
scope: ClaudeOAuthScope,
|
|
144
|
+
input: { attemptId: string; code: string },
|
|
145
|
+
reauthorize: (poolScope?: "workspace" | "user" | "organization") => Promise<void>,
|
|
146
|
+
fetchImpl: typeof fetch = globalThis.fetch,
|
|
147
|
+
) {
|
|
148
|
+
const key = requireEnvironmentEncryption(deps.settings);
|
|
149
|
+
const encrypted = await loadIntegrationOAuthPendingState(deps.db, {
|
|
150
|
+
...scope,
|
|
151
|
+
id: input.attemptId,
|
|
152
|
+
});
|
|
153
|
+
const parsed = (() => {
|
|
154
|
+
try {
|
|
155
|
+
return Attempt.safeParse(JSON.parse(decryptEnvironmentValue(key, encrypted!)));
|
|
156
|
+
} catch {
|
|
157
|
+
return { success: false as const };
|
|
158
|
+
}
|
|
159
|
+
})();
|
|
160
|
+
if (!parsed.success)
|
|
161
|
+
throw new HTTPException(410, {
|
|
162
|
+
message: "Claude sign-in expired or was already used. Start again.",
|
|
163
|
+
});
|
|
164
|
+
const attempt = parsed.data;
|
|
165
|
+
if (
|
|
166
|
+
attempt.accountId !== scope.accountId ||
|
|
167
|
+
attempt.workspaceId !== scope.workspaceId ||
|
|
168
|
+
attempt.actorSubjectId !== scope.actorSubjectId ||
|
|
169
|
+
!same(attempt.browserSessionHash, scope.browserSessionHash)
|
|
170
|
+
)
|
|
171
|
+
throw new HTTPException(403, {
|
|
172
|
+
message: "Complete Claude sign-in in the browser where you started it.",
|
|
173
|
+
});
|
|
174
|
+
await reauthorize(attempt.poolScope);
|
|
175
|
+
const current = attempt.credentialId ? await target(deps, scope, attempt.credentialId) : null;
|
|
176
|
+
if (
|
|
177
|
+
attempt.credentialId &&
|
|
178
|
+
(!current ||
|
|
179
|
+
current.account.version !== attempt.credentialVersion ||
|
|
180
|
+
current.account.scope !== attempt.poolScope ||
|
|
181
|
+
JSON.stringify(current.authoritySnapshot) !== JSON.stringify(attempt.authoritySnapshot))
|
|
182
|
+
)
|
|
183
|
+
throw changed();
|
|
184
|
+
if (attempt.stage === "complete")
|
|
185
|
+
return ClaudeSubscriptionOAuthCompleteResponse.parse({
|
|
186
|
+
connected: true,
|
|
187
|
+
accountId: attempt.credentialId,
|
|
188
|
+
scope: attempt.poolScope,
|
|
189
|
+
credentialVersion: attempt.credentialVersion,
|
|
190
|
+
});
|
|
191
|
+
const parts = input.code.trim().split("#");
|
|
192
|
+
if (parts.length !== 2 || !parts[0] || !same(parts[1]!, attempt.state))
|
|
193
|
+
throw new HTTPException(422, {
|
|
194
|
+
message: "Copy the full authorization code from the Claude page.",
|
|
195
|
+
});
|
|
196
|
+
if (
|
|
197
|
+
!(await consumeIntegrationOAuthPendingState(deps.db, {
|
|
198
|
+
...scope,
|
|
199
|
+
id: input.attemptId,
|
|
200
|
+
stateEncrypted: encrypted!,
|
|
201
|
+
}))
|
|
202
|
+
)
|
|
203
|
+
throw new HTTPException(410, {
|
|
204
|
+
message: "Claude sign-in expired or was already used. Start again.",
|
|
205
|
+
});
|
|
206
|
+
let tokens: z.infer<typeof Tokens>;
|
|
207
|
+
try {
|
|
208
|
+
const signal = AbortSignal.timeout(30_000);
|
|
209
|
+
const response = await fetchImpl(CLAUDE_OAUTH_TOKEN_URL, {
|
|
210
|
+
method: "POST",
|
|
211
|
+
redirect: "error",
|
|
212
|
+
signal,
|
|
213
|
+
headers: { "content-type": "application/json" },
|
|
214
|
+
body: JSON.stringify({
|
|
215
|
+
grant_type: "authorization_code",
|
|
216
|
+
client_id: CLAUDE_OAUTH_CLIENT_ID,
|
|
217
|
+
redirect_uri: CLAUDE_OAUTH_REDIRECT_URL,
|
|
218
|
+
code: parts[0],
|
|
219
|
+
code_verifier: attempt.verifier,
|
|
220
|
+
state: attempt.state,
|
|
221
|
+
}),
|
|
222
|
+
});
|
|
223
|
+
if (!response.ok) {
|
|
224
|
+
await response.body?.cancel().catch(() => undefined);
|
|
225
|
+
throw new Error("Token exchange refused");
|
|
226
|
+
}
|
|
227
|
+
tokens = Tokens.parse(
|
|
228
|
+
await readResponseJsonBounded(response, 64 * 1024, "Claude sign-in", { signal }),
|
|
229
|
+
);
|
|
230
|
+
if (!CLAUDE_OAUTH_SCOPES.every((required) => tokens.scope.split(/\s+/).includes(required)))
|
|
231
|
+
throw new Error("Required scopes missing");
|
|
232
|
+
} catch {
|
|
233
|
+
throw new HTTPException(502, {
|
|
234
|
+
message: "Claude sign-in could not be completed. Start sign-in again.",
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const scopes = tokens.scope.split(/\s+/).filter(Boolean);
|
|
238
|
+
const direct = parseClaudeSubscriptionProfile({
|
|
239
|
+
account: tokens.account,
|
|
240
|
+
organization: tokens.organization,
|
|
241
|
+
});
|
|
242
|
+
const profile = await fetchClaudeSubscriptionProfile(tokens.access_token, scopes, fetchImpl);
|
|
243
|
+
if (tokens.account && profile && tokens.account.uuid !== profile.accountUuid)
|
|
244
|
+
throw new HTTPException(502, {
|
|
245
|
+
message: "Claude returned inconsistent account details. Start sign-in again.",
|
|
246
|
+
});
|
|
247
|
+
const accountUuid = tokens.account?.uuid ?? profile?.accountUuid ?? "";
|
|
248
|
+
if (
|
|
249
|
+
current?.account.providerAccountId &&
|
|
250
|
+
!/^(setup|oauth):/.test(current.account.providerAccountId) &&
|
|
251
|
+
current.account.providerAccountId !== accountUuid
|
|
252
|
+
)
|
|
253
|
+
throw new HTTPException(409, {
|
|
254
|
+
message:
|
|
255
|
+
"Sign in to the same Claude account to reconnect. Use Add account for a different subscription.",
|
|
256
|
+
});
|
|
257
|
+
await reauthorize(attempt.poolScope);
|
|
258
|
+
const expiresAt = new Date(Date.now() + tokens.expires_in * 1000);
|
|
259
|
+
const bundle = ClaudeSubscriptionCredential.parse({
|
|
260
|
+
...JSON.parse(
|
|
261
|
+
prepareClaudeSubscriptionCredential(
|
|
262
|
+
deps.settings,
|
|
263
|
+
scope.workspaceId ? "workspace:" + scope.workspaceId : "organization:" + scope.accountId,
|
|
264
|
+
tokens.access_token,
|
|
265
|
+
),
|
|
266
|
+
),
|
|
267
|
+
identity: {
|
|
268
|
+
accountUuid,
|
|
269
|
+
deviceId: createHmac("sha256", key)
|
|
270
|
+
.update("claude-device:" + (scope.workspaceId ?? scope.accountId))
|
|
271
|
+
.digest("hex"),
|
|
272
|
+
},
|
|
273
|
+
oauth: { refreshToken: tokens.refresh_token, expiresAt: expiresAt.toISOString(), scopes },
|
|
274
|
+
});
|
|
275
|
+
const providerAccountId =
|
|
276
|
+
accountUuid || "oauth:" + createHmac("sha256", key).update(tokens.access_token).digest("hex");
|
|
277
|
+
const details = {
|
|
278
|
+
encryptionKey: key,
|
|
279
|
+
secret: bundle,
|
|
280
|
+
providerAccountId,
|
|
281
|
+
accountEmail: profile?.email ?? direct?.email ?? null,
|
|
282
|
+
planType: profile?.plan ?? direct?.plan ?? null,
|
|
283
|
+
expiresAt,
|
|
284
|
+
label: current?.account.label ?? null,
|
|
285
|
+
};
|
|
286
|
+
const saved = await withRlsContext(deps.db, scope, async (tx) => {
|
|
287
|
+
const connected = scope.workspaceId
|
|
288
|
+
? await upsertClaudeSubscriptionAccount(tx, {
|
|
289
|
+
...details,
|
|
290
|
+
accountId: scope.accountId,
|
|
291
|
+
workspaceId: scope.workspaceId,
|
|
292
|
+
subjectId: scope.actorSubjectId,
|
|
293
|
+
scope: attempt.poolScope as "workspace" | "user",
|
|
294
|
+
...(attempt.credentialId
|
|
295
|
+
? {
|
|
296
|
+
credentialId: attempt.credentialId,
|
|
297
|
+
expectedCredentialVersion: attempt.credentialVersion,
|
|
298
|
+
expectedProviderAccountId: current!.account.providerAccountId,
|
|
299
|
+
authoritySnapshot: attempt.authoritySnapshot!,
|
|
300
|
+
}
|
|
301
|
+
: {}),
|
|
302
|
+
})
|
|
303
|
+
: await upsertOrganizationClaudeSubscription(tx, {
|
|
304
|
+
...details,
|
|
305
|
+
organizationId: scope.accountId,
|
|
306
|
+
actorSubjectId: scope.actorSubjectId,
|
|
307
|
+
...(attempt.credentialId
|
|
308
|
+
? {
|
|
309
|
+
credentialId: attempt.credentialId,
|
|
310
|
+
expectedCredentialVersion: attempt.credentialVersion,
|
|
311
|
+
expectedProviderAccountId: current!.account.providerAccountId,
|
|
312
|
+
}
|
|
313
|
+
: {}),
|
|
314
|
+
});
|
|
315
|
+
const authoritySnapshot =
|
|
316
|
+
"authoritySnapshot" in connected
|
|
317
|
+
? connected.authoritySnapshot
|
|
318
|
+
: ({ version: 1, scope: "organization" } as const);
|
|
319
|
+
if (scope.workspaceId)
|
|
320
|
+
await setInitialActiveClaudeCredential(tx, {
|
|
321
|
+
accountId: scope.accountId,
|
|
322
|
+
workspaceId: scope.workspaceId,
|
|
323
|
+
subjectId: scope.actorSubjectId,
|
|
324
|
+
credentialId: connected.account.id,
|
|
325
|
+
authoritySnapshot,
|
|
326
|
+
});
|
|
327
|
+
await storeIntegrationOAuthPendingState(tx, {
|
|
328
|
+
...scope,
|
|
329
|
+
id: input.attemptId,
|
|
330
|
+
expiresAt: new Date(Date.now() + 10 * 60_000),
|
|
331
|
+
stateEncrypted: encryptEnvironmentValue(
|
|
332
|
+
key,
|
|
333
|
+
JSON.stringify({
|
|
334
|
+
purpose: "claude-subscription-oauth",
|
|
335
|
+
...scope,
|
|
336
|
+
stage: "complete",
|
|
337
|
+
poolScope: attempt.poolScope,
|
|
338
|
+
credentialId: connected.account.id,
|
|
339
|
+
credentialVersion: connected.account.version,
|
|
340
|
+
authoritySnapshot,
|
|
341
|
+
}),
|
|
342
|
+
),
|
|
343
|
+
});
|
|
344
|
+
return connected.account;
|
|
345
|
+
}).catch(claudeAccountMutationFailure);
|
|
346
|
+
if (scope.workspaceId)
|
|
347
|
+
await wakeClaudeCapacityWaiters(deps.db, {
|
|
348
|
+
workspaceId: scope.workspaceId,
|
|
349
|
+
subjectId: scope.actorSubjectId,
|
|
350
|
+
authoritySnapshot:
|
|
351
|
+
attempt.poolScope === "workspace"
|
|
352
|
+
? { version: 1, scope: "workspace" }
|
|
353
|
+
: (await target(deps, scope, saved.id))!.authoritySnapshot,
|
|
354
|
+
reason: "claude_account_connected",
|
|
355
|
+
});
|
|
356
|
+
return ClaudeSubscriptionOAuthCompleteResponse.parse({
|
|
357
|
+
connected: true,
|
|
358
|
+
accountId: saved.id,
|
|
359
|
+
scope: attempt.poolScope,
|
|
360
|
+
credentialVersion: saved.version,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { readResponseJsonBounded } from "@opengeni/network";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
const Account = z
|
|
5
|
+
.object({
|
|
6
|
+
uuid: z.string().uuid(),
|
|
7
|
+
email: z.string().trim().email().max(320).optional(),
|
|
8
|
+
email_address: z.string().trim().email().max(320).optional(),
|
|
9
|
+
has_claude_max: z.boolean().optional(),
|
|
10
|
+
has_claude_pro: z.boolean().optional(),
|
|
11
|
+
})
|
|
12
|
+
.passthrough();
|
|
13
|
+
const Profile = z
|
|
14
|
+
.object({
|
|
15
|
+
account: Account,
|
|
16
|
+
organization: z
|
|
17
|
+
.object({
|
|
18
|
+
organization_type: z.string().trim().min(1).max(128).optional(),
|
|
19
|
+
})
|
|
20
|
+
.passthrough()
|
|
21
|
+
.optional(),
|
|
22
|
+
})
|
|
23
|
+
.passthrough();
|
|
24
|
+
|
|
25
|
+
export type ClaudeSubscriptionProfile = {
|
|
26
|
+
accountUuid: string;
|
|
27
|
+
email: string | null;
|
|
28
|
+
plan: string | null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** Only provider-reported identity; never derive email from a token or an organization name. */
|
|
32
|
+
export function parseClaudeSubscriptionProfile(value: unknown): ClaudeSubscriptionProfile | null {
|
|
33
|
+
const parsed = Profile.safeParse(value);
|
|
34
|
+
if (!parsed.success) return null;
|
|
35
|
+
const { account, organization } = parsed.data;
|
|
36
|
+
return {
|
|
37
|
+
accountUuid: account.uuid,
|
|
38
|
+
email: account.email ?? account.email_address ?? null,
|
|
39
|
+
plan:
|
|
40
|
+
organization?.organization_type ??
|
|
41
|
+
(account.has_claude_max === true
|
|
42
|
+
? "claude_max"
|
|
43
|
+
: account.has_claude_pro === true
|
|
44
|
+
? "claude_pro"
|
|
45
|
+
: null),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Optional profile read. A profile outage must not discard a valid sign-in. */
|
|
50
|
+
export async function fetchClaudeSubscriptionProfile(
|
|
51
|
+
token: string,
|
|
52
|
+
scopes: readonly string[],
|
|
53
|
+
fetchImpl: typeof fetch = globalThis.fetch,
|
|
54
|
+
): Promise<ClaudeSubscriptionProfile | null> {
|
|
55
|
+
if (!scopes.includes("user:profile")) return null;
|
|
56
|
+
try {
|
|
57
|
+
const signal = AbortSignal.timeout(10_000);
|
|
58
|
+
const response = await fetchImpl("https://api.anthropic.com/api/oauth/profile", {
|
|
59
|
+
headers: { authorization: `Bearer ${token}`, accept: "application/json" },
|
|
60
|
+
redirect: "error",
|
|
61
|
+
signal,
|
|
62
|
+
});
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
await response.body?.cancel().catch(() => undefined);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return parseClaudeSubscriptionProfile(
|
|
68
|
+
await readResponseJsonBounded(response, 64 * 1024, "Claude profile", { signal }),
|
|
69
|
+
);
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { parseClaudeUsageResponse, type Settings } from "@opengeni/config";
|
|
2
|
+
import { emptyClaudeUsage } from "@opengeni/config";
|
|
3
|
+
import type { ClaudeSubscriptionUsage } from "@opengeni/contracts";
|
|
4
|
+
import {
|
|
5
|
+
resolveClaudeSubscriptionCredential,
|
|
6
|
+
ClaudeSubscriptionConnectionChanged,
|
|
7
|
+
ClaudeSubscriptionRefreshUnavailable,
|
|
8
|
+
recordClaudeSubscriptionUsage,
|
|
9
|
+
type ClaudeUsageScope,
|
|
10
|
+
type Database,
|
|
11
|
+
} from "@opengeni/db";
|
|
12
|
+
import { readResponseJsonBounded } from "@opengeni/network";
|
|
13
|
+
import { HTTPException } from "hono/http-exception";
|
|
14
|
+
|
|
15
|
+
export async function requestClaudeUsage(
|
|
16
|
+
token: string,
|
|
17
|
+
fetchImpl: typeof fetch = globalThis.fetch,
|
|
18
|
+
) {
|
|
19
|
+
const checkedAt = new Date().toISOString();
|
|
20
|
+
let status: ClaudeSubscriptionUsage["refreshStatus"] = "unavailable";
|
|
21
|
+
let observation: ReturnType<typeof parseClaudeUsageResponse> = null;
|
|
22
|
+
try {
|
|
23
|
+
const signal = AbortSignal.timeout(10_000);
|
|
24
|
+
const response = await fetchImpl("https://api.anthropic.com/api/oauth/usage", {
|
|
25
|
+
headers: {
|
|
26
|
+
authorization: `Bearer ${token}`,
|
|
27
|
+
"content-type": "application/json",
|
|
28
|
+
"cache-control": "no-cache",
|
|
29
|
+
},
|
|
30
|
+
redirect: "error",
|
|
31
|
+
signal,
|
|
32
|
+
});
|
|
33
|
+
if (response.ok || response.status === 403) {
|
|
34
|
+
const body = await readResponseJsonBounded<unknown>(response, 64 * 1024, "Claude usage", {
|
|
35
|
+
signal,
|
|
36
|
+
});
|
|
37
|
+
if (response.ok) {
|
|
38
|
+
observation = parseClaudeUsageResponse(body, new Date(checkedAt));
|
|
39
|
+
status = observation ? "available" : "unavailable";
|
|
40
|
+
} else {
|
|
41
|
+
const error = body && typeof body === "object" && "error" in body ? body.error : null;
|
|
42
|
+
const message =
|
|
43
|
+
error && typeof error === "object" && "message" in error ? error.message : null;
|
|
44
|
+
status =
|
|
45
|
+
typeof message === "string" &&
|
|
46
|
+
message.includes("scope requirement") &&
|
|
47
|
+
message.includes("user:profile")
|
|
48
|
+
? "scope_required"
|
|
49
|
+
: "unavailable";
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
status = response.status === 401 ? "reconnect" : "unavailable";
|
|
53
|
+
await response.body?.cancel().catch(() => undefined);
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// Retain the last provider observation; never imply an unavailable quota is zero.
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { status, observation, checkedAt };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** No inference calls. Inference-only setup tokens retain their response-header cache. */
|
|
63
|
+
export async function refreshClaudeSubscriptionUsage(
|
|
64
|
+
db: Database,
|
|
65
|
+
settings: Settings,
|
|
66
|
+
scope: ClaudeUsageScope,
|
|
67
|
+
fetchImpl: typeof fetch = globalThis.fetch,
|
|
68
|
+
) {
|
|
69
|
+
const credential = await resolveClaudeSubscriptionCredential(db, settings, scope, {
|
|
70
|
+
fetchImpl,
|
|
71
|
+
}).catch((error) => {
|
|
72
|
+
if (error instanceof ClaudeSubscriptionConnectionChanged)
|
|
73
|
+
throw new HTTPException(409, { message: error.message });
|
|
74
|
+
if (error instanceof ClaudeSubscriptionRefreshUnavailable)
|
|
75
|
+
throw new HTTPException(503, { message: error.message });
|
|
76
|
+
throw error;
|
|
77
|
+
});
|
|
78
|
+
if (!credential) return emptyClaudeUsage(null);
|
|
79
|
+
if ("reconnectRequired" in credential) return credential.usage;
|
|
80
|
+
const current = credential.usage;
|
|
81
|
+
if (
|
|
82
|
+
current.refreshStatus === "scope_required" ||
|
|
83
|
+
(current.refreshCheckedAt && Date.now() - Date.parse(current.refreshCheckedAt) < 30_000)
|
|
84
|
+
)
|
|
85
|
+
return current;
|
|
86
|
+
const { status, observation, checkedAt } = await requestClaudeUsage(credential.token, fetchImpl);
|
|
87
|
+
const result = await recordClaudeSubscriptionUsage(db, settings, scope, {
|
|
88
|
+
token: credential.token,
|
|
89
|
+
expectedConnectionId: credential.connectionId,
|
|
90
|
+
expectedCredentialVersion: credential.credentialVersion,
|
|
91
|
+
...(observation ? { observation } : {}),
|
|
92
|
+
refresh: { status, checkedAt },
|
|
93
|
+
});
|
|
94
|
+
if (!result)
|
|
95
|
+
throw new HTTPException(409, { message: "Claude connection changed; reload usage." });
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createHmac } from "node:crypto";
|
|
2
|
+
import { requireEnvironmentEncryption } from "@opengeni/core";
|
|
3
|
+
import { ClaudeSubscriptionCredential, type Settings } from "@opengeni/config";
|
|
4
|
+
import { HTTPException } from "hono/http-exception";
|
|
5
|
+
|
|
6
|
+
/** Validate the canonical model lane before it bypasses integration acquisition. */
|
|
7
|
+
export function assertClaudeWorkspaceCredential(
|
|
8
|
+
settings: Pick<Settings, "claudeSubscriptionEnabled">,
|
|
9
|
+
input: {
|
|
10
|
+
subjectId: string | null;
|
|
11
|
+
providerDomain: string;
|
|
12
|
+
kind: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
credential: unknown;
|
|
15
|
+
},
|
|
16
|
+
): void {
|
|
17
|
+
const role = input.metadata?.credentialRole;
|
|
18
|
+
if (role !== "anthropic" && role !== "claude_subscription") return;
|
|
19
|
+
if (role === "claude_subscription" && !settings.claudeSubscriptionEnabled)
|
|
20
|
+
throw new HTTPException(404, { message: "Claude subscriptions are not enabled" });
|
|
21
|
+
if (role === "claude_subscription")
|
|
22
|
+
throw new HTTPException(410, { message: "Use individual Claude subscription accounts." });
|
|
23
|
+
if (
|
|
24
|
+
input.subjectId !== null ||
|
|
25
|
+
input.providerDomain !== "api.anthropic.com" ||
|
|
26
|
+
input.kind !== "api_key"
|
|
27
|
+
)
|
|
28
|
+
throw new HTTPException(422, {
|
|
29
|
+
message: "Claude model connections must belong to this workspace",
|
|
30
|
+
});
|
|
31
|
+
const credential = input.credential;
|
|
32
|
+
const key =
|
|
33
|
+
credential && typeof credential === "object" && "apiKey" in credential
|
|
34
|
+
? credential.apiKey
|
|
35
|
+
: null;
|
|
36
|
+
if (typeof key !== "string")
|
|
37
|
+
throw new HTTPException(422, { message: "Claude connection credential is required" });
|
|
38
|
+
if (role === "anthropic") {
|
|
39
|
+
if (!/^sk-ant-api[0-9]+-\S+$/.test(key))
|
|
40
|
+
throw new HTTPException(422, {
|
|
41
|
+
message: "Enter an Anthropic API key. Use Claude subscription for setup tokens.",
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Stable installation identity; setup tokens do not grant profile access. */
|
|
48
|
+
export function prepareClaudeSubscriptionCredential(
|
|
49
|
+
settings: Settings,
|
|
50
|
+
scope: string,
|
|
51
|
+
token: string,
|
|
52
|
+
identity?: { accountUuid: string; deviceId: string },
|
|
53
|
+
): string {
|
|
54
|
+
if (!settings.claudeSubscriptionEnabled)
|
|
55
|
+
throw new HTTPException(404, { message: "Claude subscriptions are not enabled" });
|
|
56
|
+
if (!/^sk-ant-oat[0-9]+-\S+$/.test(token))
|
|
57
|
+
throw new HTTPException(422, { message: "Enter the setup token from claude setup-token." });
|
|
58
|
+
return JSON.stringify(
|
|
59
|
+
ClaudeSubscriptionCredential.parse({
|
|
60
|
+
version: 1,
|
|
61
|
+
token,
|
|
62
|
+
identity: identity ?? {
|
|
63
|
+
accountUuid: "",
|
|
64
|
+
deviceId: createHmac("sha256", requireEnvironmentEncryption(settings))
|
|
65
|
+
.update("claude-device:" + scope)
|
|
66
|
+
.digest("hex"),
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function prepareClaudeWorkspaceCredential(
|
|
73
|
+
settings: Settings,
|
|
74
|
+
workspaceId: string,
|
|
75
|
+
metadata: Record<string, unknown> | undefined,
|
|
76
|
+
credential: Record<string, unknown>,
|
|
77
|
+
): Record<string, unknown> {
|
|
78
|
+
if (metadata?.credentialRole !== "claude_subscription") return credential;
|
|
79
|
+
if (!settings.claudeSubscriptionEnabled)
|
|
80
|
+
throw new HTTPException(404, { message: "Claude subscriptions are not enabled" });
|
|
81
|
+
throw new HTTPException(410, { message: "Use individual Claude subscription accounts." });
|
|
82
|
+
}
|