From 3bd40ddd4eb7aa6cdcb78cd20812ae200e8689c6 Mon Sep 17 00:00:00 2001 From: yuanhe Date: Thu, 1 Oct 2026 17:43:03 +0800 Subject: [PATCH 1/2] Add VitePress website with landing page and GitHub Pages deployment The website reads the existing docs/, contracts/ and docs.json in place: the sidebar, landing documentation index and llms.txt are generated from docs.json, page sources are published as .md copies, and README paths redirect to their section index. The landing page is bilingual and reuses the architecture diagrams and console screenshots from docs/assets. --- .github/workflows/website.yml | 91 ++ CONTRIBUTING.md | 1 + pnpm-lock.yaml | 1394 ++++++++++++++++- pnpm-workspace.yaml | 1 + scripts/ci_plan.py | 1 + scripts/ci_plan_test.py | 1 + website/.gitignore | 2 + website/.vitepress/config.mts | 179 +++ website/.vitepress/docs-nav.mts | 61 + website/.vitepress/env.d.ts | 1 + website/.vitepress/repository-links.mts | 29 + website/.vitepress/theme/Layout.vue | 15 + .../theme/components/AsciiCanvas.vue | 300 ++++ .../theme/components/ComposeLab.vue | 335 ++++ .../.vitepress/theme/components/Landing.vue | 1301 +++++++++++++++ .../theme/components/TypeTerminal.vue | 136 ++ website/.vitepress/theme/docs.data.mts | 49 + website/.vitepress/theme/index.ts | 15 + website/.vitepress/theme/landing-content.ts | 437 ++++++ website/.vitepress/theme/style.css | 97 ++ website/README.md | 26 + website/index.md | 8 + website/package.json | 20 + website/tests/dist.test.mjs | 47 + website/tests/docs-nav.test.mjs | 43 + website/tests/harnesses.test.mjs | 27 + website/zh/index.md | 8 + 27 files changed, 4618 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/website.yml create mode 100644 website/.gitignore create mode 100644 website/.vitepress/config.mts create mode 100644 website/.vitepress/docs-nav.mts create mode 100644 website/.vitepress/env.d.ts create mode 100644 website/.vitepress/repository-links.mts create mode 100644 website/.vitepress/theme/Layout.vue create mode 100644 website/.vitepress/theme/components/AsciiCanvas.vue create mode 100644 website/.vitepress/theme/components/ComposeLab.vue create mode 100644 website/.vitepress/theme/components/Landing.vue create mode 100644 website/.vitepress/theme/components/TypeTerminal.vue create mode 100644 website/.vitepress/theme/docs.data.mts create mode 100644 website/.vitepress/theme/index.ts create mode 100644 website/.vitepress/theme/landing-content.ts create mode 100644 website/.vitepress/theme/style.css create mode 100644 website/README.md create mode 100644 website/index.md create mode 100644 website/package.json create mode 100644 website/tests/dist.test.mjs create mode 100644 website/tests/docs-nav.test.mjs create mode 100644 website/tests/harnesses.test.mjs create mode 100644 website/zh/index.md diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml new file mode 100644 index 00000000..0405b949 --- /dev/null +++ b/.github/workflows/website.yml @@ -0,0 +1,91 @@ +# Builds the website (landing page and documentation) on every change to its +# sources, and publishes it to GitHub Pages from main. The website reads the +# existing docs/, contracts/ and docs.json in place; see website/README.md. +name: website + +on: + push: + branches: [main] + paths: + - website/** + - docs/** + - contracts/** + - docs.json + - package.json + - pnpm-lock.yaml + - pnpm-workspace.yaml + - .github/actions/node/** + - .github/workflows/website.yml + pull_request: + paths: + - website/** + - docs/** + - contracts/** + - docs.json + - package.json + - pnpm-lock.yaml + - pnpm-workspace.yaml + - .github/actions/node/** + - .github/workflows/website.yml + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: website-${{ github.ref }} + # Never cancel a run that may be deploying main. + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + build: + runs-on: ${{ vars.OAC_USE_GITHUB_RUNNERS == 'true' && 'ubuntu-24.04' || 'blacksmith-2vcpu-ubuntu-2404' }} + timeout-minutes: 10 + permissions: + contents: read + # configure-pages reads the site's base path. + pages: read + env: + PUBLISH: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/main' }} + steps: + - uses: actions/checkout@v7 + with: + # Full history gives each page its last-updated date. + fetch-depth: 0 + - uses: ./.github/actions/node + - name: Install website dependencies + run: pnpm install --frozen-lockfile --filter @oac/website... + - name: Read the Pages base path + id: pages + if: env.PUBLISH == 'true' + uses: actions/configure-pages@v6 + - name: Build + run: pnpm --filter @oac/website build + env: + WEBSITE_BASE: ${{ steps.pages.outputs.base_path }} + - name: Test navigation and build output + run: pnpm --filter @oac/website test + - name: Upload the site + if: env.PUBLISH == 'true' + uses: actions/upload-pages-artifact@v5 + with: + path: website/.vitepress/dist + + deploy: + needs: build + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + runs-on: ${{ vars.OAC_USE_GITHUB_RUNNERS == 'true' && 'ubuntu-24.04' || 'blacksmith-2vcpu-ubuntu-2404' }} + timeout-minutes: 10 + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + concurrency: + group: website-pages + cancel-in-progress: false + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 582a014e..d2546942 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,6 +31,7 @@ This guide owns how to work in the repository: documentation ownership, the repo | Runtime telemetry responses | [Runtime telemetry API](contracts/agents-api/runtime-observability-api.md) | | Runtime observation, sampling, retention and export | [Runtime observability](contracts/agents-api/runtime-observability.md) | | Distribution builds, Runtime image builds, CI and publication | [Maintainer guide](docs/maintainers.md) | +| Website: landing page, documentation site build and GitHub Pages publication | [Website guide](website/README.md) | | Self-hosted Runtime installation, recovery and local operation | [Self-hosted execution](docs/getting-started/self-hosted.md) | | Installer lifecycle, locking, generated state, managed HTTPS and downloads | [Installer design rules](deploy/install/README.md) | | Operator installation and alternatives | [Installation](docs/getting-started/install.md), [installation options](docs/getting-started/install-options.md) | diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96ca785e..745f56d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -256,8 +256,100 @@ importers: specifier: 0.28.2 version: 0.28.2 + website: + dependencies: + '@fontsource-variable/geist-mono': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource-variable/inter': + specifier: ^5.3.0 + version: 5.3.0 + devDependencies: + vitepress: + specifier: ^1.6.4 + version: 1.6.4(@algolia/client-search@5.59.0)(@types/node@26.1.0)(lightningcss@1.33.0)(postcss@8.5.28)(search-insights@2.17.3)(typescript@5.9.3) + vue: + specifier: ^3.5.31 + version: 3.5.43(typescript@5.9.3) + packages: + '@algolia/abtesting@1.25.0': + resolution: {integrity: sha512-rSTin9Uta23uaewYVQEp8XI9T3iA/zrg0/1G2vhf8oFFDxFL5vybnZ5IQwsVAg4JpKxPX4/WYNKdcfWrZymk7w==} + engines: {node: '>= 14.0.0'} + + '@algolia/autocomplete-core@1.17.7': + resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7': + resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.17.7': + resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.17.7': + resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.59.0': + resolution: {integrity: sha512-bm2XN0hCSMYwStSsCBT0/PUB2BDxoyR1Lnub3c392HMEy9bi8PUSW8vR6zltVEKWG2t4PQFWMD5C07bmJxGgPg==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.59.0': + resolution: {integrity: sha512-XOFPOTa69WuqHR6c5tMgnUUwwqQgNSzMpxmhrgA9KmxRf8WIqEa0cokHJvohk5CYb7CZx0xeSL6Bk2IUJ7Lv7Q==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.59.0': + resolution: {integrity: sha512-PC8ipLOYFKRTfIUY1J3FJxS6ryzWziaXmIX9/sNMoUR8L+XhF7hX2QAeUI87YVl5zujvlYTSOb+HAIqKXDjyHQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.59.0': + resolution: {integrity: sha512-yFNcCMM5fHiyoR0HuxMrzy+VjDcmhFUZTm2IJ2DHwGsVH3B5SEob4zTmeEZ3j/AZqNnmrJOCKl/tJnBg7+84bA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.59.0': + resolution: {integrity: sha512-GYja6HkDt2VrQhWmB2cLx3Z5fDwI9no7q+xwCWcFrTPm5CLH9QZvK0XLO9co1FcNIDxxkMaP3AXM39/XcecEOg==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.59.0': + resolution: {integrity: sha512-Wofg7bMpWh8N5qDDZs0wy6whc+KMmdNsxgrIGp9Ug2s1Bka0uq7AjyckdxReKPHLA0Q1qH8X/SNh0wn2t2X2Lw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.59.0': + resolution: {integrity: sha512-fHnALZfbEnODczGk14Y/1YBRApp6UEpZUTexGcMUPzY7RDc7q4HN2Y6jh0KUG8Jo9B+q+wOoQEc3ziR0ErbuFg==} + engines: {node: '>= 14.0.0'} + + '@algolia/ingestion@1.59.0': + resolution: {integrity: sha512-Fa38s1mHgoaLCT117sfJ6P78rtxUt93CYxBYpq1VOIcslaF+cH+1h5uikAPsKfxLnsuEDZHiBiaI6eNPTsQMRA==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.59.0': + resolution: {integrity: sha512-NyNsRSqM2tF1MX7ZGw/j4rduoEJiQ5wfvbSo/CFVdEzYCjyxbGFRPOZyS/GETJG9rk1TdHOq8NZqKqk/u1fm4g==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.59.0': + resolution: {integrity: sha512-nXBK2uygWtvbCOffMWqqfjjcEtp9enDKY5/2Pw/Hhw8VVaOyK2ThbGK04bNX/Le+qoK1VHYkodWGVTKfw0Otkw==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.59.0': + resolution: {integrity: sha512-yb+4afX/zja8QwX0KmV4/ae2kyYkRknsE79CRusYS2U5D8qwn2wq0cn1x12f3tm3F3+5FgmDdbuGdQTuCLSKMQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.59.0': + resolution: {integrity: sha512-Lp52TmpA1QtNmdHzs505Xwf4tgII7RAapkDSF9AAtWIBETcGaTIaXci4Rd9nS6SdaWK1BWTSqAPp5wzh6UN3Ag==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.59.0': + resolution: {integrity: sha512-YYHLEs5rC6oRTFwT7bJaKBR9NSFzikeVHGAT1ffATmUQzR8XqyhHXjoieAUoR7fBU6I5cfy1FVeh5GoRgriuzA==} + engines: {node: '>= 14.0.0'} + '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.269': resolution: {integrity: sha512-o7vdVlbJjkX9RL7+Mwa6owEGFvHNVfbNRRpoYSJ0jzyvimxKa+8kSjwZo7nYBRr+5WydxB/ApSE+xrWflDUhKQ==} cpu: [arm64] @@ -319,10 +411,27 @@ packages: zod: optional: true + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@base-ui/react@1.8.0': resolution: {integrity: sha512-P0/1sxo6SBVZOklKMIedvTWqw2s2IQzi9x5bIVsXu980cuSOD4NeuRSs+/L7LZQfDkZP/uRZyGPyfFl/B1oH+Q==} engines: {node: '>=14.0.0'} @@ -350,102 +459,227 @@ packages: '@types/react': optional: true + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + + '@docsearch/js@3.8.2': + resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} + + '@docsearch/react@3.8.2': + resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} + peerDependencies: + '@types/react': '>= 16.8.0 < 19.0.0' + react: '>= 16.8.0 < 19.0.0' + react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.28.2': resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.28.2': resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.28.2': resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.28.2': resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.28.2': resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.28.2': resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.28.2': resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.28.2': resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.28.2': resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.28.2': resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.28.2': resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.28.2': resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.28.2': resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.28.2': resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.28.2': resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.28.2': resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.28.2': resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} engines: {node: '>=18'} @@ -458,6 +692,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.28.2': resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} engines: {node: '>=18'} @@ -470,6 +710,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.28.2': resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} engines: {node: '>=18'} @@ -482,24 +728,48 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.28.2': resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.28.2': resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.28.2': resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.28.2': resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} engines: {node: '>=18'} @@ -533,6 +803,12 @@ packages: peerDependencies: hono: ^4 + '@iconify-json/simple-icons@1.2.98': + resolution: {integrity: sha512-35CEBG1H/3nsohVgJaRWtOZ/P5syRTL++ZXTISdQSC76o1mAFXngFoSm9EOnnBiDApKPu8WN/DY+z69yCycL9A==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -559,6 +835,13 @@ packages: '@cfworker/json-schema': optional: true + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + libc: [glibc] + '@number-flow/react@0.6.2': resolution: {integrity: sha512-WjZuV4aA+vhRCgCF+adGLgFVVAJ8vdvq6EchRT2FiBIgElTXtDOA3YgkcMr9UHpvpS9v4H70AB5wZv0D9jc3QA==} peerDependencies: @@ -1166,6 +1449,168 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/rollup-android-arm-eabi@4.63.6': + resolution: {integrity: sha512-G6xF9OVRWsbHadMfoPNUDwW/Jt70QYG+ZDUvtYCFxjf3SPhqVAO0u9ShXZ7Cf5silVoRWyN4bDjCEKnJkXkw0w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.63.6': + resolution: {integrity: sha512-Us/kTH5e2anr1CQvO8MEq4eeCVcIXI3Ag7OFxDI30n2OW7Q7xWuxo8ItbYq28PG2Sm1NWmZzG9+SqVEaj25SFA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.63.6': + resolution: {integrity: sha512-fwaSNrSHp9PJuEh4bKtd0mYC+G3FeVDjSTmsSiSlIYu7TFCpxrfB6lk7Ao8cIbswFyklPB1ZDUV8LkLBM15VWw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.63.6': + resolution: {integrity: sha512-QR2tx26gCeGh4eAGnUasfnJuRc+d7Vc0Jo1/fYtKGuiKqCuhbVpbNEvqgYGBwGDZGyS04jj+zdHi9W8uvb1Sdw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.63.6': + resolution: {integrity: sha512-7E1wRJEW8r7WlqmCy58ugndWHrzoEV/pRt2x0D9el7Inra62+Cw00lYW9XhW6iUCtFjhNw49McsSCIzJ/nG8uw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.63.6': + resolution: {integrity: sha512-13KJeF+vDswMzEHmMIouCVcEQdov3CzAA3ZhM8QFmBJV/FZ/VMZI49UHDvPXTK+wokk86kYPmQ9Qb3xGGJbZoQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.63.6': + resolution: {integrity: sha512-hV+W1r8HM84PER4If94QuzrrD0DNk4CxobNhVUiz9ZiiWHDzJss4Jra2AYGH1a7GOTzjS1sIyZ35yYEhw/aq+g==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.63.6': + resolution: {integrity: sha512-5dWs/GENZufRph8PEfl0TrNH/R3DHJA+VxF/dqlfV2oUZfxRtN3qmFXg2DZj8DYM8d+Vd3dgZtuVmoBE/nb2YQ==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.63.6': + resolution: {integrity: sha512-inQYLPVIUvYk+s7zpxQldqwrajUNs+t4TJjPGzsdPTvsT8gsQXVcW/sQFolHZ3YSWVk0nMMwU7TtRi0Ow+QMuw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.63.6': + resolution: {integrity: sha512-sH+0MV1HmDC1q7+bQnlN9ywKBmHqkA2EdD/hHYrrz654ybc+vNC1FRNyzuYKicMQVtZZ4X2jz0osJYmsOtnj4g==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.63.6': + resolution: {integrity: sha512-mCECvRr6HGekdBc6dvHzUWubqEti4JyOqlNePrOzl4Wimk26iW5Zy9rld2mQbC9id1NYjfF8nHLFq5fr3CuOKQ==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.63.6': + resolution: {integrity: sha512-bb9rdoCPwM4DEsbOP3CJgFnvhSJ3XKCUYyUhxnlSs/XGT0DknpOYgLCPbK8VUzwl+cZGzFQm0KbBYRaSGZC2YQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.63.6': + resolution: {integrity: sha512-aIJRoGCHev45JSu7JMzVrMzu1NX3nQrXx1Vb/6y4AblxW7JpCMpk4e8C6lHzvSElxi3UEcMdFJldsuWQjV1ddA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.63.6': + resolution: {integrity: sha512-LvjnulezHjiaM5Ga9MPCDLTVddjvcrJhZc6vEz9fxeFRGPJsPAX9HuTuPTUQ3l8d+nYpZB/xWzUpuKJlLQm3VQ==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.63.6': + resolution: {integrity: sha512-YmhSBeYZwJ937s8tKpYAhms48N+tj7e+oFkbkVVxfMRdAiynK2AxqRapJXB4utyXxc/iYTkMfPRQ+7ZP0IO78w==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.63.6': + resolution: {integrity: sha512-9+YukhzqTvJvzfDXJJzkURK9TDY01nofNH3pyHGuucGC4xPvBVtIfCAqJjkjnmBEGJYNXB8sBc2EDt7xDgGenw==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.63.6': + resolution: {integrity: sha512-9G5AtEkpN/A89BILHHEL9aYRYTuFjwa8PUQqoEg8SDrwOZBKpwYqNlQCBNI5QPwGrf7qFlb6LNFilo8FCSA3Wg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.63.6': + resolution: {integrity: sha512-Ezx2E5D6Siz805j41x91JiGbzuCm8i2U98U37SQ7ytY737+wsJq+P9nPL6UmqDww491zO7MTivYOTrtmTTeCSw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.63.6': + resolution: {integrity: sha512-8EZ1Q3PB7yUjvR4MbFAnCBF1wF0XRe5QQqbtlJXP7ZX5C8iYlSQJugUhj9oS0gbUP4jp9qGhaIgFnV9Nvod86Q==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.63.6': + resolution: {integrity: sha512-3JI27TALItfZ/qaxXKdbRXfV6WepUoUa3IN07FE+1xxoRdl3JIyrp5PiDF0vOHsPgEiOFkF8s3B3BRymQkdwGA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.63.6': + resolution: {integrity: sha512-BNOGeNFNRZDT7Hfc7b9JbTZFXkpAXeXl/CZWtxdd5CNfBaor/0RkJjg+q+E2gwVIvGqqp/NclThKFGrhuScVhw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.63.6': + resolution: {integrity: sha512-D3eEyLSIim5WF5fHbwnx+ryk0delN7fJyBrDTO3wVCCNBZ/cA8aCGwLS3SiuK+vRI4E7dqikAlStGuR20dBMuw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.63.6': + resolution: {integrity: sha512-xwbs9g8zLbHzj1poG1uh27xtubsu27Gk2nuN6kvvGDybjiTfwMksQAnF+phlvI5oeXkJWWDc6kg1t3XpgUYvrg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.63.6': + resolution: {integrity: sha512-Tr6rhRNnFthvXr45zWVZQ5P8jZ5UwZnXNc7Qnu80cP/VVYtSioOPmvv7wP6UMgRfkBCJr2Pm60ua0YBDSJjgnw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.63.6': + resolution: {integrity: sha512-0RAxO1pS/6lY1mSoD2Apm+6aPyfUlm+OWKgncIdj/eVN9I+pDY3lKgD7xsUNFei1u7MZQETGOo6m8POXMDp53A==} + cpu: [x64] + os: [win32] + + '@shikijs/core@2.5.0': + resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} + + '@shikijs/engine-javascript@2.5.0': + resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} + + '@shikijs/engine-oniguruma@2.5.0': + resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + + '@shikijs/langs@2.5.0': + resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + + '@shikijs/themes@2.5.0': + resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + + '@shikijs/transformers@2.5.0': + resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} + + '@shikijs/types@2.5.0': + resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@silvia-odwyer/photon-node@0.3.4': resolution: {integrity: sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==} @@ -1314,9 +1759,18 @@ packages: '@types/hast@3.0.5': resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.2.0': + resolution: {integrity: sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -1337,6 +1791,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + '@ungap/structured-clone@1.4.0': resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} @@ -1356,6 +1813,13 @@ packages: oxc-transform-react: optional: true + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + '@vitest/expect@4.1.11': resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} @@ -1385,6 +1849,92 @@ packages: '@vitest/utils@4.1.11': resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + '@vue/compiler-core@3.5.43': + resolution: {integrity: sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==} + + '@vue/compiler-dom@3.5.43': + resolution: {integrity: sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==} + + '@vue/compiler-sfc@3.5.43': + resolution: {integrity: sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==} + + '@vue/compiler-ssr@3.5.43': + resolution: {integrity: sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==} + + '@vue/devtools-api@7.7.10': + resolution: {integrity: sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==} + + '@vue/devtools-kit@7.7.10': + resolution: {integrity: sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==} + + '@vue/devtools-shared@7.7.10': + resolution: {integrity: sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==} + + '@vue/reactivity@3.5.43': + resolution: {integrity: sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==} + + '@vue/runtime-core@3.5.43': + resolution: {integrity: sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==} + + '@vue/runtime-dom@3.5.43': + resolution: {integrity: sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==} + + '@vue/server-renderer@3.5.43': + resolution: {integrity: sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==} + + '@vue/shared@3.5.43': + resolution: {integrity: sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==} + + '@vueuse/core@12.8.2': + resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} + + '@vueuse/integrations@12.8.2': + resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@12.8.2': + resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} + + '@vueuse/shared@12.8.2': + resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -1400,6 +1950,10 @@ packages: ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + algoliasearch@5.59.0: + resolution: {integrity: sha512-wUXzaeI7B526W4y1gFg3lcxgDZ67XSgRJIiellYWOas/pLpO7rOtxm9Gr2E/C8aiSDXIgx1q5TdSsvK67Uakqw==} + engines: {node: '>= 14.0.0'} + aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} @@ -1415,6 +1969,9 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + birpc@2.9.0: + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} + body-parser@2.3.0: resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} @@ -1491,6 +2048,10 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + copy-anything@4.1.5: + resolution: {integrity: sha512-vePst2VLTSqPglW7Q98VwFpGiGpOHTyXIo81MO5VyLecA5zMbZw/P/V1vmwD1jksa2iJpn/YkDrJ3XhTfzUyhQ==} + engines: {node: '>=18'} + cors@2.8.6: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} @@ -1548,6 +2109,9 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -1556,6 +2120,10 @@ packages: resolution: {integrity: sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w==} engines: {node: '>=10.13.0'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1571,6 +2139,11 @@ packages: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.28.2: resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} engines: {node: '>=18'} @@ -1589,6 +2162,9 @@ packages: estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -1646,6 +2222,9 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} + focus-trap@7.8.0: + resolution: {integrity: sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -1704,6 +2283,9 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + hast-util-to-jsx-runtime@2.3.6: resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} @@ -1717,12 +2299,18 @@ packages: resolution: {integrity: sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==} engines: {node: '>=16.9.0'} + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + html-parse-stringify@4.0.1: resolution: {integrity: sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==} html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -1951,6 +2539,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -2112,6 +2703,12 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} + minisearch@7.2.0: + resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + motion-dom@13.4.2: resolution: {integrity: sha512-mY293Iyj4rOQo5sDxUwPYLOKiveLQrJoK1tKfG8V1H3MMGMfan3s2i7NBW18WnD39DD79ogyiL/1fmy22GLG5w==} @@ -2167,6 +2764,9 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + oniguruma-to-es@3.1.1: + resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} + parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} @@ -2184,6 +2784,9 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2213,6 +2816,14 @@ packages: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} + preact@10.29.8: + resolution: {integrity: sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==} + peerDependencies: + preact-render-to-string: '>=5' + peerDependenciesMeta: + preact-render-to-string: + optional: true + property-information@7.2.0: resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} @@ -2293,6 +2904,15 @@ packages: resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} engines: {node: '>=0.10.0'} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} @@ -2312,11 +2932,19 @@ packages: reselect@5.3.0: resolution: {integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rolldown@1.2.9: resolution: {integrity: sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rollup@4.63.6: + resolution: {integrity: sha512-w4+GKyTBqshj84F1GF3s2zxAPFZPEmNkCbQtqilsYS7GsksmX6VoV7UggfZg8sUHCCOkBe7UtQp6fZwbH311FQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -2327,6 +2955,9 @@ packages: scheduler@0.28.0: resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -2349,6 +2980,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shiki@2.5.0: + resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} + side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -2375,6 +3009,10 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2397,6 +3035,13 @@ packages: style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + superjson@2.2.6: + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} + engines: {node: '>=16'} + + tabbable@6.5.0: + resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==} + tailwind-merge@3.7.0: resolution: {integrity: sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==} @@ -2522,6 +3167,37 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + vite@8.3.0: resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2565,6 +3241,18 @@ packages: yaml: optional: true + vitepress@1.6.4: + resolution: {integrity: sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + vitest@4.1.11: resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2606,6 +3294,14 @@ packages: jsdom: optional: true + vue@3.5.43: + resolution: {integrity: sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -2629,16 +3325,128 @@ packages: peerDependencies: zod: ^3.25.28 || ^4 - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@algolia/abtesting@1.25.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 + + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0) + '@algolia/client-search': 5.59.0 + algoliasearch: 5.59.0 + + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)': + dependencies: + '@algolia/client-search': 5.59.0 + algoliasearch: 5.59.0 + + '@algolia/client-abtesting@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 + + '@algolia/client-analytics@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 + + '@algolia/client-common@5.59.0': {} + + '@algolia/client-insights@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 + + '@algolia/client-personalization@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 + + '@algolia/client-query-suggestions@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 + + '@algolia/client-search@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 + + '@algolia/ingestion@1.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + '@algolia/monitoring@1.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + '@algolia/recommend@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 -snapshots: + '@algolia/requester-browser-xhr@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + + '@algolia/requester-fetch@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 + + '@algolia/requester-node-http@5.59.0': + dependencies: + '@algolia/client-common': 5.59.0 '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.269': optional: true @@ -2686,8 +3494,21 @@ snapshots: optionalDependencies: zod: 4.4.3 + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.9': + dependencies: + '@babel/types': 7.29.8 + '@babel/runtime@7.29.7': {} + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@base-ui/react@1.8.0(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: '@babel/runtime': 7.29.7 @@ -2711,81 +3532,175 @@ snapshots: optionalDependencies: '@types/react': 19.3.0 + '@docsearch/css@3.8.2': {} + + '@docsearch/js@3.8.2(@algolia/client-search@5.59.0)(search-insights@2.17.3)': + dependencies: + '@docsearch/react': 3.8.2(@algolia/client-search@5.59.0)(search-insights@2.17.3) + preact: 10.29.8 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - preact-render-to-string + - react + - react-dom + - search-insights + + '@docsearch/react@3.8.2(@algolia/client-search@5.59.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0) + '@docsearch/css': 3.8.2 + algoliasearch: 5.59.0 + optionalDependencies: + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + + '@esbuild/aix-ppc64@0.21.5': + optional: true + '@esbuild/aix-ppc64@0.28.2': optional: true + '@esbuild/android-arm64@0.21.5': + optional: true + '@esbuild/android-arm64@0.28.2': optional: true + '@esbuild/android-arm@0.21.5': + optional: true + '@esbuild/android-arm@0.28.2': optional: true + '@esbuild/android-x64@0.21.5': + optional: true + '@esbuild/android-x64@0.28.2': optional: true + '@esbuild/darwin-arm64@0.21.5': + optional: true + '@esbuild/darwin-arm64@0.28.2': optional: true + '@esbuild/darwin-x64@0.21.5': + optional: true + '@esbuild/darwin-x64@0.28.2': optional: true + '@esbuild/freebsd-arm64@0.21.5': + optional: true + '@esbuild/freebsd-arm64@0.28.2': optional: true + '@esbuild/freebsd-x64@0.21.5': + optional: true + '@esbuild/freebsd-x64@0.28.2': optional: true + '@esbuild/linux-arm64@0.21.5': + optional: true + '@esbuild/linux-arm64@0.28.2': optional: true + '@esbuild/linux-arm@0.21.5': + optional: true + '@esbuild/linux-arm@0.28.2': optional: true + '@esbuild/linux-ia32@0.21.5': + optional: true + '@esbuild/linux-ia32@0.28.2': optional: true + '@esbuild/linux-loong64@0.21.5': + optional: true + '@esbuild/linux-loong64@0.28.2': optional: true + '@esbuild/linux-mips64el@0.21.5': + optional: true + '@esbuild/linux-mips64el@0.28.2': optional: true + '@esbuild/linux-ppc64@0.21.5': + optional: true + '@esbuild/linux-ppc64@0.28.2': optional: true + '@esbuild/linux-riscv64@0.21.5': + optional: true + '@esbuild/linux-riscv64@0.28.2': optional: true + '@esbuild/linux-s390x@0.21.5': + optional: true + '@esbuild/linux-s390x@0.28.2': optional: true + '@esbuild/linux-x64@0.21.5': + optional: true + '@esbuild/linux-x64@0.28.2': optional: true '@esbuild/netbsd-arm64@0.28.2': optional: true + '@esbuild/netbsd-x64@0.21.5': + optional: true + '@esbuild/netbsd-x64@0.28.2': optional: true '@esbuild/openbsd-arm64@0.28.2': optional: true + '@esbuild/openbsd-x64@0.21.5': + optional: true + '@esbuild/openbsd-x64@0.28.2': optional: true '@esbuild/openharmony-arm64@0.28.2': optional: true + '@esbuild/sunos-x64@0.21.5': + optional: true + '@esbuild/sunos-x64@0.28.2': optional: true + '@esbuild/win32-arm64@0.21.5': + optional: true + '@esbuild/win32-arm64@0.28.2': optional: true + '@esbuild/win32-ia32@0.21.5': + optional: true + '@esbuild/win32-ia32@0.28.2': optional: true + '@esbuild/win32-x64@0.21.5': + optional: true + '@esbuild/win32-x64@0.28.2': optional: true @@ -2814,6 +3729,12 @@ snapshots: dependencies: hono: 4.13.7 + '@iconify-json/simple-icons@1.2.98': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -2877,6 +3798,9 @@ snapshots: transitivePeerDependencies: - supports-color + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + '@number-flow/react@0.6.2(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: esm-env: 1.2.2 @@ -3370,6 +4294,121 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} + '@rollup/rollup-android-arm-eabi@4.63.6': + optional: true + + '@rollup/rollup-android-arm64@4.63.6': + optional: true + + '@rollup/rollup-darwin-arm64@4.63.6': + optional: true + + '@rollup/rollup-darwin-x64@4.63.6': + optional: true + + '@rollup/rollup-freebsd-arm64@4.63.6': + optional: true + + '@rollup/rollup-freebsd-x64@4.63.6': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.63.6': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.63.6': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.63.6': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.63.6': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.63.6': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.63.6': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.63.6': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.63.6': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.63.6': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.63.6': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.63.6': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.63.6': + optional: true + + '@rollup/rollup-linux-x64-musl@4.63.6': + optional: true + + '@rollup/rollup-openbsd-x64@4.63.6': + optional: true + + '@rollup/rollup-openharmony-arm64@4.63.6': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.63.6': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.63.6': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.63.6': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.63.6': + optional: true + + '@shikijs/core@2.5.0': + dependencies: + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 3.1.1 + + '@shikijs/engine-oniguruma@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/themes@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/transformers@2.5.0': + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/types': 2.5.0 + + '@shikijs/types@2.5.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/vscode-textmate@10.0.2': {} + '@silvia-odwyer/photon-node@0.3.4': {} '@sinclair/typebox@0.34.52': {} @@ -3487,10 +4526,19 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.2.0': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 + '@types/mdurl@2.0.0': {} + '@types/ms@2.1.0': {} '@types/node@26.1.0': @@ -3509,6 +4557,8 @@ snapshots: '@types/unist@3.0.3': {} + '@types/web-bluetooth@0.0.21': {} + '@ungap/structured-clone@1.4.0': {} '@vitejs/plugin-react@6.1.1(vite@8.3.0(@types/node@26.1.0)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.8.0))': @@ -3516,6 +4566,11 @@ snapshots: '@rolldown/pluginutils': 1.0.1 vite: 8.3.0(@types/node@26.1.0)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.8.0) + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@26.1.0)(lightningcss@1.33.0))(vue@3.5.43(typescript@5.9.3))': + dependencies: + vite: 5.4.21(@types/node@26.1.0)(lightningcss@1.33.0) + vue: 3.5.43(typescript@5.9.3) + '@vitest/expect@4.1.11': dependencies: '@standard-schema/spec': 1.1.0 @@ -3557,6 +4612,105 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.1 + '@vue/compiler-core@3.5.43': + dependencies: + '@babel/parser': 7.29.9 + '@vue/shared': 3.5.43 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.43': + dependencies: + '@vue/compiler-core': 3.5.43 + '@vue/shared': 3.5.43 + + '@vue/compiler-sfc@3.5.43': + dependencies: + '@babel/parser': 7.29.9 + '@vue/compiler-core': 3.5.43 + '@vue/compiler-dom': 3.5.43 + '@vue/compiler-ssr': 3.5.43 + '@vue/shared': 3.5.43 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.28 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.43': + dependencies: + '@vue/compiler-dom': 3.5.43 + '@vue/shared': 3.5.43 + + '@vue/devtools-api@7.7.10': + dependencies: + '@vue/devtools-kit': 7.7.10 + + '@vue/devtools-kit@7.7.10': + dependencies: + '@vue/devtools-shared': 7.7.10 + birpc: 2.9.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.6 + + '@vue/devtools-shared@7.7.10': + dependencies: + rfdc: 1.4.1 + + '@vue/reactivity@3.5.43': + dependencies: + '@vue/shared': 3.5.43 + + '@vue/runtime-core@3.5.43': + dependencies: + '@vue/reactivity': 3.5.43 + '@vue/shared': 3.5.43 + + '@vue/runtime-dom@3.5.43': + dependencies: + '@vue/reactivity': 3.5.43 + '@vue/runtime-core': 3.5.43 + '@vue/shared': 3.5.43 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.43': + dependencies: + '@vue/compiler-ssr': 3.5.43 + '@vue/runtime-dom': 3.5.43 + '@vue/shared': 3.5.43 + + '@vue/shared@3.5.43': {} + + '@vueuse/core@12.8.2(typescript@5.9.3)': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 12.8.2 + '@vueuse/shared': 12.8.2(typescript@5.9.3) + vue: 3.5.43(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + '@vueuse/integrations@12.8.2(focus-trap@7.8.0)(typescript@5.9.3)': + dependencies: + '@vueuse/core': 12.8.2(typescript@5.9.3) + '@vueuse/shared': 12.8.2(typescript@5.9.3) + vue: 3.5.43(typescript@5.9.3) + optionalDependencies: + focus-trap: 7.8.0 + transitivePeerDependencies: + - typescript + + '@vueuse/metadata@12.8.2': {} + + '@vueuse/shared@12.8.2(typescript@5.9.3)': + dependencies: + vue: 3.5.43(typescript@5.9.3) + transitivePeerDependencies: + - typescript + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -3573,6 +4727,23 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + algoliasearch@5.59.0: + dependencies: + '@algolia/abtesting': 1.25.0 + '@algolia/client-abtesting': 5.59.0 + '@algolia/client-analytics': 5.59.0 + '@algolia/client-common': 5.59.0 + '@algolia/client-insights': 5.59.0 + '@algolia/client-personalization': 5.59.0 + '@algolia/client-query-suggestions': 5.59.0 + '@algolia/client-search': 5.59.0 + '@algolia/ingestion': 1.59.0 + '@algolia/monitoring': 1.59.0 + '@algolia/recommend': 5.59.0 + '@algolia/requester-browser-xhr': 5.59.0 + '@algolia/requester-fetch': 5.59.0 + '@algolia/requester-node-http': 5.59.0 + aria-hidden@1.2.6: dependencies: tslib: 2.8.1 @@ -3583,6 +4754,8 @@ snapshots: balanced-match@4.0.4: {} + birpc@2.9.0: {} + body-parser@2.3.0: dependencies: bytes: 3.1.2 @@ -3647,6 +4820,8 @@ snapshots: cookie@0.7.2: {} + copy-anything@4.1.5: {} + cors@2.8.6: dependencies: object-assign: 4.1.1 @@ -3692,6 +4867,8 @@ snapshots: ee-first@1.1.1: {} + emoji-regex-xs@1.0.0: {} + encodeurl@2.0.0: {} enhanced-resolve@5.25.1: @@ -3699,6 +4876,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + entities@7.0.1: {} + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -3709,6 +4888,32 @@ snapshots: dependencies: es-errors: 1.3.0 + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + esbuild@0.28.2: optionalDependencies: '@esbuild/aix-ppc64': 0.28.2 @@ -3746,6 +4951,8 @@ snapshots: estree-util-is-identifier-name@3.0.0: {} + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -3826,6 +5033,10 @@ snapshots: transitivePeerDependencies: - supports-color + focus-trap@7.8.0: + dependencies: + tabbable: 6.5.0 + forwarded@0.2.0: {} framer-motion@13.4.3(react-dom@19.3.0(react@19.3.0))(react@19.3.0): @@ -3876,6 +5087,20 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + hast-util-to-jsx-runtime@2.3.6: dependencies: '@types/estree': 1.0.9 @@ -3904,10 +5129,14 @@ snapshots: hono@4.13.7: {} + hookable@5.5.3: {} + html-parse-stringify@4.0.1: {} html-url-attributes@3.0.1: {} + html-void-elements@3.0.0: {} + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -4070,6 +5299,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 + mark.js@8.11.1: {} + markdown-table@3.0.4: {} marked@18.0.12: {} @@ -4434,6 +5665,10 @@ snapshots: dependencies: brace-expansion: 5.0.12 + minisearch@7.2.0: {} + + mitt@3.0.1: {} + motion-dom@13.4.2: dependencies: motion-utils: 13.3.0 @@ -4476,6 +5711,12 @@ snapshots: dependencies: wrappy: 1.0.2 + oniguruma-to-es@3.1.1: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.1.0 + regex-recursion: 6.0.2 + parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -4494,6 +5735,8 @@ snapshots: pathe@2.0.3: {} + perfect-debounce@1.0.0: {} + picocolors@1.1.1: {} picomatch@4.0.7: {} @@ -4517,6 +5760,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + preact@10.29.8: {} + property-information@7.2.0: {} proxy-addr@2.0.7: @@ -4601,6 +5846,16 @@ snapshots: react@19.3.0: {} + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + remark-gfm@4.0.1: dependencies: '@types/mdast': 4.0.4 @@ -4639,6 +5894,8 @@ snapshots: reselect@5.3.0: {} + rfdc@1.4.1: {} + rolldown@1.2.9: dependencies: '@oxc-project/types': 0.150.0 @@ -4660,6 +5917,38 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.9 '@rolldown/binding-win32-x64-msvc': 1.2.9 + rollup@4.63.6: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.63.6 + '@rollup/rollup-android-arm64': 4.63.6 + '@rollup/rollup-darwin-arm64': 4.63.6 + '@rollup/rollup-darwin-x64': 4.63.6 + '@rollup/rollup-freebsd-arm64': 4.63.6 + '@rollup/rollup-freebsd-x64': 4.63.6 + '@rollup/rollup-linux-arm-gnueabihf': 4.63.6 + '@rollup/rollup-linux-arm-musleabihf': 4.63.6 + '@rollup/rollup-linux-arm64-gnu': 4.63.6 + '@rollup/rollup-linux-arm64-musl': 4.63.6 + '@rollup/rollup-linux-loong64-gnu': 4.63.6 + '@rollup/rollup-linux-loong64-musl': 4.63.6 + '@rollup/rollup-linux-ppc64-gnu': 4.63.6 + '@rollup/rollup-linux-ppc64-musl': 4.63.6 + '@rollup/rollup-linux-riscv64-gnu': 4.63.6 + '@rollup/rollup-linux-riscv64-musl': 4.63.6 + '@rollup/rollup-linux-s390x-gnu': 4.63.6 + '@rollup/rollup-linux-x64-gnu': 4.63.6 + '@rollup/rollup-linux-x64-musl': 4.63.6 + '@rollup/rollup-openbsd-x64': 4.63.6 + '@rollup/rollup-openharmony-arm64': 4.63.6 + '@rollup/rollup-win32-arm64-msvc': 4.63.6 + '@rollup/rollup-win32-ia32-msvc': 4.63.6 + '@rollup/rollup-win32-x64-gnu': 4.63.6 + '@rollup/rollup-win32-x64-msvc': 4.63.6 + fsevents: 2.3.3 + router@2.2.0: dependencies: debug: 4.4.3 @@ -4674,6 +5963,8 @@ snapshots: scheduler@0.28.0: {} + search-insights@2.17.3: {} + send@1.2.1: dependencies: debug: 4.4.3 @@ -4709,6 +6000,17 @@ snapshots: shebang-regex@3.0.0: {} + shiki@2.5.0: + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/langs': 2.5.0 + '@shikijs/themes': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -4743,6 +6045,8 @@ snapshots: space-separated-tokens@2.0.2: {} + speakingurl@14.0.1: {} + stackback@0.0.2: {} standardwebhooks@1.1.1: @@ -4767,6 +6071,12 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + superjson@2.2.6: + dependencies: + copy-anything: 4.1.5 + + tabbable@6.5.0: {} + tailwind-merge@3.7.0: {} tailwindcss@4.3.3: {} @@ -4876,6 +6186,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + vite@5.4.21(@types/node@26.1.0)(lightningcss@1.33.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.28 + rollup: 4.63.6 + optionalDependencies: + '@types/node': 26.1.0 + fsevents: 2.3.3 + lightningcss: 1.33.0 + vite@8.3.0(@types/node@26.1.0)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.8.0): dependencies: lightningcss: 1.33.0 @@ -4890,6 +6210,56 @@ snapshots: jiti: 2.7.0 yaml: 2.8.0 + vitepress@1.6.4(@algolia/client-search@5.59.0)(@types/node@26.1.0)(lightningcss@1.33.0)(postcss@8.5.28)(search-insights@2.17.3)(typescript@5.9.3): + dependencies: + '@docsearch/css': 3.8.2 + '@docsearch/js': 3.8.2(@algolia/client-search@5.59.0)(search-insights@2.17.3) + '@iconify-json/simple-icons': 1.2.98 + '@shikijs/core': 2.5.0 + '@shikijs/transformers': 2.5.0 + '@shikijs/types': 2.5.0 + '@types/markdown-it': 14.2.0 + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@26.1.0)(lightningcss@1.33.0))(vue@3.5.43(typescript@5.9.3)) + '@vue/devtools-api': 7.7.10 + '@vue/shared': 3.5.43 + '@vueuse/core': 12.8.2(typescript@5.9.3) + '@vueuse/integrations': 12.8.2(focus-trap@7.8.0)(typescript@5.9.3) + focus-trap: 7.8.0 + mark.js: 8.11.1 + minisearch: 7.2.0 + shiki: 2.5.0 + vite: 5.4.21(@types/node@26.1.0)(lightningcss@1.33.0) + vue: 3.5.43(typescript@5.9.3) + optionalDependencies: + postcss: 8.5.28 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - preact-render-to-string + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + vitest@4.1.11(@types/node@26.1.0)(vite@8.3.0(@types/node@26.1.0)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.8.0)): dependencies: '@vitest/expect': 4.1.11 @@ -4917,6 +6287,16 @@ snapshots: transitivePeerDependencies: - msw + vue@3.5.43(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.43 + '@vue/compiler-sfc': 3.5.43 + '@vue/runtime-dom': 3.5.43 + '@vue/server-renderer': 3.5.43 + '@vue/shared': 3.5.43 + optionalDependencies: + typescript: 5.9.3 + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 37a9f6de..33fa0203 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,5 @@ packages: - "apps/web" + - "website" - "packages/*" - "example/parsar" diff --git a/scripts/ci_plan.py b/scripts/ci_plan.py index 3f9b4b56..433dd4ff 100644 --- a/scripts/ci_plan.py +++ b/scripts/ci_plan.py @@ -19,6 +19,7 @@ ".github/workflows/native.yml": ("native", "lint"), ".github/workflows/actionlint.yml": ("lint",), ".github/workflows/ci-review.yml": ("lint",), + ".github/workflows/website.yml": ("lint",), ".github/actions/node/action.yml": (*NODE_JOBS, "lint"), "scripts/ci_plan.py": JOBS, "scripts/ci_plan_test.py": ("hygiene",), diff --git a/scripts/ci_plan_test.py b/scripts/ci_plan_test.py index 6eb2ca6d..6d86be45 100644 --- a/scripts/ci_plan_test.py +++ b/scripts/ci_plan_test.py @@ -79,6 +79,7 @@ def test_workflow_changes_select_only_their_consumers(self): for workflow, selected in { "ci-review": {"hygiene", "lint"}, "actionlint": {"hygiene", "lint"}, + "website": {"hygiene", "lint"}, "native": {"hygiene", "native", "lint"}, "api-acceptance": {"hygiene", "api", "lint"}, }.items(): diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 00000000..be703ae7 --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,2 @@ +.vitepress/cache/ +.vitepress/dist/ diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts new file mode 100644 index 00000000..a46feaca --- /dev/null +++ b/website/.vitepress/config.mts @@ -0,0 +1,179 @@ +import { createRequire } from 'node:module' +import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { defineConfig, type SiteConfig } from 'vitepress' +import { repositoryLinks } from './repository-links.mts' +import { docsSidebar, legacyRedirects, pageTitle, readDocsJson, repoRoot } from './docs-nav.mts' + +// Pages outside this package resolve Vue from here, not from the repository root. +const require = createRequire(import.meta.url) +const vueDir = dirname(require.resolve('vue/package.json')) + +const repo = 'https://github.com/MiniMax-AI/OpenAgentCore' +const description = 'An open-source, self-hosted implementation of the OpenAI Agents API with multiple native harnesses.' + +// GitHub Pages passes its base path (for example `/OpenAgentCore/`, or `/` on a +// custom domain) to the build; local builds serve from the root. +const base = normalizeBase(process.env.WEBSITE_BASE) + +function normalizeBase(value: string | undefined): string { + const trimmed = (value ?? '').replace(/^\/+|\/+$/g, '') + return trimmed ? `/${trimmed}/` : '/' +} + +// The canonical mark, inlined so the favicon has no second copy of the logo. +const logoSvg = readFileSync(resolve(repoRoot, 'docs/assets/openagentcore-logo.svg'), 'utf8') +const favicon = `data:image/svg+xml,${encodeURIComponent(logoSvg.replace('currentColor', '#4ADE80'))}` + +const docsNav = [ + { text: 'Docs', link: '/docs/getting-started/', activeMatch: '^/docs/(getting-started|examples|configuration|web)' }, + { text: 'API', link: '/docs/api/public-agent-api', activeMatch: '^/(docs/api|contracts)/' }, + { text: 'Architecture', link: '/docs/architecture', activeMatch: '^/docs/(architecture|concepts|runtime|sandbox|development)' }, +] + +export default defineConfig({ + title: 'OpenAgentCore', + description, + base, + cleanUrls: true, + lastUpdated: true, + appearance: 'dark', + + // The existing documentation is published in place: the repository root is + // the content root, docs/ and contracts/ keep their paths, and the website's + // own pages are mapped onto `/` and `/zh/`. + srcDir: '..', + srcExclude: [ + '*.md', + '.github/**', + 'apps/**', + 'deploy/**', + 'example/**', + 'internal/**', + 'packages/**', + 'scripts/**', + 'services/**', + 'website/README.md', + 'website/node_modules/**', + '**/node_modules/**', + ], + rewrites: { + 'website/index.md': 'index.md', + 'website/zh/index.md': 'zh/index.md', + }, + + head: [ + ['link', { rel: 'icon', type: 'image/svg+xml', href: favicon }], + ['meta', { name: 'theme-color', content: '#0a0d0c' }], + ['meta', { property: 'og:title', content: 'OpenAgentCore — One core. Many agents.' }], + ['meta', { property: 'og:description', content: description }], + ['meta', { property: 'og:image', content: 'https://raw.githubusercontent.com/MiniMax-AI/OpenAgentCore/main/docs/assets/openagentcore-banner.jpeg' }], + ['meta', { name: 'twitter:card', content: 'summary_large_image' }], + ], + + locales: { + root: { + label: 'English', + lang: 'en', + themeConfig: { nav: docsNav }, + }, + zh: { + label: '简体中文', + lang: 'zh-CN', + link: '/zh/', + description: '开源、可自部署的 OpenAI Agents API 实现,支持多种原生 Harness。', + themeConfig: { + // The documentation is written in English; the Chinese landing page links to it. + nav: [ + { text: '文档', link: '/docs/getting-started/' }, + { text: 'API', link: '/docs/api/public-agent-api' }, + { text: '架构', link: '/docs/architecture' }, + ], + outline: { label: '本页目录' }, + docFooter: { prev: '上一页', next: '下一页' }, + }, + }, + }, + + themeConfig: { + // Only the landing page is translated, so switching language opens that locale's home page. + i18nRouting: false, + logo: { src: favicon, alt: '' }, + siteTitle: 'OpenAgentCore', + socialLinks: [{ icon: 'github', link: repo }], + sidebar: { + '/docs/': docsSidebar(), + '/contracts/': docsSidebar(), + }, + outline: { level: [2, 3] }, + search: { provider: 'local' }, + editLink: { + pattern: `${repo}/edit/main/:path`, + text: 'Edit this page on GitHub', + }, + footer: { + message: 'Released under the MIT License.', + copyright: 'One core. Many agents.', + }, + }, + + markdown: { + theme: { light: 'github-light', dark: 'github-dark' }, + languageAlias: { caddyfile: 'nginx' }, + config: (md) => repositoryLinks(md, { repoRoot, blobBase: `${repo}/blob/main`, treeBase: `${repo}/tree/main` }), + }, + + vite: { + // Repository Markdown may sit outside the website package. + server: { fs: { allow: [repoRoot] } }, + // The local search index is one lazily loaded chunk of about 0.5 MB. + build: { chunkSizeWarningLimit: 800 }, + resolve: { + alias: [ + { find: /^vue\/server-renderer$/, replacement: require.resolve('vue/server-renderer') }, + { find: /^vue$/, replacement: resolve(vueDir, 'dist/vue.runtime.esm-bundler.js') }, + ], + }, + }, + + buildEnd: (site) => { + writeMarkdownCopies(site) + writeLegacyRedirects(site) + }, +}) + +/** + * Serves each documentation page's source Markdown next to its HTML page (for + * example `/docs/architecture.md`), keeping the `.md` URLs that the repository + * uses, and lists them in `/llms.txt` for agents. + */ +function writeMarkdownCopies(site: SiteConfig) { + const pages = site.pages.filter((page) => /^(docs|contracts)\//.test(page)) + for (const page of pages) { + const target = resolve(site.outDir, page) + mkdirSync(dirname(target), { recursive: true }) + copyFileSync(resolve(site.srcDir, page), target) + } + + const origin = site.site.base + const lines = ['# OpenAgentCore', '', `> ${description}`, ''] + for (const group of readDocsJson().navigation.groups) { + lines.push(`## ${group.group}`, '') + for (const page of group.pages) lines.push(`- [${pageTitle(page)}](${origin}${page}.md)`) + lines.push('') + } + writeFileSync(resolve(site.outDir, 'llms.txt'), lines.join('\n')) +} + +/** Writes static redirects for the non-Markdown legacy paths declared in docs.json. */ +function writeLegacyRedirects(site: SiteConfig) { + for (const { from, to } of legacyRedirects()) { + const target = resolve(site.outDir, `${from}.html`) + const url = `${site.site.base}${to.replace(/^\//, '')}` + mkdirSync(dirname(target), { recursive: true }) + writeFileSync( + target, + `Redirecting…${url}\n`, + ) + } +} diff --git a/website/.vitepress/docs-nav.mts b/website/.vitepress/docs-nav.mts new file mode 100644 index 00000000..d6193d04 --- /dev/null +++ b/website/.vitepress/docs-nav.mts @@ -0,0 +1,61 @@ +// Builds website navigation from the repository's existing documentation. +// docs.json owns the page list and order; each page's frontmatter title owns +// its label. Nothing here copies or rewrites the Markdown sources. +import { existsSync, readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import type { DefaultTheme } from 'vitepress' + +export const repoRoot = fileURLToPath(new URL('../../', import.meta.url)) + +interface DocsJson { + navigation: { groups: { group: string; pages: string[] }[] } + redirects?: { source: string; destination: string }[] +} + +export function readDocsJson(): DocsJson { + return JSON.parse(readFileSync(resolve(repoRoot, 'docs.json'), 'utf8')) +} + +/** Unquotes a YAML scalar: double quotes use JSON-compatible escapes, single quotes double ''. */ +export function unquote(value: string): string { + if (value.startsWith('"')) return JSON.parse(value) + if (value.startsWith("'")) return value.slice(1, -1).replace(/''/g, "'") + return value +} + +/** Reads `title:` from a page's YAML frontmatter, falling back to its first H1. */ +export function pageTitle(page: string): string { + const file = resolve(repoRoot, `${page}.md`) + if (!existsSync(file)) throw new Error(`docs.json references a missing page: ${page}.md`) + const source = readFileSync(file, 'utf8') + const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source)?.[1] ?? '' + const title = /^title:\s*(.+)$/m.exec(frontmatter)?.[1]?.trim() + if (title) return unquote(title) + const heading = /^#\s+(.+)$/m.exec(source)?.[1]?.trim() + if (heading) return heading + throw new Error(`Page has no title: ${page}.md`) +} + +/** Maps a docs.json page id to its website route. `dir/index` becomes `dir/`. */ +export function pageLink(page: string): string { + return `/${page.replace(/(^|\/)index$/, '$1')}` +} + +export function docsSidebar(): DefaultTheme.SidebarItem[] { + return readDocsJson().navigation.groups.map((group) => ({ + text: group.group, + collapsed: false, + items: group.pages.map((page) => ({ text: pageTitle(page), link: pageLink(page) })), + })) +} + +/** + * Redirects in docs.json that point a legacy path (such as `README`) at a page. + * `.md` sources are served by the raw Markdown copies, so they are skipped. + */ +export function legacyRedirects(): { from: string; to: string }[] { + return (readDocsJson().redirects ?? []) + .filter((r) => !r.source.endsWith('.md')) + .map((r) => ({ from: r.source.replace(/^\//, ''), to: pageLink(r.destination.replace(/^\//, '')) })) +} diff --git a/website/.vitepress/env.d.ts b/website/.vitepress/env.d.ts new file mode 100644 index 00000000..5892d7d9 --- /dev/null +++ b/website/.vitepress/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/website/.vitepress/repository-links.mts b/website/.vitepress/repository-links.mts new file mode 100644 index 00000000..2d749a20 --- /dev/null +++ b/website/.vitepress/repository-links.mts @@ -0,0 +1,29 @@ +// Rewrites relative links from a published page to a repository-only file +// (a root guide, a YAML contract, a source file) into a GitHub URL, so the +// Markdown sources stay unchanged and work both on GitHub and on the website. +import { existsSync, statSync } from 'node:fs' +import { dirname, relative, resolve } from 'node:path' +import type MarkdownIt from 'markdown-it' + +const published = /^(docs|contracts)\/.+\.md$/ + +export function repositoryLinks(md: MarkdownIt, options: { repoRoot: string; blobBase: string; treeBase: string }) { + md.core.ruler.push('oac_repository_links', (state) => { + const page: string | undefined = state.env?.relativePath + if (!page) return + const pageDir = dirname(resolve(options.repoRoot, page)) + for (const block of state.tokens) { + for (const token of block.children ?? []) { + if (token.type !== 'link_open') continue + const href = token.attrGet('href') + if (!href || /^([a-z][a-z0-9+.-]*:|#|\/)/i.test(href)) continue + const [path, hash = ''] = href.split('#') + const target = resolve(pageDir, decodeURI(path)) + const repoPath = relative(options.repoRoot, target) + if (repoPath.startsWith('..') || !existsSync(target) || published.test(repoPath)) continue + const base = statSync(target).isDirectory() ? options.treeBase : options.blobBase + token.attrSet('href', `${base}/${repoPath}${hash ? `#${hash}` : ''}`) + } + } + }) +} diff --git a/website/.vitepress/theme/Layout.vue b/website/.vitepress/theme/Layout.vue new file mode 100644 index 00000000..9d189ab4 --- /dev/null +++ b/website/.vitepress/theme/Layout.vue @@ -0,0 +1,15 @@ + + + diff --git a/website/.vitepress/theme/components/AsciiCanvas.vue b/website/.vitepress/theme/components/AsciiCanvas.vue new file mode 100644 index 00000000..2a2f3600 --- /dev/null +++ b/website/.vitepress/theme/components/AsciiCanvas.vue @@ -0,0 +1,300 @@ + + + + + diff --git a/website/.vitepress/theme/components/ComposeLab.vue b/website/.vitepress/theme/components/ComposeLab.vue new file mode 100644 index 00000000..a5d58429 --- /dev/null +++ b/website/.vitepress/theme/components/ComposeLab.vue @@ -0,0 +1,335 @@ + + + + + diff --git a/website/.vitepress/theme/components/Landing.vue b/website/.vitepress/theme/components/Landing.vue new file mode 100644 index 00000000..38d26b6b --- /dev/null +++ b/website/.vitepress/theme/components/Landing.vue @@ -0,0 +1,1301 @@ + + + + + + + diff --git a/website/.vitepress/theme/components/TypeTerminal.vue b/website/.vitepress/theme/components/TypeTerminal.vue new file mode 100644 index 00000000..b5ee7a58 --- /dev/null +++ b/website/.vitepress/theme/components/TypeTerminal.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/website/.vitepress/theme/docs.data.mts b/website/.vitepress/theme/docs.data.mts new file mode 100644 index 00000000..530ac9ad --- /dev/null +++ b/website/.vitepress/theme/docs.data.mts @@ -0,0 +1,49 @@ +// Build-time data for the landing page's documentation index. The page list +// and order come from docs.json; titles and summaries come from each page, so +// new documentation appears on the landing page without editing the website. +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { defineLoader } from 'vitepress' +import { pageLink, pageTitle, readDocsJson, repoRoot } from '../docs-nav.mts' + +export interface DocsPage { + title: string + link: string + summary: string +} + +export interface DocsGroup { + group: string + pages: DocsPage[] +} + +declare const data: DocsGroup[] +export { data } + +/** The first sentence of the page body, with Markdown links and code marks removed. */ +function summary(page: string): string { + const source = readFileSync(resolve(repoRoot, `${page}.md`), 'utf8') + const body = source.replace(/^---[\s\S]*?\n---\s*/, '') + const paragraph = body + .split(/\n\s*\n/) + .map((block) => block.trim()) + .find((block) => block && !/^(#|\||```|!\[|<|\[\/\/\]|>|-|\d+\.)/.test(block)) + if (!paragraph) return '' + const text = paragraph + .replace(/!\[[^\]]*\]\([^)]*\)/g, '') + .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1') + .replace(/[`*_]/g, '') + .replace(/\s+/g, ' ') + const sentence = /^(.+?[.!?])(\s|$)/.exec(text)?.[1] ?? text + return sentence.length > 180 ? `${sentence.slice(0, 177).trimEnd()}…` : sentence +} + +export default defineLoader({ + watch: [resolve(repoRoot, 'docs.json'), resolve(repoRoot, 'docs/**/*.md'), resolve(repoRoot, 'contracts/**/*.md')], + load(): DocsGroup[] { + return readDocsJson().navigation.groups.map((group) => ({ + group: group.group, + pages: group.pages.map((page) => ({ title: pageTitle(page), link: pageLink(page), summary: summary(page) })), + })) + }, +}) diff --git a/website/.vitepress/theme/index.ts b/website/.vitepress/theme/index.ts new file mode 100644 index 00000000..7af54aba --- /dev/null +++ b/website/.vitepress/theme/index.ts @@ -0,0 +1,15 @@ +import DefaultTheme from 'vitepress/theme' +import type { Theme } from 'vitepress' +import '@fontsource-variable/inter' +import '@fontsource-variable/geist-mono' +import Layout from './Layout.vue' +import Landing from './components/Landing.vue' +import './style.css' + +export default { + extends: DefaultTheme, + Layout, + enhanceApp({ app }) { + app.component('Landing', Landing) + }, +} satisfies Theme diff --git a/website/.vitepress/theme/landing-content.ts b/website/.vitepress/theme/landing-content.ts new file mode 100644 index 00000000..24c46366 --- /dev/null +++ b/website/.vitepress/theme/landing-content.ts @@ -0,0 +1,437 @@ +// Landing page copy. Every product claim here is backed by a page in docs/ or +// contracts/; each section links to the page that owns the detail. + +export type Lang = 'en' | 'zh' + +export interface LandingCopy { + hero: { + eyebrow: string + title: [string, string] + lede: string + primary: string + secondary: string + installLabel: string + copied: string + copy: string + hud: [string, string][] + } + ticker: string[] + problem: { + index: string + kicker: string + title: string + lede: string + questions: { q: string; a: string }[] + punch: string + } + compose: { + index: string + kicker: string + title: string + lede: string + harness: string + protocol: string + environment: string + accepted: string + rejected: (harness: string, protocols: string) => string + envs: Record<'openai_hosted' | 'self_hosted', { label: string; note: string }> + input: string + footnote: string + footnoteLink: string + } + pillars: { tag: string; title: string; body: string }[] + architecture: { + index: string + kicker: string + title: string + lede: string + tabs: { id: string; label: string; alt: string }[] + boundariesTitle: string + boundaries: { from: string; to: string; doc: string; link: string }[] + more: string + } + session: { + index: string + kicker: string + title: string + lede: string + rules: { title: string; body: string; art: string }[] + honest: string + } + observe: { + index: string + kicker: string + title: string + lede: string + tabs: { id: 'overview' | 'metrics'; label: string; alt: string }[] + caption: string + } + compare: { + index: string + kicker: string + title: string + head: [string, string, string] + rows: [string, string, string][] + } + tradeoffs: { + index: string + kicker: string + title: string + motto: [string, string] + items: { title: string; body: string }[] + } + start: { + index: string + kicker: string + title: string + lede: string + steps: { title: string; body: string }[] + cta: string + trial: string + } + roadmap: { + index: string + kicker: string + title: string + lede: string + tracks: { layer: string; title: string; body: string }[] + } + docs: { + index: string + kicker: string + title: string + lede: (pages: number) => string + open: string + } + outro: { line: string; primary: string; secondary: string } +} + +const en: LandingCopy = { + hero: { + eyebrow: 'open source · self-hosted · OpenAI Agents API', + title: ['One core.', 'Many agents.'], + lede: 'An open-source, self-hosted implementation of the OpenAI Agents API. Run Codex, Claude Code or MiniMax Code with your own models, on your own machines, through the official OpenAI SDK.', + primary: 'Run your first Session', + secondary: 'Read the docs', + installLabel: 'Linux amd64 · Docker · Python 3.9+', + copied: 'copied', + copy: 'copy', + hud: [ + ['api', '/v1 · OpenAI Agents API'], + ['harness', 'codex | claude_sdk | mcode'], + ['protocol', 'responses | anthropic | chat_completions'], + ['sandbox', 'docker | microsandbox | e2b'], + ['machine', 'linux | macos | windows'], + ], + }, + ticker: ['Codex', 'Claude Code', 'MiniMax Code', 'Responses API', 'Anthropic Messages', 'Chat Completions', 'Docker', 'microsandbox', 'E2B', 'Linux', 'macOS', 'Windows', 'PostgreSQL', 'OpenAI SDK'], + problem: { + index: '01', + kicker: 'the missing layer', + title: 'Calling a model is solved. Letting an agent do the work is not.', + lede: 'An agent reads files, runs commands, waits for tests and asks for approval. A single model response is one step of that. Put it inside a product and a new set of questions appears.', + questions: [ + { q: 'The user closed the tab. What happens to the work already submitted?', a: 'A dropped connection is an observer leaving. The Turn keeps running; read its durable state.' }, + { q: 'The request timed out. Send it again, or look up the original?', a: 'Poll durable state. Supported submission paths are idempotent, so a lost response does not duplicate work.' }, + { q: 'The user pressed Stop. Did the output stop, or the agent and everything it started?', a: 'Cancellation has a receipt: Core confirms the Turn\'s execution and cleanup state.' }, + { q: 'Which model and which machine ran this? Where are the files and the log?', a: 'Sessions, Turns, Items, artifacts and usage are stored by Core and queryable through the API.' }, + { q: 'Switching from Codex to Claude Code, or Docker to E2B. How much product code changes?', a: 'Your code keeps the same API calls. The harness is a Session setting with a matching model provider, and the sandbox is the administrator\'s choice, behind a Sandbox Provider.' }, + ], + punch: 'Between an agent that works in a terminal and an agent a product can call, there is a whole layer of engineering. OpenAgentCore is that layer.', + }, + compose: { + index: '02', + kicker: 'pick each part', + title: 'Choose the model, the agent and the machine separately.', + lede: 'They are three different decisions. Each Session names its harness, its model provider and its Environment; Core validates the combination before anything runs.', + harness: 'harness', + protocol: 'model protocol', + environment: 'environment', + accepted: 'accepted · Session created', + rejected: (harness, protocols) => `rejected before the Session is created · ${harness} speaks only ${protocols}`, + envs: { + openai_hosted: { label: 'managed sandbox', note: 'Docker · microsandbox · E2B' }, + self_hosted: { label: 'your machine', note: 'Linux · macOS · Windows' }, + }, + input: 'Fix the failing test and explain the change.', + footnote: 'The supported combinations are declared by each harness, not guessed.', + footnoteLink: 'Harness capabilities', + }, + pillars: [ + { tag: 'api', title: 'Same API as OpenAI', body: 'Point the official OpenAI SDK, or plain HTTP, at your installation. No new client to learn.' }, + { tag: 'agent', title: 'Your choice of agent', body: 'Each Session runs a native harness: Codex, Claude Code or MiniMax Code, with the model provider you configure.' }, + { tag: 'machine', title: 'Your choice of machine', body: 'A managed sandbox (Docker, microsandbox or E2B), or your own Linux, macOS or Windows machine.' }, + { tag: 'swap', title: 'Every part is replaceable', body: 'Sandboxes, harnesses and model providers plug in through defined protocols. Swap one without touching Core.' }, + ], + architecture: { + index: '03', + kicker: 'protocols at every boundary', + title: 'Every part plugs in through a protocol.', + lede: 'Core keeps durable execution state and schedules work. The Runtime prepares the Environment and starts the native harness. The harness calls the model and tools, and reports back through the Runtime.', + tabs: [ + { id: 'ecosystem', label: 'ecosystem', alt: 'Applications reach OpenAgentCore through the Agents API; harnesses, models and compute connect through their own boundaries.' }, + { id: 'protocols', label: 'components', alt: 'Agents API and Core API on top of Core; Sandbox Provider, Runtime, Harness and Model Provider connected by protocols.' }, + { id: 'surfaces', label: 'api namespaces', alt: 'Three namespaces with three credentials: Agents API for applications, Core API for operators, Machine API for nodes.' }, + ], + boundariesTitle: 'one boundary · one protocol · one document', + boundaries: [ + { from: 'Application', to: 'Core', doc: 'Agents API', link: '/docs/api/public-agent-api' }, + { from: 'Core', to: 'Sandbox Provider', doc: 'Sandbox Provider guide', link: '/docs/sandbox-provider' }, + { from: 'Provider', to: 'Runtime', doc: 'Runtime bootstrap', link: '/docs/runtime-bootstrap' }, + { from: 'Core', to: 'Runtime', doc: 'Core–Runtime protocol', link: '/docs/runtime-protocol' }, + { from: 'Runtime', to: 'Harness', doc: 'Harness onboarding', link: '/contracts/agents-api/harness-onboarding' }, + { from: 'Harness', to: 'Model', doc: 'Model execution', link: '/contracts/agents-api/model-execution' }, + ], + more: 'Read the architecture', + }, + session: { + index: '04', + kicker: 'execution you can manage', + title: 'A run becomes a Session you can manage.', + lede: 'A Session is a continuing piece of agent work; a Turn is one input executed inside it. Both have identities and durable state. Four rules decide what "running", "waiting", "cancelled" and "failed" mean.', + rules: [ + { title: 'Connection ≠ work', body: 'Closing the stream only removes an observer. Submitted work stays with the execution system.', art: 'client ──────╳ disconnect\nturn ━━━━━━━━━━━━━━▶ completed' }, + { title: 'Retries have identity', body: 'Supported submission paths carry a stable key, so a lost response never creates the work twice.', art: 'POST Idempotency-Key: a1f ──▶ turn_01\nPOST Idempotency-Key: a1f ──▶ turn_01' }, + { title: 'Cancel has a receipt', body: 'Closing output proves nothing. Core confirms the target Turn\'s execution and cleanup.', art: 'cancel ──▶ turn in_progress\n ──▶ turn cancelled ✓ confirmed' }, + { title: 'Execution ≠ machine', body: 'A Turn ending, the executor closing and the Environment being reclaimed are separate lifecycles.', art: 'turn ━━━━┫\nexecutor ━━━━━━━━┫\nenvironment ━━━━━━━━━━━━━┫' }, + ], + honest: 'Recovery has limits, and they are explicit: an unknown outcome is never reported as success.', + }, + observe: { + index: '05', + kicker: 'see it run', + title: 'Operations you can actually see.', + lede: 'Web is the operator console: Sessions, node capacity, work waiting for the caller, errors, latency, tokens and tool calls, per Project.', + tabs: [ + { id: 'overview', label: 'overview', alt: 'Web console overview: service status, running Sessions, sandbox capacity, fleet and Projects.' }, + { id: 'metrics', label: 'agent metrics', alt: 'Web console agent metrics: requests, errors, latency, tokens and tool calls.' }, + ], + caption: 'Screenshot values are illustrative. Usage visibility depends on what each native harness reports.', + }, + compare: { + index: '06', + kicker: 'where it fits', + title: 'What you still own, depending on how you build.', + head: ['approach', 'fits when', 'you still own'], + rows: [ + ['Call a model API', 'One-off inference, or you want full control of the agent loop', 'Context, tool execution, the loop and the whole task lifecycle'], + ['Call a native CLI or SDK', 'Personal automation, or one integration around one harness', 'Session mapping, processes, resources and every engine\'s differences'], + ['Use a sandbox service', 'You need an isolated computer', 'The agent engine, execution, interaction, records and the product API'], + ['Use OpenAgentCore', 'Self-hosted, many native harnesses and environments behind one API', 'Business permissions, product experience, team orchestration and operating your installation'], + ], + }, + tradeoffs: { + index: '07', + kicker: 'trade-offs, stated', + title: 'The choices behind the design.', + motto: ['State belongs to the infrastructure.', 'Intelligence belongs to the harness.'], + items: [ + { title: 'Native harnesses, with their constraints', body: 'The Runtime and the native harness run inside the Environment; Core holds the durable control plane. Native engines are reused as-is, so the Environment, native history and recovery still matter.' }, + { title: 'One protocol, real differences', body: 'A capability one harness supports is not silently granted to another. Core checks each combination before running, rejects what is unsupported and records what is unverified.' }, + { title: 'Simple for the caller', body: 'Callers think in tasks, input, state and results. Machine connections, native executors and cleanup belong behind clear component boundaries. The interface is the product.' }, + ], + }, + start: { + index: '08', + kicker: 'get started', + title: 'From install to first Session.', + lede: 'On a Linux amd64 host with Docker and Python 3.9+:', + steps: [ + { title: 'Sign in to Web', body: 'Use the Core key the installer created, then configure the domain and HTTPS.' }, + { title: 'Set a default model', body: 'Then issue a Project API key for your application.' }, + { title: 'Add execution capacity', body: 'A node, E2B, or your own machine.' }, + { title: 'Run your first Session', body: 'With the official OpenAI SDK, against your own Core.' }, + ], + cta: 'Installation guide', + trial: 'Quickstart', + }, + roadmap: { + index: '09', + kicker: 'what comes next', + title: 'Version 1 is the foundation.', + lede: 'OpenAgentCore is pre-release; support is qualified per harness, environment and operation. The next layers we are working toward:', + tracks: [ + { layer: 'harness', title: 'Agent loop, decoupled', body: 'Run the agent loop on the server and tool execution on a local machine or a cloud sandbox, with enterprise authorization at the tool layer.' }, + { layer: 'compute', title: 'Faster, denser compute', body: 'Separate compute from storage, restore state quickly and scale sandbox capacity on demand.' }, + { layer: 'storage', title: 'More storage backends', body: 'S3, OSS, shared file systems and agent-native file systems.' }, + { layer: 'apps', title: 'Applications on the Agents API', body: 'Keep building real products on the public API, the same way any application uses it.' }, + ], + }, + docs: { + index: '10', + kicker: 'documentation', + title: 'Everything is in the docs.', + lede: (pages) => `${pages} pages, listed straight from the repository's docs.json. New guides appear here when they are added.`, + open: 'open', + }, + outro: { line: 'Products differ in interaction, model and engine. They can share one execution layer.', primary: 'Get started', secondary: 'Star on GitHub' }, +} + +const zh: LandingCopy = { + hero: { + eyebrow: '开源 · 可自部署 · OpenAI Agents API', + title: ['One core.', 'Many agents.'], + lede: 'OpenAI Agents API 的开源实现。用官方 OpenAI SDK,在你自己的模型和机器上运行 Codex、Claude Code 或 MiniMax Code。', + primary: '运行第一个 Session', + secondary: '阅读文档', + installLabel: 'Linux amd64 · Docker · Python 3.9+', + copied: '已复制', + copy: '复制', + hud: [ + ['api', '/v1 · OpenAI Agents API'], + ['harness', 'codex | claude_sdk | mcode'], + ['protocol', 'responses | anthropic | chat_completions'], + ['sandbox', 'docker | microsandbox | e2b'], + ['machine', 'linux | macos | windows'], + ], + }, + ticker: en.ticker, + problem: { + index: '01', + kicker: '缺失的一层', + title: '调用模型已经标准化,让 Agent 干活还没有。', + lede: 'Agent 要读文件、执行命令、等待测试,中途还可能需要用户确认。模型的一次返回,只是其中一步。把它接进产品,又会冒出另一组问题。', + questions: [ + { q: '用户关掉网页,已经提交的工作怎么办?', a: '连接与任务分开。连接断开只是观察者离开,Turn 继续执行,随时查询持久状态。' }, + { q: '请求超时了,应该再发一次,还是先查原来的任务?', a: '查询持久状态。已支持的提交路径带幂等标识,响应丢失也不会重复创建工作。' }, + { q: '点击“停止”,停的是输出,还是 Agent 和它启动的工作?', a: '取消要有回执:Core 确认目标 Turn 的执行和清理状态。' }, + { q: '这次用了哪个模型、哪台机器?输出文件和执行记录在哪?', a: 'Session、Turn、Items、产物和用量由 Core 保存,通过 API 查询。' }, + { q: '从 Codex 换到 Claude Code,从 Docker 换到 E2B,产品要改多少代码?', a: 'API 调用不变。Harness 是 Session 上的设置,配上相应协议的模型服务;沙箱由管理员选择,藏在 Sandbox Provider 后面。' }, + ], + punch: '一个 Agent 在终端里好用,和它能被产品稳定调用,中间还有一整层工程。OpenAgentCore 就是这一层。', + }, + compose: { + index: '02', + kicker: '自由组合', + title: '选模型、选 Agent、选机器,是三个独立的决定。', + lede: '每个 Session 指定自己的 Harness、模型服务和执行环境。Core 在执行前校验组合,不支持的直接拒绝。', + harness: 'harness', + protocol: '模型协议', + environment: '执行环境', + accepted: '通过校验 · Session 已创建', + rejected: (harness, protocols) => `创建前即被拒绝 · ${harness} 只支持 ${protocols}`, + envs: { + openai_hosted: { label: '托管沙箱', note: 'Docker · microsandbox · E2B' }, + self_hosted: { label: '你自己的机器', note: 'Linux · macOS · Windows' }, + }, + input: '修复失败的测试,并说明改动。', + footnote: '支持哪些组合,由每个 Harness 明确声明,而不是猜测。', + footnoteLink: 'Harness 能力表', + }, + pillars: [ + { tag: 'api', title: '与 OpenAI 相同的 API', body: '用官方 OpenAI SDK 或 HTTP,连接你自己的 Core 地址,不用学新客户端。' }, + { tag: 'agent', title: '自选 Agent', body: '每个 Session 运行一个原生 Harness:Codex、Claude Code 或 MiniMax Code,模型服务由你配置。' }, + { tag: 'machine', title: '自选机器', body: '托管沙箱(Docker、microsandbox、E2B),或你自己的 Linux、macOS、Windows 机器。' }, + { tag: 'swap', title: '部件可替换', body: '沙箱、Harness 和模型服务都通过明确的协议接入,替换任何一个都不动 Core。' }, + ], + architecture: { + index: '03', + kicker: '每条边界都是协议', + title: '每个部件,都通过协议接入。', + lede: 'Core 保存持久执行状态并调度工作;Runtime 在 Environment 中准备文件和能力,启动原生 Harness;Harness 调用模型和工具,经 Runtime 把事件回传给 Core。', + tabs: [ + { id: 'ecosystem', label: '生态', alt: '上层应用通过 Agents API 接入,Harness、模型服务和计算资源通过各自边界连接。' }, + { id: 'protocols', label: '组件', alt: 'Core 之上是 Agents API 与 Core API;Sandbox Provider、Runtime、Harness、Model Provider 通过协议连接。' }, + { id: 'surfaces', label: 'API 命名空间', alt: '三个命名空间、三种凭证:应用用 Agents API,管理员用 Core API,节点用 Machine API。' }, + ], + boundariesTitle: '一条边界 · 一个协议 · 一份文档', + boundaries: en.architecture.boundaries.map((b) => ({ ...b })), + more: '阅读架构文档', + }, + session: { + index: '04', + kicker: '可管理的执行', + title: '把一次执行,变成可以管理的 Session。', + lede: 'Session 表示一段持续的 Agent 工作,Turn 是其中一次输入的执行。它们都有标识和持久状态。四条约定,决定了“执行中”“等待处理”“已取消”“失败”分别意味着什么。', + rules: [ + { title: '连接 ≠ 任务', body: '关闭输出流只是观察者离开,已提交的工作仍由执行系统管理。', art: en.session.rules[0].art }, + { title: '重试有身份', body: '已支持的提交路径带稳定标识,响应丢失也不会重复创建工作。', art: en.session.rules[1].art }, + { title: '取消要有回执', body: '关闭输出流证明不了什么,Core 确认目标 Turn 的执行和清理状态。', art: en.session.rules[2].art }, + { title: '执行 ≠ 机器', body: '一轮任务结束、Executor 关闭、Environment 回收,是三个不同的生命周期。', art: en.session.rules[3].art }, + ], + honest: '故障恢复有边界,而且是明确的:未知结果永远不会被当成成功。', + }, + observe: { + index: '05', + kicker: '看见运行', + title: '运行情况,看得见。', + lede: 'Web 是管理员控制台:按 Project 查看 Session、节点容量、等待调用方处理的工作,以及错误、耗时、Token 和工具调用。', + tabs: [ + { id: 'overview', label: '部署概览', alt: 'Web 控制台概览:服务状态、运行中的 Session、沙箱容量、节点与 Project。' }, + { id: 'metrics', label: 'Agent 监控', alt: 'Web 控制台 Agent 监控:请求、错误、耗时、Token 与工具调用。' }, + ], + caption: '截图数值仅用于展示界面。用量可见性取决于原生 Harness 提供的数据。', + }, + compare: { + index: '06', + kicker: '适用场景', + title: '不同接法,产品还要自己负责什么。', + head: ['接法', '适合什么', '还要自己负责'], + rows: [ + ['直接调用模型 API', '单次推理,或希望完全控制 Agent 循环', '上下文、工具执行、循环推进,以及完整的任务生命周期'], + ['直接调用原生 CLI / SDK', '个人自动化,或围绕一种 Harness 构建集成', '会话映射、执行进程、资源管理和不同引擎的差异'], + ['使用沙箱服务', '需要隔离的计算环境', '接入 Agent 引擎,管理执行、交互、记录和产品接口'], + ['使用 OpenAgentCore', '自部署,通过统一 API 使用多个原生 Harness 和执行环境', '业务权限、产品体验、团队编排,以及自身部署的运维'], + ], + }, + tradeoffs: { + index: '07', + kicker: '关键取舍', + title: '设计背后的选择。', + motto: ['状态归基础设施,', '智能归 Harness。'], + items: [ + { title: '保留原生 Harness,也接受它的约束', body: 'Runtime 和原生 Harness 运行在执行环境中,持久控制面放在 Core。原生引擎得以直接复用,运行环境、原生历史和恢复能力也因此仍然重要。' }, + { title: '共享协议,保留真实差异', body: '某个 Harness 支持的能力,不会被默认赋予其他 Harness。Core 在执行前检查组合,不支持的明确拒绝,待验证的如实记录。' }, + { title: '把简单留给调用者', body: '调用者只需要理解任务、输入、状态和结果。机器连接、原生执行器、资源清理,由清楚的组件边界承接。接口本身就是产品。' }, + ], + }, + start: { + index: '08', + kicker: '开始使用', + title: '从安装到第一个 Session。', + lede: '在装有 Docker 和 Python 3.9+ 的 Linux amd64 主机上:', + steps: [ + { title: '登录 Web', body: '用安装器生成的 Core key 登录,配置域名和 HTTPS。' }, + { title: '设置默认模型', body: '再为你的应用创建 Project API key。' }, + { title: '添加执行资源', body: '节点、E2B,或你自己的机器。' }, + { title: '运行第一个 Session', body: '用官方 OpenAI SDK,连接你自己的 Core。' }, + ], + cta: '安装指南', + trial: '快速开始', + }, + roadmap: { + index: '09', + kicker: '下一步', + title: '1.0 只是地基。', + lede: 'OpenAgentCore 仍处于 pre-release 阶段,支持范围以具体的 Harness、环境和操作组合为单位验收。接下来的方向:', + tracks: [ + { layer: 'harness', title: 'Agent loop 与工具解耦', body: 'Loop 运行在服务端,工具在用户本地或云端沙箱执行,工具层实现企业级鉴权。' }, + { layer: 'compute', title: '更快、更密的计算', body: '存算分离、快速恢复状态,按需扩容沙箱容量。' }, + { layer: 'storage', title: '更多存储介质', body: 'S3、OSS、共享文件系统,以及面向 Agent 的文件系统。' }, + { layer: 'apps', title: '基于 Agents API 的应用', body: '继续在公开 API 上打磨真实产品,和任何应用的接入方式完全一样。' }, + ], + }, + docs: { + index: '10', + kicker: '文档', + title: '一切细节,都在文档里。', + lede: (pages) => `共 ${pages} 页,直接读取仓库的 docs.json 生成。新增文档会自动出现在这里。文档正文为英文。`, + open: '打开', + }, + outro: { line: '产品可以有不同的交互、模型和执行引擎,共用同一层执行基础设施。', primary: '开始使用', secondary: '在 GitHub 上 Star' }, +} + +export const copy: Record = { en, zh } + +/** Native protocols per harness, default first (contracts/agents-api/model-execution.md). */ +export const harnesses = [ + { id: 'codex', label: 'Codex', protocols: ['responses'] }, + { id: 'claude_sdk', label: 'Claude Code', protocols: ['anthropic'] }, + { id: 'mcode', label: 'MiniMax Code', protocols: ['anthropic', 'responses', 'chat_completions'] }, +] as const + +export const protocols = ['responses', 'anthropic', 'chat_completions'] as const + +export const installCommand = 'curl -fsSL https://github.com/MiniMax-AI/OpenAgentCore/releases/latest/download/install.sh | bash' + +export const repoUrl = 'https://github.com/MiniMax-AI/OpenAgentCore' diff --git a/website/.vitepress/theme/style.css b/website/.vitepress/theme/style.css new file mode 100644 index 00000000..58f812c6 --- /dev/null +++ b/website/.vitepress/theme/style.css @@ -0,0 +1,97 @@ +/* OpenAgentCore brand on top of the VitePress default theme. */ +:root { + --vp-font-family-base: 'Inter Variable', Inter, system-ui, -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif; + --vp-font-family-mono: 'Geist Mono Variable', ui-monospace, SFMono-Regular, Menlo, monospace; + + --vp-c-brand-1: #15803d; + --vp-c-brand-2: #166534; + --vp-c-brand-3: #15803d; + --vp-c-brand-soft: rgba(21, 128, 61, 0.12); + + --vp-c-bg: #fafaf9; + --vp-c-bg-alt: #f2f3f0; + --vp-c-bg-elv: #ffffff; + --vp-c-bg-soft: #f2f3f0; + + --vp-button-brand-bg: #15803d; + --vp-button-brand-hover-bg: #166534; + --vp-button-brand-active-bg: #14532d; + --vp-home-hero-name-color: #15803d; +} + +.dark { + --vp-c-brand-1: #4ade80; + --vp-c-brand-2: #22c55e; + --vp-c-brand-3: #16a34a; + --vp-c-brand-soft: rgba(74, 222, 128, 0.14); + + --vp-c-bg: #0c1010; + --vp-c-bg-alt: #080b0a; + --vp-c-bg-elv: #121817; + --vp-c-bg-soft: #121817; + --vp-c-divider: rgba(232, 241, 235, 0.08); + --vp-c-gutter: rgba(232, 241, 235, 0.06); + --vp-code-block-bg: #0b100e; + + --vp-button-brand-text: #04210f; + --vp-button-brand-bg: #4ade80; + --vp-button-brand-hover-text: #04210f; + --vp-button-brand-hover-bg: #86efac; + --vp-button-brand-active-bg: #22c55e; +} + +/* Headings in the documentation use the monospace face, as on the Web console. */ +.vp-doc h1, +.vp-doc h2, +.vp-doc h3, +.oac-doc-title { + font-family: var(--vp-font-family-mono); + letter-spacing: -0.02em; +} + +.oac-doc-title { + margin: 0 0 24px; + font-size: 32px; + line-height: 1.25; + font-weight: 650; + color: var(--vp-c-text-1); +} + +.vp-doc img { + border-radius: 8px; +} + +.dark .vp-doc img[src*='architecture'], +.dark .vp-doc img[src*='development-architecture'] { + background: #fff; +} + +.VPNavBarTitle .title { + font-family: var(--vp-font-family-mono); + font-weight: 600; + letter-spacing: -0.02em; +} + +/* Landing page: translucent navbar over the full-bleed page. The selector + matches in the pre-rendered HTML, so the navbar never flashes opaque. */ +html:has(.landing) .VPNav, +html:has(.landing) .VPNavBar, +html:has(.landing) .VPNavBar .content-body, +html:has(.landing) .VPNavBar .divider-line { + background: transparent !important; +} +html:has(.landing) .VPNav { + backdrop-filter: blur(10px) saturate(1.2); + -webkit-backdrop-filter: blur(10px) saturate(1.2); + background: color-mix(in srgb, var(--vp-c-bg) 70%, transparent) !important; + border-bottom: 1px solid var(--vp-c-divider); +} +html:has(.landing) .VPContent.is-page { + padding-top: var(--vp-nav-height); +} +html:has(.landing) .VPPage { + padding: 0 !important; +} +html:has(.landing) .VPFooter { + border-top: 1px solid var(--vp-c-divider); +} diff --git a/website/README.md b/website/README.md new file mode 100644 index 00000000..551439c9 --- /dev/null +++ b/website/README.md @@ -0,0 +1,26 @@ +# OpenAgentCore website + +The website is the landing page plus the published documentation, built with [VitePress](https://vitepress.dev) and deployed to GitHub Pages. It reads the repository's existing `docs/`, `contracts/` and `docs.json` in place: no page is copied, moved or rewritten. + +## Run it + +```sh +pnpm install --filter @oac/website... +pnpm --filter @oac/website dev # http://127.0.0.1:4180 +pnpm --filter @oac/website build # website/.vitepress/dist +pnpm --filter @oac/website test # after build: navigation and output checks +pnpm --filter @oac/website preview # serve the build at http://127.0.0.1:4181 +``` + +## How documentation reaches the site + +- The content root is the repository root. `docs/**` and `contracts/**` keep their paths as URLs, so `docs/architecture.md` is `/docs/architecture`. `website/index.md` and `website/zh/index.md` are mapped onto `/` and `/zh/`. +- `docs.json` owns the page list and order. The sidebar, the landing page's documentation index and `/llms.txt` are generated from it at build time; each page's frontmatter `title` is its label. Every Markdown page under `docs/` and `contracts/` is built; listing it in `docs.json` adds it to the sidebar, the landing page and `llms.txt`. +- Each page's source Markdown is published next to its HTML (`/docs/architecture.md`), and the `README` paths declared in `docs.json` redirect to their section index. +- Relative links from a page to a file that is not a published page, such as `CONTRIBUTING.md` or `openapi.yaml`, are rewritten to GitHub at build time, so the Markdown works unchanged on GitHub and on the site. + +The landing page lives in `.vitepress/theme/`. `landing-content.ts` holds its English and Chinese copy; every claim there must be backed by a page in `docs/` or `contracts/`, and its harness protocols follow [Model execution](../contracts/agents-api/model-execution.md). + +## Publish + +`.github/workflows/website.yml` builds and tests the site for every pull request that touches its inputs, and deploys `main` to GitHub Pages. A repository administrator enables Pages once: **Settings → Pages → Source: GitHub Actions**. The build reads the Pages base path, so the site works both at `https://.github.io//` and on a custom domain set under **Settings → Pages → Custom domain**. diff --git a/website/index.md b/website/index.md new file mode 100644 index 00000000..194c9c5e --- /dev/null +++ b/website/index.md @@ -0,0 +1,8 @@ +--- +layout: page +title: OpenAgentCore — One core. Many agents. +titleTemplate: false +description: An open-source, self-hosted implementation of the OpenAI Agents API with multiple native harnesses. +--- + + diff --git a/website/package.json b/website/package.json new file mode 100644 index 00000000..f6d58867 --- /dev/null +++ b/website/package.json @@ -0,0 +1,20 @@ +{ + "name": "@oac/website", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vitepress dev --host 127.0.0.1 --port 4180", + "build": "vitepress build", + "preview": "vitepress preview --host 127.0.0.1 --port 4181", + "test": "node --test tests/*.test.mjs" + }, + "dependencies": { + "@fontsource-variable/geist-mono": "^5.3.0", + "@fontsource-variable/inter": "^5.3.0" + }, + "devDependencies": { + "vitepress": "^1.6.4", + "vue": "^3.5.31" + } +} diff --git a/website/tests/dist.test.mjs b/website/tests/dist.test.mjs new file mode 100644 index 00000000..1f4d20a7 --- /dev/null +++ b/website/tests/dist.test.mjs @@ -0,0 +1,47 @@ +// Checks a finished build: run `pnpm build` first. Every docs.json page has +// an HTML page and a raw Markdown copy, legacy paths redirect, both landing +// pages exist and llms.txt lists every page. +import assert from 'node:assert/strict' +import { existsSync, readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import test from 'node:test' +import { fileURLToPath } from 'node:url' +import { legacyRedirects, readDocsJson } from '../.vitepress/docs-nav.mts' + +const dist = fileURLToPath(new URL('../.vitepress/dist/', import.meta.url)) +const pages = readDocsJson().navigation.groups.flatMap((g) => g.pages) + +function html(page) { + return resolve(dist, `${page}.html`) +} + +test('the build output exists', () => { + assert.ok(existsSync(resolve(dist, 'index.html')), 'run `pnpm build` before `pnpm test`') +}) + +test('both landing pages are built', () => { + for (const [file, marker] of [['index.html', 'lang="en"'], ['zh/index.html', 'lang="zh-CN"']]) { + const source = readFileSync(resolve(dist, file), 'utf8') + assert.match(source, /class="landing"/, `${file} renders the landing page`) + assert.ok(source.includes(marker), `${file} has ${marker}`) + } +}) + +test('every documentation page has HTML and a Markdown copy', () => { + for (const page of pages) { + assert.ok(existsSync(html(page)), `${page}.html`) + assert.ok(existsSync(resolve(dist, `${page}.md`)), `${page}.md`) + } +}) + +test('legacy paths redirect', () => { + for (const { from, to } of legacyRedirects()) { + const source = readFileSync(resolve(dist, `${from}.html`), 'utf8') + assert.ok(source.includes(`url=`) && source.includes(to.replace(/^\//, '')), `${from} → ${to}`) + } +}) + +test('llms.txt lists every page', () => { + const llms = readFileSync(resolve(dist, 'llms.txt'), 'utf8') + for (const page of pages) assert.ok(llms.includes(`${page}.md)`), `${page} in llms.txt`) +}) diff --git a/website/tests/docs-nav.test.mjs b/website/tests/docs-nav.test.mjs new file mode 100644 index 00000000..1de1705b --- /dev/null +++ b/website/tests/docs-nav.test.mjs @@ -0,0 +1,43 @@ +// Unit tests for the navigation that the website derives from docs.json. +import assert from 'node:assert/strict' +import { existsSync } from 'node:fs' +import { resolve } from 'node:path' +import test from 'node:test' +import { docsSidebar, legacyRedirects, pageLink, pageTitle, readDocsJson, repoRoot, unquote } from '../.vitepress/docs-nav.mts' + +test('page ids map to clean routes', () => { + assert.equal(pageLink('docs/getting-started/index'), '/docs/getting-started/') + assert.equal(pageLink('docs/architecture'), '/docs/architecture') + assert.equal(pageLink('contracts/agents-api/index'), '/contracts/agents-api/') +}) + +test('YAML titles are unquoted with their escapes', () => { + assert.equal(unquote('"Core\\u2013Runtime protocol"'), 'Core–Runtime protocol') + assert.equal(unquote("'It''s quoted'"), "It's quoted") + assert.equal(unquote('Plain title'), 'Plain title') +}) + +test('every docs.json page exists and has a title', () => { + for (const group of readDocsJson().navigation.groups) { + for (const page of group.pages) { + assert.ok(existsSync(resolve(repoRoot, `${page}.md`)), `${page}.md exists`) + assert.ok(pageTitle(page).length > 0, `${page} has a title`) + } + } +}) + +test('the sidebar follows docs.json order', () => { + const groups = readDocsJson().navigation.groups + const sidebar = docsSidebar() + assert.deepEqual(sidebar.map((g) => g.text), groups.map((g) => g.group)) + assert.deepEqual(sidebar[0].items.map((i) => i.link), groups[0].pages.map(pageLink)) +}) + +test('legacy README paths redirect to section indexes', () => { + const redirects = legacyRedirects() + assert.ok(redirects.length > 0) + for (const { from, to } of redirects) { + assert.ok(!from.endsWith('.md'), `${from} is not a Markdown copy`) + assert.ok(to.startsWith('/'), `${to} is a site route`) + } +}) diff --git a/website/tests/harnesses.test.mjs b/website/tests/harnesses.test.mjs new file mode 100644 index 00000000..4c882a7d --- /dev/null +++ b/website/tests/harnesses.test.mjs @@ -0,0 +1,27 @@ +// The landing page's harness table must match the repository's declarations: +// ids and labels from the built-in catalog, protocols from Model execution. +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import test from 'node:test' +import { repoRoot } from '../.vitepress/docs-nav.mts' +import { harnesses } from '../.vitepress/theme/landing-content.ts' + +test('harness ids and labels match the built-in catalog', () => { + const catalog = JSON.parse(readFileSync(resolve(repoRoot, 'internal/harnessconfig/builtin/catalog.json'), 'utf8')) + const expected = Object.fromEntries(catalog.map((h) => [h.kind, h.label])) + assert.deepEqual(Object.fromEntries(harnesses.map((h) => [h.id, h.label])), expected) +}) + +test('harness protocols match Model execution, default first', () => { + const doc = readFileSync(resolve(repoRoot, 'contracts/agents-api/model-execution.md'), 'utf8') + const table = doc.split('| Harness | Supported protocols, default first |')[1].split('\n\n')[0] + const rows = Object.fromEntries( + [...table.matchAll(/^\| ([^|]+) \| (.+) \|$/gm)] + .filter(([, name]) => !name.startsWith('---')) + .map(([, name, protocols]) => [name.trim(), [...protocols.matchAll(/`([a-z_]+)`/g)].map((m) => m[1])]), + ) + // The table names Claude Code by its SDK. + const docName = { codex: 'Codex', claude_sdk: 'Claude SDK', mcode: 'MiniMax Code' } + for (const h of harnesses) assert.deepEqual([...h.protocols], rows[docName[h.id]], h.id) +}) diff --git a/website/zh/index.md b/website/zh/index.md new file mode 100644 index 00000000..05facbf5 --- /dev/null +++ b/website/zh/index.md @@ -0,0 +1,8 @@ +--- +layout: page +title: OpenAgentCore — One core. Many agents. +titleTemplate: false +description: OpenAI Agents API 的开源实现,支持多种原生 Harness,可自部署。 +--- + + From 0033da062509dc173ae7b4f61248340e7123ffc4 Mon Sep 17 00:00:00 2001 From: yuanhe Date: Thu, 1 Oct 2026 17:54:03 +0800 Subject: [PATCH 2/2] Check the website in core-check Add a plan-selected website job that runs make check-website, so every tracked website source has a matching CI rule. website.yml now builds only documentation-only pull requests and deploys main. --- .github/workflows/check.yml | 15 ++++++++++++++- .github/workflows/website.yml | 20 ++++++-------------- Makefile | 7 ++++++- scripts/ci_plan.py | 5 +++-- scripts/ci_plan_test.py | 2 +- website/README.md | 2 +- 6 files changed, 31 insertions(+), 20 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7bbde54a..dd965483 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -234,6 +234,19 @@ jobs: - name: Typecheck, test and build Web and clients run: make check-web-unit + website: + needs: plan + if: needs.plan.result == 'success' && contains(fromJSON(needs.plan.outputs.jobs || '[]'), 'website') + runs-on: ${{ vars.OAC_USE_GITHUB_RUNNERS == 'true' && 'ubuntu-22.04' || 'blacksmith-2vcpu-ubuntu-2204' }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ inputs.ref || github.sha }} + - uses: ./.github/actions/node + - name: Build and test the website + run: make check-website + web-acceptance: needs: [plan, web] if: needs.web.result == 'success' && needs.plan.result == 'success' && contains(fromJSON(needs.plan.outputs.jobs || '[]'), 'web-acceptance') @@ -292,7 +305,7 @@ jobs: # Always report the required check, even when planning or a dependency fails. check: if: always() - needs: [plan, hygiene, distribution, backend, harness, example, web, web-acceptance, api, native, lint] + needs: [plan, hygiene, distribution, backend, harness, example, web, web-acceptance, website, api, native, lint] runs-on: ${{ vars.OAC_USE_GITHUB_RUNNERS == 'true' && 'ubuntu-22.04' || 'blacksmith-2vcpu-ubuntu-2204' }} timeout-minutes: 5 steps: diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 0405b949..b5e5b0ea 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -1,5 +1,5 @@ -# Builds the website (landing page and documentation) on every change to its -# sources, and publishes it to GitHub Pages from main. The website reads the +# Publishes the website (landing page and documentation) to GitHub Pages from +# main, and builds it for documentation-only pull requests. The website reads the # existing docs/, contracts/ and docs.json in place; see website/README.md. name: website @@ -16,17 +16,13 @@ on: - pnpm-workspace.yaml - .github/actions/node/** - .github/workflows/website.yml + # Changes under website/ are checked by core-check; documentation-only + # changes build the site here so broken links surface before merge. pull_request: paths: - - website/** - docs/** - contracts/** - docs.json - - package.json - - pnpm-lock.yaml - - pnpm-workspace.yaml - - .github/actions/node/** - - .github/workflows/website.yml workflow_dispatch: permissions: @@ -53,18 +49,14 @@ jobs: # Full history gives each page its last-updated date. fetch-depth: 0 - uses: ./.github/actions/node - - name: Install website dependencies - run: pnpm install --frozen-lockfile --filter @oac/website... - name: Read the Pages base path id: pages if: env.PUBLISH == 'true' uses: actions/configure-pages@v6 - - name: Build - run: pnpm --filter @oac/website build + - name: Build and test + run: make check-website env: WEBSITE_BASE: ${{ steps.pages.outputs.base_path }} - - name: Test navigation and build output - run: pnpm --filter @oac/website test - name: Upload the site if: env.PUBLISH == 'true' uses: actions/upload-pages-artifact@v5 diff --git a/Makefile b/Makefile index 128b8473..eb314f77 100644 --- a/Makefile +++ b/Makefile @@ -100,7 +100,7 @@ check-claude-sdk: node-deps pnpm --filter @oac/claude-sdk-adapter test $(MAKE) build-claude-sdk-runtime -.PHONY: check-web-unit check-web-acceptance +.PHONY: check-web-unit check-web-acceptance check-website check-web: override OAC_WEB_TEST_SHARD := check-web: check-web-unit check-web-acceptance @@ -109,6 +109,11 @@ check-web-unit: node-deps pnpm test:web pnpm --filter @oac/web build +# The website build also checks every published documentation link. +check-website: node-deps + pnpm --filter @oac/website build + pnpm --filter @oac/website test + # CI shards run in separate jobs, each with its own fixture and Web server. # An unset shard keeps the complete local make check gate. check-web-acceptance: node-deps diff --git a/scripts/ci_plan.py b/scripts/ci_plan.py index 433dd4ff..34a9224f 100644 --- a/scripts/ci_plan.py +++ b/scripts/ci_plan.py @@ -8,8 +8,8 @@ from pathlib import Path, PurePosixPath import subprocess -JOBS = ("hygiene", "distribution", "backend", "harness", "example", "web", "web-acceptance", "api", "native", "lint") -NODE_JOBS = ("harness", "example", "web", "web-acceptance", "native") +JOBS = ("hygiene", "distribution", "backend", "harness", "example", "web", "web-acceptance", "website", "api", "native", "lint") +NODE_JOBS = ("harness", "example", "web", "web-acceptance", "website", "native") GO_JOBS = ("distribution", "backend", "api", "native") # Exact file matches keep new workflows/actions conservative until classified. CI_INPUTS = { @@ -41,6 +41,7 @@ (("apps/web/", "playwright.config.ts"), WEB, ("web", "web-acceptance")), (("services/web/",), (*GO, "Dockerfile"), ("distribution", "web", "web-acceptance")), (("example/",), WEB, ("example",)), + (("website/",), (*WEB, ".vue", ".md"), ("website",)), (("services/core/",), CORE, ("backend", "api")), (("services/core/internal/nativeinstaller/",), GO, ("native", "distribution")), (("services/core/deploy/", "services/core/tools/"), CORE, ("distribution",)), diff --git a/scripts/ci_plan_test.py b/scripts/ci_plan_test.py index 6d86be45..5e0be83f 100644 --- a/scripts/ci_plan_test.py +++ b/scripts/ci_plan_test.py @@ -106,7 +106,7 @@ def test_dependencies_are_scoped_to_language_consumers(self): self.assertTrue(ci.select([path])["image"]) for path in ("package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", ".npmrc", "packages/tsconfig/base.json"): with self.subTest(path=path): - self.assertEqual(self.jobs(path), {"hygiene", "harness", "example", "web", "web-acceptance", "native"}) + self.assertEqual(self.jobs(path), {"hygiene", "harness", "example", "web", "web-acceptance", "website", "native"}) self.assertFalse(ci.select([path])["image"]) self.assertEqual(self.jobs("tsconfig.base.json"), {"hygiene", "example", "web", "web-acceptance"}) diff --git a/website/README.md b/website/README.md index 551439c9..6bc58ab8 100644 --- a/website/README.md +++ b/website/README.md @@ -23,4 +23,4 @@ The landing page lives in `.vitepress/theme/`. `landing-content.ts` holds its En ## Publish -`.github/workflows/website.yml` builds and tests the site for every pull request that touches its inputs, and deploys `main` to GitHub Pages. A repository administrator enables Pages once: **Settings → Pages → Source: GitHub Actions**. The build reads the Pages base path, so the site works both at `https://.github.io//` and on a custom domain set under **Settings → Pages → Custom domain**. +`make check-website` builds and tests the site. core-check runs it for changes under `website/` and to Node dependencies; `.github/workflows/website.yml` runs it for documentation-only pull requests and deploys `main` to GitHub Pages. A repository administrator enables Pages once: **Settings → Pages → Source: GitHub Actions**. The build reads the Pages base path, so the site works both at `https://.github.io//` and on a custom domain set under **Settings → Pages → Custom domain**.