@shoalrun/cli 0.0.0-stage → 0.1.0-alpha.12
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/AUTHORING.md +269 -0
- package/CHANGELOG.md +89 -0
- package/LICENSE +202 -0
- package/README.md +191 -2
- package/agent-docs/.well-known/shoalrun/navigation/docs-50f26fe22670.txt +24 -0
- package/agent-docs/.well-known/shoalrun/navigation/examples-tab-50f26fe22670.txt +22 -0
- package/agent-docs/.well-known/shoalrun/navigation/project-updates-50f26fe22670.txt +37 -0
- package/agent-docs/.well-known/shoalrun/navigation/reference-tab-50f26fe22670.txt +22 -0
- package/agent-docs/SNAPSHOT.json +9 -0
- package/agent-docs/about.md +49 -0
- package/agent-docs/api-docs/focused-contracts.md +68 -0
- package/agent-docs/api-docs/generate.md +189 -0
- package/agent-docs/api-docs/generate.sections/add-useful-examples.md +47 -0
- package/agent-docs/api-docs/generate.sections/choose-generated-references-or-mdx.md +50 -0
- package/agent-docs/api-docs/generate.sections/describe-filters-that-do-not-use-their-openapi-parameter-name.md +52 -0
- package/agent-docs/api-docs/generate.sections/generate-many-pages-selectively.md +71 -0
- package/agent-docs/api-docs/generate.sections/link-guides-to-api-references.md +48 -0
- package/agent-docs/api-docs/generate.sections/map-extension-values-to-shoalrun-status.md +52 -0
- package/agent-docs/api-docs/generate.sections/override-generated-reference-order-in-navigation.md +60 -0
- package/agent-docs/api-docs/generate.sections/related-reading.md +38 -0
- package/agent-docs/api-docs/generate.sections/use-a-template-for-editorial-context.md +45 -0
- package/agent-docs/api-docs/support.md +97 -0
- package/agent-docs/author/add-page.md +126 -0
- package/agent-docs/author/agent-overview.md +59 -0
- package/agent-docs/author/agent-workflow.md +65 -0
- package/agent-docs/author/agents.md +53 -0
- package/agent-docs/author/analytics.md +286 -0
- package/agent-docs/author/analytics.sections/adapter-exports-and-consent.md +51 -0
- package/agent-docs/author/analytics.sections/built-in-events.md +52 -0
- package/agent-docs/author/analytics.sections/configure-an-adapter.md +51 -0
- package/agent-docs/author/analytics.sections/custom-interactions.md +57 -0
- package/agent-docs/author/analytics.sections/event-format.md +59 -0
- package/agent-docs/author/analytics.sections/html-markdown-and-agents.md +42 -0
- package/agent-docs/author/analytics.sections/inspect-events-locally.md +36 -0
- package/agent-docs/author/analytics.sections/map-events-to-your-service.md +42 -0
- package/agent-docs/author/analytics.sections/posthog-example.md +126 -0
- package/agent-docs/author/analytics.sections/related-reading.md +42 -0
- package/agent-docs/author/authoring-agent.md +102 -0
- package/agent-docs/author/coding-agent.md +90 -0
- package/agent-docs/author/components/action-link.md +47 -0
- package/agent-docs/author/components/api-embeds.md +271 -0
- package/agent-docs/author/components/api-embeds.sections/apiexample.md +121 -0
- package/agent-docs/author/components/api-embeds.sections/html-markdown-and-agents.md +37 -0
- package/agent-docs/author/components/api-embeds.sections/related-reading.md +37 -0
- package/agent-docs/author/components/badge.md +80 -0
- package/agent-docs/author/components/breadcrumbs.md +57 -0
- package/agent-docs/author/components/callout.md +135 -0
- package/agent-docs/author/components/cards.md +168 -0
- package/agent-docs/author/components/cards.sections/add-a-lucide-icon.md +50 -0
- package/agent-docs/author/components/cards.sections/build-a-collection.md +69 -0
- package/agent-docs/author/components/cards.sections/curate-a-reading-list.md +66 -0
- package/agent-docs/author/components/cards.sections/html-markdown-and-agents.md +37 -0
- package/agent-docs/author/components/cards.sections/link-to-one-document.md +45 -0
- package/agent-docs/author/components/cards.sections/override-card-text.md +58 -0
- package/agent-docs/author/components/cards.sections/related-reading.md +43 -0
- package/agent-docs/author/components/code-blocks.md +121 -0
- package/agent-docs/author/components/collapsible.md +103 -0
- package/agent-docs/author/components/cookbook-chooser.md +90 -0
- package/agent-docs/author/components/custom/react.md +190 -0
- package/agent-docs/author/components/custom/react.sections/html-markdown-and-agents.md +36 -0
- package/agent-docs/author/components/custom/react.sections/interactive-react-example.md +88 -0
- package/agent-docs/author/components/custom/react.sections/registration.md +41 -0
- package/agent-docs/author/components/custom/react.sections/related-reading.md +30 -0
- package/agent-docs/author/components/custom/react.sections/share-the-model-with-the-browser.md +47 -0
- package/agent-docs/author/components/custom/react.sections/where-the-content-lives.md +100 -0
- package/agent-docs/author/components/custom.md +183 -0
- package/agent-docs/author/components/definition-list.md +56 -0
- package/agent-docs/author/components/diagrams/d2-gallery.md +861 -0
- package/agent-docs/author/components/diagrams/d2-gallery.sections/basic-shapes.md +163 -0
- package/agent-docs/author/components/diagrams/d2-gallery.sections/flows-and-structures.md +375 -0
- package/agent-docs/author/components/diagrams/d2-gallery.sections/html-markdown-and-agents.md +33 -0
- package/agent-docs/author/components/diagrams/d2-gallery.sections/layout-engines.md +169 -0
- package/agent-docs/author/components/diagrams/d2-gallery.sections/related-reading.md +37 -0
- package/agent-docs/author/components/diagrams/d2-gallery.sections/sequences-and-grids.md +123 -0
- package/agent-docs/author/components/diagrams/d2-gallery.sections/styles-and-code.md +125 -0
- package/agent-docs/author/components/diagrams/pele-gallery.md +1317 -0
- package/agent-docs/author/components/diagrams/pele-gallery.sections/charts-and-planning.md +341 -0
- package/agent-docs/author/components/diagrams/pele-gallery.sections/flowchart-shapes.md +269 -0
- package/agent-docs/author/components/diagrams/pele-gallery.sections/flows-and-behavior.md +273 -0
- package/agent-docs/author/components/diagrams/pele-gallery.sections/html-markdown-and-agents.md +31 -0
- package/agent-docs/author/components/diagrams/pele-gallery.sections/related-reading.md +35 -0
- package/agent-docs/author/components/diagrams/pele-gallery.sections/specialized-diagrams.md +229 -0
- package/agent-docs/author/components/diagrams/pele-gallery.sections/systems-and-structures.md +291 -0
- package/agent-docs/author/components/diagrams.md +163 -0
- package/agent-docs/author/components/doc-link.md +62 -0
- package/agent-docs/author/components/fields.md +123 -0
- package/agent-docs/author/components/file-tree.md +88 -0
- package/agent-docs/author/components/images.md +105 -0
- package/agent-docs/author/components/info-hint.md +65 -0
- package/agent-docs/author/components/kbd.md +47 -0
- package/agent-docs/author/components/math.md +43 -0
- package/agent-docs/author/components/only.md +69 -0
- package/agent-docs/author/components/progress.md +57 -0
- package/agent-docs/author/components/related-reading.md +52 -0
- package/agent-docs/author/components/steps.md +127 -0
- package/agent-docs/author/components/tables.md +68 -0
- package/agent-docs/author/components/tabs.md +133 -0
- package/agent-docs/author/components/timeline.md +80 -0
- package/agent-docs/author/components/tooltips.md +62 -0
- package/agent-docs/author/components/walkthroughs.md +392 -0
- package/agent-docs/author/components/walkthroughs.sections/attributes.md +45 -0
- package/agent-docs/author/components/walkthroughs.sections/build-up-code.md +133 -0
- package/agent-docs/author/components/walkthroughs.sections/choose-a-layout.md +141 -0
- package/agent-docs/author/components/walkthroughs.sections/explain-one-file.md +126 -0
- package/agent-docs/author/components/walkthroughs.sections/html-markdown-and-agents.md +38 -0
- package/agent-docs/author/components/walkthroughs.sections/related-reading.md +36 -0
- package/agent-docs/author/components/walkthroughs.sections/syntax.md +51 -0
- package/agent-docs/author/components/walkthroughs.sections/take-code-from-a-file.md +34 -0
- package/agent-docs/author/components.md +81 -0
- package/agent-docs/author/content.md +74 -0
- package/agent-docs/author/cookbooks.md +119 -0
- package/agent-docs/author/css.md +241 -0
- package/agent-docs/author/css.sections/borders-fields-and-navigation-tokens.md +70 -0
- package/agent-docs/author/css.sections/core-color-tokens.md +71 -0
- package/agent-docs/author/css.sections/html-markdown-and-agents.md +54 -0
- package/agent-docs/author/css.sections/page-attributes.md +69 -0
- package/agent-docs/author/css.sections/page-transitions.md +60 -0
- package/agent-docs/author/css.sections/responsive-behavior.md +58 -0
- package/agent-docs/author/css.sections/status-code-and-callout-tokens.md +89 -0
- package/agent-docs/author/css.sections/typography-shape-and-width-tokens.md +71 -0
- package/agent-docs/author/custom-metadata.md +177 -0
- package/agent-docs/author/custom-metadata.sections/add-a-custom-page-field.md +68 -0
- package/agent-docs/author/custom-metadata.sections/choose-text-for-each-surface.md +54 -0
- package/agent-docs/author/custom-metadata.sections/preview-changes-to-metadata-providers.md +33 -0
- package/agent-docs/author/custom-metadata.sections/read-metadata-from-a-project-module.md +57 -0
- package/agent-docs/author/custom-metadata.sections/related-reading.md +29 -0
- package/agent-docs/author/design-templates.md +49 -0
- package/agent-docs/author/design-writing-guides.md +73 -0
- package/agent-docs/author/frontmatter-reference.md +150 -0
- package/agent-docs/author/frontmatter-reference.sections/choose-a-publication-and-visibility.md +44 -0
- package/agent-docs/author/frontmatter-reference.sections/control-output-details.md +41 -0
- package/agent-docs/author/frontmatter-reference.sections/global-defaults-and-enforced-values.md +31 -0
- package/agent-docs/author/frontmatter-reference.sections/html-markdown-and-agents.md +31 -0
- package/agent-docs/author/frontmatter-reference.sections/identify-and-route-a-page.md +40 -0
- package/agent-docs/author/frontmatter-reference.sections/point-agents-to-it.md +37 -0
- package/agent-docs/author/frontmatter-reference.sections/project-defined-fields.md +41 -0
- package/agent-docs/author/frontmatter-reference.sections/related-reading.md +35 -0
- package/agent-docs/author/frontmatter-reference.sections/use-the-local-files-while-authoring.md +33 -0
- package/agent-docs/author/frontmatter-reference.sections/when-the-files-update.md +31 -0
- package/agent-docs/author/frontmatter.md +255 -0
- package/agent-docs/author/frontmatter.sections/audience-badges.md +66 -0
- package/agent-docs/author/frontmatter.sections/give-deprecation-guidance.md +88 -0
- package/agent-docs/author/frontmatter.sections/global-defaults-and-enforced-values.md +75 -0
- package/agent-docs/author/frontmatter.sections/optional-native-fields.md +90 -0
- package/agent-docs/author/frontmatter.sections/publication-fields.md +85 -0
- package/agent-docs/author/frontmatter.sections/publishing-policy.md +71 -0
- package/agent-docs/author/frontmatter.sections/related-metadata-tasks.md +72 -0
- package/agent-docs/author/frontmatter.sections/related-reading.md +72 -0
- package/agent-docs/author/frontmatter.sections/required-after-inheritance.md +84 -0
- package/agent-docs/author/frontmatter.sections/seo-fields.md +79 -0
- package/agent-docs/author/frontmatter.sections/validation-rules.md +75 -0
- package/agent-docs/author/markdown-metadata.md +207 -0
- package/agent-docs/author/navigation.md +269 -0
- package/agent-docs/author/navigation.sections/arrange-authored-pages.md +58 -0
- package/agent-docs/author/navigation.sections/fill-an-api-tag-group.md +48 -0
- package/agent-docs/author/navigation.sections/hide-group-page-counts.md +45 -0
- package/agent-docs/author/navigation.sections/html-markdown-and-agents.md +40 -0
- package/agent-docs/author/navigation.sections/local-api-preview.md +47 -0
- package/agent-docs/author/navigation.sections/mix-guides-and-api-references.md +65 -0
- package/agent-docs/author/navigation.sections/optionally-split-a-large-menu-into-files.md +97 -0
- package/agent-docs/author/navigation.sections/reach-navigation-while-reading.md +38 -0
- package/agent-docs/author/navigation.sections/related-reading.md +44 -0
- package/agent-docs/author/navigation.sections/split-navigation-into-tabs.md +67 -0
- package/agent-docs/author/navigation.sections/subject-lifecycle-labels.md +42 -0
- package/agent-docs/author/reader-views.md +200 -0
- package/agent-docs/author/reader-views.sections/configure-the-choices.md +65 -0
- package/agent-docs/author/reader-views.sections/connect-your-own-control.md +59 -0
- package/agent-docs/author/reader-views.sections/how-it-works-for-agents.md +41 -0
- package/agent-docs/author/reader-views.sections/mark-pages.md +49 -0
- package/agent-docs/author/reader-views.sections/mark-sections-and-add-an-entry-choice.md +49 -0
- package/agent-docs/author/reader-views.sections/place-filter-controls.md +43 -0
- package/agent-docs/author/reader-views.sections/try-the-controls-here.md +64 -0
- package/agent-docs/author/recipe-drafts.md +181 -0
- package/agent-docs/author/recipe-drafts.sections/1-write-one-reusable-step.md +82 -0
- package/agent-docs/author/recipe-drafts.sections/2-list-the-steps-for-one-complete-goal.md +60 -0
- package/agent-docs/author/recipe-drafts.sections/3-generate-a-draft-and-review-it.md +55 -0
- package/agent-docs/author/recipe-drafts.sections/4-maintain-shoalrun-s-published-recipe-snapshots.md +66 -0
- package/agent-docs/author/recipe-drafts.sections/related-reading.md +45 -0
- package/agent-docs/author/recipe-drafts.sections/start-an-ingredient-from-an-api-example.md +50 -0
- package/agent-docs/author/recipes.md +127 -0
- package/agent-docs/author/recipes.sections/how-agents-use-this.md +50 -0
- package/agent-docs/author/recipes.sections/one-example-from-shoalrun.md +64 -0
- package/agent-docs/author/recipes.sections/related-reading.md +36 -0
- package/agent-docs/author/recipes.sections/try-a-complete-workflow.md +42 -0
- package/agent-docs/author/recipes.sections/use-recipes-for-task-oriented-how-tos.md +52 -0
- package/agent-docs/author/recipes.sections/what-changing-an-ingredient-updates.md +44 -0
- package/agent-docs/author/recipes.sections/what-each-file-does.md +48 -0
- package/agent-docs/author/recipes.sections/what-happens-and-when.md +45 -0
- package/agent-docs/author/related-reading.md +147 -0
- package/agent-docs/author/related-reading.sections/automatic-footers.md +35 -0
- package/agent-docs/author/related-reading.sections/declare-relationships.md +68 -0
- package/agent-docs/author/related-reading.sections/display-related-documents.md +54 -0
- package/agent-docs/author/related-reading.sections/html-markdown-and-agents.md +28 -0
- package/agent-docs/author/related-reading.sections/inspect-output-and-changes.md +32 -0
- package/agent-docs/author/related-reading.sections/reading-paths-in-section-markdown.md +32 -0
- package/agent-docs/author/related-reading.sections/related-reading.md +34 -0
- package/agent-docs/author/related-reading.sections/where-relationship-explanations-come-from.md +34 -0
- package/agent-docs/author/release-notes.md +153 -0
- package/agent-docs/author/search.md +299 -0
- package/agent-docs/author/search.sections/choose-a-provider.md +52 -0
- package/agent-docs/author/search.sections/cli-and-mcp-ranking-details.md +49 -0
- package/agent-docs/author/search.sections/hosted-semantic-search-limits.md +55 -0
- package/agent-docs/author/search.sections/html-markdown-and-agents.md +37 -0
- package/agent-docs/author/search.sections/lexical-search.md +78 -0
- package/agent-docs/author/search.sections/local-semantic-preview.md +43 -0
- package/agent-docs/author/search.sections/read-a-search-result-with-an-agent.md +41 -0
- package/agent-docs/author/search.sections/related-reading.md +45 -0
- package/agent-docs/author/search.sections/reuse-the-lexical-index.md +81 -0
- package/agent-docs/author/search.sections/turn-search-off.md +46 -0
- package/agent-docs/author/search.sections/use-the-search-box.md +39 -0
- package/agent-docs/author/search.sections/write-a-custom-provider.md +98 -0
- package/agent-docs/author/snippets.md +122 -0
- package/agent-docs/author/taxonomy.md +121 -0
- package/agent-docs/author/templates.md +231 -0
- package/agent-docs/author/templates.sections/built-in-feature-templates.md +52 -0
- package/agent-docs/author/templates.sections/built-in-templates.md +48 -0
- package/agent-docs/author/templates.sections/choose-formatting-enforcement-for-your-project.md +69 -0
- package/agent-docs/author/templates.sections/create-or-edit-a-project-template.md +76 -0
- package/agent-docs/author/templates.sections/keep-the-four-shapes-distinct.md +46 -0
- package/agent-docs/author/templates.sections/project-added-templates.md +39 -0
- package/agent-docs/author/templates.sections/related-reading.md +47 -0
- package/agent-docs/author/templates.sections/scaffold-a-page.md +53 -0
- package/agent-docs/author/templates.sections/short-cli-and-derived-values.md +56 -0
- package/agent-docs/author/templates.sections/what-every-template-gives-you.md +44 -0
- package/agent-docs/author/workspace/commands.md +92 -0
- package/agent-docs/author/workspace.md +104 -0
- package/agent-docs/author/writing-style.md +147 -0
- package/agent-docs/author/writing-style.sections/adapt-this-example.md +28 -0
- package/agent-docs/author/writing-style.sections/choose-components-by-reader-need.md +44 -0
- package/agent-docs/author/writing-style.sections/evidence-and-uncertainty.md +32 -0
- package/agent-docs/author/writing-style.sections/example-audience-and-scope.md +28 -0
- package/agent-docs/author/writing-style.sections/ingredients-recipes-and-cookbooks.md +36 -0
- package/agent-docs/author/writing-style.sections/page-purpose-and-structure.md +49 -0
- package/agent-docs/author/writing-style.sections/procedures-and-examples.md +32 -0
- package/agent-docs/author/writing-style.sections/related-reading.md +32 -0
- package/agent-docs/author/writing-style.sections/review-a-draft.md +30 -0
- package/agent-docs/author/writing-style.sections/reviewed-writing-examples.md +39 -0
- package/agent-docs/author/writing-style.sections/voice-and-explanations.md +39 -0
- package/agent-docs/changelog/2026-10-14-agent-readiness.md +29 -0
- package/agent-docs/changelog/2026-10-15-evaluation-builds.md +84 -0
- package/agent-docs/changelog/2026-10-16-navigation-layouts.md +72 -0
- package/agent-docs/changelog/2026-10-17-mcp-hosting.md +60 -0
- package/agent-docs/changelog/2026-10-18-search-and-tooltips.md +46 -0
- package/agent-docs/changelog/2026-10-19-visual-navigation.md +44 -0
- package/agent-docs/changelog/2026-10-25.md +45 -0
- package/agent-docs/changelog/2026-10-26.md +66 -0
- package/agent-docs/changelog/2026-10-27.md +85 -0
- package/agent-docs/changelog/authoring-preview.md +40 -0
- package/agent-docs/changelog/authoring-search.md +39 -0
- package/agent-docs/changelog/authoring-tools.md +34 -0
- package/agent-docs/changelog/working-tree-skill.md +38 -0
- package/agent-docs/changelog.md +115 -0
- package/agent-docs/chat/checks.md +175 -0
- package/agent-docs/chat/checks.sections/answer-ahead-of-time.md +38 -0
- package/agent-docs/chat/checks.sections/answer-for-one-audience.md +66 -0
- package/agent-docs/chat/checks.sections/answer-without-a-model.md +54 -0
- package/agent-docs/chat/checks.sections/follow-up-questions.md +54 -0
- package/agent-docs/chat/checks.sections/how-an-answer-is-checked.md +67 -0
- package/agent-docs/chat/checks.sections/questions-with-several-parts.md +44 -0
- package/agent-docs/chat/checks.sections/related-reading.md +42 -0
- package/agent-docs/chat/checks.sections/reuse-answers.md +50 -0
- package/agent-docs/chat/host.md +222 -0
- package/agent-docs/chat/host.sections/cap-the-daily-spend.md +78 -0
- package/agent-docs/chat/host.sections/keep-a-record-of-questions.md +64 -0
- package/agent-docs/chat/host.sections/related-reading.md +43 -0
- package/agent-docs/chat/passage-index.md +228 -0
- package/agent-docs/chat/passage-index.sections/build-the-index.md +72 -0
- package/agent-docs/chat/passage-index.sections/embed-passages.md +103 -0
- package/agent-docs/chat/passage-index.sections/how-the-default-chunker-splits-a-page.md +53 -0
- package/agent-docs/chat/passage-index.sections/limits.md +45 -0
- package/agent-docs/chat/passage-index.sections/passage-ids-and-links.md +52 -0
- package/agent-docs/chat/passage-index.sections/related-reading.md +42 -0
- package/agent-docs/chat/passage-index.sections/what-a-passage-contains.md +91 -0
- package/agent-docs/chat/prepared-answers.md +116 -0
- package/agent-docs/chat/settings.md +213 -0
- package/agent-docs/chat/settings.sections/choose-the-scorer.md +63 -0
- package/agent-docs/chat/settings.sections/configure-chat.md +80 -0
- package/agent-docs/chat/settings.sections/configure-retrieval.md +62 -0
- package/agent-docs/chat/settings.sections/related-reading.md +32 -0
- package/agent-docs/chat/settings.sections/send-extra-request-fields.md +42 -0
- package/agent-docs/chat/settings.sections/use-more-than-one-model.md +62 -0
- package/agent-docs/chat/settings.sections/when-the-scorer-is-down.md +42 -0
- package/agent-docs/chat/setup.md +97 -0
- package/agent-docs/chat.md +102 -0
- package/agent-docs/cli.md +445 -0
- package/agent-docs/cli.sections/audit-a-project-and-api-sources.md +102 -0
- package/agent-docs/cli.sections/author-pages.md +94 -0
- package/agent-docs/cli.sections/build-and-serve.md +144 -0
- package/agent-docs/cli.sections/inspect-config-and-operations.md +41 -0
- package/agent-docs/cli.sections/npm-scripts-in-this-repository.md +45 -0
- package/agent-docs/cli.sections/related-reading.md +33 -0
- package/agent-docs/cli.sections/search-and-chat.md +132 -0
- package/agent-docs/cli.sections/start-a-project.md +37 -0
- package/agent-docs/cli.sections/working-tree-authoring-tools.md +35 -0
- package/agent-docs/concepts.md +133 -0
- package/agent-docs/config/apis.md +136 -0
- package/agent-docs/config/content.md +264 -0
- package/agent-docs/config/content.sections/authoring.md +58 -0
- package/agent-docs/config/content.sections/badges.md +34 -0
- package/agent-docs/config/content.sections/callouts.md +34 -0
- package/agent-docs/config/content.sections/cookbooks.md +38 -0
- package/agent-docs/config/content.sections/frontmatter.md +75 -0
- package/agent-docs/config/content.sections/readerfilters.md +44 -0
- package/agent-docs/config/content.sections/related-reading.md +34 -0
- package/agent-docs/config/content.sections/relationships.md +76 -0
- package/agent-docs/config/content.sections/snippets.md +61 -0
- package/agent-docs/config/content.sections/templates.md +57 -0
- package/agent-docs/config/content.sections/variables.md +55 -0
- package/agent-docs/config/examples.md +362 -0
- package/agent-docs/config/examples.sections/feature-showcase.md +63 -0
- package/agent-docs/config/examples.sections/minimal-site.md +85 -0
- package/agent-docs/config/examples.sections/multilingual-help-center.md +115 -0
- package/agent-docs/config/examples.sections/parcel-api-documentation.md +158 -0
- package/agent-docs/config/examples.sections/related-reading.md +55 -0
- package/agent-docs/config/examples.sections/shared-company-preset.md +123 -0
- package/agent-docs/config/integrations.md +76 -0
- package/agent-docs/config/outputs.md +160 -0
- package/agent-docs/config/outputs.sections/exports.md +57 -0
- package/agent-docs/config/outputs.sections/metadata.md +42 -0
- package/agent-docs/config/outputs.sections/related-reading.md +34 -0
- package/agent-docs/config/publishing.md +219 -0
- package/agent-docs/config/publishing.sections/navigation.md +92 -0
- package/agent-docs/config/publishing.sections/redirects.md +79 -0
- package/agent-docs/config/publishing.sections/related-reading.md +33 -0
- package/agent-docs/config/site.md +193 -0
- package/agent-docs/config/site.sections/related-reading.md +33 -0
- package/agent-docs/config/site.sections/theme.md +74 -0
- package/agent-docs/config.md +195 -0
- package/agent-docs/configuration.md +386 -0
- package/agent-docs/configuration.sections/build-several-sites-from-shared-content.md +36 -0
- package/agent-docs/configuration.sections/change-inherited-lists-deliberately.md +76 -0
- package/agent-docs/configuration.sections/extract-a-section-with-include.md +75 -0
- package/agent-docs/configuration.sections/inspect-and-validate-changes.md +77 -0
- package/agent-docs/configuration.sections/related-reading.md +42 -0
- package/agent-docs/configuration.sections/separate-config-inheritance-from-page-metadata.md +54 -0
- package/agent-docs/configuration.sections/share-defaults-through-extends.md +121 -0
- package/agent-docs/configuration.sections/start-with-one-working-file.md +83 -0
- package/agent-docs/configuration.sections/understand-the-three-stages.md +59 -0
- package/agent-docs/configuration.sections/understand-where-paths-point.md +53 -0
- package/agent-docs/connect/mcp.md +85 -0
- package/agent-docs/contribute/agent-skills.md +72 -0
- package/agent-docs/cookbooks/authoring.md +72 -0
- package/agent-docs/cookbooks/site-workflows.md +63 -0
- package/agent-docs/cookbooks/welcome-email.md +59 -0
- package/agent-docs/documents.json +19807 -0
- package/agent-docs/example-api/cedar/create-enrollment-7af3e5d2.md +594 -0
- package/agent-docs/example-api/cedar/get-legacy-parcel-6758d051.md +122 -0
- package/agent-docs/example-api/cedar/get-parcel-edee10e6.md +122 -0
- package/agent-docs/example-api/cedar/list-parcels-0345fb5f.md +271 -0
- package/agent-docs/example-api/cedar/parcel-delivered-7d1ba6db.md +122 -0
- package/agent-docs/example-api/cedar/upload-manifest-1af17891.md +101 -0
- package/agent-docs/example-api/cedar.md +39 -0
- package/agent-docs/example-api/create-note.md +344 -0
- package/agent-docs/example-api/generated/get-note-cbedb319.md +165 -0
- package/agent-docs/example-api/write-note.md +55 -0
- package/agent-docs/explore.md +54 -0
- package/agent-docs/glossary.md +823 -0
- package/agent-docs/index.md +249 -0
- package/agent-docs/ingredients/audit-site.md +27 -0
- package/agent-docs/ingredients/build-site.md +29 -0
- package/agent-docs/ingredients/compile-framework.md +28 -0
- package/agent-docs/ingredients/complete-page.md +30 -0
- package/agent-docs/ingredients/inspect-agent-bundle.md +28 -0
- package/agent-docs/ingredients/inspect-build.md +41 -0
- package/agent-docs/ingredients/preview-site.md +31 -0
- package/agent-docs/ingredients/refresh-agent-bundle.md +29 -0
- package/agent-docs/ingredients/scaffold-page.md +28 -0
- package/agent-docs/non-goals.md +58 -0
- package/agent-docs/provenance.json +10 -0
- package/agent-docs/publish/agent-discovery.md +79 -0
- package/agent-docs/publish/agent-friendliness.md +201 -0
- package/agent-docs/publish/agent-friendliness.sections/discover-a-page-with-a-small-context-budget.md +61 -0
- package/agent-docs/publish/agent-friendliness.sections/distinguish-examples-from-a-real-service.md +48 -0
- package/agent-docs/publish/agent-friendliness.sections/evidence-has-a-scope.md +46 -0
- package/agent-docs/publish/agent-friendliness.sections/keep-shared-instructions-complete.md +48 -0
- package/agent-docs/publish/agent-friendliness.sections/related-reading.md +52 -0
- package/agent-docs/publish/agent-friendliness.sections/secret-handshake.md +50 -0
- package/agent-docs/publish/agent-friendliness.sections/try-the-page-controls.md +62 -0
- package/agent-docs/publish/agent-friendliness.sections/use-the-transport-your-client-supports.md +50 -0
- package/agent-docs/publish/agent-friendliness.sections/verify-the-page-and-detect-changes.md +58 -0
- package/agent-docs/publish/agent-friendliness.sections/what-an-agent-reads.md +68 -0
- package/agent-docs/publish/agent-friendliness.sections/what-happens-to-an-unsupported-component.md +60 -0
- package/agent-docs/publish/api-references.md +219 -0
- package/agent-docs/publish/api-references.sections/author-the-reference-page.md +65 -0
- package/agent-docs/publish/api-references.sections/build-and-verify-the-output.md +48 -0
- package/agent-docs/publish/api-references.sections/inspect-the-machine-readable-contract.md +36 -0
- package/agent-docs/publish/api-references.sections/next-steps.md +62 -0
- package/agent-docs/publish/api-references.sections/preview-a-small-api-reference.md +38 -0
- package/agent-docs/publish/api-references.sections/put-the-spec-in-your-project.md +62 -0
- package/agent-docs/publish/api-references.sections/register-the-operation.md +64 -0
- package/agent-docs/publish/api-references.sections/related-reading.md +42 -0
- package/agent-docs/publish/api-references.sections/review-imported-content-before-publishing.md +54 -0
- package/agent-docs/publish/api-references.sections/what-readers-get.md +38 -0
- package/agent-docs/publish/audit.md +160 -0
- package/agent-docs/publish/audit.sections/add-project-tests.md +57 -0
- package/agent-docs/publish/audit.sections/check-agent-evaluation-reports.md +52 -0
- package/agent-docs/publish/audit.sections/check-before-pushing.md +80 -0
- package/agent-docs/publish/audit.sections/connect-a-documentation-test-suite.md +54 -0
- package/agent-docs/publish/audit.sections/read-the-report.md +67 -0
- package/agent-docs/publish/audit.sections/related-reading.md +47 -0
- package/agent-docs/publish/content-export.md +208 -0
- package/agent-docs/publish/content-export.sections/assets-downloads-and-notices.md +40 -0
- package/agent-docs/publish/content-export.sections/check-the-result.md +27 -0
- package/agent-docs/publish/content-export.sections/export-selected-pages.md +113 -0
- package/agent-docs/publish/content-export.sections/inspect-the-manifest.md +29 -0
- package/agent-docs/publish/content-export.sections/prerequisites.md +29 -0
- package/agent-docs/publish/content-export.sections/related-reading.md +35 -0
- package/agent-docs/publish/content-export.sections/review-exported-api-material.md +44 -0
- package/agent-docs/publish/content-export.sections/try-the-help-drawer-example.md +34 -0
- package/agent-docs/publish/content-export.sections/what-the-representations-retain.md +43 -0
- package/agent-docs/publish/content-negotiation.md +121 -0
- package/agent-docs/publish/content-negotiation.sections/hosting-contract.md +60 -0
- package/agent-docs/publish/content-negotiation.sections/missing-pages.md +51 -0
- package/agent-docs/publish/content-negotiation.sections/related-reading.md +45 -0
- package/agent-docs/publish/content-negotiation.sections/validators-and-manifest-links.md +49 -0
- package/agent-docs/publish/content-negotiation.sections/verify-the-deployed-url.md +62 -0
- package/agent-docs/publish/content-negotiation.sections/what-if-the-host-cannot-negotiate.md +47 -0
- package/agent-docs/publish/content-negotiation.sections/what-shoalrun-builds-and-what-the-host-does.md +49 -0
- package/agent-docs/publish/content-negotiation.sections/why-this-matters.md +47 -0
- package/agent-docs/publish/deployment-check.md +72 -0
- package/agent-docs/publish/evals.md +175 -0
- package/agent-docs/publish/evals.sections/add-the-audit-hook.md +63 -0
- package/agent-docs/publish/evals.sections/check-the-report.md +36 -0
- package/agent-docs/publish/evals.sections/define-a-task-file.md +47 -0
- package/agent-docs/publish/evals.sections/diagnose-a-different-build.md +42 -0
- package/agent-docs/publish/evals.sections/prepare-a-report-for-the-built-site.md +39 -0
- package/agent-docs/publish/evals.sections/related-reading.md +36 -0
- package/agent-docs/publish/evals.sections/run-and-review-the-tasks.md +54 -0
- package/agent-docs/publish/evals.sections/shoalrun-s-own-example.md +42 -0
- package/agent-docs/publish/host-adapters.md +60 -0
- package/agent-docs/publish/manni.md +227 -0
- package/agent-docs/publish/manni.sections/check-the-result.md +40 -0
- package/agent-docs/publish/manni.sections/how-this-site-uses-manni.md +83 -0
- package/agent-docs/publish/manni.sections/optional-custom-policies.md +42 -0
- package/agent-docs/publish/manni.sections/prerequisites.md +40 -0
- package/agent-docs/publish/mcp-analytics.md +356 -0
- package/agent-docs/publish/mcp-analytics.sections/adapter-exports.md +50 -0
- package/agent-docs/publish/mcp-analytics.sections/enable-analytics.md +45 -0
- package/agent-docs/publish/mcp-analytics.sections/event-fields-and-service-mappings.md +53 -0
- package/agent-docs/publish/mcp-analytics.sections/optionally-collect-intent-and-group-calls.md +62 -0
- package/agent-docs/publish/mcp-analytics.sections/optionally-identify-the-client-and-model.md +54 -0
- package/agent-docs/publish/mcp-analytics.sections/related-reading.md +36 -0
- package/agent-docs/publish/mcp-analytics.sections/safeguards-and-limits.md +46 -0
- package/agent-docs/publish/mcp.md +190 -0
- package/agent-docs/publish/mcp.sections/check-budgets-and-errors.md +52 -0
- package/agent-docs/publish/mcp.sections/choose-the-documentation-source.md +51 -0
- package/agent-docs/publish/mcp.sections/configure-the-client.md +55 -0
- package/agent-docs/publish/mcp.sections/generate-a-vercel-endpoint.md +50 -0
- package/agent-docs/publish/mcp.sections/related-reading.md +42 -0
- package/agent-docs/publish/mcp.sections/search-then-read.md +53 -0
- package/agent-docs/publish/mcp.sections/serve-a-public-endpoint-on-node.md +79 -0
- package/agent-docs/publish/mcp.sections/verify-the-connection.md +40 -0
- package/agent-docs/publish/mcp.sections/why-offer-mcp-for-public-docs.md +42 -0
- package/agent-docs/publish/openapi-extension-mappings.md +84 -0
- package/agent-docs/publish/output.md +220 -0
- package/agent-docs/publish/output.sections/api-contracts.md +48 -0
- package/agent-docs/publish/output.sections/code-highlighting.md +38 -0
- package/agent-docs/publish/output.sections/current-limits.md +40 -0
- package/agent-docs/publish/output.sections/incremental-sync.md +69 -0
- package/agent-docs/publish/output.sections/manifest-records-and-sections.md +46 -0
- package/agent-docs/publish/output.sections/navigation-section-indexes.md +42 -0
- package/agent-docs/publish/output.sections/page-formats.md +46 -0
- package/agent-docs/publish/output.sections/published-output-contract.md +58 -0
- package/agent-docs/publish/output.sections/reader-appearance-and-controls.md +44 -0
- package/agent-docs/publish/output.sections/related-reading.md +34 -0
- package/agent-docs/publish/output.sections/response-identity.md +38 -0
- package/agent-docs/publish/output.sections/site-files.md +64 -0
- package/agent-docs/publish/output.sections/validate-an-existing-build-locally.md +48 -0
- package/agent-docs/publish/output.sections/where-files-go.md +49 -0
- package/agent-docs/publish/publications.md +153 -0
- package/agent-docs/publish/revisions.md +104 -0
- package/agent-docs/publish/seo.md +119 -0
- package/agent-docs/publish/single-source-sites.md +168 -0
- package/agent-docs/publish/troubleshooting.md +112 -0
- package/agent-docs/publish/vercel.md +130 -0
- package/agent-docs/publish/vercel.sections/check-the-deployment.md +54 -0
- package/agent-docs/publish/vercel.sections/include-an-mcp-endpoint.md +38 -0
- package/agent-docs/publish/vercel.sections/prepare-your-project.md +52 -0
- package/agent-docs/publish/vercel.sections/related-reading.md +38 -0
- package/agent-docs/publish.md +95 -0
- package/agent-docs/recipes/add-page.md +194 -0
- package/agent-docs/recipes/add-page.sections/audit-the-shoalrun-docs-project.md +40 -0
- package/agent-docs/recipes/add-page.sections/before-you-start.md +32 -0
- package/agent-docs/recipes/add-page.sections/build-the-shoalrun-docs-site.md +42 -0
- package/agent-docs/recipes/add-page.sections/check-agent-search-and-page-lookup.md +42 -0
- package/agent-docs/recipes/add-page.sections/check-the-built-page-artifacts.md +54 -0
- package/agent-docs/recipes/add-page.sections/check-the-outcome.md +28 -0
- package/agent-docs/recipes/add-page.sections/complete-the-guide-and-add-navigation.md +42 -0
- package/agent-docs/recipes/add-page.sections/install-the-site-dependencies.md +41 -0
- package/agent-docs/recipes/add-page.sections/regenerate-the-agent-documentation-bundle.md +43 -0
- package/agent-docs/recipes/add-page.sections/related-reading.md +30 -0
- package/agent-docs/recipes/add-page.sections/scaffold-a-new-guide.md +42 -0
- package/agent-docs/recipes/audit-docs.md +121 -0
- package/agent-docs/recipes/build-docs.md +104 -0
- package/agent-docs/recipes/preview-hybrid-search.md +84 -0
- package/agent-docs/recipes/preview-page.md +123 -0
- package/agent-docs/recipes/queue-email.md +49 -0
- package/agent-docs/recipes/refresh-agent-docs.md +127 -0
- package/agent-docs/recipes/send-welcome-email.md +92 -0
- package/agent-docs/reference/agent-skills.md +73 -0
- package/agent-docs/search-and-chat.md +87 -0
- package/agent-docs/search-index.json +1 -0
- package/agent-docs/start.md +201 -0
- package/agent-docs/start.sections/add-the-page-to-navigation.md +50 -0
- package/agent-docs/start.sections/build-and-check-both-outputs.md +50 -0
- package/agent-docs/start.sections/build-this-site.md +35 -0
- package/agent-docs/start.sections/check-writing-style.md +35 -0
- package/agent-docs/start.sections/create-a-page.md +105 -0
- package/agent-docs/start.sections/make-it-yours.md +37 -0
- package/agent-docs/start.sections/migrating-from-github-packages.md +35 -0
- package/agent-docs/start.sections/prerequisites.md +37 -0
- package/agent-docs/start.sections/related-reading.md +45 -0
- package/agent-docs/start.sections/start-your-own-project.md +53 -0
- package/agent-docs/status.md +75 -0
- package/agent-docs/styling/color-modes.md +89 -0
- package/agent-docs/styling/footer.md +69 -0
- package/agent-docs/styling/header.md +67 -0
- package/agent-docs/styling/layouts.md +197 -0
- package/agent-docs/styling/layouts.sections/build-a-landing-page.md +61 -0
- package/agent-docs/styling/layouts.sections/build-a-splash-page.md +88 -0
- package/agent-docs/styling/layouts.sections/control-navigation-tabs.md +51 -0
- package/agent-docs/styling/layouts.sections/customize-the-hero.md +50 -0
- package/agent-docs/styling/layouts.sections/hero.md +83 -0
- package/agent-docs/styling/layouts.sections/html-markdown-and-agents.md +40 -0
- package/agent-docs/styling/layouts.sections/related-reading.md +42 -0
- package/agent-docs/styling/palettes.md +144 -0
- package/agent-docs/styling/share-images.md +109 -0
- package/agent-docs/styling/themes.md +128 -0
- package/agent-docs/styling/try.md +94 -0
- package/agent-docs/what-is-shoalrun.md +56 -0
- package/assets/analytics.js +72 -0
- package/assets/ask-widget.js +320 -0
- package/assets/code-samples.js +28 -0
- package/assets/components.js +32 -0
- package/assets/cookbook-chooser.js +170 -0
- package/assets/copy-markdown.js +24 -0
- package/assets/example-panel.js +77 -0
- package/assets/file-tree.js +49 -0
- package/assets/image-viewer.js +41 -0
- package/assets/mode.js +26 -0
- package/assets/navigation.js +233 -0
- package/assets/reader-filters.js +305 -0
- package/assets/reader.js +111 -0
- package/assets/search-highlight.js +32 -0
- package/assets/search-lexical.js +66 -0
- package/assets/search.js +150 -0
- package/assets/table-filter.js +63 -0
- package/assets/tabs.js +80 -0
- package/assets/theme-preview.css +17 -0
- package/assets/theme-preview.js +135 -0
- package/assets/theme-switcher.js +113 -0
- package/assets/tooltips.js +81 -0
- package/assets/walkthrough.js +233 -0
- package/dist/agent-discovery.d.ts +11 -0
- package/dist/agent-discovery.js +37 -0
- package/dist/analytics.d.ts +2 -0
- package/dist/analytics.js +13 -0
- package/dist/answer-cache.d.ts +24 -0
- package/dist/answer-cache.js +55 -0
- package/dist/answer-cards.d.ts +71 -0
- package/dist/answer-cards.js +148 -0
- package/dist/api-embeds.d.ts +22 -0
- package/dist/api-embeds.js +84 -0
- package/dist/api-identity.d.ts +6 -0
- package/dist/api-identity.js +7 -0
- package/dist/api-materialize.d.ts +12 -0
- package/dist/api-materialize.js +120 -0
- package/dist/api-publication-plan.d.ts +86 -0
- package/dist/api-publication-plan.js +187 -0
- package/dist/api-publication-review.d.ts +23 -0
- package/dist/api-publication-review.js +58 -0
- package/dist/api-selection.d.ts +21 -0
- package/dist/api-selection.js +84 -0
- package/dist/api.d.ts +74 -0
- package/dist/api.js +782 -0
- package/dist/artifact-bundle.d.ts +96 -0
- package/dist/artifact-bundle.js +306 -0
- package/dist/ask-function.d.ts +19 -0
- package/dist/ask-function.js +139 -0
- package/dist/ask-handler.d.ts +38 -0
- package/dist/ask-handler.js +157 -0
- package/dist/ask-log-kv.d.ts +14 -0
- package/dist/ask-log-kv.js +69 -0
- package/dist/ask-log.d.ts +106 -0
- package/dist/ask-log.js +159 -0
- package/dist/ask-setup.d.ts +43 -0
- package/dist/ask-setup.js +99 -0
- package/dist/ask.d.ts +152 -0
- package/dist/ask.js +532 -0
- package/dist/audit.d.ts +29 -0
- package/dist/audit.js +171 -0
- package/dist/authored-change.d.ts +22 -0
- package/dist/authored-change.js +47 -0
- package/dist/authoring-api.d.ts +18 -0
- package/dist/authoring-api.js +15 -0
- package/dist/authoring-findings.d.ts +31 -0
- package/dist/authoring-findings.js +104 -0
- package/dist/authoring-guidance.d.ts +15 -0
- package/dist/authoring-guidance.js +57 -0
- package/dist/authoring-preview.d.ts +24 -0
- package/dist/authoring-preview.js +160 -0
- package/dist/authoring-project-command.d.ts +7 -0
- package/dist/authoring-project-command.js +52 -0
- package/dist/authoring-project.d.ts +104 -0
- package/dist/authoring-project.js +287 -0
- package/dist/authoring-review-command.d.ts +1 -0
- package/dist/authoring-review-command.js +68 -0
- package/dist/authoring-review.d.ts +72 -0
- package/dist/authoring-review.js +71 -0
- package/dist/authoring-snapshot.d.ts +66 -0
- package/dist/authoring-snapshot.js +293 -0
- package/dist/authoring-validation-command.d.ts +1 -0
- package/dist/authoring-validation-command.js +54 -0
- package/dist/authoring-validation.d.ts +101 -0
- package/dist/authoring-validation.js +348 -0
- package/dist/badges.d.ts +16 -0
- package/dist/badges.js +57 -0
- package/dist/budget-store.d.ts +21 -0
- package/dist/budget-store.js +90 -0
- package/dist/budget.d.ts +45 -0
- package/dist/budget.js +128 -0
- package/dist/build-info.d.ts +10 -0
- package/dist/build-info.js +15 -0
- package/dist/callouts.d.ts +18 -0
- package/dist/callouts.js +63 -0
- package/dist/cards.d.ts +35 -0
- package/dist/cards.js +237 -0
- package/dist/chat-config.d.ts +117 -0
- package/dist/chat-config.js +400 -0
- package/dist/chat-pause.d.ts +1 -0
- package/dist/chat-pause.js +3 -0
- package/dist/chat.d.ts +46 -0
- package/dist/chat.js +78 -0
- package/dist/cli-discovery.d.ts +4 -0
- package/dist/cli-discovery.js +86 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +750 -0
- package/dist/code-samples.d.ts +50 -0
- package/dist/code-samples.js +181 -0
- package/dist/compact-discovery.d.ts +15 -0
- package/dist/compact-discovery.js +78 -0
- package/dist/compiler-config.d.ts +67 -0
- package/dist/compiler-config.js +66 -0
- package/dist/compiler.d.ts +208 -0
- package/dist/compiler.js +524 -0
- package/dist/component-bundle.d.ts +4 -0
- package/dist/component-bundle.js +32 -0
- package/dist/component-content.d.ts +78 -0
- package/dist/component-content.js +47 -0
- package/dist/component-manifest.d.ts +15 -0
- package/dist/component-manifest.js +84 -0
- package/dist/component-names.d.ts +1 -0
- package/dist/component-names.js +1 -0
- package/dist/component-plugins.d.ts +28 -0
- package/dist/component-plugins.js +141 -0
- package/dist/component-transform.d.ts +10 -0
- package/dist/component-transform.js +697 -0
- package/dist/components.d.ts +2 -0
- package/dist/components.js +1 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.js +149 -0
- package/dist/content-export-assets.d.ts +23 -0
- package/dist/content-export-assets.js +103 -0
- package/dist/content-export-command.d.ts +20 -0
- package/dist/content-export-command.js +57 -0
- package/dist/content-export-config.d.ts +16 -0
- package/dist/content-export-config.js +50 -0
- package/dist/content-export-render.d.ts +12 -0
- package/dist/content-export-render.js +132 -0
- package/dist/content-export.d.ts +30 -0
- package/dist/content-export.js +307 -0
- package/dist/content-filter.d.ts +4 -0
- package/dist/content-filter.js +16 -0
- package/dist/content-source.d.ts +100 -0
- package/dist/content-source.js +42 -0
- package/dist/content-workspace.d.ts +293 -0
- package/dist/content-workspace.js +69 -0
- package/dist/content.d.ts +41 -0
- package/dist/content.js +176 -0
- package/dist/cookbook-chooser.d.ts +19 -0
- package/dist/cookbook-chooser.js +56 -0
- package/dist/dev.d.ts +2 -0
- package/dist/dev.js +113 -0
- package/dist/diagram-svg.d.ts +1 -0
- package/dist/diagram-svg.js +44 -0
- package/dist/diagrams.d.ts +7 -0
- package/dist/diagrams.js +102 -0
- package/dist/docs-author.d.ts +86 -0
- package/dist/docs-author.js +380 -0
- package/dist/docs-command.d.ts +8 -0
- package/dist/docs-command.js +62 -0
- package/dist/docs-eval.d.ts +146 -0
- package/dist/docs-eval.js +231 -0
- package/dist/docs-mcp.d.ts +12 -0
- package/dist/docs-mcp.js +82 -0
- package/dist/docs-tool.d.ts +81 -0
- package/dist/docs-tool.js +7 -0
- package/dist/document-sync.d.ts +36 -0
- package/dist/document-sync.js +58 -0
- package/dist/documentation-core.d.ts +148 -0
- package/dist/documentation-core.js +160 -0
- package/dist/embeddings.d.ts +68 -0
- package/dist/embeddings.js +198 -0
- package/dist/focused-audit.d.ts +10 -0
- package/dist/focused-audit.js +69 -0
- package/dist/focused-openapi.d.ts +2 -0
- package/dist/focused-openapi.js +140 -0
- package/dist/fonts.d.ts +54 -0
- package/dist/fonts.js +92 -0
- package/dist/freshness.d.ts +7 -0
- package/dist/freshness.js +58 -0
- package/dist/frontmatter.d.ts +24 -0
- package/dist/frontmatter.js +342 -0
- package/dist/function-package.d.ts +1 -0
- package/dist/function-package.js +58 -0
- package/dist/highlight.d.ts +14 -0
- package/dist/highlight.js +114 -0
- package/dist/html-renderer.d.ts +10 -0
- package/dist/html-renderer.js +63 -0
- package/dist/icons.d.ts +8 -0
- package/dist/icons.js +40 -0
- package/dist/index-build.d.ts +55 -0
- package/dist/index-build.js +227 -0
- package/dist/ingredient-draft.d.ts +17 -0
- package/dist/ingredient-draft.js +94 -0
- package/dist/ingredient-workflow.d.ts +8 -0
- package/dist/ingredient-workflow.js +25 -0
- package/dist/init.d.ts +8 -0
- package/dist/init.js +66 -0
- package/dist/jev.d.ts +62 -0
- package/dist/jev.js +190 -0
- package/dist/link-audit.d.ts +12 -0
- package/dist/link-audit.js +146 -0
- package/dist/llms-discovery.d.ts +11 -0
- package/dist/llms-discovery.js +23 -0
- package/dist/markdown-sections.d.ts +15 -0
- package/dist/markdown-sections.js +60 -0
- package/dist/markdown-size.d.ts +7 -0
- package/dist/markdown-size.js +6 -0
- package/dist/mcp-analytics.d.ts +38 -0
- package/dist/mcp-analytics.js +35 -0
- package/dist/mcp-artifacts.d.ts +2 -0
- package/dist/mcp-artifacts.js +78 -0
- package/dist/mcp-client-metadata.d.ts +18 -0
- package/dist/mcp-client-metadata.js +69 -0
- package/dist/mcp-config.d.ts +23 -0
- package/dist/mcp-config.js +56 -0
- package/dist/mcp-http.d.ts +19 -0
- package/dist/mcp-http.js +157 -0
- package/dist/mcp-integrity.d.ts +3 -0
- package/dist/mcp-integrity.js +4 -0
- package/dist/mcp-node.d.ts +4 -0
- package/dist/mcp-node.js +68 -0
- package/dist/mcp-vercel-runtime.d.ts +3 -0
- package/dist/mcp-vercel-runtime.js +36 -0
- package/dist/mcp-vercel.d.ts +15 -0
- package/dist/mcp-vercel.js +65 -0
- package/dist/metadata-provider.d.ts +19 -0
- package/dist/metadata-provider.js +38 -0
- package/dist/negotiation.d.ts +10 -0
- package/dist/negotiation.js +35 -0
- package/dist/not-found.d.ts +16 -0
- package/dist/not-found.js +25 -0
- package/dist/output-commit.d.ts +1 -0
- package/dist/output-commit.js +133 -0
- package/dist/output-location.d.ts +3 -0
- package/dist/output-location.js +30 -0
- package/dist/page-materialization.d.ts +78 -0
- package/dist/page-materialization.js +132 -0
- package/dist/page-renderer.d.ts +71 -0
- package/dist/page-renderer.js +177 -0
- package/dist/palettes.d.ts +41 -0
- package/dist/palettes.js +160 -0
- package/dist/procedure-components.d.ts +2 -0
- package/dist/procedure-components.js +94 -0
- package/dist/project-navigation.d.ts +41 -0
- package/dist/project-navigation.js +190 -0
- package/dist/provider-http.d.ts +10 -0
- package/dist/provider-http.js +60 -0
- package/dist/publication-manifest.d.ts +229 -0
- package/dist/publication-manifest.js +53 -0
- package/dist/publication-plan.d.ts +34 -0
- package/dist/publication-plan.js +85 -0
- package/dist/publication-render.d.ts +71 -0
- package/dist/publication-render.js +92 -0
- package/dist/reader-filters.d.ts +30 -0
- package/dist/reader-filters.js +105 -0
- package/dist/recipe-draft.d.ts +17 -0
- package/dist/recipe-draft.js +147 -0
- package/dist/reference-audit.d.ts +18 -0
- package/dist/reference-audit.js +169 -0
- package/dist/remote-import.d.ts +62 -0
- package/dist/remote-import.js +292 -0
- package/dist/representation-tree.d.ts +3 -0
- package/dist/representation-tree.js +320 -0
- package/dist/retrieval-config.d.ts +30 -0
- package/dist/retrieval-config.js +83 -0
- package/dist/retrieval.d.ts +60 -0
- package/dist/retrieval.js +228 -0
- package/dist/scaffold.d.ts +39 -0
- package/dist/scaffold.js +167 -0
- package/dist/search-config.d.ts +8 -0
- package/dist/search-config.js +26 -0
- package/dist/search-handler.d.ts +16 -0
- package/dist/search-handler.js +111 -0
- package/dist/search-identifier.d.ts +3 -0
- package/dist/search-identifier.js +25 -0
- package/dist/search-literal.d.ts +1 -0
- package/dist/search-literal.js +28 -0
- package/dist/search-query.d.ts +54 -0
- package/dist/search-query.js +120 -0
- package/dist/search-results.d.ts +6 -0
- package/dist/search-results.js +45 -0
- package/dist/search-snippet.d.ts +11 -0
- package/dist/search-snippet.js +93 -0
- package/dist/search-synonyms.d.ts +8 -0
- package/dist/search-synonyms.js +46 -0
- package/dist/semantic-markdown.d.ts +10 -0
- package/dist/semantic-markdown.js +39 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +223 -0
- package/dist/share-images.d.ts +24 -0
- package/dist/share-images.js +187 -0
- package/dist/site-path.d.ts +2 -0
- package/dist/site-path.js +15 -0
- package/dist/sitemap.d.ts +8 -0
- package/dist/sitemap.js +58 -0
- package/dist/snippets.d.ts +19 -0
- package/dist/snippets.js +84 -0
- package/dist/structured-model-output.d.ts +6 -0
- package/dist/structured-model-output.js +21 -0
- package/dist/subject-deprecation.d.ts +21 -0
- package/dist/subject-deprecation.js +57 -0
- package/dist/surface-text.d.ts +4 -0
- package/dist/surface-text.js +42 -0
- package/dist/taxonomy.d.ts +14 -0
- package/dist/taxonomy.js +88 -0
- package/dist/turnstile.d.ts +21 -0
- package/dist/turnstile.js +52 -0
- package/dist/vale.d.ts +28 -0
- package/dist/vale.js +103 -0
- package/dist/vercel-function.d.ts +21 -0
- package/dist/vercel-function.js +108 -0
- package/dist/vercel.d.ts +11 -0
- package/dist/vercel.js +103 -0
- package/dist/walkthrough.d.ts +25 -0
- package/dist/walkthrough.js +336 -0
- package/dist/yaml-value.d.ts +2 -0
- package/dist/yaml-value.js +36 -0
- package/example-configs/api-docs/README.md +59 -0
- package/example-configs/api-docs/apis/cedar-parcel.yaml +413 -0
- package/example-configs/api-docs/content/create-enrollment.mdx +11 -0
- package/example-configs/api-docs/content/overview.mdx +36 -0
- package/example-configs/api-docs/content/quickstart.mdx +52 -0
- package/example-configs/api-docs/docs.config.yaml +104 -0
- package/example-configs/faceted-sites/README.md +31 -0
- package/example-configs/faceted-sites/base.yaml +30 -0
- package/example-configs/faceted-sites/content/api.md +12 -0
- package/example-configs/faceted-sites/content/home.md +10 -0
- package/example-configs/faceted-sites/content/operations.md +12 -0
- package/example-configs/faceted-sites/developers.config.yaml +8 -0
- package/example-configs/faceted-sites/docs.config.yaml +7 -0
- package/example-configs/faceted-sites/europe.config.yaml +9 -0
- package/example-configs/minimal/content/install.mdx +12 -0
- package/example-configs/minimal/content/welcome.mdx +8 -0
- package/example-configs/minimal/docs.config.yaml +31 -0
- package/example-configs/multilingual/content/de/home.mdx +9 -0
- package/example-configs/multilingual/content/de/reset-password.mdx +9 -0
- package/example-configs/multilingual/content/home.mdx +10 -0
- package/example-configs/multilingual/content/reset-password.mdx +12 -0
- package/example-configs/multilingual/docs.config.yaml +61 -0
- package/example-configs/multilingual/snippets/contact.mdx +1 -0
- package/example-configs/notifications/apis/notifications.yaml +132 -0
- package/example-configs/notifications/content/cookbook.mdx +17 -0
- package/example-configs/notifications/content/get-message.mdx +14 -0
- package/example-configs/notifications/content/queue-email.mdx +42 -0
- package/example-configs/notifications/content/queue-push.mdx +33 -0
- package/example-configs/notifications/content/queue-sms.mdx +33 -0
- package/example-configs/notifications/content/send-email.mdx +14 -0
- package/example-configs/notifications/content/send-push.mdx +14 -0
- package/example-configs/notifications/content/send-sms.mdx +14 -0
- package/example-configs/notifications/content/start.mdx +13 -0
- package/example-configs/notifications/content/welcome-cookbook.mdx +21 -0
- package/example-configs/notifications/content/welcome-email.mdx +82 -0
- package/example-configs/notifications/demo.mjs +43 -0
- package/example-configs/notifications/docs.config.yaml +88 -0
- package/example-configs/notifications/ingredient-drafts/README.md +41 -0
- package/example-configs/notifications/ingredient-drafts/check-delivery.mdx +13 -0
- package/example-configs/notifications/ingredient-drafts/queue-welcome-email.mdx +48 -0
- package/example-configs/notifications/ingredient-specs/queue-welcome-email.yaml +7 -0
- package/example-configs/notifications/ingredients/check-delivery.mdx +42 -0
- package/example-configs/notifications/ingredients/queue-welcome-email.mdx +37 -0
- package/example-configs/notifications/recipe-drafts/send-welcome-email.mdx +91 -0
- package/example-configs/notifications/recipe-specs/send-welcome-email.yaml +11 -0
- package/example-configs/reference-stress/README.md +32 -0
- package/example-configs/reference-stress/apis/cedar-parcel.yaml +413 -0
- package/example-configs/reference-stress/content/enrollment-shapes.mdx +16 -0
- package/example-configs/reference-stress/content/overview.mdx +14 -0
- package/example-configs/reference-stress/docs.config.yaml +55 -0
- package/example-configs/shared-preset/shared/company-docs.yaml +46 -0
- package/example-configs/shared-preset/shared/runbook.mdx +7 -0
- package/example-configs/shared-preset/site/content/failed-invoices.mdx +15 -0
- package/example-configs/shared-preset/site/content/start.mdx +9 -0
- package/example-configs/shared-preset/site/docs.config.yaml +18 -0
- package/example-configs/showcase/README.md +20 -0
- package/example-configs/showcase/apis/cedar-parcel.yaml +413 -0
- package/example-configs/showcase/content/assets/collection.svg +1 -0
- package/example-configs/showcase/content/code.mdx +100 -0
- package/example-configs/showcase/content/create-enrollment.mdx +11 -0
- package/example-configs/showcase/content/features.mdx +39 -0
- package/example-configs/showcase/content/landing.mdx +36 -0
- package/example-configs/showcase/content/overview.mdx +42 -0
- package/example-configs/showcase/content/personalization.mdx +38 -0
- package/example-configs/showcase/content/preview.mdx +35 -0
- package/example-configs/showcase/content/procedures.mdx +88 -0
- package/example-configs/showcase/content/quickstart.mdx +64 -0
- package/example-configs/showcase/content/reading.mdx +99 -0
- package/example-configs/showcase/content/verify.mdx +27 -0
- package/example-configs/showcase/content/visuals.mdx +47 -0
- package/example-configs/showcase/content/workflows.mdx +12 -0
- package/example-configs/showcase/docs.config.yaml +149 -0
- package/example-configs/showcase/snippets/sandbox.mdx +1 -0
- package/examples/analytics/README.md +56 -0
- package/examples/analytics/inspector.js +25 -0
- package/examples/analytics/posthog.js +36 -0
- package/examples/components/comparison.mjs +8 -0
- package/examples/components/components.json +6 -0
- package/examples/components/copy-text/LICENSE.react-dom.txt +21 -0
- package/examples/components/copy-text/LICENSE.react.txt +21 -0
- package/examples/components/copy-text/LICENSE.scheduler.txt +21 -0
- package/examples/components/copy-text/browser.jsx +21 -0
- package/examples/components/copy-text/components.json +6 -0
- package/examples/components/copy-text/package-lock.json +41 -0
- package/examples/components/copy-text/package.json +9 -0
- package/examples/components/copy-text/plugin.mjs +5 -0
- package/examples/components/retry-budget/LICENSE.react-dom.txt +21 -0
- package/examples/components/retry-budget/LICENSE.react.txt +21 -0
- package/examples/components/retry-budget/LICENSE.scheduler.txt +21 -0
- package/examples/components/retry-budget/browser.jsx +50 -0
- package/examples/components/retry-budget/components.json +6 -0
- package/examples/components/retry-budget/model.js +22 -0
- package/examples/components/retry-budget/package-lock.json +41 -0
- package/examples/components/retry-budget/package.json +9 -0
- package/examples/components/retry-budget/plugin.mjs +31 -0
- package/examples/components/retry-budget/policy.json +9 -0
- package/examples/components/retry-budget/style.css +25 -0
- package/examples/diagrams/d2-gallery.json +138 -0
- package/examples/diagrams/gallery.json +236 -0
- package/examples/help-drawer/README.md +44 -0
- package/examples/help-drawer/app.js +53 -0
- package/examples/help-drawer/components/components.json +1 -0
- package/examples/help-drawer/components/support.mjs +10 -0
- package/examples/help-drawer/content/account.md +8 -0
- package/examples/help-drawer/content/billing.mdx +29 -0
- package/examples/help-drawer/content/keys.mdx +46 -0
- package/examples/help-drawer/content/settings.svg +1 -0
- package/examples/help-drawer/docs.config.yaml +30 -0
- package/examples/help-drawer/index.html +26 -0
- package/examples/help-drawer/preview.mjs +33 -0
- package/examples/mcp-analytics/README.md +59 -0
- package/examples/mcp-analytics/posthog.mjs +159 -0
- package/package.json +100 -4
- package/schemas/content-export.schema.json +55 -0
- package/skills/shoalrun-author/SKILL.md +52 -0
- package/skills/shoalrun-component-author/SKILL.md +24 -0
- package/skills/shoalrun-recipes/SKILL.md +25 -0
- package/skills/shoalrun-setup/SKILL.md +34 -0
- package/skills/shoalrun-walkthrough/SKILL.md +18 -0
- package/styles/fonts/figtree-LICENSE.txt +93 -0
- package/styles/fonts/figtree-italic.woff2 +0 -0
- package/styles/fonts/figtree.woff2 +0 -0
- package/styles/fonts/inter-LICENSE.txt +93 -0
- package/styles/fonts/inter-italic.woff2 +0 -0
- package/styles/fonts/inter.woff2 +0 -0
- package/styles/fonts/jetbrains-mono-LICENSE.txt +93 -0
- package/styles/fonts/jetbrains-mono.woff2 +0 -0
- package/styles/fonts/jost-LICENSE.txt +93 -0
- package/styles/fonts/jost-italic.woff2 +0 -0
- package/styles/fonts/jost.woff2 +0 -0
- package/styles/fonts/share/figtree-400.ttf +0 -0
- package/styles/fonts/share/figtree-700.ttf +0 -0
- package/styles/fonts/share/inter-400.ttf +0 -0
- package/styles/fonts/share/inter-700.ttf +0 -0
- package/styles/fonts/share/jetbrains-mono-700.ttf +0 -0
- package/styles/fonts/share/jost-italic-800.ttf +0 -0
- package/styles/fonts/share/metrics.json +1 -0
- package/styles/fonts/share/source-serif-4-400.ttf +0 -0
- package/styles/fonts/source-serif-4-LICENSE.txt +93 -0
- package/styles/fonts/source-serif-4-italic.woff2 +0 -0
- package/styles/fonts/source-serif-4.woff2 +0 -0
- package/styles/header.css +56 -0
- package/styles/site.css +825 -0
- package/styles/themes/ledger.css +93 -0
- package/styles/themes/palettes/amethyst.yaml +127 -0
- package/styles/themes/palettes/coral.yaml +127 -0
- package/styles/themes/palettes/harbor.yaml +127 -0
- package/styles/themes/palettes/ink.yaml +127 -0
- package/styles/themes/palettes/kelp.yaml +127 -0
- package/styles/themes/palettes/vice.yaml +127 -0
- package/styles/themes/reef.css +68 -0
- package/styles/themes/vice.css +56 -0
- package/styles/tokens.css +91 -0
- package/templates/api-reference.mdx +133 -0
- package/templates/cookbook.mdx +38 -0
- package/templates/explanation.mdx +76 -0
- package/templates/guide.mdx +175 -0
- package/templates/how-to.mdx +171 -0
- package/templates/ingredient.mdx +53 -0
- package/templates/recipe.mdx +76 -0
- package/templates/reference.mdx +171 -0
- package/templates/troubleshooting.mdx +159 -0
- package/templates/tutorial.mdx +165 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { markdownTokens } from './markdown-size.js';
|
|
6
|
+
import { queryBuiltSite } from './search-query.js';
|
|
7
|
+
export const bundledDocumentation = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../agent-docs');
|
|
8
|
+
export class DocumentationError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
constructor(code, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export const documentationFailure = (error) => ({ status: 'error', error: { code: error instanceof DocumentationError ? error.code : 'internal_error', message: error instanceof DocumentationError ? error.message : 'Documentation could not be loaded; check the built site files.' } });
|
|
16
|
+
export const documentationTokens = markdownTokens;
|
|
17
|
+
function validate(options) {
|
|
18
|
+
const maxTokens = options.maxTokens ?? 4096;
|
|
19
|
+
if (!Number.isSafeInteger(maxTokens) || maxTokens < 256 || maxTokens > 100_000)
|
|
20
|
+
throw new DocumentationError('invalid_input', 'maxTokens must be an integer from 256 to 100000');
|
|
21
|
+
for (const key of ['channel', 'version', 'language'])
|
|
22
|
+
if (options[key] !== undefined && (typeof options[key] !== 'string' || !options[key]?.trim() || options[key].length > 128))
|
|
23
|
+
throw new DocumentationError('invalid_input', `${key} must be nonempty text of at most 128 characters`);
|
|
24
|
+
return maxTokens;
|
|
25
|
+
}
|
|
26
|
+
function budget(data, maxTokens, truncated) {
|
|
27
|
+
const result = { ...data, budget: { maxTokens, encoding: 'cl100k_base', tokens: 0, truncated } };
|
|
28
|
+
// Reserve all digits of the largest possible count, including the metadata itself.
|
|
29
|
+
result.budget.tokens = maxTokens;
|
|
30
|
+
result.budget.tokens = documentationTokens(JSON.stringify(result));
|
|
31
|
+
let actual = documentationTokens(JSON.stringify(result));
|
|
32
|
+
for (let i = 0; actual > result.budget.tokens && i < 8; i++) {
|
|
33
|
+
result.budget.tokens = actual;
|
|
34
|
+
actual = documentationTokens(JSON.stringify(result));
|
|
35
|
+
}
|
|
36
|
+
return { result, fits: actual <= result.budget.tokens && result.budget.tokens <= maxTokens };
|
|
37
|
+
}
|
|
38
|
+
async function loadJson(directory, name) {
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(await readFile(path.join(directory, name), 'utf8'));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error.code === 'ENOENT')
|
|
44
|
+
throw new DocumentationError('missing_index', `Missing ${name}; build the documentation first${name === 'search-index.json' ? ' with search indexing enabled' : ''}.`);
|
|
45
|
+
throw new DocumentationError('internal_error', `Cannot read valid ${name}. Rebuild the documentation.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function searchDocumentation(directory, query, options = {}) {
|
|
49
|
+
const maxTokens = validate(options);
|
|
50
|
+
if (typeof query !== 'string' || !query.trim() || query.length > 512)
|
|
51
|
+
throw new DocumentationError('invalid_input', 'query must be nonempty text of at most 512 characters');
|
|
52
|
+
if (options.limit !== undefined && (!Number.isSafeInteger(options.limit) || options.limit < 1 || options.limit > 100))
|
|
53
|
+
throw new DocumentationError('invalid_input', 'limit must be an integer from 1 to 100');
|
|
54
|
+
await loadJson(directory, 'search-index.json');
|
|
55
|
+
let search;
|
|
56
|
+
try {
|
|
57
|
+
search = await queryBuiltSite(directory, query, options);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const message = error.message;
|
|
61
|
+
if (/selected publication|multiple publications/.test(message))
|
|
62
|
+
throw new DocumentationError('invalid_input', message);
|
|
63
|
+
throw new DocumentationError('internal_error', 'Cannot search the documentation index. Rebuild the documentation.');
|
|
64
|
+
}
|
|
65
|
+
let results = search.results;
|
|
66
|
+
while (true) {
|
|
67
|
+
const bounded = budget({ ...search, results }, maxTokens, results.length < search.total);
|
|
68
|
+
if (bounded.fits)
|
|
69
|
+
return bounded.result;
|
|
70
|
+
if (!results.length)
|
|
71
|
+
throw new DocumentationError('invalid_input', 'maxTokens is too small for the search metadata. Increase the budget or shorten the query.');
|
|
72
|
+
results = results.slice(0, -1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function selectDocument(directory, id, options) {
|
|
76
|
+
if (typeof id !== 'string' || !id.trim() || id.length > 256)
|
|
77
|
+
throw new DocumentationError('invalid_input', 'id must be nonempty text of at most 256 characters');
|
|
78
|
+
const manifest = await loadJson(directory, 'documents.json');
|
|
79
|
+
if (manifest?.schemaVersion !== 1 || !Array.isArray(manifest.documents) || manifest.documents.some((doc) => !doc || typeof doc.id !== 'string' || typeof doc.title !== 'string' || typeof doc.url !== 'string' || typeof doc.markdownUrl !== 'string' || !doc.variant || ['channel', 'version', 'language'].some(key => typeof doc.variant[key] !== 'string')))
|
|
80
|
+
throw new DocumentationError('internal_error', 'Invalid documents.json. Rebuild the documentation.');
|
|
81
|
+
const matching = manifest.documents.filter((doc) => doc.id === id);
|
|
82
|
+
if (!matching.length)
|
|
83
|
+
throw new DocumentationError('unknown_page', `No document with ID ${id}; search the documentation first.`);
|
|
84
|
+
const selected = matching.filter((doc) => ['channel', 'version', 'language'].every(key => !options[key] || doc.variant[key] === options[key]));
|
|
85
|
+
if (selected.length !== 1)
|
|
86
|
+
throw new DocumentationError('invalid_input', selected.length ? 'Page has multiple publications; select channel, version, and language.' : 'Page does not match the selected publication.');
|
|
87
|
+
return selected[0];
|
|
88
|
+
}
|
|
89
|
+
async function readMarkdown(directory, markdownUrl) {
|
|
90
|
+
let content;
|
|
91
|
+
try {
|
|
92
|
+
const root = await realpath(directory);
|
|
93
|
+
if (!markdownUrl.startsWith('/') || /[?#\\]/.test(markdownUrl))
|
|
94
|
+
throw new Error('Invalid Markdown URL');
|
|
95
|
+
const file = await realpath(path.resolve(root, decodeURIComponent(markdownUrl.slice(1))));
|
|
96
|
+
if (!file.startsWith(`${root}${path.sep}`) || !file.endsWith('.md'))
|
|
97
|
+
throw new Error('Markdown path escapes site');
|
|
98
|
+
content = await readFile(file, 'utf8');
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
throw new DocumentationError('internal_error', 'Cannot read the published Markdown page. Rebuild the documentation.');
|
|
102
|
+
}
|
|
103
|
+
return content;
|
|
104
|
+
}
|
|
105
|
+
export async function getDocumentationPage(directory, id, options = {}) {
|
|
106
|
+
const maxTokens = validate(options);
|
|
107
|
+
const doc = await selectDocument(directory, id, options);
|
|
108
|
+
const content = await readMarkdown(directory, doc.markdownUrl);
|
|
109
|
+
const metadata = { status: 'ok', id: doc.id, title: doc.title, url: doc.url, markdownUrl: doc.markdownUrl, publication: doc.variant };
|
|
110
|
+
const full = budget({ ...metadata, content }, maxTokens, false);
|
|
111
|
+
if (full.fits)
|
|
112
|
+
return full.result;
|
|
113
|
+
if (!budget({ ...metadata, content: '' }, maxTokens, true).fits)
|
|
114
|
+
throw new DocumentationError('invalid_input', 'maxTokens is too small for the page metadata. Increase the budget.');
|
|
115
|
+
const characters = [...content];
|
|
116
|
+
let low = 0, high = characters.length;
|
|
117
|
+
// Each accepted candidate fits; a final count is made over the serialized result.
|
|
118
|
+
while (low < high) {
|
|
119
|
+
const middle = Math.ceil((low + high) / 2);
|
|
120
|
+
if (budget({ ...metadata, content: characters.slice(0, middle).join('') }, maxTokens, true).fits)
|
|
121
|
+
low = middle;
|
|
122
|
+
else
|
|
123
|
+
high = middle - 1;
|
|
124
|
+
}
|
|
125
|
+
return budget({ ...metadata, content: characters.slice(0, low).join('') }, maxTokens, true).result;
|
|
126
|
+
}
|
|
127
|
+
export async function getDocumentationOutline(directory, id, options = {}) {
|
|
128
|
+
const maxTokens = validate(options);
|
|
129
|
+
const doc = await selectDocument(directory, id, options);
|
|
130
|
+
if (!Array.isArray(doc.sections))
|
|
131
|
+
throw new DocumentationError('missing_index', 'Section outline is unavailable; rebuild the documentation.');
|
|
132
|
+
const data = { status: 'ok', id: doc.id, title: doc.title, publication: doc.variant, markdownUrl: doc.markdownUrl, digest: doc.digest, sections: doc.sections };
|
|
133
|
+
const result = budget(data, maxTokens, false);
|
|
134
|
+
if (!result.fits) {
|
|
135
|
+
const complete = budget(data, 100_000, false);
|
|
136
|
+
if (!complete.fits)
|
|
137
|
+
throw new DocumentationError('invalid_input', 'The complete outline exceeds the 100000-token limit. Use search_docs to find a page or section read instead.');
|
|
138
|
+
const retryTokens = Math.min(100_000, Math.ceil(complete.result.budget.tokens / 1000) * 1000);
|
|
139
|
+
throw new DocumentationError('invalid_input', `The complete outline needs about ${complete.result.budget.tokens} tokens including metadata. Retry get_outline with maxTokens=${retryTokens}.`);
|
|
140
|
+
}
|
|
141
|
+
return result.result;
|
|
142
|
+
}
|
|
143
|
+
export async function getDocumentationSection(directory, id, sectionId, options = {}) {
|
|
144
|
+
const maxTokens = validate(options);
|
|
145
|
+
if (typeof sectionId !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(sectionId) || sectionId.length > 512)
|
|
146
|
+
throw new DocumentationError('invalid_input', 'section must be an ID returned by the page outline');
|
|
147
|
+
const doc = await selectDocument(directory, id, options);
|
|
148
|
+
if (!Array.isArray(doc.sections))
|
|
149
|
+
throw new DocumentationError('missing_index', 'Section outline is unavailable; rebuild the documentation.');
|
|
150
|
+
const section = doc.sections.find((item) => item.id === sectionId);
|
|
151
|
+
if (!section)
|
|
152
|
+
throw new DocumentationError('unknown_section', `No section ${sectionId}; read the page outline first.`);
|
|
153
|
+
const content = await readMarkdown(directory, section.markdownUrl);
|
|
154
|
+
if (section.parentDigest !== doc.digest || section.parentMarkdownUrl !== doc.markdownUrl || createHash('sha256').update(content).digest('hex') !== section.digest)
|
|
155
|
+
throw new DocumentationError('internal_error', 'Section or parent metadata is stale; rebuild the documentation.');
|
|
156
|
+
const result = budget({ status: 'ok', id: doc.id, publication: doc.variant, section, content }, maxTokens, false);
|
|
157
|
+
if (!result.fits)
|
|
158
|
+
throw new DocumentationError('invalid_input', 'maxTokens is too small for the complete section. Increase the budget; sections are never cut mid-block.');
|
|
159
|
+
return result.result;
|
|
160
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type Chunk, type ChunkIndex } from './index-build.js';
|
|
2
|
+
import { type Transport } from './provider-http.js';
|
|
3
|
+
export type EmbedInputType = 'document' | 'query';
|
|
4
|
+
export type EmbeddingProvider = {
|
|
5
|
+
name: string;
|
|
6
|
+
model: string;
|
|
7
|
+
dimension: number;
|
|
8
|
+
space: string;
|
|
9
|
+
backend?: string;
|
|
10
|
+
documentFingerprint?: string;
|
|
11
|
+
embed(texts: string[], inputType: EmbedInputType, signal?: AbortSignal): Promise<Float32Array[]>;
|
|
12
|
+
};
|
|
13
|
+
export type VoyageOptions = Transport & {
|
|
14
|
+
model: string;
|
|
15
|
+
dimension?: number;
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
keyEnv?: string;
|
|
18
|
+
endpoint?: string;
|
|
19
|
+
batchSize?: number;
|
|
20
|
+
};
|
|
21
|
+
export declare function voyageProvider(options: VoyageOptions): EmbeddingProvider;
|
|
22
|
+
export type OpenAIOptions = Transport & {
|
|
23
|
+
model: string;
|
|
24
|
+
dimension?: number;
|
|
25
|
+
apiKey?: string;
|
|
26
|
+
keyEnv?: string;
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
batchSize?: number;
|
|
29
|
+
queryPrefix?: string;
|
|
30
|
+
documentPrefix?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function openAIProvider(options: OpenAIOptions): EmbeddingProvider;
|
|
33
|
+
export type ProviderConfig = Partial<OpenAIOptions & VoyageOptions> & {
|
|
34
|
+
provider?: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function createProvider(config?: ProviderConfig): EmbeddingProvider;
|
|
37
|
+
export type EmbeddingsMeta = {
|
|
38
|
+
schemaVersion: 1;
|
|
39
|
+
provider: string;
|
|
40
|
+
model: string;
|
|
41
|
+
space?: string;
|
|
42
|
+
dimension: number;
|
|
43
|
+
sourceDigest: string;
|
|
44
|
+
ids: string[];
|
|
45
|
+
digests: string[];
|
|
46
|
+
backend?: string;
|
|
47
|
+
documentFingerprint?: string;
|
|
48
|
+
inputDigests?: string[];
|
|
49
|
+
};
|
|
50
|
+
export type StoredEmbeddings = {
|
|
51
|
+
meta: EmbeddingsMeta;
|
|
52
|
+
vectors: Float32Array[];
|
|
53
|
+
};
|
|
54
|
+
export declare function writeEmbeddings(directory: string, meta: EmbeddingsMeta, vectors: Float32Array[]): Promise<{
|
|
55
|
+
metaFile: string;
|
|
56
|
+
vectorFile: string;
|
|
57
|
+
bytes: number;
|
|
58
|
+
}>;
|
|
59
|
+
export declare function readEmbeddings(directory: string): Promise<StoredEmbeddings | undefined>;
|
|
60
|
+
export type EmbedResult = {
|
|
61
|
+
passages: number;
|
|
62
|
+
embedded: number;
|
|
63
|
+
reused: number;
|
|
64
|
+
model: string;
|
|
65
|
+
dimension: number;
|
|
66
|
+
bytes: number;
|
|
67
|
+
};
|
|
68
|
+
export declare function embedChunkIndex(directory: string, index: ChunkIndex, provider: EmbeddingProvider, input: (chunk: Chunk) => string): Promise<EmbedResult>;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { readFile, realpath, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { aiDirectory } from './index-build.js';
|
|
5
|
+
import { postJson, throwIfCancelled } from './provider-http.js';
|
|
6
|
+
const hash = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
7
|
+
function normalize(vector) {
|
|
8
|
+
let length = 0;
|
|
9
|
+
for (const value of vector)
|
|
10
|
+
length = Math.hypot(length, value);
|
|
11
|
+
return length ? vector.map(value => value / length) : vector;
|
|
12
|
+
}
|
|
13
|
+
function validVector(vector) {
|
|
14
|
+
let length = 0;
|
|
15
|
+
for (const value of vector) {
|
|
16
|
+
if (!Number.isFinite(value))
|
|
17
|
+
return false;
|
|
18
|
+
length = Math.hypot(length, value);
|
|
19
|
+
}
|
|
20
|
+
return length > 0;
|
|
21
|
+
}
|
|
22
|
+
// Both APIs answer `{ data: [{ index, embedding }] }`. Check the answer matches the request before trusting it.
|
|
23
|
+
function vectorsFrom(label, body, count, dimension, model) {
|
|
24
|
+
const invalid = () => new Error(`${label} returned an unexpected response for model ${model} at ${dimension} dimensions`);
|
|
25
|
+
const rows = body?.data;
|
|
26
|
+
if (!Array.isArray(rows) || rows.length !== count)
|
|
27
|
+
throw invalid();
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
const vectors = rows.map((row) => {
|
|
30
|
+
if (!row || typeof row !== 'object')
|
|
31
|
+
throw invalid();
|
|
32
|
+
const { index, embedding } = row;
|
|
33
|
+
if (!Number.isInteger(index) || index < 0 || index >= count || seen.has(index) || !Array.isArray(embedding) || embedding.length !== dimension || embedding.some(value => typeof value !== 'number' || !Number.isFinite(value)))
|
|
34
|
+
throw invalid();
|
|
35
|
+
seen.add(index);
|
|
36
|
+
const vector = Float32Array.from(embedding);
|
|
37
|
+
if (!validVector(vector))
|
|
38
|
+
throw invalid();
|
|
39
|
+
return { index: index, vector: normalize(vector) };
|
|
40
|
+
});
|
|
41
|
+
return vectors.sort((a, b) => a.index - b.index).map(row => row.vector);
|
|
42
|
+
}
|
|
43
|
+
function batched(texts, size, request, signal) {
|
|
44
|
+
return (async () => {
|
|
45
|
+
const vectors = [];
|
|
46
|
+
for (let start = 0; start < texts.length; start += size) {
|
|
47
|
+
throwIfCancelled(signal);
|
|
48
|
+
vectors.push(...await request(texts.slice(start, start + size)));
|
|
49
|
+
}
|
|
50
|
+
return vectors;
|
|
51
|
+
})();
|
|
52
|
+
}
|
|
53
|
+
function checkBatch(size, limit, label) {
|
|
54
|
+
if (!Number.isSafeInteger(size) || size < 1 || size > limit)
|
|
55
|
+
throw new Error(`${label} batch size must be an integer from 1 to ${limit}`);
|
|
56
|
+
return size;
|
|
57
|
+
}
|
|
58
|
+
const voyageDimensions = [256, 512, 1024, 2048];
|
|
59
|
+
const voyageSharedSpace = new Set(['voyage-4-large', 'voyage-4', 'voyage-4-lite', 'voyage-4-nano']);
|
|
60
|
+
const voyageSpace = (model) => voyageSharedSpace.has(model) ? 'voyage-4' : model;
|
|
61
|
+
// Voyage separates queries from documents with `input_type`. The key comes from VOYAGE_API_KEY, or from the variable `keyEnv`
|
|
62
|
+
// names, and is never stored.
|
|
63
|
+
export function voyageProvider(options) {
|
|
64
|
+
const model = options.model;
|
|
65
|
+
if (!model)
|
|
66
|
+
throw new Error('Choose a Voyage model, for example voyage-4-large');
|
|
67
|
+
const dimension = options.dimension ?? 1024;
|
|
68
|
+
if (!voyageDimensions.includes(dimension))
|
|
69
|
+
throw new Error(`Voyage dimension must be one of ${voyageDimensions.join(', ')}`);
|
|
70
|
+
const batchSize = checkBatch(options.batchSize ?? 64, 1000, 'Voyage');
|
|
71
|
+
const endpoint = options.endpoint ?? 'https://api.voyageai.com/v1/embeddings';
|
|
72
|
+
return {
|
|
73
|
+
name: 'voyage', model, dimension, space: voyageSpace(model), backend: hash(endpoint), documentFingerprint: hash(['voyage', endpoint, model, dimension, 'document', true]),
|
|
74
|
+
embed(texts, inputType, signal) {
|
|
75
|
+
return batched(texts, batchSize, async (input) => {
|
|
76
|
+
const keyEnv = options.keyEnv ?? 'VOYAGE_API_KEY';
|
|
77
|
+
const apiKey = options.apiKey ?? process.env[keyEnv];
|
|
78
|
+
if (!apiKey)
|
|
79
|
+
throw new Error(`Set ${keyEnv} to embed text`);
|
|
80
|
+
const body = await postJson('Voyage', endpoint, { authorization: `Bearer ${apiKey}` }, { input, model, input_type: inputType, output_dimension: dimension, truncation: true }, { ...options, signal });
|
|
81
|
+
return vectorsFrom('Voyage', body, input.length, dimension, model);
|
|
82
|
+
}, signal);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const openAIDimensions = { 'text-embedding-3-small': 1536, 'text-embedding-3-large': 3072, 'text-embedding-ada-002': 1536 };
|
|
87
|
+
// OpenAI's embeddings API and the many servers that copy it (Azure, OpenRouter, Ollama, vLLM, and others). It has no query or
|
|
88
|
+
// document distinction, so both use the same call, and each model is its own space. The optional prefixes serve models that
|
|
89
|
+
// expect one, such as `query: ` and `passage: `. Only `text-embedding-3` models accept a `dimensions` request.
|
|
90
|
+
export function openAIProvider(options) {
|
|
91
|
+
const model = options.model;
|
|
92
|
+
if (!model)
|
|
93
|
+
throw new Error('Choose an embedding model, for example text-embedding-3-small');
|
|
94
|
+
const dimension = options.dimension ?? openAIDimensions[model];
|
|
95
|
+
if (!dimension)
|
|
96
|
+
throw new Error(`Pass a dimension for model ${model}; only OpenAI's own models have a known default`);
|
|
97
|
+
if (!Number.isSafeInteger(dimension) || dimension < 1)
|
|
98
|
+
throw new Error('Dimension must be a positive integer');
|
|
99
|
+
const batchSize = checkBatch(options.batchSize ?? 64, 2048, 'OpenAI');
|
|
100
|
+
const baseUrl = (options.baseUrl ?? 'https://api.openai.com/v1').replace(/\/+$/, '');
|
|
101
|
+
const local = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:|\/|$)/.test(baseUrl);
|
|
102
|
+
const keyEnv = options.keyEnv ?? 'OPENAI_API_KEY';
|
|
103
|
+
const sendDimensions = model.startsWith('text-embedding-3');
|
|
104
|
+
return {
|
|
105
|
+
name: 'openai', model, dimension, space: model, backend: hash(baseUrl), documentFingerprint: hash(['openai', baseUrl, model, dimension, options.documentPrefix ?? '', sendDimensions]),
|
|
106
|
+
embed(texts, inputType, signal) {
|
|
107
|
+
const prefix = inputType === 'query' ? options.queryPrefix : options.documentPrefix;
|
|
108
|
+
return batched(texts, batchSize, async (input) => {
|
|
109
|
+
const apiKey = options.apiKey ?? process.env[keyEnv];
|
|
110
|
+
if (!apiKey && !local)
|
|
111
|
+
throw new Error(`Set ${keyEnv} to embed text`);
|
|
112
|
+
const body = await postJson('Embeddings API', `${baseUrl}/embeddings`, apiKey ? { authorization: `Bearer ${apiKey}` } : {}, { input: prefix ? input.map(text => prefix + text) : input, model, encoding_format: 'float', ...(sendDimensions ? { dimensions: dimension } : {}) }, { ...options, signal });
|
|
113
|
+
return vectorsFrom('Embeddings API', body, input.length, dimension, model);
|
|
114
|
+
}, signal);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export function createProvider(config = {}) {
|
|
119
|
+
const { provider, model } = config;
|
|
120
|
+
if (!provider)
|
|
121
|
+
throw new Error('Choose an embedding provider: voyage or openai');
|
|
122
|
+
if (!model)
|
|
123
|
+
throw new Error(`Choose an embedding model for ${provider}`);
|
|
124
|
+
if (provider === 'voyage') {
|
|
125
|
+
if (config.baseUrl)
|
|
126
|
+
throw new Error('baseUrl applies only to the openai provider');
|
|
127
|
+
return voyageProvider({ ...config, model });
|
|
128
|
+
}
|
|
129
|
+
if (provider === 'openai')
|
|
130
|
+
return openAIProvider({ ...config, model });
|
|
131
|
+
throw new Error(`Unknown embedding provider ${provider}; use voyage or openai`);
|
|
132
|
+
}
|
|
133
|
+
// Vectors are unit-length float32, one row per passage in `ids` order. The metadata file says which model made them and which
|
|
134
|
+
// passage text each row was made from, so a vector is never reused for text that changed.
|
|
135
|
+
export async function writeEmbeddings(directory, meta, vectors) {
|
|
136
|
+
if (vectors.length !== meta.ids.length || meta.digests.length !== meta.ids.length || (meta.inputDigests && meta.inputDigests.length !== meta.ids.length) || vectors.some(vector => vector.length !== meta.dimension || !validVector(vector)))
|
|
137
|
+
throw new Error('Embeddings do not match their metadata');
|
|
138
|
+
const target = await aiDirectory(directory);
|
|
139
|
+
const buffer = Buffer.alloc(vectors.length * meta.dimension * 4);
|
|
140
|
+
vectors.forEach((vector, row) => Buffer.from(vector.buffer, vector.byteOffset, vector.byteLength).copy(buffer, row * meta.dimension * 4));
|
|
141
|
+
const vectorFile = path.join(target, 'embeddings.bin');
|
|
142
|
+
const metaFile = path.join(target, 'embeddings.json');
|
|
143
|
+
await writeFile(vectorFile, buffer);
|
|
144
|
+
await writeFile(metaFile, JSON.stringify(meta) + '\n');
|
|
145
|
+
return { metaFile, vectorFile, bytes: buffer.length };
|
|
146
|
+
}
|
|
147
|
+
export async function readEmbeddings(directory) {
|
|
148
|
+
const target = path.join(path.resolve(directory), 'ai');
|
|
149
|
+
try {
|
|
150
|
+
const real = await realpath(target);
|
|
151
|
+
const root = await realpath(path.resolve(directory));
|
|
152
|
+
if (real !== root && !real.startsWith(root + path.sep))
|
|
153
|
+
throw new Error('The ai directory leaves the site directory');
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (error.code === 'ENOENT')
|
|
157
|
+
return undefined;
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
let meta;
|
|
161
|
+
let bytes;
|
|
162
|
+
try {
|
|
163
|
+
meta = JSON.parse(await readFile(path.join(target, 'embeddings.json'), 'utf8'));
|
|
164
|
+
bytes = await readFile(path.join(target, 'embeddings.bin'));
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (error.code === 'ENOENT')
|
|
168
|
+
return undefined;
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
if (meta?.schemaVersion !== 1 || !Number.isSafeInteger(meta.dimension) || meta.dimension < 1 || !Array.isArray(meta.ids) || !Array.isArray(meta.digests) || meta.ids.length !== meta.digests.length || (meta.inputDigests !== undefined && (!Array.isArray(meta.inputDigests) || meta.inputDigests.length !== meta.ids.length)))
|
|
172
|
+
throw new Error('Unsupported or invalid embeddings.json');
|
|
173
|
+
if (bytes.length !== meta.ids.length * meta.dimension * 4)
|
|
174
|
+
throw new Error('embeddings.bin does not match embeddings.json; run shoalrun index --embed again');
|
|
175
|
+
const floats = new Float32Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
176
|
+
meta.space ??= meta.provider === 'voyage' ? voyageSpace(meta.model) : meta.model;
|
|
177
|
+
const vectors = meta.ids.map((_, row) => floats.slice(row * meta.dimension, (row + 1) * meta.dimension));
|
|
178
|
+
if (vectors.some(vector => !validVector(vector)))
|
|
179
|
+
throw new Error('embeddings.bin contains invalid vectors; run shoalrun index --embed again');
|
|
180
|
+
return { meta, vectors };
|
|
181
|
+
}
|
|
182
|
+
// Embed every passage that has no current vector. Reuse requires the same provider, model, vector space, dimension, and
|
|
183
|
+
// passage digest, so editing one page re-embeds that page's passages and nothing else.
|
|
184
|
+
export async function embedChunkIndex(directory, index, provider, input) {
|
|
185
|
+
const stored = await readEmbeddings(directory).catch(() => undefined);
|
|
186
|
+
const inputs = index.chunks.map(input);
|
|
187
|
+
const inputDigests = inputs.map(text => hash(text));
|
|
188
|
+
const known = new Map();
|
|
189
|
+
if (stored && stored.meta.provider === provider.name && stored.meta.model === provider.model && stored.meta.space === provider.space && stored.meta.dimension === provider.dimension && stored.meta.backend === provider.backend && stored.meta.documentFingerprint === provider.documentFingerprint && stored.meta.inputDigests)
|
|
190
|
+
stored.meta.inputDigests.forEach((digest, row) => known.set(digest, stored.vectors[row]));
|
|
191
|
+
const missing = [...new Map(inputDigests.map((digest, at) => [digest, inputs[at]]).filter(([digest]) => !known.has(digest))).values()];
|
|
192
|
+
const fresh = missing.length ? await provider.embed(missing, 'document') : [];
|
|
193
|
+
if (fresh.length !== missing.length)
|
|
194
|
+
throw new Error('Embedding provider returned the wrong number of vectors');
|
|
195
|
+
missing.forEach((item, at) => known.set(hash(item), fresh[at]));
|
|
196
|
+
const written = await writeEmbeddings(directory, { schemaVersion: 1, provider: provider.name, model: provider.model, space: provider.space, dimension: provider.dimension, sourceDigest: index.sourceDigest, ids: index.chunks.map(chunk => chunk.id), digests: index.chunks.map(chunk => chunk.digest), inputDigests, ...(provider.backend ? { backend: provider.backend } : {}), ...(provider.documentFingerprint ? { documentFingerprint: provider.documentFingerprint } : {}) }, inputDigests.map(digest => known.get(digest)));
|
|
197
|
+
return { passages: index.chunks.length, embedded: missing.length, reused: index.chunks.length - missing.length, model: provider.model, dimension: provider.dimension, bytes: written.bytes };
|
|
198
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const methods = new Set(['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']);
|
|
4
|
+
export async function auditFocusedContracts(siteDirectory) {
|
|
5
|
+
const root = path.resolve(siteDirectory);
|
|
6
|
+
const manifest = JSON.parse(await readFile(path.join(root, 'documents.json'), 'utf8'));
|
|
7
|
+
const pages = manifest.documents.filter((doc) => doc.focusedContractUrl);
|
|
8
|
+
const failures = [];
|
|
9
|
+
let totalBytes = 0;
|
|
10
|
+
let largestBytes = 0;
|
|
11
|
+
for (const page of pages) {
|
|
12
|
+
try {
|
|
13
|
+
const route = page.focusedContractUrl;
|
|
14
|
+
if (!/^\/api\/operations\/[A-Za-z0-9._/-]+\.json$/.test(route) || route.includes('..'))
|
|
15
|
+
throw new Error(`Unsafe focused contract route ${route}`);
|
|
16
|
+
const file = path.join(root, route.slice(1));
|
|
17
|
+
const contract = JSON.parse(await readFile(file, 'utf8'));
|
|
18
|
+
const bytes = (await stat(file)).size;
|
|
19
|
+
totalBytes += bytes;
|
|
20
|
+
largestBytes = Math.max(largestBytes, bytes);
|
|
21
|
+
if (!/^3\.(0|1)\./.test(String(contract.openapi)))
|
|
22
|
+
throw new Error('Missing supported OpenAPI version');
|
|
23
|
+
if (typeof contract.info?.title !== 'string' || typeof contract.info?.version !== 'string')
|
|
24
|
+
throw new Error('Missing OpenAPI info');
|
|
25
|
+
const paths = Object.entries(contract.paths ?? contract.webhooks ?? {});
|
|
26
|
+
if (contract.paths && contract.webhooks)
|
|
27
|
+
throw new Error('Contract must select paths or webhooks, not both');
|
|
28
|
+
if (paths.length !== 1 || Object.keys(paths[0][1]).filter(key => methods.has(key)).length !== 1)
|
|
29
|
+
throw new Error('Contract must contain exactly one operation');
|
|
30
|
+
const operation = Object.values(paths[0][1]).find(value => value?.operationId);
|
|
31
|
+
if (!operation)
|
|
32
|
+
throw new Error('Contract operation lacks operationId');
|
|
33
|
+
const walk = (value) => {
|
|
34
|
+
if (!value || typeof value !== 'object')
|
|
35
|
+
return;
|
|
36
|
+
if (Array.isArray(value)) {
|
|
37
|
+
value.forEach(walk);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (typeof value.$ref === 'string') {
|
|
41
|
+
const ref = value.$ref;
|
|
42
|
+
if (!ref.startsWith('#/components/'))
|
|
43
|
+
throw new Error(`External reference ${ref}`);
|
|
44
|
+
const resolved = ref.slice(2).split('/').reduce((at, segment) => at?.[segment.replaceAll('~1', '/').replaceAll('~0', '~')], contract);
|
|
45
|
+
if (resolved === undefined || resolved === null)
|
|
46
|
+
throw new Error(`Unresolved reference ${ref}`);
|
|
47
|
+
}
|
|
48
|
+
if (value.discriminator?.mapping) {
|
|
49
|
+
for (const target of Object.values(value.discriminator.mapping)) {
|
|
50
|
+
if (typeof target !== 'string' || !target.startsWith('#/components/'))
|
|
51
|
+
throw new Error(`Unresolved discriminator mapping ${target}`);
|
|
52
|
+
const resolved = target.slice(2).split('/').reduce((at, segment) => at?.[segment.replaceAll('~1', '/').replaceAll('~0', '~')], contract);
|
|
53
|
+
if (resolved === undefined || resolved === null)
|
|
54
|
+
throw new Error(`Missing discriminator target ${target}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
Object.values(value).forEach(walk);
|
|
58
|
+
};
|
|
59
|
+
walk(contract);
|
|
60
|
+
const markdown = await readFile(path.join(root, page.markdownUrl.slice(1)), 'utf8');
|
|
61
|
+
if (!markdown.includes(route))
|
|
62
|
+
throw new Error('Markdown page does not link to its contract');
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
failures.push({ documentId: page.id, message: error.message });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { status: failures.length ? 'error' : 'ok', contracts: pages.length, totalBytes, largestBytes, failures };
|
|
69
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const componentKinds = new Set(['schemas', 'responses', 'parameters', 'examples', 'requestBodies', 'headers', 'securitySchemes', 'links', 'callbacks', 'pathItems']);
|
|
4
|
+
const kindByField = { schema: 'schemas', schemas: 'schemas', parameters: 'parameters', requestBody: 'requestBodies', requestBodies: 'requestBodies', responses: 'responses', examples: 'examples', headers: 'headers', links: 'links', callbacks: 'callbacks', securitySchemes: 'securitySchemes', pathItems: 'pathItems' };
|
|
5
|
+
const escapePointer = (value) => value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
6
|
+
const unescapePointer = (value) => value.replaceAll('~1', '/').replaceAll('~0', '~');
|
|
7
|
+
const hash = (value) => createHash('sha256').update(value).digest('hex').slice(0, 10);
|
|
8
|
+
export async function focusedOpenApi(reader, sourceFile, sourceOperationId) {
|
|
9
|
+
const source = await reader.load(sourceFile);
|
|
10
|
+
const api = source.value;
|
|
11
|
+
if (!/^3\.(0|1)\./.test(String(api.openapi)))
|
|
12
|
+
throw new Error('Focused OpenAPI export supports 3.0.x and 3.1.x');
|
|
13
|
+
let match;
|
|
14
|
+
for (const [collection, webhook] of [[api.paths ?? {}, false], [api.webhooks ?? {}, true]])
|
|
15
|
+
for (const [route, initial] of Object.entries(collection)) {
|
|
16
|
+
const selected = initial?.$ref ? await reader.ref(source, initial.$ref) : { value: initial, source };
|
|
17
|
+
const pathItem = selected.value;
|
|
18
|
+
for (const [method, operation] of Object.entries(pathItem)) {
|
|
19
|
+
if (!['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'].includes(method) || operation?.operationId !== sourceOperationId)
|
|
20
|
+
continue;
|
|
21
|
+
if (match)
|
|
22
|
+
throw new Error(`Duplicate OpenAPI operationId ${sourceOperationId}`);
|
|
23
|
+
match = { route, method, pathItem, pathSource: selected.source, operation, webhook };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!match)
|
|
27
|
+
throw new Error(`Missing OpenAPI operationId ${sourceOperationId}`);
|
|
28
|
+
if (match.pathItem.$ref)
|
|
29
|
+
throw new Error(`Focused OpenAPI does not yet support Path Item references: ${sourceOperationId}`);
|
|
30
|
+
const components = {};
|
|
31
|
+
const names = new Map();
|
|
32
|
+
const register = async (from, reference, inferredKind) => {
|
|
33
|
+
const separator = reference.indexOf('#');
|
|
34
|
+
const relative = separator < 0 ? reference : reference.slice(0, separator);
|
|
35
|
+
const pointer = separator < 0 ? '' : reference.slice(separator + 1);
|
|
36
|
+
const parsed = /^\/components\/([^/]+)\/([^/]+)(\/.*)?$/.exec(pointer);
|
|
37
|
+
const declaredKind = parsed?.[1];
|
|
38
|
+
if (declaredKind && !componentKinds.has(declaredKind))
|
|
39
|
+
throw new Error(`Unsupported component kind ${declaredKind}`);
|
|
40
|
+
const kind = declaredKind ?? inferredKind;
|
|
41
|
+
if (!kind || !componentKinds.has(kind))
|
|
42
|
+
throw new Error(`Cannot classify focused OpenAPI reference ${reference}`);
|
|
43
|
+
const basePointer = parsed ? `/components/${parsed[1]}/${parsed[2]}` : pointer;
|
|
44
|
+
const baseReference = `${relative}#${basePointer}`;
|
|
45
|
+
const resolved = await reader.ref(from, baseReference);
|
|
46
|
+
const identity = `${resolved.source.file}#${basePointer}|${kind}`;
|
|
47
|
+
let target = names.get(identity);
|
|
48
|
+
if (!target) {
|
|
49
|
+
const originalName = parsed ? unescapePointer(parsed[2]) : '';
|
|
50
|
+
const portableIdentity = `${path.relative(path.dirname(source.file), resolved.source.file).split(path.sep).join('/')}#${basePointer}|${kind}`;
|
|
51
|
+
const localName = resolved.source.file === source.file && parsed ? originalName : `Focused_${hash(portableIdentity)}`;
|
|
52
|
+
if (!/^[A-Za-z0-9._-]+$/.test(localName))
|
|
53
|
+
throw new Error(`Invalid focused component name ${localName}`);
|
|
54
|
+
target = { kind, name: localName };
|
|
55
|
+
names.set(identity, target);
|
|
56
|
+
const bucket = (components[kind] ??= {});
|
|
57
|
+
if (bucket[localName] !== undefined)
|
|
58
|
+
throw new Error(`Focused component collision ${kind}/${localName}`);
|
|
59
|
+
bucket[localName] = null;
|
|
60
|
+
bucket[localName] = await walk(resolved.value, resolved.source, kind);
|
|
61
|
+
}
|
|
62
|
+
return `#/components/${target.kind}/${escapePointer(target.name)}${parsed?.[3] ?? ''}`;
|
|
63
|
+
};
|
|
64
|
+
const walk = async (value, from, kind) => {
|
|
65
|
+
if (Array.isArray(value))
|
|
66
|
+
return Promise.all(value.map(item => walk(item, from, kind)));
|
|
67
|
+
if (!value || typeof value !== 'object')
|
|
68
|
+
return value;
|
|
69
|
+
if (kind === 'schemas' && (value.$id || value.$anchor || value.$dynamicRef || value.$dynamicAnchor))
|
|
70
|
+
throw new Error(`Focused OpenAPI needs an adapter for JSON Schema IDs or anchors in ${from.file}`);
|
|
71
|
+
const result = {};
|
|
72
|
+
for (const [key, child] of Object.entries(value)) {
|
|
73
|
+
if (key.startsWith('x-'))
|
|
74
|
+
continue;
|
|
75
|
+
if (key === '$ref') {
|
|
76
|
+
if (typeof child !== 'string')
|
|
77
|
+
throw new Error(`Invalid $ref in ${from.file}`);
|
|
78
|
+
result.$ref = await register(from, child, kind);
|
|
79
|
+
}
|
|
80
|
+
else if (key === 'discriminator' && child?.mapping && kind === 'schemas') {
|
|
81
|
+
const mapping = {};
|
|
82
|
+
for (const [name, target] of Object.entries(child.mapping)) {
|
|
83
|
+
if (typeof target !== 'string')
|
|
84
|
+
throw new Error(`Invalid discriminator mapping ${name}`);
|
|
85
|
+
if (from.value.components?.schemas?.[target])
|
|
86
|
+
mapping[name] = await register(from, `#/components/schemas/${escapePointer(target)}`, 'schemas');
|
|
87
|
+
else if (target.includes('#') || target.startsWith('.') || target.includes('/') || /\.(?:ya?ml|json)$/i.test(target))
|
|
88
|
+
mapping[name] = await register(from, target, 'schemas');
|
|
89
|
+
else
|
|
90
|
+
throw new Error(`Unresolved discriminator mapping ${name}: ${target}`);
|
|
91
|
+
}
|
|
92
|
+
result[key] = { ...child, mapping };
|
|
93
|
+
}
|
|
94
|
+
else if (['example', 'default', 'enum', 'const', 'security'].includes(key) || (kind === 'examples' && key === 'value') || (kind === 'schemas' && key === 'examples')) {
|
|
95
|
+
// Payloads and requirement names are data, not OpenAPI objects.
|
|
96
|
+
result[key] = child;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const childKind = kind === 'schemas' ? 'schemas' : kindByField[key] ?? kind;
|
|
100
|
+
const isMap = kind === 'schemas'
|
|
101
|
+
? ['properties', 'patternProperties', '$defs', 'definitions', 'dependentSchemas'].includes(key)
|
|
102
|
+
: ['content', 'responses', 'examples', 'headers', 'links', 'callbacks', 'variables', 'flows', 'scopes'].includes(key);
|
|
103
|
+
// Map keys are user-defined names, including names such as x-trace or $ref.
|
|
104
|
+
result[key] = isMap && child && typeof child === 'object' && !Array.isArray(child)
|
|
105
|
+
? Object.fromEntries(await Promise.all(Object.entries(child).map(async ([name, entry]) => [name, await walk(entry, from, childKind)])))
|
|
106
|
+
: await walk(child, from, childKind);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
const { route, method, pathItem, pathSource, operation, webhook } = match;
|
|
112
|
+
const operationFields = ['tags', 'summary', 'description', 'externalDocs', 'operationId', 'parameters', 'requestBody', 'responses', 'deprecated', 'security', 'servers'];
|
|
113
|
+
const selectedOperation = Object.fromEntries(operationFields.filter(key => operation[key] !== undefined).map(key => [key, operation[key]]));
|
|
114
|
+
const selectedPath = Object.fromEntries(['summary', 'description', 'parameters', 'servers'].filter(key => pathItem[key] !== undefined).map(key => [key, pathItem[key]]));
|
|
115
|
+
const focusedPath = { ...await walk(selectedPath, pathSource), [method]: await walk(selectedOperation, pathSource) };
|
|
116
|
+
const effectiveSecurity = operation.security ?? api.security;
|
|
117
|
+
for (const requirement of effectiveSecurity ?? []) {
|
|
118
|
+
for (const name of Object.keys(requirement)) {
|
|
119
|
+
if (!api.components?.securitySchemes?.[name])
|
|
120
|
+
throw new Error(`Unknown security scheme ${name}`);
|
|
121
|
+
await register(source, `#/components/securitySchemes/${escapePointer(name)}`, 'securitySchemes');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const info = { title: String(api.info?.title ?? 'API'), version: String(api.info?.version ?? 'unspecified') };
|
|
125
|
+
const output = { openapi: api.openapi, info };
|
|
126
|
+
if (api.jsonSchemaDialect)
|
|
127
|
+
output.jsonSchemaDialect = api.jsonSchemaDialect;
|
|
128
|
+
if (!webhook && api.servers && !pathItem.servers && !operation.servers)
|
|
129
|
+
output.servers = await walk(api.servers, source);
|
|
130
|
+
if (operation.security === undefined && api.security !== undefined)
|
|
131
|
+
output.security = api.security;
|
|
132
|
+
if (Array.isArray(api.tags) && Array.isArray(operation.tags))
|
|
133
|
+
output.tags = api.tags.filter((tag) => operation.tags.includes(tag.name));
|
|
134
|
+
output[webhook ? 'webhooks' : 'paths'] = { [route]: focusedPath };
|
|
135
|
+
if (Object.keys(components).length)
|
|
136
|
+
output.components = Object.fromEntries(Object.entries(components).sort(([a], [b]) => a.localeCompare(b)).map(([kind, bucket]) => [
|
|
137
|
+
kind, Object.fromEntries(Object.entries(bucket).sort(([a], [b]) => a.localeCompare(b)))
|
|
138
|
+
]));
|
|
139
|
+
return output;
|
|
140
|
+
}
|