@redocly/reef 0.140.0-next.1 → 0.140.0-next.13
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/CHANGELOG.md +419 -199
- package/dist/api-schemas/mcp-fetch-count.d.ts +2 -18
- package/dist/api-schemas/otel-traces.d.ts +7 -1345
- package/dist/api-schemas/otel-traces.js +1 -1
- package/dist/api-schemas/search-facets.d.ts +3 -31
- package/dist/api-schemas/search.d.ts +6 -52
- package/dist/cli/build/index.js +1 -1
- package/dist/cli/eject/resolveEjectParams.js +1 -1
- package/dist/cli/generate/get-schema-template.js +1 -1
- package/dist/client/app/hooks/markdown/useMarkdocRenderer.js +1 -1
- package/dist/client/app/hooks/markdown/useMarkdownText.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-code-sample.d.ts +2 -0
- package/dist/client/app/markdoc/custom-components/openapi/openapi-code-sample.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/use-markdown-adapter.d.ts +1 -33
- package/dist/client/app/useGlobalData.d.ts +1 -1
- package/dist/client/providers/post-message/use-post-message.d.ts +1 -1
- package/dist/client/templates/api-docs/helpers.js +3 -3
- package/dist/compiled/@redocly/pii/index.mjs +1 -0
- package/dist/compiled/@redocly/pii/package.json +1 -0
- package/dist/compiled/svgo/svgo-node.js +1 -1
- package/dist/constants/l10n/langs/ar.js +1 -1
- package/dist/constants/l10n/langs/de.js +1 -1
- package/dist/constants/l10n/langs/en.js +1 -1
- package/dist/constants/l10n/langs/es.js +1 -1
- package/dist/constants/l10n/langs/fr.js +1 -1
- package/dist/constants/l10n/langs/hi.js +1 -1
- package/dist/constants/l10n/langs/it.js +1 -1
- package/dist/constants/l10n/langs/ja.js +1 -1
- package/dist/constants/l10n/langs/ko.js +1 -1
- package/dist/constants/l10n/langs/pl.js +1 -1
- package/dist/constants/l10n/langs/pt-BR.js +1 -1
- package/dist/constants/l10n/langs/pt.js +1 -1
- package/dist/constants/l10n/langs/ru.js +1 -1
- package/dist/constants/l10n/langs/uk.js +1 -1
- package/dist/constants/l10n/langs/zh.js +1 -1
- package/dist/markdoc/attributes/definition-path.d.ts +1 -1
- package/dist/markdoc/attributes/json-example-ref.d.ts +1 -1
- package/dist/markdoc/attributes/json-schema-ref.d.ts +1 -1
- package/dist/markdoc/attributes/parsed-yaml.d.ts +1 -1
- package/dist/markdoc/attributes/raw-content.d.ts +1 -1
- package/dist/markdoc/attributes/sample-from-json-schema.d.ts +1 -1
- package/dist/markdoc/helpers/extract-rbac-from-condition-node.d.ts +1 -1
- package/dist/markdoc/helpers/get-inner-text.d.ts +1 -1
- package/dist/markdoc/helpers/get-node-attribute.d.ts +1 -1
- package/dist/markdoc/helpers/get-variable.d.ts +1 -1
- package/dist/markdoc/helpers/guards/is-conditional-node.d.ts +1 -1
- package/dist/markdoc/helpers/guards/is-function.d.ts +1 -1
- package/dist/markdoc/helpers/guards/is-node.d.ts +1 -1
- package/dist/markdoc/helpers/guards/is-prompt-node.d.ts +1 -1
- package/dist/markdoc/helpers/guards/is-tag.d.ts +1 -1
- package/dist/markdoc/helpers/guards/is-variable.d.ts +1 -1
- package/dist/markdoc/helpers/parse-partials.d.ts +1 -1
- package/dist/markdoc/helpers/parse-partials.js +1 -1
- package/dist/markdoc/helpers/remove-markdoc-tags.js +1 -1
- package/dist/markdoc/helpers/set-node-attribute-value.d.ts +1 -1
- package/dist/markdoc/nodes/fence/index.d.ts +1 -1
- package/dist/markdoc/nodes/fence/index.js +1 -1
- package/dist/markdoc/nodes/gfm-list-item.d.ts +1 -1
- package/dist/markdoc/nodes/gfm-list-item.js +1 -1
- package/dist/markdoc/nodes/heading.d.ts +1 -1
- package/dist/markdoc/nodes/heading.js +1 -1
- package/dist/markdoc/nodes/index.d.ts +6 -165
- package/dist/markdoc/nodes/index.js +1 -1
- package/dist/markdoc/nodes/table.d.ts +1 -1
- package/dist/markdoc/nodes/table.js +1 -1
- package/dist/markdoc/nodes/text.d.ts +1 -1
- package/dist/markdoc/tags/json-example.js +1 -1
- package/dist/markdoc/tags/openapi-code-sample.js +1 -1
- package/dist/server/api-routes/execute-api-route.js +1 -1
- package/dist/server/api-routes/run-api-routes-worker.js +1 -1
- package/dist/server/config/env-config.d.ts +26 -24
- package/dist/server/config/env-schema.d.ts +87 -326
- package/dist/server/config/env-schemas/api-urls.d.ts +2 -14
- package/dist/server/config/env-schemas/auth.d.ts +10 -32
- package/dist/server/config/env-schemas/catalog.d.ts +6 -9
- package/dist/server/config/env-schemas/database.d.ts +2 -8
- package/dist/server/config/env-schemas/environment-detection.d.ts +20 -18
- package/dist/server/config/env-schemas/feature-flags.d.ts +29 -33
- package/dist/server/config/env-schemas/feature-flags.js +1 -1
- package/dist/server/config/env-schemas/organization-project.d.ts +3 -21
- package/dist/server/config/env-schemas/scorecards.d.ts +2 -6
- package/dist/server/config/env-schemas/search.d.ts +9 -15
- package/dist/server/config/env-schemas/server-config.d.ts +6 -31
- package/dist/server/config/env-schemas/site.d.ts +1 -5
- package/dist/server/config/env-schemas/ssr.d.ts +7 -12
- package/dist/server/config/env-schemas/telemetry.d.ts +1 -7
- package/dist/server/constants/common.d.ts +3 -0
- package/dist/server/constants/common.js +1 -1
- package/dist/server/esbuild/plugins/dependency-resolver.js +1 -1
- package/dist/server/esbuild/plugins/styled-components-ssr.js +1 -1
- package/dist/server/esbuild/plugins/themes-resolver.js +3 -3
- package/dist/server/external-plugins/resolve-external-plugins.js +1 -1
- package/dist/server/node-bundle-entry.js +1 -1
- package/dist/server/plugins/analytics/amplitude/index.js +1 -1
- package/dist/server/plugins/analytics/ga/build-scripts.js +6 -6
- package/dist/server/plugins/api-docs/definition-access.d.ts +22 -0
- package/dist/server/plugins/api-docs/definition-access.js +1 -0
- package/dist/server/plugins/api-docs/index.js +1 -1
- package/dist/server/plugins/api-docs/loader.d.ts +1 -1
- package/dist/server/plugins/api-docs/options.js +1 -1
- package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +23 -23
- package/dist/server/plugins/api-docs/spec-download.api.js +1 -1
- package/dist/server/plugins/config-parser/index.js +1 -1
- package/dist/server/plugins/config-parser/loaders/utils/read-and-validate-config.js +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/code-walkthrough/filesets-resolver.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/helpers/load-openapi-bundle.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/index.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-code-snippet-from-file.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-diagram-from-file.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-html-href.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-html-source-attribute.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-image-gallery.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-image-src-set.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-image-src.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-json-example-ref.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-json-schema-ref.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-link.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-native-md-link.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-nav-links.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-open-api-ref.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-openapi-code-samples.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-openapi-code-samples.js +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-openapi-response-samples.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-parsed-yaml.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-raw-content.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-relative-or-cdn-icon.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-relative-path.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-sample-from-json-schema.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-svg-content.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-tag-raw-content.d.ts +1 -1
- package/dist/server/plugins/markdown/attribute-resolvers/resolve-tag-raw-content.js +2 -2
- package/dist/server/plugins/markdown/attribute-resolvers/utils.d.ts +1 -1
- package/dist/server/plugins/markdown/compiler.d.ts +1 -1
- package/dist/server/plugins/markdown/compiler.js +1 -1
- package/dist/server/plugins/markdown/errors.d.ts +1 -1
- package/dist/server/plugins/markdown/get-server-props.js +1 -1
- package/dist/server/plugins/markdown/html/normalize-html-headings.d.ts +1 -1
- package/dist/server/plugins/markdown/html/normalize-html-headings.js +1 -1
- package/dist/server/plugins/markdown/markdoc/functions/concat.d.ts +1 -1
- package/dist/server/plugins/markdown/markdoc/markdoc-options.d.ts +5 -165
- package/dist/server/plugins/markdown/markdoc/markdoc-options.js +1 -1
- package/dist/server/plugins/markdown/markdoc/partials.d.ts +1 -1
- package/dist/server/plugins/markdown/markdoc/plugins/headings.d.ts +1 -1
- package/dist/server/plugins/markdown/markdoc/plugins/utils.d.ts +1 -1
- package/dist/server/plugins/markdown/markdoc/reparse-example-fences.d.ts +1 -1
- package/dist/server/plugins/markdown/markdoc/reparse-example-fences.js +1 -1
- package/dist/server/plugins/markdown/runtime-transform.d.ts +2 -2
- package/dist/server/plugins/markdown/runtime-transform.js +1 -1
- package/dist/server/plugins/markdown/search/get-ai-search-documents.js +1 -1
- package/dist/server/plugins/markdown/search/nodes/section-node.d.ts +1 -1
- package/dist/server/plugins/markdown/search/search-resolver.js +1 -1
- package/dist/server/plugins/markdown/search/to-markdown.d.ts +1 -1
- package/dist/server/plugins/markdown/search/walk-sections.d.ts +1 -1
- package/dist/server/plugins/markdown/search/walk-sections.js +1 -1
- package/dist/server/plugins/markdown/utils/stringify-tag-children-objects.d.ts +1 -1
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.d.ts +1 -1
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +1 -3
- package/dist/server/plugins/mcp/docs-mcp/tools/graphql/get-graphql-type-list.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/graphql/list-graphql-apis.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/graphql/types.d.ts +4 -4
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/list-apis.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/types.d.ts +2 -2
- package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
- package/dist/server/plugins/mcp/handlers/errors.d.ts +0 -5
- package/dist/server/plugins/mcp/handlers/errors.js +1 -1
- package/dist/server/plugins/mcp/handlers/handle-mcp-request.js +1 -1
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -1
- package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +2 -2
- package/dist/server/plugins/mcp/types.d.ts +10 -0
- package/dist/server/plugins/mcp/utils/available-tools.d.ts +5 -0
- package/dist/server/plugins/mcp/utils/available-tools.js +1 -0
- package/dist/server/plugins/mcp/workers/worker-context.d.ts +4 -0
- package/dist/server/plugins/mcp/workers/worker-context.js +1 -0
- package/dist/server/plugins/scorecard-classic/compute-scorecard.d.ts +2 -2
- package/dist/server/plugins/scorecard-classic/compute-scorecard.js +1 -1
- package/dist/server/plugins/scorecard-classic/index.js +1 -1
- package/dist/server/plugins/scorecard-classic/shared-utils.js +1 -1
- package/dist/server/plugins/scorecard-classic/template/Grid/GridHeader.js +3 -3
- package/dist/server/plugins/scorecard-classic/template/views.js +1 -1
- package/dist/server/plugins/search/engines/typesense/index.js +1 -1
- package/dist/server/store-config.d.ts +33 -0
- package/dist/server/store-config.js +1 -0
- package/dist/server/store.d.ts +14 -16
- package/dist/server/store.js +1 -1
- package/dist/server/tools/notifiers/formatter.js +3 -3
- package/dist/server/tools/notifiers/helpers/colors.d.ts +1 -1
- package/dist/server/tools/notifiers/logger.d.ts +4 -0
- package/dist/server/tools/notifiers/reporter.js +1 -1
- package/dist/server/types/plugins/api-routes.d.ts +4 -1
- package/dist/server/types/plugins/common.d.ts +2 -1
- package/dist/server/utils/codeframes/codeframes.d.ts +1 -1
- package/dist/server/utils/codeframes/yaml-ast.d.ts +1 -1
- package/dist/server/utils/decode-request-path.d.ts +2 -0
- package/dist/server/utils/decode-request-path.js +1 -0
- package/dist/server/utils/envs/env-boolean-string.d.ts +9 -3
- package/dist/server/utils/envs/get-api-route-allowed-env-variables.d.ts +2 -0
- package/dist/server/utils/envs/get-api-route-allowed-env-variables.js +1 -1
- package/dist/server/utils/envs/interpolate-env-variables.d.ts +5 -1
- package/dist/server/utils/envs/interpolate-env-variables.js +1 -1
- package/dist/server/utils/envs/load-env-variables.js +1 -1
- package/dist/server/utils/envs/replace-env-variables-deep.d.ts +17 -9
- package/dist/server/utils/envs/replace-env-variables-deep.js +1 -1
- package/dist/server/utils/errors.d.ts +3 -0
- package/dist/server/utils/errors.js +1 -1
- package/dist/server/utils/globs.d.ts +12 -0
- package/dist/server/utils/globs.js +1 -1
- package/dist/server/utils/llmstxt/resolve-llms-txt-md.js +1 -1
- package/dist/server/utils/rbac-expand.js +1 -1
- package/dist/server/utils/rbac.d.ts +5 -1
- package/dist/server/utils/rbac.js +1 -1
- package/dist/server/utils/report-all-errors.js +1 -1
- package/dist/server/web-server/auth.d.ts +2 -0
- package/dist/server/web-server/auth.js +2 -2
- package/dist/server/web-server/middleware/jsonValidatorMiddleware.d.ts +5 -325
- package/dist/server/web-server/routes/auth.js +1 -1
- package/dist/server/web-server/routes/catalog/helpers/create-entity-schema.d.ts +1756 -1756
- package/dist/server/web-server/routes/catalog/helpers/create-entity-update-schema.d.ts +295 -295
- package/dist/server/web-server/routes/dynamic-route.js +1 -1
- package/dist/server/web-server/routes/error.js +1 -1
- package/dist/server/web-server/routes/helpers/process-redirects.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-consent-page.d.ts +2 -2
- package/dist/server/web-server/routes/mcp-routes/mcp-consent-page.js +18 -18
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
- package/dist/server/web-server/utils/ssrf-safe-lookup.d.ts +2 -1
- package/dist/server/web-server/utils/ssrf-safe-lookup.js +1 -1
- package/dist/server/workers/mcp-tool-worker-methods.d.ts +7 -0
- package/dist/server/workers/mcp-tool-worker-methods.js +1 -0
- package/dist/server/workers/mcp-tool-worker-pool.d.ts +0 -1
- package/dist/server/workers/mcp-tool-worker-pool.js +1 -1
- package/dist/server/workers/mcp-tool-worker.js +1 -1
- package/dist/server/workers/types.d.ts +4 -8
- package/dist/server/workers/worker-config.d.ts +33 -0
- package/dist/server/workers/worker-config.js +1 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/markdoc.d.ts +1 -1
- package/package.json +12 -12
- package/dist/server/plugins/markdown/html/html-jsx-attribute-mapping.d.ts +0 -6
- package/dist/server/plugins/markdown/html/html-jsx-attribute-mapping.js +0 -1
- package/dist/server/plugins/markdown/html/process-html-tokens.d.ts +0 -2
- package/dist/server/plugins/markdown/html/process-html-tokens.js +0 -1
- package/dist/server/plugins/markdown/source.d.ts +0 -4
- package/dist/server/plugins/markdown/source.js +0 -1
- package/dist/server/plugins/mcp/utils/available-sandbox-tools.d.ts +0 -4
- package/dist/server/plugins/mcp/utils/available-sandbox-tools.js +0 -1
- package/dist/server/tools/notifiers/helpers/privacy/mask-email.d.ts +0 -25
- package/dist/server/tools/notifiers/helpers/privacy/mask-email.js +0 -1
- package/dist/server/tools/notifiers/helpers/privacy/mask-subject.d.ts +0 -13
- package/dist/server/tools/notifiers/helpers/privacy/mask-subject.js +0 -1
- package/dist/server/tools/notifiers/helpers/privacy/remove-pii-from-url.d.ts +0 -25
- package/dist/server/tools/notifiers/helpers/privacy/remove-pii-from-url.js +0 -1
- package/dist/server/tools/notifiers/helpers/privacy/remove-pii.d.ts +0 -36
- package/dist/server/tools/notifiers/helpers/privacy/remove-pii.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import m from"path";import{realpath as
|
|
1
|
+
import m from"path";import{realpath as G}from"fs/promises";import{REDOCLY_ROUTE_RBAC as z,REDOCLY_TEAMS_RBAC as j}from"@redocly/config";import{combineUrls as V,withoutPathPrefix as ie,withPathPrefix as R}from"@redocly/theme/core/utils";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as W,CACHE_CONTROL_PUBLIC_HTML as ae,DEFAULT_IMMUTABLE_CACHE_MAX_AGE as se,DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as ce,PAGE_DATA_FOLDER as le,PUBLIC_API_DEFINITIONS_FOLDER as me,PUBLIC_ASSETS_FOLDER as ue,STATIC_DATA_FOLDER as de,X_REDOCLY_CACHE_CONTROL_HEADER as fe,X_REDOCLY_CACHE_CONTROL_PUBLIC_HTML as ge}from"../../constants/common.js";import{removeTrailingSlash as Z}from"../../../utils/url/remove-trailing-slash.js";import{findInIterable as pe}from"../../../utils/collection/find-in-iterable.js";import{sanitizeRedirectPathname as he}from"../../../utils/url/sanitize-redirect-pathname.js";import{envConfig as Re}from"../../config/env-config.js";import{addLeadingSlash as Ae}from"../../../utils/url/add-leading-slash.js";import{isPathInFolder as D}from"../../../utils/path/is-path-in-folder.js";import{getLlmsTxtMdPathBySlug as Ee}from"../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";import{resolveLlmsTxtMd as J}from"../../utils/llmstxt/resolve-llms-txt-md.js";import{removeLeadingSlash as I}from"../../../utils/url/remove-leading-slash.js";import{decodeRequestPath as Ce}from"../../utils/decode-request-path.js";import{AI_INDEX_EXPORT_FOLDER as _e,SEARCH_DATA_EXPORT_FOLDER as Le}from"../../constants/plugins/search.js";import{processRedirects as Te}from"./helpers/process-redirects.js";import{renderPage as Pe,getServerProps as Oe}from"../../ssr/index.js";import{canAccessAsset as we,canAccessResource as K}from"../../utils/rbac.js";import{handleErrorPageRender as A,handleUnauthorized as Q,handleUnauthorizedAsset as x}from"../utils.js";import{DEFAULT_MAX_AGE_FOR_MIME_TYPE as De,MIME_TYPES as Ie}from"../mime-types.js";import{fileExistsAsync as Se}from"../../utils/index.js";import{isAiAgentRequest as ye}from"../../utils/ai-agent-detection.js";import{getRedirectRoute as Ue}from"../utils/legacy-openapi-redirects.js";import{getContentTypeHeaderValue as ee}from"../utils/content-type.js";import{telemetry as be}from"../../../cli/telemetry/index.js";import{telemetry as Me}from"../../telemetry/index.js";const Fe=[Le,_e,de,I(me),I(ce),le];async function qe(e,E){try{const[C,u]=await Promise.all([G(e),G(E)]);return Fe.some(t=>D(C,m.join(u,t)))}catch{return!1}}function mt(e,E,C,u){return async t=>{const S=t.get("logger"),i=t.req,c=new URL(i.url),_=c.pathname.replace(/\/{2,}/g,"/"),y=Ce(_);if(y===void 0)return t.text("Bad Request",400);const d=Re.isProductionEnv?301:302;if(_!==c.pathname)return t.newResponse(null,d,{Location:_+c.search});const r=ie(y),U=m.parse(r).ext.toLowerCase()===".md",b=ye({accept:i.header("accept"),signatureAgent:i.header("signature-agent"),signature:i.header("signature"),signatureInput:i.header("signature-input"),userAgent:i.header("user-agent")}),a=Ae(Z(r)),o=(i.method==="GET"||i.method==="HEAD")&&!U?e.getRouteBySlug(a,{followRedirect:!1})||pe(e.routesBySlug?.values(),n=>n.hasClientRoutes&&(r===n.slug||r.startsWith(n.slug+"/"))):void 0,M=e.getRedirect(a);if(M){const n=Te({redirect:M,reqUrlSearch:c.search});return Me.sendRedirectMessage([{id:a,object:"redirect",uri:`urn:redocly:realm:api:redirect:${a}`,from:a,templateId:n.type.toString()}]),t.newResponse(null,n.type,{Location:n.location})}if(o?.metadata?.type==="openapi"){const n=Ue(c.pathname);if(n)return S.info("Legacy OpenAPI docs redirect from "+c.pathname),t.newResponse(null,d,{Location:encodeURI(n)});if(r.match(/[A-Z]/))return S.warn("Redirect to lowercase route to avoid 404 error"),t.newResponse(null,d,{Location:encodeURI(R(r.toLowerCase()))})}if(r.endsWith("/")&&r!=="/"){const n=he(new URL(a||"/",t.req.url).pathname);return t.newResponse(null,d,{Location:R((n==="/"?"/":Z(n))+c.search)})}const{isAuthenticated:f,teams:L,claims:{email:T}}=t.get("auth");if(o&&!K(o,{isAuthenticated:f,email:T,teams:L},e.config.access?.rbac,e.config.access?.requiresLogin))return f?A(t,e,{slug:o.slug,[j]:o[j],[z]:o[z]},403):Q(t,e,o.slug);const g=o&&b?Ee(o.slug):void 0,F=g?await J(g,e,u):void 0,P=F?.exists??!1;if(o&&(!b||!P)){const n=await E(o),N=await Oe(o,t,n,e),{html:ne,statusCode:X,error:oe}=await Pe(o,N,t,e,be),Y=e.config.access?.rbac,$=!e.config.access?.requiresLogin&&(!Y||!Object.keys(Y).length),k=X>=400||!!oe||!!e.compilationErrors?.length,re=$&&!k;let h;return k?h=W:$?h=ae:h="private",t.html(ne,X,{"Cache-Control":h,...re&&{[fe]:ge}})}const p=g&&P?g:r,te=I(p),s=m.resolve(e.outdir,te);if(!D(s,e.outdir))return x(t);const q=U?await J(r,e,u):void 0,l=q?.route;if(l&&!K(l,{isAuthenticated:f,email:T,teams:L},e.config.access?.rbac,e.config.access?.requiresLogin))return Q(t,e,r);if(!P&&!l){if(await qe(s,e.outdir))return A(t,e,{slug:a},404);const n=e.getGlobalConfig("access");if(!we(p,n?.rbac||{},n?.requiresLogin||!1,e.getGlobalConfig("directoryPaths"),{isAuthenticated:f,email:T,teams:L}))return D(p,ue)?x(t):A(t,e,{slug:a},404)}const O=Ie[m.extname(s)]||"text/plain",w=s.match(/assets\/.*\.[a-f0-9]{8,}\..+/)||s.match(/runtime\/chunks\/.*/)?se:De[O],H=w?{"Cache-Control":`public, max-age=${w}, immutable`,Expires:new Date(Date.now()+w*1e3).toUTCString()}:{"Cache-Control":W},B=i.query("download")!=null,v=F?.content??q?.content;if(v!==void 0)return t.newResponse(v,200,{"Content-Type":ee(O),"Access-Control-Allow-Origin":"*",...H,...l&&{"X-Robots-Tag":"noindex, follow",...e.config.seo?.siteUrl&&{Link:`<${V(e.config.seo.siteUrl,R(l.slug))}>; rel="canonical"`}},...B&&{"Content-Disposition":`attachment; filename="${m.basename(p)}"`}});if(await Se(s)){const n=await C(s);return t.newResponse(n,200,{"Content-Type":ee(O),"Access-Control-Allow-Origin":"*",...H,...l&&{"X-Robots-Tag":"noindex, follow",...e.config.seo?.siteUrl&&{Link:`<${V(e.config.seo.siteUrl,R(l.slug))}>; rel="canonical"`}},...B&&{"Content-Disposition":`attachment; filename="${m.basename(s)}"`}})}else return A(t,e,{slug:a},404)}}export{mt as dynamicRouteHandler};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{context as s,SpanStatusCode as n,trace as
|
|
1
|
+
import{context as s,SpanStatusCode as n,trace as o}from"@opentelemetry/api";import{HTTPException as a}from"hono/http-exception";import{envConfig as i}from"../../config/env-config.js";import{logger as r}from"../../tools/notifiers/logger.js";import{OidcDiscoveryError as f}from"../../utils/errors.js";const g=e=>{const t=o?.getSpan(s.active());return e instanceof Error&&"name"in e&&e.name==="URIError"?(t&&(t.setStatus({code:n.OK}),t.end()),new Response("Malformed URL",{status:400})):e instanceof SyntaxError?(t&&(t.setStatus({code:n.OK}),t.end()),new Response("Invalid JSON",{status:400})):e instanceof a&&e.status<500?(t&&(t.setStatus({code:n.OK}),t.end()),e.getResponse()):(t&&(t.setStatus({code:n.ERROR,message:e.message}),t.recordException(e),t.end()),e instanceof f?(r.error(e.message),new Response("Bad gateway: the identity provider returned an invalid response",{status:502})):i.isDevelopMode&&e?.name==="PageMissingDefaultExportError"?new Response(e.message,{status:200}):new Response("Internal Server Error",{status:500}))};export{g as errorHandler};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{removeTrailingSlash as
|
|
1
|
+
import{removeTrailingSlash as i}from"../../../../utils/url/remove-trailing-slash.js";import{isLocalLink as e}from"../../../../utils/path/is-local-link.js";import{withPathPrefix as r}from"@redocly/theme/core/utils";function m({redirect:o,reqUrlSearch:t=""}){const n=e(o.to)?r(o.to):o.to;return{location:a(i(n))+t,type:o?.type||301}}function a(o){return encodeURI(o).replace(/%25([0-9A-Fa-f]{2})/g,"%$1")}export{m as processRedirects};
|
|
@@ -15,10 +15,10 @@ export type ConsentPageProps = {
|
|
|
15
15
|
clientIdHost: string | undefined;
|
|
16
16
|
redirectUrl: URL;
|
|
17
17
|
scopes: readonly string[];
|
|
18
|
-
|
|
18
|
+
accessLifetimeDays: number;
|
|
19
19
|
rememberDays: number | undefined;
|
|
20
20
|
consentToken: string;
|
|
21
21
|
};
|
|
22
|
-
export declare function consentPage({ site, user, clientName, clientIdHost, redirectUrl, scopes,
|
|
22
|
+
export declare function consentPage({ site, user, clientName, clientIdHost, redirectUrl, scopes, accessLifetimeDays, rememberDays, consentToken, }: ConsentPageProps): string;
|
|
23
23
|
export declare function consentInvalidPage(site: PageSite): string;
|
|
24
24
|
//# sourceMappingURL=mcp-consent-page.d.ts.map
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import{ICONS as d,code as E,escapeHtml as a,htmlDocument as p,siteHeader as N}from"../../../plugins/mcp/handlers/credentials-page/layout.js";import{statusPage as L}from"../../../plugins/mcp/handlers/credentials-page/outcome-pages.js";const $=300,
|
|
2
|
-
<h2 id="${
|
|
1
|
+
import{ICONS as d,code as E,escapeHtml as a,htmlDocument as p,siteHeader as N}from"../../../plugins/mcp/handlers/credentials-page/layout.js";import{statusPage as L}from"../../../plugins/mcp/handlers/credentials-page/outcome-pages.js";const $=300,s={TITLE:"Allow MCP access",INTRO:"An MCP client asks to use this portal with your account.",WARNING:"Allow access only if you started this connection in your MCP client.",REDIRECT_LABEL:"Sends access to",REDIRECT_HINT:"The app receives access at this address.",USER_LABEL:"Signed in as",NOT_PROVIDED:"Not provided",CLIENT_LABEL:"App",CLIENT_UNKNOWN:"The app did not send a name. Continue only if you just started a connection yourself.",CLIENT_NAME_HINT:"The app chose this name itself. We do not check it.",CLIENT_ID_HOST:e=>`App details loaded from ${E(e)}`,PANEL_TITLE:"What the client gets",ACCOUNT_DETAILS:"Your account details, including your name and email address",ACT_AS_YOU:"Use of the MCP server of this portal as you",ACCESS_LIFETIME:e=>`Access that it can renew for up to ${e===1?"1 day":`${e} days`}`,REMEMBER:e=>`If you allow access, we do not ask again for this client for ${e} days.`,SCOPES_LABEL:"Scopes:",ALLOW:"Allow access",DENY:"Deny",INVALID_TITLE:"This request is no longer valid",INVALID_BODY:e=>`An access request works for ${e} minutes. This one has expired or can no longer be used.`,INVALID_NEXT:"Go back to your MCP client and start the connection again."},h={"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","X-Frame-Options":"DENY","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; img-src 'self'; frame-ancestors 'none'; base-uri 'none'"};function _({protocol:e,host:n,pathname:t,search:o}){return`${e}${n?`//${n}`:""}${t}${o}`}function r(e,n,t){return`<section aria-labelledby="${e}">
|
|
2
|
+
<h2 id="${e}">${a(n)}</h2>
|
|
3
3
|
${t.filter(Boolean).join(`
|
|
4
4
|
`)}
|
|
5
|
-
</section>`}function m(
|
|
6
|
-
<li>${
|
|
7
|
-
<li>${
|
|
8
|
-
<li>${
|
|
9
|
-
</ol>`,c=t?`<p>${
|
|
5
|
+
</section>`}function m(e,n){return r("consent-client",s.CLIENT_LABEL,[e?`<p>${a(e)}</p><p class="muted small">${s.CLIENT_NAME_HINT}</p>`:`<p>${s.CLIENT_UNKNOWN}</p>`,n?`<p class="muted small">${s.CLIENT_ID_HOST(n)}</p>`:""])}function A({scopes:e,accessLifetimeDays:n,rememberDays:t}){const o=`${d.shield}${s.PANEL_TITLE}`,i=`<ol class="steps">
|
|
6
|
+
<li>${s.ACCOUNT_DETAILS}</li>
|
|
7
|
+
<li>${s.ACT_AS_YOU}</li>
|
|
8
|
+
<li>${s.ACCESS_LIFETIME(n)}</li>
|
|
9
|
+
</ol>`,c=t?`<p>${s.REMEMBER(t)}</p>`:"";return`<aside class="panel" aria-labelledby="panel-title">
|
|
10
10
|
<div class="panel-steps"><h2 id="panel-title">${o}</h2>${i}</div>
|
|
11
11
|
<details class="panel-steps-toggle"><summary><h2>${o}</h2></summary>${i}</details>
|
|
12
12
|
<div class="link-facts">
|
|
13
13
|
${c}
|
|
14
|
-
<div class="tags"><span>${
|
|
14
|
+
<div class="tags"><span>${s.SCOPES_LABEL}</span>${e.map(l=>`<span class="tag">${a(l)}</span>`).join("")}</div>
|
|
15
15
|
</div>
|
|
16
|
-
</aside>`}function S({site:
|
|
16
|
+
</aside>`}function S({site:e,user:n,clientName:t,clientIdHost:o,redirectUrl:i,scopes:c,accessLifetimeDays:l,rememberDays:T,consentToken:u}){const I=`<div class="page">
|
|
17
17
|
<main class="card">
|
|
18
|
-
${N(
|
|
19
|
-
<h1>${
|
|
20
|
-
<p class="lede">${
|
|
21
|
-
<div class="banner warning" role="note">${d.warning}<p>${
|
|
18
|
+
${N(e)}
|
|
19
|
+
<h1>${s.TITLE}</h1>
|
|
20
|
+
<p class="lede">${s.INTRO}</p>
|
|
21
|
+
<div class="banner warning" role="note">${d.warning}<p>${s.WARNING}</p></div>
|
|
22
22
|
<div class="info-card">
|
|
23
23
|
${m(t,o)}
|
|
24
|
-
${r("consent-user",
|
|
25
|
-
${r("consent-redirect",
|
|
24
|
+
${r("consent-user",s.USER_LABEL,[`<p>${a(n||s.NOT_PROVIDED)}</p>`])}
|
|
25
|
+
${r("consent-redirect",s.REDIRECT_LABEL,[`<p>${E(_(i))}</p>`,`<p class="muted small">${s.REDIRECT_HINT}</p>`])}
|
|
26
26
|
</div>
|
|
27
27
|
<form class="credentials" method="post" autocomplete="off">
|
|
28
28
|
<input type="hidden" name="consent" value="${a(u)}">
|
|
29
29
|
<div class="actions">
|
|
30
|
-
<div class="actions-row"><button class="secondary" type="submit" name="decision" value="deny">${
|
|
30
|
+
<div class="actions-row"><button class="secondary" type="submit" name="decision" value="deny">${s.DENY}</button><button class="primary" type="submit" name="decision" value="allow">${s.ALLOW}</button></div>
|
|
31
31
|
</div>
|
|
32
32
|
</form>
|
|
33
33
|
</main>
|
|
34
|
-
${A({scopes:c,
|
|
35
|
-
</div>`;return p({title:
|
|
34
|
+
${A({scopes:c,accessLifetimeDays:l,rememberDays:T})}
|
|
35
|
+
</div>`;return p({title:s.TITLE,body:I},e.colorScheme)}function y(e){return p(L("warning",s.INVALID_TITLE,[s.INVALID_BODY($/60),s.INVALID_NEXT]),e.colorScheme)}export{h as CONSENT_PAGE_HEADERS,$ as MCP_CONSENT_TTL_SEC,y as consentInvalidPage,S as consentPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getCookie as H}from"hono/cookie";import{createHash as G,timingSafeEqual as oe}from"node:crypto";import{ulid as ie}from"ulid";import{isRedoclySso as se,isOidcProviderConfig as R,createMcpAuthorizationCode as ae,verifyMcpAuthorizationCode as de,createMcpRefreshToken as K,verifyMcpRefreshToken as ce,createMcpSessionResource as M,oidcRefreshTokenGrant as pe,getOidcMetadata as ue,getUserParamsFromCookies as le,idTokenIsForThisProject as _e}from"../../auth.js";import{isMcpContentProtected as P}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolveSiteBranding as fe}from"../../../plugins/mcp/handlers/credentials-page/site-branding.js";import{SUPPORTED_MCP_SCOPES as E}from"../../../plugins/mcp/constants.js";import{AUTH_URL as he,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as b,MCP_REFRESH_SESSION_LIFETIME_SEC as B}from"../../../constants/common.js";import{ServerRoutes as w}from"../../../../constants/api.js";import{AuthCookieNames as v}from"../../../../constants/common.js";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{telemetry as y}from"../../../telemetry/index.js";import{envConfig as q}from"../../../config/env-config.js";import{hasProjectDatabase as j}from"../../../providers/database/has-project-database.js";import*as C from"../../jwt/jwt.js";import{InternalTokenTypes as O,McpAccessTokenMarker as U}from"../../jwt/token-types.js";import{AlgorithmTypes as A,JwtAlgorithmNotImplemented as me,JwtTokenExpired as ge,JwtTokenInvalid as Ce,JwtTokenNotBefore as Te,JwtTokenSignatureMismatched as ke}from"../../jwt/types.js";import{getRequestOrigin as L}from"../../utils/get-request-origin.js";import{encryptClaimValue as J}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as Se}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as V}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as we,isClientMetadataDocumentUrl as z,isLoopbackRedirect as W,isRedirectUriRegistered as Ie}from"./mcp-client-metadata.js";import{CONSENT_PAGE_HEADERS as Y,MCP_CONSENT_TTL_SEC as Me,consentInvalidPage as Ee,consentPage as ye}from"./mcp-consent-page.js";import{MCP_CONSENT_MEMORY_DAYS as Ae,hasConsent as Re,redeemAuthorizationCode as Pe,redeemRefreshToken as ve,saveConsent as Oe}from"./mcp-oauth-store.js";const s=(r,e,t=200,n)=>r.json(e,t,{"Content-Type":"application/json",...n??{}}),$=new Set(["S256"]),F=["authorization_code","refresh_token"];function Ue(r,e,t){if(!r||!$.has(e||""))return!1;const n=G("sha256").update(t).digest("base64url"),i=Buffer.from(n),a=Buffer.from(r);return i.length!==a.length?!1:oe(i,a)}async function Le(r){const e=Math.floor(Date.now()/1e3);return C.sign({type:O.MCP_CONTEXT,...r,iat:e,exp:e+600},m,A.HS256)}async function Ne(r){await C.verify(r,m,A.HS256);const{payload:e}=C.decode(r);if(e.type!==O.MCP_CONTEXT)throw new Error("Invalid context token type");return e}function fr(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const t=L(e);return s(e,{resource:`${t}${I("/mcp")}`,authorization_servers:[t],bearer_methods_supported:["header"],resource_documentation:`${t}${w.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...E],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function hr(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const t=L(e);return s(e,{issuer:t,authorization_endpoint:`${t}${I(w.MCP_AUTHORIZATION)}`,token_endpoint:`${t}${I(w.MCP_TOKEN_PORTAL)}`,scopes_supported:[...E],registration_endpoint:`${t}${I(w.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...F],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function mr(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:q.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...F],response_types:["code"],scope:E.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(t){return s(e,{error:"invalid_request",error_description:t?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}const De=new Set(["http:","https:","ws:","wss:","ftp:","file:"]),He=new Set(["javascript:","data:","blob:","about:","vbscript:","intent:","chrome:","view-source:","ms-msdt:","ms-appx-web:"]),be=2048,qe=/[\s\p{Cc}]/u;function Z(r){if(!r||r.length>be||r.includes("#")||qe.test(r))return;const e=URL.parse(r);if(e){if(e.protocol==="https:"||W(e))return r.toLowerCase().startsWith(`${e.protocol}//`)?e:void 0;if(!(De.has(e.protocol)||He.has(e.protocol)))return e}}function X(r){const e=r||{},t=Object.keys(e),n=t.find(a=>R(e[a])&&se(e[a])),i=t.find(a=>R(e[a]));return n||i||t[0]}function gr(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const t=new URL(e.req.url),{searchParams:n}=t,i=n.get("redirect_uri"),a=n.get("client_id"),d=n.get("code_challenge"),o=n.get("code_challenge_method"),c=ie();if(!i||!Z(i))return s(e,{error:"invalid_request",error_description:"redirect_uri must be an https URL, a loopback http URL, or a private-use scheme"},400);if(!d)return s(e,{error:"invalid_request",error_description:"code_challenge is required"},400);if(!$.has(o||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...$].join(", ")}`},400);let p;if(z(a))try{const u=await we(a);if(p=u.client_name,!Ie(i,u.redirect_uris))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(u){const f=u instanceof Error?u.message:String(u);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${f}`},400)}y.sendMcpAuthorizationStartedMessage([{...M(c),redirect_uri:i||null}]);const l=L(e),k={isMcpFlow:!0,originalRedirectUri:i,mcpClientId:a,mcpState:n.get("state"),mcpSessionId:c,mcpCodeChallenge:d,mcpCodeChallengeMethod:o,mcpClientName:p,timestamp:Date.now()};try{const u=await Le(k),f=new URL(I(w.IDP_LOGIN),l);f.searchParams.set("redirectTo",`${w.MCP_CALLBACK}/${u}`);const h=X(r.getConfig().ssoDirect);return h?(f.searchParams.set("idpId",h),e.redirect(f.toString())):s(e,{error:"not_found"},404)}catch(u){const f=u instanceof Error?u.message:String(u),h=u instanceof Error?u.stack:String(u);y.sendMcpAuthorizationFailedMessage([{...M(c),error:f,error_details:h}]);const S=new URL(I(`${he}/oauth2/auth`));return S.search=n.toString(),e.redirect(S.toString())}}}async function Q(r){const e=Math.floor(Date.now()/1e3),t=r.ttlSec?e+r.ttlSec:Math.min(typeof r.claims.exp=="number"?r.claims.exp:1/0,e+b);return{accessToken:await C.sign({...r.claims,[U.CLAIM]:U.VALUE,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e}:{},exp:t},m,A.HS256,r.kid),expiresIn:Math.max(t-e,0)}}async function je(r,e,t){const n=T=>s(r,{error:"invalid_grant",error_description:T},400),i=r.get("logger"),a=t.get("refresh_token"),d=t.get("client_id")||void 0;if(!a)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let o;try{o=await ce(a)}catch(T){return i?.warn("MCP refresh token rejected: %s",T instanceof Error?T.message:String(T)),n("Invalid refresh token")}if(d&&d!==o.client_id)return n("Client mismatch");const c=o.idp_id?e.getConfig().ssoDirect?.[o.idp_id]:void 0;let p,l,k=o.idp_access_token_enc,u=o.idp_refresh_token_enc;if(o.idp_refresh_token_enc){let T;try{T=Se(o.idp_refresh_token_enc,m)}catch{return n("Invalid refresh token")}if(!o.idp_id||!R(c))return n("Unknown identity provider");let _;try{const g=await ue(o.idp_id,c);if(!g?.token_endpoint)return n("Identity provider has no token endpoint");_=await pe(g.token_endpoint,T,c)}catch(g){return i?.warn("MCP refresh failed to reach the IdP token endpoint: %s",g instanceof Error?g.message:String(g)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(_.error||!(_.id_token||_.access_token))return i?.warn("MCP refresh rejected by IdP: %s",_.error||"empty token response"),n("Refresh rejected by identity provider");if(_.refresh_token&&(u=V(_.refresh_token,m)),_.access_token&&(k=J(_.access_token,m)),_.id_token){try{const g=C.decode(_.id_token);p=g.payload,l=g.header.kid}catch{return n("Invalid token response from identity provider")}if(p.iss!==o.claims.iss||p.sub!==o.claims.sub)return i?.warn("MCP refresh rejected: the id_token names another issuer or subject"),n("Invalid refresh token")}}const f=Math.min(R(c)&&c.tokenExpirationTime||b,b),h=p??o.claims;if(!_e(h,c))return i?.warn("MCP refresh rejected: the id_token was issued for another project"),n("Invalid refresh token");if(j()&&!await ve(await e.getKv(),a,o.exp))return i?.warn("MCP refresh rejected: the refresh token was already used"),n("Refresh token already used");const S=await Q({claims:h,kid:l,idpId:o.idp_id,idpAccessTokenEnc:k,ttlSec:p?void 0:f}),D=await K({clientId:o.client_id,idpId:o.idp_id,claims:h,idpAccessTokenEnc:k,idpRefreshTokenEnc:u,sessionExp:o.session_exp});return s(r,{access_token:S.accessToken,token_type:"Bearer",expires_in:S.expiresIn,refresh_token:D,scope:E.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function Cr(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const t=await e.req.formData(),n=t.get("grant_type"),i=t.get("code"),a=t.get("redirect_uri")||void 0,d=t.get("code_verifier")||void 0;if(!F.includes(n))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(n==="refresh_token")return await je(e,r,t);if(!i)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const o=await de(i);if(a&&a!==o.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(!d)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!Ue(o.code_challenge,o.code_challenge_method,d))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400);if(q.OAUTH_CLIENT_ID&&o.client_id&&!z(o.client_id)&&o.client_id!==q.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const c=o.id_token;if(typeof c!="string"||c.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const p=await x(c,r.getConfig().ssoDirect);if(!p)return s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400);if(j()&&!await Pe(await r.getKv(),i,o.exp))return s(e,{error:"invalid_grant",error_description:"Authorization code already used"},400);const l=o.idp_access_token_enc,{accessToken:k,expiresIn:u}=await Q({claims:p.payload,kid:p.kid,idpId:o.idp_id,idpAccessTokenEnc:l}),h=Math.floor(Date.now()/1e3)+B,S=await K({clientId:o.client_id,idpId:o.idp_id,claims:p.payload,idpAccessTokenEnc:l,idpRefreshTokenEnc:o.idp_refresh_token_enc,sessionExp:h}),D={access_token:k,token_type:"Bearer",expires_in:u,refresh_token:S,scope:E.join(" ")};return s(e,D,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(o){return e.get("logger")?.warn("MCP token exchange failed: %s",o instanceof Error?o.message:String(o)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(t){const n=t instanceof Error?t.message:String(t);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:n},500)}}}async function x(r,e){const t=await le(e||{},{[v.AUTHORIZATION]:r});if(t.isAuthenticated!==!0)return;const{payload:n,header:i}=C.decode(r);if(n[U.CLAIM]!==U.VALUE)return{payload:n,kid:i.kid,idpId:typeof t.idpId=="string"?t.idpId:void 0}}function N(r){return G("sha256").update(r).digest("base64url")}async function ze(r){const e=Math.floor(Date.now()/1e3);return C.sign({type:O.MCP_CONSENT,session_hash:N(r.sessionToken),context_hash:N(r.contextToken),iat:e,exp:e+Me},m,A.HS256)}async function $e(r,e){try{await C.verify(r,m,A.HS256)}catch{return!1}const{payload:t}=C.decode(r);return t.type===O.MCP_CONSENT&&t.session_hash===N(e.sessionToken)&&t.context_hash===N(e.contextToken)}function ee(r,e){return async t=>{const n=new URL(t.req.url);let i=n.searchParams.get("context");if(!i&&n.pathname.startsWith(I(`${w.MCP_CALLBACK}/`))){const d=n.pathname.split("/");i=d[d.length-1]}if(!i)return t.text("Missing context parameter",400);let a=null;try{const d=await Ne(i);a=d.mcpSessionId||null;const o=d.originalRedirectUri,c=Z(o);if(!d.isMcpFlow||!o||!c)throw new Error("Invalid MCP context");const p=H(t,v.AUTHORIZATION),l=p?await x(p,r.getConfig().ssoDirect):void 0;return!p||!l?(y.sendMcpAuthorizationFailedMessage([{...M(a),error:"Authentication required",error_details:null}]),t.text("Authentication required",401)):await e(t,{contextToken:i,mcpContext:d,redirectUri:o,redirectUrl:c,sessionToken:p,session:l})}catch(d){const o=Ke(d);return y.sendMcpAuthorizationFailedMessage([{...M(a),...o}]),t.text(`Invalid MCP callback: ${o.error}`,400)}}}async function re(r,e,{mcpContext:t,redirectUri:n,redirectUrl:i,sessionToken:a,session:d}){const o=H(r,v.IDP_ACCESS_TOKEN),c=H(r,v.IDP_REFRESH_TOKEN),p=await ae({idToken:a,idpId:d.idpId??X(e.getConfig().ssoDirect),idpAccessTokenEnc:o?J(o,m):void 0,idpRefreshTokenEnc:c?V(c,m):void 0,clientId:t.mcpClientId||"",redirectUri:n,codeChallenge:t.mcpCodeChallenge||void 0,codeChallengeMethod:t.mcpCodeChallengeMethod||void 0,ttlSec:600}),l=new URL(i);return l.searchParams.set("code",p),t.mcpState&&l.searchParams.set("state",t.mcpState),y.sendMcpAuthorizationCompletedMessage([{...M(t.mcpSessionId||null),redirect_uri:n}]),r.redirect(l.toString())}function Fe(r,{mcpContext:e,redirectUrl:t}){const n=new URL(t);return n.searchParams.set("error","access_denied"),e.mcpState&&n.searchParams.set("state",e.mcpState),y.sendMcpAuthorizationFailedMessage([{...M(e.mcpSessionId||null),error:"access_denied",error_details:null}]),r.redirect(n.toString())}function Ge({payload:r}){return[r.email,r.name,r.sub].find(e=>typeof e=="string"&&e.length>0)}function te({mcpContext:r,redirectUri:e,session:t}){const{sub:n}=t.payload;if(!(!j()||typeof n!="string"||n.length===0))return{idpId:t.idpId,subject:n,clientId:r.mcpClientId||"",redirectUri:e}}function ne(r,e){const{seo:t,colorMode:n}=e.getConfig(),{logo:i}=e.getGlobalData();return{...fe({seo:t,logo:i,colorMode:n}),host:new URL(L(r)).host}}function Tr(r){return ee(r,async(e,t)=>{const n=te(t);if(W(t.redirectUrl)||n&&await Re(await r.getKv(),n))return re(e,r,t);const{mcpClientId:i,mcpClientName:a}=t.mcpContext,d=ye({site:ne(e,r),user:Ge(t.session),clientName:a||void 0,clientIdHost:z(i)?new URL(i).host:void 0,redirectUrl:t.redirectUrl,scopes:E,accessLifetimeHours:B/3600,rememberDays:n?Ae:void 0,consentToken:await ze(t)});return new Response(d,{status:200,headers:Y})})}function kr(r){return ee(r,async(e,t)=>{const n=await e.req.formData(),i=n.get("consent");if(typeof i!="string"||!await $e(i,t))return new Response(Ee(ne(e,r)),{status:403,headers:Y});if(n.get("decision")==="allow"){const a=te(t);return a&&await Oe(await r.getKv(),a),re(e,r,t)}return Fe(e,t)})}function Ke(r){return r instanceof Error&&[me,ge,Ce,Te,ke].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{F as SUPPORTED_MCP_GRANT_TYPES,Le as createMcpContextToken,gr as mcpAuthorizationHandler,Tr as mcpCallbackHandler,kr as mcpConsentHandler,mr as mcpDynamicClientRegistrationHandler,hr as mcpOAuthAuthorizationServerHandler,fr as mcpOAuthProtectedResourceHandler,Cr as mcpTokenPortalHandler,X as resolveMcpIdpId,Ne as verifyAndParseMcpContextToken,Ue as verifyPkce};
|
|
1
|
+
import{getCookie as v}from"hono/cookie";import{createHash as G,timingSafeEqual as se}from"node:crypto";import{ulid as ae}from"ulid";import{isRedoclySso as ce,isOidcProviderConfig as O,createMcpAuthorizationCode as de,verifyMcpAuthorizationCode as pe,createMcpRefreshToken as B,verifyMcpRefreshToken as ue,createMcpSessionResource as M,oidcRefreshTokenGrant as le,getOidcMetadata as _e,getUserParamsFromCookies as fe,idTokenIsForThisProject as he}from"../../auth.js";import{isMcpContentProtected as U}from"../../../plugins/mcp/auth/auth-handlers.js";import{resolveSiteBranding as me}from"../../../plugins/mcp/handlers/credentials-page/site-branding.js";import{SUPPORTED_MCP_SCOPES as E}from"../../../plugins/mcp/constants.js";import{AUTH_URL as ge,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as b,MCP_CLAIMS_ONLY_SESSION_LIFETIME_SEC as K,MCP_REFRESH_SESSION_LIFETIME_SEC as Ce}from"../../../constants/common.js";import{ServerRoutes as I}from"../../../../constants/api.js";import{AuthCookieNames as A}from"../../../../constants/common.js";import{withPathPrefix as w}from"@redocly/theme/core/utils";import{telemetry as y}from"../../../telemetry/index.js";import{envConfig as q}from"../../../config/env-config.js";import{hasProjectDatabase as j}from"../../../providers/database/has-project-database.js";import*as C from"../../jwt/jwt.js";import{InternalTokenTypes as L,McpAccessTokenMarker as N}from"../../jwt/token-types.js";import{AlgorithmTypes as R,JwtAlgorithmNotImplemented as Te,JwtTokenExpired as ke,JwtTokenInvalid as Se,JwtTokenNotBefore as Ie,JwtTokenSignatureMismatched as we}from"../../jwt/types.js";import{getRequestOrigin as D}from"../../utils/get-request-origin.js";import{encryptClaimValue as J}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as Me}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as V}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as Ee,isClientMetadataDocumentUrl as z,isLoopbackRedirect as W,isRedirectUriRegistered as ye}from"./mcp-client-metadata.js";import{CONSENT_PAGE_HEADERS as Y,MCP_CONSENT_TTL_SEC as Ae,consentInvalidPage as Re,consentPage as Pe}from"./mcp-consent-page.js";import{MCP_CONSENT_MEMORY_DAYS as ve,hasConsent as Oe,redeemAuthorizationCode as Ue,redeemRefreshToken as Le,saveConsent as Ne}from"./mcp-oauth-store.js";const s=(r,e,t=200,n)=>r.json(e,t,{"Content-Type":"application/json",...n??{}}),$=new Set(["S256"]),F=["authorization_code","refresh_token"];function De(r,e,t){if(!r||!$.has(e||""))return!1;const n=G("sha256").update(t).digest("base64url"),i=Buffer.from(n),a=Buffer.from(r);return i.length!==a.length?!1:se(i,a)}async function He(r){const e=Math.floor(Date.now()/1e3);return C.sign({type:L.MCP_CONTEXT,...r,iat:e,exp:e+600},m,R.HS256)}async function be(r){await C.verify(r,m,R.HS256);const{payload:e}=C.decode(r);if(e.type!==L.MCP_CONTEXT)throw new Error("Invalid context token type");return e}function gr(r){return async e=>{if(!U(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const t=D(e);return s(e,{resource:`${t}${w("/mcp")}`,authorization_servers:[t],bearer_methods_supported:["header"],resource_documentation:`${t}${I.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...E],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function Cr(r){return async e=>{if(!U(r))return s(e,{error:"not_found"},404);const t=D(e);return s(e,{issuer:t,authorization_endpoint:`${t}${w(I.MCP_AUTHORIZATION)}`,token_endpoint:`${t}${w(I.MCP_TOKEN_PORTAL)}`,scopes_supported:[...E],registration_endpoint:`${t}${w(I.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...F],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function Tr(r){return async e=>{if(!U(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:q.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...F],response_types:["code"],scope:E.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(t){return s(e,{error:"invalid_request",error_description:t?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}const qe=new Set(["http:","https:","ws:","wss:","ftp:","file:"]),je=new Set(["javascript:","data:","blob:","about:","vbscript:","intent:","chrome:","view-source:","ms-msdt:","ms-appx-web:"]),ze=2048,$e=/[\s\p{Cc}]/u;function Z(r){return r?Ce:K}function X(r){if(!r||r.length>ze||r.includes("#")||$e.test(r))return;const e=URL.parse(r);if(e){if(e.protocol==="https:"||W(e))return r.toLowerCase().startsWith(`${e.protocol}//`)?e:void 0;if(!(qe.has(e.protocol)||je.has(e.protocol)))return e}}function Q(r){const e=r||{},t=Object.keys(e),n=t.find(a=>O(e[a])&&ce(e[a])),i=t.find(a=>O(e[a]));return n||i||t[0]}function kr(r){return async e=>{if(!U(r))return s(e,{error:"not_found"},404);const t=new URL(e.req.url),{searchParams:n}=t,i=n.get("redirect_uri"),a=n.get("client_id"),c=n.get("code_challenge"),o=n.get("code_challenge_method"),p=ae();if(!i||!X(i))return s(e,{error:"invalid_request",error_description:"redirect_uri must be an https URL, a loopback http URL, or a private-use scheme"},400);if(!c)return s(e,{error:"invalid_request",error_description:"code_challenge is required"},400);if(!$.has(o||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...$].join(", ")}`},400);let d;if(z(a))try{const u=await Ee(a);if(d=u.client_name,!ye(i,u.redirect_uris))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(u){const h=u instanceof Error?u.message:String(u);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${h}`},400)}y.sendMcpAuthorizationStartedMessage([{...M(p),redirect_uri:i||null}]);const l=D(e),k={isMcpFlow:!0,originalRedirectUri:i,mcpClientId:a,mcpState:n.get("state"),mcpSessionId:p,mcpCodeChallenge:c,mcpCodeChallengeMethod:o,mcpClientName:d,timestamp:Date.now()};try{const u=await He(k),h=new URL(w(I.IDP_LOGIN),l);h.searchParams.set("redirectTo",`${I.MCP_CALLBACK}/${u}`);const _=Q(r.getConfig().ssoDirect);return _?(h.searchParams.set("idpId",_),e.redirect(h.toString())):s(e,{error:"not_found"},404)}catch(u){const h=u instanceof Error?u.message:String(u),_=u instanceof Error?u.stack:String(u);y.sendMcpAuthorizationFailedMessage([{...M(p),error:h,error_details:_}]);const S=new URL(w(`${ge}/oauth2/auth`));return S.search=n.toString(),e.redirect(S.toString())}}}async function x(r){const e=Math.floor(Date.now()/1e3),t=r.ttlSec?e+r.ttlSec:Math.min(typeof r.claims.exp=="number"?r.claims.exp:1/0,e+b);return{accessToken:await C.sign({...r.claims,[N.CLAIM]:N.VALUE,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e}:{},exp:t},m,R.HS256,r.kid),expiresIn:Math.max(t-e,0)}}async function Fe(r,e,t){const n=T=>s(r,{error:"invalid_grant",error_description:T},400),i=r.get("logger"),a=t.get("refresh_token"),c=t.get("client_id")||void 0;if(!a)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let o;try{o=await ue(a)}catch(T){return i?.warn("MCP refresh token rejected: %s",T instanceof Error?T.message:String(T)),n("Invalid refresh token")}if(c&&c!==o.client_id)return n("Client mismatch");const p=o.idp_id?e.getConfig().ssoDirect?.[o.idp_id]:void 0;let d,l,k=o.idp_access_token_enc,u=o.idp_refresh_token_enc;if(o.idp_refresh_token_enc){let T;try{T=Me(o.idp_refresh_token_enc,m)}catch{return n("Invalid refresh token")}if(!o.idp_id||!O(p))return n("Unknown identity provider");let f;try{const g=await _e(o.idp_id,p);if(!g?.token_endpoint)return n("Identity provider has no token endpoint");f=await le(g.token_endpoint,T,p)}catch(g){return i?.warn("MCP refresh failed to reach the IdP token endpoint: %s",g instanceof Error?g.message:String(g)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(f.error||!(f.id_token||f.access_token))return i?.warn("MCP refresh rejected by IdP: %s",f.error||"empty token response"),n("Refresh rejected by identity provider");if(f.refresh_token&&(u=V(f.refresh_token,m)),f.access_token&&(k=J(f.access_token,m)),f.id_token){try{const g=C.decode(f.id_token);d=g.payload,l=g.header.kid}catch{return n("Invalid token response from identity provider")}if(d.iss!==o.claims.iss||d.sub!==o.claims.sub)return i?.warn("MCP refresh rejected: the id_token names another issuer or subject"),n("Invalid refresh token")}}const h=Math.min(O(p)&&p.tokenExpirationTime||b,b),_=d??o.claims;if(!he(_,p))return i?.warn("MCP refresh rejected: the id_token was issued for another project"),n("Invalid refresh token");const S=d||typeof _.iat!="number"?o.session_exp:Math.min(o.session_exp,_.iat+K);if(S<=Math.floor(Date.now()/1e3))return i?.warn("MCP refresh rejected: the claims snapshot is older than the claims-only window"),n("Refresh token expired");if(j()&&!await Le(await e.getKv(),a,o.exp))return i?.warn("MCP refresh rejected: the refresh token was already used"),n("Refresh token already used");const P=await x({claims:_,kid:l,idpId:o.idp_id,idpAccessTokenEnc:k,ttlSec:d?void 0:h}),ie=await B({clientId:o.client_id,idpId:o.idp_id,claims:_,idpAccessTokenEnc:k,idpRefreshTokenEnc:u,sessionExp:S});return s(r,{access_token:P.accessToken,token_type:"Bearer",expires_in:P.expiresIn,refresh_token:ie,scope:E.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function Sr(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const t=await e.req.formData(),n=t.get("grant_type"),i=t.get("code"),a=t.get("redirect_uri")||void 0,c=t.get("code_verifier")||void 0;if(!F.includes(n))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(n==="refresh_token")return await Fe(e,r,t);if(!i)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const o=await pe(i);if(a&&a!==o.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(!c)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!De(o.code_challenge,o.code_challenge_method,c))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400);if(q.OAUTH_CLIENT_ID&&o.client_id&&!z(o.client_id)&&o.client_id!==q.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const p=o.id_token;if(typeof p!="string"||p.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const d=await ee(p,r.getConfig().ssoDirect);if(!d)return s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400);if(j()&&!await Ue(await r.getKv(),i,o.exp))return s(e,{error:"invalid_grant",error_description:"Authorization code already used"},400);const l=o.idp_access_token_enc,{accessToken:k,expiresIn:u}=await x({claims:d.payload,kid:d.kid,idpId:o.idp_id,idpAccessTokenEnc:l}),_=Math.floor(Date.now()/1e3)+Z(!!o.idp_refresh_token_enc),S=await B({clientId:o.client_id,idpId:o.idp_id,claims:d.payload,idpAccessTokenEnc:l,idpRefreshTokenEnc:o.idp_refresh_token_enc,sessionExp:_}),P={access_token:k,token_type:"Bearer",expires_in:u,refresh_token:S,scope:E.join(" ")};return s(e,P,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(o){return e.get("logger")?.warn("MCP token exchange failed: %s",o instanceof Error?o.message:String(o)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(t){const n=t instanceof Error?t.message:String(t);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:n},500)}}}async function ee(r,e){const t=await fe(e||{},{[A.AUTHORIZATION]:r});if(t.isAuthenticated!==!0)return;const{payload:n,header:i}=C.decode(r);if(n[N.CLAIM]!==N.VALUE)return{payload:n,kid:i.kid,idpId:typeof t.idpId=="string"?t.idpId:void 0}}function H(r){return G("sha256").update(r).digest("base64url")}async function Ge(r){const e=Math.floor(Date.now()/1e3);return C.sign({type:L.MCP_CONSENT,session_hash:H(r.sessionToken),context_hash:H(r.contextToken),iat:e,exp:e+Ae},m,R.HS256)}async function Be(r,e){try{await C.verify(r,m,R.HS256)}catch{return!1}const{payload:t}=C.decode(r);return t.type===L.MCP_CONSENT&&t.session_hash===H(e.sessionToken)&&t.context_hash===H(e.contextToken)}function re(r,e){return async t=>{const n=new URL(t.req.url);let i=n.searchParams.get("context");if(!i&&n.pathname.startsWith(w(`${I.MCP_CALLBACK}/`))){const c=n.pathname.split("/");i=c[c.length-1]}if(!i)return t.text("Missing context parameter",400);let a=null;try{const c=await be(i);a=c.mcpSessionId||null;const o=c.originalRedirectUri,p=X(o);if(!c.isMcpFlow||!o||!p)throw new Error("Invalid MCP context");const d=v(t,A.AUTHORIZATION),l=d?await ee(d,r.getConfig().ssoDirect):void 0;return!d||!l?(y.sendMcpAuthorizationFailedMessage([{...M(a),error:"Authentication required",error_details:null}]),t.text("Authentication required",401)):await e(t,{contextToken:i,mcpContext:c,redirectUri:o,redirectUrl:p,sessionToken:d,session:l})}catch(c){const o=Ve(c);return y.sendMcpAuthorizationFailedMessage([{...M(a),...o}]),t.text(`Invalid MCP callback: ${o.error}`,400)}}}async function te(r,e,{mcpContext:t,redirectUri:n,redirectUrl:i,sessionToken:a,session:c}){const o=v(r,A.IDP_ACCESS_TOKEN),p=v(r,A.IDP_REFRESH_TOKEN),d=await de({idToken:a,idpId:c.idpId??Q(e.getConfig().ssoDirect),idpAccessTokenEnc:o?J(o,m):void 0,idpRefreshTokenEnc:p?V(p,m):void 0,clientId:t.mcpClientId||"",redirectUri:n,codeChallenge:t.mcpCodeChallenge||void 0,codeChallengeMethod:t.mcpCodeChallengeMethod||void 0,ttlSec:600}),l=new URL(i);return l.searchParams.set("code",d),t.mcpState&&l.searchParams.set("state",t.mcpState),y.sendMcpAuthorizationCompletedMessage([{...M(t.mcpSessionId||null),redirect_uri:n}]),r.redirect(l.toString())}function Ke(r,{mcpContext:e,redirectUrl:t}){const n=new URL(t);return n.searchParams.set("error","access_denied"),e.mcpState&&n.searchParams.set("state",e.mcpState),y.sendMcpAuthorizationFailedMessage([{...M(e.mcpSessionId||null),error:"access_denied",error_details:null}]),r.redirect(n.toString())}function Je({payload:r}){return[r.email,r.name,r.sub].find(e=>typeof e=="string"&&e.length>0)}function ne({mcpContext:r,redirectUri:e,session:t}){const{sub:n}=t.payload;if(!(!j()||typeof n!="string"||n.length===0))return{idpId:t.idpId,subject:n,clientId:r.mcpClientId||"",redirectUri:e}}function oe(r,e){const{seo:t,colorMode:n}=e.getConfig(),{logo:i}=e.getGlobalData();return{...me({seo:t,logo:i,colorMode:n}),host:new URL(D(r)).host}}function Ir(r){return re(r,async(e,t)=>{const n=ne(t);if(W(t.redirectUrl)||n&&await Oe(await r.getKv(),n))return te(e,r,t);const{mcpClientId:i,mcpClientName:a}=t.mcpContext,c=Pe({site:oe(e,r),user:Je(t.session),clientName:a||void 0,clientIdHost:z(i)?new URL(i).host:void 0,redirectUrl:t.redirectUrl,scopes:E,accessLifetimeDays:Z(!!v(e,A.IDP_REFRESH_TOKEN))/(1440*60),rememberDays:n?ve:void 0,consentToken:await Ge(t)});return new Response(c,{status:200,headers:Y})})}function wr(r){return re(r,async(e,t)=>{const n=await e.req.formData(),i=n.get("consent");if(typeof i!="string"||!await Be(i,t))return new Response(Re(oe(e,r)),{status:403,headers:Y});if(n.get("decision")==="allow"){const a=ne(t);return a&&await Ne(await r.getKv(),a),te(e,r,t)}return Ke(e,t)})}function Ve(r){return r instanceof Error&&[Te,ke,Se,Ie,we].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{F as SUPPORTED_MCP_GRANT_TYPES,He as createMcpContextToken,kr as mcpAuthorizationHandler,Ir as mcpCallbackHandler,wr as mcpConsentHandler,Tr as mcpDynamicClientRegistrationHandler,Cr as mcpOAuthAuthorizationServerHandler,gr as mcpOAuthProtectedResourceHandler,Sr as mcpTokenPortalHandler,Q as resolveMcpIdpId,be as verifyAndParseMcpContextToken,De as verifyPkce};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type LookupFunction } from 'node:net';
|
|
2
2
|
/**
|
|
3
3
|
* Returns a drop-in replacement for `dns.lookup` that errors with
|
|
4
|
-
* {@link SsrfBlockedError} if any resolved address is in the blocklist.
|
|
4
|
+
* {@link SsrfBlockedError} if any resolved address is in the blocklist. The error message names
|
|
5
|
+
* the hostname only, never a resolved address, so callers may show it to the client.
|
|
5
6
|
*
|
|
6
7
|
* Install on an http/https agent (`connect.lookup` for undici, or `lookup` for a node:https Agent)
|
|
7
8
|
* so that every socket connection — including those made while following redirects — is re-validated
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{lookup as
|
|
1
|
+
import{lookup as m}from"node:dns";import{isIP as w}from"node:net";import{isBlockedIp as H,isLoopback as $,SsrfBlockedError as i}from"./ssrf-blocklist.js";function B({allowLoopback:l=!1}={}){const n=r=>H(r)&&!(l&&$(r));return(r,d,o)=>{const f=d,s=t=>{if(f.all){o(null,t);return}const[e]=t;if(!e){o(new i(`Hostname "${r}" did not resolve`),"",0);return}o(null,e.address,e.family)},u=w(r);if(u!==0){if(n(r)){o(new i(`Hostname "${r}" is a blocked address`),"",0);return}s([{address:r,family:u}]);return}m(r,{...f,all:!0},(t,e)=>{if(t){o(t,"",0);return}if(e.some(({address:p})=>n(p))){o(new i(`Hostname "${r}" resolves to a blocked address`),"",0);return}if(!e.length){o(new i(`Hostname "${r}" did not resolve`),"",0);return}s(e)})}}export{B as createSsrfSafeLookup};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WorkerParams } from './types.js';
|
|
2
|
+
import { MCP_TOOL_AVAILABILITY_KEY, MCP_TOOL_WORKER_KEY } from './mcp-tool-worker-pool.js';
|
|
3
|
+
export declare const mcpToolWorkerMethods: {
|
|
4
|
+
executeMcpTool: (params: WorkerParams<typeof MCP_TOOL_WORKER_KEY>[0]) => Promise<import("../plugins/mcp/types.js").McpToolWorkerResponse>;
|
|
5
|
+
mcpToolAvailability: (params: WorkerParams<typeof MCP_TOOL_AVAILABILITY_KEY>[0]) => Promise<Record<string, boolean>>;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=mcp-tool-worker-methods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{executeMcpTool as e,mcpToolAvailability as r}from"../plugins/mcp/workers/execute-mcp-tool.js";import{fromWorkerContext as t}from"../plugins/mcp/workers/worker-context.js";import{MCP_TOOL_AVAILABILITY_KEY as c,MCP_TOOL_WORKER_KEY as m}from"./mcp-tool-worker-pool.js";const T={[m]:o=>e({...o,context:t(o.context)}),[c]:o=>r({...o,context:t(o.context)})};export{T as mcpToolWorkerMethods};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { WorkerPool } from './worker-pool.js';
|
|
2
2
|
export declare const MCP_TOOL_WORKER_KEY = "executeMcpTool";
|
|
3
|
-
export declare const MCP_TOOL_IS_AVAILABLE_KEY = "isMcpToolAvailable";
|
|
4
3
|
export declare const MCP_TOOL_AVAILABILITY_KEY = "mcpToolAvailability";
|
|
5
4
|
export declare const mcpToolWorkers: WorkerPool;
|
|
6
5
|
//# sourceMappingURL=mcp-tool-worker-pool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getAllowedEnvs as o}from"../utils/envs/get-api-route-allowed-env-variables.js";import{WorkerPool as r}from"./worker-pool.js";const p="executeMcpTool",
|
|
1
|
+
import{getAllowedEnvs as o}from"../utils/envs/get-api-route-allowed-env-variables.js";import{WorkerPool as r}from"./worker-pool.js";const p="executeMcpTool",c="mcpToolAvailability",e=5,l=new r({workerScript:"./mcp-tool-worker",minWorkers:1,maxWorkers:e,workerType:"process",forkOpts:{stdio:"inherit"},resolveForkEnv:o,lazy:!0,sandbox:!0});export{c as MCP_TOOL_AVAILABILITY_KEY,p as MCP_TOOL_WORKER_KEY,l as mcpToolWorkers};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"workerpool";import{
|
|
1
|
+
import o from"workerpool";import{mcpToolWorkerMethods as r}from"./mcp-tool-worker-methods.js";o.worker(r);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ApiRoutesWorkerParams, ApiRoutesWorkerResponse } from '../types/plugins/api-routes.js';
|
|
2
2
|
import type { RenderPayload } from '../../types/ssr.js';
|
|
3
3
|
import type { ScorecardsWorkerParams, ScorecardsWorkerResponse } from '../types/plugins/scorecards.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { McpToolWorkerContext, McpToolWorkerParams, McpToolWorkerResponse } from '../plugins/mcp/types.js';
|
|
5
5
|
import type { SCORECARDS_WORKER_KEY } from './scorecards-worker-pool.js';
|
|
6
6
|
import type { API_ROUTES_WORKER_KEY } from './api-routes-worker-pool.js';
|
|
7
7
|
import type { SSR_WORKER_KEY } from './ssr-worker-pool.js';
|
|
8
|
-
import type { MCP_TOOL_AVAILABILITY_KEY,
|
|
8
|
+
import type { MCP_TOOL_AVAILABILITY_KEY, MCP_TOOL_WORKER_KEY } from './mcp-tool-worker-pool.js';
|
|
9
9
|
export type SsrWorkerResponse = {
|
|
10
10
|
html: string;
|
|
11
11
|
statusCode: 200 | 500;
|
|
@@ -25,17 +25,13 @@ export type WorkerTypeMapping = {
|
|
|
25
25
|
response: ScorecardsWorkerResponse;
|
|
26
26
|
};
|
|
27
27
|
[MCP_TOOL_WORKER_KEY]: {
|
|
28
|
-
params: [
|
|
28
|
+
params: [McpToolWorkerParams];
|
|
29
29
|
response: McpToolWorkerResponse;
|
|
30
30
|
};
|
|
31
|
-
[MCP_TOOL_IS_AVAILABLE_KEY]: {
|
|
32
|
-
params: [Omit<McpToolExecutionParams, 'args' | 'extra'>];
|
|
33
|
-
response: boolean;
|
|
34
|
-
};
|
|
35
31
|
[MCP_TOOL_AVAILABILITY_KEY]: {
|
|
36
32
|
params: [{
|
|
37
33
|
toolNames: string[];
|
|
38
|
-
context:
|
|
34
|
+
context: McpToolWorkerContext;
|
|
39
35
|
}];
|
|
40
36
|
response: Record<string, boolean>;
|
|
41
37
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RedoclyConfig } from '@redocly/config';
|
|
2
|
+
type ConfigFileRef = {
|
|
3
|
+
path: string;
|
|
4
|
+
env: Record<string, string>;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* The store config file of a build output, and the env values that resolve its placeholders in a
|
|
8
|
+
* worker. `env` has no variable that the worker env excludes: those placeholders stay unresolved.
|
|
9
|
+
* Main process only: `config` is the store config that was loaded from the file.
|
|
10
|
+
*/
|
|
11
|
+
export type StoreConfigFile = ConfigFileRef & {
|
|
12
|
+
config: RedoclyConfig;
|
|
13
|
+
};
|
|
14
|
+
/** The config as it crosses to a worker: the config file entries, overridden by `entries`. */
|
|
15
|
+
export type WorkerConfig = {
|
|
16
|
+
file: ConfigFileRef;
|
|
17
|
+
entries: Partial<RedoclyConfig>;
|
|
18
|
+
} | {
|
|
19
|
+
file?: undefined;
|
|
20
|
+
entries: RedoclyConfig;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Sends only the top-level entries of `config` that are not the same values as in the config file.
|
|
24
|
+
* Without a config file (`develop`), sends all of `config`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function toWorkerConfig(config: RedoclyConfig, configFile: StoreConfigFile | undefined): WorkerConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Worker side. Returns a config that the call owns: a change to it, at any depth, is not seen by
|
|
29
|
+
* other calls. Throws when the config file cannot be read.
|
|
30
|
+
*/
|
|
31
|
+
export declare function readWorkerConfig(workerConfig: WorkerConfig): RedoclyConfig;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=worker-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as a}from"node:fs";import{MARKDOC_PARTIALS_DATA_KEY as f}from"../constants/common.js";import{resolveStoreConfig as l}from"../store-config.js";let s;function m(n,e){if(!e)return{entries:n};const o=e.config;return{file:{path:e.path,env:e.env},entries:Object.fromEntries(Object.entries(n).filter(([t,r])=>o[t]!==r))}}function j(n){const{file:e,entries:o}=n;if(!e)return o;const t=d(e),r=Object.fromEntries(Object.keys(t).map(i=>[i,()=>structuredClone(t[i])]));return r[f]=()=>u(e)[f],O(r,o)}function d(n){const e=JSON.stringify([n.path,n.env]);if(s?.key!==e){const{[f]:o,...t}=u(n);s={key:e,config:t}}return s.config}function u({path:n,env:e}){let o;try{o=JSON.parse(a(n,"utf8"))}catch(t){throw new Error(`Could not read the store config file "${n}": ${String(t)}`)}return l(o,t=>e[t]).config}function O(n,e){const o={},t=(r,i)=>{Object.defineProperty(o,r,{value:i,writable:!0,enumerable:!0,configurable:!0})};for(const[r,i]of Object.entries(n)){if(Object.hasOwn(e,r)){t(r,e[r]);continue}Object.defineProperty(o,r,{enumerable:!0,configurable:!0,get:()=>{const c=i();return t(r,c),c},set:c=>t(r,c)})}for(const[r,i]of Object.entries(e))Object.hasOwn(o,r)||t(r,i);return o}export{j as readWorkerConfig,m as toWorkerConfig};
|
package/dist/types/errors.d.ts
CHANGED
package/dist/types/markdoc.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Node, Variable, RenderableTreeNode, Function } from '@
|
|
1
|
+
import type { Node, Variable, RenderableTreeNode, Function } from '@redocly/markdoc';
|
|
2
2
|
import type { RenderForLlmsRenderContext } from '@redocly/theme/markdoc/tags/types';
|
|
3
3
|
export type RenderTagFn = (node: Node, context: RenderForLlmsRenderContext) => string | undefined;
|
|
4
4
|
export type GetInnerContentFnOptions = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/reef",
|
|
3
|
-
"version": "0.140.0-next.
|
|
3
|
+
"version": "0.140.0-next.13",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"@hono/zod-validator": "0.8.0",
|
|
21
21
|
"@jitl/quickjs-singlefile-mjs-release-sync": "0.32.0",
|
|
22
22
|
"@libsql/client": "0.17.3",
|
|
23
|
-
"@markdoc/markdoc": "0.
|
|
24
|
-
"@modelcontextprotocol/server": "2.
|
|
23
|
+
"@markdoc/markdoc": "npm:@redocly/markdoc@0.7.0-beta.2",
|
|
24
|
+
"@modelcontextprotocol/server": "2.3.1",
|
|
25
25
|
"@opentelemetry/api": "1.9.1",
|
|
26
26
|
"@opentelemetry/context-zone": "2.0.1",
|
|
27
27
|
"@opentelemetry/core": "2.8.0",
|
|
@@ -33,13 +33,14 @@
|
|
|
33
33
|
"@opentelemetry/sdk-trace-web": "2.8.0",
|
|
34
34
|
"@opentelemetry/semantic-conventions": "1.40.0",
|
|
35
35
|
"@redocly/ajv": "8.18.0",
|
|
36
|
-
"@redocly/api-docs": "0.5.0-next.
|
|
37
|
-
"@redocly/config": "0.
|
|
38
|
-
"@redocly/
|
|
36
|
+
"@redocly/api-docs": "0.5.0-next.10",
|
|
37
|
+
"@redocly/config": "0.71.0",
|
|
38
|
+
"@redocly/markdoc": "0.7.0-beta.2",
|
|
39
|
+
"@redocly/openapi-core": "2.73.0",
|
|
39
40
|
"@redocly/portal-legacy-ui": "0.19.0",
|
|
40
|
-
"@redocly/portal-plugin-mock-server": "0.
|
|
41
|
+
"@redocly/portal-plugin-mock-server": "0.25.0-next.3",
|
|
41
42
|
"@redocly/realm-asyncapi-sdk": "0.17.0",
|
|
42
|
-
"@redocly/theme": "0.72.0-next.
|
|
43
|
+
"@redocly/theme": "0.72.0-next.7",
|
|
43
44
|
"@shikijs/transformers": "3.21.0",
|
|
44
45
|
"@tanstack/react-query": "5.62.3",
|
|
45
46
|
"@tanstack/react-table": "8.21.3",
|
|
@@ -62,12 +63,11 @@
|
|
|
62
63
|
"flexsearch": "0.7.43",
|
|
63
64
|
"graphql": "16.14.2",
|
|
64
65
|
"hono": "4.13.13",
|
|
65
|
-
"htmlparser2": "8.0.2",
|
|
66
66
|
"i18next": "22.4.15",
|
|
67
67
|
"is-glob": "4.0.3",
|
|
68
68
|
"js-yaml": "4.3.2",
|
|
69
69
|
"lru-cache": "11.1.0",
|
|
70
|
-
"minimatch": "
|
|
70
|
+
"minimatch": "11.0.3",
|
|
71
71
|
"mri": "1.2.0",
|
|
72
72
|
"nanoid": "5.1.16",
|
|
73
73
|
"openapi-sampler": "^1.7.9",
|
|
@@ -91,14 +91,14 @@
|
|
|
91
91
|
"tty-browserify": "0.0.1",
|
|
92
92
|
"typesense": "2.1.0",
|
|
93
93
|
"ulid": "^3.0.2",
|
|
94
|
-
"undici": "^6.
|
|
94
|
+
"undici": "^6.29.1",
|
|
95
95
|
"web-vitals": "3.3.1",
|
|
96
96
|
"workerpool": "9.2.0",
|
|
97
97
|
"ws": "^8.21.0",
|
|
98
98
|
"xml-crypto": "6.1.0",
|
|
99
99
|
"xpath": "0.0.34",
|
|
100
100
|
"yaml-ast-parser": "0.0.43",
|
|
101
|
-
"zod": "^
|
|
101
|
+
"zod": "^4.6.6"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
104
|
"react": "^19.2.7",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const a={for:"htmlFor",class:"className","accept-charset":"acceptCharset",accesskey:"accessKey",allowfullscreen:"allowFullScreen",allowtransparency:"allowTransparency",autocomplete:"autoComplete",autoplay:"autoPlay",cellpadding:"cellPadding",cellspacing:"cellSpacing",charset:"charSet",classid:"classID",colspan:"colSpan",contenteditable:"contentEditable",contextmenu:"contextMenu",crossorigin:"crossOrigin",datetime:"dateTime",enctype:"encType",formaction:"formAction",formenctype:"formEncType",formmethod:"formMethod",formnovalidate:"formNoValidate",formtarget:"formTarget",frameborder:"frameBorder",hreflang:"hrefLang","http-equiv":"httpEquiv",inputmode:"inputMode",keyparams:"keyParams",keytype:"keyType",marginheight:"marginHeight",marginwidth:"marginWidth",maxlength:"maxLength",mediagroup:"mediaGroup",minlength:"minLength",novalidate:"noValidate",playsinline:"playsInline",radiogroup:"radioGroup",readonly:"readOnly",referrerpolicy:"referrerPolicy",rowspan:"rowSpan",spellcheck:"spellCheck",srcdoc:"srcDoc",srclang:"srcLang",srcset:"srcSet",tabindex:"tabIndex",usemap:"useMap",autocapitalize:"autoCapitalize",autocorrect:"autoCorrect",autosave:"autoSave",itemprop:"itemProp",itemscope:"itemScope",itemtype:"itemType",itemid:"itemID",itemref:"itemRef","accent-height":"accentHeight","alignment-baseline":"alignmentBaseline","arabic-form":"arabicForm","baseline-shift":"baselineShift","cap-height":"capHeight","clip-path":"clipPath","clip-rule":"clipRule","color-interpolation":"colorInterpolation","color-interpolation-filters":"colorInterpolationFilters","color-profile":"colorProfile","color-rendering":"colorRendering","dominant-baseline":"dominantBaseline","enable-background":"enableBackground","fill-opacity":"fillOpacity","fill-rule":"fillRule","flood-color":"floodColor","flood-opacity":"floodOpacity","font-family":"fontFamily","font-size":"fontSize","font-size-adjust":"fontSizeAdjust","font-stretch":"fontStretch","font-style":"fontStyle","font-variant":"fontVariant","font-weight":"fontWeight","glyph-name":"glyphName","glyph-orientation-horizontal":"glyphOrientationHorizontal","glyph-orientation-vertical":"glyphOrientationVertical","horiz-adv-x":"horizAdvX","horiz-origin-x":"horizOriginX","image-rendering":"imageRendering","letter-spacing":"letterSpacing","lighting-color":"lightingColor","marker-end":"markerEnd","marker-mid":"markerMid","marker-start":"markerStart","overline-position":"overlinePosition","overline-thickness":"overlineThickness","paint-order":"paintOrder","panose-1":"panose1","pointer-events":"pointerEvents","rendering-intent":"renderingIntent","shape-rendering":"shapeRendering","stop-color":"stopColor","stop-opacity":"stopOpacity","strikethrough-position":"strikethroughPosition","strikethrough-thickness":"strikethroughThickness","stroke-dasharray":"strokeDasharray","stroke-dashoffset":"strokeDashoffset","stroke-linecap":"strokeLinecap","stroke-linejoin":"strokeLinejoin","stroke-miterlimit":"strokeMiterlimit","stroke-opacity":"strokeOpacity","stroke-width":"strokeWidth","text-anchor":"textAnchor","text-decoration":"textDecoration","text-rendering":"textRendering","underline-position":"underlinePosition","underline-thickness":"underlineThickness","unicode-bidi":"unicodeBidi","unicode-range":"unicodeRange","units-per-em":"unitsPerEm","v-alphabetic":"vAlphabetic","v-hanging":"vHanging","v-ideographic":"vIdeographic","v-mathematical":"vMathematical","vector-effect":"vectorEffect","vert-adv-y":"vertAdvY","vert-origin-x":"vertOriginX","vert-origin-y":"vertOriginY","word-spacing":"wordSpacing","writing-mode":"writingMode","x-height":"xHeight","xlink:actuate":"xlinkActuate","xlink:arcrole":"xlinkArcrole","xlink:href":"xlinkHref","xlink:role":"xlinkRole","xlink:show":"xlinkShow","xlink:title":"xlinkTitle","xlink:type":"xlinkType","xml:base":"xmlBase","xmlns:xlink":"xmlnsXlink","xml:lang":"xmlLang","xml:space":"xmlSpace"};function s(r,t){const e={};for(const[i,n]of Object.entries(r))switch(i){case"value":t==="input"||t==="textarea"?e.defaultValue=n:e.value=n;break;case"checked":t==="input"?e.defaultChecked=!0:e.checked=n;break;case"style":e.style=c(n);break;default:e[a[i]||i]=n}return e}function l(r){return r.match(/^--/)?r:r.replace(/-[a-z]/g,t=>t[1].toUpperCase())}function c(r){return r.split(";").filter(t=>t.trim().length).reduce((t,e)=>{const i=e.indexOf(":"),n=i===-1?e:e.slice(0,i),o=i===-1?"":e.slice(i+1);return t[l(n.trim())]=o.trim(),t},{})}export{s as getJsxConvertedAttributes};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Parser as p}from"htmlparser2";import{getJsxConvertedAttributes as i}from"./html-jsx-attribute-mapping.js";function l(r){const e=[],n=[];let o=null;const s=new p({onopentag(t,a){n.push({name:t,token:o}),e.push({type:"tag_open",nesting:1,meta:{tag:"html",attributes:[{type:"attribute",name:"name",value:t},{type:"attribute",name:"attrs",value:i(a,t)}]}})},ontext(t){t?.trim().length>0&&e.push({type:"text",content:t})},onclosetag(){n.pop(),e.push({type:"tag_close",nesting:-1,meta:{tag:"html"}})}});for(const t of r){if(t.type.startsWith("html")){o=t,s.write(t.content);continue}t.type==="inline"&&(t.children=l(t.children)),e.push(t)}for(const t of n)e.push({type:"error",meta:{error:{message:`HTML tag <${t.name}> is not closed, opened at`,location:t?.token?.map?{start:{line:t.token.map[0]},end:{line:t.token.map[1]}}:void 0}}}),e.push({type:"tag_close",nesting:-1,meta:{tag:"html"}});return e}export{l as processHtmlTokens};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
function s(e,t){if(e.lines.length===0)return"";let l="",i=[];if(e.location){const n=e.lines[0]+1;if(l+=`#L${n}`,n!==e.lines[e.lines.length-1]){const r=e.lines[e.lines.length-1];l+=`-L${e.lines[e.lines.length-1]}`,i=t.slice(n-1,r)}else i=[t[n-1]]}return{range:l,source:i}}function h(e){let t=5381,l=e.length;for(;l;)t=t*33^e.charCodeAt(--l);return(t>>>0).toString(16)}export{s as getSourceRange,h as hashSourceContent};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { McpToolSchema } from '../../../types';
|
|
2
|
-
import type { McpToolContext } from '../types.js';
|
|
3
|
-
export declare function availableSandboxTools(candidates: McpToolSchema[], context: McpToolContext): Promise<McpToolSchema[]>;
|
|
4
|
-
//# sourceMappingURL=available-sandbox-tools.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{logger as a}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as n,MCP_TOOL_AVAILABILITY_KEY as m}from"../../../workers/mcp-tool-worker-pool.js";async function p(t,l){if(t.length===0)return t;const e=t.map(o=>o.name);let r;try{r=await n.exec(m,[{toolNames:e,context:l}],{timeout:1e4})}catch(o){return i(t,o instanceof Error?o.message:String(o))}return!r||typeof r!="object"?i(t,"the worker returned no availability map"):t.filter(o=>r[o.name]!==!1)}function i(t,l){const e=t.filter(r=>r.hasAvailabilityGate);return e.length===0?t:(a.warn(`MCP tool availability is unknown (${l}); the sandbox tool list drops the gated tools: ${e.map(r=>r.name).join(", ")}.`),t.filter(r=>!r.hasAvailabilityGate))}export{p as availableSandboxTools};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Masks the username part of an email address for privacy or anonymization.
|
|
3
|
-
*
|
|
4
|
-
* The function preserves the domain while replacing most characters in the username
|
|
5
|
-
* with asterisks (`*`). It keeps the first two characters visible when the username
|
|
6
|
-
* is longer than two characters. If the input is invalid or not a string containing
|
|
7
|
-
* an `@` symbol, it returns the original value unchanged.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* maskEmail("john.doe@example.com");
|
|
12
|
-
* // → "jo******@example.com"
|
|
13
|
-
*
|
|
14
|
-
* maskEmail("ab@example.com");
|
|
15
|
-
* // → "ab@example.com"
|
|
16
|
-
*
|
|
17
|
-
* maskEmail("a@example.com");
|
|
18
|
-
* // → "*@example.com"
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* @param email - The email address to mask.
|
|
22
|
-
* @returns A masked version of the email, or the original value if invalid.
|
|
23
|
-
*/
|
|
24
|
-
export declare const maskEmail: (email: string) => string;
|
|
25
|
-
//# sourceMappingURL=mask-email.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const r=e=>{if(!e||typeof e!="string"||!e.includes("@"))return e;const[n,t]=e.split("@");return`${n.length>2?n.slice(0,2)+"*".repeat(n.length-2):"*".repeat(n.length)}@${t}`};export{r as maskEmail};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Masks all email addresses found within a given subject string.
|
|
3
|
-
*
|
|
4
|
-
* This utility searches the input text for any email-like patterns and replaces
|
|
5
|
-
* each one using the {@link maskEmail} function, preserving the rest of the string.
|
|
6
|
-
* It is typically used to sanitize or anonymize email addresses before logging,
|
|
7
|
-
* displaying, or transmitting text content.
|
|
8
|
-
*
|
|
9
|
-
* @param subject - The text that may contain one or more email addresses.
|
|
10
|
-
* @returns A new string with all detected email addresses masked.
|
|
11
|
-
*/
|
|
12
|
-
export declare const maskSubject: (subject: string) => string;
|
|
13
|
-
//# sourceMappingURL=mask-subject.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{maskEmail as c}from"./mask-email.js";const r=/[a-zA-Z0-9._%+-]/,m=/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/,d=e=>{let o="",i=0,a=0;for(;a<e.length;){const n=e.indexOf("@",a);if(n===-1)break;let t=n;for(;t>i&&r.test(e.charAt(t-1));)t--;const s=t<n?m.exec(e.slice(n+1))?.[0]:void 0;if(!s){a=n+1;continue}const l=n+1+s.length;o+=e.slice(i,t)+c(e.slice(t,l)),i=l,a=l}return o+e.slice(i)};export{d as maskSubject};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sanitizes personally identifiable information (PII) found within a URL or query string.
|
|
3
|
-
*
|
|
4
|
-
* This function scans all query parameter values and removes or masks any PII using
|
|
5
|
-
* the {@link removePii} function. It gracefully handles both absolute and relative URLs.
|
|
6
|
-
*
|
|
7
|
-
* - If the input is not a string, it is returned unchanged.
|
|
8
|
-
* - If parsing as a URL fails, the function falls back to applying {@link removePii}
|
|
9
|
-
* directly on the raw string.
|
|
10
|
-
* - The output preserves the original format (absolute or relative).
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* removePiiFromUrl('https://example.com?email=john.doe@example.com&phone=555-123-4567');
|
|
15
|
-
* // → 'https://example.com/?email=[email_removed]&phone=[phone_removed]'
|
|
16
|
-
*
|
|
17
|
-
* removePiiFromUrl('/profile?ssn=123-45-6789');
|
|
18
|
-
* // → '/profile?ssn=[SSN_removed]'
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* @param url - The input URL string or relative path to sanitize.
|
|
22
|
-
* @returns A sanitized URL string with PII masked, or the input value unchanged if not a string.
|
|
23
|
-
*/
|
|
24
|
-
export declare function removePiiFromUrl(url: unknown): unknown;
|
|
25
|
-
//# sourceMappingURL=remove-pii-from-url.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isString as n}from"../../../../../utils/string/is-string.js";import{removePii as e}from"./remove-pii.js";function m(t){if(!n(t))return t;try{const r=new URL(t,"http://example.com");for(const[i,s]of r.searchParams.entries()){const a=String(e(s));r.searchParams.set(i,a)}return t.startsWith("http://")||t.startsWith("https://")?r.toString():r.pathname+r.search+r.hash}catch{return String(e(t))}}export{m as removePiiFromUrl};
|