@hotmeshio/long-tail 0.23.2 → 0.24.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/build/adapters/express.d.ts +9 -1
- package/build/adapters/express.js +32 -41
- package/build/api/auth-sso.d.ts +1 -1
- package/build/api/bot-accounts.d.ts +2 -0
- package/build/api/bot-accounts.js +4 -0
- package/build/api/capabilities.d.ts +7 -2
- package/build/api/capabilities.js +13 -2
- package/build/api/escalations/analytics.js +5 -0
- package/build/api/escalations/bulk.d.ts +2 -0
- package/build/api/escalations/bulk.js +9 -4
- package/build/api/escalations/helpers.d.ts +5 -0
- package/build/api/escalations/helpers.js +7 -0
- package/build/api/escalations/metadata.js +3 -0
- package/build/api/escalations/single.d.ts +3 -2
- package/build/api/escalations/single.js +8 -3
- package/build/api/mcp/tools.d.ts +12 -0
- package/build/api/mcp/tools.js +113 -9
- package/build/api/scan-codes/choice.js +31 -7
- package/build/api/settings.js +9 -0
- package/build/api/tasks.d.ts +3 -3
- package/build/api/tasks.js +30 -7
- package/build/api/workflows/input-lookups.js +1 -1
- package/build/api/workflows/invocable.js +2 -2
- package/build/api/workflows/invocation.js +1 -1
- package/build/api/yaml-workflows/deploy.d.ts +10 -2
- package/build/api/yaml-workflows/deploy.js +42 -2
- package/build/lib/db/schemas/038_oauth_server.sql +46 -0
- package/build/lib/events/browser-view.d.ts +15 -0
- package/build/lib/events/browser-view.js +101 -0
- package/build/lib/events/nats-ws-filter.d.ts +29 -0
- package/build/lib/events/nats-ws-filter.js +94 -0
- package/build/lib/events/nats-ws-proxy.d.ts +2 -0
- package/build/lib/events/nats-ws-proxy.js +39 -5
- package/build/lib/events/nats-ws-server-filter.d.ts +16 -0
- package/build/lib/events/nats-ws-server-filter.js +56 -0
- package/build/lib/events/nats-ws-ticket.d.ts +10 -0
- package/build/lib/events/nats-ws-ticket.js +42 -0
- package/build/lib/events/socketio.d.ts +10 -1
- package/build/lib/events/socketio.js +10 -3
- package/build/lib/http/index.d.ts +27 -0
- package/build/lib/http/index.js +55 -0
- package/build/modules/auth.d.ts +15 -24
- package/build/modules/auth.js +43 -126
- package/build/modules/capabilities.d.ts +62 -0
- package/build/modules/capabilities.js +188 -0
- package/build/modules/config.d.ts +14 -0
- package/build/modules/config.js +20 -0
- package/build/modules/defaults.d.ts +10 -0
- package/build/modules/defaults.js +12 -1
- package/build/modules/mcp-auth.d.ts +11 -0
- package/build/modules/mcp-auth.js +56 -0
- package/build/modules/oauth-server.d.ts +18 -0
- package/build/modules/oauth-server.js +40 -0
- package/build/routes/agents.js +46 -14
- package/build/routes/announcements.js +2 -2
- package/build/routes/auth-sso.js +2 -2
- package/build/routes/auth.js +2 -2
- package/build/routes/bot-accounts.js +26 -3
- package/build/routes/capabilities.js +4 -4
- package/build/routes/controlplane.js +2 -2
- package/build/routes/dba.js +2 -2
- package/build/routes/delegation.js +2 -2
- package/build/routes/diagnostics.js +2 -2
- package/build/routes/docs.js +2 -2
- package/build/routes/domain.js +2 -2
- package/build/routes/escalations/accumulate.d.ts +1 -1
- package/build/routes/escalations/acting.d.ts +1 -1
- package/build/routes/escalations/analytics.d.ts +1 -1
- package/build/routes/escalations/bulk.d.ts +1 -1
- package/build/routes/escalations/facets.d.ts +1 -1
- package/build/routes/escalations/index.js +2 -2
- package/build/routes/escalations/list.d.ts +1 -1
- package/build/routes/escalations/metadata.d.ts +1 -1
- package/build/routes/escalations/related.d.ts +1 -1
- package/build/routes/escalations/resolve.d.ts +1 -1
- package/build/routes/escalations/single.d.ts +1 -1
- package/build/routes/escalations/single.js +1 -1
- package/build/routes/exports.js +14 -13
- package/build/routes/file-browser.js +9 -8
- package/build/routes/files.js +2 -2
- package/build/routes/index.js +4 -2
- package/build/routes/insight.js +5 -4
- package/build/routes/knowledge.js +2 -2
- package/build/routes/maintenance.js +2 -2
- package/build/routes/mcp-endpoint.d.ts +3 -1
- package/build/routes/mcp-endpoint.js +18 -8
- package/build/routes/mcp.js +35 -15
- package/build/routes/me.js +2 -2
- package/build/routes/namespaces.js +4 -3
- package/build/routes/nats-credentials.js +27 -3
- package/build/routes/oauth-server/authorize.d.ts +2 -0
- package/build/routes/oauth-server/authorize.js +108 -0
- package/build/routes/oauth-server/grants.d.ts +2 -0
- package/build/routes/oauth-server/grants.js +35 -0
- package/build/routes/oauth-server/index.d.ts +8 -0
- package/build/routes/oauth-server/index.js +52 -0
- package/build/routes/oauth-server/registration.d.ts +2 -0
- package/build/routes/oauth-server/registration.js +38 -0
- package/build/routes/oauth-server/token.d.ts +2 -0
- package/build/routes/oauth-server/token.js +100 -0
- package/build/routes/oauth-well-known.d.ts +8 -0
- package/build/routes/oauth-well-known.js +31 -0
- package/build/routes/oauth.js +2 -2
- package/build/routes/overview.js +2 -2
- package/build/routes/personas.js +2 -2
- package/build/routes/pipelines.js +6 -5
- package/build/routes/roles.js +2 -2
- package/build/routes/scan-codes.js +2 -2
- package/build/routes/settings.js +2 -2
- package/build/routes/tasks.js +5 -5
- package/build/routes/topics.js +21 -9
- package/build/routes/users.js +25 -22
- package/build/routes/workflow-sets.js +13 -12
- package/build/routes/workflows/config.js +15 -2
- package/build/routes/workflows/discovery.js +2 -2
- package/build/routes/workflows/index.js +2 -2
- package/build/routes/workflows/invocation.js +11 -10
- package/build/routes/yaml-workflows/cron.js +19 -6
- package/build/routes/yaml-workflows/crud.js +13 -12
- package/build/routes/yaml-workflows/deployment.js +11 -10
- package/build/routes/yaml-workflows/index.js +2 -2
- package/build/routes/yaml-workflows/versions.js +2 -2
- package/build/services/agent/authority.d.ts +14 -0
- package/build/services/agent/authority.js +53 -0
- package/build/services/auth/bot-api-key.d.ts +2 -0
- package/build/services/auth/bot-api-key.js +10 -0
- package/build/services/auth/oauth-server/access-token.d.ts +36 -0
- package/build/services/auth/oauth-server/access-token.js +80 -0
- package/build/services/auth/oauth-server/authorization.d.ts +47 -0
- package/build/services/auth/oauth-server/authorization.js +54 -0
- package/build/services/auth/oauth-server/constants.d.ts +11 -0
- package/build/services/auth/oauth-server/constants.js +16 -0
- package/build/services/auth/oauth-server/index.d.ts +3 -0
- package/build/services/auth/oauth-server/index.js +26 -0
- package/build/services/auth/oauth-server/metadata.d.ts +21 -0
- package/build/services/auth/oauth-server/metadata.js +53 -0
- package/build/services/auth/oauth-server/registration.d.ts +42 -0
- package/build/services/auth/oauth-server/registration.js +134 -0
- package/build/services/auth/oauth-server/sql.d.ts +34 -0
- package/build/services/auth/oauth-server/sql.js +123 -0
- package/build/services/auth/oauth-server/store.d.ts +74 -0
- package/build/services/auth/oauth-server/store.js +151 -0
- package/build/services/auth/sql.d.ts +1 -0
- package/build/services/auth/sql.js +3 -1
- package/build/services/escalation/queries.js +7 -2
- package/build/services/export/index.d.ts +10 -0
- package/build/services/export/index.js +27 -0
- package/build/services/invocation-access.d.ts +25 -4
- package/build/services/invocation-access.js +39 -7
- package/build/services/mcp/client/connection-dispatch.d.ts +6 -1
- package/build/services/mcp/client/connection-dispatch.js +3 -2
- package/build/services/mcp/client/connection-lifecycle.d.ts +10 -2
- package/build/services/mcp/client/connection-lifecycle.js +45 -7
- package/build/services/mcp/client/connection-test.js +2 -6
- package/build/services/mcp/client/connection.d.ts +1 -1
- package/build/services/mcp/client/connection.js +3 -1
- package/build/services/mcp/client/index.d.ts +2 -2
- package/build/services/mcp/client/index.js +4 -1
- package/build/services/mcp/client/network-transport.d.ts +12 -0
- package/build/services/mcp/client/network-transport.js +20 -0
- package/build/services/mcp/client/tools.d.ts +13 -0
- package/build/services/mcp/client/tools.js +18 -0
- package/build/services/mcp/db-server/index.js +2 -1
- package/build/services/mcp/external-server.d.ts +9 -4
- package/build/services/mcp/external-server.js +34 -11
- package/build/services/mcp/playwright-server/index.js +2 -1
- package/build/services/mcp/registered-tools.d.ts +3 -0
- package/build/services/mcp/registered-tools.js +7 -0
- package/build/services/mcp/server-lifecycle.js +2 -1
- package/build/services/workflow-invocation.d.ts +16 -3
- package/build/services/workflow-invocation.js +42 -10
- package/build/services/yaml-workflow/deployer.d.ts +10 -2
- package/build/services/yaml-workflow/deployer.js +17 -5
- package/build/services/yaml-workflow/invoke.js +27 -26
- package/build/start/config.js +7 -0
- package/build/start/index.js +4 -22
- package/build/start/server.d.ts +6 -4
- package/build/start/server.js +11 -35
- package/build/start/socket-auth.js +2 -1
- package/build/start/workers.js +1 -1
- package/build/system/mcp-servers/admin/agent-subscriptions.js +24 -1
- package/build/system/mcp-servers/admin/agents.js +25 -2
- package/build/system/mcp-servers/admin/announcements.js +3 -2
- package/build/system/mcp-servers/admin/bot-accounts.js +37 -5
- package/build/system/mcp-servers/admin/escalations.d.ts +3 -5
- package/build/system/mcp-servers/admin/escalations.js +74 -70
- package/build/system/mcp-servers/admin/exports.js +7 -1
- package/build/system/mcp-servers/admin/index.js +24 -25
- package/build/system/mcp-servers/admin/overview.d.ts +2 -2
- package/build/system/mcp-servers/admin/run-read.d.ts +2 -0
- package/build/system/mcp-servers/admin/run-read.js +5 -0
- package/build/system/mcp-servers/admin/scan-codes.d.ts +2 -2
- package/build/system/mcp-servers/admin/scan-codes.js +5 -11
- package/build/system/mcp-servers/admin/schemas.d.ts +20 -20
- package/build/system/mcp-servers/admin/tasks.js +7 -2
- package/build/system/mcp-servers/admin/users.js +31 -4
- package/build/system/mcp-servers/admin/workflow-config.js +12 -1
- package/build/system/mcp-servers/admin/workflows.js +27 -5
- package/build/system/mcp-servers/admin/yaml-workflows.js +7 -1
- package/build/system/mcp-servers/caller-auth.d.ts +18 -0
- package/build/system/mcp-servers/caller-auth.js +29 -0
- package/build/system/mcp-servers/claude-code.js +2 -1
- package/build/system/mcp-servers/db-query/index.js +2 -1
- package/build/system/mcp-servers/file-storage.js +2 -1
- package/build/system/mcp-servers/http-fetch.js +2 -1
- package/build/system/mcp-servers/human-queue.js +7 -3
- package/build/system/mcp-servers/oauth.js +31 -4
- package/build/system/mcp-servers/schema-exchange.js +2 -1
- package/build/system/mcp-servers/vision.d.ts +1 -0
- package/build/system/mcp-servers/vision.js +17 -7
- package/build/system/seed/tool-manifests-admin.d.ts +2 -17985
- package/build/system/seed/tool-manifests-admin.js +121 -121
- package/build/system/seed/tool-manifests-data.d.ts +8 -374
- package/build/system/seed/tool-manifests-data.js +22 -4
- package/build/system/seed/tool-manifests-escalation.d.ts +2 -352
- package/build/system/seed/tool-manifests-escalation.js +11 -1
- package/build/system/seed/tool-manifests-events.d.ts +2 -116
- package/build/system/seed/tool-manifests-events.js +6 -2
- package/build/system/seed/tool-manifests-knowledge.d.ts +2 -221
- package/build/system/seed/tool-manifests-knowledge.js +41 -1
- package/build/system/seed/tool-manifests-workflows.d.ts +2 -71
- package/build/system/seed/tool-manifests-workflows.js +3 -1
- package/build/system/workflows/shared/principal-tools.d.ts +20 -0
- package/build/system/workflows/shared/principal-tools.js +64 -0
- package/build/system/workflows/shared/tool-executor.js +21 -11
- package/build/system/workflows/shared/tool-loader.js +8 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/auth.d.ts +1 -1
- package/build/types/capability.d.ts +14 -0
- package/build/types/capability.js +2 -0
- package/build/types/events.d.ts +1 -1
- package/build/types/index.d.ts +3 -1
- package/build/types/mcp.d.ts +3 -1
- package/build/types/oauth-server.d.ts +52 -0
- package/build/types/oauth-server.js +2 -0
- package/build/types/startup.d.ts +15 -7
- package/build/vitest.config.js +2 -1
- package/build/vitest.fast.config.d.ts +2 -0
- package/build/vitest.fast.config.js +20 -0
- package/build/vitest.llm.config.d.ts +2 -0
- package/build/vitest.llm.config.js +16 -0
- package/dashboard/dist/assets/{AdminDashboard-BPWsgMfZ.js → AdminDashboard-B3mkMIOT.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-BPWsgMfZ.js.map → AdminDashboard-B3mkMIOT.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-De9imyo5.js → AgentConfigPage-CQj2B678.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-De9imyo5.js.map → AgentConfigPage-CQj2B678.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-B6o96Y5G.js → AgentDetailPage-CzrD8JJI.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-B6o96Y5G.js.map → AgentDetailPage-CzrD8JJI.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-DMEmiK_W.js → AgentsPage-jg2OFwyW.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-DMEmiK_W.js.map → AgentsPage-jg2OFwyW.js.map} +1 -1
- package/dashboard/dist/assets/{AvailableEscalationsPage-DeYMngwE.js → AvailableEscalationsPage-IS28XBRe.js} +2 -2
- package/dashboard/dist/assets/{AvailableEscalationsPage-DeYMngwE.js.map → AvailableEscalationsPage-IS28XBRe.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-DDmpgGHZ.js → BotPicker-DvEJFpwS.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DDmpgGHZ.js.map → BotPicker-DvEJFpwS.js.map} +1 -1
- package/dashboard/dist/assets/{BulkAssignModal-BqlklomG.js → BulkAssignModal-BoQCi-YC.js} +2 -2
- package/dashboard/dist/assets/{BulkAssignModal-BqlklomG.js.map → BulkAssignModal-BoQCi-YC.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-DOtjDTbK.js → CapabilitiesPage-kwlrcb_t.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-DOtjDTbK.js.map → CapabilitiesPage-kwlrcb_t.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-CLr8LP0V.js → CollapsibleSection-UMIV4KIj.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-CLr8LP0V.js.map → CollapsibleSection-UMIV4KIj.js.map} +1 -1
- package/dashboard/dist/assets/ConfirmDeleteModal-C5puwMg7.js +2 -0
- package/dashboard/dist/assets/ConfirmDeleteModal-C5puwMg7.js.map +1 -0
- package/dashboard/dist/assets/ConnectedAppsPage-drZa65kP.js +2 -0
- package/dashboard/dist/assets/ConnectedAppsPage-drZa65kP.js.map +1 -0
- package/dashboard/dist/assets/{CountdownTimer-DxX72PwV.js → CountdownTimer-LG03dVgR.js} +2 -2
- package/dashboard/dist/assets/{CountdownTimer-DxX72PwV.js.map → CountdownTimer-LG03dVgR.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-CObc9Rs5.js → CredentialsPage-CaFQ6vLb.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-CObc9Rs5.js.map → CredentialsPage-CaFQ6vLb.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-Bb8GoNY5.js → CronLabel-Co5j7tzW.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-Bb8GoNY5.js.map → CronLabel-Co5j7tzW.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-CmGhVlYl.js → ElapsedCell-WJyE9G0T.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-CmGhVlYl.js.map → ElapsedCell-WJyE9G0T.js.map} +1 -1
- package/dashboard/dist/assets/{EntityTimelinePanel-B4Jwy1w3.js → EntityTimelinePanel-CDFFfX9d.js} +2 -2
- package/dashboard/dist/assets/{EntityTimelinePanel-B4Jwy1w3.js.map → EntityTimelinePanel-CDFFfX9d.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-CZZbZ4Aa.js → EscalationListSchemaPage-CMrUlaKI.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-CZZbZ4Aa.js.map → EscalationListSchemaPage-CMrUlaKI.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-D0ko59_S.js → EscalationSchemaPage-Dx2Lyfrz.js} +2 -2
- package/dashboard/dist/assets/{EscalationSchemaPage-D0ko59_S.js.map → EscalationSchemaPage-Dx2Lyfrz.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationTimeline-BcnJEKFD.js → EscalationTimeline-d3nvshOk.js} +2 -2
- package/dashboard/dist/assets/{EscalationTimeline-BcnJEKFD.js.map → EscalationTimeline-d3nvshOk.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationTitleSelect-P0q5sck4.js → EscalationTitleSelect-DWQieOM9.js} +2 -2
- package/dashboard/dist/assets/{EscalationTitleSelect-P0q5sck4.js.map → EscalationTitleSelect-DWQieOM9.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BLPmh4AF.js → EscalationsOverview-BX8Cpi0X.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BLPmh4AF.js.map → EscalationsOverview-BX8Cpi0X.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-gBzi5tIS.js → EventTable-D1-ITVXS.js} +2 -2
- package/dashboard/dist/assets/{EventTable-gBzi5tIS.js.map → EventTable-D1-ITVXS.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-BulSsWb2.js → FilterBar-ABC9ctin.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-BulSsWb2.js.map → FilterBar-ABC9ctin.js.map} +1 -1
- package/dashboard/dist/assets/{FormattedValue-BMHRrvrL.js → FormattedValue-k27T4bG7.js} +2 -2
- package/dashboard/dist/assets/{FormattedValue-BMHRrvrL.js.map → FormattedValue-k27T4bG7.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage--997yNRr.js → GraphInvokePage-DKUErxWc.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage--997yNRr.js.map → GraphInvokePage-DKUErxWc.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-CwXFz--v.js → HomePage-C6u9dIpR.js} +2 -2
- package/dashboard/dist/assets/{HomePage-CwXFz--v.js.map → HomePage-C6u9dIpR.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-Df4qZryJ.js → ListToolbar-gA5rFzkz.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-Df4qZryJ.js.map → ListToolbar-gA5rFzkz.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-DrogIAKJ.js → McpOverview-BOPo3J_i.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-DrogIAKJ.js.map → McpOverview-BOPo3J_i.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-CJmvCAE6.js → McpQueryDetailPage-BRVVi0Zh.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-CJmvCAE6.js.map → McpQueryDetailPage-BRVVi0Zh.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-lygo4Yq0.js → McpQueryPage-Dmbb6SAc.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-lygo4Yq0.js.map → McpQueryPage-Dmbb6SAc.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-hvns2sCm.js → McpRunDetailPage-bMXNO-6V.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-hvns2sCm.js.map → McpRunDetailPage-bMXNO-6V.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-C2_7spTX.js → McpRunsPage-Bi9AMWja.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-C2_7spTX.js.map → McpRunsPage-Bi9AMWja.js.map} +1 -1
- package/dashboard/dist/assets/{OperationsPage-BlK8WlsY.js → OperationsPage-CvR74ZUJ.js} +3 -3
- package/dashboard/dist/assets/{OperationsPage-BlK8WlsY.js.map → OperationsPage-CvR74ZUJ.js.map} +1 -1
- package/dashboard/dist/assets/{OperatorDashboard-BeWBiMGX.js → OperatorDashboard-DctmZw10.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-BeWBiMGX.js.map → OperatorDashboard-DctmZw10.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-CNHpsX_i.js → PersonaDetailPage-Ubqm2rQH.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-CNHpsX_i.js.map → PersonaDetailPage-Ubqm2rQH.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-ChASEZRg.js → PersonasPage-BpFL2rpo.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-ChASEZRg.js.map → PersonasPage-BpFL2rpo.js.map} +1 -1
- package/dashboard/dist/assets/{PortalPage-D5Lk-PKF.js → PortalPage-Dfc_Q02R.js} +2 -2
- package/dashboard/dist/assets/{PortalPage-D5Lk-PKF.js.map → PortalPage-Dfc_Q02R.js.map} +1 -1
- package/dashboard/dist/assets/ProcessDetailPage-liJspvQh.js +2 -0
- package/dashboard/dist/assets/{ProcessDetailPage-DNgCIwRl.js.map → ProcessDetailPage-liJspvQh.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-pRjWuWlH.js → ProcessesListPage-C3OM5ZUy.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-pRjWuWlH.js.map → ProcessesListPage-C3OM5ZUy.js.map} +1 -1
- package/dashboard/dist/assets/ResolverForm-B1_x8Blv.js +8 -0
- package/dashboard/dist/assets/ResolverForm-B1_x8Blv.js.map +1 -0
- package/dashboard/dist/assets/{RoleDetailPage-Bh_UcfYY.js → RoleDetailPage-CrlYCh-R.js} +2 -2
- package/dashboard/dist/assets/{RoleDetailPage-Bh_UcfYY.js.map → RoleDetailPage-CrlYCh-R.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-CK17N7zz.js → RolesPage-BaazL_pt.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-CK17N7zz.js.map → RolesPage-BaazL_pt.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-BEKUP0p2.js → RunAsSelector-DxuV0fa1.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-BEKUP0p2.js.map → RunAsSelector-DxuV0fa1.js.map} +1 -1
- package/dashboard/dist/assets/{ScanCodesPage-Bd0Pflqx.js → ScanCodesPage-C0mvmYTq.js} +2 -2
- package/dashboard/dist/assets/{ScanCodesPage-Bd0Pflqx.js.map → ScanCodesPage-C0mvmYTq.js.map} +1 -1
- package/dashboard/dist/assets/{ScanSchemeDetailPage-CnuhozmJ.js → ScanSchemeDetailPage-CiJgt5dZ.js} +2 -2
- package/dashboard/dist/assets/{ScanSchemeDetailPage-CnuhozmJ.js.map → ScanSchemeDetailPage-CiJgt5dZ.js.map} +1 -1
- package/dashboard/dist/assets/{ScanStationPage-CoLc-q3h.js → ScanStationPage-DzGaXc40.js} +2 -2
- package/dashboard/dist/assets/{ScanStationPage-CoLc-q3h.js.map → ScanStationPage-DzGaXc40.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-COrfvkBi.js → StickyPagination-CqbjVY6T.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-COrfvkBi.js.map → StickyPagination-CqbjVY6T.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-Cif2egLy.js → StreamMessageDetail-wX3i0aqs.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-Cif2egLy.js.map → StreamMessageDetail-wX3i0aqs.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-BnZYTrAh.js → SwimlaneTimeline-C50PsFoV.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-BnZYTrAh.js.map → SwimlaneTimeline-C50PsFoV.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-DEz_dqyR.js → TaskDetailPage-bbNSv4Ae.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-DEz_dqyR.js.map → TaskDetailPage-bbNSv4Ae.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-oEfEGj0_.js → TasksListPage-CDWnm_d9.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-oEfEGj0_.js.map → TasksListPage-CDWnm_d9.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-Dz5TgZZZ.js → TimeAgo-BLjqlfoX.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-Dz5TgZZZ.js.map → TimeAgo-BLjqlfoX.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-BUTgE244.js → TimestampCell-CnW0ENb5.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-BUTgE244.js.map → TimestampCell-CnW0ENb5.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-BS4g6vt-.js → ToolTestPanel-DkMgCB-k.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-BS4g6vt-.js.map → ToolTestPanel-DkMgCB-k.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-C_I6JIVD.js → TopicDetailPage-g3pljKKX.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-C_I6JIVD.js.map → TopicDetailPage-g3pljKKX.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-Cr6-wfhJ.js → TopicsPage-CVcCYdy-.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-Cr6-wfhJ.js.map → TopicsPage-CVcCYdy-.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-ZFpWMaFF.js → UserName-D6zGIK2i.js} +2 -2
- package/dashboard/dist/assets/{UserName-ZFpWMaFF.js.map → UserName-D6zGIK2i.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-ByPixDZW.js → WorkflowExecutionPage-B8g81FCJ.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-ByPixDZW.js.map → WorkflowExecutionPage-B8g81FCJ.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-CWsJdMdR.js → WorkflowsDashboard-nxT3bv6W.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-CWsJdMdR.js.map → WorkflowsDashboard-nxT3bv6W.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-Dk9gyqZN.js → WorkflowsOverview-grI-KAI2.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-Dk9gyqZN.js.map → WorkflowsOverview-grI-KAI2.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CHwcrsaT.js → YamlWorkflowDetailPage-BoZ-hfBE.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-CHwcrsaT.js.map → YamlWorkflowDetailPage-BoZ-hfBE.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-B5jRX2Ph.js → YamlWorkflowsPage-Bf04rwRv.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-B5jRX2Ph.js.map → YamlWorkflowsPage-Bf04rwRv.js.map} +1 -1
- package/dashboard/dist/assets/{agents-Dd7E-27d.js → agents-Dphpdi8l.js} +2 -2
- package/dashboard/dist/assets/{agents-Dd7E-27d.js.map → agents-Dphpdi8l.js.map} +1 -1
- package/dashboard/dist/assets/{bots-CpaYjZTk.js → bots-DIFE298x.js} +2 -2
- package/dashboard/dist/assets/{bots-CpaYjZTk.js.map → bots-DIFE298x.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-CNjg_qv1.js → capabilities-D7Y7LClY.js} +2 -2
- package/dashboard/dist/assets/{capabilities-CNjg_qv1.js.map → capabilities-D7Y7LClY.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-DgmvnAtU.js → controlplane-D1TGcYsS.js} +2 -2
- package/dashboard/dist/assets/{controlplane-DgmvnAtU.js.map → controlplane-D1TGcYsS.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-Uj4U9g5H.js → escalation-columns-cwZE6xyo.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-Uj4U9g5H.js.map → escalation-columns-cwZE6xyo.js.map} +1 -1
- package/dashboard/dist/assets/{index-x5z73M9N.js → index-BzYcUPdD.js} +2 -2
- package/dashboard/dist/assets/{index-x5z73M9N.js.map → index-BzYcUPdD.js.map} +1 -1
- package/dashboard/dist/assets/index-CeCtqujG.js +67 -0
- package/dashboard/dist/assets/index-CeCtqujG.js.map +1 -0
- package/dashboard/dist/assets/{index-RIFT3RlO.js → index-ChV7pBjM.js} +2 -2
- package/dashboard/dist/assets/{index-RIFT3RlO.js.map → index-ChV7pBjM.js.map} +1 -1
- package/dashboard/dist/assets/{index-OfO0173w.js → index-CniRa65p.js} +2 -2
- package/dashboard/dist/assets/{index-OfO0173w.js.map → index-CniRa65p.js.map} +1 -1
- package/dashboard/dist/assets/{index-ByBy9M3L.js → index-CxdjT1Yk.js} +2 -2
- package/dashboard/dist/assets/{index-ByBy9M3L.js.map → index-CxdjT1Yk.js.map} +1 -1
- package/dashboard/dist/assets/{index-C_K7OlzT.js → index-DAvi5_2T.js} +2 -2
- package/dashboard/dist/assets/{index-C_K7OlzT.js.map → index-DAvi5_2T.js.map} +1 -1
- package/dashboard/dist/assets/{index-BrwqtXRh.js → index-DN-kX7hL.js} +2 -2
- package/dashboard/dist/assets/{index-BrwqtXRh.js.map → index-DN-kX7hL.js.map} +1 -1
- package/dashboard/dist/assets/index-Dl_8hbYI.js +2 -0
- package/dashboard/dist/assets/index-Dl_8hbYI.js.map +1 -0
- package/dashboard/dist/assets/{index-D4Dlznn9.js → index-DpKOr311.js} +2 -2
- package/dashboard/dist/assets/{index-D4Dlznn9.js.map → index-DpKOr311.js.map} +1 -1
- package/dashboard/dist/assets/{index-CnOvJz6U.js → index-Dy1wWu2e.js} +2 -2
- package/dashboard/dist/assets/{index-CnOvJz6U.js.map → index-Dy1wWu2e.js.map} +1 -1
- package/dashboard/dist/assets/{index-BT1Ms5Q5.js → index-HLu98Sqh.js} +2 -2
- package/dashboard/dist/assets/{index-BT1Ms5Q5.js.map → index-HLu98Sqh.js.map} +1 -1
- package/dashboard/dist/assets/{index-BFASQ7_X.js → index-ag2pCAPu.js} +2 -2
- package/dashboard/dist/assets/{index-BFASQ7_X.js.map → index-ag2pCAPu.js.map} +1 -1
- package/dashboard/dist/assets/{index-BsHVgWH6.js → index-gkxPN7Vx.js} +2 -2
- package/dashboard/dist/assets/{index-BsHVgWH6.js.map → index-gkxPN7Vx.js.map} +1 -1
- package/dashboard/dist/assets/index-m9A3UYst.css +1 -0
- package/dashboard/dist/assets/{knowledge-Cci_r6Kl.js → knowledge-DnMs-SOJ.js} +2 -2
- package/dashboard/dist/assets/{knowledge-Cci_r6Kl.js.map → knowledge-DnMs-SOJ.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-NY4iL0L-.js → mcp-CwW-K3r4.js} +2 -2
- package/dashboard/dist/assets/{mcp-NY4iL0L-.js.map → mcp-CwW-K3r4.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-CbvZo_I_.js → mcp-query-BZt5e8_Q.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-CbvZo_I_.js.map → mcp-query-BZt5e8_Q.js.map} +1 -1
- package/dashboard/dist/assets/{personas-Fy-f7QQd.js → personas-UA-Xn60f.js} +2 -2
- package/dashboard/dist/assets/{personas-Fy-f7QQd.js.map → personas-UA-Xn60f.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-DKiuozxV.js → pipelines-Cu3PARum.js} +2 -2
- package/dashboard/dist/assets/{pipelines-DKiuozxV.js.map → pipelines-Cu3PARum.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-CdM1Rkk9.js → tasks-BO6h8GJE.js} +2 -2
- package/dashboard/dist/assets/{tasks-CdM1Rkk9.js.map → tasks-BO6h8GJE.js.map} +1 -1
- package/dashboard/dist/assets/{topics-XNy2mf3n.js → topics-CWjCR-17.js} +2 -2
- package/dashboard/dist/assets/{topics-XNy2mf3n.js.map → topics-CWjCR-17.js.map} +1 -1
- package/dashboard/dist/assets/{useDebouncedValue-CyGcU3_S.js → useDebouncedValue-6t0jx1RB.js} +2 -2
- package/dashboard/dist/assets/{useDebouncedValue-CyGcU3_S.js.map → useDebouncedValue-6t0jx1RB.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-C2RIh_IN.js → useNamespace-CqpoQx6D.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-C2RIh_IN.js.map → useNamespace-CqpoQx6D.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-CDiV5fjf.js → useYamlActivityEvents-hmCQQwzB.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-CDiV5fjf.js.map → useYamlActivityEvents-hmCQQwzB.js.map} +1 -1
- package/dashboard/dist/assets/{validate-DcGWN82Y.js → validate-BIRn4a6d.js} +2 -2
- package/dashboard/dist/assets/{validate-DcGWN82Y.js.map → validate-BIRn4a6d.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-CH8UrouH.js → yaml-workflows-ziUdZhKY.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-CH8UrouH.js.map → yaml-workflows-ziUdZhKY.js.map} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/docs/agents.md +3 -1
- package/docs/api/http/agents.md +15 -1
- package/docs/api/http/escalations.md +3 -3
- package/docs/api/http/exports.md +3 -1
- package/docs/api/http/file-browser.md +3 -1
- package/docs/api/http/mcp-endpoint.md +5 -2
- package/docs/api/http/mcp-servers.md +34 -4
- package/docs/api/http/namespaces.md +2 -0
- package/docs/api/http/pipelines.md +2 -0
- package/docs/api/http/roles.md +11 -8
- package/docs/api/http/service-accounts.md +8 -2
- package/docs/api/http/settings.md +29 -2
- package/docs/api/http/tasks.md +3 -1
- package/docs/api/http/topics.md +7 -1
- package/docs/api/http/users.md +8 -2
- package/docs/api/http/workflows.md +25 -10
- package/docs/api/http/yaml-workflows.md +21 -0
- package/docs/api/mcp/admin.md +409 -49
- package/docs/api/mcp/file-storage.md +4 -0
- package/docs/api/mcp/http-fetch.md +6 -2
- package/docs/api/mcp/human-queue.md +53 -0
- package/docs/api/mcp/index.md +13 -10
- package/docs/api/mcp/knowledge.md +34 -0
- package/docs/api/mcp/oauth.md +1 -1
- package/docs/api/mcp/ortho.md +113 -0
- package/docs/api/mcp/vision.md +1 -1
- package/docs/api/sdk/bot-accounts.md +1 -0
- package/docs/api/sdk/escalations.md +2 -2
- package/docs/api/sdk/mcp.md +2 -2
- package/docs/api/sdk/settings.md +1 -1
- package/docs/api/sdk/tasks.md +2 -2
- package/docs/api/sdk/workflows.md +2 -2
- package/docs/api/sdk/yaml-workflows.md +1 -1
- package/docs/architecture.md +3 -1
- package/docs/auth.md +3 -2
- package/docs/cloud.md +7 -2
- package/docs/dashboard.md +16 -7
- package/docs/data.md +1 -1
- package/docs/escalation-strategies.md +9 -20
- package/docs/events.md +6 -0
- package/docs/iam.md +7 -5
- package/docs/mcp.md +159 -210
- package/docs/oauth-and-delegation.md +32 -21
- package/docs/topics.md +2 -0
- package/docs/workflows.md +14 -112
- package/package.json +5 -3
- package/dashboard/dist/assets/ConfirmDeleteModal-DW4DrPur.js +0 -2
- package/dashboard/dist/assets/ConfirmDeleteModal-DW4DrPur.js.map +0 -1
- package/dashboard/dist/assets/ProcessDetailPage-DNgCIwRl.js +0 -2
- package/dashboard/dist/assets/ResolverForm-CKIOAGtY.js +0 -8
- package/dashboard/dist/assets/ResolverForm-CKIOAGtY.js.map +0 -1
- package/dashboard/dist/assets/index-CtlBjl8o.js +0 -2
- package/dashboard/dist/assets/index-CtlBjl8o.js.map +0 -1
- package/dashboard/dist/assets/index-LRYC__mE.js +0 -67
- package/dashboard/dist/assets/index-LRYC__mE.js.map +0 -1
- package/dashboard/dist/assets/index-MYI2_Xb3.css +0 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dynamic client registration (RFC 7591) for public MCP clients: validation
|
|
4
|
+
* of the requested client metadata, and a per-address registration limit.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RegistrationLimiter = exports.REGISTRATION_ERRORS = void 0;
|
|
8
|
+
exports.hasControlCharacters = hasControlCharacters;
|
|
9
|
+
exports.displayableClientName = displayableClientName;
|
|
10
|
+
exports.registrationAddressKey = registrationAddressKey;
|
|
11
|
+
exports.isAllowedRedirectUri = isAllowedRedirectUri;
|
|
12
|
+
exports.validateClientMetadata = validateClientMetadata;
|
|
13
|
+
const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '[::1]']);
|
|
14
|
+
/** C0 controls, DEL and C1 controls: never part of a token, an id or a URI. */
|
|
15
|
+
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/;
|
|
16
|
+
/** Characters that reorder or hide text: a self-chosen client name must not use them to spoof another. */
|
|
17
|
+
const DISPLAY_SPOOFING = /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u202a-\u202e\u2060-\u2069\ufeff]/g;
|
|
18
|
+
/** Whether a value carries a control character. */
|
|
19
|
+
function hasControlCharacters(value) {
|
|
20
|
+
return CONTROL_CHARACTERS.test(value);
|
|
21
|
+
}
|
|
22
|
+
/** A client name as the consent page may show it: no controls, no direction overrides, trimmed. */
|
|
23
|
+
function displayableClientName(name) {
|
|
24
|
+
return name.replace(DISPLAY_SPOOFING, '').trim();
|
|
25
|
+
}
|
|
26
|
+
/** An IPv6 address as its eight groups, or null when it is not one. */
|
|
27
|
+
function ipv6Groups(ip) {
|
|
28
|
+
const halves = ip.split('::');
|
|
29
|
+
if (halves.length > 2)
|
|
30
|
+
return null;
|
|
31
|
+
const head = halves[0] ? halves[0].split(':') : [];
|
|
32
|
+
if (halves.length === 1)
|
|
33
|
+
return head.length === 8 ? head : null;
|
|
34
|
+
const tail = halves[1] ? halves[1].split(':') : [];
|
|
35
|
+
const missing = 8 - head.length - tail.length;
|
|
36
|
+
return missing < 0 ? null : [...head, ...Array(missing).fill('0'), ...tail];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The address a registration counts against: IPv4 as is, IPv6 by its /64,
|
|
40
|
+
* which one host or network usually holds in full.
|
|
41
|
+
*/
|
|
42
|
+
function registrationAddressKey(address) {
|
|
43
|
+
const ip = address.split('%')[0];
|
|
44
|
+
if (ip.startsWith('::ffff:') && ip.includes('.'))
|
|
45
|
+
return ip.slice(7);
|
|
46
|
+
if (!ip.includes(':'))
|
|
47
|
+
return ip;
|
|
48
|
+
const groups = ipv6Groups(ip);
|
|
49
|
+
if (!groups)
|
|
50
|
+
return ip;
|
|
51
|
+
return `${groups.slice(0, 4).map((g) => (g.replace(/^0+(?=.)/, '') || '0').toLowerCase()).join(':')}::/64`;
|
|
52
|
+
}
|
|
53
|
+
const MAX_REDIRECT_URIS = 10;
|
|
54
|
+
const MAX_URI_LENGTH = 2048;
|
|
55
|
+
const MAX_NAME_LENGTH = 200;
|
|
56
|
+
const GRANT_TYPES = new Set(['authorization_code', 'refresh_token']);
|
|
57
|
+
const RESPONSE_TYPES = new Set(['code']);
|
|
58
|
+
exports.REGISTRATION_ERRORS = {
|
|
59
|
+
INVALID_REDIRECT_URI: 'invalid_redirect_uri',
|
|
60
|
+
INVALID_CLIENT_METADATA: 'invalid_client_metadata',
|
|
61
|
+
};
|
|
62
|
+
function invalid(error, description) {
|
|
63
|
+
return { ok: false, error, description };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Loopback http or https on any port, or an https URI equal, in canonical
|
|
67
|
+
* form, to one the deployment allows. Matching whole URIs, not hosts, keeps a
|
|
68
|
+
* code from landing on any other path, query or port of an allowed host.
|
|
69
|
+
*/
|
|
70
|
+
function isAllowedRedirectUri(uri, allowedUris) {
|
|
71
|
+
if (uri.length > MAX_URI_LENGTH || hasControlCharacters(uri) || uri.trim() !== uri)
|
|
72
|
+
return false;
|
|
73
|
+
let url;
|
|
74
|
+
try {
|
|
75
|
+
url = new URL(uri);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (url.hash || url.username || url.password)
|
|
81
|
+
return false;
|
|
82
|
+
if (url.protocol === 'http:')
|
|
83
|
+
return LOOPBACK_HOSTS.has(url.hostname);
|
|
84
|
+
if (url.protocol !== 'https:')
|
|
85
|
+
return false;
|
|
86
|
+
if (LOOPBACK_HOSTS.has(url.hostname))
|
|
87
|
+
return true;
|
|
88
|
+
return allowedUris.includes(url.href);
|
|
89
|
+
}
|
|
90
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((v) => typeof v === 'string');
|
|
91
|
+
function validateClientMetadata(body, allowedUris) {
|
|
92
|
+
const input = (body ?? {});
|
|
93
|
+
const { redirect_uris, client_name, token_endpoint_auth_method, grant_types, response_types } = input;
|
|
94
|
+
if (!isStringArray(redirect_uris) || redirect_uris.length === 0 || redirect_uris.length > MAX_REDIRECT_URIS) {
|
|
95
|
+
return invalid(exports.REGISTRATION_ERRORS.INVALID_REDIRECT_URI, `redirect_uris must list 1 to ${MAX_REDIRECT_URIS} URIs`);
|
|
96
|
+
}
|
|
97
|
+
const refused = redirect_uris.find((uri) => !isAllowedRedirectUri(uri, allowedUris));
|
|
98
|
+
if (refused !== undefined) {
|
|
99
|
+
return invalid(exports.REGISTRATION_ERRORS.INVALID_REDIRECT_URI, `redirect URI not allowed: ${refused.slice(0, 200)}`);
|
|
100
|
+
}
|
|
101
|
+
if (client_name !== undefined && (typeof client_name !== 'string' || client_name.length > MAX_NAME_LENGTH)) {
|
|
102
|
+
return invalid(exports.REGISTRATION_ERRORS.INVALID_CLIENT_METADATA, `client_name must be a string of at most ${MAX_NAME_LENGTH} characters`);
|
|
103
|
+
}
|
|
104
|
+
const shownName = typeof client_name === 'string' ? displayableClientName(client_name) : undefined;
|
|
105
|
+
if (token_endpoint_auth_method !== undefined && token_endpoint_auth_method !== 'none') {
|
|
106
|
+
return invalid(exports.REGISTRATION_ERRORS.INVALID_CLIENT_METADATA, 'only public clients are supported: token_endpoint_auth_method must be none');
|
|
107
|
+
}
|
|
108
|
+
if (grant_types !== undefined && !(isStringArray(grant_types) && grant_types.every((g) => GRANT_TYPES.has(g)))) {
|
|
109
|
+
return invalid(exports.REGISTRATION_ERRORS.INVALID_CLIENT_METADATA, 'grant_types may include only authorization_code and refresh_token');
|
|
110
|
+
}
|
|
111
|
+
if (response_types !== undefined && !(isStringArray(response_types) && response_types.every((r) => RESPONSE_TYPES.has(r)))) {
|
|
112
|
+
return invalid(exports.REGISTRATION_ERRORS.INVALID_CLIENT_METADATA, 'response_types may include only code');
|
|
113
|
+
}
|
|
114
|
+
return { ok: true, clientName: shownName || undefined, redirectUris: redirect_uris };
|
|
115
|
+
}
|
|
116
|
+
/** A fixed-window count of registrations per address, in memory, per process. */
|
|
117
|
+
class RegistrationLimiter {
|
|
118
|
+
constructor(limit, windowMs) {
|
|
119
|
+
this.limit = limit;
|
|
120
|
+
this.windowMs = windowMs;
|
|
121
|
+
this.windows = new Map();
|
|
122
|
+
}
|
|
123
|
+
/** Count one registration from `address`; false when over the limit. */
|
|
124
|
+
allow(address, now = Date.now()) {
|
|
125
|
+
for (const [key, w] of this.windows)
|
|
126
|
+
if (now - w.start >= this.windowMs)
|
|
127
|
+
this.windows.delete(key);
|
|
128
|
+
const window = this.windows.get(address) ?? { start: now, count: 0 };
|
|
129
|
+
window.count += 1;
|
|
130
|
+
this.windows.set(address, window);
|
|
131
|
+
return window.count <= this.limit;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.RegistrationLimiter = RegistrationLimiter;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const INSERT_CLIENT = "\n INSERT INTO lt_oauth_clients (client_id, client_name, redirect_uris)\n VALUES ($1, $2, $3)\n RETURNING client_id, client_name, redirect_uris, created_at";
|
|
2
|
+
export declare const GET_CLIENT = "\n SELECT client_id, client_name, redirect_uris, created_at\n FROM lt_oauth_clients WHERE client_id = $1";
|
|
3
|
+
/** Create the grant and its authorization code together. */
|
|
4
|
+
export declare const ISSUE_CODE = "\n WITH g AS (\n INSERT INTO lt_oauth_grants (user_id, client_id, policy, scope)\n VALUES ($1, $2, $3, $4)\n RETURNING id\n )\n INSERT INTO lt_oauth_codes (code_hash, grant_id, redirect_uri, code_challenge, resource, expires_at)\n SELECT $5, g.id, $6, $7, $8, NOW() + make_interval(secs => $9) FROM g\n RETURNING grant_id";
|
|
5
|
+
/**
|
|
6
|
+
* Consume a code and issue the first refresh token. The code must be unused,
|
|
7
|
+
* unexpired, for this client and redirect URI, and match the PKCE challenge;
|
|
8
|
+
* the grant must be live and its owner active.
|
|
9
|
+
*/
|
|
10
|
+
export declare const EXCHANGE_CODE = "\n WITH c AS (\n UPDATE lt_oauth_codes c SET used_at = NOW()\n FROM lt_oauth_grants g\n WHERE c.code_hash = $1 AND c.used_at IS NULL AND c.expires_at > NOW()\n AND c.redirect_uri = $3 AND c.code_challenge = $4\n AND g.id = c.grant_id AND g.client_id = $2 AND g.revoked_at IS NULL\n RETURNING c.grant_id, c.resource\n ),\n g AS (\n SELECT g.* FROM lt_oauth_grants g\n JOIN c ON c.grant_id = g.id\n JOIN lt_users u ON u.id = g.user_id AND u.status = 'active'\n ),\n rt AS (\n INSERT INTO lt_oauth_refresh_tokens (token_hash, grant_id, expires_at)\n SELECT $5, g.id, NOW() + make_interval(secs => $6) FROM g\n RETURNING grant_id\n )\n SELECT g.id AS grant_id, g.user_id, g.client_id, g.policy, g.scope, c.resource, \n COALESCE((\n SELECT json_agg(json_build_object(\n 'role', r.role, 'type', r.type, 'read_scope', r.read_scope, 'write_scope', r.write_scope\n ) ORDER BY r.role)\n FROM lt_user_roles r WHERE r.user_id = g.user_id\n ), '[]'::json) AS roles\n FROM g JOIN rt ON rt.grant_id = g.id JOIN c ON c.grant_id = g.id";
|
|
11
|
+
/**
|
|
12
|
+
* The grant an access token names, as it stands now: only while the grant is
|
|
13
|
+
* unrevoked and its owner active, with the owner's current memberships. One
|
|
14
|
+
* primary-key lookup per /mcp request, so a revocation, a deactivation or a
|
|
15
|
+
* role change takes effect on the next request in every process.
|
|
16
|
+
*/
|
|
17
|
+
export declare const GET_LIVE_GRANT = "\n SELECT g.id AS grant_id, g.user_id, g.client_id, g.policy, g.scope, \n COALESCE((\n SELECT json_agg(json_build_object(\n 'role', r.role, 'type', r.type, 'read_scope', r.read_scope, 'write_scope', r.write_scope\n ) ORDER BY r.role)\n FROM lt_user_roles r WHERE r.user_id = g.user_id\n ), '[]'::json) AS roles\n FROM lt_oauth_grants g\n JOIN lt_users u ON u.id = g.user_id AND u.status = 'active'\n WHERE g.id = $1 AND g.user_id = $2 AND g.revoked_at IS NULL";
|
|
18
|
+
/**
|
|
19
|
+
* Rotate a refresh token: mark the old one used and issue its successor, only
|
|
20
|
+
* for the client that holds it and while the grant is live and its owner
|
|
21
|
+
* active. Returns current roles.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ROTATE_REFRESH_TOKEN = "\n WITH old AS (\n UPDATE lt_oauth_refresh_tokens t SET used_at = NOW()\n FROM lt_oauth_grants og\n WHERE t.token_hash = $1 AND t.used_at IS NULL AND t.expires_at > NOW()\n AND og.id = t.grant_id AND og.client_id = $4\n RETURNING t.grant_id\n ),\n g AS (\n SELECT g.* FROM lt_oauth_grants g\n JOIN old ON old.grant_id = g.id\n JOIN lt_users u ON u.id = g.user_id AND u.status = 'active'\n WHERE g.revoked_at IS NULL\n ),\n rt AS (\n INSERT INTO lt_oauth_refresh_tokens (token_hash, grant_id, expires_at)\n SELECT $2, g.id, NOW() + make_interval(secs => $3) FROM g\n RETURNING grant_id\n )\n SELECT g.id AS grant_id, g.user_id, g.client_id, g.policy, g.scope, \n COALESCE((\n SELECT json_agg(json_build_object(\n 'role', r.role, 'type', r.type, 'read_scope', r.read_scope, 'write_scope', r.write_scope\n ) ORDER BY r.role)\n FROM lt_user_roles r WHERE r.user_id = g.user_id\n ), '[]'::json) AS roles\n FROM g JOIN rt ON rt.grant_id = g.id";
|
|
24
|
+
/**
|
|
25
|
+
* Revoke the grant of a refresh token presented again after it was used,
|
|
26
|
+
* outside the retry grace window: the token has leaked.
|
|
27
|
+
*/
|
|
28
|
+
export declare const REVOKE_ON_REFRESH_REUSE = "\n UPDATE lt_oauth_grants g SET revoked_at = NOW()\n FROM lt_oauth_refresh_tokens t\n WHERE t.token_hash = $1 AND t.used_at IS NOT NULL\n AND t.used_at < NOW() - make_interval(secs => $2)\n AND g.id = t.grant_id AND g.revoked_at IS NULL\n RETURNING g.id AS grant_id, g.user_id";
|
|
29
|
+
/** Revoke the grant a refresh token belongs to, for the client presenting it. */
|
|
30
|
+
export declare const REVOKE_BY_REFRESH_TOKEN = "\n UPDATE lt_oauth_grants g SET revoked_at = NOW()\n FROM lt_oauth_refresh_tokens t\n WHERE t.token_hash = $1 AND g.id = t.grant_id AND g.client_id = $2 AND g.revoked_at IS NULL\n RETURNING g.id AS grant_id, g.user_id";
|
|
31
|
+
/** Revoke a grant by id, optionally only when it belongs to the given user. */
|
|
32
|
+
export declare const REVOKE_GRANT = "\n UPDATE lt_oauth_grants SET revoked_at = NOW()\n WHERE id = $1 AND ($2::uuid IS NULL OR user_id = $2) AND revoked_at IS NULL\n RETURNING id AS grant_id, user_id";
|
|
33
|
+
/** A person's live grants that a client has put to use, newest first. */
|
|
34
|
+
export declare const LIST_GRANTS = "\n SELECT g.id AS grant_id, g.client_id, c.client_name, g.policy, g.scope, g.created_at,\n (SELECT MAX(t.created_at) FROM lt_oauth_refresh_tokens t WHERE t.grant_id = g.id) AS last_used_at\n FROM lt_oauth_grants g\n JOIN lt_oauth_clients c ON c.client_id = g.client_id\n WHERE g.user_id = $1 AND g.revoked_at IS NULL\n AND EXISTS (SELECT 1 FROM lt_oauth_refresh_tokens t WHERE t.grant_id = g.id)\n ORDER BY g.created_at DESC";
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ── OAuth authorization server queries ─────────────────────────────────────
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.LIST_GRANTS = exports.REVOKE_GRANT = exports.REVOKE_BY_REFRESH_TOKEN = exports.REVOKE_ON_REFRESH_REUSE = exports.ROTATE_REFRESH_TOKEN = exports.GET_LIVE_GRANT = exports.EXCHANGE_CODE = exports.ISSUE_CODE = exports.GET_CLIENT = exports.INSERT_CLIENT = void 0;
|
|
5
|
+
/** The grant owner's current memberships, as a JSON array. Expects alias `g`. */
|
|
6
|
+
const GRANT_ROLES = `
|
|
7
|
+
COALESCE((
|
|
8
|
+
SELECT json_agg(json_build_object(
|
|
9
|
+
'role', r.role, 'type', r.type, 'read_scope', r.read_scope, 'write_scope', r.write_scope
|
|
10
|
+
) ORDER BY r.role)
|
|
11
|
+
FROM lt_user_roles r WHERE r.user_id = g.user_id
|
|
12
|
+
), '[]'::json)`;
|
|
13
|
+
exports.INSERT_CLIENT = `
|
|
14
|
+
INSERT INTO lt_oauth_clients (client_id, client_name, redirect_uris)
|
|
15
|
+
VALUES ($1, $2, $3)
|
|
16
|
+
RETURNING client_id, client_name, redirect_uris, created_at`;
|
|
17
|
+
exports.GET_CLIENT = `
|
|
18
|
+
SELECT client_id, client_name, redirect_uris, created_at
|
|
19
|
+
FROM lt_oauth_clients WHERE client_id = $1`;
|
|
20
|
+
/** Create the grant and its authorization code together. */
|
|
21
|
+
exports.ISSUE_CODE = `
|
|
22
|
+
WITH g AS (
|
|
23
|
+
INSERT INTO lt_oauth_grants (user_id, client_id, policy, scope)
|
|
24
|
+
VALUES ($1, $2, $3, $4)
|
|
25
|
+
RETURNING id
|
|
26
|
+
)
|
|
27
|
+
INSERT INTO lt_oauth_codes (code_hash, grant_id, redirect_uri, code_challenge, resource, expires_at)
|
|
28
|
+
SELECT $5, g.id, $6, $7, $8, NOW() + make_interval(secs => $9) FROM g
|
|
29
|
+
RETURNING grant_id`;
|
|
30
|
+
/**
|
|
31
|
+
* Consume a code and issue the first refresh token. The code must be unused,
|
|
32
|
+
* unexpired, for this client and redirect URI, and match the PKCE challenge;
|
|
33
|
+
* the grant must be live and its owner active.
|
|
34
|
+
*/
|
|
35
|
+
exports.EXCHANGE_CODE = `
|
|
36
|
+
WITH c AS (
|
|
37
|
+
UPDATE lt_oauth_codes c SET used_at = NOW()
|
|
38
|
+
FROM lt_oauth_grants g
|
|
39
|
+
WHERE c.code_hash = $1 AND c.used_at IS NULL AND c.expires_at > NOW()
|
|
40
|
+
AND c.redirect_uri = $3 AND c.code_challenge = $4
|
|
41
|
+
AND g.id = c.grant_id AND g.client_id = $2 AND g.revoked_at IS NULL
|
|
42
|
+
RETURNING c.grant_id, c.resource
|
|
43
|
+
),
|
|
44
|
+
g AS (
|
|
45
|
+
SELECT g.* FROM lt_oauth_grants g
|
|
46
|
+
JOIN c ON c.grant_id = g.id
|
|
47
|
+
JOIN lt_users u ON u.id = g.user_id AND u.status = 'active'
|
|
48
|
+
),
|
|
49
|
+
rt AS (
|
|
50
|
+
INSERT INTO lt_oauth_refresh_tokens (token_hash, grant_id, expires_at)
|
|
51
|
+
SELECT $5, g.id, NOW() + make_interval(secs => $6) FROM g
|
|
52
|
+
RETURNING grant_id
|
|
53
|
+
)
|
|
54
|
+
SELECT g.id AS grant_id, g.user_id, g.client_id, g.policy, g.scope, c.resource, ${GRANT_ROLES} AS roles
|
|
55
|
+
FROM g JOIN rt ON rt.grant_id = g.id JOIN c ON c.grant_id = g.id`;
|
|
56
|
+
/**
|
|
57
|
+
* The grant an access token names, as it stands now: only while the grant is
|
|
58
|
+
* unrevoked and its owner active, with the owner's current memberships. One
|
|
59
|
+
* primary-key lookup per /mcp request, so a revocation, a deactivation or a
|
|
60
|
+
* role change takes effect on the next request in every process.
|
|
61
|
+
*/
|
|
62
|
+
exports.GET_LIVE_GRANT = `
|
|
63
|
+
SELECT g.id AS grant_id, g.user_id, g.client_id, g.policy, g.scope, ${GRANT_ROLES} AS roles
|
|
64
|
+
FROM lt_oauth_grants g
|
|
65
|
+
JOIN lt_users u ON u.id = g.user_id AND u.status = 'active'
|
|
66
|
+
WHERE g.id = $1 AND g.user_id = $2 AND g.revoked_at IS NULL`;
|
|
67
|
+
/**
|
|
68
|
+
* Rotate a refresh token: mark the old one used and issue its successor, only
|
|
69
|
+
* for the client that holds it and while the grant is live and its owner
|
|
70
|
+
* active. Returns current roles.
|
|
71
|
+
*/
|
|
72
|
+
exports.ROTATE_REFRESH_TOKEN = `
|
|
73
|
+
WITH old AS (
|
|
74
|
+
UPDATE lt_oauth_refresh_tokens t SET used_at = NOW()
|
|
75
|
+
FROM lt_oauth_grants og
|
|
76
|
+
WHERE t.token_hash = $1 AND t.used_at IS NULL AND t.expires_at > NOW()
|
|
77
|
+
AND og.id = t.grant_id AND og.client_id = $4
|
|
78
|
+
RETURNING t.grant_id
|
|
79
|
+
),
|
|
80
|
+
g AS (
|
|
81
|
+
SELECT g.* FROM lt_oauth_grants g
|
|
82
|
+
JOIN old ON old.grant_id = g.id
|
|
83
|
+
JOIN lt_users u ON u.id = g.user_id AND u.status = 'active'
|
|
84
|
+
WHERE g.revoked_at IS NULL
|
|
85
|
+
),
|
|
86
|
+
rt AS (
|
|
87
|
+
INSERT INTO lt_oauth_refresh_tokens (token_hash, grant_id, expires_at)
|
|
88
|
+
SELECT $2, g.id, NOW() + make_interval(secs => $3) FROM g
|
|
89
|
+
RETURNING grant_id
|
|
90
|
+
)
|
|
91
|
+
SELECT g.id AS grant_id, g.user_id, g.client_id, g.policy, g.scope, ${GRANT_ROLES} AS roles
|
|
92
|
+
FROM g JOIN rt ON rt.grant_id = g.id`;
|
|
93
|
+
/**
|
|
94
|
+
* Revoke the grant of a refresh token presented again after it was used,
|
|
95
|
+
* outside the retry grace window: the token has leaked.
|
|
96
|
+
*/
|
|
97
|
+
exports.REVOKE_ON_REFRESH_REUSE = `
|
|
98
|
+
UPDATE lt_oauth_grants g SET revoked_at = NOW()
|
|
99
|
+
FROM lt_oauth_refresh_tokens t
|
|
100
|
+
WHERE t.token_hash = $1 AND t.used_at IS NOT NULL
|
|
101
|
+
AND t.used_at < NOW() - make_interval(secs => $2)
|
|
102
|
+
AND g.id = t.grant_id AND g.revoked_at IS NULL
|
|
103
|
+
RETURNING g.id AS grant_id, g.user_id`;
|
|
104
|
+
/** Revoke the grant a refresh token belongs to, for the client presenting it. */
|
|
105
|
+
exports.REVOKE_BY_REFRESH_TOKEN = `
|
|
106
|
+
UPDATE lt_oauth_grants g SET revoked_at = NOW()
|
|
107
|
+
FROM lt_oauth_refresh_tokens t
|
|
108
|
+
WHERE t.token_hash = $1 AND g.id = t.grant_id AND g.client_id = $2 AND g.revoked_at IS NULL
|
|
109
|
+
RETURNING g.id AS grant_id, g.user_id`;
|
|
110
|
+
/** Revoke a grant by id, optionally only when it belongs to the given user. */
|
|
111
|
+
exports.REVOKE_GRANT = `
|
|
112
|
+
UPDATE lt_oauth_grants SET revoked_at = NOW()
|
|
113
|
+
WHERE id = $1 AND ($2::uuid IS NULL OR user_id = $2) AND revoked_at IS NULL
|
|
114
|
+
RETURNING id AS grant_id, user_id`;
|
|
115
|
+
/** A person's live grants that a client has put to use, newest first. */
|
|
116
|
+
exports.LIST_GRANTS = `
|
|
117
|
+
SELECT g.id AS grant_id, g.client_id, c.client_name, g.policy, g.scope, g.created_at,
|
|
118
|
+
(SELECT MAX(t.created_at) FROM lt_oauth_refresh_tokens t WHERE t.grant_id = g.id) AS last_used_at
|
|
119
|
+
FROM lt_oauth_grants g
|
|
120
|
+
JOIN lt_oauth_clients c ON c.client_id = g.client_id
|
|
121
|
+
WHERE g.user_id = $1 AND g.revoked_at IS NULL
|
|
122
|
+
AND EXISTS (SELECT 1 FROM lt_oauth_refresh_tokens t WHERE t.grant_id = g.id)
|
|
123
|
+
ORDER BY g.created_at DESC`;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { LTGrantPolicy, LTGrantSnapshot, LTGrantSummary, LTOAuthClient } from '../../../types/oauth-server';
|
|
2
|
+
/** A refresh token reused within this many seconds is treated as a client retry. */
|
|
3
|
+
export declare const REFRESH_REUSE_GRACE_SECONDS = 10;
|
|
4
|
+
export interface RevokedGrant {
|
|
5
|
+
grant_id: string;
|
|
6
|
+
user_id: string;
|
|
7
|
+
}
|
|
8
|
+
/** A new random secret: 32 bytes, base64url. */
|
|
9
|
+
export declare function createSecret(): string;
|
|
10
|
+
/** SHA-256 of a secret. Codes and refresh tokens are stored only as hashes. */
|
|
11
|
+
export declare function hashSecret(secret: string): string;
|
|
12
|
+
/** The PKCE S256 challenge for a code verifier (RFC 7636). */
|
|
13
|
+
export declare function pkceChallenge(verifier: string): string;
|
|
14
|
+
export declare function registerClient(input: {
|
|
15
|
+
clientName?: string;
|
|
16
|
+
redirectUris: string[];
|
|
17
|
+
}): Promise<LTOAuthClient>;
|
|
18
|
+
export declare const isClientId: (value: string) => boolean;
|
|
19
|
+
export declare function getClient(clientId: string): Promise<LTOAuthClient | null>;
|
|
20
|
+
/** Record the person's consent as a grant and return a one-time code for it. */
|
|
21
|
+
export declare function issueAuthorizationCode(input: {
|
|
22
|
+
userId: string;
|
|
23
|
+
clientId: string;
|
|
24
|
+
policy: LTGrantPolicy;
|
|
25
|
+
scope: string;
|
|
26
|
+
redirectUri: string;
|
|
27
|
+
codeChallenge: string;
|
|
28
|
+
resource?: string;
|
|
29
|
+
ttlSeconds: number;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
code: string;
|
|
32
|
+
grantId: string;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Exchange a code for the grant and its first refresh token. Null when the
|
|
36
|
+
* code is unknown, used, expired, for another client or redirect URI, fails
|
|
37
|
+
* PKCE, or its grant or owner is no longer active.
|
|
38
|
+
*/
|
|
39
|
+
export declare function exchangeAuthorizationCode(input: {
|
|
40
|
+
code: string;
|
|
41
|
+
clientId: string;
|
|
42
|
+
redirectUri: string;
|
|
43
|
+
codeVerifier: string;
|
|
44
|
+
refreshTtlSeconds: number;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
snapshot: LTGrantSnapshot;
|
|
47
|
+
resource: string | null;
|
|
48
|
+
refreshToken: string;
|
|
49
|
+
} | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Rotate a refresh token for the client that holds it. Null when it is
|
|
52
|
+
* unknown, used, expired, another client's, or its grant or owner is no
|
|
53
|
+
* longer active. A used token presented again outside the
|
|
54
|
+
* grace window revokes its grant, and `revoked` reports it.
|
|
55
|
+
*/
|
|
56
|
+
export declare function rotateRefreshToken(input: {
|
|
57
|
+
refreshToken: string;
|
|
58
|
+
clientId: string;
|
|
59
|
+
refreshTtlSeconds: number;
|
|
60
|
+
}): Promise<{
|
|
61
|
+
snapshot: LTGrantSnapshot;
|
|
62
|
+
refreshToken: string;
|
|
63
|
+
} | {
|
|
64
|
+
snapshot: null;
|
|
65
|
+
revoked: RevokedGrant | null;
|
|
66
|
+
}>;
|
|
67
|
+
/** The live grant an access token names; null when it is revoked, its owner inactive, or it is another user's. */
|
|
68
|
+
export declare function getLiveGrant(grantId: string, userId: string): Promise<LTGrantSnapshot | null>;
|
|
69
|
+
/** Revoke the grant behind a refresh token presented by its own client (RFC 7009). */
|
|
70
|
+
export declare function revokeByRefreshToken(refreshToken: string, clientId: string): Promise<RevokedGrant | null>;
|
|
71
|
+
/** Revoke a grant; with `userId`, only when it belongs to that user. */
|
|
72
|
+
export declare function revokeGrant(grantId: string, userId?: string): Promise<RevokedGrant | null>;
|
|
73
|
+
/** The person's live grants that a client has put to use. */
|
|
74
|
+
export declare function listGrants(userId: string): Promise<LTGrantSummary[]>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isClientId = exports.REFRESH_REUSE_GRACE_SECONDS = void 0;
|
|
37
|
+
exports.createSecret = createSecret;
|
|
38
|
+
exports.hashSecret = hashSecret;
|
|
39
|
+
exports.pkceChallenge = pkceChallenge;
|
|
40
|
+
exports.registerClient = registerClient;
|
|
41
|
+
exports.getClient = getClient;
|
|
42
|
+
exports.issueAuthorizationCode = issueAuthorizationCode;
|
|
43
|
+
exports.exchangeAuthorizationCode = exchangeAuthorizationCode;
|
|
44
|
+
exports.rotateRefreshToken = rotateRefreshToken;
|
|
45
|
+
exports.getLiveGrant = getLiveGrant;
|
|
46
|
+
exports.revokeByRefreshToken = revokeByRefreshToken;
|
|
47
|
+
exports.revokeGrant = revokeGrant;
|
|
48
|
+
exports.listGrants = listGrants;
|
|
49
|
+
const crypto = __importStar(require("crypto"));
|
|
50
|
+
const db_1 = require("../../../lib/db");
|
|
51
|
+
const uuid_1 = require("../../../lib/uuid");
|
|
52
|
+
const sql_1 = require("./sql");
|
|
53
|
+
/** A refresh token reused within this many seconds is treated as a client retry. */
|
|
54
|
+
exports.REFRESH_REUSE_GRACE_SECONDS = 10;
|
|
55
|
+
/** A new random secret: 32 bytes, base64url. */
|
|
56
|
+
function createSecret() {
|
|
57
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
58
|
+
}
|
|
59
|
+
/** SHA-256 of a secret. Codes and refresh tokens are stored only as hashes. */
|
|
60
|
+
function hashSecret(secret) {
|
|
61
|
+
return crypto.createHash('sha256').update(secret).digest('hex');
|
|
62
|
+
}
|
|
63
|
+
/** The PKCE S256 challenge for a code verifier (RFC 7636). */
|
|
64
|
+
function pkceChallenge(verifier) {
|
|
65
|
+
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
66
|
+
}
|
|
67
|
+
async function registerClient(input) {
|
|
68
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.INSERT_CLIENT, [
|
|
69
|
+
`ltc_${createSecret()}`, input.clientName ?? null, input.redirectUris,
|
|
70
|
+
]);
|
|
71
|
+
return rows[0];
|
|
72
|
+
}
|
|
73
|
+
/** A client id as registerClient issues it; anything else names no client and never reaches SQL. */
|
|
74
|
+
const CLIENT_ID = /^ltc_[A-Za-z0-9_-]{43}$/;
|
|
75
|
+
const isClientId = (value) => CLIENT_ID.test(value);
|
|
76
|
+
exports.isClientId = isClientId;
|
|
77
|
+
async function getClient(clientId) {
|
|
78
|
+
if (!(0, exports.isClientId)(clientId))
|
|
79
|
+
return null;
|
|
80
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.GET_CLIENT, [clientId]);
|
|
81
|
+
return rows[0] ?? null;
|
|
82
|
+
}
|
|
83
|
+
/** Record the person's consent as a grant and return a one-time code for it. */
|
|
84
|
+
async function issueAuthorizationCode(input) {
|
|
85
|
+
if (!(0, uuid_1.isUuid)(input.userId))
|
|
86
|
+
throw new Error('issueAuthorizationCode: userId must be a UUID');
|
|
87
|
+
const code = createSecret();
|
|
88
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.ISSUE_CODE, [
|
|
89
|
+
input.userId, input.clientId, input.policy, input.scope,
|
|
90
|
+
hashSecret(code), input.redirectUri, input.codeChallenge, input.resource ?? null, input.ttlSeconds,
|
|
91
|
+
]);
|
|
92
|
+
return { code, grantId: rows[0].grant_id };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Exchange a code for the grant and its first refresh token. Null when the
|
|
96
|
+
* code is unknown, used, expired, for another client or redirect URI, fails
|
|
97
|
+
* PKCE, or its grant or owner is no longer active.
|
|
98
|
+
*/
|
|
99
|
+
async function exchangeAuthorizationCode(input) {
|
|
100
|
+
const refreshToken = createSecret();
|
|
101
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.EXCHANGE_CODE, [
|
|
102
|
+
hashSecret(input.code), input.clientId, input.redirectUri, pkceChallenge(input.codeVerifier),
|
|
103
|
+
hashSecret(refreshToken), input.refreshTtlSeconds,
|
|
104
|
+
]);
|
|
105
|
+
if (!rows[0])
|
|
106
|
+
return null;
|
|
107
|
+
const { resource, ...snapshot } = rows[0];
|
|
108
|
+
return { snapshot, resource, refreshToken };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Rotate a refresh token for the client that holds it. Null when it is
|
|
112
|
+
* unknown, used, expired, another client's, or its grant or owner is no
|
|
113
|
+
* longer active. A used token presented again outside the
|
|
114
|
+
* grace window revokes its grant, and `revoked` reports it.
|
|
115
|
+
*/
|
|
116
|
+
async function rotateRefreshToken(input) {
|
|
117
|
+
const pool = (0, db_1.getPool)();
|
|
118
|
+
const next = createSecret();
|
|
119
|
+
const oldHash = hashSecret(input.refreshToken);
|
|
120
|
+
const { rows } = await pool.query(sql_1.ROTATE_REFRESH_TOKEN, [oldHash, hashSecret(next), input.refreshTtlSeconds, input.clientId]);
|
|
121
|
+
if (rows[0])
|
|
122
|
+
return { snapshot: rows[0], refreshToken: next };
|
|
123
|
+
const reuse = await pool.query(sql_1.REVOKE_ON_REFRESH_REUSE, [oldHash, exports.REFRESH_REUSE_GRACE_SECONDS]);
|
|
124
|
+
return { snapshot: null, revoked: reuse.rows[0] ?? null };
|
|
125
|
+
}
|
|
126
|
+
/** The live grant an access token names; null when it is revoked, its owner inactive, or it is another user's. */
|
|
127
|
+
async function getLiveGrant(grantId, userId) {
|
|
128
|
+
if (!(0, uuid_1.isUuid)(grantId) || !(0, uuid_1.isUuid)(userId))
|
|
129
|
+
return null;
|
|
130
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.GET_LIVE_GRANT, [grantId, userId]);
|
|
131
|
+
return rows[0] ?? null;
|
|
132
|
+
}
|
|
133
|
+
/** Revoke the grant behind a refresh token presented by its own client (RFC 7009). */
|
|
134
|
+
async function revokeByRefreshToken(refreshToken, clientId) {
|
|
135
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.REVOKE_BY_REFRESH_TOKEN, [hashSecret(refreshToken), clientId]);
|
|
136
|
+
return rows[0] ?? null;
|
|
137
|
+
}
|
|
138
|
+
/** Revoke a grant; with `userId`, only when it belongs to that user. */
|
|
139
|
+
async function revokeGrant(grantId, userId) {
|
|
140
|
+
if (!(0, uuid_1.isUuid)(grantId) || (userId !== undefined && !(0, uuid_1.isUuid)(userId)))
|
|
141
|
+
return null;
|
|
142
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.REVOKE_GRANT, [grantId, userId ?? null]);
|
|
143
|
+
return rows[0] ?? null;
|
|
144
|
+
}
|
|
145
|
+
/** The person's live grants that a client has put to use. */
|
|
146
|
+
async function listGrants(userId) {
|
|
147
|
+
if (!(0, uuid_1.isUuid)(userId))
|
|
148
|
+
return [];
|
|
149
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.LIST_GRANTS, [userId]);
|
|
150
|
+
return rows;
|
|
151
|
+
}
|
|
@@ -3,6 +3,7 @@ export declare const GET_BOT_KEYS_BY_USER = "\n SELECT id, name, user_id, key_h
|
|
|
3
3
|
export declare const GET_ALL_ACTIVE_BOT_KEYS = "\n SELECT id, name, user_id, key_hash, scopes\n FROM lt_bot_api_keys\n WHERE (expires_at IS NULL OR expires_at > NOW())";
|
|
4
4
|
export declare const UPDATE_BOT_KEY_LAST_USED = "\n UPDATE lt_bot_api_keys SET last_used_at = NOW() WHERE id = $1";
|
|
5
5
|
export declare const DELETE_BOT_KEY = "\n DELETE FROM lt_bot_api_keys WHERE id = $1";
|
|
6
|
+
export declare const GET_BOT_KEY_OWNER = "\n SELECT user_id FROM lt_bot_api_keys WHERE id = $1";
|
|
6
7
|
export declare const LIST_BOT_KEYS_BY_USER = "\n SELECT id, name, user_id, scopes, expires_at, last_used_at, created_at, updated_at\n FROM lt_bot_api_keys WHERE user_id = $1 ORDER BY created_at";
|
|
7
8
|
export declare const INSERT_SERVICE_TOKEN = "\n INSERT INTO lt_service_tokens (name, token_hash, server_id, scopes, expires_at)\n VALUES ($1, $2, $3, $4, $5) RETURNING id";
|
|
8
9
|
export declare const GET_ALL_ACTIVE_SERVICE_TOKENS = "\n SELECT id, name, token_hash FROM lt_service_tokens\n WHERE (expires_at IS NULL OR expires_at > NOW())";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ── Bot API key queries ─────────────────────────────────────────────────────
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.LIST_SERVICE_TOKENS_BY_SERVER = exports.DELETE_SERVICE_TOKEN = exports.UPDATE_SERVICE_TOKEN_LAST_USED = exports.GET_ALL_ACTIVE_SERVICE_TOKENS = exports.INSERT_SERVICE_TOKEN = exports.LIST_BOT_KEYS_BY_USER = exports.DELETE_BOT_KEY = exports.UPDATE_BOT_KEY_LAST_USED = exports.GET_ALL_ACTIVE_BOT_KEYS = exports.GET_BOT_KEYS_BY_USER = exports.INSERT_BOT_KEY = void 0;
|
|
4
|
+
exports.LIST_SERVICE_TOKENS_BY_SERVER = exports.DELETE_SERVICE_TOKEN = exports.UPDATE_SERVICE_TOKEN_LAST_USED = exports.GET_ALL_ACTIVE_SERVICE_TOKENS = exports.INSERT_SERVICE_TOKEN = exports.LIST_BOT_KEYS_BY_USER = exports.GET_BOT_KEY_OWNER = exports.DELETE_BOT_KEY = exports.UPDATE_BOT_KEY_LAST_USED = exports.GET_ALL_ACTIVE_BOT_KEYS = exports.GET_BOT_KEYS_BY_USER = exports.INSERT_BOT_KEY = void 0;
|
|
5
5
|
exports.INSERT_BOT_KEY = `
|
|
6
6
|
INSERT INTO lt_bot_api_keys (name, user_id, key_hash, scopes, expires_at)
|
|
7
7
|
VALUES ($1, $2, $3, $4, $5) RETURNING id`;
|
|
@@ -18,6 +18,8 @@ exports.UPDATE_BOT_KEY_LAST_USED = `
|
|
|
18
18
|
UPDATE lt_bot_api_keys SET last_used_at = NOW() WHERE id = $1`;
|
|
19
19
|
exports.DELETE_BOT_KEY = `
|
|
20
20
|
DELETE FROM lt_bot_api_keys WHERE id = $1`;
|
|
21
|
+
exports.GET_BOT_KEY_OWNER = `
|
|
22
|
+
SELECT user_id FROM lt_bot_api_keys WHERE id = $1`;
|
|
21
23
|
exports.LIST_BOT_KEYS_BY_USER = `
|
|
22
24
|
SELECT id, name, user_id, scopes, expires_at, last_used_at, created_at, updated_at
|
|
23
25
|
FROM lt_bot_api_keys WHERE user_id = $1 ORDER BY created_at`;
|
|
@@ -278,8 +278,13 @@ async function listDistinctTypes() {
|
|
|
278
278
|
* and an out-of-scope role yields an empty result, not someone else's rows.
|
|
279
279
|
*/
|
|
280
280
|
function narrowRoleWithinScope(role, visibleRoles) {
|
|
281
|
-
if (!
|
|
282
|
-
return { role, roles:
|
|
281
|
+
if (!visibleRoles)
|
|
282
|
+
return { role, roles: undefined, empty: false };
|
|
283
|
+
// An empty scope sees nothing; the SDK would read an empty list as no filter.
|
|
284
|
+
if (visibleRoles.length === 0)
|
|
285
|
+
return { empty: true };
|
|
286
|
+
if (!role)
|
|
287
|
+
return { roles: visibleRoles, empty: false };
|
|
283
288
|
if (!visibleRoles.includes(role))
|
|
284
289
|
return { empty: true };
|
|
285
290
|
return { role, roles: undefined, empty: false };
|
|
@@ -48,6 +48,16 @@ export interface WorkflowEnvelopes {
|
|
|
48
48
|
* so, making this the natural polling body.
|
|
49
49
|
*/
|
|
50
50
|
export declare function getWorkflowEnvelopes(workflowId: string, taskQueue: string, workflowName: string): Promise<WorkflowEnvelopes>;
|
|
51
|
+
/**
|
|
52
|
+
* The identity a run was started with: its start envelope's `lt` block
|
|
53
|
+
* (`userId` is who it runs as; `initiatedBy` is the person who started it
|
|
54
|
+
* under `execute_as`). Read from the run's own input, so it holds for every
|
|
55
|
+
* registered or durable run. Null when the run cannot be found.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getWorkflowStartIdentity(workflowId: string): Promise<{
|
|
58
|
+
userId?: string;
|
|
59
|
+
initiatedBy?: string;
|
|
60
|
+
} | null>;
|
|
51
61
|
/**
|
|
52
62
|
* Return the current job state of a workflow.
|
|
53
63
|
* If the workflow is complete this is also the output.
|
|
@@ -4,11 +4,13 @@ exports.exportWorkflow = exportWorkflow;
|
|
|
4
4
|
exports.getWorkflowStatus = getWorkflowStatus;
|
|
5
5
|
exports.exportWorkflowExecution = exportWorkflowExecution;
|
|
6
6
|
exports.getWorkflowEnvelopes = getWorkflowEnvelopes;
|
|
7
|
+
exports.getWorkflowStartIdentity = getWorkflowStartIdentity;
|
|
7
8
|
exports.getWorkflowState = getWorkflowState;
|
|
8
9
|
exports.listJobs = listJobs;
|
|
9
10
|
const db_1 = require("../../lib/db");
|
|
10
11
|
const hotmesh_utils_1 = require("../hotmesh-utils");
|
|
11
12
|
const client_1 = require("./client");
|
|
13
|
+
const resolve_1 = require("../task/resolve");
|
|
12
14
|
const post_process_1 = require("./post-process");
|
|
13
15
|
const workflow_state_1 = require("../workflow-state");
|
|
14
16
|
/** Error thrown when a workflow job is not found (expired or never existed). */
|
|
@@ -123,6 +125,31 @@ async function getWorkflowEnvelopes(workflowId, taskQueue, workflowName) {
|
|
|
123
125
|
const output = await handle.output().catch(() => null);
|
|
124
126
|
return { workflow_id: workflowId, status: reading.state, input, output: output ?? null };
|
|
125
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* The identity a run was started with: its start envelope's `lt` block
|
|
130
|
+
* (`userId` is who it runs as; `initiatedBy` is the person who started it
|
|
131
|
+
* under `execute_as`). Read from the run's own input, so it holds for every
|
|
132
|
+
* registered or durable run. Null when the run cannot be found.
|
|
133
|
+
*/
|
|
134
|
+
async function getWorkflowStartIdentity(workflowId) {
|
|
135
|
+
let resolved;
|
|
136
|
+
try {
|
|
137
|
+
resolved = await (0, resolve_1.resolveWorkflowHandle)(workflowId);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const handle = await (0, client_1.getHandle)(resolved.taskQueue, resolved.workflowName, workflowId);
|
|
143
|
+
const args = await handle.input().catch(() => undefined);
|
|
144
|
+
const envelope = Array.isArray(args) && args.length >= 1 ? args[0] : null;
|
|
145
|
+
const lt = (envelope && typeof envelope === 'object' ? envelope.lt : null) ?? null;
|
|
146
|
+
if (!lt)
|
|
147
|
+
return {};
|
|
148
|
+
return {
|
|
149
|
+
...(typeof lt.userId === 'string' ? { userId: lt.userId } : {}),
|
|
150
|
+
...(typeof lt.initiatedBy === 'string' ? { initiatedBy: lt.initiatedBy } : {}),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
126
153
|
/**
|
|
127
154
|
* Return the current job state of a workflow.
|
|
128
155
|
* If the workflow is complete this is also the output.
|